fix(asa-inbox): send-via-inbox with unconditional explicit refs (no simulate) [PERA-4655]#1022
Merged
Merged
Conversation
…ly, drop simulate fallback [PERA-4655]
wjbeau
approved these changes
Jul 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 whensummary.inbox_addresswas set, and otherwise fell back tobuildPopulatedGroup(populateAppCallResources→ algodPOST /v2/transactions/simulate). But a send to a receiver who has no inbox yet — the normal case — getsinbox_address = nullfrom 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 ontosimulate, which the production algod proxy rejects → the generic banner. Claiming was unaffected because an inbox always exists when you claim, soinbox_addressis always present there.Fix. Build the send group with explicit references unconditionally and always use
buildGroup(neversimulate). 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. ThebuildPopulatedGroup/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/simulateexactly like the prod proxy, signing and submitting real transactions:simulate(403) — the QA error.[receiver], no inbox) builds withbuildGroup, submits, and confirms; the inbox is created and receives the asset.[receiver, inbox](verified[receiver]-only fails withunavailable Account), and works with the inbox included.Unit + suite:
useArc59Transaction.test.tsupdated (the null-inbox_addresscase now asserts explicit[receiver]refs and no simulate); asa-inbox 97, transactions 215, claim-assets/messages integration 8,pnpm buildall green.Related Issues
Checklist
Additional Notes
simulatefor send and claim, but confirming why the release algod proxy rejectsPOST /v2/transactions/simulateis still worthwhile — any future simulate-based feature would hit the same wall.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