Skip to content
Open
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
14 changes: 11 additions & 3 deletions contracts/test/EVMPrecompileTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const fs = require('fs');
const path = require('path');

const { expectRevert } = require('@openzeppelin/test-helpers');
const { setupSigners, getAdmin, deployWasm, storeWasm, execute, isDocker, ABI, createTokenFactoryTokenAndMint, getSeiBalance, rawHttpDebugTraceWithCallTracer} = require("./lib");
const { setupSigners, getAdmin, deployWasm, storeWasm, execute, isDocker, ABI, createTokenFactoryTokenAndMint, getSeiBalance, rawHttpDebugTraceWithCallTracer, proposeParamChange} = require("./lib");

function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
Expand Down Expand Up @@ -125,8 +125,16 @@ describe("EVM Precompile Tester", function () {
let govProposal;

before(async function () {
const govProposalResponse = JSON.parse(await execute(`seid tx gov submit-proposal param-change ../contracts/test/param_change_proposal.json --from admin --fees 20000usei -b block -y -o json`))
govProposal = govProposalResponse.logs[0].events[3].attributes[1].value;
const proposalSpec = require('./param_change_proposal.json');
govProposal = await proposeParamChange(
proposalSpec.title,
proposalSpec.description,
proposalSpec.changes,
"200000000usei",
"20000usei",
"admin",
proposalSpec.is_expedited,
);

const signer = accounts[0].signer
const contractABIPath = '../../precompiles/gov/abi.json';
Expand Down
102 changes: 75 additions & 27 deletions contracts/test/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ async function delay() {
await sleep(1000)
}

// Default 2 because the very next block after submit can be empty
// (tx still in mempool, lands one block later).
async function waitForBlocks(blocks=2, timeoutMs=15000) {
const start = await ethers.provider.getBlockNumber()
const deadline = Date.now() + timeoutMs
while (Date.now() < deadline) {
const cur = await ethers.provider.getBlockNumber()
if (cur >= start + blocks) return
await sleep(50)
}
throw new Error(`block didn't advance by ${blocks} in ${timeoutMs}ms (start=${start})`)
}

async function getCosmosTx(provider, evmTxHash) {
return await provider.send("sei_getCosmosTx", [evmTxHash])
}
Expand All @@ -91,24 +104,25 @@ async function getEvmTx(provider, cosmosTxHash) {
}

async function fundAddress(addr, amount="1000000000000000000000") {
const result = await evmSend(addr, adminKeyName, amount)
await delay()
return result
return await evmSend(addr, adminKeyName, amount)
}

async function evmSend(addr, fromKey, amount="10000000000000000000000000") {
const output = await execute(`seid tx evm send ${addr} ${amount} --from ${fromKey} -b block -y`);
const output = await execute(`seid tx evm send ${addr} ${amount} --from ${fromKey} -b sync -y`);
await waitForBlocks()
Comment thread
cursor[bot] marked this conversation as resolved.
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()
const result = await execute(`seid tx bank send ${fromKey} ${toAddr} ${amount}${denom} -b sync --fees 20000usei -y`);
await waitForBlocks()
return result
}

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

async function getSeiBalance(seiAddr, denom="usei") {
Expand Down Expand Up @@ -141,7 +155,6 @@ async function getNativeAccount(keyName) {
await associateKey(adminKeyName)
const seiAddress = await getKeySeiAddress(keyName)
await fundSeiAddress(seiAddress)
await delay()
const evmAddress = await getEvmAddress(seiAddress)
return {
seiAddress,
Expand All @@ -160,8 +173,8 @@ async function getKeySeiAddress(name) {

async function associateKey(keyName) {
try {
await execute(`seid tx evm associate-address --from ${keyName} -b block`)
await delay()
await execute(`seid tx evm associate-address --from ${keyName} -b sync`)
await waitForBlocks()
}catch(e){
console.log("skipping associate")
}
Expand Down Expand Up @@ -243,15 +256,21 @@ async function rawHttpDebugTraceWithCallTracer(txHash) {
}

async function createTokenFactoryTokenAndMint(name, amount, recipient, from=adminKeyName) {
const command = `seid tx tokenfactory create-denom ${name} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`
const output = await execute(command);
const response = JSON.parse(output)
const token_denom = getEventAttribute(response, "create_denom", "new_token_denom")
const mint_command = `seid tx tokenfactory mint ${amount}${token_denom} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`
await execute(mint_command);

const send_command = `seid tx bank send ${from} ${recipient} ${amount}${token_denom} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`
await execute(send_command);
const command = `seid tx tokenfactory create-denom ${name} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`
const response = JSON.parse(await execute(command))
if (response.code !== 0) throw new Error(`create-denom rejected: ${response.raw_log}`)
// Tokenfactory denom is deterministic: factory/<creator-bech32>/<subdenom>.
const token_denom = `factory/${await getKeySeiAddress(from)}/${name}`
await waitForBlocks()
const mint_command = `seid tx tokenfactory mint ${amount}${token_denom} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`
const mintResp = JSON.parse(await execute(mint_command))
if (mintResp.code !== 0) throw new Error(`mint rejected: ${mintResp.raw_log}`)
await waitForBlocks()

const send_command = `seid tx bank send ${from} ${recipient} ${amount}${token_denom} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`
const sendResp = JSON.parse(await execute(send_command))
if (sendResp.code !== 0) throw new Error(`bank send rejected: ${sendResp.raw_log}`)
await waitForBlocks()
return token_denom
}

Expand Down Expand Up @@ -426,19 +445,50 @@ async function proposeParamChange(title, description, changes, deposit="20000000
};
const proposalJson = JSON.stringify(proposal);
const tempFile = `/tmp/param_change_${Date.now()}.json`;

// Use base64 encoding to avoid quote escaping issues in Docker
const base64Json = Buffer.from(proposalJson).toString('base64');
await execute(`echo ${base64Json} | base64 -d > ${tempFile}`);

const command = `seid tx gov submit-proposal param-change ${tempFile} --from ${from} --fees ${fees} -y -o json --broadcast-mode=block`;

// Identify the new proposal by diffing gov state before vs after submit.
let maxIdBefore = await maxProposalId();

const command = `seid tx gov submit-proposal param-change ${tempFile} --from ${from} --fees ${fees} -y -o json -b sync`;
const output = await execute(command);
await execute(`rm ${tempFile}`);
const response = JSON.parse(output);
if (response.code !== 0) {
throw new Error(`Failed to submit proposal: ${response.raw_log}`);
}
return getEventAttribute(response, "submit_proposal", "proposal_id");

const deadline = Date.now() + 30000;
while (Date.now() < deadline) {
const cur = await maxProposalId();
for (let id = maxIdBefore + 1; id <= cur; id++) {
const detail = JSON.parse(await execute(`seid q gov proposal ${id} -o json`));
const observedTitle = detail.content?.title ?? detail.title;
if (observedTitle === title) return String(id);
}
maxIdBefore = cur;
await sleep(250);
}
Comment thread
cursor[bot] marked this conversation as resolved.
throw new Error(`proposal submitted (tx ${response.txhash}) but did not appear in gov state within 30s`);
}

// Returns the highest existing proposal id, or 0 if there are no proposals.
async function maxProposalId() {
let out;
try {
// seid exits non-zero ("Error: no proposals found") on an empty
// gov set; the try/catch treats that as id=0.
out = await execute(`seid q gov proposals --reverse --limit 1 -o json 2>/dev/null`);
} catch (e) {
return 0;
}
if (!out || !out.trim()) return 0;
const proposals = JSON.parse(out).proposals || [];
if (proposals.length === 0) return 0;
return Number(proposals[0].proposal_id || proposals[0].id);
}

async function proposeDisableWasm(title="Disable WASM", description="Disable cosmwasm store code and instantiate operations", deposit="200000000usei", fees="200000usei", from=adminKeyName) {
Expand Down Expand Up @@ -522,9 +572,9 @@ async function ensureWasmDisabled(from=adminKeyName) {

async function passProposal(proposalId, desposit="200000000usei", fees="200000usei", from=adminKeyName) {
if(await isDocker()) {
await executeOnAllNodes(`seid tx gov vote ${proposalId} yes --from node_admin -b block -y --fees ${fees}`)
await executeOnAllNodes(`seid tx gov vote ${proposalId} yes --from node_admin -b sync -y --fees ${fees}`)
} else {
await execute(`seid tx gov vote ${proposalId} yes --from ${from} -b block -y --fees ${fees}`)
await execute(`seid tx gov vote ${proposalId} yes --from ${from} -b sync -y --fees ${fees}`)
}
// Poll for proposal status with shorter delay for faster tests
for(let i=0; i<200; i++) {
Expand Down Expand Up @@ -604,10 +654,8 @@ async function setupSigners(signers) {
let seiAddress = await associateSigner(signer);
if (seiAddress) {
await fundSeiAddress(seiAddress);
await delay()
} else {
await fundAddress(evmAddress);
await delay()
const resp = await signer.sendTransaction({
to: evmAddress,
value: 0
Expand Down
Loading