From 03a525eeddbd1050b55e1f70c408283e08e5040a Mon Sep 17 00:00:00 2001 From: Michael Buntarman Date: Wed, 1 Oct 2025 16:06:12 +0700 Subject: [PATCH 01/10] chore: release ami build during version release (#1187) --- .github/workflows/ami-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ami-build.yml b/.github/workflows/ami-build.yml index f5352369f..b3b64e20b 100644 --- a/.github/workflows/ami-build.yml +++ b/.github/workflows/ami-build.yml @@ -84,6 +84,7 @@ jobs: echo "Deploying AMI pipeline infrastructure for stage: ${{ env.STAGE }}..." cdk bootstrap --require-approval never cdk deploy AMI-Pipeline-${{ env.STAGE }}-Stack \ + --app "go run ami-cdk.go" \ --context stage=${{ env.STAGE }} \ --require-approval never From deca98f5699ea6258c7e9dba4a61b8e88aa89aa0 Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Wed, 1 Oct 2025 16:40:18 +0700 Subject: [PATCH 02/10] chore: change escrow address --- internal/migrations/erc20-bridge/000-extension.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/migrations/erc20-bridge/000-extension.sql b/internal/migrations/erc20-bridge/000-extension.sql index b70d50627..cf01a2bfa 100644 --- a/internal/migrations/erc20-bridge/000-extension.sql +++ b/internal/migrations/erc20-bridge/000-extension.sql @@ -2,5 +2,5 @@ USE erc20 { chain: 'sepolia', - escrow: '0x2D4f435867066737bA1617ef024E073413909Ad2' + escrow: '0x502430eD0BbE0f230215870c9C2853e126eE5Ae3' } AS sepolia_bridge; \ No newline at end of file From b7945f28bb1bbd2161c95ea6959f14e66456f6b9 Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 12:44:28 +0700 Subject: [PATCH 03/10] chore: add internal transfer fee --- .../erc20-bridge/002-public-transfer-actions.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql index 3c6d0de6a..5cf32e85b 100644 --- a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql +++ b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql @@ -13,6 +13,16 @@ CREATE OR REPLACE ACTION sepolia_transfer($to_address TEXT, $amount TEXT) PUBLIC ERROR('Transfer amount must be positive'); } + $fee := 1000000000000000000::NUMERIC(78, 0); -- 1 TRUF with 18 decimals + $caller_balance := ethereum_bridge.balance(@caller); + + IF ($caller_balance - $amount::NUMERIC(78, 0)) < $fee { + ERROR('Insufficient balance for transfer. Requires an extra 1 TRUF fee on top of the transfer amount'); + } + + $leader_addr TEXT := encode(@leader_sender, 'hex')::TEXT; + sepolia_bridge.transfer($leader_addr, $total_fee); + -- Execute transfer using the bridge extension sepolia_bridge.transfer($to_address, $amount::NUMERIC(78, 0)); }; From 980defb543a49dd89c5c6eab70c81af606b8143a Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 12:45:47 +0700 Subject: [PATCH 04/10] fix: var name --- .../migrations/erc20-bridge/002-public-transfer-actions.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql index 5cf32e85b..195dfc2e0 100644 --- a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql +++ b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql @@ -21,7 +21,7 @@ CREATE OR REPLACE ACTION sepolia_transfer($to_address TEXT, $amount TEXT) PUBLIC } $leader_addr TEXT := encode(@leader_sender, 'hex')::TEXT; - sepolia_bridge.transfer($leader_addr, $total_fee); + sepolia_bridge.transfer($leader_addr, $fee); -- Execute transfer using the bridge extension sepolia_bridge.transfer($to_address, $amount::NUMERIC(78, 0)); From 08c243519b77bf1fbde9f7e94ec4beda7206287e Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 12:48:06 +0700 Subject: [PATCH 05/10] chore: add ethereum fee --- .../erc20-bridge/002-public-transfer-actions.sql | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql index 195dfc2e0..21b67a8f7 100644 --- a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql +++ b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql @@ -13,8 +13,9 @@ CREATE OR REPLACE ACTION sepolia_transfer($to_address TEXT, $amount TEXT) PUBLIC ERROR('Transfer amount must be positive'); } + -- Fee $fee := 1000000000000000000::NUMERIC(78, 0); -- 1 TRUF with 18 decimals - $caller_balance := ethereum_bridge.balance(@caller); + $caller_balance := sepolia_bridge.balance(@caller); IF ($caller_balance - $amount::NUMERIC(78, 0)) < $fee { ERROR('Insufficient balance for transfer. Requires an extra 1 TRUF fee on top of the transfer amount'); @@ -40,6 +41,17 @@ CREATE OR REPLACE ACTION ethereum_transfer($to_address TEXT, $amount TEXT) PUBLI ERROR('Transfer amount must be positive'); } + -- Fee + $fee := 1000000000000000000::NUMERIC(78, 0); -- 1 TRUF with 18 decimals + $caller_balance := ethereum_bridge.balance(@caller); + + IF ($caller_balance - $amount::NUMERIC(78, 0)) < $fee { + ERROR('Insufficient balance for transfer. Requires an extra 1 TRUF fee on top of the transfer amount'); + } + + $leader_addr TEXT := encode(@leader_sender, 'hex')::TEXT; + ethereum_bridge.transfer($leader_addr, $fee); + -- Execute transfer using the bridge extension ethereum_bridge.transfer($to_address, $amount::NUMERIC(78, 0)); }; From 56474b3a89ffe3c86f477ada6de642026973d1b0 Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 15:15:21 +0700 Subject: [PATCH 06/10] chore: add tests --- tests/extensions/erc20/common_test.go | 23 ++- .../erc20_bridge_transfer_actions_test.go | 148 +++++++++++++++--- .../erc20/erc20_bridge_transfer_test.go | 5 +- 3 files changed, 151 insertions(+), 25 deletions(-) diff --git a/tests/extensions/erc20/common_test.go b/tests/extensions/erc20/common_test.go index 1deec0fcd..7d8cd6a18 100644 --- a/tests/extensions/erc20/common_test.go +++ b/tests/extensions/erc20/common_test.go @@ -8,6 +8,8 @@ import ( "testing" "github.com/trufnetwork/kwil-db/common" + "github.com/trufnetwork/kwil-db/core/crypto" + coreauth "github.com/trufnetwork/kwil-db/core/crypto/auth" kwilTesting "github.com/trufnetwork/kwil-db/testing" "github.com/trufnetwork/node/internal/migrations" testutils "github.com/trufnetwork/node/tests/streams/utils" @@ -22,6 +24,8 @@ const ( TestERC20 = "0x2222222222222222222222222222222222222222" TestUserA = "0xabc0000000000000000000000000000000000001" TestUserB = "0xabc0000000000000000000000000000000000002" + TestUserC = "0xabc0000000000000000000000000000000000003" + TestUserD = "0xabc0000000000000000000000000000000000004" TestAmount1 = "1000000000000000000" // 1.0 tokens TestAmount2 = "2000000000000000000" // 2.0 tokens ) @@ -58,13 +62,22 @@ func seedAndRun(t TestingT, name string, fn kwilTesting.TestFunc) { // engCtx creates a standard EngineContext for testing func engCtx(ctx context.Context, platform *kwilTesting.Platform, caller string, height int64, overrideAuthz bool) *common.EngineContext { + // Generate a leader public key for fee collection + // This is required for actions that transfer fees to @leader_sender + _, leaderPubGeneric, err := crypto.GenerateSecp256k1Key(nil) + if err != nil { + panic(fmt.Sprintf("failed to generate leader key: %v", err)) + } + leaderPub := leaderPubGeneric.(*crypto.Secp256k1PublicKey) + return &common.EngineContext{ TxContext: &common.TxContext{ - Ctx: ctx, - BlockContext: &common.BlockContext{Height: height}, - Signer: platform.Deployer, - Caller: caller, - TxID: platform.Txid(), + Ctx: ctx, + BlockContext: &common.BlockContext{Height: height, Proposer: leaderPub}, + Signer: platform.Deployer, + Caller: caller, + TxID: platform.Txid(), + Authenticator: coreauth.EthPersonalSignAuth, }, OverrideAuthz: overrideAuthz, } diff --git a/tests/extensions/erc20/erc20_bridge_transfer_actions_test.go b/tests/extensions/erc20/erc20_bridge_transfer_actions_test.go index 8c5e85db3..75f8d9f78 100644 --- a/tests/extensions/erc20/erc20_bridge_transfer_actions_test.go +++ b/tests/extensions/erc20/erc20_bridge_transfer_actions_test.go @@ -30,25 +30,28 @@ func TestSepoliaTransferActions(t *testing.T) { require.NoError(t, err) // Credit initial balance to TestUserA using configured escrow + // Need 3.0 TRUF: 1.0 for transfer + 1.0 fee + 1.0 remaining + initialAmount := "3000000000000000000" // 3.0 TRUF err = testerc20.InjectERC20Transfer(ctx, platform, - TestChain, configuredEscrow, TestERC20, TestUserA, TestUserA, TestAmount2, 10, nil) + TestChain, configuredEscrow, TestERC20, TestUserA, TestUserA, initialAmount, 10, nil) require.NoError(t, err) // Verify initial balance via sepolia_wallet_balance action balanceA, err := callSepoliaWalletBalance(ctx, platform, TestUserA) require.NoError(t, err) - require.Equal(t, TestAmount2, balanceA, "UserA should have initial deposit") + require.Equal(t, initialAmount, balanceA, "UserA should have initial deposit") // Verify TestUserB has zero balance initially balanceB, err := callSepoliaWalletBalance(ctx, platform, TestUserB) require.NoError(t, err) require.Equal(t, "0", balanceB, "UserB should have zero balance initially") - // Execute transfer via sepolia_transfer action + // Execute transfer via sepolia_transfer action (1.0 TRUF + 1.0 fee) err = callSepoliaTransfer(ctx, platform, TestUserA, TestUserB, TestAmount1) require.NoError(t, err) // Verify balances after transfer + // UserA: 3.0 - 1.0 transfer - 1.0 fee = 1.0 remaining balanceA, err = callSepoliaWalletBalance(ctx, platform, TestUserA) require.NoError(t, err) require.Equal(t, TestAmount1, balanceA, "UserA should have remaining amount after transfer") @@ -114,13 +117,13 @@ func TestTransferActionValidation(t *testing.T) { t.Log("ERROR: Expected error for insufficient balance (nil) but got none") } require.Error(t, err) - require.Contains(t, err.Error(), "insufficient balance") + require.Contains(t, err.Error(), "Insufficient balance") - // Test Case 6: Insufficient balance (user has some balance but not enough) + // Test Case 6: Insufficient balance (user has some balance but not enough for transfer + fee) t.Log("Testing insufficient balance with partial balance...") - // Give TestUserA a small balance (half of what they'll try to transfer) - smallAmount := "500000000000000000" // 0.5 tokens (half of TestAmount1 which is 1.0) + // Give TestUserA 1.5 TRUF (not enough for 1.0 transfer + 1.0 fee) + smallAmount := "1500000000000000000" // 1.5 tokens err = testerc20.InjectERC20Transfer(ctx, platform, TestChain, configuredEscrow, TestERC20, TestUserA, TestUserA, smallAmount, 10, nil) require.NoError(t, err) @@ -130,14 +133,14 @@ func TestTransferActionValidation(t *testing.T) { require.NoError(t, err) require.Equal(t, smallAmount, balance, "TestUserA should have small balance") - // Try to transfer more than they have (TestAmount1 = 1.0 tokens > smallAmount = 0.5 tokens) + // Try to transfer 1.0 TRUF (but needs 2.0 total: 1.0 + 1.0 fee, only has 1.5) err = callSepoliaTransfer(ctx, platform, TestUserA, TestUserB, TestAmount1) if err == nil { t.Log("ERROR: Expected error for insufficient balance (partial) but got none") } require.Error(t, err) - require.Contains(t, err.Error(), "insufficient balance") - require.Contains(t, err.Error(), smallAmount) // Should show actual balance they have + require.Contains(t, err.Error(), "Insufficient balance") + require.Contains(t, err.Error(), "Requires an extra 1 TRUF fee") // Test balance query with invalid address t.Log("Testing balance query with invalid address...") @@ -166,38 +169,39 @@ func TestMultipleTransferActions(t *testing.T) { // Define test users userA := TestUserA userB := TestUserB - userC := "0xabc0000000000000000000000000000000000003" + userC := TestUserC // Credit large initial balance to userA + // Need: 3.0 + 1.0 fee + 2.0 + 1.0 fee = 7.0 TRUF for A's transfers initialAmount := "10000000000000000000" // 10.0 tokens err = testerc20.InjectERC20Transfer(ctx, platform, TestChain, configuredEscrow, TestERC20, userA, userA, initialAmount, 10, nil) require.NoError(t, err) - // Transfer A -> B (3 tokens) + // Transfer A -> B (3 tokens + 1 fee = 4 deducted from A) err = callSepoliaTransfer(ctx, platform, userA, userB, "3000000000000000000") require.NoError(t, err) - // Transfer A -> C (2 tokens) + // Transfer A -> C (2 tokens + 1 fee = 3 deducted from A) err = callSepoliaTransfer(ctx, platform, userA, userC, TestAmount2) require.NoError(t, err) - // Transfer B -> C (1 token) + // Transfer B -> C (1 token + 1 fee = 2 deducted from B) err = callSepoliaTransfer(ctx, platform, userB, userC, TestAmount1) require.NoError(t, err) // Verify final balances - // UserA: 10 - 3 - 2 = 5 + // UserA: 10 - (3 + 1 fee) - (2 + 1 fee) = 10 - 4 - 3 = 3 balanceA, err := callSepoliaWalletBalance(ctx, platform, userA) require.NoError(t, err) - require.Equal(t, "5000000000000000000", balanceA, "UserA should have 5 tokens remaining") + require.Equal(t, "3000000000000000000", balanceA, "UserA should have 3 tokens remaining after transfers + fees") - // UserB: 3 - 1 = 2 + // UserB: 3 received - (1 + 1 fee) = 3 - 2 = 1 balanceB, err := callSepoliaWalletBalance(ctx, platform, userB) require.NoError(t, err) - require.Equal(t, TestAmount2, balanceB, "UserB should have 2 tokens remaining") + require.Equal(t, TestAmount1, balanceB, "UserB should have 1 token remaining after transfer + fee") - // UserC: 2 + 1 = 3 + // UserC: 2 received + 1 received = 3 balanceC, err := callSepoliaWalletBalance(ctx, platform, userC) require.NoError(t, err) require.Equal(t, "3000000000000000000", balanceC, "UserC should have 3 tokens total") @@ -243,6 +247,112 @@ func callSepoliaWalletBalance(ctx context.Context, platform *kwilTesting.Platfor return balance, nil } +// TestTransferActionFeeExactBalance tests transfer with exactly enough balance (amount + 1 TRUF fee). +func TestTransferActionFeeExactBalance(t *testing.T) { + seedAndRun(t, "transfer_action_fee_exact", func(ctx context.Context, platform *kwilTesting.Platform) error { + // Enable instance with alias in one step + err := erc20shim.ForTestingSeedAndActivateInstance(ctx, platform, TestChain, TestEscrowA, TestERC20, 18, 60, TestExtensionAlias) + require.NoError(t, err) + + // Get the configured escrow address from bridge info + configuredEscrow, err := getBridgeEscrowAddress(ctx, platform) + require.NoError(t, err) + + // Give TestUserA exactly 2.0 TRUF (1.0 transfer + 1.0 fee) + exactAmount := "2000000000000000000" // 2.0 TRUF + err = testerc20.InjectERC20Transfer(ctx, platform, + TestChain, configuredEscrow, TestERC20, TestUserA, TestUserA, exactAmount, 10, nil) + require.NoError(t, err) + + transferAmount := TestAmount1 // 1.0 TRUF + + // Transfer should succeed + err = callSepoliaTransfer(ctx, platform, TestUserA, TestUserB, transferAmount) + require.NoError(t, err) + + // Verify sender now has 0 balance (2.0 - 1.0 transfer - 1.0 fee = 0) + balanceA, err := callSepoliaWalletBalance(ctx, platform, TestUserA) + require.NoError(t, err) + require.Equal(t, "0", balanceA, "UserA should have 0 balance after transfer + fee") + + // Verify recipient received only the transfer amount (not the fee) + balanceB, err := callSepoliaWalletBalance(ctx, platform, TestUserB) + require.NoError(t, err) + require.Equal(t, transferAmount, balanceB, "UserB should have received transfer amount") + + return nil + }) +} + +// TestTransferActionFeeInsufficientBalance tests that transfers fail when user doesn't have enough for fee. +func TestTransferActionFeeInsufficientBalance(t *testing.T) { + seedAndRun(t, "transfer_action_fee_insufficient", func(ctx context.Context, platform *kwilTesting.Platform) error { + // Enable instance with alias in one step + err := erc20shim.ForTestingSeedAndActivateInstance(ctx, platform, TestChain, TestEscrowA, TestERC20, 18, 60, TestExtensionAlias) + require.NoError(t, err) + + // Get the configured escrow address from bridge info + configuredEscrow, err := getBridgeEscrowAddress(ctx, platform) + require.NoError(t, err) + + // Give TestUserC exactly 1.0 TRUF (not enough for 0.5 transfer + 1.0 fee) + err = testerc20.InjectERC20Transfer(ctx, platform, + TestChain, configuredEscrow, TestERC20, TestUserC, TestUserC, TestAmount1, 10, nil) + require.NoError(t, err) + + // Try to transfer 0.5 TRUF (but needs 1.5 total: 0.5 + 1.0 fee) + halfAmount := "500000000000000000" // 0.5 TRUF + err = callSepoliaTransfer(ctx, platform, TestUserC, TestUserB, halfAmount) + require.Error(t, err) + require.Contains(t, err.Error(), "Insufficient balance for transfer") + require.Contains(t, err.Error(), "Requires an extra 1 TRUF fee") + + return nil + }) +} + +// TestTransferActionFeeMultipleTransfers tests that fee is deducted on each transfer. +func TestTransferActionFeeMultipleTransfers(t *testing.T) { + seedAndRun(t, "transfer_action_fee_multiple", func(ctx context.Context, platform *kwilTesting.Platform) error { + // Enable instance with alias in one step + err := erc20shim.ForTestingSeedAndActivateInstance(ctx, platform, TestChain, TestEscrowA, TestERC20, 18, 60, TestExtensionAlias) + require.NoError(t, err) + + // Get the configured escrow address from bridge info + configuredEscrow, err := getBridgeEscrowAddress(ctx, platform) + require.NoError(t, err) + + // Give TestUserD 4.0 TRUF (enough for 1.0 transfer + 1.0 fee, twice) + fourTRUF := "4000000000000000000" // 4.0 TRUF + err = testerc20.InjectERC20Transfer(ctx, platform, + TestChain, configuredEscrow, TestERC20, TestUserD, TestUserD, fourTRUF, 10, nil) + require.NoError(t, err) + + // Verify TestUserD has 4.0 TRUF initially + balanceD, err := callSepoliaWalletBalance(ctx, platform, TestUserD) + require.NoError(t, err) + require.Equal(t, fourTRUF, balanceD, "UserD should have 4.0 TRUF initially") + + // First transfer: 1.0 TRUF + 1.0 fee = 2.0 deducted + err = callSepoliaTransfer(ctx, platform, TestUserD, TestUserB, TestAmount1) + require.NoError(t, err) + + balanceD, err = callSepoliaWalletBalance(ctx, platform, TestUserD) + require.NoError(t, err) + require.Equal(t, "2000000000000000000", balanceD, "UserD should have 2.0 after first transfer+fee") + + // Second transfer: 1.0 TRUF + 1.0 fee = 2.0 deducted + err = callSepoliaTransfer(ctx, platform, TestUserD, TestUserB, TestAmount1) + require.NoError(t, err) + + balanceD, err = callSepoliaWalletBalance(ctx, platform, TestUserD) + require.NoError(t, err) + require.Equal(t, "0", balanceD, "UserD should have 0 after second transfer+fee") + + return nil + }) +} + // Helper function to get the configured escrow address from bridge info func getBridgeEscrowAddress(ctx context.Context, platform *kwilTesting.Platform) (string, error) { engineCtx := engCtx(ctx, platform, "0x0000000000000000000000000000000000000000", 1, false) diff --git a/tests/extensions/erc20/erc20_bridge_transfer_test.go b/tests/extensions/erc20/erc20_bridge_transfer_test.go index 87e76e1a3..916b4c309 100644 --- a/tests/extensions/erc20/erc20_bridge_transfer_test.go +++ b/tests/extensions/erc20/erc20_bridge_transfer_test.go @@ -31,6 +31,8 @@ func TestERC20BridgeTransferBalances(t *testing.T) { require.NoError(t, err) // Step 1: Inject deposit for userA + // Note: Extension's transfer() method doesn't have the 1 TRUF fee + // The fee is only in the public SQL actions (sepolia_transfer/ethereum_transfer) err = testerc20.InjectERC20Transfer(ctx, platform, TestChain, TestEscrowA, TestERC20, TestUserA, TestUserA, TestAmount2, 10, nil) require.NoError(t, err) @@ -44,7 +46,8 @@ func TestERC20BridgeTransferBalances(t *testing.T) { require.NoError(t, err) require.Equal(t, "0", balanceB, "userB should have zero balance initially") - // Step 2: Transfer half amount from userA to userB + // Step 2: Transfer half amount from userA to userB using extension method + // Note: This uses the extension's transfer() method directly, which doesn't charge the 1 TRUF fee engineCtx := engCtx(ctx, platform, TestUserA, 2, false) // transfer expects amount as numeric(78,0) From db068ff4bb09c45f1d8191a751b6c5f6e02e9da4 Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 15:17:25 +0700 Subject: [PATCH 07/10] chore: revert comments --- tests/extensions/erc20/erc20_bridge_transfer_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/extensions/erc20/erc20_bridge_transfer_test.go b/tests/extensions/erc20/erc20_bridge_transfer_test.go index 916b4c309..7d1852514 100644 --- a/tests/extensions/erc20/erc20_bridge_transfer_test.go +++ b/tests/extensions/erc20/erc20_bridge_transfer_test.go @@ -31,8 +31,6 @@ func TestERC20BridgeTransferBalances(t *testing.T) { require.NoError(t, err) // Step 1: Inject deposit for userA - // Note: Extension's transfer() method doesn't have the 1 TRUF fee - // The fee is only in the public SQL actions (sepolia_transfer/ethereum_transfer) err = testerc20.InjectERC20Transfer(ctx, platform, TestChain, TestEscrowA, TestERC20, TestUserA, TestUserA, TestAmount2, 10, nil) require.NoError(t, err) @@ -47,7 +45,6 @@ func TestERC20BridgeTransferBalances(t *testing.T) { require.Equal(t, "0", balanceB, "userB should have zero balance initially") // Step 2: Transfer half amount from userA to userB using extension method - // Note: This uses the extension's transfer() method directly, which doesn't charge the 1 TRUF fee engineCtx := engCtx(ctx, platform, TestUserA, 2, false) // transfer expects amount as numeric(78,0) From 1956337351c84f278367fe4e563dcd3d0e8e1632 Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 15:17:45 +0700 Subject: [PATCH 08/10] chore: revert comments --- tests/extensions/erc20/erc20_bridge_transfer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/extensions/erc20/erc20_bridge_transfer_test.go b/tests/extensions/erc20/erc20_bridge_transfer_test.go index 7d1852514..87e76e1a3 100644 --- a/tests/extensions/erc20/erc20_bridge_transfer_test.go +++ b/tests/extensions/erc20/erc20_bridge_transfer_test.go @@ -44,7 +44,7 @@ func TestERC20BridgeTransferBalances(t *testing.T) { require.NoError(t, err) require.Equal(t, "0", balanceB, "userB should have zero balance initially") - // Step 2: Transfer half amount from userA to userB using extension method + // Step 2: Transfer half amount from userA to userB engineCtx := engCtx(ctx, platform, TestUserA, 2, false) // transfer expects amount as numeric(78,0) From 5aff827e08d08e82ad3619a7f0b30ccc17e2b403 Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 15:28:57 +0700 Subject: [PATCH 09/10] chore: zero sum guard --- .../erc20-bridge/002-public-transfer-actions.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql index 21b67a8f7..5569d527b 100644 --- a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql +++ b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql @@ -15,9 +15,9 @@ CREATE OR REPLACE ACTION sepolia_transfer($to_address TEXT, $amount TEXT) PUBLIC -- Fee $fee := 1000000000000000000::NUMERIC(78, 0); -- 1 TRUF with 18 decimals - $caller_balance := sepolia_bridge.balance(@caller); + $caller_balance := COALESCE(sepolia_bridge.balance(@caller), 0::NUMERIC(78, 0)); - IF ($caller_balance - $amount::NUMERIC(78, 0)) < $fee { + IF ($caller_balance < ($amount::NUMERIC(78, 0) + $fee)) < $fee { ERROR('Insufficient balance for transfer. Requires an extra 1 TRUF fee on top of the transfer amount'); } @@ -43,9 +43,9 @@ CREATE OR REPLACE ACTION ethereum_transfer($to_address TEXT, $amount TEXT) PUBLI -- Fee $fee := 1000000000000000000::NUMERIC(78, 0); -- 1 TRUF with 18 decimals - $caller_balance := ethereum_bridge.balance(@caller); + $caller_balance := COALESCE(ethereum_bridge.balance(@caller), 0::NUMERIC(78, 0)); - IF ($caller_balance - $amount::NUMERIC(78, 0)) < $fee { + IF ($caller_balance < ($amount::NUMERIC(78, 0) + $fee)) { ERROR('Insufficient balance for transfer. Requires an extra 1 TRUF fee on top of the transfer amount'); } From 76dd1961bb84519c13a0ebf2d51298f34d53d70e Mon Sep 17 00:00:00 2001 From: williamrusdyputra Date: Mon, 3 Nov 2025 15:33:50 +0700 Subject: [PATCH 10/10] fix: leftover --- .../migrations/erc20-bridge/002-public-transfer-actions.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql index 5569d527b..7a55b715b 100644 --- a/internal/migrations/erc20-bridge/002-public-transfer-actions.sql +++ b/internal/migrations/erc20-bridge/002-public-transfer-actions.sql @@ -17,7 +17,7 @@ CREATE OR REPLACE ACTION sepolia_transfer($to_address TEXT, $amount TEXT) PUBLIC $fee := 1000000000000000000::NUMERIC(78, 0); -- 1 TRUF with 18 decimals $caller_balance := COALESCE(sepolia_bridge.balance(@caller), 0::NUMERIC(78, 0)); - IF ($caller_balance < ($amount::NUMERIC(78, 0) + $fee)) < $fee { + IF ($caller_balance < ($amount::NUMERIC(78, 0) + $fee)) { ERROR('Insufficient balance for transfer. Requires an extra 1 TRUF fee on top of the transfer amount'); }