@vultisig/core-chain 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/chains/solana/spl/verification.d.ts +38 -0
  3. package/dist/chains/solana/spl/verification.d.ts.map +1 -0
  4. package/dist/chains/solana/spl/verification.js +45 -0
  5. package/dist/chains/solana/spl/verification.js.map +1 -0
  6. package/dist/chains/solana/spl/verifiedRegistry.d.ts +34 -0
  7. package/dist/chains/solana/spl/verifiedRegistry.d.ts.map +1 -0
  8. package/dist/chains/solana/spl/verifiedRegistry.js +65 -0
  9. package/dist/chains/solana/spl/verifiedRegistry.js.map +1 -0
  10. package/dist/chains/ton/failure.d.ts +70 -0
  11. package/dist/chains/ton/failure.d.ts.map +1 -0
  12. package/dist/chains/ton/failure.js +170 -0
  13. package/dist/chains/ton/failure.js.map +1 -0
  14. package/dist/chains/ton/jetton/symbol.d.ts +3 -5
  15. package/dist/chains/ton/jetton/symbol.d.ts.map +1 -1
  16. package/dist/chains/ton/jetton/symbol.js +5 -58
  17. package/dist/chains/ton/jetton/symbol.js.map +1 -1
  18. package/dist/chains/ton/jetton/verification.d.ts +1 -1
  19. package/dist/chains/ton/jetton/verification.d.ts.map +1 -1
  20. package/dist/chains/ton/jetton/verification.js +3 -3
  21. package/dist/chains/ton/jetton/verification.js.map +1 -1
  22. package/dist/chains/ton/jetton/verifiedRegistry.d.ts +1 -1
  23. package/dist/chains/ton/jetton/verifiedRegistry.d.ts.map +1 -1
  24. package/dist/chains/ton/jetton/verifiedRegistry.js +3 -3
  25. package/dist/chains/ton/jetton/verifiedRegistry.js.map +1 -1
  26. package/dist/chains/ton/wallet.d.ts +50 -0
  27. package/dist/chains/ton/wallet.d.ts.map +1 -0
  28. package/dist/chains/ton/wallet.js +54 -0
  29. package/dist/chains/ton/wallet.js.map +1 -0
  30. package/dist/chains/ton/walletV5R1.d.ts +27 -0
  31. package/dist/chains/ton/walletV5R1.d.ts.map +1 -0
  32. package/dist/chains/ton/walletV5R1.js +53 -0
  33. package/dist/chains/ton/walletV5R1.js.map +1 -0
  34. package/dist/coin/coingecko/coingeckoToken.d.ts +11 -0
  35. package/dist/coin/coingecko/coingeckoToken.d.ts.map +1 -1
  36. package/dist/coin/coingecko/getCoingeckoId.d.ts +10 -0
  37. package/dist/coin/coingecko/getCoingeckoId.d.ts.map +1 -1
  38. package/dist/coin/coingecko/getCoingeckoId.js +30 -1
  39. package/dist/coin/coingecko/getCoingeckoId.js.map +1 -1
  40. package/dist/coin/find/resolvers/solana/index.d.ts +22 -1
  41. package/dist/coin/find/resolvers/solana/index.d.ts.map +1 -1
  42. package/dist/coin/find/resolvers/solana/index.js +79 -11
  43. package/dist/coin/find/resolvers/solana/index.js.map +1 -1
  44. package/dist/coin/jupiter/api.d.ts +18 -0
  45. package/dist/coin/jupiter/api.d.ts.map +1 -0
  46. package/dist/coin/jupiter/api.js +52 -0
  47. package/dist/coin/jupiter/api.js.map +1 -0
  48. package/dist/coin/jupiter/token.d.ts +4 -0
  49. package/dist/coin/jupiter/token.d.ts.map +1 -1
  50. package/dist/coin/jupiter/token.js.map +1 -1
  51. package/dist/coin/token/metadata/resolvers/solana.d.ts +3 -1
  52. package/dist/coin/token/metadata/resolvers/solana.d.ts.map +1 -1
  53. package/dist/coin/token/metadata/resolvers/solana.js +10 -6
  54. package/dist/coin/token/metadata/resolvers/solana.js.map +1 -1
  55. package/dist/coin/tokenSymbol.d.ts +10 -0
  56. package/dist/coin/tokenSymbol.d.ts.map +1 -0
  57. package/dist/coin/tokenSymbol.js +62 -0
  58. package/dist/coin/tokenSymbol.js.map +1 -0
  59. package/dist/publicKey/address/deriveAddress.d.ts +8 -1
  60. package/dist/publicKey/address/deriveAddress.d.ts.map +1 -1
  61. package/dist/publicKey/address/deriveAddress.js +5 -1
  62. package/dist/publicKey/address/deriveAddress.js.map +1 -1
  63. package/dist/publicKey/address/getChainAddress.d.ts +4 -1
  64. package/dist/publicKey/address/getChainAddress.d.ts.map +1 -1
  65. package/dist/publicKey/address/getChainAddress.js +2 -2
  66. package/dist/publicKey/address/getChainAddress.js.map +1 -1
  67. package/dist/security/blockaid/tx/simulation/api/core.d.ts +17 -0
  68. package/dist/security/blockaid/tx/simulation/api/core.d.ts.map +1 -1
  69. package/dist/security/blockaid/tx/simulation/api/core.js +58 -41
  70. package/dist/security/blockaid/tx/simulation/api/core.js.map +1 -1
  71. package/dist/swap/general/GeneralSwapQuote.d.ts +10 -0
  72. package/dist/swap/general/GeneralSwapQuote.d.ts.map +1 -1
  73. package/dist/swap/general/swapkit/api/getSwapKitQuote.d.ts.map +1 -1
  74. package/dist/swap/general/swapkit/api/getSwapKitQuote.js +40 -14
  75. package/dist/swap/general/swapkit/api/getSwapKitQuote.js.map +1 -1
  76. package/dist/swap/native/NativeSwapQuote.d.ts +6 -1
  77. package/dist/swap/native/NativeSwapQuote.d.ts.map +1 -1
  78. package/dist/tx/broadcast/resolvers/ton.d.ts.map +1 -1
  79. package/dist/tx/broadcast/resolvers/ton.js +8 -0
  80. package/dist/tx/broadcast/resolvers/ton.js.map +1 -1
  81. package/dist/tx/fee/evm/clampEvmPriorityFee.d.ts +10 -4
  82. package/dist/tx/fee/evm/clampEvmPriorityFee.d.ts.map +1 -1
  83. package/dist/tx/fee/evm/clampEvmPriorityFee.js +35 -14
  84. package/dist/tx/fee/evm/clampEvmPriorityFee.js.map +1 -1
  85. package/dist/tx/fee/evm/evmGasLimit.d.ts +29 -0
  86. package/dist/tx/fee/evm/evmGasLimit.d.ts.map +1 -1
  87. package/dist/tx/fee/evm/evmGasLimit.js +47 -17
  88. package/dist/tx/fee/evm/evmGasLimit.js.map +1 -1
  89. package/dist/tx/fee/evm/gasPrice.d.ts +4 -0
  90. package/dist/tx/fee/evm/gasPrice.d.ts.map +1 -0
  91. package/dist/tx/fee/evm/gasPrice.js +4 -0
  92. package/dist/tx/fee/evm/gasPrice.js.map +1 -0
  93. package/dist/tx/fee/evm/maxPriorityFeePerGas.d.ts +8 -0
  94. package/dist/tx/fee/evm/maxPriorityFeePerGas.d.ts.map +1 -1
  95. package/dist/tx/fee/evm/maxPriorityFeePerGas.js +30 -1
  96. package/dist/tx/fee/evm/maxPriorityFeePerGas.js.map +1 -1
  97. package/dist/tx/status/index.d.ts +2 -2
  98. package/dist/tx/status/index.d.ts.map +1 -1
  99. package/dist/tx/status/resolver.d.ts +15 -0
  100. package/dist/tx/status/resolver.d.ts.map +1 -1
  101. package/dist/tx/status/resolvers/ton.d.ts +5 -1
  102. package/dist/tx/status/resolvers/ton.d.ts.map +1 -1
  103. package/dist/tx/status/resolvers/ton.js +8 -29
  104. package/dist/tx/status/resolvers/ton.js.map +1 -1
  105. package/package.json +41 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # @vultisig/core-chain
