diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..644d192 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "examples/exec-scripts/lib/forge-std"] + path = examples/exec-scripts/lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/Justfile b/Justfile index 10f5d1f..d8e6c5b 100644 --- a/Justfile +++ b/Justfile @@ -49,3 +49,7 @@ clippy: # Build for the native target build *args='': cargo build --workspace --all $@ + +# Generates all test fixtures for scripts in examples/exec-scripts +gen fork_url: + @just ./examples/exec-scripts/gen {{fork_url}} diff --git a/examples/exec-scripts/.github/workflows/test.yml b/examples/exec-scripts/.github/workflows/test.yml new file mode 100644 index 0000000..9282e82 --- /dev/null +++ b/examples/exec-scripts/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: test + +on: workflow_dispatch + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge build + run: | + forge --version + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/examples/exec-scripts/.gitignore b/examples/exec-scripts/.gitignore new file mode 100644 index 0000000..2c1c525 --- /dev/null +++ b/examples/exec-scripts/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/examples/exec-scripts/Justfile b/examples/exec-scripts/Justfile new file mode 100644 index 0000000..e8012b0 --- /dev/null +++ b/examples/exec-scripts/Justfile @@ -0,0 +1,21 @@ +set positional-arguments +# default recipe to display help information +default: + @just --list + +# Installs the opt8n binary +install-opt8n: + cargo install --path ../../bin/opt8n --locked + +# Cleans build artifacts and cache +forge-updates: + @forge clean + @forge update + +# Generates all execution test fixtures for scripts in this project +gen fork_url: install-opt8n forge-updates + just gen-weth9 {{fork_url}} + +# Generates the execution test fixture for the weth9 precompile script +gen-weth9 fork_url: install-opt8n forge-updates + opt8n --output ../../fixtures/execution/Weth9Precompile.json --fork-url {{fork_url}} script Weth9Precompile diff --git a/examples/exec-scripts/README.md b/examples/exec-scripts/README.md new file mode 100644 index 0000000..9265b45 --- /dev/null +++ b/examples/exec-scripts/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/examples/exec-scripts/foundry.toml b/examples/exec-scripts/foundry.toml new file mode 100644 index 0000000..25b918f --- /dev/null +++ b/examples/exec-scripts/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/examples/exec-scripts/lib/forge-std b/examples/exec-scripts/lib/forge-std new file mode 160000 index 0000000..2cbff06 --- /dev/null +++ b/examples/exec-scripts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 2cbff0602d340503dba9828ab6981053704d1384 diff --git a/examples/exec-scripts/script/Weth9Precompile.s.sol b/examples/exec-scripts/script/Weth9Precompile.s.sol new file mode 100644 index 0000000..f232fd6 --- /dev/null +++ b/examples/exec-scripts/script/Weth9Precompile.s.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script} from "forge-std/Script.sol"; +import { StdAssertions } from "forge-std/StdAssertions.sol"; + +interface Weth9 { + function name() external view returns (string memory); + function symbol() external view returns (string memory); + function decimals() external pure returns (uint8); + function balanceOf(address owner) external view returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); + function deposit() external payable; + function withdraw(uint256 wad) external; +} + +/// Calls the WETH9 Precompile +contract Weth9Precompile is Script, StdAssertions { + + Weth9 constant WETH9 = Weth9(address(0x4200000000000000000000000000000000000006)); + + function setUp() public {} + + function run() public { + // Validate Weth9 Precompile Metadata + assertEq(WETH9.name(), "Wrapped Ether"); + assertEq(WETH9.symbol(), "WETH"); + assertEq(WETH9.decimals(), 18); + + // Deposit 1 Ether + vm.broadcast(); + WETH9.deposit{value: 1 ether}(); + assertEq(WETH9.balanceOf(address(this)), 1 ether); + + // Withdraw 1 Ether + vm.broadcast(); + WETH9.withdraw(1 ether); + assertEq(WETH9.balanceOf(address(this)), 0); + } +} diff --git a/fixtures/execution/Weth9Precompile.json b/fixtures/execution/Weth9Precompile.json new file mode 100644 index 0000000..ebe45d6 --- /dev/null +++ b/fixtures/execution/Weth9Precompile.json @@ -0,0 +1,93 @@ +{ + "env": { + "currentCoinbase": "0x0000000000000000000000000000000000000000", + "currentDifficulty": "0x7642a519106489a25ccce64b86090c8e7db9372f32e9eda11200bd1c717f2625", + "currentGasLimit": "0x1c9c380", + "previousHash": "0xcf51399fdee5e4825ae91080390d0aa107c460e1056792ff0800ae02538825d3", + "currentNumber": "0x759ffd8", + "currentTimestamp": "0x66a7d969" + }, + "alloc": { + "0x4200000000000000000000000000000000000006": { + "balance": "0x86ab06629fccad08705", + "code": "0x6080604052600436106100bc5760003560e01c8063313ce56711610074578063a9059cbb1161004e578063a9059cbb146102cb578063d0e30db0146100bc578063dd62ed3e14610311576100bc565b8063313ce5671461024b57806370a082311461027657806395d89b41146102b6576100bc565b806318160ddd116100a557806318160ddd146101aa57806323b872dd146101d15780632e1a7d4d14610221576100bc565b806306fdde03146100c6578063095ea7b314610150575b6100c4610359565b005b3480156100d257600080fd5b506100db6103a8565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101155781810151838201526020016100fd565b50505050905090810190601f1680156101425780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015c57600080fd5b506101966004803603604081101561017357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610454565b604080519115158252519081900360200190f35b3480156101b657600080fd5b506101bf6104c7565b60408051918252519081900360200190f35b3480156101dd57600080fd5b50610196600480360360608110156101f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602081013590911690604001356104cb565b34801561022d57600080fd5b506100c46004803603602081101561024457600080fd5b503561066b565b34801561025757600080fd5b50610260610700565b6040805160ff9092168252519081900360200190f35b34801561028257600080fd5b506101bf6004803603602081101561029957600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610709565b3480156102c257600080fd5b506100db61071b565b3480156102d757600080fd5b50610196600480360360408110156102ee57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610793565b34801561031d57600080fd5b506101bf6004803603604081101561033457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166107a7565b33600081815260036020908152604091829020805434908101909155825190815291517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9281900390910190a2565b6000805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b820191906000526020600020905b81548152906001019060200180831161042f57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b4790565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600360205260408120548211156104fd57600080fd5b73ffffffffffffffffffffffffffffffffffffffff84163314801590610573575073ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14155b156105ed5773ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020548211156105b557600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020805483900390555b73ffffffffffffffffffffffffffffffffffffffff808516600081815260036020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060019392505050565b3360009081526003602052604090205481111561068757600080fd5b33600081815260036020526040808220805485900390555183156108fc0291849190818181858888f193505050501580156106c6573d6000803e3d6000fd5b5060408051828152905133917f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65919081900360200190a250565b60025460ff1681565b60036020526000908152604090205481565b60018054604080516020600284861615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b60006107a03384846104cb565b9392505050565b60046020908152600092835260408084209091529082529020548156fea265627a7a7231582091c18790e0cca5011d2518024840ee00fecc67e11f56fd746f2cf84d5b583e0064736f6c63430005110032" + }, + "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { + "balance": "0x21e19e0c9bab2400000" + }, + "0x0000000000000000000000000000000000000000": { + "balance": "0x117b08b474fb13e6", + "nonce": 3 + } + }, + "outAlloc": { + "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { + "balance": "0x21e0c0012fca42e977a", + "nonce": 1 + }, + "0x0000000000000000000000000000000000000000": { + "balance": "0x117b08bb864b91a6" + }, + "0x4200000000000000000000000000000000000006": { + "balance": "0x86abe46e0b072348705", + "storage": { + "0x6a706e96617ceafd46f24757f71aad32b88c8bf0b8ae22ec3de611582d2d4a6c": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" + } + } + }, + "txs": [ + { + "EIP1559": { + "chainId": "0xa", + "nonce": "0x0", + "gasLimit": "0x10050", + "maxFeePerGas": "0x13742c", + "maxPriorityFeePerGas": "0xa52e0", + "to": "0x4200000000000000000000000000000000000006", + "value": "0xde0b6b3a7640000", + "accessList": [], + "input": "0xd0e30db0", + "r": "0xf59b22eebd70918b8c00865574d3c50a2ea431e3b305bf1d3dbc6cd9fb5f473d", + "s": "0x4063dd1731c08ec0983dc4be06d4f134528549b08464bed5ba3f367750939ea6", + "yParity": "0x1", + "hash": "0x09f71cc85e9af7422e76d7db6d9510b85a627f5972f150a0d8727fd2f1eb6cc8" + } + } + ], + "result": { + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "txRoot": "0x39195e50de80168acf9316a7ebd991daca9ca041faebe8355153c3ac5212ad89", + "receiptRoot": "0xc4b290f69c74db3a91e2758c1489a26e40885f0e07e93a5158ffbb86ddbe36c0", + "logsBloom": "0x00000000000000000000040000000000000000000000000000040000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000400000000000000000", + "receipts": [ + { + "root": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionHash": "0x09f71cc85e9af7422e76d7db6d9510b85a627f5972f150a0d8727fd2f1eb6cc8", + "gasUsed": "0xaf42", + "blockHash": "0x32da196d87e53a77c0aef14839a1272afac5757efc3f99a29c90149a1000c4bb", + "transactionIndex": "0x0", + "type": "0x2", + "status": "0x1", + "cumulativeGasUsed": "0xaf42", + "logs": [ + { + "address": "0x4200000000000000000000000000000000000006", + "topics": [ + "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c", + "0x000000000000000000000000a0ee7a142d267c1f36714e4a8f75612f20a79720" + ], + "data": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000", + "blockHash": "0x32da196d87e53a77c0aef14839a1272afac5757efc3f99a29c90149a1000c4bb", + "blockNumber": "0x759ffd8", + "blockTimestamp": "0x66a7d969", + "transactionHash": "0x09f71cc85e9af7422e76d7db6d9510b85a627f5972f150a0d8727fd2f1eb6cc8", + "transactionIndex": "0x0", + "logIndex": "0x0", + "removed": false + } + ], + "logsBloom": "0x00000000000000000000040000000000000000000000000000040000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000400000000000000000" + } + ] + } +} \ No newline at end of file