Skip to content
Draft
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
107 changes: 79 additions & 28 deletions contracts/test/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,43 @@ async function fundAddress(addr, amount="1000000000000000000000") {
return result
}

// submitAndWaitForTx submits a tx (the command must already use -b sync /
// --broadcast-mode sync) and polls `seid query tx <hash>` until the tx is
// included in a block, then returns the post-execution response with
// `code`, `events`, `logs`, etc. populated.
//
// Use this in helpers that need to read post-execution fields and previously
// relied on -b block. Under Autobahn -b block hangs and times out, so the
// "submit and forget" -b sync pattern is the only viable broadcast mode;
// this helper recovers the post-execution data the previous -b block
// callers depended on.
//
// On CheckTx rejection (mempool admission failure), returns the broadcast
// response immediately (its `code` is non-zero and callers should detect
// the failure at that point).
async function submitAndWaitForTx(command, opts = {}) {
const maxAttempts = opts.maxAttempts || 30
const pollIntervalMs = opts.pollIntervalMs || 500

const submitOutput = await execute(command)
const submitResponse = JSON.parse(submitOutput)
if (submitResponse.code !== 0) {
return submitResponse
}
const txHash = submitResponse.txhash

for (let i = 0; i < maxAttempts; i++) {
await sleep(pollIntervalMs)
try {
const queryOut = await execute(`seid query tx ${txHash} -o json`)
return JSON.parse(queryOut)
} catch (e) {
// tx not yet included; continue polling
}
}
throw new Error(`tx ${txHash} not included after ${maxAttempts * pollIntervalMs}ms`)
}

