Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding EVM example readme.
Browse files Browse the repository at this point in the history
Tweaking the binding to address the recent change.
dssei committed Apr 17, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 29b37ae commit afd20fd
Showing 8 changed files with 182 additions and 32 deletions.
2 changes: 1 addition & 1 deletion contracts/sei-tester/Cargo.toml
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ cw2 = "0.13.2"
schemars = "0.8.8"
serde = { version = "1.0.137", default-features = false, features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
base64 = { version = "0.13.0" }
base64 = { version = "0.21.7" }
thiserror = { version = "1.0.31" }
protobuf = { version = "3.2.0", features = ["with-bytes"] }

154 changes: 152 additions & 2 deletions contracts/sei-tester/README.md
Original file line number Diff line number Diff line change
@@ -12,10 +12,160 @@ sei-chain integration in your smart contracts.
First we need deploy an EVM contract on the Sei chain. Let's use a simple counter contract and
foundry tooling to deploy it.

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

```shell

Create a new project following the [Creating New Project Guide](https://book.getfoundry.sh/projects/creating-a-new-project).

Once project is created, tweak the contract code to the following, by adding a `getCounter` function:

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
function getCount() public view returns (uint256) {
return number;
}
}
```

And the test to the following:
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
counter.setNumber(0);
}
function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
}
function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
function test_GetCount() public {
uint256 initialCount = counter.getCount();
counter.increment();
assertEq(counter.getCount(), initialCount + 1);
}
}
```
Run the tests with the following command:
```shell
forge test
```
If tests pass, deploy the contract to the Sei chain with the following command:
```shell
forge create --rpc-url $SEI_NODE_URI --mnemonic $MNEMONIC src/Counter.sol:Counter
```
Where `$SEI_NODE_URI` is the URI of the Sei node and `$MNEMONIC` is the mnemonic of the account that will deploy the
contract.
If you run local Sei node the address will be `http://localhost:8545`.
If deployment is successful, you will get the EVM contract address.
```shell
[⠒] Compiling...
No files changed, compilation skipped
Deployer: 0xAddress
Deployed to: 0xContractAddress
Transaction hash: 0xTransactionHash

```
Let's use the `cast` command to query the contract:
```shell
cast call $0X_CONTRACT_ADDRESS "getCount()(uint256)"
```
Where `$0X_CONTRACT_ADDRESS` is the address of the deployed contract that you take from `Deployed to` row in the output
of the previous command.
The command should return `0` as the initial value of the counter.

Now we can use the `cast` command to call the `increment` function:
```shell
cast send $0X_CONTRACT_ADDRESS "increment()" --mnemonic $MNEMONIC
```
If command is successful, you will get the transaction hash and other info back.

Now let's call the `getCount` function again and this case it should return `1`.

If that works, we can now deploy the CosmWasm contract that will call the EVM contract.

Follow the instructions in [this README](../../README.md) to deploy the test contract.

Once the contract is deployed, we can query EVM contract from the CosmWasm contract.
Please note the contract address of the CosmWasm contract or export it as `$COSMWASM_CONTRACT_ADDRESS`.
Before we do that, we need to get the correct inputs.
To generate inputs, we can use the `seid` command line tool.

First we need to get the ABI of the EVM contract. It was generated by the foundry tooling and is usually located in the
`out` folder. E.g. at path `out/Counter.sol/Counter.json`.

For our example the ABI looks like this:

```json
[{"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"}]
```
For convenience, we can save the ABI to a file, e.g. `abi.json`.

Now let's generate the inputs for the `seid` command line tool.

```shell
seid q evm payload abi.json increment
```
That could produce result like `d09de08a`. This is hex encoded function input.
For our example though we need bash64 encoded bytes. So let's convert it:
```shell
echo -n 'd09de08a' | xxd -r -p | base64
```
This should produce result like `0J3gig==`

```shell
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
```
This command will call the `increment` function of the EVM contract from the CosmWasm contract.
So now the counter should be `2`. Let's verify with the `cast` command first:
```shell
cast call $0X_CONTRACT_ADDRESS "getCount()(uint256)"
```
And now also with the `seid` command:
```shell
seid q evm payload out/abi.json getCount
```
```shell
echo -n 'a87d942c' | xxd -r -p | base64
```
```shell
seid q wasm contract-state smart $COSMWASM_CONTRACT_ADDRESS '{"static_call": {"from": "$SEI_ACCOUNT_ADDRESS", "to": "$0X_CONTRACT_ADDRESS", "data": "qH2ULA==" }}'
```
The above command should return output like this:
```shell
data:
encoded_data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=
```
Let's decode it:
```shell
echo 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=' | base64 -D | od -t u1 -An -j 31
```
This should return `2` as well.
18 changes: 6 additions & 12 deletions contracts/sei-tester/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use base64::{Engine as _, engine::{general_purpose}};
use cosmwasm_std::to_json_binary;
// #[cfg(not(feature = "library"))]
#[cfg(not(feature = "library"))]
use cosmwasm_std::{
coin, entry_point, Attribute, BankMsg, Binary, Coin, Decimal, Deps, DepsMut, Env, MessageInfo,
Order as IteratorOrder, Reply, Response, StdError, StdResult, SubMsg, SubMsgResponse, Uint128,
@@ -12,14 +13,7 @@ use crate::{
types::{OrderData, PositionEffect},
};
use protobuf::Message;
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,
};
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};

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

