Skip to content

feat: deposit and withdraw using hoodi#1287

Merged
MicBun merged 3 commits into
mainfrom
hoodiAction
Dec 29, 2025
Merged

feat: deposit and withdraw using hoodi#1287
MicBun merged 3 commits into
mainfrom
hoodiAction

Conversation

@MicBun
Copy link
Copy Markdown
Contributor

@MicBun MicBun commented Dec 29, 2025

resolves: https://github.com/truflation/website/issues/3041

prepare for testnet. Also contain go mod upgrades

Summary by CodeRabbit

  • New Features

    • Added Hoodi testnet support for the ERC20 bridge: wallet balance queries, token bridging with optional custom recipients, multi-deposit flows, and withdrawal fee handling (deduction, leader fee routing, and ledger recording).
  • Tests

    • Added comprehensive Hoodi ERC20 bridge tests covering end-to-end flows, custom recipients, multiple deposits, withdrawal fees, insufficient-balance handling, and ledger verification.
  • Chores

    • Updated kwil-db dependency versions.

✏️ Tip: You can customize this high-level summary in your review settings.

@MicBun MicBun self-assigned this Dec 29, 2025
@MicBun MicBun added the type: feat New feature or request label Dec 29, 2025
@holdex
Copy link
Copy Markdown

holdex Bot commented Dec 29, 2025

Time Submission Status

Member Status Time Action Last Update
MicBun ✅ Submitted 5h Update time Dec 29, 2025, 1:53 PM

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Adds Hoodi testnet support for the ERC20 bridge: bumps kwil-db pseudo-versions, adds a Hoodi extension block, introduces Hoodi-specific SQL actions (info, balance, withdrawal with fee handling), and adds integration tests validating fee, reward, and error flows.

Changes

Cohort / File(s) Summary
Dependency updates
go.mod
Updated github.com/kwilteam/kwil-db and github.com/kwilteam/kwil-db/core pseudo-versions to v0.10.3-0.20251229074241-278c1930ac63 / v0.4.3-0.20251229074241-278c1930ac63.
ERC20 bridge extension
internal/migrations/erc20-bridge/000-extension.sql
Added a Hoodi testnet extension block (chain, escrow, distribution_period) for test environments; left Sepolia block intact.
ERC20 bridge actions (SQL)
internal/migrations/erc20-bridge/001-actions.sql
Added Hoodi-specific public actions: hoodi_get_erc20_bridge_info(), hoodi_wallet_balance(wallet_address TEXT), and hoodi_bridge_tokens($recipient TEXT DEFAULT NULL, $amount TEXT) implementing fee validation, leader-fee transfer, withdrawal, and transaction event recording.
Hoodi integration tests
tests/extensions/erc20/erc20_bridge_hoodi_test.go, tests/streams/hoodi_withdrawal_fee_test.go
Added end-to-end and focused tests covering multiple deposit/withdraw scenarios, fee deduction, leader fee receipt, insufficient-balance cases, and transaction_event ledger checks.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as User (wallet)
  participant Leader as Leader (signer/fee recipient)
  participant DB as Database (SQL actions)
  participant Bridge as Hoodi Bridge Engine

  Note over User,Leader: Initiate withdrawal with fee flow
  User->>Leader: submit withdrawal request (recipient?, amount)
  Leader->>DB: CALL hoodi_bridge_tokens(recipient, amount) with leader signer
  DB->>DB: check wallet balance >= amount + fee
  alt insufficient balance
    DB-->>Leader: RETURN error
  else sufficient
    DB->>Leader: transfer leader fee (internal ledger entry)
    DB->>Bridge: invoke hoodi_bridge.withdraw(recipient, amount)
    Bridge-->>DB: withdrawal result (success/failure)
    DB->>DB: record transaction_event (withdrawal, fee)
    DB-->>Leader: RETURN success + event ID
  end
  Leader-->>User: response (success/error)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • pr-time-tracker
  • williamrusdyputra
  • outerlook

Poem

🐰 I hopped to Hoodi with a curious peep,

New SQL tunnels and tests to keep,
Fees and rewards in tidy rows,
Leaders earning where the river flows,
A carrot-coded bridge — now let it leap! 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: deposit and withdraw using hoodi' accurately describes the main changes: new Hoodi testnet actions for depositing and withdrawing ERC20 tokens, new SQL migrations, and comprehensive test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hoodiAction

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
internal/migrations/erc20-bridge/000-extension.sql (1)

