Skip to content

feat: compare USDT0 and Orchestra outbound routes - #167

Merged
ben-kaufman merged 2 commits into
masterfrom
feat/usdt-orchestra-outbound-20261005
Oct 7, 2026
Merged

ben-kaufman merged 2 commits into
masterfrom
feat/usdt-orchestra-outbound-20261005

Conversation

@ben-kaufman

@ben-kaufman ben-kaufman commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Adds Orchestra as an alternative for outbound USDT payments from Arbitrum One. Existing USDT0 routes remain available. Where both providers support a destination, Core compares expected received USDT against the maximum total debit, including source fees, and chooses the best usable quote. Ties prefer USDT0.

Orchestra adds Base, BNB Smart Chain, Solana and Tron, and competes on Ethereum, Polygon and Plasma. Stable continues through USDT0. Destinations are enabled explicitly by the gateway; all source funding and fees remain in USDT. Orchestra delivery is an estimate: route costs are deducted from the principal and source network fees are additional.

The chosen provider, funding address and recovery ticket are saved with the signed operation before submission. Recovery follows that same payment, including successor orders and verified Arbitrum refunds; it never starts another provider payment after ambiguous submission. Funding and refund receipts are reconciled independently through the existing reorg window.

The gateway keeps provider credentials server-side and validates the quote's pinned route and exact USDT funding transaction. This PR exposes destination discovery, network-specific address validation and bridge details through matching Swift bindings. No migration or application routing framework is added.

QA Notes

Reviewer checks:

cargo fmt --check
cargo test --locked --lib modules::usdt
cargo clippy --locked --lib --tests
  • cargo fmt --all --check passes. Swift bindings were regenerated from the rebased source and pass typechecking.
  • cargo test --locked --lib modules::usdt: 78 passed, one optional live-fork test skipped.
  • cargo clippy --locked --lib --tests: no USDT diagnostics and no new warnings compared with master; existing unrelated repository warnings remain.
  • Tests cover provider selection/fallback, destination discovery, persisted funding identity, delivery recovery, destination validation and canonical refunds/reorgs. Funding reorg coverage verifies restart recovery for both delivered and refunded payments.

Requires the companion synonymdev/bitkit-usdt-service outbound endpoint and its durable ticket-signing secret. Funded Orchestra outbound delivery/refund acceptance and deployment remain outstanding; this PR does not claim those live tests passed.

@ben-kaufman
ben-kaufman marked this pull request as ready for review October 6, 2026 14:38
Base automatically changed from feat/usdt-orchestra-deposits-20261005 to master October 7, 2026 01:33
@ben-kaufman
ben-kaufman force-pushed the feat/usdt-orchestra-outbound-20261005 branch from 53c6b23 to c6c987d Compare October 7, 2026 01:57

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve

Review: diff 14 files.

Findings:
2 inline (1 MEDIUM, 1 LOW)

QA:
No tests ran; the PR description asks for automated cargo checks and leaves funded Orchestra delivery and refund acceptance outstanding.


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

Comment thread src/modules/usdt/orchestra.rs
Comment thread src/modules/usdt/store.rs
@ovitrif

ovitrif commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

@ben-kaufman conflicts and stale unresolved threads

@ben-kaufman
ben-kaufman force-pushed the feat/usdt-orchestra-outbound-20261005 branch from c6c987d to 45ec0e7 Compare October 7, 2026 18:53
@ben-kaufman

Copy link
Copy Markdown
Collaborator Author

@ovitrif Rebased onto current master and pushed 45ec0e7. GitHub now reports no merge conflicts. Both test gaps are covered and the threads are resolved: destination discovery, and funding-reorg recovery across restart for delivered/refunded Orchestra payments.

Validation: 77 USDT tests passed (one optional live-fork test skipped), formatting passed, regenerated Swift bindings typecheck, and Clippy has no new warnings compared with master. CI is running. No live transfers were made for this update.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve

Reaudit: diff 3 files.
No new findings; the rest is in the review.

QA:
No test ran because the description lists automated cargo checks, asks for no manual test or journey, and leaves funded Orchestra live acceptance outstanding.

Replies:

ben-kaufman: @ovitrif Rebased onto current master and pushed 45ec0e7. GitHub now reports no merge conflicts. Both test gaps are covered and the threads are resolved:… (comment)

I checked the added test assertions against both prior findings; both are addressed in 45ec0e7.


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@coreyphillips

Copy link
Copy Markdown
Collaborator

Two independent reviews, nothing blocking a merge.