Ok(res)
}
11 changes: 7 additions & 4 deletions contracts/sei-tester/tests/sei_tester_integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use base64::{Engine as _, engine::{general_purpose}};
use cosmwasm_std::{
coin, from_json,
testing::{MockApi, MockStorage},
@@ -10,7 +11,7 @@ use cw_multi_test::{
StakeKeeper, WasmKeeper,
};

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};
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};
use sei_integration_tests::{
helper::{get_balance, mock_app},
module::{SeiModule, EVM_ADDRESS, SEI_ADDRESS},
@@ -935,19 +936,21 @@ fn test_static_call_query() {
let mut app = mock_app(init_default_balances, vec![]);
let sei_tester_addr = setup_test(&mut app);

let res: String = app
let res: StaticCallResponse = app
.wrap()
.query_wasm_smart(
sei_tester_addr.clone(),
&QueryMsg::StaticCall {
from: SEI_ADDRESS.to_string(),
to: EVM_ADDRESS.to_string(),
data: "qH2ULA==".to_string(),
data: "".to_string(),
},
)
.unwrap();

let expected_res = "static call response".to_string();
let expected_res = StaticCallResponse {
encoded_data: general_purpose::STANDARD.encode(b"static call response")
};
assert_eq!(res, expected_res);

}
6 changes: 3 additions & 3 deletions packages/sei-cosmwasm/src/querier.rs
Original file line number Diff line number Diff line change
@@ -7,10 +7,10 @@ use crate::query::{
Erc721NameSymbolResponse, Erc721OwnerResponse, Erc721UriResponse, ErcPayloadResponse,
EvmAddressResponse, ExchangeRatesResponse, GetLatestPriceResponse, GetOrderByIdResponse,
GetOrdersResponse, OracleTwapsResponse, OrderSimulationResponse, SeiAddressResponse, SeiQuery,
SeiQueryWrapper,
SeiQueryWrapper,StaticCallResponse
};
use crate::route::SeiRoute;
use crate::Order;
use crate::{Order};

/// This is a helper wrapper to easily use our custom queries
pub struct SeiQuerier<'a> {
@@ -196,7 +196,7 @@ impl<'a> SeiQuerier<'a> {
from: String,
to: String,
data: String,
) -> StdResult<String> {
) -> StdResult<StaticCallResponse> {
let request = SeiQueryWrapper {
route: SeiRoute::Evm,
query_data: SeiQuery::StaticCall { from, to, data },
2 changes: 1 addition & 1 deletion packages/sei-cosmwasm/src/query.rs
Original file line number Diff line number Diff line change
@@ -272,7 +272,7 @@ pub struct DenomsFromCreatorResponse {
pub struct StaticCallResponse {
/// The result of the static call to the EVM contract. It's represented as a base64 encoded
/// string.
pub data: String, // base64
pub encoded_data: String, // base64
}

/// `ErcPayloadResponse` is a struct that represents a response containing the encoded payload for
4 changes: 3 additions & 1 deletion packages/sei-integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -14,4 +14,6 @@ cosmwasm-std = "1.0.0"
cw20-base = "0.13.4"
schemars = "0.8.8"
serde = { version = "1.0.137", default-features = false, features = ["derive"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
[dependencies]
base64 = "0.21.7"
17 changes: 9 additions & 8 deletions packages/sei-integration-tests/src/module.rs
Original file line number Diff line number Diff line change
@@ -5,19 +5,14 @@ use cosmwasm_std::{
};
use cw_multi_test::{AppResponse, BankSudo, CosmosRouter, Module, SudoMsg};
use schemars::JsonSchema;
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, SudoMsg as SeiSudoMsg,
};
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};
use serde::de::DeserializeOwned;
use std::{
collections::HashMap,
fmt::Debug,
ops::{Add, Div, Mul, Sub},
};
use base64::{Engine as _, engine::{general_purpose}};

pub struct SeiModule {
epoch: Epoch,
@@ -192,7 +187,7 @@ impl Module for SeiModule {
);
}
SeiQuery::StaticCall { .. } => {
Ok(to_json_binary("static call response")?)
Ok(to_json_binary(&get_static_call_response())?)
}
SeiQuery::GetEvmAddress { sei_address } => {
Ok(to_json_binary(&get_evm_address(sei_address))?)
@@ -658,6 +653,12 @@ fn get_epoch(epoch: Epoch) -> EpochResponse {
EpochResponse { epoch: epoch }
}

fn get_static_call_response() -> StaticCallResponse {
StaticCallResponse {
encoded_data: general_purpose::STANDARD.encode(b"static call response")
}
}

fn get_evm_address(sei_address: String) -> EvmAddressResponse {
let (evm_address, associated) = match sei_address.as_str() {
SEI_ADDRESS => (EVM_ADDRESS.to_string(), true),

0 comments on commit afd20fd

Please sign in to comment.