16-26: Parameter inconsistency between sepolia_bridge and hoodi_bridge.

The hoodi_bridge includes distribution_period: '30m' but the sepolia_bridge on lines 16-19 omits this parameter. If distribution_period is intentionally different (relying on a default for sepolia), this is fine, but the inconsistency may cause confusion during maintenance.

Consider adding a comment explaining why sepolia uses the default period or add the explicit parameter for consistency.

🔎 Suggested fix for consistency
 USE erc20 {
     chain: 'sepolia',
-    escrow: '0x502430eD0BbE0f230215870c9C2853e126eE5Ae3'
+    escrow: '0x502430eD0BbE0f230215870c9C2853e126eE5Ae3',
+    distribution_period: '30m'
 } AS sepolia_bridge;
tests/extensions/erc20/erc20_bridge_hoodi_test.go (2)

76-80: Consider adding bounds check or column name verification.

Direct index access to row.Values[6] and row.Values[8] is fragile if the schema changes. While acceptable in tests, consider adding a comment documenting the expected column order or using a more robust approach.

🔎 Suggested improvement for clarity
 		resInfo, errInfo := platform.Engine.Call(engineCtx, platform.DB, TestExtensionAliasHoodi, "info", []any{}, func(row *common.Row) error {
 			if len(row.Values) < 9 {
 				return nil
 			}
+			// Column order: chain(0), escrow(1), epoch_period(2), erc20(3), decimals(4), balance(5), synced(6), synced_at(7), enabled(8)
 			syncedResult = row.Values[6].(bool)  // synced column
 			enabledResult = row.Values[8].(bool) // enabled column
 			return nil
 		})

276-278: Fragile column index access for recipient verification.

Accessing row.Values[4] assumes a specific column order in list_wallet_rewards. If the return schema changes, this will break silently or cause a panic.

🔎 Suggested defensive check
 		r, err = platform.Engine.Call(engineCtx, platform.DB, TestExtensionAliasHoodi, "list_wallet_rewards", []any{TestUserHoodiB, false}, func(row *common.Row) error {
 			rewardRows++
 			// Verify the recipient column contains User B's address
+			require.GreaterOrEqual(t, len(row.Values), 5, "expected at least 5 columns in list_wallet_rewards result")
 			recipientValue, ok := row.Values[4].(string)
 			require.True(t, ok, "recipient should be string")
 			require.True(t, strings.EqualFold(TestUserHoodiB, recipientValue), "recipient should be User B")
 			return nil
 		})
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5755bd3 and a3d45f6.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • go.mod
  • internal/migrations/erc20-bridge/000-extension.sql
  • internal/migrations/erc20-bridge/001-actions.sql
  • tests/extensions/erc20/erc20_bridge_hoodi_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
tests/extensions/erc20/erc20_bridge_hoodi_test.go (3)
deployments/infra/config/node/values.go (1)
  • Values (96-110)
tests/streams/utils/erc20/inject.go (1)
  • InjectERC20Transfer (24-85)
tests/streams/utils/erc20/helper.go (1)
  • GetUserBalance (49-74)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: acceptance-test
  • GitHub Check: lint
🔇 Additional comments (8)
internal/migrations/erc20-bridge/001-actions.sql (3)

30-43: Verify cross-chain fee collection is intentional.

The hoodi_bridge_tokens action checks balance and collects fees from ethereum_bridge (lines 30, 43) but performs the withdrawal from hoodi_bridge (line 49). This pattern is consistent with sepolia_bridge_tokens, suggesting a cross-chain fee model where fees are always collected on Ethereum mainnet.

If this is intentional, consider adding a comment explaining this design decision. If users are expected to have funds on both chains, the error message on line 33-36 should clarify which chain the balance is checked on.

Please confirm this cross-chain fee collection model is correct:

  1. User must have sufficient balance on ethereum_bridge (not hoodi_bridge) to cover withdrawal + fee
  2. Fee is transferred on Ethereum mainnet
  3. Withdrawal is executed on Hoodi testnet

1-22: Hoodi info and balance actions look good.

The hoodi_get_erc20_bridge_info and hoodi_wallet_balance actions correctly use hoodi_bridge and follow the same pattern as the existing Sepolia counterparts.


59-59: Minor: Comment label change.

