fix(sdk-coin-near): validate receiving address in NEAR tx builders#9292
Draft
bitgo-ai-agent-dev[bot] wants to merge 1 commit into
Draft
fix(sdk-coin-near): validate receiving address in NEAR tx builders#9292bitgo-ai-agent-dev[bot] wants to merge 1 commit into
bitgo-ai-agent-dev[bot] wants to merge 1 commit into
Conversation
Contributor
Author
|
@claude please review this PR |
bitgo-ai-agent-dev
Bot
force-pushed
the
coins-1221-near-receiver-address-validation
branch
from
July 20, 2026 10:39
69dd36f to
626b648
Compare
…ddresses - Fix receiverId(), ftReceiverId(), and beneficiaryId() in the NEAR transaction builders, which called utils.isValidAddress() but discarded the return value, silently accepting empty, malformed, or over-64-char receiving addresses. Now throw AddressValidationError, matching the existing pattern in sender(). - Add an on-chain recipient existence check in Near.verifyTransaction(): queries the NEAR RPC (view_account) to confirm the recipient account exists before the transaction is signed/broadcast, for all recipient-bearing transaction types except enabletoken (whose recipient is the wallet's own storage/token contract, not a user-supplied destination). Implicit (64-char hex) accounts are exempt, since an unfunded implicit account also reports UNKNOWN_ACCOUNT even though sending to it is how such accounts are created on-chain. Without either fix, a malformed or never-created receiver_id would pass validation and be broadcast, only to fail on-chain with AccountDoesNotExist and burn gas. Ticket: COINS-1221
nayandas190
force-pushed
the
coins-1221-near-receiver-address-validation
branch
from
July 21, 2026 12:52
02715a7 to
00b7175
Compare
nayandas190
marked this pull request as ready for review
July 21, 2026 13:07
abhishekagrawal080
approved these changes
Jul 21, 2026
abhishekagrawal080
left a comment
Contributor
There was a problem hiding this comment.
Looks good to me
nayandas190
marked this pull request as draft
July 21, 2026 13:59
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.
Summary
NEAR transaction builders were not validating the recipient (
receiverId/ftReceiverId/beneficiaryId) address before including it in a transaction. As a result, invalid or non-existent addresses could pass through and be broadcast on-chain, where they'd fail withAccountDoesNotExist, burning gas with no way for the user to have known in advance (COINS-1221).This PR closes that gap in two parts: fixing the address-format validation that was silently being ignored, and adding an on-chain existence check for the recipient before broadcast.
Context
receiverId(),ftReceiverId(), andbeneficiaryId()all calledutils.isValidAddress(accountId)but discarded its boolean return value, so malformed addresses (empty, spaces, special characters, over 64 chars) were silently accepted.sender()in the same file already validated correctly and threw — the receiving-address setters had the same gap and were simply missed.NEAR has two account formats, and only one of them needs an existence check. Named accounts (e.g.
alice.near,pool.f863973.m0) are human-readable and must be explicitly created on-chain via aCreateAccountaction before anything can be sent to them. Implicit accounts (64-char lowercase hex, a raw ed25519 public key) don't need to be pre-created — a transfer to a fresh, never-used implicit account is itself how it gets created on-chain.The reported issue (COINS-1221) was exactly this gap for named accounts: a user submitted a transfer to a syntactically valid but never-created named
receiver_id.isValidAddress()is a purely offline regex/format check — it validates that a string looks like a named account, but has no way to know whether that named account was ever actually created. Because NEAR (unlike EVM, where any address is inherently valid and needs no "creation") distinguishes between well-formed and existing accounts, format validation alone cannot catch this — it requires an on-chain RPC lookup, which is what the second half of this PR adds.Changes
Address format validation (
transactionBuilder.ts,fungibleTokenTransferBuilder.ts,storageDepositTransferBuilder.ts)AddressValidationErrorwhenutils.isValidAddress()returns false, matching the existingsender()pattern.On-chain existence check (
near.ts)doesAccountExist(), reusing the existinggetDataFromNode()/view_accountRPC pattern already used bygetAccountBalance().verifyTransaction()for all recipient-bearing transaction types exceptenabletoken(its recipient is the wallet's own storage/token contract, not a user-supplied destination).UNKNOWN_ACCOUNT, even though sending to it is a valid way to create it on-chain. Only named accounts are existence-checked.Decision log
AddressValidationErroroverBuildTransactionError: it's the purpose-built type for this case and extendsBuildTransactionError, so existing callers catchingBuildTransactionErrorare unaffected.verifyTransaction()is already async and already has the parsed recipient available, so the check lives there instead.enabletoken: a wrong validator/pool address in a staking transaction fails on-chain the same way a bad transfer recipient does.UNKNOWN_ACCOUNT, so exempting it prevents false positives on legitimate first-time transfers to valid implicit accounts.Test plan
receiverId(''), spaces, special chars, and over-64-char addresses throwAddressValidationErrorinTransactionBuilder,TransferBuilder,FungibleTokenTransferBuilder, andStorageDepositTransferBuilderverifyTransaction()succeeds when the named recipient account exists on-chain (mocked RPC)verifyTransaction()throwsRecipient account '...' does not exist on NEARwhen the RPC reportsUNKNOWN_ACCOUNTverifyTransaction()propagates other RPC errors from the existence checkenabletokenrecipients skip the RPC call entirelyTicket: COINS-1221