async function evmSend(addr, fromKey, amount="10000000000000000000000000") {
const output = await execute(`seid tx evm send ${addr} ${amount} --from ${fromKey} -b block -y`);
return output.replace(/.*0x/, "0x").trim()
Expand Down Expand Up @@ -260,9 +297,11 @@ async function getPointerForNative(name) {
}

async function storeWasm(path, from=adminKeyName) {
const command = `seid tx wasm store ${path} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`
const output = await execute(command);
const response = JSON.parse(output)
// -b sync + submitAndWaitForTx: -b block hangs under Autobahn (no
// CometBFT consensus to wait on). The poll-for-tx-by-hash pattern
// recovers the post-execution `events` we need to extract the code_id.
const command = `seid tx wasm store ${path} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`
const response = await submitAndWaitForTx(command)
if (response.code !== 0) {
throw new Error(`storeWasm failed: ${response.raw_log}`)
}
Expand All @@ -288,7 +327,7 @@ async function getPointerForCw1155(cw1155Address) {
}

async function deployErc20PointerForCw20(provider, cw20Address, attempts=10, from=adminKeyName, evmRpc="") {
let command = `seid tx evm register-evm-pointer CW20 ${cw20Address} --from=${from} -b block`
let command = `seid tx evm register-evm-pointer CW20 ${cw20Address} --from=${from} -b sync`
if (evmRpc) {
command = command + ` --evm-rpc=${evmRpc}`
}
Expand All @@ -309,7 +348,7 @@ async function deployErc20PointerForCw20(provider, cw20Address, attempts=10, fro
}

async function deployErc20PointerNative(provider, name, from=adminKeyName, evmRpc="") {
let command = `seid tx evm call-precompile pointer addNativePointer ${name} --from=${from} -b block`
let command = `seid tx evm call-precompile pointer addNativePointer ${name} --from=${from} -b sync`
if (evmRpc) {
command = command + ` --evm-rpc=${evmRpc}`
}
Expand All @@ -328,7 +367,7 @@ async function deployErc20PointerNative(provider, name, from=adminKeyName, evmRp
}

async function deployErc721PointerForCw721(provider, cw721Address, from=adminKeyName, evmRpc="") {
let command = `seid tx evm register-evm-pointer CW721 ${cw721Address} --from=${from} -b block`
let command = `seid tx evm register-evm-pointer CW721 ${cw721Address} --from=${from} -b sync`
if (evmRpc) {
command = command + ` --evm-rpc=${evmRpc}`
}
Expand All @@ -349,7 +388,7 @@ async function deployErc721PointerForCw721(provider, cw721Address, from=adminKey
}

async function deployErc1155PointerForCw1155(provider, cw1155Address, from=adminKeyName, evmRpc="") {
let command = `seid tx evm register-evm-pointer CW1155 ${cw1155Address} --from=${from} -b block`
let command = `seid tx evm register-evm-pointer CW1155 ${cw1155Address} --from=${from} -b sync`
if (evmRpc) {
command = command + ` --evm-rpc=${evmRpc}`
}
Expand All @@ -375,20 +414,23 @@ async function deployWasm(path, adminAddr, label, args = {}, from=adminKeyName)
}

async function instantiateWasm(codeId, adminAddr, label, args = {}, from=adminKeyName) {
// -b sync + submitAndWaitForTx for Autobahn compatibility; we need
// post-execution `events` to read the instantiated contract address.
const jsonString = JSON.stringify(args).replace(/"/g, '\\"');
const command = `seid tx wasm instantiate ${codeId} "${jsonString}" --label ${label} --admin ${adminAddr} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`;
const output = await execute(command);
const response = JSON.parse(output);
const command = `seid tx wasm instantiate ${codeId} "${jsonString}" --label ${label} --admin ${adminAddr} --from ${from} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`;
const response = await submitAndWaitForTx(command);
if (response.code !== 0) {
throw new Error(`instantiateWasm failed: ${response.raw_log}`)
}
return getEventAttribute(response, "instantiate", "_contract_address");
}

async function proposeCW20toERC20Upgrade(erc20Address, cw20Address, title="erc20-pointer", version=99, description="erc20 pointer",fees="200000usei", from=adminKeyName) {
const command = `seid tx evm add-cw-erc20-pointer "${title}" "${description}" ${erc20Address} ${version} 200000000usei ${cw20Address} --from ${from} --fees ${fees} -y -o json --broadcast-mode=block`
const output = await execute(command);
const proposalId = getEventAttribute(JSON.parse(output), "submit_proposal", "proposal_id")
// -b sync + submitAndWaitForTx for Autobahn compatibility; we need
// the post-execution `events` to read the submitted proposal_id.
const command = `seid tx evm add-cw-erc20-pointer "${title}" "${description}" ${erc20Address} ${version} 200000000usei ${cw20Address} --from ${from} --fees ${fees} -y -o json --broadcast-mode=sync`
const response = await submitAndWaitForTx(command)
const proposalId = getEventAttribute(response, "submit_proposal", "proposal_id")
return await passProposal(proposalId)
}

Expand Down Expand Up @@ -518,29 +560,32 @@ async function passProposal(proposalId, desposit="200000000usei", fees="200000u
}

async function registerPointerForERC20(erc20Address, fees="20000usei", from=adminKeyName) {
const command = `seid tx evm register-cw-pointer ERC20 ${erc20Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json`
const output = await execute(command);
const response = JSON.parse(output)
// -b sync + submitAndWaitForTx for Autobahn compatibility; we need the
// post-execution `events` to read the registered pointer_address.
const command = `seid tx evm register-cw-pointer ERC20 ${erc20Address} --from ${from} --fees ${fees} --broadcast-mode sync -y -o json`
const response = await submitAndWaitForTx(command)
if(response.code !== 0) {
throw new Error("contract deployment failed")
}
return getEventAttribute(response, "pointer_registered", "pointer_address")
}

async function registerPointerForERC721(erc721Address, fees="20000usei", from=adminKeyName) {
const command = `seid tx evm register-cw-pointer ERC721 ${erc721Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json`
const output = await execute(command);
const response = JSON.parse(output)
// -b sync + submitAndWaitForTx for Autobahn compatibility; we need the
// post-execution `events` to read the registered pointer_address.
const command = `seid tx evm register-cw-pointer ERC721 ${erc721Address} --from ${from} --fees ${fees} --broadcast-mode sync -y -o json`
const response = await submitAndWaitForTx(command)
if(response.code !== 0) {
throw new Error("contract deployment failed")
}
return getEventAttribute(response, "pointer_registered", "pointer_address")
}

async function registerPointerForERC1155(erc1155Address, fees="200000usei", from=adminKeyName) {
const command = `seid tx evm register-cw-pointer ERC1155 ${erc1155Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json`
const output = await execute(command);
const response = JSON.parse(output)
// -b sync + submitAndWaitForTx for Autobahn compatibility; we need the
// post-execution `events` to read the registered pointer_address.
const command = `seid tx evm register-cw-pointer ERC1155 ${erc1155Address} --from ${from} --fees ${fees} --broadcast-mode sync -y -o json`
const response = await submitAndWaitForTx(command)
if(response.code !== 0) {
throw new Error("contract deployment failed")
}
Expand Down Expand Up @@ -602,16 +647,22 @@ async function queryWasm(contractAddress, operation, args={}){
}

async function executeWasm(contractAddress, msg, coins = "0usei") {
// -b sync + submitAndWaitForTx: callers (e.g. CW1155toERC1155PointerTest's
// "should not transfer an NFT if not owned") read the post-execution
// res.code to assert that DeliverTx failed. Under naked -b sync we'd
// get the CheckTx code instead (always 0 for wellformed txs). The
// poll-for-tx-by-hash recovers the DeliverTx code so those assertions
// remain meaningful under Autobahn.
const jsonString = JSON.stringify(msg).replace(/"/g, '\\"'); // Properly escape JSON string
const command = `seid tx wasm execute ${contractAddress} "${jsonString}" --amount ${coins} --from ${adminKeyName} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`;
const output = await execute(command);
return JSON.parse(output);
const command = `seid tx wasm execute ${contractAddress} "${jsonString}" --amount ${coins} --from ${adminKeyName} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`;
return await submitAndWaitForTx(command);
}

async function associateWasm(contractAddress) {
const command = `seid tx evm associate-contract-address ${contractAddress} --from ${adminKeyName} --gas=5000000 --fees=1000000usei -y --broadcast-mode block -o json`;
const output = await execute(command);
return JSON.parse(output);
// -b sync + submitAndWaitForTx for Autobahn compatibility; callers read
// the post-execution code/events.
const command = `seid tx evm associate-contract-address ${contractAddress} --from ${adminKeyName} --gas=5000000 --fees=1000000usei -y --broadcast-mode sync -o json`;
return await submitAndWaitForTx(command);
}

async function printClaimMsg(sender, claimer) {
Expand Down
65 changes: 65 additions & 0 deletions integration_test/contracts/_tx_helpers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash
# Shared tx submission helpers for integration_test/contracts/*.sh.
#
# Source this file at the top of a deploy script:
# source $(dirname "$0")/_tx_helpers.sh
#
# Background: deploy scripts in this directory historically used
# `--broadcast-mode=block` to wait for the tx to be included in a block
# before reading the post-execution `code_id` / `events` / `logs` from
# the response. Under Autobahn, --broadcast-mode=block hangs and times
# out at 60s (CometBFT consensus is disabled, so the chain has no
# "block-acceptance" round-trip to wait on).
#
# submit_and_wait_for_tx implements the equivalent semantics on top of
# --broadcast-mode=sync: it reads the broadcast response from stdin
# (which contains the txhash + a CheckTx code), then polls
# `$seidbin query tx <hash>` until the tx is included in a block, and
# prints the included tx response on stdout. The shape of the output
# is the same as `--broadcast-mode=block` would have produced, so
# callers don't need to change how they parse it.
#
# Usage:
# tx_response=$(printf "12345678\n" | $seidbin tx wasm store ... \
# --broadcast-mode=sync --output=json \
# | submit_and_wait_for_tx "$seidbin")
#
# Optional env vars:
# SUBMIT_MAX_ATTEMPTS (default 30) poll attempts before giving up
# SUBMIT_POLL_INTERVAL_SEC (default 1) seconds between attempts
submit_and_wait_for_tx() {
local seidbin="${1:-$HOME/go/bin/seid}"
local max_attempts="${SUBMIT_MAX_ATTEMPTS:-30}"
local poll_interval="${SUBMIT_POLL_INTERVAL_SEC:-1}"

local submit_output
submit_output=$(cat)

# CheckTx rejection — return the broadcast response unchanged so the
# caller can read .code / .raw_log to detect mempool failures.
local check_code
check_code=$(echo "$submit_output" | jq -r '.code // 0')
if [ "$check_code" != "0" ]; then
echo "$submit_output"
return 0
fi

local txhash
txhash=$(echo "$submit_output" | jq -r '.txhash // empty')
if [ -z "$txhash" ]; then
echo "$submit_output"
return 1
fi

local i
for ((i=0; i<max_attempts; i++)); do
sleep "$poll_interval"
local tx_response
if tx_response=$($seidbin query tx "$txhash" --output json 2>/dev/null); then
echo "$tx_response"
return 0
fi
done
echo "ERROR: tx $txhash not included after $((max_attempts * poll_interval))s" >&2
return 1
}
8 changes: 5 additions & 3 deletions integration_test/contracts/create_tokenfactory_denoms.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

source "$(dirname "$0")/_tx_helpers.sh"

seidbin=$(which ~/go/bin/seid | tr -d '"')
keyname=$(printf "12345678\n" | $seidbin keys list --output json | jq ".[0].name" | tr -d '"')
keyaddress=$(printf "12345678\n" | $seidbin keys list --output json | jq ".[0].address" | tr -d '"')
Expand All @@ -17,7 +19,7 @@ echo "$keyaddress" > $seihome/integration_test/contracts/tfk_creator_id.txt
for i in {1..10}
do
echo "Creating first set of tokenfactory denoms #$i..."
create_denom_result=$(printf "12345678\n" | $seidbin tx tokenfactory create-denom "$i" -y --from="$keyname" --chain-id="$chainid" --gas=500000 --fees=100000usei --broadcast-mode=block --output=json)
create_denom_result=$(printf "12345678\n" | $seidbin tx tokenfactory create-denom "$i" -y --from="$keyname" --chain-id="$chainid" --gas=500000 --fees=100000usei --broadcast-mode=sync --output=json | submit_and_wait_for_tx "$seidbin")
new_token_denom=$(echo "$create_denom_result" | jq -r '.logs[].events[].attributes[] | select(.key == "new_token_denom").value')
echo "Got token $new_token_denom for iteration $i"
done
Expand All @@ -32,7 +34,7 @@ sleep 5
for i in {11..20}
do
echo "Creating first set of tokenfactory denoms #$i..."
create_denom_result=$(printf "12345678\n" | $seidbin tx tokenfactory create-denom "$i" -y --from="$keyname" --chain-id="$chainid" --gas=500000 --fees=100000usei --broadcast-mode=block --output=json)
create_denom_result=$(printf "12345678\n" | $seidbin tx tokenfactory create-denom "$i" -y --from="$keyname" --chain-id="$chainid" --gas=500000 --fees=100000usei --broadcast-mode=sync --output=json | submit_and_wait_for_tx "$seidbin")
new_token_denom=$(echo "$create_denom_result" | jq -r '.logs[].events[].attributes[] | select(.key == "new_token_denom").value')
echo "Got token $new_token_denom for iteration $i"
done
Expand All @@ -46,7 +48,7 @@ sleep 5
for i in {21..30}
do
echo "Creating first set of tokenfactory denoms #$i..."
create_denom_result=$(printf "12345678\n" | $seidbin tx tokenfactory create-denom "$i" -y --from="$keyname" --chain-id="$chainid" --gas=500000 --fees=100000usei --broadcast-mode=block --output=json)
create_denom_result=$(printf "12345678\n" | $seidbin tx tokenfactory create-denom "$i" -y --from="$keyname" --chain-id="$chainid" --gas=500000 --fees=100000usei --broadcast-mode=sync --output=json | submit_and_wait_for_tx "$seidbin")
new_token_denom=$(echo "$create_denom_result" | jq -r '.logs[].events[].attributes[] | select(.key == "new_token_denom").value')
echo "Got token $new_token_denom for iteration $i"
done
Expand Down
Loading
Loading