The comment was updated from "TESTNET" to "SEPOLIA TESTNET (kept for reference)" which improves clarity now that there are multiple testnets.

tests/extensions/erc20/erc20_bridge_hoodi_test.go (4)

1-42: Well-structured test constants and configuration.

Using fake test addresses (0x333... and 0x444...) to avoid conflicts with migration-created instances is a good practice. The constants are well-documented and consistent with the Sepolia test pattern.


58-188: Comprehensive end-to-end test for Hoodi bridge.

The test covers the complete flow: seed instance → inject deposit → verify balance → bridge action → verify pending rewards → finalize epoch → verify wallet rewards. Good use of intermediate assertions to catch failures early.


318-347: Good practice: chaining deposits with prev pointer.

Properly linking the second deposit (pointB) to the first (pointA) via prevPoint ensures correct ordered-sync processing. This accurately simulates production deposit ordering.


201-289: Custom recipient and multiple deposit tests provide good coverage.

Both tests validate important edge cases:

  • Custom recipient ensures withdrawal proofs are generated for the correct address
  • Multiple deposits verifies per-user balance tracking and partial withdrawals

Also applies to: 305-424

go.mod (1)

22-23: Dependency versions for Hoodi testnet support are confirmed.

Both kwil-db and kwil-db/core are updated to the same commit (278c1930ac63a4b3a6c4f7967766e0227b7b5f13), ensuring consistency. The versions exist in the Go module proxy and align with the Hoodi bridge extension tests in the repository.

Comment on lines +44 to +57
// TestERC20BridgeEndToEndHoodi tests the complete bridge flow on Hoodi testnet.
//
// Test flow:
// 1) Initialize Hoodi bridge instance with test alias
// 2) Inject deposit to give user a balance (simulates Hoodi → Kwil deposit)
// 3) Call bridge action to lock tokens and create withdrawal request
// 4) Finalize and confirm epoch (makes withdrawal ready)
// 5) Verify user has wallet rewards (withdrawal proof available)
//
// This validates:
// - Hoodi chain is recognized by kwil-db
// - Deposit listener would work (simulated via injection)
// - Withdrawal flow works end-to-end
// - Epoch management functions correctly
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for tests that call hoodi_bridge_tokens or similar SQL actions
rg -n "hoodi_bridge_tokens|hoodi_get_erc20_bridge_info|hoodi_wallet_balance" --type go tests/

Repository: trufnetwork/node

Length of output: 42


🏁 Script executed:

# Also check the structure of the test file and related test files
find tests/ -name "*hoodi*" -o -name "*bridge*" | head -20

Repository: trufnetwork/node

Length of output: 662


🏁 Script executed:

# Check what SQL files exist and what actions they define
find . -name "*.sql" | grep -i "hoodi\|bridge" | head -20

Repository: trufnetwork/node

Length of output: 230


🏁 Script executed:

# Check the content of 001-actions.sql to see what SQL actions are defined
cat ./internal/migrations/erc20-bridge/001-actions.sql | head -100

Repository: trufnetwork/node

Length of output: 3648


🏁 Script executed:

# Search for patterns where test files execute SQL actions
rg -n "hoodi|bridge" tests/extensions/erc20/ --type go | grep -i "action\|exec\|call" | head -30

Repository: trufnetwork/node

Length of output: 3580


🏁 Script executed:

# Check if the test files actually call SQL-based actions (look for ExecuteAction or similar patterns)
rg -n "ExecuteAction|ExecAction|CallAction" tests/extensions/erc20/erc20_bridge_hoodi_test.go

Repository: trufnetwork/node

Length of output: 42


🏁 Script executed:

# Search more broadly for any calls to *_bridge_tokens in all test files
rg -n "_bridge_tokens" tests/ --type go

Repository: trufnetwork/node

Length of output: 511


🏁 Script executed:

# Check what TestExtensionAliasHoodi refers to - see if it maps to SQL action or extension
rg -n "TestExtensionAliasHoodi" tests/extensions/erc20/ --type go -A 3 -B 3 | head -50

Repository: trufnetwork/node

Length of output: 4631


🏁 Script executed:

# Check the hoodi_test.go file to see the complete test structure
cat tests/extensions/erc20/erc20_bridge_hoodi_test.go | head -150

Repository: trufnetwork/node

Length of output: 6131


🏁 Script executed:

# Search for any tests that might call hoodi_bridge_tokens explicitly or via action execution
rg -n "hoodi_bridge_tokens|hoodi.*bridge.*tokens" . --type go

Repository: trufnetwork/node

Length of output: 42


🏁 Script executed:

# Check if there are withdrawal_fee_test.go or transaction_events_ledger_test.go that test bridge token actions for other chains
# and verify they also test hoodi
cat tests/streams/withdrawal_fee_test.go | head -300

Repository: trufnetwork/node

Length of output: 11798


🏁 Script executed:

# Look at what the engine.Call with "bridge" method does - check the extension definition
find . -name "*.go" -type f | xargs grep -l "func.*bridge.*Hoodi\|HoodiInstance" | head -5

Repository: trufnetwork/node

Length of output: 42


🏁 Script executed:

# Check if there are any hoodi-specific fee or withdrawal tests
rg -n "hoodi.*fee|hoodi.*withdrawal" tests/ --type go

Repository: trufnetwork/node

Length of output: 42


🏁 Script executed:

# Check the transaction_events_ledger_test.go to see if it tests hoodi_bridge_tokens
rg -n "hoodi|bridge_tokens" tests/streams/transaction_events_ledger_test.go --type go | head -30

Repository: trufnetwork/node

Length of output: 186


🏁 Script executed:

# Verify what SQL actions exist for each chain
rg -n "CREATE OR REPLACE ACTION.*bridge_tokens|CREATE OR REPLACE ACTION.*get_erc20_bridge_info" ./internal/migrations/erc20-bridge/ --type sql

Repository: trufnetwork/node

Length of output: 855


SQL action hoodi_bridge_tokens is not tested directly.

The hoodi_test.go file exercises the underlying bridge extension method (via TestExtensionAliasHoodi), not the SQL action hoodi_bridge_tokens defined in 001-actions.sql which includes fee collection logic (40 TRUF withdrawal fee). Other chains (Sepolia, Ethereum) have dedicated test coverage for their SQL actions in withdrawal_fee_test.go and transaction_events_ledger_test.go. Add similar tests for hoodi_bridge_tokens to ensure fee collection and balance validation work correctly at the SQL action level.

🤖 Prompt for AI Agents
tests/extensions/erc20/erc20_bridge_hoodi_test.go around lines 44-57: the review
notes that the SQL action hoodi_bridge_tokens (defined in 001-actions.sql) is
not exercised by the current end-to-end test and must be covered directly to
validate the 40 TRUF withdrawal fee and balance logic; add a unit/integration
test that invokes the hoodi_bridge_tokens SQL action directly (via the test
harness method that executes SQL actions or db.Exec against the test database),
set up a user/account with a deposit balance, call the action with parameters
that trigger a withdrawal, assert the resulting balance decrement equals
amount+40 TRUF fee, verify fee is recorded in the expected fee/treasury ledger
table and any withdrawal record is created, and add at least one failure case
asserting action rejects insufficient-balance attempts; keep test scaffolding
consistent with existing withdrawal_fee_test.go and
transaction_events_ledger_test.go patterns.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/streams/hoodi_withdrawal_fee_test.go (2)

32-36: Package-level mutable state may cause test flakiness.

hoodiPointCounter and hoodiPrevPoint are shared across all tests. If tests run in parallel (via t.Parallel()) or in an unexpected order, this shared state could cause race conditions or unexpected behavior. Consider passing these values through a test context struct or resetting them in each test's setup.


185-187: Consider removing debug logging.

The DEBUG test3: log statement appears to be leftover from development. Either remove it or, if intentionally kept for troubleshooting, ensure consistency by adding similar logging to other tests that reset state.

🔎 Proposed fix
 		// Reset deposit chain for this test to avoid cross-test interference
-		oldPrevPoint := hoodiPrevPoint
 		hoodiPrevPoint = nil
-		t.Logf("DEBUG test3: Reset prev point from %v to nil", oldPrevPoint)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3d45f6 and 83de8e1.

📒 Files selected for processing (2)
  • internal/migrations/erc20-bridge/001-actions.sql
  • tests/streams/hoodi_withdrawal_fee_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/migrations/erc20-bridge/001-actions.sql
🧰 Additional context used
🧬 Code graph analysis (1)
tests/streams/hoodi_withdrawal_fee_test.go (4)
tests/streams/utils/runner.go (1)
  • RunSchemaTest (37-116)
