Skip to content

fix(asa-inbox): send-via-inbox with unconditional explicit refs (no simulate) [PERA-4655]#1022

Merged
fmsouza merged 1 commit into
mainfrom
fmsouza/pera-4655-send-followup
Jul 24, 2026
Merged

fix(asa-inbox): send-via-inbox with unconditional explicit refs (no simulate) [PERA-4655]#1022
fmsouza merged 1 commit into
mainfrom
fmsouza/pera-4655-send-followup

Conversation

@fmsouza

@fmsouza fmsouza commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-up to #1020. That PR fixed claiming from the Asset Inbox but QA found sending via inbox still triggers the same generic error. This fixes the send path.

Root cause. #1020 attached explicit ARC-59 resource references (so the group builds without a live simulate) only when summary.inbox_address was set, and otherwise fell back to buildPopulatedGroup (populateAppCallResources → algod POST /v2/transactions/simulate). But a send to a receiver who has no inbox yet — the normal case — gets inbox_address = null from the backend (the ARC-59 router stores the inbox address in a box and returns the zero address until it exists; it is not derivable ahead of creation). So the common send fell straight back onto simulate, which the production algod proxy rejects → the generic banner. Claiming was unaffected because an inbox always exists when you claim, so inbox_address is always present there.

Fix. Build the send group with explicit references unconditionally and always use buildGroup (never simulate). Everything the router needs is derivable client-side:

  • arc59_sendAsset: accountReferences = inbox_address ? [receiver, inbox_address] : [receiver], assetReferences: [assetId], boxReferences: [{ appId, name: decodeAddress(receiver).publicKey }].
  • arc59_optRouterIn: assetReferences: [assetId].

The receiver's inbox is referenced only when it already exists. On a first send the inbox is created inside arc59_sendAsset (inner txn); an account created in-call is available without being pre-referenced, so referencing the (unknown) inbox address is both impossible and unnecessary — [receiver] alone is correct there. The buildPopulatedGroup/simulate fallback is removed from the send path entirely.

Verification — LocalNet, against a simulate-blocked node

I deployed the real ARC-59 router (compiled programs pulled from the live testnet app) to LocalNet and exercised the flows through a custom algod client that blocks POST /v2/transactions/simulate exactly like the prod proxy, signing and submitting real transactions:

  • Reproduced the bug: the pre-fix builder's fresh-receiver send fails at simulate (403) — the QA error.
  • Fresh-receiver send with the fixed refs ([receiver], no inbox) builds with buildGroup, submits, and confirms; the inbox is created and receives the asset.
  • Existing-inbox send needs [receiver, inbox] (verified [receiver]-only fails with unavailable Account), and works with the inbox included.
  • Full round-trip (fresh send → claim) completes end-to-end with simulate blocked throughout.

Unit + suite: useArc59Transaction.test.ts updated (the null-inbox_address case now asserts explicit [receiver] refs and no simulate); asa-inbox 97, transactions 215, claim-assets/messages integration 8, pnpm build all green.

Related Issues

Checklist

  • Have you tested your changes locally? — unit + integration + full build, plus on-chain send/claim round-trip against a simulate-blocked LocalNet node.
  • Have you reviewed the code for any potential issues?
  • Have you documented any necessary changes in the project's documentation? — n/a
  • Have you added any necessary tests for your changes? — updated send-builder tests for the null-inbox (fresh-receiver) explicit-ref path.
  • Have you updated any relevant dependencies? — no dependency changes.

Additional Notes

  • Ops (still relevant): the fix removes the app's dependence on simulate for send and claim, but confirming why the release algod proxy rejects POST /v2/transactions/simulate is still worthwhile — any future simulate-based feature would hit the same wall.
  • Claim robustness (optional follow-up): claim/reject still keep a simulate fallback for a null inbox_address. That path isn't reachable today (an inbox always exists when claiming), but if we want zero simulate dependence everywhere, claim/reject could adopt the same unconditional-explicit-refs approach.

🤖 Generated with Claude Code

@fmsouza
fmsouza requested a review from a team as a code owner July 24, 2026 15:23
@fmsouza
fmsouza merged commit eec6f11 into main Jul 24, 2026
9 checks passed
@fmsouza
fmsouza deleted the fmsouza/pera-4655-send-followup branch July 24, 2026 18:00
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.

2 participants