diff --git a/go.mod b/go.mod index e20fc66a9..d8d968f7a 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.37.0 - github.com/trufnetwork/kwil-db v0.10.3-0.20260201152833-1a21f34293d9 - github.com/trufnetwork/kwil-db/core v0.4.3-0.20260201152833-1a21f34293d9 + github.com/trufnetwork/kwil-db v0.10.3-0.20260213161930-a361ae2e7c85 + github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213161930-a361ae2e7c85 github.com/trufnetwork/sdk-go v0.3.2-0.20250630062504-841b40cdb709 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa diff --git a/go.sum b/go.sum index 1bd50d3cd..b720c414c 100644 --- a/go.sum +++ b/go.sum @@ -1254,6 +1254,36 @@ github.com/trufnetwork/kwil-db v0.10.3-0.20260120153326-4fab48fcfa11 h1:9oUJRGlP github.com/trufnetwork/kwil-db v0.10.3-0.20260120153326-4fab48fcfa11/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= github.com/trufnetwork/kwil-db v0.10.3-0.20260201152833-1a21f34293d9 h1:xNcapuINfWoqGIfaaVXUF1TR/CGeSnkt0e9UWB7Kj/s= github.com/trufnetwork/kwil-db v0.10.3-0.20260201152833-1a21f34293d9/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260212164355-ee2d45c145d4 h1:PSki2TPipwk4u0AAnpsXpHOuNPAiFHYgvHsMVF5tLK4= +github.com/trufnetwork/kwil-db v0.10.3-0.20260212164355-ee2d45c145d4/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213073903-4c04529bd727 h1:ulrfWzwbfmOr+fch3ycx+iMmTzmFofq4rn/C9+uQboc= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213073903-4c04529bd727/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213075249-8a89984b65de h1:CkaYGjpDKsuV/k5vvkpNevKfaPSoA8KITzGk8rzG2eE= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213075249-8a89984b65de/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213080648-b5b6d7a0b207 h1:E13ElZxhyuRCBbn7kdV2cmmYAhPV6pdLz15LyZm34i8= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213080648-b5b6d7a0b207/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213084139-d06740e1f0bf h1:IwfOGBVLn+t4rV66+o9x3cbLn7mQKt9TDIlqf4LE6hA= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213084139-d06740e1f0bf/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213085754-1df2c7017232 h1:zJwS0ssOu6vYufBoRXcS2Uk4os5lFuA5VqzdA67AsfU= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213085754-1df2c7017232/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213101028-0f15353b1a61 h1:xpq7pFx8FXMty+4EXj3x29GIpE1nXqi07JegPlObbbE= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213101028-0f15353b1a61/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213115557-d11cabc04bf3 h1:HcTlpaNVVyimod8xUpj80opVtcv2GfqLSXjimJOPf38= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213115557-d11cabc04bf3/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213121225-6250d3c192c1 h1:Vsz15yoALMIDqQh9EauTTCGPGKM0WzW5eJm5AkYuDts= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213121225-6250d3c192c1/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213122920-c104333714a1 h1:1E3o6gD7+ektIyTmN9YOIgeqHMp+blHKMe14NGtbklE= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213122920-c104333714a1/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213132303-dfef68f261c4 h1:SaF4rTpGmMk+4H6rX6nQtBqRFSQOdGqCruTFmoqXEjs= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213132303-dfef68f261c4/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213134012-0a8ed786ecb4 h1:F4po4CPfLFoWMjamUCcm0ZnKK915UNZ9U28Ihm5DoT8= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213134012-0a8ed786ecb4/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213134819-0c9a24c43f99 h1:02PqsADbelVdSCO2XEC8im7a+QovC+l2p4daymo9I0c= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213134819-0c9a24c43f99/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213151651-0fe72cd5e0ca h1:NMFTTzQGGw7na16CmDJp6gRiZXHftjpUD59a3WZOoOQ= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213151651-0fe72cd5e0ca/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213161930-a361ae2e7c85 h1:C0vcmkUIphPe9XLFmSe2nEVQjV1q6cw8x1PN2SxpiTE= +github.com/trufnetwork/kwil-db v0.10.3-0.20260213161930-a361ae2e7c85/go.mod h1:LiBAC48uZl2B0IiLtD2hpOce7RNfpuDdghVAOc3u1Qo= github.com/trufnetwork/kwil-db/core v0.4.3-0.20260107154136-b8af58932e24 h1:5RcJ0Cyt9UaXwv71d9jYgwGL2zwyTJdP9m4wkk6B6Z8= github.com/trufnetwork/kwil-db/core v0.4.3-0.20260107154136-b8af58932e24/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= github.com/trufnetwork/kwil-db/core v0.4.3-0.20260108132315-b1fcfb33a848 h1:/0naLqfmAqfL5XWdN1yulk5auImOP14Taw0B1baq3GU= @@ -1296,6 +1326,36 @@ github.com/trufnetwork/kwil-db/core v0.4.3-0.20260120153326-4fab48fcfa11 h1:/q4x github.com/trufnetwork/kwil-db/core v0.4.3-0.20260120153326-4fab48fcfa11/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= github.com/trufnetwork/kwil-db/core v0.4.3-0.20260201152833-1a21f34293d9 h1:blscjdYlio+RF6lnaEYLo5d0iiiBvDgt0g6Dx8z3WNI= github.com/trufnetwork/kwil-db/core v0.4.3-0.20260201152833-1a21f34293d9/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260212164355-ee2d45c145d4 h1:nbB+CXQdA6UD6XUnTxV9GfPqJ5NfdACxP4a9K8TcpVc= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260212164355-ee2d45c145d4/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213073903-4c04529bd727 h1:plvAMgWat+XTp7iwfdtNXQNgr1HHPsebovR1jb0p5Kk= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213073903-4c04529bd727/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213075249-8a89984b65de h1:nNDV/ka/iP2jdxfq/hRW5Ujgsm7atX4Wmsm+ppsyJjI= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213075249-8a89984b65de/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213080648-b5b6d7a0b207 h1:OM5WFFXDDb/Ae3iZZbElL5sbimam0SpomrXpXfVR7Hk= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213080648-b5b6d7a0b207/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213084139-d06740e1f0bf h1:xTcfqZS8RjWKAnaYulOLHUdkgagIlI12H6W9/YJHnoo= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213084139-d06740e1f0bf/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213085754-1df2c7017232 h1:vqKkOi26R2EAT1knZ2pEUpzDyPIufzJrKY7NsX6okSQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213085754-1df2c7017232/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213101028-0f15353b1a61 h1:7ZyXQGxRsjkpI14m1ikwEBU3cQkCuJip2p7c866gD5M= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213101028-0f15353b1a61/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213115557-d11cabc04bf3 h1:nFuJWFcFavGS1k9hLXdvWpYIR1ITWWMV9VAKO1OZwXQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213115557-d11cabc04bf3/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213121225-6250d3c192c1 h1:9Ut8dQgftGZJsBWOFzwYjPMJuEDWDYRoZb3vQOBBDyM= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213121225-6250d3c192c1/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213122920-c104333714a1 h1:vO54vyOijKYxQQ4G/EFeX+4BmZ5qkOXH27o1/7EZ0HI= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213122920-c104333714a1/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213132303-dfef68f261c4 h1:R2OcNeIuO/OSTQ25WWjAqM/UWcp9sqfrosR7yONV3hA= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213132303-dfef68f261c4/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213134012-0a8ed786ecb4 h1:z/r1avv0zNO1e49+QkywvzL6imN8pdNKUlNTLIMhkY4= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213134012-0a8ed786ecb4/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213134819-0c9a24c43f99 h1:DRQilIySqZwBmKB+xuOuucGc8DIUDWunmpxoS0tYGys= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213134819-0c9a24c43f99/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213151651-0fe72cd5e0ca h1:6hJHTYiIAYZPC2889DEqpiOb/dSSPaRO4x5/5RaGysk= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213151651-0fe72cd5e0ca/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213161930-a361ae2e7c85 h1:HbF22Bp5oo0HIDkNiSw9jY769dQUETgLBa9ReyOr3bY= +github.com/trufnetwork/kwil-db/core v0.4.3-0.20260213161930-a361ae2e7c85/go.mod h1:HnOsh9+BN13LJCjiH0+XKaJzyjWKf+H9AofFFp90KwQ= github.com/trufnetwork/openzeppelin-merkle-tree-go v0.0.2 h1:DCq8MzbWH0wZmICNmMVsSzUHUPl+2vqRhluEABjxl88= github.com/trufnetwork/openzeppelin-merkle-tree-go v0.0.2/go.mod h1:Y0MJpPp9QXU5vC6Gpoilql2NkgmGNcbHm9HYC2v2N8s= github.com/trufnetwork/sdk-go v0.3.2-0.20250630062504-841b40cdb709 h1:d9EqPXIjbq/atzEncK5dM3Z9oStx1BxCGuL/sjefeCw= diff --git a/internal/migrations/erc20-bridge/005-history-actions.sql b/internal/migrations/erc20-bridge/005-history-actions.sql new file mode 100644 index 000000000..84196492e --- /dev/null +++ b/internal/migrations/erc20-bridge/005-history-actions.sql @@ -0,0 +1,86 @@ +-- History Actions for ERC20 Bridges +-- These actions expose the unified transaction history for each bridge instance. + +-- HOODI TESTNET - Test Token (TT) +CREATE OR REPLACE ACTION hoodi_tt_get_history($wallet_address TEXT, $limit INT, $offset INT) +PUBLIC VIEW RETURNS TABLE ( + type TEXT, + amount NUMERIC(78, 0), + from_address BYTEA, + to_address BYTEA, + internal_tx_hash BYTEA, + external_tx_hash BYTEA, + status TEXT, + block_height INT8, + block_timestamp INT8, + external_block_height INT8 +) { + FOR $row IN hoodi_tt.get_history($wallet_address, $limit, $offset) { + RETURN NEXT $row.type, $row.amount, $row.from_address, $row.to_address, + $row.internal_tx_hash, $row.external_tx_hash, $row.status, + $row.block_height, $row.block_timestamp, $row.external_block_height; + } +}; + +-- HOODI TESTNET - Test Token 2 (TT2) +CREATE OR REPLACE ACTION hoodi_tt2_get_history($wallet_address TEXT, $limit INT, $offset INT) +PUBLIC VIEW RETURNS TABLE ( + type TEXT, + amount NUMERIC(78, 0), + from_address BYTEA, + to_address BYTEA, + internal_tx_hash BYTEA, + external_tx_hash BYTEA, + status TEXT, + block_height INT8, + block_timestamp INT8, + external_block_height INT8 +) { + FOR $row IN hoodi_tt2.get_history($wallet_address, $limit, $offset) { + RETURN NEXT $row.type, $row.amount, $row.from_address, $row.to_address, + $row.internal_tx_hash, $row.external_tx_hash, $row.status, + $row.block_height, $row.block_timestamp, $row.external_block_height; + } +}; + +-- SEPOLIA TESTNET +CREATE OR REPLACE ACTION sepolia_get_history($wallet_address TEXT, $limit INT, $offset INT) +PUBLIC VIEW RETURNS TABLE ( + type TEXT, + amount NUMERIC(78, 0), + from_address BYTEA, + to_address BYTEA, + internal_tx_hash BYTEA, + external_tx_hash BYTEA, + status TEXT, + block_height INT8, + block_timestamp INT8, + external_block_height INT8 +) { + FOR $row IN sepolia_bridge.get_history($wallet_address, $limit, $offset) { + RETURN NEXT $row.type, $row.amount, $row.from_address, $row.to_address, + $row.internal_tx_hash, $row.external_tx_hash, $row.status, + $row.block_height, $row.block_timestamp, $row.external_block_height; + } +}; + +-- MAINNET +CREATE OR REPLACE ACTION ethereum_get_history($wallet_address TEXT, $limit INT, $offset INT) +PUBLIC VIEW RETURNS TABLE ( + type TEXT, + amount NUMERIC(78, 0), + from_address BYTEA, + to_address BYTEA, + internal_tx_hash BYTEA, + external_tx_hash BYTEA, + status TEXT, + block_height INT8, + block_timestamp INT8, + external_block_height INT8 +) { + FOR $row IN ethereum_bridge.get_history($wallet_address, $limit, $offset) { + RETURN NEXT $row.type, $row.amount, $row.from_address, $row.to_address, + $row.internal_tx_hash, $row.external_tx_hash, $row.status, + $row.block_height, $row.block_timestamp, $row.external_block_height; + } +}; diff --git a/tests/extensions/erc20/erc20_bridge_history_test.go b/tests/extensions/erc20/erc20_bridge_history_test.go new file mode 100644 index 000000000..d71400777 --- /dev/null +++ b/tests/extensions/erc20/erc20_bridge_history_test.go @@ -0,0 +1,129 @@ +//go:build kwiltest + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/trufnetwork/kwil-db/common" + "github.com/trufnetwork/kwil-db/core/types" + kwilTesting "github.com/trufnetwork/kwil-db/testing" + + erc20shim "github.com/trufnetwork/kwil-db/node/exts/erc20-bridge/erc20" + testerc20 "github.com/trufnetwork/node/tests/streams/utils/erc20" +) + +// TestERC20BridgeHistory verifies the transaction history API end-to-end. +func TestERC20BridgeHistory(t *testing.T) { + seedAndRun(t, "erc20_bridge_history", func(ctx context.Context, platform *kwilTesting.Platform) error { + // Initialize instance + err := erc20shim.ForTestingSeedAndActivateInstance(ctx, platform, TestChain, TestEscrowA, TestERC20, 18, 60, TestExtensionAlias) + require.NoError(t, err) + + // 1. Inject a Deposit (simulates external deposit) + // This will create a 'deposit' record in transaction_history + err = testerc20.InjectERC20Transfer(ctx, platform, TestChain, TestEscrowA, TestERC20, TestUserA, TestUserA, TestAmount1, 10, nil) + require.NoError(t, err) + + // 2. Perform a Transfer (internal) + // This will create a 'transfer' record + // We use the extension's 'transfer' action via engine call + engineCtx := engCtx(ctx, platform, TestUserA, 20, false) // Block height 20 + transferAmt, err := types.ParseDecimalExplicit("500000000000000000", 78, 0) // 0.5 tokens + require.NoError(t, err) + + // Transfer from UserA to UserB + res, err := platform.Engine.Call(engineCtx, platform.DB, TestExtensionAlias, "transfer", []any{TestUserB, transferAmt}, nil) + require.NoError(t, err) + require.Nil(t, res.Error) + + // 3. Query History for UserA (should see Deposit + Transfer Sent) + // Params: wallet, limit (10), offset (0) + var historyRows [][]any + res, err = platform.Engine.Call(engineCtx, platform.DB, TestExtensionAlias, "get_history", []any{TestUserA, int64(10), int64(0)}, func(row *common.Row) error { + if len(row.Values) != 10 { + return fmt.Errorf("unexpected column count: %d, expected 10", len(row.Values)) + } + historyRows = append(historyRows, row.Values) + return nil + }) + require.NoError(t, err) + require.Nil(t, res.Error) + + require.Len(t, historyRows, 2, "UserA should have 2 history records") + + // Expect newest first (Transfer at block 20, Deposit at block 10) + transferRow := historyRows[0] + depositRow := historyRows[1] + + // Verify Transfer Record + require.Equal(t, "transfer", transferRow[0].(string)) + require.Equal(t, transferAmt.String(), transferRow[1].(*types.Decimal).String()) + // from_address + // to_address + // internal_tx_hash + // external_tx_hash (should be nil) + require.Equal(t, "completed", transferRow[6].(string)) + // block_height + + // Verify Deposit Record + require.Equal(t, "deposit", depositRow[0].(string)) + require.Equal(t, TestAmount1, depositRow[1].(*types.Decimal).String()) + // external_tx_hash (should be set from InjectERC20Transfer mock) + require.Equal(t, "completed", depositRow[6].(string)) + + // 4. Query History for UserB (should see Transfer Received) + historyRows = nil + res, err = platform.Engine.Call(engineCtx, platform.DB, TestExtensionAlias, "get_history", []any{TestUserB, int64(10), int64(0)}, func(row *common.Row) error { + if len(row.Values) != 10 { + return fmt.Errorf("unexpected column count: %d, expected 10", len(row.Values)) + } + historyRows = append(historyRows, row.Values) + return nil + }) + require.NoError(t, err) + require.Nil(t, res.Error) + + require.Len(t, historyRows, 1, "UserB should have 1 history record") + require.Equal(t, "transfer", historyRows[0][0].(string)) + require.Equal(t, transferAmt.String(), historyRows[0][1].(*types.Decimal).String()) + + // 5. Perform a Withdrawal (bridge out) + // This will create a 'withdrawal' record with status 'pending_epoch' + withdrawalAmt, err := types.ParseDecimalExplicit("100000000000000000", 78, 0) // 0.1 tokens + require.NoError(t, err) + withdrawalRecipient := TestUserB // External address distinct from Escrow + + engineCtx3 := engCtx(ctx, platform, TestUserA, 30, false) // Block height 30 + res, err = platform.Engine.Call(engineCtx3, platform.DB, TestExtensionAlias, "bridge", []any{withdrawalRecipient, withdrawalAmt}, nil) + require.NoError(t, err) + require.Nil(t, res.Error) + + // 6. Query History for UserA again (should see Withdrawal + Transfer + Deposit) + historyRows = nil + res, err = platform.Engine.Call(engineCtx3, platform.DB, TestExtensionAlias, "get_history", []any{TestUserA, int64(10), int64(0)}, func(row *common.Row) error { + if len(row.Values) != 10 { + return fmt.Errorf("unexpected column count: %d, expected 10", len(row.Values)) + } + historyRows = append(historyRows, row.Values) + return nil + }) + require.NoError(t, err) + require.Nil(t, res.Error) + + require.Len(t, historyRows, 3, "UserA should have 3 history records") + + // Expect newest first (Withdrawal at block 30) + withdrawalRow := historyRows[0] + require.Equal(t, "withdrawal", withdrawalRow[0].(string)) + require.Equal(t, withdrawalAmt.String(), withdrawalRow[1].(*types.Decimal).String()) + // to_address should match recipient + require.Equal(t, "pending_epoch", withdrawalRow[6].(string)) + require.Equal(t, int64(30), withdrawalRow[7].(int64)) // block_height + + return nil + }) +}