internal/migrations/migration.go (1)
  • GetSeedScriptStatements (60-109)
tests/streams/utils/erc20/inject.go (1)
  • InjectERC20Transfer (24-85)
tests/streams/utils/erc20/helper.go (1)
  • GetUserBalance (49-74)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: acceptance-test
🔇 Additional comments (8)
tests/streams/hoodi_withdrawal_fee_test.go (8)

1-21: LGTM!

Build tag and imports are properly structured for a test file under the kwiltest build constraint.


44-58: LGTM!

The test suite structure follows the established pattern from RunSchemaTest and properly sequences setup before test functions.


60-94: LGTM!

The setup function properly initializes the Hoodi bridge test environment with clear comments explaining the critical initialization steps. Error handling is consistent with proper context wrapping.


96-140: LGTM!

Test properly validates the fee deduction logic with clear assertions and informative log messages. The arithmetic using big.Int ensures precision.


142-171: LGTM!

Good negative test case validating that insufficient balance is correctly rejected with an appropriate error message.


173-227: LGTM on test logic!

The test correctly validates that the leader receives the 40 TRUF fee. Good use of key generation and address derivation for the leader.


292-334: LGTM!

Helper functions giveHoodiBalance and getHoodiBalance are well-implemented with proper error handling and deposit chaining logic.


336-372: LGTM!

The withdrawal action helpers properly set up the transaction context with the leader as proposer and handle both call errors and result errors.

Comment thread tests/streams/hoodi_withdrawal_fee_test.go
Comment thread tests/streams/hoodi_withdrawal_fee_test.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
tests/streams/hoodi_withdrawal_fee_test.go (1)

32-36: Consider consistent global state reset across all tests.

The global hoodiPrevPoint is reset in setupHoodiWithdrawalTestEnvironment and in tests 3 and 4, but not in tests 1 and 2. While the current sequential execution works, inconsistent reset patterns can make test behavior harder to reason about.

Consider either:

  • Resetting hoodiPrevPoint at the start of every test function for explicit isolation, or
  • Documenting why certain tests need fresh chains while others can reuse the global state

Also applies to: 67-67, 189-189, 244-244

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83de8e1 and f647fe7.

📒 Files selected for processing (1)
  • tests/streams/hoodi_withdrawal_fee_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
tests/streams/hoodi_withdrawal_fee_test.go (5)
tests/streams/utils/runner.go (1)
  • RunSchemaTest (37-116)
internal/migrations/migration.go (1)
  • GetSeedScriptStatements (60-109)
deployments/infra/config/node/values.go (1)
  • Values (96-110)
tests/streams/utils/erc20/inject.go (1)
  • InjectERC20Transfer (24-85)
tests/streams/utils/erc20/helper.go (1)
  • GetUserBalance (49-74)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: acceptance-test
  • GitHub Check: lint
🔇 Additional comments (6)
tests/streams/hoodi_withdrawal_fee_test.go (6)

38-45: Previous review comments have been addressed.

Both issues flagged in the previous review have been fixed:

  1. mustParseHoodiBigInt now checks the SetString return value and panics with a descriptive message on parse failure.
  2. Type assertions in the ledger query use safe comma-ok checks and return errors for unexpected types.

Also applies to: 272-295


99-143: LGTM!

The test correctly validates that withdrawing 10 TRUF deducts 50 TRUF total (10 withdrawal + 40 fee). The balance assertions and error messages are clear.


145-174: LGTM!

The test properly validates the insufficient balance error path. The error message assertion ensures the failure reason is clear.


176-230: LGTM!

The test correctly validates that the block leader receives the 40 TRUF withdrawal fee. The explicit state reset and debug logging demonstrate good test hygiene.


232-308: LGTM!

The test validates that withdrawal fees are properly recorded in the transaction ledger. The SQL query explicitly casts fee_amount to TEXT and the type assertions are safe.


312-390: LGTM!

The helper functions are well-structured:

  • giveHoodiBalance properly manages deposit chaining with the global point tracker
  • getHoodiBalance safely parses balance strings
  • callHoodiWithdrawalAction correctly sets up the transaction context with block height 2 (post-setup)

@MicBun MicBun merged commit 8146f5f into main Dec 29, 2025
5 of 6 checks passed
@MicBun MicBun deleted the hoodiAction branch December 29, 2025 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feat New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant