Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EVM] Merge balances on associate tx #1630

Merged
merged 6 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions contracts/test/AssociateTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const { fundAddress, fundSeiAddress, getSeiBalance, associateKey, importKey, waitForReceipt, bankSend, evmSend, getNativeAccount} = require("./lib");
const { expect } = require("chai");

describe("Associate Balances", function () {

const keys = {
"test1": {
seiAddress: 'sei1nzdg7e6rvkrmvp5zzmp5tupuj0088nqsa4mze4',
evmAddress: '0x90684e7F229f2d8E2336661f79caB693E4228Ff7'
},
"test2": {
seiAddress: 'sei1jqgph9jpdtvv64e3rzegxtssvgmh7lxnn8vmdq',
evmAddress: '0x28b2B0621f76A2D08A9e04acb7F445E61ba5b7E7'
},
"test3": {
seiAddress: 'sei1qkawqt7dw09rkvn53lm2deamtfcpuq9v0h6zur',
evmAddress: '0xCb2FB25A6a34Ca874171Ac0406d05A49BC45a1cF'
}
}

const addresses = {
seiAddress: 'sei1nzdg7e6rvkrmvp5zzmp5tupuj0088nqsa4mze4',
evmAddress: '0x90684e7F229f2d8E2336661f79caB693E4228Ff7'
}

function truncate(num, byThisManyDecimals) {
return parseFloat(`${num}`.slice(0, 12))
}

async function verifyAssociation(seiAddr, evmAddr, associateFunc) {
const beforeSei = BigInt(await getSeiBalance(seiAddr))
const beforeEvm = await ethers.provider.getBalance(evmAddr)
const gas = await associateFunc(seiAddr)
const afterSei = BigInt(await getSeiBalance(seiAddr))
const afterEvm = await ethers.provider.getBalance(evmAddr)

// console.log(`SEI Balance (before): ${beforeSei}`)
// console.log(`EVM Balance (before): ${beforeEvm}`)
// console.log(`SEI Balance (after): ${afterSei}`)
// console.log(`EVM Balance (after): ${afterEvm}`)

const multiplier = BigInt(1000000000000)
expect(afterEvm).to.equal((beforeSei * multiplier) + beforeEvm - (gas * multiplier))
expect(afterSei).to.equal(truncate(beforeSei - gas))
}

before(async function(){
await importKey("test1", "../contracts/test/test1.key")
await importKey("test2", "../contracts/test/test2.key")
await importKey("test3", "../contracts/test/test3.key")
})

it("should associate with sei transaction", async function(){
const addr = keys.test1
await fundSeiAddress(addr.seiAddress, "10000000000")
await fundAddress(addr.evmAddress, "200");

await verifyAssociation(addr.seiAddress, addr.evmAddress, async function(){
await bankSend(addr.seiAddress, "test1")
return BigInt(20000)
})
});

it("should associate with evm transaction", async function(){
const addr = keys.test2
await fundSeiAddress(addr.seiAddress, "10000000000")
await fundAddress(addr.evmAddress, "200");

await verifyAssociation(addr.seiAddress, addr.evmAddress, async function(){
const txHash = await evmSend(addr.evmAddress, "test2", "0")
const receipt = await waitForReceipt(txHash)
return BigInt(receipt.gasUsed * (receipt.gasPrice / BigInt(1000000000000)))
})
});

it("should associate with associate transaction", async function(){
const addr = keys.test3
await fundSeiAddress(addr.seiAddress, "10000000000")
await fundAddress(addr.evmAddress, "200");

await verifyAssociation(addr.seiAddress, addr.evmAddress, async function(){
await associateKey("test3")
return BigInt(0)
})
});

})
85 changes: 74 additions & 11 deletions contracts/test/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,72 @@ async function delay() {
await sleep(1000)
}

