Skip to content

Commit afd20fd

Browse files
committed
Adding EVM example readme.
Tweaking the binding to address the recent change.
1 parent 29b37ae commit afd20fd

File tree

8 files changed

+182
-32
lines changed

8 files changed

+182
-32
lines changed

Diff for: contracts/sei-tester/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ cw2 = "0.13.2"
4747
schemars = "0.8.8"
4848
serde = { version = "1.0.137", default-features = false, features = ["derive"] }
4949
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
50-
base64 = { version = "0.13.0" }
50+
base64 = { version = "0.21.7" }
5151
thiserror = { version = "1.0.31" }
5252
protobuf = { version = "3.2.0", features = ["with-bytes"] }
5353

Diff for: contracts/sei-tester/README.md

+152-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,160 @@ sei-chain integration in your smart contracts.
1212
First we need deploy an EVM contract on the Sei chain. Let's use a simple counter contract and
1313
foundry tooling to deploy it.
1414

15-
Install the [foundry tooling](https://book.getfoundry.sh/) by following this [guide](https://book.getfoundry.sh/getting-started/installation.html).
15+
Install the [foundry tooling](https://book.getfoundry.sh/) by following this [installation guide](https://book.getfoundry.sh/getting-started/installation.html).
1616

17-
```shell
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:
1821

1922
```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 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+
}
2064
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.
89+
```shell
90+
[⠒] Compiling...
91+
No files changed, compilation skipped
92+
Deployer: 0xAddress
93+
Deployed to: 0xContractAddress
94+
Transaction hash: 0xTransactionHash
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+
Where `$0X_CONTRACT_ADDRESS` is the address of the deployed contract that you take from `Deployed to` row in the output
102+
of the previous command.
103+
The command should return `0` as the initial value of the counter.
104+
105+
Now we can use the `cast` command to call the `increment` function:
106+
```shell
107+
cast send $0X_CONTRACT_ADDRESS "increment()" --mnemonic $MNEMONIC
108+
```
109+
If command is successful, you will get the transaction hash and other info back.
110+
111+
Now let's call the `getCount` function again and this case it should return `1`.
112+
113+
If that works, we can now deploy the CosmWasm contract that will call the EVM contract.
114+
115+
Follow the instructions in [this README](../../README.md) to deploy the test contract.
116+
117+
Once the contract is deployed, we can query EVM contract from the CosmWasm contract.
118+
Please note the contract address of the CosmWasm contract or export it as `$COSMWASM_CONTRACT_ADDRESS`.
119+
Before we do that, we need to get the correct inputs.
120+
To generate inputs, we can use the `seid` command line tool.
121+
122+
First we need to get the ABI of the EVM contract. It was generated by the foundry tooling and is usually located in the
123+
`out` folder. E.g. at path `out/Counter.sol/Counter.json`.
124+
125+
For our example the ABI looks like this:
126+
127+
```json
128+
[{"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"}]
129+
```
130+
For convenience, we can save the ABI to a file, e.g. `abi.json`.
131+
132+
Now let's generate the inputs for the `seid` command line tool.
133+
134+
```shell
135+
seid q evm payload abi.json increment
136+
```
137+
That could produce result like `d09de08a`. This is hex encoded function input.
138+
For our example though we need bash64 encoded bytes. So let's convert it:
139+
```shell
140+
echo -n 'd09de08a' | xxd -r -p | base64
141+
```
142+
This should produce result like `0J3gig==`
143+
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+
This command will call the `increment` function of the EVM contract from the CosmWasm contract.
148+
So now the counter should be `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
21170
```
171+
This should return `2` as well.

Diff for: contracts/sei-tester/src/contract.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
use base64::{Engine as _, engine::{general_purpose}};
12
use cosmwasm_std::to_json_binary;
2-
// #[cfg(not(feature = "library"))]
3+
#[cfg(not(feature = "library"))]
34
use cosmwasm_std::{
45
coin, entry_point, Attribute, BankMsg, Binary, Coin, Decimal, Deps, DepsMut, Env, MessageInfo,
56
Order as IteratorOrder, Reply, Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128,
@@ -12,14 +13,7 @@ use crate::{
1213
types::{OrderData, PositionEffect},
1314
};
1415
use protobuf::Message;
15-
use sei_cosmwasm::{
16-
BulkOrderPlacementsResponse, Cancellation, DenomAuthorityMetadataResponse, DenomUnit,
17-
DenomsFromCreatorResponse, DepositInfo, DexTwapsResponse, EpochResponse, EvmAddressResponse,
18-
ExchangeRatesResponse, GetLatestPriceResponse, GetOrderByIdResponse, GetOrdersResponse,
19-
Metadata, MsgPlaceOrdersResponse, OracleTwapsResponse, Order, OrderSimulationResponse,
20-
OrderType, PositionDirection, SeiAddressResponse, SeiMsg, SeiQuerier, SeiQueryWrapper,
21-
SettlementEntry, SudoMsg,
22-
};
16+
use sei_cosmwasm::{BulkOrderPlacementsResponse, Cancellation, DenomAuthorityMetadataResponse, DenomUnit, DenomsFromCreatorResponse, DepositInfo, DexTwapsResponse, EpochResponse, EvmAddressResponse, ExchangeRatesResponse, GetLatestPriceResponse, GetOrderByIdResponse, GetOrdersResponse, Metadata, MsgPlaceOrdersResponse, OracleTwapsResponse, Order, OrderSimulationResponse, OrderType, PositionDirection, SeiAddressResponse, SeiMsg, SeiQuerier, SeiQueryWrapper, SettlementEntry, SudoMsg, StaticCallResponse};
2317

2418
const PLACE_ORDER_REPLY_ID: u64 = 1;
2519
// version info for migration info
@@ -338,7 +332,7 @@ pub fn process_bulk_order_placements(
338332
Ok(val) => val,
339333
Err(error) => panic!("Problem parsing response: {:?}", error),
340334
};
341-
let base64_json_str = base64::encode(serialized_json);
335+
let base64_json_str = general_purpose::STANDARD.encode(serialized_json);
342336
let binary = match Binary::from_base64(base64_json_str.as_ref()) {
343337
Ok(val) => val,
344338
Err(error) => panic!("Problem converting binary for order request: {:?}", error),
@@ -575,10 +569,10 @@ pub fn query_static_call(
575569
from: String,
576570
to: String,
577571
data: String,
578-
) -> StdResult<String> {
572+
) -> StdResult<StaticCallResponse> {
579573
let valid_from_addr = deps.api.addr_validate(&from)?;
580574
let querier = SeiQuerier::new(&deps.querier);
581-
let res: String = querier.static_call(valid_from_addr.to_string(), to, data)?;
575+
let res: StaticCallResponse = querier.static_call(valid_from_addr.to_string(), to, data)?;
582576

583577
Ok(res)
584578
}

Diff for: contracts/sei-tester/tests/sei_tester_integration_tests.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use base64::{Engine as _, engine::{general_purpose}};
12
use cosmwasm_std::{
23
coin, from_json,
34
testing::{MockApi, MockStorage},
@@ -10,7 +11,7 @@ use cw_multi_test::{
1011
StakeKeeper, WasmKeeper,
1112
};
1213

13-
use sei_cosmwasm::{Cancellation, DenomOracleExchangeRatePair, DexPair, DexTwap, DexTwapsResponse, EpochResponse, EvmAddressResponse, ExchangeRatesResponse, GetOrderByIdResponse, GetOrdersResponse, OracleExchangeRate, OracleTwapsResponse, Order, OrderSimulationResponse, OrderStatus, OrderType, PositionDirection, SeiAddressResponse, SeiMsg, SeiQuery, SeiQueryWrapper, SeiRoute};
14+
use sei_cosmwasm::{Cancellation, DenomOracleExchangeRatePair, DexPair, DexTwap, DexTwapsResponse, EpochResponse, EvmAddressResponse, ExchangeRatesResponse, GetOrderByIdResponse, GetOrdersResponse, OracleExchangeRate, OracleTwapsResponse, Order, OrderSimulationResponse, OrderStatus, OrderType, PositionDirection, SeiAddressResponse, SeiMsg, SeiQuery, SeiQueryWrapper, SeiRoute, StaticCallResponse};
1415
use sei_integration_tests::{
1516
helper::{get_balance, mock_app},
1617
module::{SeiModule, EVM_ADDRESS, SEI_ADDRESS},
@@ -935,19 +936,21 @@ fn test_static_call_query() {
935936
let mut app = mock_app(init_default_balances, vec![]);
936937
let sei_tester_addr = setup_test(&mut app);
937938

938-
let res: String = app
939+
let res: StaticCallResponse = app
939940
.wrap()
940941
.query_wasm_smart(
941942
sei_tester_addr.clone(),
942943
&QueryMsg::StaticCall {
943944
from: SEI_ADDRESS.to_string(),
944945
to: EVM_ADDRESS.to_string(),
945-
data: "qH2ULA==".to_string(),
946+
data: "".to_string(),
946947
},
947948
)
948949
.unwrap();
949950

950-
let expected_res = "static call response".to_string();
951+
let expected_res = StaticCallResponse {
952+
encoded_data: general_purpose::STANDARD.encode(b"static call response")
953+
};
951954
assert_eq!(res, expected_res);
952955

953956
}

Diff for: packages/sei-cosmwasm/src/querier.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use crate::query::{
77
Erc721NameSymbolResponse, Erc721OwnerResponse, Erc721UriResponse, ErcPayloadResponse,
88
EvmAddressResponse, ExchangeRatesResponse, GetLatestPriceResponse, GetOrderByIdResponse,
99
GetOrdersResponse, OracleTwapsResponse, OrderSimulationResponse, SeiAddressResponse, SeiQuery,
10-
SeiQueryWrapper,
10+
SeiQueryWrapper,StaticCallResponse
1111
};
1212
use crate::route::SeiRoute;
13-
use crate::Order;
13+
use crate::{Order};
1414

1515
/// This is a helper wrapper to easily use our custom queries
1616
pub struct SeiQuerier<'a> {
@@ -196,7 +196,7 @@ impl<'a> SeiQuerier<'a> {
196196
from: String,
197197
to: String,
198198
data: String,
199-
) -> StdResult<String> {
199+
) -> StdResult<StaticCallResponse> {
200200
let request = SeiQueryWrapper {
201201
route: SeiRoute::Evm,
202202
query_data: SeiQuery::StaticCall { from, to, data },

Diff for: packages/sei-cosmwasm/src/query.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ pub struct DenomsFromCreatorResponse {
272272
pub struct StaticCallResponse {
273273
/// The result of the static call to the EVM contract. It's represented as a base64 encoded
274274
/// string.
275-
pub data: String, // base64
275+
pub encoded_data: String, // base64
276276
}
277277

278278
/// `ErcPayloadResponse` is a struct that represents a response containing the encoded payload for

Diff for: packages/sei-integration-tests/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ cosmwasm-std = "1.0.0"
1414
cw20-base = "0.13.4"
1515
schemars = "0.8.8"
1616
serde = { version = "1.0.137", default-features = false, features = ["derive"] }
17-
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
17+
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
18+
[dependencies]
19+
base64 = "0.21.7"

Diff for: packages/sei-integration-tests/src/module.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,14 @@ use cosmwasm_std::{
55
};
66
use cw_multi_test::{AppResponse, BankSudo, CosmosRouter, Module, SudoMsg};
77
use schemars::JsonSchema;
8-
use sei_cosmwasm::{
9-
Cancellation, DenomOracleExchangeRatePair, DexPair, DexTwap, DexTwapsResponse, Epoch,
10-
EpochResponse, EvmAddressResponse, ExchangeRatesResponse, GetOrderByIdResponse,
11-
GetOrdersResponse, OracleTwap, OracleTwapsResponse, Order, OrderResponse,
12-
OrderSimulationResponse, OrderStatus, PositionDirection, SeiAddressResponse, SeiMsg, SeiQuery,
13-
SeiQueryWrapper, SudoMsg as SeiSudoMsg,
14-
};
8+
use sei_cosmwasm::{Cancellation, DenomOracleExchangeRatePair, DexPair, DexTwap, DexTwapsResponse, Epoch, EpochResponse, EvmAddressResponse, ExchangeRatesResponse, GetOrderByIdResponse, GetOrdersResponse, OracleTwap, OracleTwapsResponse, Order, OrderResponse, OrderSimulationResponse, OrderStatus, PositionDirection, SeiAddressResponse, SeiMsg, SeiQuery, SeiQueryWrapper, StaticCallResponse, SudoMsg as SeiSudoMsg};
159
use serde::de::DeserializeOwned;
1610
use std::{
1711
collections::HashMap,
1812
fmt::Debug,
1913
ops::{Add, Div, Mul, Sub},
2014
};
15+
use base64::{Engine as _, engine::{general_purpose}};
2116

2217
pub struct SeiModule {
2318
epoch: Epoch,
@@ -192,7 +187,7 @@ impl Module for SeiModule {
192187
);
193188
}
194189
SeiQuery::StaticCall { .. } => {
195-
Ok(to_json_binary("static call response")?)
190+
Ok(to_json_binary(&get_static_call_response())?)
196191
}
197192
SeiQuery::GetEvmAddress { sei_address } => {
198193
Ok(to_json_binary(&get_evm_address(sei_address))?)
@@ -658,6 +653,12 @@ fn get_epoch(epoch: Epoch) -> EpochResponse {
658653
EpochResponse { epoch: epoch }
659654
}
660655

656+
fn get_static_call_response() -> StaticCallResponse {
657+
StaticCallResponse {
658+
encoded_data: general_purpose::STANDARD.encode(b"static call response")
659+
}
660+
}
661+
661662
fn get_evm_address(sei_address: String) -> EvmAddressResponse {
662663
let (evm_address, associated) = match sei_address.as_str() {
663664
SEI_ADDRESS => (EVM_ADDRESS.to_string(), true),

0 commit comments

Comments
 (0)