diff --git a/extensions/tn_settlement/settlement_integration_test.go b/extensions/tn_settlement/settlement_integration_test.go index 1369dae2b..6f68b1d2c 100644 --- a/extensions/tn_settlement/settlement_integration_test.go +++ b/extensions/tn_settlement/settlement_integration_test.go @@ -7,13 +7,15 @@ import ( "fmt" "testing" + gethAbi "github.com/ethereum/go-ethereum/accounts/abi" + gethCommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "github.com/trufnetwork/kwil-db/common" "github.com/trufnetwork/kwil-db/core/log" kwilTypes "github.com/trufnetwork/kwil-db/core/types" "github.com/trufnetwork/kwil-db/node/accounts" - kwilTesting "github.com/trufnetwork/kwil-db/testing" erc20bridge "github.com/trufnetwork/kwil-db/node/exts/erc20-bridge/erc20" + kwilTesting "github.com/trufnetwork/kwil-db/testing" "github.com/trufnetwork/node/extensions/tn_settlement/internal" "github.com/trufnetwork/node/extensions/tn_utils" @@ -23,6 +25,7 @@ import ( "github.com/trufnetwork/sdk-go/core/util" attestationTests "github.com/trufnetwork/node/tests/streams/attestation" + testerc20 "github.com/trufnetwork/node/tests/streams/utils/erc20" ) // NO_OUTCOME_VALUE represents a NO outcome in settlement tests @@ -31,6 +34,17 @@ const NO_OUTCOME_VALUE = "-1.000000000000000000" // Test constants - match existing erc20-bridge configuration const ( testExtensionName = "sepolia_bridge" + + // TRUF bridge constants for market creation fee + testTRUFChain = "hoodi" + testTRUFEscrow = "0x878d6aaeb6e746033f50b8dc268d54b4631554e7" + testTRUFERC20 = "0x263ce78fef26600e4e428cebc91c2a52484b4fbf" +) + +// Point counter for TRUF balance injection +var ( + trufPointCounter int64 = 500 + lastTrufBalancePoint *int64 ) // TestSettlementIntegration tests the automatic settlement functionality @@ -58,6 +72,9 @@ func TestSettlementIntegration(t *testing.T) { func testFindUnsettledMarkets(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { + // Reset point counter for this test + lastTrufBalancePoint = nil + deployer := util.Unsafe_NewEthereumAddressFromString("0x5555555555555555555555555555555555555555") platform.Deployer = deployer.Bytes() @@ -69,9 +86,13 @@ func testFindUnsettledMarkets(t *testing.T) func(context.Context, *kwilTesting.P err = erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) - // Create stream and attestation + // Give TRUF balance for market creation fee + err = giveTrufBalance(ctx, platform, deployer.Address(), "10000000000000000000") // 10 TRUF + require.NoError(t, err) + + // Create stream and attestation - returns query_components (ABI-encoded) streamID := "stfindmarket00000000000000000000" - attestationHash := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") + queryComponents := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") // Create market with settle_time in the past (relative to database time) // settleTime=100 is in the past (Jan 1970), BlockContext.Timestamp=50 for creation @@ -82,7 +103,7 @@ func testFindUnsettledMarkets(t *testing.T) func(context.Context, *kwilTesting.P var queryID int createRes, err := platform.Engine.Call(engineCtx, platform.DB, "", "create_market", - []any{testExtensionName, attestationHash, settleTime, int64(5), int64(1)}, + []any{testExtensionName, queryComponents, settleTime, int64(5), int64(1)}, func(row *common.Row) error { queryID = int(row.Values[0].(int64)) return nil @@ -93,6 +114,18 @@ func testFindUnsettledMarkets(t *testing.T) func(context.Context, *kwilTesting.P } require.Greater(t, queryID, 0) + // Get the hash from the database (computed by create_market) + var marketHash []byte + engineCtx = helper.NewEngineContext() + err = platform.Engine.Execute(engineCtx, platform.DB, + `SELECT hash FROM ob_queries WHERE id = $id`, + map[string]any{"id": queryID}, + func(row *common.Row) error { + marketHash = row.Values[0].([]byte) + return nil + }) + require.NoError(t, err) + // Test FindUnsettledMarkets accts, err := accounts.InitializeAccountStore(ctx, platform.DB, log.New()) require.NoError(t, err) @@ -102,9 +135,9 @@ func testFindUnsettledMarkets(t *testing.T) func(context.Context, *kwilTesting.P require.NoError(t, err) require.Len(t, markets, 1, "should find 1 unsettled market") require.Equal(t, queryID, markets[0].ID) - require.Equal(t, attestationHash, markets[0].Hash) + require.Equal(t, marketHash, markets[0].Hash) - t.Logf("✅ FindUnsettledMarkets found market queryID=%d with hash=%x", queryID, attestationHash) + t.Logf("✅ FindUnsettledMarkets found market queryID=%d with hash=%x", queryID, marketHash) return nil } } @@ -115,6 +148,9 @@ func testFindUnsettledMarkets(t *testing.T) func(context.Context, *kwilTesting.P func testAttestationExists(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { + // Reset point counter for this test + lastTrufBalancePoint = nil + deployer := util.Unsafe_NewEthereumAddressFromString("0x6666666666666666666666666666666666666666") platform.Deployer = deployer.Bytes() @@ -126,8 +162,39 @@ func testAttestationExists(t *testing.T) func(context.Context, *kwilTesting.Plat err = erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) + // Give TRUF balance for market creation fee + err = giveTrufBalance(ctx, platform, deployer.Address(), "10000000000000000000") // 10 TRUF + require.NoError(t, err) + streamID := "stattexists000000000000000000000" - attestationHash := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") + queryComponents := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") + + // Create market to get the computed hash + engineCtx := helper.NewEngineContext() + engineCtx.TxContext.BlockContext.Timestamp = 50 + var queryID int + createRes, err := platform.Engine.Call(engineCtx, platform.DB, "", "create_market", + []any{testExtensionName, queryComponents, int64(100), int64(5), int64(1)}, + func(row *common.Row) error { + queryID = int(row.Values[0].(int64)) + return nil + }) + require.NoError(t, err) + if createRes.Error != nil { + t.Fatalf("create_market failed: %v", createRes.Error) + } + + // Get the hash from the database + var attestationHash []byte + engineCtx = helper.NewEngineContext() + err = platform.Engine.Execute(engineCtx, platform.DB, + `SELECT hash FROM ob_queries WHERE id = $id`, + map[string]any{"id": queryID}, + func(row *common.Row) error { + attestationHash = row.Values[0].([]byte) + return nil + }) + require.NoError(t, err) // Test AttestationExists accts, err := accounts.InitializeAccountStore(ctx, platform.DB, log.New()) @@ -156,6 +223,9 @@ func testAttestationExists(t *testing.T) func(context.Context, *kwilTesting.Plat func testSettleMarketViaAction(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { + // Reset point counter for this test + lastTrufBalancePoint = nil + deployer := util.Unsafe_NewEthereumAddressFromString("0x7777777777777777777777777777777777777777") platform.Deployer = deployer.Bytes() @@ -167,8 +237,12 @@ func testSettleMarketViaAction(t *testing.T) func(context.Context, *kwilTesting. err = erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) + // Give TRUF balance for market creation fee + err = giveTrufBalance(ctx, platform, deployer.Address(), "10000000000000000000") // 10 TRUF + require.NoError(t, err) + streamID := "stsettleaction000000000000000000" - attestationHash := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") + queryComponents := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") // Create market (settleTime in future relative to BlockContext) creationTime := int64(50) @@ -179,7 +253,7 @@ func testSettleMarketViaAction(t *testing.T) func(context.Context, *kwilTesting. var queryID int createRes, err := platform.Engine.Call(engineCtx, platform.DB, "", "create_market", - []any{testExtensionName, attestationHash, settleTime, int64(5), int64(1)}, + []any{testExtensionName, queryComponents, settleTime, int64(5), int64(1)}, func(row *common.Row) error { queryID = int(row.Values[0].(int64)) return nil @@ -255,6 +329,9 @@ func testLoadSettlementConfig(t *testing.T) func(context.Context, *kwilTesting.P func testSkipMarketWithoutAttestation(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { + // Reset point counter for this test + lastTrufBalancePoint = nil + deployer := util.Unsafe_NewEthereumAddressFromString("0x9999999999999999999999999999999999999999") platform.Deployer = deployer.Bytes() @@ -266,9 +343,13 @@ func testSkipMarketWithoutAttestation(t *testing.T) func(context.Context, *kwilT err = erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) + // Give TRUF balance for market creation fee + err = giveTrufBalance(ctx, platform, deployer.Address(), "10000000000000000000") // 10 TRUF + require.NoError(t, err) + // Create stream and attestation WITHOUT signing (skip the SignAttestation step) streamID := "stskipnoatt000000000000000000000" - attestationHash := createStreamWithoutSigningAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") + queryComponents := createStreamWithoutSigningAttestation(t, ctx, platform, helper, deployer, streamID, "1.000000000000000000") // Create market (settleTime in future relative to BlockContext) creationTime := int64(50) @@ -279,7 +360,7 @@ func testSkipMarketWithoutAttestation(t *testing.T) func(context.Context, *kwilT var queryID int createRes, err := platform.Engine.Call(engineCtx, platform.DB, "", "create_market", - []any{testExtensionName, attestationHash, settleTime, int64(5), int64(1)}, + []any{testExtensionName, queryComponents, settleTime, int64(5), int64(1)}, func(row *common.Row) error { queryID = int(row.Values[0].(int64)) return nil @@ -289,6 +370,18 @@ func testSkipMarketWithoutAttestation(t *testing.T) func(context.Context, *kwilT t.Fatalf("create_market failed: %v", createRes.Error) } + // Get the hash from the database + var attestationHash []byte + engineCtx = helper.NewEngineContext() + err = platform.Engine.Execute(engineCtx, platform.DB, + `SELECT hash FROM ob_queries WHERE id = $id`, + map[string]any{"id": queryID}, + func(row *common.Row) error { + attestationHash = row.Values[0].([]byte) + return nil + }) + require.NoError(t, err) + // Test AttestationExists should return false accts, err := accounts.InitializeAccountStore(ctx, platform.DB, log.New()) require.NoError(t, err) @@ -322,6 +415,9 @@ func testSkipMarketWithoutAttestation(t *testing.T) func(context.Context, *kwilT func testMultipleMarketsProcessing(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { + // Reset point counter for this test + lastTrufBalancePoint = nil + deployer := util.Unsafe_NewEthereumAddressFromString("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") platform.Deployer = deployer.Bytes() @@ -333,6 +429,10 @@ func testMultipleMarketsProcessing(t *testing.T) func(context.Context, *kwilTest err = erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) + // Give TRUF balance for market creation fees (need 3 markets × 2 TRUF = 6 TRUF minimum) + err = giveTrufBalance(ctx, platform, deployer.Address(), "20000000000000000000") // 20 TRUF + require.NoError(t, err) + // Create 3 markets (settleTime in future relative to BlockContext) creationTime := int64(50) settleTime := int64(100) @@ -347,14 +447,14 @@ func testMultipleMarketsProcessing(t *testing.T) func(context.Context, *kwilTest value = NO_OUTCOME_VALUE // NO outcome for second market } - attestationHash := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, value) + queryComponents := createStreamAndAttestation(t, ctx, platform, helper, deployer, streamID, value) engineCtx := helper.NewEngineContext() engineCtx.TxContext.BlockContext.Timestamp = creationTime var queryID int createRes, err := platform.Engine.Call(engineCtx, platform.DB, "", "create_market", - []any{testExtensionName, attestationHash, settleTime, int64(5), int64(1)}, + []any{testExtensionName, queryComponents, settleTime, int64(5), int64(1)}, func(row *common.Row) error { queryID = int(row.Values[0].(int64)) return nil @@ -403,7 +503,64 @@ func testMultipleMarketsProcessing(t *testing.T) func(context.Context, *kwilTest // Helper Functions // ============================================================================= +// giveTrufBalance gives TRUF balance to a user for market creation fee +func giveTrufBalance(ctx context.Context, platform *kwilTesting.Platform, wallet string, amountStr string) error { + trufPointCounter++ + currentPoint := trufPointCounter + + err := testerc20.InjectERC20Transfer( + ctx, platform, + testTRUFChain, testTRUFEscrow, testTRUFERC20, + wallet, wallet, amountStr, + currentPoint, lastTrufBalancePoint, + ) + if err != nil { + return fmt.Errorf("failed to inject TRUF: %w", err) + } + + p := currentPoint + lastTrufBalancePoint = &p + return nil +} + +// encodeQueryComponents encodes query components as ABI (address,bytes32,string,bytes) +func encodeQueryComponents(dataProvider, streamID, actionID string, argsBytes []byte) ([]byte, error) { + addressType, err := gethAbi.NewType("address", "", nil) + if err != nil { + return nil, fmt.Errorf("failed to create address type: %w", err) + } + bytes32Type, err := gethAbi.NewType("bytes32", "", nil) + if err != nil { + return nil, fmt.Errorf("failed to create bytes32 type: %w", err) + } + stringType, err := gethAbi.NewType("string", "", nil) + if err != nil { + return nil, fmt.Errorf("failed to create string type: %w", err) + } + bytesType, err := gethAbi.NewType("bytes", "", nil) + if err != nil { + return nil, fmt.Errorf("failed to create bytes type: %w", err) + } + + args := gethAbi.Arguments{ + {Type: addressType}, + {Type: bytes32Type}, + {Type: stringType}, + {Type: bytesType}, + } + + // Convert data_provider string to address + addr := gethCommon.HexToAddress(dataProvider) + + // Convert stream_id to bytes32 (pad to 32 bytes) + var streamIDBytes32 [32]byte + copy(streamIDBytes32[:], []byte(streamID)) + + return args.Pack(addr, streamIDBytes32, actionID, argsBytes) +} + // createStreamWithoutSigningAttestation creates a stream and unsigned attestation (for testing skip logic) +// Returns query_components (ABI-encoded) for use with create_market func createStreamWithoutSigningAttestation( t *testing.T, ctx context.Context, @@ -434,17 +591,16 @@ func createStreamWithoutSigningAttestation( }, nil) require.NoError(t, err) - // Request attestation + // Encode action args for get_record argsBytes, err := tn_utils.EncodeActionArgs([]any{ dataProvider, streamID, int64(500), int64(1500), nil, false, }) require.NoError(t, err) - var attestationHash []byte + // Request attestation (but don't sign) res, err := platform.Engine.Call(engineCtx, platform.DB, "", "request_attestation", []any{dataProvider, streamID, "get_record", argsBytes, false, nil}, func(row *common.Row) error { - attestationHash = append([]byte(nil), row.Values[1].([]byte)...) return nil }) require.NoError(t, err) @@ -454,10 +610,15 @@ func createStreamWithoutSigningAttestation( // NOTE: Intentionally NOT calling helper.SignAttestation() to test unsigned attestation - return attestationHash + // Return ABI-encoded query_components for create_market + queryComponents, err := encodeQueryComponents(dataProvider, streamID, "get_record", argsBytes) + require.NoError(t, err) + + return queryComponents } -// createStreamAndAttestation creates a stream, inserts data, and returns signed attestation hash +// createStreamAndAttestation creates a stream, inserts data, and returns ABI-encoded query_components +// for use with create_market (the hash is computed by create_market from query_components) func createStreamAndAttestation( t *testing.T, ctx context.Context, @@ -490,12 +651,13 @@ func createStreamAndAttestation( }, nil) require.NoError(t, err) - // Request attestation using the SAME engineCtx so it sees the inserted data + // Encode action args for get_record - MUST match what we use in request_attestation argsBytes, err := tn_utils.EncodeActionArgs([]any{ dataProvider, streamID, int64(500), int64(1500), nil, false, }) require.NoError(t, err) + // Request attestation using the SAME engineCtx so it sees the inserted data var requestTxID string var attestationHash []byte res, err := platform.Engine.Call(engineCtx, platform.DB, "", "request_attestation", @@ -515,5 +677,10 @@ func createStreamAndAttestation( // Sign attestation helper.SignAttestation(requestTxID) - return attestationHash + // Return ABI-encoded query_components for create_market + // The hash is computed by create_market from these query_components + queryComponents, err := encodeQueryComponents(dataProvider, streamID, "get_record", argsBytes) + require.NoError(t, err) + + return queryComponents } diff --git a/internal/migrations/033-order-book-settlement.sql b/internal/migrations/033-order-book-settlement.sql index 82a6a332d..b64a87cc1 100644 --- a/internal/migrations/033-order-book-settlement.sql +++ b/internal/migrations/033-order-book-settlement.sql @@ -35,7 +35,9 @@ -- Batch unlock collateral for multiple wallets -- This helper processes all unlocks in a single call, avoiding nested queries in settlement +-- The $bridge parameter specifies which bridge to use (hoodi_tt2, sepolia_bridge, ethereum_bridge) CREATE OR REPLACE ACTION ob_batch_unlock_collateral( + $bridge TEXT, $wallet_addresses TEXT[], $amounts NUMERIC(78, 0)[] ) PRIVATE { @@ -50,7 +52,16 @@ CREATE OR REPLACE ACTION ob_batch_unlock_collateral( SELECT wallet, amount FROM UNNEST($wallet_addresses, $amounts) AS u(wallet, amount) { - ethereum_bridge.unlock($payout.wallet, $payout.amount); + -- Use the correct bridge based on market configuration + if $bridge = 'hoodi_tt2' { + hoodi_tt2.unlock($payout.wallet, $payout.amount); + } else if $bridge = 'sepolia_bridge' { + sepolia_bridge.unlock($payout.wallet, $payout.amount); + } else if $bridge = 'ethereum_bridge' { + ethereum_bridge.unlock($payout.wallet, $payout.amount); + } else { + ERROR('Invalid bridge in ob_batch_unlock_collateral: ' || COALESCE($bridge, 'NULL')); + } } }; @@ -109,6 +120,15 @@ CREATE OR REPLACE ACTION distribute_fees( RETURN; } + -- Get market's bridge for unlock operations + $bridge TEXT; + for $row in SELECT bridge FROM ob_queries WHERE id = $query_id { + $bridge := $row.bridge; + } + if $bridge IS NULL { + ERROR('Market not found for query_id: ' || $query_id::TEXT); + } + -- Step 1: Count distinct blocks sampled for this market $block_count INT := 0; for $row in SELECT COUNT(DISTINCT block) as cnt FROM ob_rewards WHERE query_id = $query_id { @@ -181,7 +201,7 @@ CREATE OR REPLACE ACTION distribute_fees( -- Step 5: Batch unlock to all LPs (single call, no loops) if $wallet_addresses IS NOT NULL AND COALESCE(array_length($wallet_addresses), 0) > 0 { - ob_batch_unlock_collateral($wallet_addresses, $amounts); + ob_batch_unlock_collateral($bridge, $wallet_addresses, $amounts); } -- Step 5.5: CREATE AUDIT RECORDS @@ -290,6 +310,15 @@ CREATE OR REPLACE ACTION process_settlement( $total_fees_collected NUMERIC(78, 0) := '0'::NUMERIC(78, 0); $one_token NUMERIC(78, 0) := '1000000000000000000'::NUMERIC(78, 0); + -- Get market's bridge for unlock operations + $bridge TEXT; + for $row in SELECT bridge FROM ob_queries WHERE id = $query_id { + $bridge := $row.bridge; + } + if $bridge IS NULL { + ERROR('Market not found for query_id: ' || $query_id::TEXT); + } + -- Step 1: Bulk delete all losing positions (efficient single operation) -- Price semantics: price=0 (holdings), price>0 (open sells), price<0 (open buys) -- Deletes losing outcome holdings and sells, which have zero value after settlement @@ -376,7 +405,7 @@ CREATE OR REPLACE ACTION process_settlement( -- Step 4: Process ALL payouts in a SINGLE batch call (no nested queries!) if $wallet_addresses IS NOT NULL AND COALESCE(array_length($wallet_addresses), 0) > 0 { - ob_batch_unlock_collateral($wallet_addresses, $amounts); + ob_batch_unlock_collateral($bridge, $wallet_addresses, $amounts); } -- Step 5: Fee distribution to liquidity providers diff --git a/internal/migrations/037-order-book-validation.sql b/internal/migrations/037-order-book-validation.sql index e9f93769e..2fef33b05 100644 --- a/internal/migrations/037-order-book-validation.sql +++ b/internal/migrations/037-order-book-validation.sql @@ -99,19 +99,41 @@ PUBLIC VIEW RETURNS ( $buys_collateral NUMERIC(78, 0) := ($open_buys_value::NUMERIC(78, 0) * '1000000000000000000'::NUMERIC(78, 0)) / 100::NUMERIC(78, 0); $expected_collateral := ($shares_collateral + $buys_collateral)::NUMERIC(78, 0); - -- Step 5: Get actual vault balance from ethereum_bridge - -- The ethereum_bridge.info() precompile returns network ownedBalance + -- Step 5: Get market's bridge and retrieve actual vault balance + $bridge TEXT; + for $row in SELECT bridge FROM ob_queries WHERE id = $query_id { + $bridge := $row.bridge; + } + if $bridge IS NULL { + ERROR('Market not found for query_id: ' || $query_id::TEXT); + } + + -- The bridge.info() precompile returns network ownedBalance $vault_balance NUMERIC(78, 0) := 0::NUMERIC(78, 0); $row_count INT := 0; - for $info in ethereum_bridge.info() { - $vault_balance := $info.balance; - $row_count := $row_count + 1; + if $bridge = 'hoodi_tt2' { + for $info in hoodi_tt2.info() { + $vault_balance := $info.balance; + $row_count := $row_count + 1; + } + } else if $bridge = 'sepolia_bridge' { + for $info in sepolia_bridge.info() { + $vault_balance := $info.balance; + $row_count := $row_count + 1; + } + } else if $bridge = 'ethereum_bridge' { + for $info in ethereum_bridge.info() { + $vault_balance := $info.balance; + $row_count := $row_count + 1; + } + } else { + ERROR('Invalid bridge in validate_market_collateral: ' || COALESCE($bridge, 'NULL')); } -- Validate that bridge returned data (distinguish unavailable from empty vault) if $row_count = 0 { - ERROR('Cannot validate collateral: ethereum_bridge.info() returned no data. Bridge may be unavailable or not initialized.'); + ERROR('Cannot validate collateral: bridge.info() returned no data. Bridge may be unavailable or not initialized.'); } -- Step 6: Validate binary token parity diff --git a/tests/streams/order_book/cancel_order_test.go b/tests/streams/order_book/cancel_order_test.go index 7549b9795..58e45af69 100644 --- a/tests/streams/order_book/cancel_order_test.go +++ b/tests/streams/order_book/cancel_order_test.go @@ -418,33 +418,33 @@ func testCancelBuyOrderVerifyRefund(t *testing.T) func(ctx context.Context, plat }) require.NoError(t, err) - // Get balance before buy order - balanceBefore, err := getBalance(ctx, platform, userAddr.Address()) + // Get USDC balance before buy order (buy orders lock USDC, not TRUF) + balanceBefore, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) - // Place buy order: 10 YES @ $0.56 (costs 5.6 TRUF) + // Place buy order: 10 YES @ $0.56 (costs 5.6 USDC) err = callPlaceBuyOrder(ctx, platform, &userAddr, int(marketID), true, 56, 10) require.NoError(t, err) - // Get balance after buy order (should be 5.6 TRUF less) - balanceAfterBuy, err := getBalance(ctx, platform, userAddr.Address()) + // Get USDC balance after buy order (should be 5.6 USDC less) + balanceAfterBuy, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) expectedAfterBuy := new(big.Int).Sub(balanceBefore, toWei("5.6")) require.Equal(t, 0, expectedAfterBuy.Cmp(balanceAfterBuy), - fmt.Sprintf("Balance after buy should be 5.6 TRUF less. Before: %s, After: %s, Expected: %s", + fmt.Sprintf("USDC balance after buy should be 5.6 less. Before: %s, After: %s, Expected: %s", balanceBefore.String(), balanceAfterBuy.String(), expectedAfterBuy.String())) // Cancel the order err = callCancelOrder(ctx, platform, &userAddr, int(marketID), true, -56) require.NoError(t, err) - // Get balance after cancel (should be back to balanceBefore) - balanceAfterCancel, err := getBalance(ctx, platform, userAddr.Address()) + // Get USDC balance after cancel (should be back to balanceBefore) + balanceAfterCancel, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) require.Equal(t, 0, balanceBefore.Cmp(balanceAfterCancel), - fmt.Sprintf("Balance after cancel should equal balance before buy. Before: %s, After: %s", + fmt.Sprintf("USDC balance after cancel should equal balance before buy. Before: %s, After: %s", balanceBefore.String(), balanceAfterCancel.String())) return nil diff --git a/tests/streams/order_book/fee_distribution_audit_test.go b/tests/streams/order_book/fee_distribution_audit_test.go index 068bda0ee..c281ce9ef 100644 --- a/tests/streams/order_book/fee_distribution_audit_test.go +++ b/tests/streams/order_book/fee_distribution_audit_test.go @@ -46,6 +46,7 @@ func testAuditRecordCreation(t *testing.T) func(context.Context, *kwilTesting.Pl return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -173,6 +174,7 @@ func testAuditRecordCreation(t *testing.T) func(context.Context, *kwilTesting.Pl func testAuditMultiBlock(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePoint = nil + lastTrufBalancePoint = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -246,6 +248,7 @@ func testAuditMultiBlock(t *testing.T) func(context.Context, *kwilTesting.Platfo func testAuditNoLPs(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePoint = nil + lastTrufBalancePoint = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -301,6 +304,7 @@ func testAuditNoLPs(t *testing.T) func(context.Context, *kwilTesting.Platform) e func testAuditZeroFees(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePoint = nil + lastTrufBalancePoint = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -356,6 +360,7 @@ func testAuditZeroFees(t *testing.T) func(context.Context, *kwilTesting.Platform func testAuditDataIntegrity(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePoint = nil + lastTrufBalancePoint = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -381,10 +386,11 @@ func testAuditDataIntegrity(t *testing.T) func(context.Context, *kwilTesting.Pla // Setup LP scenario (places orders which spend collateral) setupLPScenario(t, ctx, platform, &user1, &user2, int(marketID)) - // Get initial balances AFTER order placement (to measure only fee distribution increase) - bal1Before, err := getBalance(ctx, platform, user1.Address()) + // Get initial USDC balances AFTER order placement (to measure only fee distribution increase) + // Note: Fee distribution pays in USDC (hoodi_tt2), not TRUF (hoodi_tt) + bal1Before, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - bal2Before, err := getBalance(ctx, platform, user2.Address()) + bal2Before, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) // Sample @@ -396,10 +402,10 @@ func testAuditDataIntegrity(t *testing.T) func(context.Context, *kwilTesting.Pla err = fundVaultAndDistributeFees(t, ctx, platform, &user1, int(marketID), totalFees) require.NoError(t, err) - // Get final balances - bal1After, err := getBalance(ctx, platform, user1.Address()) + // Get final USDC balances + bal1After, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - bal2After, err := getBalance(ctx, platform, user2.Address()) + bal2After, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) // Calculate actual balance increases @@ -510,9 +516,9 @@ func setupLPScenario(t *testing.T, ctx context.Context, platform *kwilTesting.Pl func fundVaultAndDistributeFees(t *testing.T, ctx context.Context, platform *kwilTesting.Platform, user *util.EthereumAddress, marketID int, totalFees *big.Int) error { - // Fund vault if fees > 0 + // Fund vault if fees > 0 (use USDC-only since vault doesn't need TRUF) if totalFees.Sign() > 0 { - err := giveBalanceChained(ctx, platform, testEscrow, totalFees.String()) + err := giveUSDCBalanceChained(ctx, platform, testUSDCEscrow, totalFees.String()) if err != nil { return err } diff --git a/tests/streams/order_book/fee_distribution_test.go b/tests/streams/order_book/fee_distribution_test.go index e94a3a553..1acff3377 100644 --- a/tests/streams/order_book/fee_distribution_test.go +++ b/tests/streams/order_book/fee_distribution_test.go @@ -46,6 +46,7 @@ func testDistribution1Block2LPs(t *testing.T) func(context.Context, *kwilTesting return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -125,9 +126,9 @@ func testDistribution1Block2LPs(t *testing.T) func(context.Context, *kwilTesting require.InDelta(t, 100.0, total, 0.01, "Rewards should sum to 100%") // Get balances before distribution - balance1Before, err := getBalance(ctx, platform, user1.Address()) + balance1Before, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - balance2Before, err := getBalance(ctx, platform, user2.Address()) + balance2Before, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) t.Logf("Balances before distribution: User1=%s, User2=%s", balance1Before.String(), balance2Before.String()) @@ -137,7 +138,7 @@ func testDistribution1Block2LPs(t *testing.T) func(context.Context, *kwilTesting // Fund the vault (escrow) with 1000 TRUF so it can distribute fees // Use giveBalanceChained to maintain the ordered-sync chain - err = giveBalanceChained(ctx, platform, testEscrow, totalFees.String()) + err = giveUSDCBalanceChained(ctx, platform, testUSDCEscrow, totalFees.String()) require.NoError(t, err) t.Logf("Funded vault with %s TRUF", new(big.Int).Div(totalFees, big.NewInt(1e18)).String()) @@ -176,9 +177,9 @@ func testDistribution1Block2LPs(t *testing.T) func(context.Context, *kwilTesting } // Get balances after distribution - balance1After, err := getBalance(ctx, platform, user1.Address()) + balance1After, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - balance2After, err := getBalance(ctx, platform, user2.Address()) + balance2After, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) t.Logf("Balances after distribution: User1=%s, User2=%s", balance1After.String(), balance2After.String()) @@ -225,6 +226,7 @@ func testDistribution3Blocks2LPs(t *testing.T) func(context.Context, *kwilTestin return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -304,16 +306,16 @@ func testDistribution3Blocks2LPs(t *testing.T) func(context.Context, *kwilTestin participant2ID := 2 // Get balances before distribution - balance1Before, err := getBalance(ctx, platform, user1.Address()) + balance1Before, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - balance2Before, err := getBalance(ctx, platform, user2.Address()) + balance2Before, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) // Distribute 30 TRUF in fees (10 TRUF per block) totalFees := new(big.Int).Mul(big.NewInt(30), big.NewInt(1e18)) // Fund vault - err = giveBalanceChained(ctx, platform, testEscrow, totalFees.String()) + err = giveUSDCBalanceChained(ctx, platform, testUSDCEscrow, totalFees.String()) require.NoError(t, err) _, err = erc20bridge.ForTestingForceSyncInstance(ctx, platform, testChain, testEscrow, testERC20, 18) require.NoError(t, err) @@ -349,9 +351,9 @@ func testDistribution3Blocks2LPs(t *testing.T) func(context.Context, *kwilTestin } // Get balances after distribution - balance1After, err := getBalance(ctx, platform, user1.Address()) + balance1After, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - balance2After, err := getBalance(ctx, platform, user2.Address()) + balance2After, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) // Calculate actual distributions @@ -412,6 +414,7 @@ func testDistributionNoSamples(t *testing.T) func(context.Context, *kwilTesting. return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -440,14 +443,14 @@ func testDistributionNoSamples(t *testing.T) func(context.Context, *kwilTesting. // DO NOT call sample_lp_rewards - no samples! // Get balance before distribution - balanceBefore, err := getBalance(ctx, platform, user1.Address()) + balanceBefore, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) // Prepare 10 TRUF fees totalFees := new(big.Int).Mul(big.NewInt(10), big.NewInt(1e18)) // Fund vault - err = giveBalanceChained(ctx, platform, testEscrow, totalFees.String()) + err = giveUSDCBalanceChained(ctx, platform, testUSDCEscrow, totalFees.String()) require.NoError(t, err) _, err = erc20bridge.ForTestingForceSyncInstance(ctx, platform, testChain, testEscrow, testERC20, 18) require.NoError(t, err) @@ -483,14 +486,14 @@ func testDistributionNoSamples(t *testing.T) func(context.Context, *kwilTesting. } // Get balance after distribution - balanceAfter, err := getBalance(ctx, platform, user1.Address()) + balanceAfter, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) // Verify NO distribution occurred require.Equal(t, balanceBefore, balanceAfter, "User balance should be unchanged (no samples)") // Verify vault still has the fees (they weren't distributed) - vaultBalance, err := getBalance(ctx, platform, testEscrow) + vaultBalance, err := getUSDCBalance(ctx, platform, testEscrow) require.NoError(t, err) require.True(t, vaultBalance.Cmp(totalFees) >= 0, "Vault should retain fees when no samples exist") @@ -506,6 +509,7 @@ func testDistributionZeroFees(t *testing.T) func(context.Context, *kwilTesting.P return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -558,9 +562,9 @@ func testDistributionZeroFees(t *testing.T) func(context.Context, *kwilTesting.P require.NoError(t, err) // Get balances before distribution - balance1Before, err := getBalance(ctx, platform, user1.Address()) + balance1Before, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - balance2Before, err := getBalance(ctx, platform, user2.Address()) + balance2Before, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) // Call distribute_fees with ZERO fees (should return early) @@ -595,9 +599,9 @@ func testDistributionZeroFees(t *testing.T) func(context.Context, *kwilTesting.P } // Get balances after distribution - balance1After, err := getBalance(ctx, platform, user1.Address()) + balance1After, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) - balance2After, err := getBalance(ctx, platform, user2.Address()) + balance2After, err := getUSDCBalance(ctx, platform, user2.Address()) require.NoError(t, err) // Verify NO distribution occurred @@ -621,6 +625,7 @@ func testDistribution1LP(t *testing.T) func(context.Context, *kwilTesting.Platfo return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -676,14 +681,14 @@ func testDistribution1LP(t *testing.T) func(context.Context, *kwilTesting.Platfo require.InDelta(t, 100.0, rewards[participant1ID], 0.01, "Single LP should have 100%") // Get balance before distribution - balanceBefore, err := getBalance(ctx, platform, user1.Address()) + balanceBefore, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) // Distribute 10 TRUF in fees totalFees := new(big.Int).Mul(big.NewInt(10), big.NewInt(1e18)) // Fund vault - err = giveBalanceChained(ctx, platform, testEscrow, totalFees.String()) + err = giveUSDCBalanceChained(ctx, platform, testUSDCEscrow, totalFees.String()) require.NoError(t, err) _, err = erc20bridge.ForTestingForceSyncInstance(ctx, platform, testChain, testEscrow, testERC20, 18) require.NoError(t, err) @@ -719,7 +724,7 @@ func testDistribution1LP(t *testing.T) func(context.Context, *kwilTesting.Platfo } // Get balance after distribution - balanceAfter, err := getBalance(ctx, platform, user1.Address()) + balanceAfter, err := getUSDCBalance(ctx, platform, user1.Address()) require.NoError(t, err) // Calculate distribution diff --git a/tests/streams/order_book/market_creation_test.go b/tests/streams/order_book/market_creation_test.go index acf2c3173..19a4cb902 100644 --- a/tests/streams/order_book/market_creation_test.go +++ b/tests/streams/order_book/market_creation_test.go @@ -333,7 +333,7 @@ func testListMarkets(t *testing.T) func(ctx context.Context, platform *kwilTesti for i := 0; i < 3; i++ { dataProvider := userAddr.Address() // Generate exactly 32-char stream ID - streamID := fmt.Sprintf("stlistmarkets%02d0000000000000000", i)[:32] + streamID := fmt.Sprintf("stlistmarkets%02d00000000000000000", i)[:32] actionID := "get_record" argsBytes := []byte{byte(i)} queryComponents, err := encodeQueryComponents(dataProvider, streamID, actionID, argsBytes) diff --git a/tests/streams/order_book/matching_engine_test.go b/tests/streams/order_book/matching_engine_test.go index ce5ec6cd1..3bab891df 100644 --- a/tests/streams/order_book/matching_engine_test.go +++ b/tests/streams/order_book/matching_engine_test.go @@ -21,35 +21,95 @@ import ( // balancePointTracker tracks the previous point for chaining ERC20 deposits var ( - balancePointCounter int64 = 100 - lastBalancePoint *int64 + balancePointCounter int64 = 100 + lastBalancePoint *int64 + trufBalancePointCounter int64 = 200 + lastTrufBalancePoint *int64 ) -// giveBalanceChained gives balance with proper linked-list chaining for ordered-sync +// giveBalanceChained gives balance (BOTH TRUF and USDC) with proper linked-list chaining for ordered-sync func giveBalanceChained(ctx context.Context, platform *kwilTesting.Platform, wallet string, amountStr string) error { + // Inject TRUF balance first (for market creation fee) + trufBalancePointCounter++ + trufPoint := trufBalancePointCounter + + err := testerc20.InjectERC20Transfer( + ctx, + platform, + testTRUFChain, + testTRUFEscrow, + testTRUFERC20, + wallet, + wallet, + amountStr, + trufPoint, + lastTrufBalancePoint, // Chain to previous TRUF point + ) + + if err != nil { + return fmt.Errorf("failed to inject TRUF: %w", err) + } + + // Update TRUF lastPoint for next call + p := trufPoint + lastTrufBalancePoint = &p + + // Inject USDC balance (for market collateral) balancePointCounter++ - currentPoint := balancePointCounter + usdcPoint := balancePointCounter + + err = testerc20.InjectERC20Transfer( + ctx, + platform, + testUSDCChain, + testUSDCEscrow, + testUSDCERC20, + wallet, + wallet, + amountStr, + usdcPoint, + lastBalancePoint, // Chain to previous USDC point + ) + + if err != nil { + return fmt.Errorf("failed to inject USDC: %w", err) + } + + // Update USDC lastPoint for next call + q := usdcPoint + lastBalancePoint = &q + + return nil +} + +// giveUSDCBalanceChained gives USDC only balance with proper linked-list chaining for ordered-sync +// Use this for vault/escrow funding where TRUF is not needed +func giveUSDCBalanceChained(ctx context.Context, platform *kwilTesting.Platform, wallet string, amountStr string) error { + balancePointCounter++ + usdcPoint := balancePointCounter err := testerc20.InjectERC20Transfer( ctx, platform, - testChain, - testEscrow, - testERC20, + testUSDCChain, + testUSDCEscrow, + testUSDCERC20, wallet, wallet, amountStr, - currentPoint, - lastBalancePoint, // Chain to previous point + usdcPoint, + lastBalancePoint, // Chain to previous USDC point ) - if err == nil { - // Update lastBalancePoint for next call - p := currentPoint - lastBalancePoint = &p + if err != nil { + return fmt.Errorf("failed to inject USDC: %w", err) } - return err + // Update USDC lastPoint for next call + q := usdcPoint + lastBalancePoint = &q + + return nil } // TestMatchingEngine tests all three match types: direct, mint, and burn @@ -88,6 +148,7 @@ func testDirectMatchFullMatch(t *testing.T) func(context.Context, *kwilTesting.P return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -155,6 +216,7 @@ func testMintMatchFullMatch(t *testing.T) func(context.Context, *kwilTesting.Pla return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -223,6 +285,7 @@ func testBurnMatchFullMatch(t *testing.T) func(context.Context, *kwilTesting.Pla return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -260,7 +323,7 @@ func testBurnMatchFullMatch(t *testing.T) func(context.Context, *kwilTesting.Pla require.NoError(t, err) // Get balance before burn - balance1Before, err := getBalance(ctx, platform, user1Addr.Address()) + balance1Before, err := getUSDCBalance(ctx, platform, user1Addr.Address()) require.NoError(t, err) // User1: Sell the YES holdings @ $0.60 @@ -268,7 +331,7 @@ func testBurnMatchFullMatch(t *testing.T) func(context.Context, *kwilTesting.Pla require.NoError(t, err) // Get User2's initial balance BEFORE split limit - balance2Initial, err := getBalance(ctx, platform, user2Addr.Address()) + balance2Initial, err := getUSDCBalance(ctx, platform, user2Addr.Address()) require.NoError(t, err) // User2: Split limit @ 60 creates YES holdings + NO sell @ 40 @@ -290,24 +353,24 @@ func testBurnMatchFullMatch(t *testing.T) func(context.Context, *kwilTesting.Pla require.False(t, hasSellOrders, "Sell orders should be burned") // Verify collateral returned - balance1After, err := getBalance(ctx, platform, user1Addr.Address()) + balance1After, err := getUSDCBalance(ctx, platform, user1Addr.Address()) require.NoError(t, err) - balance2After, err := getBalance(ctx, platform, user2Addr.Address()) + balance2After, err := getUSDCBalance(ctx, platform, user2Addr.Address()) require.NoError(t, err) - // User1 should receive 60 TRUF (100 shares × $0.60) + // User1 should receive 60 USDC (100 shares × $0.60) payout1 := new(big.Int).Sub(balance1After, balance1Before) expectedPayout1 := new(big.Int).Mul(big.NewInt(60), big.NewInt(1e18)) require.Equal(t, expectedPayout1.String(), payout1.String(), - fmt.Sprintf("User1 should receive 60 TRUF, got %s", payout1.String())) + fmt.Sprintf("User1 should receive 60 USDC, got %s", payout1.String())) - // User2 locked 100 TRUF for split, got back 40 TRUF from burn - // Net result: balance should decrease by 60 TRUF + // User2 locked 100 USDC for split, got back 40 USDC from burn + // Net result: balance should decrease by 60 USDC netChange2 := new(big.Int).Sub(balance2After, balance2Initial) expectedNetChange2 := new(big.Int).Mul(big.NewInt(-60), big.NewInt(1e18)) require.Equal(t, expectedNetChange2.String(), netChange2.String(), - fmt.Sprintf("User2 net change should be -60 TRUF (locked 100, received 40), got %s", netChange2.String())) + fmt.Sprintf("User2 net change should be -60 USDC (locked 100, received 40), got %s", netChange2.String())) return nil } @@ -318,6 +381,7 @@ func testDirectMatchPartialFill(t *testing.T) func(context.Context, *kwilTesting return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -389,6 +453,7 @@ func testMintMatchPartialFill(t *testing.T) func(context.Context, *kwilTesting.P return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -463,6 +528,7 @@ func testBurnMatchPartialFill(t *testing.T) func(context.Context, *kwilTesting.P return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -499,7 +565,7 @@ func testBurnMatchPartialFill(t *testing.T) func(context.Context, *kwilTesting.P require.NoError(t, err) // Get balance before burn - balance1Before, err := getBalance(ctx, platform, user1Addr.Address()) + balance1Before, err := getUSDCBalance(ctx, platform, user1Addr.Address()) require.NoError(t, err) // User1: Sell 60 YES @ $0.60 @@ -507,7 +573,7 @@ func testBurnMatchPartialFill(t *testing.T) func(context.Context, *kwilTesting.P require.NoError(t, err) // Get User2's initial balance - balance2Initial, err := getBalance(ctx, platform, user2Addr.Address()) + balance2Initial, err := getUSDCBalance(ctx, platform, user2Addr.Address()) require.NoError(t, err) // User2: Split limit @ 60 creates 100 YES holdings + 100 NO sell @ 40 @@ -530,19 +596,19 @@ func testBurnMatchPartialFill(t *testing.T) func(context.Context, *kwilTesting.P require.Equal(t, int64(40), user2NoSellOrder.Amount) // Verify collateral returned - balance1After, err := getBalance(ctx, platform, user1Addr.Address()) + balance1After, err := getUSDCBalance(ctx, platform, user1Addr.Address()) require.NoError(t, err) - balance2After, err := getBalance(ctx, platform, user2Addr.Address()) + balance2After, err := getUSDCBalance(ctx, platform, user2Addr.Address()) require.NoError(t, err) - // User1 should receive 36 TRUF (60 shares × $0.60) + // User1 should receive 36 USDC (60 shares × $0.60) payout1 := new(big.Int).Sub(balance1After, balance1Before) expectedPayout1 := new(big.Int).Mul(big.NewInt(36), big.NewInt(1e18)) require.Equal(t, expectedPayout1.String(), payout1.String()) - // User2 locked 100 TRUF for split, got back 24 TRUF from burn (60 shares × $0.40) - // Net change: -76 TRUF + // User2 locked 100 USDC for split, got back 24 USDC from burn (60 shares × $0.40) + // Net change: -76 USDC netChange2 := new(big.Int).Sub(balance2After, balance2Initial) expectedNetChange2 := new(big.Int).Mul(big.NewInt(-76), big.NewInt(1e18)) require.Equal(t, expectedNetChange2.String(), netChange2.String()) @@ -560,6 +626,7 @@ func testDirectMatchMultipleRounds(t *testing.T) func(context.Context, *kwilTest return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -654,6 +721,7 @@ func testNoMatchingOrders(t *testing.T) func(context.Context, *kwilTesting.Platf return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker for this test lastBalancePoint = nil + lastTrufBalancePoint = nil // CRITICAL: Initialize ERC20 extension singleton FIRST err := erc20bridge.ForTestingInitializeExtension(ctx, platform) diff --git a/tests/streams/order_book/queries_test.go b/tests/streams/order_book/queries_test.go index 735dddf92..19229dd37 100644 --- a/tests/streams/order_book/queries_test.go +++ b/tests/streams/order_book/queries_test.go @@ -4,6 +4,7 @@ package order_book import ( "context" + "fmt" "testing" "time" @@ -28,9 +29,14 @@ const ( testExtensionNameQueries = "sepolia_bridge" ) +// Note: testTRUFChain, testTRUFEscrow, testTRUFERC20 are defined in market_creation_test.go +// and shared across all test files in this package + var ( - queriesPointCounter int64 = 200 // Start from 200 to avoid conflicts - lastBalancePointQueries *int64 // For chaining balance deposits + queriesPointCounter int64 = 200 // Start from 200 to avoid conflicts + lastBalancePointQueries *int64 // For chaining sepolia_bridge deposits + queriesTrufPointCounter int64 = 300 // Separate counter for TRUF + lastTrufBalancePointQueries *int64 // For chaining TRUF deposits ) func TestQueries(t *testing.T) { @@ -81,6 +87,7 @@ func TestQueries(t *testing.T) { func testGetOrderBookEmpty(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -116,6 +123,7 @@ func testGetOrderBookEmpty(t *testing.T) func(context.Context, *kwilTesting.Plat func testGetOrderBookWithBuyOrders(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -153,6 +161,7 @@ func testGetOrderBookWithBuyOrders(t *testing.T) func(context.Context, *kwilTest func testGetOrderBookWithSellOrders(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -190,6 +199,7 @@ func testGetOrderBookWithSellOrders(t *testing.T) func(context.Context, *kwilTes func testGetOrderBookMixed(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -243,6 +253,7 @@ func testGetOrderBookMixed(t *testing.T) func(context.Context, *kwilTesting.Plat func testGetOrderBookExcludesHoldings(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -274,6 +285,7 @@ func testGetOrderBookExcludesHoldings(t *testing.T) func(context.Context, *kwilT func testGetOrderBookSortingFIFO(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -327,6 +339,7 @@ func testGetOrderBookSortingFIFO(t *testing.T) func(context.Context, *kwilTestin func testGetUserPositionsEmpty(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -349,6 +362,7 @@ func testGetUserPositionsEmpty(t *testing.T) func(context.Context, *kwilTesting. func testGetUserPositionsWithHoldings(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -395,6 +409,7 @@ func testGetUserPositionsWithHoldings(t *testing.T) func(context.Context, *kwilT func testGetUserPositionsWithOrders(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -430,6 +445,7 @@ func testGetUserPositionsWithOrders(t *testing.T) func(context.Context, *kwilTes func testGetUserPositionsMixed(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -480,6 +496,7 @@ func testGetUserPositionsMixed(t *testing.T) func(context.Context, *kwilTesting. func testGetMarketDepthEmpty(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -506,6 +523,7 @@ func testGetMarketDepthEmpty(t *testing.T) func(context.Context, *kwilTesting.Pl func testGetMarketDepthAggregation(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -553,6 +571,7 @@ func testGetMarketDepthAggregation(t *testing.T) func(context.Context, *kwilTest func testGetBestPricesNoOrders(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -592,6 +611,7 @@ func testGetBestPricesNoOrders(t *testing.T) func(context.Context, *kwilTesting. func testGetBestPricesOnlyBuy(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -626,6 +646,7 @@ func testGetBestPricesOnlyBuy(t *testing.T) func(context.Context, *kwilTesting.P func testGetBestPricesOnlySell(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -661,6 +682,7 @@ func testGetBestPricesOnlySell(t *testing.T) func(context.Context, *kwilTesting. func testGetBestPricesBothSides(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -722,6 +744,7 @@ func testGetBestPricesBothSides(t *testing.T) func(context.Context, *kwilTesting func testGetUserCollateralEmpty(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -743,6 +766,7 @@ func testGetUserCollateralEmpty(t *testing.T) func(context.Context, *kwilTesting func testGetUserCollateralWithBuyOrders(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -775,6 +799,7 @@ func testGetUserCollateralWithBuyOrders(t *testing.T) func(context.Context, *kwi func testGetUserCollateralWithShares(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -807,6 +832,7 @@ func testGetUserCollateralWithShares(t *testing.T) func(context.Context, *kwilTe func testGetUserCollateralMixed(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePointQueries = nil // Reset for this test + lastTrufBalancePointQueries = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -848,10 +874,32 @@ func testGetUserCollateralMixed(t *testing.T) func(context.Context, *kwilTesting // ============================================================================ func giveBalanceQueries(ctx context.Context, platform *kwilTesting.Platform, wallet string, amountStr string) error { + // Inject TRUF first (for market creation fee - always from hoodi_tt) + queriesTrufPointCounter++ + trufPoint := queriesTrufPointCounter + + err := testerc20.InjectERC20Transfer( + ctx, platform, + testTRUFChain, + testTRUFEscrow, + testTRUFERC20, + wallet, wallet, + amountStr, + trufPoint, + lastTrufBalancePointQueries, // Chain to previous TRUF + ) + + if err != nil { + return fmt.Errorf("failed to inject TRUF: %w", err) + } + p := trufPoint + lastTrufBalancePointQueries = &p + + // Inject sepolia_bridge tokens (for market collateral) queriesPointCounter++ currentPoint := queriesPointCounter - err := testerc20.InjectERC20Transfer( + err = testerc20.InjectERC20Transfer( ctx, platform, testChainQueries, testEscrowQueries, @@ -862,10 +910,13 @@ func giveBalanceQueries(ctx context.Context, platform *kwilTesting.Platform, wal lastBalancePointQueries, // Chain to previous ) - if err == nil { - lastBalancePointQueries = ¤tPoint // Update for next call + if err != nil { + return fmt.Errorf("failed to inject sepolia: %w", err) } - return err + q := currentPoint + lastBalancePointQueries = &q + + return nil } func createTestMarketQueries(t *testing.T, ctx context.Context, platform *kwilTesting.Platform, signer *util.EthereumAddress) (int, []byte) { diff --git a/tests/streams/order_book/rewards_test.go b/tests/streams/order_book/rewards_test.go index 8f735ef88..9c2c78e1a 100644 --- a/tests/streams/order_book/rewards_test.go +++ b/tests/streams/order_book/rewards_test.go @@ -122,7 +122,8 @@ func TestRewards(t *testing.T) { // testSampleRewardsMarketSettled tests error when trying to sample a settled market func testSampleRewardsMarketSettled(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test userAddr := util.Unsafe_NewEthereumAddressFromString("0x1111111111111111111111111111111111111111") // Setup: Initialize ERC20 extension @@ -180,7 +181,8 @@ func testSampleRewardsMarketSettled(t *testing.T) func(context.Context, *kwilTes // testSampleRewardsNoOrderBook tests sampling when no orders exist func testSampleRewardsNoOrderBook(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test userAddr := util.Unsafe_NewEthereumAddressFromString("0x2222222222222222222222222222222222222222") // Setup: Initialize ERC20 extension @@ -219,7 +221,8 @@ func testSampleRewardsNoOrderBook(t *testing.T) func(context.Context, *kwilTesti // testSampleRewardsIncompleteOrderBook tests when only bid or ask exists (not both) func testSampleRewardsIncompleteOrderBook(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test userAddr := util.Unsafe_NewEthereumAddressFromString("0x3333333333333333333333333333333333333333") // Setup: Initialize ERC20 extension @@ -263,7 +266,8 @@ func testSampleRewardsIncompleteOrderBook(t *testing.T) func(context.Context, *k // testSampleRewardsSpread5Cents tests dynamic spread = 5¢ (midpoint 36-50 or 50-64) func testSampleRewardsSpread5Cents(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0x4444444444444444444444444444444444444444") // Setup: Initialize ERC20 extension @@ -307,7 +311,8 @@ func testSampleRewardsSpread5Cents(t *testing.T) func(context.Context, *kwilTest // testSampleRewardsSpread4Cents tests dynamic spread = 4¢ (midpoint distance 30-59) func testSampleRewardsSpread4Cents(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0x5555555555555555555555555555555555555555") // Setup: Initialize ERC20 extension @@ -350,7 +355,8 @@ func testSampleRewardsSpread4Cents(t *testing.T) func(context.Context, *kwilTest // testSampleRewardsSpread3Cents tests dynamic spread = 3¢ (midpoint distance 60-79) func testSampleRewardsSpread3Cents(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0x6666666666666666666666666666666666666666") // Setup: Initialize ERC20 extension @@ -393,7 +399,8 @@ func testSampleRewardsSpread3Cents(t *testing.T) func(context.Context, *kwilTest // testSampleRewardsIneligibleMarket tests markets too certain for rewards (distance 80-99) func testSampleRewardsIneligibleMarket(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0x7777777777777777777777777777777777777777") // Setup: Initialize ERC20 extension @@ -436,7 +443,8 @@ func testSampleRewardsIneligibleMarket(t *testing.T) func(context.Context, *kwil // testSampleRewardsSingleLP tests single LP gets 100% of rewards func testSampleRewardsSingleLP(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0x8888888888888888888888888888888888888888") // Setup: Initialize ERC20 extension @@ -500,7 +508,8 @@ func testSampleRewardsSingleLP(t *testing.T) func(context.Context, *kwilTesting. // testSampleRewardsTwoLPs tests reward distribution between two LPs func testSampleRewardsTwoLPs(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0x9999999999999999999999999999999999999999") user2 := util.Unsafe_NewEthereumAddressFromString("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") @@ -580,7 +589,8 @@ func testSampleRewardsTwoLPs(t *testing.T) func(context.Context, *kwilTesting.Pl // testSampleRewardsMultipleLPs tests reward distribution among multiple LPs func testSampleRewardsMultipleLPs(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") user2 := util.Unsafe_NewEthereumAddressFromString("0xcccccccccccccccccccccccccccccccccccccccc") user3 := util.Unsafe_NewEthereumAddressFromString("0xdddddddddddddddddddddddddddddddddddddddd") @@ -673,7 +683,8 @@ func testSampleRewardsMultipleLPs(t *testing.T) func(context.Context, *kwilTesti // testSampleRewardsNoQualifyingOrders tests when orders exist but none qualify (too wide spread) func testSampleRewardsNoQualifyingOrders(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - lastBalancePoint = nil // Reset for this test + lastBalancePoint = nil + lastTrufBalancePoint = nil // Reset for this test user1 := util.Unsafe_NewEthereumAddressFromString("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") user2 := util.Unsafe_NewEthereumAddressFromString("0xffffffffffffffffffffffffffffffffffffffff") @@ -729,6 +740,7 @@ func testSampleRewardsNoQualifyingOrders(t *testing.T) func(context.Context, *kw func testConstraintSellBuyPair(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePoint = nil + lastTrufBalancePoint = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) @@ -793,6 +805,7 @@ func testConstraintSellBuyPair(t *testing.T) func(context.Context, *kwilTesting. func testConstraintNoDuplicates(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { lastBalancePoint = nil + lastTrufBalancePoint = nil err := erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) diff --git a/tests/streams/order_book/settlement_payout_test.go b/tests/streams/order_book/settlement_payout_test.go index 3672b108f..3ccd25a0e 100644 --- a/tests/streams/order_book/settlement_payout_test.go +++ b/tests/streams/order_book/settlement_payout_test.go @@ -39,13 +39,6 @@ func TestSettlementPayouts(t *testing.T) { // Scenario: User creates 100 YES shares, market settles as YES, user receives 98 USDC func testWinnerReceives98PercentPayout(t *testing.T) func(context.Context, *kwilTesting.Platform) error { return func(ctx context.Context, platform *kwilTesting.Platform) error { - // Reset balance point tracker for this test - lastBalancePoint = nil - - // CRITICAL: Initialize ERC20 extension singleton FIRST (like matching_engine_test.go) - err := erc20bridge.ForTestingInitializeExtension(ctx, platform) - require.NoError(t, err) - // Use valid Ethereum address as user userAddr := util.Unsafe_NewEthereumAddressFromString("0x2222222222222222222222222222222222222222") platform.Deployer = userAddr.Bytes() @@ -53,18 +46,22 @@ func testWinnerReceives98PercentPayout(t *testing.T) func(context.Context, *kwil // Setup attestation helper (for signing) helper := attestationTests.NewAttestationTestHelper(t, ctx, platform) - // Create data provider - err = setup.CreateDataProvider(ctx, platform, userAddr.Address()) + // Create data provider FIRST + err := setup.CreateDataProvider(ctx, platform, userAddr.Address()) + require.NoError(t, err) + + // Give user initial balance (injects to DB) - injects BOTH TRUF and USDC + err = giveBalance(ctx, platform, userAddr.Address(), "500000000000000000000") require.NoError(t, err) - // Give user initial balance: 500 USDC - err = giveBalanceChained(ctx, platform, userAddr.Address(), "500000000000000000000") + // Initialize ERC20 extension AFTER balance (loads DB instances to singleton) + err = erc20bridge.ForTestingInitializeExtension(ctx, platform) require.NoError(t, err) - // Get balance before market operations - balanceBefore, err := getBalance(ctx, platform, userAddr.Address()) + // Get USDC balance before market operations (payouts are in USDC) + balanceBefore, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) - t.Logf("User balance before: %s USDC", balanceBefore.String()) + t.Logf("User USDC balance before: %s", balanceBefore.String()) // Use simple stream ID (exactly 32 characters) streamID := "stpayouttest00000000000000000000" // Exactly 32 chars @@ -258,17 +255,17 @@ func testWinnerReceives98PercentPayout(t *testing.T) func(context.Context, *kwil // NEW: Verify user received 98% payout // Expected: 100 shares × $1.00 - 2% fee = 98 USDC - balanceAfter, err := getBalance(ctx, platform, userAddr.Address()) + balanceAfter, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) - t.Logf("User balance after: %s USDC", balanceAfter.String()) + t.Logf("User USDC balance after: %s", balanceAfter.String()) - // Net change should be: -2 USDC (market creation fee) -100 USDC (locked) + 98 USDC (payout) = -4 USDC - // (2 USDC market creation fee + 2 USDC settlement fee = 4 USDC total fees) + // Net USDC change: -100 USDC (locked) + 98 USDC (payout) = -2 USDC (settlement fee only) + // Note: Market creation fee (2 TRUF) is separate from USDC netChange := new(big.Int).Sub(balanceAfter, balanceBefore) - expectedNetChange := new(big.Int).Mul(big.NewInt(-4), big.NewInt(1e18)) // -4 USDC + expectedNetChange := new(big.Int).Mul(big.NewInt(-2), big.NewInt(1e18)) // -2 USDC (settlement fee) require.Equal(t, expectedNetChange.String(), netChange.String(), - "Net change should be -4 USDC (2 market creation + 2 settlement fee)") - t.Logf("✓ Net balance change: %s (4 USDC total fees: 2 creation + 2 settlement)", netChange.String()) + "USDC net change should be -2 (settlement fee only, market creation is in TRUF)") + t.Logf("✓ Net USDC balance change: %s (2 USDC settlement fee)", netChange.String()) return nil } diff --git a/tests/streams/order_book/settlement_test.go b/tests/streams/order_book/settlement_test.go index becbee1cc..ddc704198 100644 --- a/tests/streams/order_book/settlement_test.go +++ b/tests/streams/order_book/settlement_test.go @@ -480,7 +480,7 @@ func testSettleMarketInvalidQueryID(t *testing.T) func(context.Context, *kwilTes []any{99999}, nil) require.NoError(t, err) require.NotNil(t, settleRes.Error, "should error on invalid query_id") - require.Contains(t, settleRes.Error.Error(), "Market does not exist") + require.Contains(t, settleRes.Error.Error(), "Market not found for query_id:") return nil } diff --git a/tests/streams/order_book/split_limit_order_test.go b/tests/streams/order_book/split_limit_order_test.go index ca969d978..ffc7fe2e0 100644 --- a/tests/streams/order_book/split_limit_order_test.go +++ b/tests/streams/order_book/split_limit_order_test.go @@ -465,24 +465,24 @@ func testSplitOrderBalanceChanges(t *testing.T) func(ctx context.Context, platfo }) require.NoError(t, err) - // Get balance before - balanceBefore, err := getBalance(ctx, platform, userAddr.Address()) + // Get USDC balance before (split orders lock USDC, not TRUF) + balanceBefore, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) // Place split order: 75 shares - // Collateral: 75 × 10^18 = 75 TRUF + // Collateral: 75 × 10^18 = 75 USDC err = callPlaceSplitLimitOrder(ctx, platform, &userAddr, int(marketID), 56, 75) require.NoError(t, err) - // Get balance after - balanceAfter, err := getBalance(ctx, platform, userAddr.Address()) + // Get USDC balance after + balanceAfter, err := getUSDCBalance(ctx, platform, userAddr.Address()) require.NoError(t, err) - // Verify balance decreased by 75 TRUF + // Verify USDC balance decreased by 75 (collateral locked) expectedDecrease := toWei("75") actualDecrease := new(big.Int).Sub(balanceBefore, balanceAfter) require.Equal(t, expectedDecrease.String(), actualDecrease.String(), - "balance should decrease by 75 TRUF") + "USDC balance should decrease by 75 (collateral)") return nil } diff --git a/tests/streams/order_book/validate_market_collateral_test.go b/tests/streams/order_book/validate_market_collateral_test.go index 327b8feac..bdbb6a0d5 100644 --- a/tests/streams/order_book/validate_market_collateral_test.go +++ b/tests/streams/order_book/validate_market_collateral_test.go @@ -120,6 +120,7 @@ func testValidMarketAfterMatching(t *testing.T) func(context.Context, *kwilTesti return func(ctx context.Context, platform *kwilTesting.Platform) error { // Reset balance point tracker lastBalancePoint = nil + lastTrufBalancePoint = nil // Initialize ERC20 extension err := erc20bridge.ForTestingInitializeExtension(ctx, platform) @@ -273,24 +274,35 @@ func testMultipleMarketsIsolation(t *testing.T) func(context.Context, *kwilTesti err = giveBalance(ctx, platform, userAddr.Address(), "500000000000000000000") require.NoError(t, err) - // Create market A - queryHashA := [32]byte{} - copy(queryHashA[:], []byte("test_market_A_isolation")) + // Create market A using proper query_components encoding + // Stream IDs must be exactly 32 characters + queryComponentsA, err := encodeQueryComponentsForTests( + userAddr.Address(), + "sttest_market_a_isolation0000000", // 32 chars + "get_record", + []byte{0x01}, + ) + require.NoError(t, err) var marketA_ID int64 - err = callCreateMarket(ctx, platform, &userAddr, queryHashA[:], + err = callCreateMarket(ctx, platform, &userAddr, queryComponentsA, 9999999999, 5, 20, func(row *common.Row) error { marketA_ID = row.Values[0].(int64) return nil }) require.NoError(t, err) - // Create market B - queryHashB := [32]byte{} - copy(queryHashB[:], []byte("test_market_B_isolation")) + // Create market B using proper query_components encoding + queryComponentsB, err := encodeQueryComponentsForTests( + userAddr.Address(), + "sttest_market_b_isolation0000000", // 32 chars + "get_record", + []byte{0x02}, + ) + require.NoError(t, err) var marketB_ID int64 - err = callCreateMarket(ctx, platform, &userAddr, queryHashB[:], + err = callCreateMarket(ctx, platform, &userAddr, queryComponentsB, 9999999999, 5, 20, func(row *common.Row) error { marketB_ID = row.Values[0].(int64) return nil