|
| 1 | +# Purpose |
| 2 | + |
| 3 | +This package primarily serves as testing support for the sei-cosmwasm package. |
| 4 | +It includes an example contract that can be used to test package behavior |
| 5 | +locally and can be used as a reference for implementation details to include |
| 6 | +sei-chain integration in your smart contracts. |
| 7 | + |
| 8 | +## Examples |
| 9 | + |
| 10 | +### Call an EVM contract from a CosmWasm contract |
| 11 | + |
| 12 | +First, we need to deploy an EVM contract on the Sei chain. Let's use a simple counter contract and |
| 13 | +foundry tooling to deploy it. |
| 14 | + |
| 15 | +Install the [foundry tooling](https://book.getfoundry.sh/) by following this [Installation guide](https://book.getfoundry.sh/getting-started/installation.html). |
| 16 | + |
| 17 | + |
| 18 | +Create a new project following the [Creating New Project Guide](https://book.getfoundry.sh/projects/creating-a-new-project). |
| 19 | + |
| 20 | +Once project is created, tweak the contract code to the following, by adding a `getCounter` function: |
| 21 | + |
| 22 | +```solidity |
| 23 | +// SPDX-License-Identifier: UNLICENSED |
| 24 | +pragma solidity ^0.8.13; |
| 25 | +
|
| 26 | +contract Counter { |
| 27 | + uint256 public number; |
| 28 | +
|
| 29 | + function setNumber(uint256 newNumber) public { |
| 30 | + number = newNumber; |
| 31 | + } |
| 32 | +
|
| 33 | + function increment() public { |
| 34 | + number++; |
| 35 | + } |
| 36 | +
|
| 37 | + function getCount() public view returns (uint256) { |
| 38 | + return number; |
| 39 | + } |
| 40 | +} |
| 41 | +
|
| 42 | +``` |
| 43 | + |
| 44 | +And the test code to the following: |
| 45 | +```solidity |
| 46 | +// SPDX-License-Identifier: UNLICENSED |
| 47 | +pragma solidity ^0.8.13; |
| 48 | +
|
| 49 | +import {Test, console} from "forge-std/Test.sol"; |
| 50 | +import {Counter} from "../src/Counter.sol"; |
| 51 | +
|
| 52 | +contract CounterTest is Test { |
| 53 | + Counter public counter; |
| 54 | +
|
| 55 | + function setUp() public { |
| 56 | + counter = new Counter(); |
| 57 | + counter.setNumber(0); |
| 58 | + } |
| 59 | +
|
| 60 | + function test_Increment() public { |
| 61 | + counter.increment(); |
| 62 | + assertEq(counter.number(), 1); |
| 63 | + } |
| 64 | +
|
| 65 | + function testFuzz_SetNumber(uint256 x) public { |
| 66 | + counter.setNumber(x); |
| 67 | + assertEq(counter.number(), x); |
| 68 | + } |
| 69 | +
|
| 70 | + function test_GetCount() public { |
| 71 | + uint256 initialCount = counter.getCount(); |
| 72 | + counter.increment(); |
| 73 | + assertEq(counter.getCount(), initialCount + 1); |
| 74 | + } |
| 75 | +} |
| 76 | +``` |
| 77 | +Run the tests with the following command: |
| 78 | +```shell |
| 79 | +forge test |
| 80 | +``` |
| 81 | +If tests pass, deploy the contract to the Sei chain with the following command: |
| 82 | +```shell |
| 83 | +forge create --rpc-url $SEI_NODE_URI --mnemonic $MNEMONIC src/Counter.sol:Counter |
| 84 | +``` |
| 85 | +Where `$SEI_NODE_URI` is the URI of the Sei node and `$MNEMONIC` is the mnemonic of the account that will deploy the |
| 86 | +contract. |
| 87 | +If you run local Sei node, the address will be `http://localhost:8545`. |
| 88 | +If deployment is successful, you will get the EVM contract address in the output. |
| 89 | +```shell |
| 90 | +[⠒] Compiling... |
| 91 | +No files changed, compilation skipped |
| 92 | +Deployer: $0X_DEPLOYER_ADDRESS |
| 93 | +Deployed to: $0X_CONTRACT_ADDRESS |
| 94 | +Transaction hash: $0X_TX_HASH |
| 95 | + |
| 96 | +``` |
| 97 | +Let's use the `cast` command to query the contract: |
| 98 | +```shell |
| 99 | +cast call $0X_CONTRACT_ADDRESS "getCount()(uint256)" |
| 100 | +``` |
| 101 | +The command should return `0` as the initial value of the counter. |
| 102 | + |
| 103 | +Now we can use the `cast` command to call the `increment` function: |
| 104 | +```shell |
| 105 | +cast send $0X_CONTRACT_ADDRESS "increment()" --mnemonic $MNEMONIC |
| 106 | +``` |
| 107 | +If command is successful, you will get the transaction hash and other info back. |
| 108 | + |
| 109 | +Now let's call the `getCount` function again and this case it should return `1`. |
| 110 | + |
| 111 | +Let's deploy the CosmWasm contract that will call the EVM contract. |
| 112 | + |
| 113 | +Follow the instructions in [this README](../../README.md) to deploy the test contract. |
| 114 | + |
| 115 | +Once the contract is deployed, please note the contract address of the CosmWasm contract or export it as |
| 116 | +`$COSMWASM_CONTRACT_ADDRESS`. |
| 117 | + |
| 118 | +We can now query EVM contract from the CosmWasm contract. But before, we need to get the correct inputs. |
| 119 | +To generate inputs, we can use the `seid` command line tool. |
| 120 | + |
| 121 | +First we need to get the ABI of the EVM contract. It was generated by the foundry tooling and is usually located in the |
| 122 | +`out` folder. E.g. at path `out/Counter.sol/Counter.json`. |
| 123 | + |
| 124 | +For our example the ABI looks like this: |
| 125 | + |
| 126 | +```json |
| 127 | +[{"type":"function","name":"getCount","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"increment","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"number","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"setNumber","inputs":[{"name":"newNumber","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"}] |
| 128 | +``` |
| 129 | +For convenience, we can save the ABI to a file, e.g. `abi.json`. |
| 130 | + |
| 131 | +Now let's generate the inputs to invoke the `increcemnt` function with the `seid` command line tool. |
| 132 | + |
| 133 | +```shell |
| 134 | +seid q evm payload abi.json increment |
| 135 | +``` |
| 136 | +Command produces result like `d09de08a`. This is the hex encoded EVM contract function input. |
| 137 | +For our example though we need bash64 encoded bytes. So let's decode the hex to bytes and then base64encode them: |
| 138 | +```shell |
| 139 | +echo -n 'd09de08a' | xxd -r -p | base64 |
| 140 | +``` |
| 141 | +This should produce result like `0J3gig==`, that is base64 encoded instruction to invoke EVM function. |
| 142 | + |
| 143 | +Let's call the `increment` function of the EVM contract from the CosmWasm contract. |
| 144 | +```shell |
| 145 | +seid tx wasm execute $COSMWASM_CONTRACT_ADDRESS '{"call_evm": {"value": "0", "to": "$0X_CONTRACT_ADDRESS", "data": "0J3gig==" }}' --from=admin --broadcast-mode=block --fees=200000usei --gas=3000000 |
| 146 | +``` |
| 147 | + |
| 148 | +So now the counter should be set to `2`. Let's verify with the `cast` command first: |
| 149 | +```shell |
| 150 | +cast call $0X_CONTRACT_ADDRESS "getCount()(uint256)" |
| 151 | +``` |
| 152 | +And now also with the `seid` command: |
| 153 | +```shell |
| 154 | +seid q evm payload out/abi.json getCount |
| 155 | +``` |
| 156 | +```shell |
| 157 | +echo -n 'a87d942c' | xxd -r -p | base64 |
| 158 | +``` |
| 159 | +```shell |
| 160 | +seid q wasm contract-state smart $COSMWASM_CONTRACT_ADDRESS '{"static_call": {"from": "$SEI_ACCOUNT_ADDRESS", "to": "$0X_CONTRACT_ADDRESS", "data": "qH2ULA==" }}' |
| 161 | +``` |
| 162 | +The above command should return output like this: |
| 163 | +```shell |
| 164 | +data: |
| 165 | + encoded_data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI= |
| 166 | +``` |
| 167 | +Let's decode it: |
| 168 | +```shell |
| 169 | +echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=' | base64 -D | od -t u1 -An -j 31 |
| 170 | +``` |
| 171 | +This should return `2` as well. |
0 commit comments