worth doing, does not block

  • Refund proof accepts any equal-amount USDT transfer into the wallet (src/modules/usdt/wallet.rs:1142). verify_refund (src/modules/usdt/wallet.rs:1123) accepts any canonical USDT Transfer log in the gateway-supplied tx where to == self.address and value == amount. It never checks the sender (the funding address or a known Orchestra refund source). It also never checks that the same refund tx hasn't already been used to settle another transfer. The README says BridgeRefunded requires a canonical receipt paying the refund, so this works as designed only if the gateway is honest. Failure case: the wallet receives an unrelated incoming payment of exactly the refundable amount. A buggy or compromised gateway reports that tx as the refund. The transfer then shows BridgeRefunded and polling stops for good, even though delivery may still be in flight. The same tx can also be reported as the refund for two different transfers. Found by reading the code; I did not reproduce it.
  • Completed delivery amount is not bounded by the sent principal (src/modules/usdt/wallet.rs:1094). For a completed delivery, bridge_delivery only checks that received_amount is greater than 0 (wallet.rs:1094). The quote path rejects received_amount > amount, but the status path doesn't, so a bad gateway response can store and display a received amount larger than what was sent. The refund branch already bounds refund_amount <= transfer.amount, so the delivery branch could apply the same bound.
  • One refund receipt can settle multiple payments (src/modules/usdt/wallet.rs:737). verify_refund accepts any canonical USDT transfer with the reported recipient and amount, without binding it uniquely to the payment. Returning one refund transaction for two same-amount payments marks both BridgeRefunded, although the wallet received one refund. A self-transfer also passes despite returning no funds. Require refund provenance and prevent reuse of a refund transaction.
  • Optional bridge configuration breaks existing Swift callers (src/modules/usdt/wallet.rs:56). Adding bridge_url to the sole constructor removes the existing four-argument Swift initializer. I confirmed that an existing UsdtWallet(address:storagePath:rpcUrl:bundlerUrl:) call now fails typechecking with a missing bridgeUrl argument. Preserve the original constructor and add a separately named bridge-enabled constructor.
  • Public recipient validation disagrees with quote validation (src/modules/usdt/orchestra.rs:195). usdt_validate_recipient accepts the Arbitrum OFT and bridge helper addresses, while quote_transfer rejects both immediately. Callers using the new public validator therefore accept values that cannot be quoted. Apply the Arbitrum exclusions in the shared validator.

nits

  • Every successful delivery poll now rewrites the transfer row (src/modules/usdt/wallet.rs:632). refresh_bridges used to take the operation lock and write only when the status changed (Ok(Ok(status)) if status != previous.status). It now takes the lock and calls update_delivery after every successful lookup, including unchanged USDT0 Bridging results. This is correct but adds lock contention with send, plus a SQLite write roughly every poll interval per in-flight bridge. Skipping the write when updated == previous and refund_block is None would restore the old behaviour.

@ben-kaufman

Copy link
Copy Markdown
Collaborator Author

Thanks, addressed in 7192be6:

  • Refunds require a matching USDT transfer and at least that amount in net credit, so self-transfers and round trips cannot count as refunds. The same refund transaction cannot settle two payments. The claim survives restart and receipt pruning, and is released on a refund reorg.
  • Completed delivery amounts must be positive and no greater than the sent principal.
  • The shared recipient validator now rejects the Arbitrum OFT and helper addresses too.
  • Unchanged delivery polls skip the operation lock and database update.
  • bridgeUrl now defaults to nil in generated Swift bindings (null in Kotlin). This keeps the four-argument Swift call working without adding a second constructor.

The broader refund-provenance concern remains a trust boundary. The current gateway contract does not pin a refund sender, so I have not assumed refunds must come from the funding address. The gateway validates the provider's order against the saved quote and source payment, but Core cannot independently prove that an otherwise valid incoming transfer belongs to that order. This is now explicit in the README. The duplicate-receipt check conservatively allows one payment per refund transaction, including for batched receipts.

Validation: 78 USDT tests passed, one optional live-fork test skipped, formatting passed, both Swift constructor forms typecheck, and all Clippy diagnostics match the previous report in unchanged files. No live transfers were made.

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verdict: ✅ Approve

Reaudit: diff 6 files.
No new findings; the rest is in the review.

QA:
No test ran because the QA Notes list only automated cargo checks, ask for no manual test, and leave funded Orchestra live acceptance outstanding.

Replies:

coreyphillips: Two independent reviews, nothing blocking a merge. worth doing, does not block - Refund proof accepts any equal-amount USDT transfer into the wallet… (comment)

I verified the receipt credit and duplicate claim checks, delivery amount bound, shared recipient exclusions, Swift constructor default and unchanged polling fast path. Refund order attribution remains the gateway trust boundary, now stated explicitly in the README.

ben-kaufman: Thanks, addressed in 7192be6: - Refunds require a matching USDT transfer and at least that amount in net credit, so self-transfers and round trips cannot… (comment)

I verified that refund claims persist through restart and pruning, and release on a refund reorg. Your explanation and the README clarify that Core proves receipt of funds while trusting the gateway for order attribution.


Reviewed by gpt-6.1-sol-high via gh-pr-review-loop skill
Commands: @ovi-reviewer review · test · retest

@ben-kaufman
ben-kaufman merged commit e13d68d into master Oct 7, 2026
5 of 6 checks passed
@ben-kaufman
ben-kaufman deleted the feat/usdt-orchestra-outbound-20261005 branch October 7, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants