diff --git a/README.md b/README.md index b7092d8..e3d9f07 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,27 @@ contract Example { } ``` -### protocols/ChatGPT.sol +### protocols/EthJsonRPC.sol -Helper library to send completion requests to ChatGPT. +Helper library to interact with the Ethereum JsonRPC protocol. #### Example usage +```solidity +import "suave-std/protocols/EthJsonRPC.sol"; + +contract Example { + function example() { + EthJsonRPC jsonrpc = new EthJsonRPC("http://..."); + jsonrpc.nonce(address(this)); + } +} +``` + +### protocols/ChatGPT.sol + +Helper library to send completion requests to ChatGPT. + ```solidity import "suave-std/protocols/ChatGPT.sol"; diff --git a/src/forge/SuaveAddrs.sol b/src/forge/SuaveAddrs.sol index dd4be46..487c361 100644 --- a/src/forge/SuaveAddrs.sol +++ b/src/forge/SuaveAddrs.sol @@ -6,26 +6,27 @@ import "../suavelib/Suave.sol"; library SuaveAddrs { function getSuaveAddrs() external pure returns (address[] memory) { - address[] memory addrList = new address[](18); + address[] memory addrList = new address[](19); addrList[0] = Suave.IS_CONFIDENTIAL_ADDR; addrList[1] = Suave.BUILD_ETH_BLOCK; addrList[2] = Suave.CONFIDENTIAL_RETRIEVE; addrList[3] = Suave.CONFIDENTIAL_STORE; - addrList[4] = Suave.DO_HTTPREQUEST; - addrList[5] = Suave.ETHCALL; - addrList[6] = Suave.EXTRACT_HINT; - addrList[7] = Suave.FETCH_DATA_RECORDS; - addrList[8] = Suave.FILL_MEV_SHARE_BUNDLE; - addrList[9] = Suave.NEW_BUILDER; - addrList[10] = Suave.NEW_DATA_RECORD; - addrList[11] = Suave.PRIVATE_KEY_GEN; - addrList[12] = Suave.SIGN_ETH_TRANSACTION; - addrList[13] = Suave.SIGN_MESSAGE; - addrList[14] = Suave.SIMULATE_BUNDLE; - addrList[15] = Suave.SIMULATE_TRANSACTION; - addrList[16] = Suave.SUBMIT_BUNDLE_JSON_RPC; - addrList[17] = Suave.SUBMIT_ETH_BLOCK_TO_RELAY; + addrList[4] = Suave.CONTEXT_GET; + addrList[5] = Suave.DO_HTTPREQUEST; + addrList[6] = Suave.ETHCALL; + addrList[7] = Suave.EXTRACT_HINT; + addrList[8] = Suave.FETCH_DATA_RECORDS; + addrList[9] = Suave.FILL_MEV_SHARE_BUNDLE; + addrList[10] = Suave.NEW_BUILDER; + addrList[11] = Suave.NEW_DATA_RECORD; + addrList[12] = Suave.PRIVATE_KEY_GEN; + addrList[13] = Suave.SIGN_ETH_TRANSACTION; + addrList[14] = Suave.SIGN_MESSAGE; + addrList[15] = Suave.SIMULATE_BUNDLE; + addrList[16] = Suave.SIMULATE_TRANSACTION; + addrList[17] = Suave.SUBMIT_BUNDLE_JSON_RPC; + addrList[18] = Suave.SUBMIT_ETH_BLOCK_TO_RELAY; return addrList; } diff --git a/src/protocols/EthJsonRPC.sol b/src/protocols/EthJsonRPC.sol new file mode 100644 index 0000000..03b70ed --- /dev/null +++ b/src/protocols/EthJsonRPC.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import "src/suavelib/Suave.sol"; +import "solady/src/utils/JSONParserLib.sol"; +import "solady/src/utils/LibString.sol"; + +contract EthJsonRPC { + using JSONParserLib for *; + + string endpoint; + + constructor(string memory _endpoint) { + endpoint = _endpoint; + } + + function nonce(address addr) public returns (uint256) { + bytes memory body = abi.encodePacked( + '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["', + LibString.toHexStringChecksummed(addr), + '","latest"],"id":1}' + ); + + JSONParserLib.Item memory item = doRequest(string(body)); + uint256 val = JSONParserLib.parseUintFromHex(trimQuotes(item.value())); + return val; + } + + function doRequest(string memory body) public returns (JSONParserLib.Item memory) { + Suave.HttpRequest memory request; + request.method = "POST"; + request.url = endpoint; + request.headers = new string[](1); + request.headers[0] = "Content-Type: application/json"; + request.body = bytes(body); + + bytes memory output = Suave.doHTTPRequest(request); + + JSONParserLib.Item memory item = string(output).parse(); + return item.at('"result"'); + } + + function trimQuotes(string memory input) private pure returns (string memory) { + bytes memory inputBytes = bytes(input); + require( + inputBytes.length >= 2 && inputBytes[0] == '"' && inputBytes[inputBytes.length - 1] == '"', "Invalid input" + ); + + bytes memory result = new bytes(inputBytes.length - 2); + + for (uint256 i = 1; i < inputBytes.length - 1; i++) { + result[i - 1] = inputBytes[i]; + } + + return string(result); + } +} diff --git a/test/protocols/EthJsonRPC.t.sol b/test/protocols/EthJsonRPC.t.sol new file mode 100644 index 0000000..47ff4a8 --- /dev/null +++ b/test/protocols/EthJsonRPC.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/Test.sol"; +import "solady/src/utils/LibString.sol"; +import "src/protocols/EthJsonRPC.sol"; + +contract EthJsonRPCTest is Test, SuaveEnabled { + function testEthJsonRPCGetNonce() public { + EthJsonRPC ethjsonrpc = getEthJsonRPC(); + + uint256 nonce = ethjsonrpc.nonce(address(this)); + assertEq(nonce, 0); + } + + function getEthJsonRPC() public returns (EthJsonRPC ethjsonrpc) { + try vm.envString("JSONRPC_ENDPOINT") returns (string memory endpoint) { + ethjsonrpc = new EthJsonRPC(endpoint); + } catch { + vm.skip(true); + } + } +}