Sei EVM Smart Contract Development with Foundry
Since Sei is an EVM compatible chain, existing EVM tooling like foundry forge or other could be re-used.
In this example we will be using foundry tooling .
Install the foundry tooling by following this Installation guide .
Create a new project following the Creating New Project Guide .
Also make sure you have a wallet on Sei network.
Once project is created, tweak the contract code to the following, by adding a getCounter
function:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;
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 code to the following:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;
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:
$ forge test
If tests pass, deploy the contract to the Sei chain with the following command:
$ forge create --rpc-url $SEI_NODE_URI --mnemonic $MNEMONIC src/Counter.sol:Counter --broadcast
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
, otherwise you could grab a evm_rpc
url from the registry . If deployment is successful, you will get the EVM contract address in the output.
[⠒] Compiling...
No files changed, compilation skipped
Deployer: $0X_DEPLOYER_ADDRESS
Deployed to: $0X_CONTRACT_ADDRESS
Transaction hash: $0X_TX_HASH
Let’s use the cast
command to query the contract:
$ cast call $0X_CONTRACT_ADDRESS "getCount()(uint256)" --rpc-url $SEI_NODE_URI
The command should return 0
as the initial value of the counter.
Now we can use the cast
command to call the increment
function:
$ cast send $0X_CONTRACT_ADDRESS "increment()" --mnemonic $MNEMONIC --rpc-url $SEI_NODE_URI
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
.
$ cast call $0X_CONTRACT_ADDRESS "getCount()(uint256)" --rpc-url $SEI_NODE_URI
Foundry generates the ABI for the contract in the
out
folder. You can use this ABI to interact with the contract from other tools likeethers.js
orweb3.js
.
Calling contract using ethers.js
To call or query the contract from a frontend or a NodeJS script, you could use ethers.js
like:
import { ethers } from 'ethers';
const privateKey = YOUR_PRIVATE_KEY;
const evmRpcEndpoint = YOUR_EVM_RPC_ENDPOINT;
const provider = new ethers.JsonRpcProvider(evmRpcEndpoint);
const signer = new ethers.Wallet(privateKey, provider);
if (!signer) {
console.log('No signer found');
return;
}
const abi = [
{
"type": "function",
"name": "setNumber",
"inputs": [
{
"name": "newNumber",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "getCount",
"inputs": [],
"outputs": [
{
"name": "",
"type": "int256",
"internalType": "int256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "increment",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
}
];
// Define the address of the deployed contract
const contractAddress = `0X_CONTRACT_ADDRESS`;
// Create a new instance of the ethers.js Contract object
const contract = new ethers.Contract(contractAddress, abi, signer);
// Call the contract's functions
async function getCount() {
const count = await contract.getCount();
console.log(count.toString());
}
async function increment() {
const txResponse = await contract.increment();
const mintedTx = await txResponse.wait();
console.log(mintedTx);
}
await increment();
await getCount();