async function fundAddress(addr) {
return await execute(`seid tx evm send ${addr} 10000000000000000000 --from ${adminKeyName}`);
async function fundAddress(addr, amount="10000000000000000000") {
const result = await evmSend(addr, adminKeyName, amount)
await delay()
return result
}

async function getAdmin() {
await associateAdmin()
const seiAddress = await getAdminSeiAddress()
async function evmSend(addr, fromKey, amount="100000000000000000000000") {
const output = await execute(`seid tx evm send ${addr} ${amount} --from ${fromKey} -b block -y`);
return output.replace(/.*0x/, "0x").trim()
}

async function bankSend(toAddr, fromKey, amount="100000000000", denom="usei") {
const result = await execute(`seid tx bank send ${fromKey} ${toAddr} ${amount}${denom} -b block --fees 20000usei -y`);
await delay()
return result
}

async function fundSeiAddress(seiAddr, amount="100000000000", denom="usei") {
return await execute(`seid tx bank send ${adminKeyName} ${seiAddr} ${amount}${denom} -b block --fees 20000usei -y`);
}

async function getSeiBalance(seiAddr, denom="usei") {
const result = await execute(`seid query bank balances ${seiAddr} -o json`);
const balances = JSON.parse(result)
for(let b of balances.balances) {
if(b.denom === denom) {
return parseInt(b.amount, 10)
}
}
return 0
}

async function importKey(name, keyfile) {
try {
return await execute(`seid keys import ${name} ${keyfile}`, `printf "12345678\\n12345678\\n"`)
} catch(e) {
console.log("not importing key (skipping)")
console.log(e)
}
}

async function getNativeAccount(keyName) {
await associateKey(adminKeyName)
const seiAddress = await getKeySeiAddress(keyName)
await fundSeiAddress(seiAddress)
await delay()
const evmAddress = await getEvmAddress(seiAddress)
return {
seiAddress,
evmAddress
}
}

async function getAdminSeiAddress() {
return (await execute(`seid keys show ${adminKeyName} -a`)).trim()
async function getAdmin() {
await associateKey(adminKeyName)
return await getNativeAccount(adminKeyName)
}

async function getKeySeiAddress(name) {
return (await execute(`seid keys show ${name} -a`)).trim()
}

async function associateAdmin() {
async function associateKey(keyName) {
try {
return await execute(`seid tx evm associate-address --from ${adminKeyName}`)
await execute(`seid tx evm associate-address --from ${keyName} -b block`)
await delay()
}catch(e){
console.log("skipping associate")
}
Expand Down Expand Up @@ -174,14 +219,14 @@ async function executeWasm(contractAddress, msg, coins = "0usei") {
return JSON.parse(output);
}

async function execute(command) {
async function execute(command, interaction=`printf "12345678\\n"`){
return new Promise((resolve, reject) => {
// Check if the Docker container 'sei-node-0' is running
exec("docker ps --filter 'name=sei-node-0' --format '{{.Names}}'", (error, stdout, stderr) => {
if (stdout.includes('sei-node-0')) {
// The container is running, modify the command to execute inside Docker
command = command.replace(/\.\.\//g, "/sei-protocol/sei-chain/");
const dockerCommand = `docker exec sei-node-0 /bin/bash -c 'export PATH=$PATH:/root/go/bin:/root/.foundry/bin && printf "12345678\\n" | ${command}'`;
const dockerCommand = `docker exec sei-node-0 /bin/bash -c 'export PATH=$PATH:/root/go/bin:/root/.foundry/bin && ${interaction} | ${command}'`;
execCommand(dockerCommand, resolve, reject);
} else {
// The container is not running, execute command normally
Expand All @@ -205,8 +250,19 @@ function execCommand(command, resolve, reject) {
});
}

async function waitForReceipt(txHash) {
let receipt = await ethers.provider.getTransactionReceipt(txHash)
while(!receipt) {
await delay()
receipt = await ethers.provider.getTransactionReceipt(txHash)
}
return receipt
}

module.exports = {
fundAddress,
fundSeiAddress,
getSeiBalance,
storeWasm,
deployWasm,
instantiateWasm,
Expand All @@ -220,4 +276,11 @@ module.exports = {
deployEvmContract,
deployErc20PointerForCw20,
deployErc721PointerForCw721,
importKey,
getNativeAccount,
associateKey,
delay,
bankSend,
evmSend,
waitForReceipt,
};
9 changes: 9 additions & 0 deletions contracts/test/test1.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN TENDERMINT PRIVATE KEY-----
kdf: bcrypt
salt: 377151BDAA4DCDD8761F9489519D3E90
type: secp256k1

aMtUz1ow5FBmictYj67Wm5qJksJsiF0O86PrjOHnoINVBn0aCzYNxdkvNg0PP0Qb
jHfcKaDPgk3PaeW16YqNJwveVX71A7lwpUmfT44=
=46/d
-----END TENDERMINT PRIVATE KEY-----
9 changes: 9 additions & 0 deletions contracts/test/test2.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN TENDERMINT PRIVATE KEY-----
kdf: bcrypt
salt: 72E32FD4A4089E256F1EFCCBBA576592
type: secp256k1

MZ77pmO6izHdpkKVWYhGLGjZUAhf//WGURYVqUqhjJbCGtAXEkFKrjEAEpO3pyn1
tYe8Hlry3BiqLoLY+j7CwE7W6g3WshJouXCrUiY=
=VWqu
-----END TENDERMINT PRIVATE KEY-----
9 changes: 9 additions & 0 deletions contracts/test/test3.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN TENDERMINT PRIVATE KEY-----
kdf: bcrypt
salt: CDAEDE2709FFE1049C3E217BCD179BB2
type: secp256k1

l8hOkMcbc9ugxiUmS3k1wdziq6N+qhas25H0fjWD9tDK/BPn0QkY3pIz39TZJNuf
/l9ImHhYxccWPoyTybKmXEjMaKLpFRZLdu+t844=
=lvXk
-----END TENDERMINT PRIVATE KEY-----
1 change: 1 addition & 0 deletions integration_test/evm_module/scripts/evm_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ cd contracts
npm ci
npx hardhat test --network seilocal test/EVMCompatabilityTest.js
npx hardhat test --network seilocal test/EVMPrecompileTest.js
npx hardhat test --network seilocal test/AssociateTest.js
38 changes: 23 additions & 15 deletions x/evm/ante/preprocess.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,7 @@ func (p *EVMPreprocessDecorator) AssociateAddresses(ctx sdk.Context, seiAddr sdk
}
p.accountKeeper.SetAccount(ctx, acc)
}
castAddr := sdk.AccAddress(evmAddr[:])
castAddrBalances := p.evmKeeper.BankKeeper().GetAllBalances(ctx, castAddr)
if !castAddrBalances.IsZero() {
if err := p.evmKeeper.BankKeeper().SendCoins(ctx, castAddr, seiAddr, castAddrBalances); err != nil {
return err
}
}
castAddrWei := p.evmKeeper.BankKeeper().GetWeiBalance(ctx, castAddr)
if !castAddrWei.IsZero() {
if err := p.evmKeeper.BankKeeper().SendCoinsAndWei(ctx, castAddr, seiAddr, sdk.ZeroInt(), castAddrWei); err != nil {
return err
}
}
p.evmKeeper.AccountKeeper().RemoveAccount(ctx, authtypes.NewBaseAccountWithAddress(castAddr))
return nil
return migrateBalance(ctx, p.evmKeeper, evmAddr, seiAddr)
}

// stateless
Expand Down Expand Up @@ -340,6 +326,24 @@ func NewEVMAddressDecorator(evmKeeper *evmkeeper.Keeper, accountKeeper *accountk
return &EVMAddressDecorator{evmKeeper: evmKeeper, accountKeeper: accountKeeper}
}

func migrateBalance(ctx sdk.Context, evmKeeper *evmkeeper.Keeper, evmAddr common.Address, seiAddr sdk.AccAddress) error {
castAddr := sdk.AccAddress(evmAddr[:])
castAddrBalances := evmKeeper.BankKeeper().GetAllBalances(ctx, castAddr)
if !castAddrBalances.IsZero() {
if err := evmKeeper.BankKeeper().SendCoins(ctx, castAddr, seiAddr, castAddrBalances); err != nil {
return err
}
}
castAddrWei := evmKeeper.BankKeeper().GetWeiBalance(ctx, castAddr)
if !castAddrWei.IsZero() {
if err := evmKeeper.BankKeeper().SendCoinsAndWei(ctx, castAddr, seiAddr, sdk.ZeroInt(), castAddrWei); err != nil {
return err
}
}
evmKeeper.AccountKeeper().RemoveAccount(ctx, authtypes.NewBaseAccountWithAddress(castAddr))
return nil
}

//nolint:revive
func (p *EVMAddressDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
sigTx, ok := tx.(authsigning.SigVerifiableTx)
Expand Down Expand Up @@ -367,6 +371,10 @@ func (p *EVMAddressDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo
continue
}
p.evmKeeper.SetAddressMapping(ctx, signer, evmAddr)
if err := migrateBalance(ctx, p.evmKeeper, evmAddr, signer); err != nil {
ctx.Logger().Error(fmt.Sprintf("failed to migrate EVM address balance (%s) %s", evmAddr.Hex(), err))
return ctx, err
}
}
return next(ctx, tx, simulate)
}
Loading