Skip to content

fix(abstract-cosmos): verify memo-suffixed recipients against tx memo - #9940

Draft
ralph-bitgo[bot] wants to merge 1 commit into
masterfrom
COINS-1760-memo-recipient-verify
Draft

ralph-bitgo[bot] wants to merge 1 commit into
masterfrom
COINS-1760-memo-recipient-verify

Conversation

@ralph-bitgo

@ralph-bitgo ralph-bitgo Bot commented Oct 12, 2026

Copy link
Copy Markdown

Description

What: CosmosCoin.verifyTransaction in @bitgo/abstract-cosmos compared txParams.recipients against the decoded transaction outputs as raw {address, amount} strings. Recipients using the BitGo combined address+memo convention (address?memoId=X) therefore never matched the plain on-chain address that explainTransaction() reports, and local validation failed with Tx outputs does not match with expected txParams recipients even when the prebuild was correct.

This change normalizes both sides to a common {address, amount, memo?} shape via getAddressDetails before comparing, which also checks the recipient memoId against the transaction memo. The per-coin fix previously applied only in sdk-coin-zeta (COINS-1676) is lifted into the shared base class so TIA and every other Cosmos coin (ATOM, OSMO, SEI, ...) behave consistently; the now-redundant Zeta.verifyTransaction override is removed and TIA regression tests are added.

Why: TIA withdrawals to memo-tagged recipients fail client-side before signing — reproduced on wallet 6ac4da812f35c0437d86b20a4307e41a in COINS-1760. The same base-class comparison backs all Cosmos coins, so any customer withdrawal to a tagged/exchange address written as address?memoId=X fails the same way on every Cosmos coin, not just TIA.

Behavior note: verification is now memo-aware in the fail-closed direction: a transaction that carries a memo whose recipient did not declare ?memoId= is rejected (previously the memo was ignored). A declared ?memoId= must match the transaction memo.

Issue Number

COINS-1760 — https://linear.app/bitgo/issue/COINS-1760/tia-withdrawal-fails-on-testnet-with-recipient-mismatch-error

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • yarn run unit-test scoped to all 17 Cosmos coin modules that depend on abstract-cosmos (asi, atom, baby, bld, coreum, cosmos, cronos, hash, initia, injective, islm, mantra, osmo, rune, sei, tia, zeta): all suites pass (TIA 80, ZETA 79; only pre-existing pending skips).
  • New TIA regression tests in modules/sdk-coin-tia/test/unit/tia.ts using the existing TEST_TX_WITH_MEMO fixture: (1) recipient to?memoId=5 verifies successfully against the memo'd transfer, (2) ?memoId=999999 is rejected, (3) bare recipient against the memo'd tx is rejected.
  • RED/GREEN proven against the reported failure: with the pre-fix cosmosCoin.ts, tests (1) and (3) fail (test 1 with the exact Tx outputs does not match with expected txParams recipients from COINS-1760); with the fix all three pass.
  • The existing Zeta memo tests from the COINS-1676 fix now exercise the base-class implementation and pass unchanged.
  • yarn lint and yarn check-fmt clean in abstract-cosmos, sdk-coin-zeta, sdk-coin-tia.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My code compiles correctly for both Node and Browser environments
  • I have commented my code, particularly in hard-to-understand areas
  • My commits follow Conventional Commits and I have properly described any BREAKING CHANGES
  • The ticket or github issue was included in the commit message as a reference
  • I have made corresponding changes to the documentation and on any new/updated functions and/or methods - jsdoc
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Ticket: COINS-1760

@linear-code

linear-code Bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

COINS-1760

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Unit tests are failing on Node 26.x (Current release line, non-blocking). This is not an LTS version yet, so it does not block merge, but it signals an incompatibility to fix before Node 26.x becomes LTS.

View run

verifyTransaction compared txParams.recipients to the decoded outputs
as raw {address, amount} strings. Recipients using the BitGo combined
address+memo convention (address?memoId=X) therefore never matched the
plain on-chain address reported by explainTransaction(), so every
withdrawal to a memo-tagged recipient failed local validation with
"Tx outputs does not match with expected txParams recipients" even
though the prebuild was correct. This blocked TIA testnet withdrawals
in COINS-1760.

Normalize both sides to a {address, amount, memo?} shape via
getAddressDetails before comparing, which also checks the recipient
memoId against the transaction memo. This lifts the fix previously
applied per-coin in sdk-coin-zeta (COINS-1676) into the shared base
class so TIA and all other Cosmos coins behave consistently. The
now-redundant Zeta override and its unused lodash dependency are
removed, and regression tests cover the memo-suffixed recipient cases
for TIA.

Ticket: COINS-1760
Session-Id: 59955df8-a88a-4a5d-b7b4-b42d2da9596e
Task-Id: a5b99c75-8301-4e58-8dae-43b673a64970
@ralph-bitgo
ralph-bitgo Bot force-pushed the COINS-1760-memo-recipient-verify branch from f6df366 to 74ae57a Compare October 12, 2026 08:34

This branch has not been deployed

No deployments
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