2
2
 
3
+ ## 5.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2325](https://github.com/vultisig/vultisig-sdk/pull/2325) [`7c95286`](https://github.com/vultisig/vultisig-sdk/commit/7c9528622702a1e6f10cd5ff72017779d25e509a) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - EVM fee quotes now size the gas reserve from the transaction itself instead of a flat 600k floor. An aggregator swap is signed with the larger of the route's own gas and 1.5x its simulation (1.5x the 600k default when it cannot be simulated, e.g. a token route quoted before its allowance exists); a THORChain or Maya swap deposit (any transaction carrying the native swap payload) takes a fixed 120k; a plain transfer, including a memo-carrying vault deposit that carries no swap payload, takes its simulation raised to a per-chain floor with no inflation (when it cannot be simulated, that floor plus the intrinsic cost of its memo calldata); a dApp or other contract call keeps 1.5x headroom over its simulation. Base-fee headroom drops from 50% to 20% (32% for swaps), legacy-priced chains (BSC) are priced from `eth_gasPrice` with no tip, and the tip is the highest recent 5th-percentile reward from `eth_feeHistory`, capped at the gas price, with per-chain floors (1 gwei on tip-auction chains, 30 gwei on Polygon, 20 wei on OP-stack rollups) and a zero tip on Arbitrum, Mantle and Robinhood.
8
+
9
+ `getEvmFeeQuote`'s `minimumGasLimit` now only raises the value that stands in for a failed simulation and never a successful estimate. `getEvmTransferGasLimit`, `getEvmContractCallGasLimit` and `evmRouterDepositGasLimit` are exported from `@vultisig/core-chain/tx/fee/evm/evmGasLimit`, and `getEvmGasPrice` from `@vultisig/core-chain/tx/fee/evm/gasPrice`.
10
+
11
+ - [#2328](https://github.com/vultisig/vultisig-sdk/pull/2328) [`741272f`](https://github.com/vultisig/vultisig-sdk/commit/741272f8d3872afdf12d9487d2121dee04c2f363) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - Move `slippage_bps` on a native swap quote from the top level into `fees`, where THORChain and MayaChain actually send it.
12
+
13
+ `NativeSwapQuote.slippage_bps` was never populated: THORChain's `QuoteSwapResponse` has no such property, and its `QuoteFees` schema declares `slippage_bps` as a required integer. MayaChain's spec agrees. Because the field was optional, every consumer reading it silently got `undefined` rather than a compile error — which is how the price-impact row went missing in the desktop app and extension.
14
+
15
+ `NativeSwapFees` now carries `slippage_bps?: number`, and the phantom top-level field is gone so the wrong read cannot compile. This is a type-only change; `getNativeSwapQuote` already spreads the response through verbatim, so no runtime behavior changes.
16
+
17
+ Note that `slippage_bps` is the price impact alone and is not interchangeable with the neighbouring `total_bps`, which is the total fee relative to the amount out.
18
+
19
+ - [#2331](https://github.com/vultisig/vultisig-sdk/pull/2331) [`982d464`](https://github.com/vultisig/vultisig-sdk/commit/982d4645467272f32e33a2df20883c1a7171a7ee) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - Publish the SwapKit swap-fee work to the core packages that hold it. [#2315](https://github.com/vultisig/vultisig-sdk/issues/2315) landed
20
+ `swap_fee` on `SwapKitSwapPayload`, the `getKeysignSwapFeeFields` reader, and the
21
+ `sub_provider` route tag in `@vultisig/core-mpc`, plus the transfer-route fee
22
+ resolution in `@vultisig/core-chain` — but its changeset named only
23
+ `@vultisig/sdk`, so neither core package was versioned and the release skipped
24
+ both. Clients that consume `@vultisig/core-mpc` directly, rather than through
25
+ `@vultisig/sdk`, cannot reach the new fee group or its reader until these are
26
+ republished.
27
+
28
+ - [#2339](https://github.com/vultisig/vultisig-sdk/pull/2339) [`95cf397`](https://github.com/vultisig/vultisig-sdk/commit/95cf39722b8a4d3e197b26bcef9983ecef6c3703) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - feat(solana): verified / unverified / scam classification for SPL tokens, and verified-only discovery
29
+
30
+ Solana discovery kept a mint when a price id could be found for it. That hid legitimate tokens that simply had no CoinGecko listing, and did nothing about the airdropped counterfeits and zero-decimal spam a Solana wallet accumulates — a priced impostor was auto-added, an unpriced real token was not, and neither carried a label.
31
+
32
+ `findSolanaCoins` now returns **verified mints only**, priced or not, mirroring TON. Zero-balance token accounts are skipped, Jupiter's search endpoint answers for a hundred mints per call instead of one call per token, CoinGecko ids come from its on-chain multi-token endpoint thirty mints per call, decimals come from the token account itself, and curated metadata wins for tokens we ship ourselves. When Jupiter cannot be reached, listed mints are still discovered from the registry's metadata; a failed price-id lookup, by contrast, fails the round instead of saving the token without a price id for good. Unverified and scam mints can still be added by hand, where the UI labels them.
33
+
34
+ Verification lives in `@vultisig/core-chain/chains/solana/spl/verification`. The registry of verified mints (`chains/solana/spl/verifiedRegistry`) merges our curated Solana tokens with Jupiter's verified list, fetched once an hour and degrading to the curated list alone when unreachable. `resolveSolanaTokenVerification` is pure: a listed mint — or one Jupiter itself flags verified — is `verified`; an unlisted mint is `scam` when its symbol or name collapses onto a verified token's, and `unverified` otherwise. `getSolanaTokenVerification({ id, ticker })` is the one-call form for token rows and approval cards; a listed mint is answered from the registry alone, any other mint is judged by what it claims on Jupiter, falling back to the local ticker offline. The tiers are the chain-agnostic `TokenVerification` type.
35
+
36
+ The symbol normaliser behind the counterfeit heuristic moves to `@vultisig/core-chain/coin/tokenSymbol` as `normalizeTokenSymbol`; `chains/ton/jetton/symbol` keeps exporting `normalizeJettonSymbol` as an alias. `coin/jupiter/api` gains `getJupiterTokens` (batched, keyed by mint, filtered to the mints asked for) and `getJupiterVerifiedTokens`, `SolanaJupiterToken` carries Jupiter's `isVerified` flag and `tags`, and `coin/coingecko/getCoingeckoId` gains the batched `getSolanaCoingeckoIds`. `getSolanaTokenMetadata` now fails with a clear error for a mint Jupiter does not index instead of a `TypeError`.
37
+
38
+ ### Patch Changes
39
+
40
+ - [#2002](https://github.com/vultisig/vultisig-sdk/pull/2002) [`2e1ed70`](https://github.com/vultisig/vultisig-sdk/commit/2e1ed704ccd0a360eaf760f1258c7cd8a4401e22) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Net Solana Blockaid simulation diffs by resolved mint (native SOL and the WSOL mint share a bucket) before classifying the result as a `swap` or `transfer`. The wrap-then-spend case previously surfaced as a bogus "SOL to WSOL swap" whose destination amount was only the token account's rent-exempt residual, at up to ~29x smaller than the amount actually leaving. The reverse unwrap/close-account case now rejects the receive-only result instead of inventing a transfer. The parser now nets same-mint legs and only classifies as a swap when two distinct mints remain.
41
+
42
+ ## 5.2.0
43
+
44
+ ### Minor Changes
45
+
46
+ - [#2306](https://github.com/vultisig/vultisig-sdk/pull/2306) [`a5def09`](https://github.com/vultisig/vultisig-sdk/commit/a5def098e06cf7269174eb6840dc516d78ec55f9) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - feat(ton): explain TON failures in plain language — seqno replay, expired deadline, fees, and the rest
47
+
48
+ A TON send that fails today comes back as an opaque `exitcode=133` in a toncenter rejection, or as a bare `error` status with nothing attached. Those two codes — 133 (W5) / 33 (v4) for a replayed seqno and 136 / 36 for an expired `valid_until`, which is almost always a device clock that drifted — dominate TON support load, and the fix is different for each.
49
+
50
+ `@vultisig/core-chain/chains/ton/failure` maps a failure to a `TonTxFailure`: a stable `reason` (`seqno-mismatch`, `expired`, `invalid-signature`, `wallet-id-mismatch`, `insufficient-funds`, `out-of-gas`, `invalid-destination`, `not-enough-jettons`, `jetton-unauthorized`, `action-failed`, `action-partially-failed`, `aborted`, `contract-rejected`), the phase it came from, the raw exit code, and an English `message` that says what happened and what to do ("make sure your device's date and time are set automatically, then send it again"; "keep about 0.05 TON spare for fees"). Compute-phase codes cover wallet v3/v4 and W5 alike, the TVM out-of-gas codes and the standard jetton wallet's 705/706 (707 and 709 stay generic: the reference contract reuses each for two unrelated checks); action-phase codes distinguish 36 (invalid destination) from the wallet's 36 (expired), and read 37 as not enough TON. Action code 40 stays generic on purpose: it reads "not enough funds, the message is too large, or its Merkle depth is too big", so it is a funding failure only when the node's own `no_funds` flag backs it, and any other unnamed code is treated the same way.
51
+
52
+ A generic action-phase failure only claims "nothing was sent" when the node reports `msgs_created: 0`. Every Vultisig TON send goes out with `IGNORE_ERRORS`, under which a failing action is skipped and the rest still leave, so a batch can lose one transfer and deliver the others — telling that user to retry is how a transfer goes out twice. Without that evidence the failure is `action-partially-failed`, whose message says at least one transfer did not happen, that others may have, and to check the transaction history before sending again.
53
+
54
+ `TxStatusResult` gains an optional `failure?: TxFailureInfo` (`reason`, `message`, `exitCode?`, `phase?`), which the TON status resolver now fills for every `error`. `broadcastTonTx` classifies a wallet-contract refusal at broadcast time: the failed result's `cause` is a `TonBroadcastRejectedError` carrying the same `failure`, its `message` is the human explanation, the original toncenter error stays in `cause`, and the refusal is marked non-retryable — resending the same bytes can only be refused again. A seqno refusal whose message is already on chain (a co-signer broadcast first) is still accepted through the existing hash verification. The types, `getTonTxFailure`, `parseTonBroadcastRejection` and `TonBroadcastRejectedError` are re-exported from `@vultisig/sdk`, including its React Native entry — RN broadcasts TON itself and its `broadcastTonTx` rejects with toncenter's raw `exitcode=<n>` text.
55
+
56
+ - [#2299](https://github.com/vultisig/vultisig-sdk/pull/2299) [`bcca32c`](https://github.com/vultisig/vultisig-sdk/commit/bcca32c885066e2bf224aa37d6666c68a3684956) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - feat(ton): W5 (wallet v5r1) support as an explicit per-account opt-in
57
+
58
+ Every Vultisig TON account has been hard-pinned to the V4R2 wallet contract. W5 is the default for new wallets in Tonkeeper and Telegram Wallet and is the gateway to what users now expect from TON — up to 255 messages per request instead of 4, lower fees, and relayer-paid ("gasless") transactions. WalletCore has supported it for a while; nothing here used it.
59
+
60
+ A W5 wallet is a _different address_ for the same key, with its own balance, so this is not a switch: V4R2 stays the default everywhere and W5 is selected per account.
61
+
62
+ - `@vultisig/core-chain/chains/ton/wallet` (new): `TonWalletVersion` (`'v4r2' | 'v5r1'`), `deriveTonAddress` for either contract, `resolveTonWalletVersion` to tell which contract an address is for a key, the W5 mainnet wallet id, and the per-contract message limits.
63
+ - `vault.setTonWalletVersion('v5r1')` selects which of the key's two TON accounts the vault acts on: `send`, balances, swaps, fee estimation and every other address lookup follow it, so one selected account is used consistently. `vault.address(chain, { tonWalletVersion })` — like `deriveAddress` / `getChainAddress` / `deriveAddressFromKeys` — names a contract for a single lookup without changing the selection, so a client can show both accounts side by side for a migration flow.
64
+ - Balance cache keys name a chain and an asset, not an account, so switching between the two accounts drops the balance scope and any fetch already in flight for the old account is neither cached nor announced: it still answers its own caller, but `balanceUpdated` carries no account identity, so emitting it after a switch would credit the old account's balance to the new one.
65
+ - The keysign signing-input resolver derives the contract from the sender address — the payload has no wallet-version field, and every co-signer reaches the same answer from the shared vault key — and refuses an address that is neither of the key's wallets rather than assuming V4R2. W5 requests carry `IGNORE_ACTION_PHASE_ERRORS`, which the W5 code requires of every external action (its replay protection) and WalletCore enforces; the TON status resolver's action-phase check covers the blindness that flag would otherwise cause. Message counts are capped per contract.
66
+ - The RN-safe builders (`buildTonSendTx`, `buildTonJettonTransferTx`, `buildTonTxFromSigningPayload`, `deriveTonAddress`, `prepareJettonTransferTxFromKeys`) take `walletVersion`; W5 uses the `signed_external` request layout with the signature appended, byte-identical to WalletCore. New `buildV5R1Wallet` / `TON_V5R1_WALLET_ID` alongside the V4R2 helpers.
67
+
68
+ Golden vectors (`testdata/cross-encoder-golden/ton-w5-*.json`) pin the W5 pre-images and are verified against real WalletCore, including the full signed external message. Client-side migration UI (show both accounts, move funds, reconnect dApps) is separate work per platform.
69
+
3
70
  ## 5.1.0
4
71
 
5
72
  ### Minor Changes
@@ -0,0 +1,38 @@
1
+ import { TokenVerification } from '@vultisig/core-chain/coin/tokenVerification';
2
+ import { SolanaVerifiedTokenRegistry } from './verifiedRegistry.js';
3
+ type ResolveSolanaTokenVerificationInput = {
4
+ /** Mint address. */
5
+ address: string;
6
+ symbol?: string;
7
+ name?: string;
8
+ /** Jupiter's own verification flag for the mint, when known. */
9
+ isVerified?: boolean | null;
10
+ registry: SolanaVerifiedTokenRegistry;
11
+ };
12
+ /**
13
+ * Classifies a mint against the verified registry. A listed mint is `verified`
14
+ * whatever it calls itself, and so is one Jupiter itself marks verified — that
15
+ * covers a listing newer than the registry's hourly refresh and the degraded,
16
+ * curated-only registry. An unlisted mint is `scam` when its symbol or name
17
+ * collapses (see `normalizeTokenSymbol`) onto a verified token's symbol or
18
+ * name — the fake-USDT pattern, where the counterfeit is only distinguishable
19
+ * by address. Anything else is `unverified`.
20
+ */
21
+ export declare const resolveSolanaTokenVerification: ({ address, symbol, name, isVerified, registry, }: ResolveSolanaTokenVerificationInput) => TokenVerification;
22
+ type GetSolanaTokenVerificationInput = {
23
+ /** Mint address. */
24
+ id: string;
25
+ /** Ticker already known locally, used when Jupiter has no entry for the mint. */
26
+ ticker?: string;
27
+ };
28
+ /**
29
+ * Verification tier for one mint, for token rows and approval cards. A mint the
30
+ * registry lists is verified without any further lookup, so a token list full
31
+ * of legitimate tokens costs no Jupiter calls. For any other mint the symbol
32
+ * and name are read from Jupiter so a counterfeit is judged by what it actually
33
+ * claims to be, not by the ticker stored locally; falls back to that ticker
34
+ * when the lookup fails, so the label still renders offline.
35
+ */
36
+ export declare const getSolanaTokenVerification: ({ id, ticker, }: GetSolanaTokenVerificationInput) => Promise<TokenVerification>;
37
+ export {};
38
+ //# sourceMappingURL=verification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/spl/verification.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAA;AAG/E,OAAO,EAAkC,2BAA2B,EAAE,MAAM,oBAAoB,CAAA;AAEhG,KAAK,mCAAmC,GAAG;IACzC,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,2BAA2B,CAAA;CACtC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,GAAI,kDAM5C,mCAAmC,KAAG,iBAUxC,CAAA;AAED,KAAK,+BAA+B,GAAG;IACrC,oBAAoB;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GAAU,iBAG9C,+BAA+B,KAAG,OAAO,CAAC,iBAAiB,CAc7D,CAAA"}
@@ -0,0 +1,45 @@
1
+ import { getJupiterTokens } from '@vultisig/core-chain/coin/jupiter/api';
2
+ import { normalizeTokenSymbol } from '@vultisig/core-chain/coin/tokenSymbol';
3
+ import { attempt } from '@vultisig/lib-utils/attempt';
4
+ import { getSolanaVerifiedTokenRegistry } from './verifiedRegistry.js';
5
+ /**
6
+ * Classifies a mint against the verified registry. A listed mint is `verified`
7
+ * whatever it calls itself, and so is one Jupiter itself marks verified — that
8
+ * covers a listing newer than the registry's hourly refresh and the degraded,
9
+ * curated-only registry. An unlisted mint is `scam` when its symbol or name
10
+ * collapses (see `normalizeTokenSymbol`) onto a verified token's symbol or
11
+ * name — the fake-USDT pattern, where the counterfeit is only distinguishable
12
+ * by address. Anything else is `unverified`.
13
+ */
14
+ export const resolveSolanaTokenVerification = ({ address, symbol, name, isVerified, registry, }) => {
15
+ if (registry.byAddress[address] || isVerified)
16
+ return 'verified';
17
+ const impersonates = [symbol, name].some(label => {
18
+ const skeleton = label ? normalizeTokenSymbol(label) : '';
19
+ return !!skeleton && (registry.symbols.has(skeleton) || registry.names.has(skeleton));
20
+ });
21
+ return impersonates ? 'scam' : 'unverified';
22
+ };
23
+ /**
24
+ * Verification tier for one mint, for token rows and approval cards. A mint the
25
+ * registry lists is verified without any further lookup, so a token list full
26
+ * of legitimate tokens costs no Jupiter calls. For any other mint the symbol
27
+ * and name are read from Jupiter so a counterfeit is judged by what it actually
28
+ * claims to be, not by the ticker stored locally; falls back to that ticker
29
+ * when the lookup fails, so the label still renders offline.
30
+ */
31
+ export const getSolanaTokenVerification = async ({ id, ticker, }) => {
32
+ const registry = await getSolanaVerifiedTokenRegistry();
33
+ if (registry.byAddress[id])
34
+ return 'verified';
35
+ const tokens = await attempt(getJupiterTokens([id]));
36
+ const token = 'data' in tokens ? tokens.data?.[id] : undefined;
37
+ return resolveSolanaTokenVerification({
38
+ address: id,
39
+ symbol: token?.symbol ?? ticker,
40
+ name: token?.name,
41
+ isVerified: token?.isVerified,
42
+ registry,
43
+ });
44
+ };
45
+ //# sourceMappingURL=verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/spl/verification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAA;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAA;AAE5E,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAErD,OAAO,EAAE,8BAA8B,EAA+B,MAAM,oBAAoB,CAAA;AAYhG;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,EAC7C,OAAO,EACP,MAAM,EACN,IAAI,EACJ,UAAU,EACV,QAAQ,GAC4B,EAAqB,EAAE;IAC3D,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,UAAU;QAAE,OAAO,UAAU,CAAA;IAEhE,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QAC/C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAEzD,OAAO,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IACvF,CAAC,CAAC,CAAA;IAEF,OAAO,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAA;AAC7C,CAAC,CAAA;AASD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,KAAK,EAAE,EAC/C,EAAE,EACF,MAAM,GAC0B,EAA8B,EAAE;IAChE,MAAM,QAAQ,GAAG,MAAM,8BAA8B,EAAE,CAAA;IACvD,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAAE,OAAO,UAAU,CAAA;IAE7C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAE9D,OAAO,8BAA8B,CAAC;QACpC,OAAO,EAAE,EAAE;QACX,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,MAAM;QAC/B,IAAI,EAAE,KAAK,EAAE,IAAI;QACjB,UAAU,EAAE,KAAK,EAAE,UAAU;QAC7B,QAAQ;KACT,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -0,0 +1,34 @@
1
+ /** A mint we treat as legitimate, keyed by its exact base58 address. */
2
+ export type VerifiedSolanaToken = {
3
+ address: string;
4
+ symbol: string;
5
+ name?: string;
6
+ decimals?: number;
7
+ logo?: string;
8
+ };
9
+ /**
10
+ * Verified mints indexed for the two questions verification asks: "is this
11
+ * mint listed?" and "does this symbol or name belong to a listed token?".
12
+ * `symbols` and `names` hold `normalizeTokenSymbol` skeletons. Mints are
13
+ * case-sensitive base58, so `byAddress` is keyed exactly.
14
+ */
15
+ export type SolanaVerifiedTokenRegistry = {
16
+ byAddress: Record<string, VerifiedSolanaToken>;
17
+ symbols: Set<string>;
18
+ names: Set<string>;
19
+ };
20
+ /**
21
+ * Builds a registry from a list of verified tokens. On a mint collision the
22
+ * earlier entry's metadata is kept in `byAddress`, but every entry's symbol and
23
+ * name are indexed: two entries for one mint describe the same verified token,
24
+ * and a counterfeit may copy either spelling.
25
+ */
26
+ export declare const makeSolanaVerifiedTokenRegistry: (tokens: VerifiedSolanaToken[]) => SolanaVerifiedTokenRegistry;
27
+ /**
28
+ * The mints we consider verified: our own curated Solana tokens (which win, so
29
+ * their tickers and logos are kept) merged with Jupiter's verified list.
30
+ * Degrades to the curated list alone when Jupiter cannot be fetched, so
31
+ * discovery and labels keep working offline — with fewer tokens recognised.
32
+ */
33
+ export declare const getSolanaVerifiedTokenRegistry: () => Promise<SolanaVerifiedTokenRegistry>;
34
+ //# sourceMappingURL=verifiedRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifiedRegistry.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/spl/verifiedRegistry.ts"],"names":[],"mappings":"AAQA,wEAAwE;AACxE,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;IAC9C,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACpB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACnB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,GAAI,QAAQ,mBAAmB,EAAE,KAAG,2BAc/E,CAAA;AAwCD;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,QAAa,OAAO,CAAC,2BAA2B,CAO1F,CAAA"}
@@ -0,0 +1,65 @@
1
+ import { Chain } from '@vultisig/core-chain/Chain';
2
+ import { getJupiterVerifiedTokens } from '@vultisig/core-chain/coin/jupiter/api';
3
+ import { knownTokens } from '@vultisig/core-chain/coin/knownTokens';
4
+ import { normalizeTokenSymbol } from '@vultisig/core-chain/coin/tokenSymbol';
5
+ import { attempt } from '@vultisig/lib-utils/attempt';
6
+ import { memoizeAsync } from '@vultisig/lib-utils/memoizeAsync';
7
+ import { convertDuration } from '@vultisig/lib-utils/time/convertDuration';
8
+ /**
9
+ * Builds a registry from a list of verified tokens. On a mint collision the
10
+ * earlier entry's metadata is kept in `byAddress`, but every entry's symbol and
11
+ * name are indexed: two entries for one mint describe the same verified token,
12
+ * and a counterfeit may copy either spelling.
13
+ */
14
+ export const makeSolanaVerifiedTokenRegistry = (tokens) => {
15
+ const registry = { byAddress: {}, symbols: new Set(), names: new Set() };
16
+ for (const token of tokens) {
17
+ registry.byAddress[token.address] ??= token;
18
+ const symbol = normalizeTokenSymbol(token.symbol);
19
+ if (symbol)
20
+ registry.symbols.add(symbol);
21
+ const name = token.name ? normalizeTokenSymbol(token.name) : '';
22
+ if (name)
23
+ registry.names.add(name);
24
+ }
25
+ return registry;
26
+ };
27
+ const getCuratedTokens = () => knownTokens[Chain.Solana].flatMap(({ id, ticker, decimals, logo }) => id ? [{ address: id, symbol: ticker, decimals, logo }] : []);
28
+ const fetchJupiterVerifiedTokens = async () => (await getJupiterVerifiedTokens()).flatMap(({ id, symbol, name, decimals, icon }) => {
29
+ const ticker = symbol.trim();
30
+ if (!ticker)
31
+ return [];
32
+ return [
33
+ {
34
+ address: id,
35
+ symbol: ticker,
36
+ ...(name ? { name } : {}),
37
+ ...(typeof decimals === 'number' ? { decimals } : {}),
38
+ ...(icon ? { logo: icon } : {}),
39
+ },
40
+ ];
41
+ });
42
+ // Only a successful fetch is cached: a rejected promise is never stored, so an
43
+ // outage is retried on the next call instead of pinning the degraded registry.
44
+ const getFullRegistry = memoizeAsync(async () => makeSolanaVerifiedTokenRegistry([...getCuratedTokens(), ...(await fetchJupiterVerifiedTokens())]), { cacheTime: convertDuration(1, 'h', 'ms') });
45
+ let curatedRegistry;
46
+ const getCuratedRegistry = () => {
47
+ if (!curatedRegistry) {
48
+ curatedRegistry = makeSolanaVerifiedTokenRegistry(getCuratedTokens());
49
+ }
50
+ return curatedRegistry;
51
+ };
52
+ /**
53
+ * The mints we consider verified: our own curated Solana tokens (which win, so
54
+ * their tickers and logos are kept) merged with Jupiter's verified list.
55
+ * Degrades to the curated list alone when Jupiter cannot be fetched, so
56
+ * discovery and labels keep working offline — with fewer tokens recognised.
57
+ */
58
+ export const getSolanaVerifiedTokenRegistry = async () => {
59
+ const result = await attempt(getFullRegistry());
60
+ if ('data' in result && result.data)
61
+ return result.data;
62
+ console.warn('[solana] verified token list unavailable; using the curated list only', result.error);
63
+ return getCuratedRegistry();
64
+ };
65
+ //# sourceMappingURL=verifiedRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifiedRegistry.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/spl/verifiedRegistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Why a TON transaction did not move funds, in terms a user can act on. The
3
+ * wallet contract's own checks (`seqno-mismatch`, `expired`, `invalid-signature`,
4
+ * `wallet-id-mismatch`) fire in the compute phase of the sender's wallet; the
5
+ * money-related ones fire when the wallet tries to emit the transfer (action
6
+ * phase) or in the jetton wallet's compute phase.
7
+ */
8
+ export declare const tonTxFailureReasons: readonly ["seqno-mismatch", "expired", "invalid-signature", "wallet-id-mismatch", "insufficient-funds", "out-of-gas", "invalid-destination", "not-enough-jettons", "jetton-unauthorized", "action-failed", "action-partially-failed", "aborted", "contract-rejected"];
9
+ export type TonTxFailureReason = (typeof tonTxFailureReasons)[number];
10
+ /** Which phase of the TON transaction produced the code: TVM execution or emitting the outgoing messages. */
11
+ export type TonTxPhase = 'compute' | 'action';
12
+ export type TonTxFailure = {
13
+ reason: TonTxFailureReason;
14
+ phase: TonTxPhase;
15
+ exitCode?: number;
16
+ /** Plain-language explanation with the remedy, in English. */
17
+ message: string;
18
+ };
19
+ /** Classifies a compute-phase exit code (0 and 1 are success and yield `undefined`). */
20
+ export declare const getTonComputeFailure: (exitCode: number | undefined) => TonTxFailure | undefined;
21
+ type TonActionPhaseOutcome = {
22
+ success?: boolean;
23
+ no_funds?: boolean;
24
+ result_code?: number;
25
+ skipped_actions?: number;
26
+ /** Outgoing messages the phase actually produced, when the node reports it. */
27
+ msgs_created?: number;
28
+ };
29
+ /**
30
+ * Classifies a failed action phase. A result code the table names wins; an
31
+ * unnamed one is read as a funding failure only when `no_funds` backs it, and
32
+ * otherwise stays generic with the code attached. `no_funds` without any code
33
+ * still means the transfer could not be paid for, and a skipped or unsuccessful
34
+ * action with neither is generic — worded by `genericActionReason`, which will not
35
+ * claim nothing was sent unless the node says so.
36
+ */
37
+ export declare const getTonActionFailure: (action: TonActionPhaseOutcome | undefined) => TonTxFailure | undefined;
38
+ type TonTransactionOutcome = {
39
+ aborted?: boolean;
40
+ compute_ph?: {
41
+ exit_code?: number;
42
+ };
43
+ action?: TonActionPhaseOutcome;
44
+ };
45
+ /**
46
+ * Explains why an indexed TON transaction failed, or `undefined` when it did
47
+ * not. The compute phase is consulted first because it carries the specific
48
+ * exit code; an aborted transaction with no readable phase is reported as such.
49
+ */
50
+ export declare const getTonTxFailure: (description: TonTransactionOutcome) => TonTxFailure | undefined;
51
+ /**
52
+ * Reads the wallet contract's exit code out of a toncenter `sendBoc` rejection
53
+ * ("… inbound external message rejected by transaction …: exitcode=133, steps=49,
54
+ * gas_used=0 …"). The message never reached the chain, so the code is always the
55
+ * sender's wallet refusing it in its compute phase. Returns `undefined` for
56
+ * transport errors and any other rejection shape.
57
+ */
58
+ export declare const parseTonBroadcastRejection: (error: unknown) => TonTxFailure | undefined;
59
+ /**
60
+ * A TON broadcast the sender's wallet contract refused, with the refusal
61
+ * classified. `message` is the human-readable explanation so any consumer that
62
+ * only prints `error.message` already shows the right remedy; the original
63
+ * toncenter error stays in `cause`.
64
+ */
65
+ export declare class TonBroadcastRejectedError extends Error {
66
+ readonly failure: TonTxFailure;
67
+ constructor(failure: TonTxFailure, cause: unknown);
68
+ }
69
+ export {};
70
+ //# sourceMappingURL=failure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/failure.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,uQActB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,6GAA6G;AAC7G,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE7C,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,kBAAkB,CAAA;IAC1B,KAAK,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAsFD,wFAAwF;AACxF,eAAO,MAAM,oBAAoB,GAAI,UAAU,MAAM,GAAG,SAAS,KAAG,YAAY,GAAG,SAIlF,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAeD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ,qBAAqB,GAAG,SAAS,KAAG,YAAY,GAAG,SAkB9F,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnC,MAAM,CAAC,EAAE,qBAAqB,CAAA;CAC/B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,qBAAqB,KAAG,YAAY,GAAG,SAGJ,CAAA;AAIhF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,OAAO,KAAG,YAAY,GAAG,SAK1E,CAAA;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,OAAO,EAAE,YAAY;gBAArB,OAAO,EAAE,YAAY,EACrC,KAAK,EAAE,OAAO;CAKjB"}
@@ -0,0 +1,170 @@
1
+ import { extractErrorMsg } from '@vultisig/lib-utils/error/extractErrorMsg';
2
+ /**
3
+ * Why a TON transaction did not move funds, in terms a user can act on. The
4
+ * wallet contract's own checks (`seqno-mismatch`, `expired`, `invalid-signature`,
5
+ * `wallet-id-mismatch`) fire in the compute phase of the sender's wallet; the
6
+ * money-related ones fire when the wallet tries to emit the transfer (action
7
+ * phase) or in the jetton wallet's compute phase.
8
+ */
9
+ export const tonTxFailureReasons = [
10
+ 'seqno-mismatch',
11
+ 'expired',
12
+ 'invalid-signature',
13
+ 'wallet-id-mismatch',
14
+ 'insufficient-funds',
15
+ 'out-of-gas',
16
+ 'invalid-destination',
17
+ 'not-enough-jettons',
18
+ 'jetton-unauthorized',
19
+ 'action-failed',
20
+ 'action-partially-failed',
21
+ 'aborted',
22
+ 'contract-rejected',
23
+ ];
24
+ /**
25
+ * Compute-phase exit codes. The sender's wallet contract throws the first block
26
+ * from `recv_external`: wallet v3/v4 use 33–36, W5 uses 133–136 for the same
27
+ * checks. 13 / -14 are the TVM's out-of-gas codes. 705 and 706 come from the
28
+ * standard jetton wallet (TEP-74) and mean the same thing on a transfer and on
29
+ * a burn; its 707 and 709 are deliberately absent because the reference
30
+ * contract reuses each for two unrelated checks (707: unauthorized incoming
31
+ * transfer, or too little TON on a burn; 709: too little TON on a transfer, or
32
+ * an unexpected bounced op), and the code alone cannot tell which. Everything
33
+ * else is a contract-specific revert we can only report by number.
34
+ */
35
+ const computeExitCodeReasons = {
36
+ 33: 'seqno-mismatch',
37
+ 133: 'seqno-mismatch',
38
+ 34: 'wallet-id-mismatch',
39
+ 134: 'wallet-id-mismatch',
40
+ 35: 'invalid-signature',
41
+ 135: 'invalid-signature',
42
+ 36: 'expired',
43
+ 136: 'expired',
44
+ 13: 'out-of-gas',
45
+ [-14]: 'out-of-gas',
46
+ 705: 'jetton-unauthorized',
47
+ 706: 'not-enough-jettons',
48
+ };
49
+ /**
50
+ * Action-phase result codes that mean one thing. 36 here is an invalid destination,
51
+ * not the wallet's `expired` (36), which can only come from the compute phase. 37 is
52
+ * "Not enough GRAMs" and says so on its own.
53
+ *
54
+ * 40 is deliberately absent. It reads "Cannot process a message — not enough funds,
55
+ * the message is too large, or its Merkle depth is too big", so naming it a funding
56
+ * failure would send a user to top up their balance when the payload is what has to
57
+ * change. Codes like it fall through to `no_funds` below, the only authoritative
58
+ * funding signal in the phase.
59
+ */
60
+ const actionResultCodeReasons = {
61
+ 36: 'invalid-destination',
62
+ 37: 'insufficient-funds',
63
+ };
64
+ /**
65
+ * Human-readable explanations. Each says what happened and what to do; the
66
+ * remedy matters more than the code, because the two failures that dominate
67
+ * TON support — a replayed seqno and an expired `valid_until` — both look like
68
+ * "the network rejected it" and have opposite fixes.
69
+ */
70
+ const failureMessages = {
71
+ 'seqno-mismatch': 'Another transaction from this wallet was processed first, so the network rejected this one as out of order. Check your history: if this transfer is not there, send it again.',
72
+ expired: "The transaction's time window closed before the network processed it. Make sure your device's date and time are set automatically, then send it again.",
73
+ 'invalid-signature': 'The wallet contract rejected the signature. Sign the transaction again; if it keeps failing, the wallet contract at this address does not match this vault.',
74
+ 'wallet-id-mismatch': 'The transaction was built for a different wallet contract version than the one deployed at this address.',
75
+ 'insufficient-funds': 'Not enough TON to cover the amount plus network fees. Keep about 0.05 TON spare for fees and try again.',
76
+ 'out-of-gas': 'The transaction ran out of gas before it could finish. Attach more TON to the transfer and try again.',
77
+ 'invalid-destination': 'The destination address is not valid on TON. Check the address and try again.',
78
+ 'not-enough-jettons': 'This wallet does not hold enough of the token to send that amount.',
79
+ 'jetton-unauthorized': 'The token contract refused the transfer because this wallet is not allowed to move these tokens.',
80
+ 'action-failed': 'The wallet accepted the transaction but could not carry out the transfer, so nothing was sent. The network fee was still charged. Check the transaction and try again.',
81
+ 'action-partially-failed': 'The wallet could not carry out at least one transfer in this transaction, and others in it may have gone through. The network fee was still charged. Check your transaction history before sending again.',
82
+ aborted: 'The network aborted this transaction before it could carry out the transfer.',
83
+ 'contract-rejected': 'The contract rejected the transaction.',
84
+ };
85
+ const describe = (reason, exitCode) => reason === 'contract-rejected' && exitCode !== undefined
86
+ ? `The contract rejected the transaction (exit code ${exitCode}).`
87
+ : failureMessages[reason];
88
+ const makeFailure = (reason, phase, exitCode) => ({
89
+ reason,
90
+ phase,
91
+ ...(exitCode === undefined ? {} : { exitCode }),
92
+ message: describe(reason, exitCode),
93
+ });
94
+ /** Classifies a compute-phase exit code (0 and 1 are success and yield `undefined`). */
95
+ export const getTonComputeFailure = (exitCode) => {
96
+ if (exitCode === undefined || exitCode === 0 || exitCode === 1)
97
+ return undefined;
98
+ return makeFailure(computeExitCodeReasons[exitCode] ?? 'contract-rejected', 'compute', exitCode);
99
+ };
100
+ /**
101
+ * Which generic explanation a failed action phase gets.
102
+ *
103
+ * Under `IGNORE_ERRORS` — the mode every Vultisig TON send uses — a failing action
104
+ * is skipped and the remaining ones still go out, so a batch can lose one transfer
105
+ * and deliver the rest. Telling that user nothing was sent and to try again is how a
106
+ * duplicate transfer happens, so only the node's own `msgs_created: 0` earns the
107
+ * "nothing was sent" wording. A node that reports nothing proves nothing either way
108
+ * and gets the neutral explanation, which reads correctly for both outcomes.
109
+ */
110
+ const genericActionReason = ({ msgs_created }) => msgs_created === 0 ? 'action-failed' : 'action-partially-failed';
111
+ /**
112
+ * Classifies a failed action phase. A result code the table names wins; an
113
+ * unnamed one is read as a funding failure only when `no_funds` backs it, and
114
+ * otherwise stays generic with the code attached. `no_funds` without any code
115
+ * still means the transfer could not be paid for, and a skipped or unsuccessful
116
+ * action with neither is generic — worded by `genericActionReason`, which will not
117
+ * claim nothing was sent unless the node says so.
118
+ */
119
+ export const getTonActionFailure = (action) => {
120
+ if (!action)
121
+ return undefined;
122
+ const { success, no_funds, result_code, skipped_actions } = action;
123
+ if (result_code !== undefined && result_code !== 0) {
124
+ // A code the table does not name is a funding failure only when the node says so.
125
+ const reason = actionResultCodeReasons[result_code] ?? (no_funds === true ? 'insufficient-funds' : genericActionReason(action));
126
+ return makeFailure(reason, 'action', result_code);
127
+ }
128
+ if (no_funds === true)
129
+ return makeFailure('insufficient-funds', 'action');
130
+ if (success === false || (skipped_actions ?? 0) > 0)
131
+ return makeFailure(genericActionReason(action), 'action');
132
+ return undefined;
133
+ };
134
+ /**
135
+ * Explains why an indexed TON transaction failed, or `undefined` when it did
136
+ * not. The compute phase is consulted first because it carries the specific
137
+ * exit code; an aborted transaction with no readable phase is reported as such.
138
+ */
139
+ export const getTonTxFailure = (description) => getTonComputeFailure(description.compute_ph?.exit_code) ??
140
+ getTonActionFailure(description.action) ??
141
+ (description.aborted === true ? makeFailure('aborted', 'compute') : undefined);
142
+ const rejectionExitCodePattern = /exitcode=(-?\d+)/i;
143
+ /**
144
+ * Reads the wallet contract's exit code out of a toncenter `sendBoc` rejection
145
+ * ("… inbound external message rejected by transaction …: exitcode=133, steps=49,
146
+ * gas_used=0 …"). The message never reached the chain, so the code is always the
147
+ * sender's wallet refusing it in its compute phase. Returns `undefined` for
148
+ * transport errors and any other rejection shape.
149
+ */
150
+ export const parseTonBroadcastRejection = (error) => {
151
+ const match = rejectionExitCodePattern.exec(String(extractErrorMsg(error)));
152
+ if (!match)
153
+ return undefined;
154
+ return getTonComputeFailure(Number(match[1]));
155
+ };
156
+ /**
157
+ * A TON broadcast the sender's wallet contract refused, with the refusal
158
+ * classified. `message` is the human-readable explanation so any consumer that
159
+ * only prints `error.message` already shows the right remedy; the original
160
+ * toncenter error stays in `cause`.
161
+ */
162
+ export class TonBroadcastRejectedError extends Error {
163
+ failure;
164
+ constructor(failure, cause) {
165
+ super(failure.message, { cause });
166
+ this.failure = failure;
167
+ this.name = 'TonBroadcastRejectedError';
168
+ }
169
+ }
170
+ //# sourceMappingURL=failure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure.js","sourceRoot":"","sources":["../../../../../../packages/core/chain/chains/ton/failure.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,7 @@
1
1
  /**
2
- * Collapses a jetton symbol or name to the Latin skeleton a user perceives, so
3
- * `USD₮`, `UЅDT` (Cyrillic Ѕ), `$USĐ₮` and `usdt` all compare equal to `USDT`.
4
- * Compatibility decomposition folds full-width and mathematical letter forms,
5
- * diacritics are dropped, known homoglyphs are mapped, and everything that is
6
- * not `A–Z`/`0–9` is removed. Returns an empty string when nothing survives.
2
+ * Jetton-named alias of the chain-agnostic `normalizeTokenSymbol`, kept so
3
+ * importers of this subpath keep working. New code should import the shared
4
+ * normalizer from `coin/tokenSymbol` directly.
7
5
  */
8
6
  export declare const normalizeJettonSymbol: (value: string) => string;
9
7
  //# sourceMappingURL=symbol.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"symbol.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/ton/jetton/symbol.ts"],"names":[],"mappings":"AAmDA;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,KAAG,MAIxB,CAAA"}
1
+ {"version":3,"file":"symbol.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/ton/jetton/symbol.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,2BAAuB,CAAA"}