Conversation
…t and refactoring
…and attach resolved Stellar memos on send build path
…nd migrate resolution to TransactionService
…llar-memo-confirm-ui
…t and removing legacy RPC support
…ation, and support numeric memos in UI
|
| * When true, SEP-29 memo-required checks are skipped during local simulation. | ||
| * Used to build a draft envelope for a recoverable RequiresMemo confirmation. | ||
| */ | ||
| skipMemoRequirementCheck?: boolean; |
There was a problem hiding this comment.
see comment above
| ): Promise<void> { | ||
| const { id } = options; | ||
| await resolveInterface(id, false); | ||
| await resolveInterface(id, { confirmed: false }); |
There was a problem hiding this comment.
We don't keep a boolean and read memo from context after resolve because after resolveInterface, the dialog is done; pulling memo from context again is awkward with the snaps interface lifecycle. Returning memo in the resolve payload is more straightforward handoff to confirmSend.
Why not put memo on confirmSend RPC params?
Memo is UI-owned for SEP-29 recovery (add/edit in the dialog). We deliberately don’t add client wire memo / memoType.
So: object result = confirm + optional memo in one shot; cancel uses the same shape for consistency.
| * @param params.destination - The destination address. | ||
| * @param params.destinationAccount - The destination account. | ||
| * @param params.memo - Optional Stellar memo value to attach to the envelope. | ||
| * @param params.skipMemoRequirementCheck - When true, skips SEP-29 memo-required checks. |
There was a problem hiding this comment.
Before this PR, a send to a SEP-29 memo-required destination with no memo failed validation and opened a dismiss-only error confirmation — user couldn’t fix the memo in-flow.
We still need a built envelope to open the normal send confirm (fees, scan, etc.). So after RequiresMemoException, confirmSend rebuilds once with skipMemoRequirementCheck: true to skip only the memo-required check and open a recoverable confirm with Add/Update memo.
That flag is not used for signing. After the user adds a memo (context.memo), refresh / post-confirm rebuild run without the flag so SEP-29 is enforced again before sign.
…ror handling tests
stanleyyconsensys
left a comment
There was a problem hiding this comment.
left batch 1 comments
| * @param value - Raw draft from the confirmation UI (may include whitespace). | ||
| * @returns Locale error key, or `null` when empty/whitespace or valid. | ||
| */ | ||
| export function getMemoDraftValidationError( |
There was a problem hiding this comment.
not sure if we should put it here, it is similar to resolveStellarMemo
| * @returns The transaction and whether memo recovery UI should be shown. | ||
| * @throws {UserRejectedRequestError} After the pre-submit error dialog is dismissed. | ||
| */ | ||
| async #buildSendTransactionForConfirm(params: { |
There was a problem hiding this comment.
i think it is a bit repeat
all we wanna is to skip memo require at first time
and then return to caller , we need memo
why not just skip it and build it
and then use assertMemoWhenDestinationRequires to validate ?
| if (requiresMemoRecovery && !confirmedMemo) { | ||
| return { | ||
| valid: false, | ||
| errors: [{ code: MultiChainSendErrorCodes.Invalid }], | ||
| }; | ||
| } |
There was a problem hiding this comment.
you dont need it, refreshTransactionAfterConfirmation will do the job
| if (requiresMemoRecovery && !confirmedMemo) { | |
| return { | |
| valid: false, | |
| errors: [{ code: MultiChainSendErrorCodes.Invalid }], | |
| }; | |
| } |
| const trimmedMemo = dialogResult.memo?.trim(); | ||
| const confirmedMemo = trimmedMemo === '' ? undefined : trimmedMemo; |
There was a problem hiding this comment.
nit:
|| allow "" to undefined
?? not allow
| const trimmedMemo = dialogResult.memo?.trim(); | |
| const confirmedMemo = trimmedMemo === '' ? undefined : trimmedMemo; | |
| const trimmedMemo = dialogResult.memo?.trim() || undefined |
| // Keep the original request so Add/Update memo still works on this | ||
| // dead-end validation dialog (no localSimulation / refresh). | ||
| request, |
There was a problem hiding this comment.
display error is not necessary to own the request, becoz we skip the memo error , this displayDialogWithErrorMessage only handle non-memo error
| assetId: KnownCaip19AssetIdOrSlip44Id; | ||
| destination: string; | ||
| memo?: string; | ||
| skipMemoRequirementCheck?: boolean; |
There was a problem hiding this comment.
i would prefer
skipExceptions: TransactionValidationException[]
it is more flexible
0d0005c to
d7fc796
Compare


Explanation
Today, when a Stellar send destination requires a memo (SEP-29
RequiresMemo), validation fails before the user can fix it in the send confirmation UI. There is no way to open a draft confirmation, edit a memo, and continue to a successful rebuild/sign.This PR adds the confirm-UI recovery path on top of memo attach (#289) and recoverable refresh (#291):
Screen.Recording.2026-09-17.at.10.36.22.mov
RequiresMemo, rebuild a draft send withskipMemoRequirementCheck: trueand open the confirmation dialog with a banner / Add–Update memo flow (MemoEdit).context.memo), not on clientconfirmSendwire params (memo/memoType).{ confirmed, memo? }. Live refresh treatsRequiresMemoas recoverable so saving a memo can restart validation/security scanning without nullingsecurityScanRequest.Memo resolution uses
resolveStellarMemofrom the attach path: all-digit uint64 → memoid, otherwise text (≤ 28 UTF-8 bytes). Draft UI validation follows the same rules viagetMemoDraftValidationErrorinapi/memo.Depends on / must land after: #289 (memo attach /
resolveStellarMemo) and #291 (recoverable confirmation refresh). This PR merges both and adds the UI +context.memowiring.References
Requirements
Related to WPN-2033 / WPN-2034 (SEP-29 memo recovery)
Checklist