@vultisig/core-chain 2.29.0 → 2.29.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @vultisig/core-chain
2
2
 
3
+ ## 2.29.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1568](https://github.com/vultisig/vultisig-sdk/pull/1568) [`859ab28`](https://github.com/vultisig/vultisig-sdk/commit/859ab287d3574c508b4abce5950e8e42c17f8198) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - feat(ripple): add XRPL issued-currency (custom token) support
8
+
9
+ XRPL issued currencies (trust-line tokens / IOUs) had no token metadata resolver,
10
+ so custom tokens could not be added on Ripple. Adds `getRippleTokenMetadata` and
11
+ registers Ripple in `chainsWithTokenMetadataDiscovery`. Unlike EVM/Tron there is no
12
+ on-ledger metadata call: XRPL exposes no per-token decimal metadata — so the SDK
13
+ applies its fixed issued-currency decimal policy (`rippleIssuedCurrencyDecimals`) —
14
+ and the ticker is derived from the currency code, so the resolver fetches nothing.
15
+ Curated tokens
16
+ (RLUSD) get their logo and price provider merged in; an arbitrary token gets neither
17
+ rather than borrowing a known token's identity, since two issuers can share a ticker
18
+ on XRPL.
19
+
20
+ `isValidTokenId` now also accepts a human ticker (`SOLO`, `RLUSD`) wherever an
21
+ on-ledger currency code is accepted — the form shown on explorers like xrpscan — and
22
+ a new `normalizeTokenId` canonicalises a pasted id to the on-ledger form.
23
+
24
+ `BalanceService.addToken`/`removeToken` normalise the token id (and its matching
25
+ `contractAddress`) before it enters persisted state, so a manually added
26
+ `RLUSD.<issuer>` collapses onto the canonical `524C…<issuer>` that ledger discovery
27
+ stores instead of being kept as a second, distinct token. A no-op for chains whose
28
+ ids are already canonical.
29
+
3
30
  ## 2.29.0
4
31
 
5
32
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  import { DeriveChainKind } from '@vultisig/core-chain/ChainKind';
2
2
  import { OtherChain } from '../../../Chain.js';
3
- export declare const chainsWithTokenMetadataDiscovery: readonly ("Arbitrum" | "Base" | "Blast" | "Optimism" | "Zksync" | "Mantle" | "Avalanche" | "CronosChain" | "BSC" | "Ethereum" | "Polygon" | "Hyperliquid" | "Sei" | "Cosmos" | "Osmosis" | "Dydx" | "Kujira" | "Terra" | "TerraClassic" | "Noble" | "Akash" | "THORChain" | "MayaChain" | OtherChain.Sui | OtherChain.Solana | OtherChain.Ton | OtherChain.Tron | OtherChain.Cardano)[];
3
+ export declare const chainsWithTokenMetadataDiscovery: readonly ("Arbitrum" | "Base" | "Blast" | "Optimism" | "Zksync" | "Mantle" | "Avalanche" | "CronosChain" | "BSC" | "Ethereum" | "Polygon" | "Hyperliquid" | "Sei" | "Cosmos" | "Osmosis" | "Dydx" | "Kujira" | "Terra" | "TerraClassic" | "Noble" | "Akash" | "THORChain" | "MayaChain" | OtherChain.Sui | OtherChain.Solana | OtherChain.Ton | OtherChain.Ripple | OtherChain.Tron | OtherChain.Cardano)[];
4
4
  export type ChainWithTokenMetadataDiscovery = (typeof chainsWithTokenMetadataDiscovery)[number];
5
5
  export type ChainKindWithTokenMetadataDiscovery = DeriveChainKind<ChainWithTokenMetadataDiscovery>;
6
6
  //# sourceMappingURL=chains.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chains.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/chains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAEhE,OAAO,EAAyB,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElE,eAAO,MAAM,gCAAgC,yXAQnC,CAAA;AAEV,MAAM,MAAM,+BAA+B,GAAG,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/F,MAAM,MAAM,mCAAmC,GAAG,eAAe,CAAC,+BAA+B,CAAC,CAAA"}
1
+ {"version":3,"file":"chains.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/chains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAEhE,OAAO,EAAyB,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElE,eAAO,MAAM,gCAAgC,6YASnC,CAAA;AAEV,MAAM,MAAM,+BAA+B,GAAG,CAAC,OAAO,gCAAgC,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/F,MAAM,MAAM,mCAAmC,GAAG,eAAe,CAAC,+BAA+B,CAAC,CAAA"}
@@ -6,6 +6,7 @@ export const chainsWithTokenMetadataDiscovery = [
6
6
  OtherChain.Tron,
7
7
  OtherChain.Cardano,
8
8
  OtherChain.Sui,
9
+ OtherChain.Ripple,
9
10
  ...Object.values(CosmosChain),
10
11
  ];
11
12
  //# sourceMappingURL=chains.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chains.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/chains.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElE,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC1B,UAAU,CAAC,MAAM;IACjB,UAAU,CAAC,GAAG;IACd,UAAU,CAAC,IAAI;IACf,UAAU,CAAC,OAAO;IAClB,UAAU,CAAC,GAAG;IACd,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;CACrB,CAAA"}
1
+ {"version":3,"file":"chains.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/chains.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAElE,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC1B,UAAU,CAAC,MAAM;IACjB,UAAU,CAAC,GAAG;IACd,UAAU,CAAC,IAAI;IACf,UAAU,CAAC,OAAO;IAClB,UAAU,CAAC,GAAG;IACd,UAAU,CAAC,MAAM;IACjB,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;CACrB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAmBlD,eAAO,MAAM,gBAAgB,EAAE,qBAM9B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAqBlD,eAAO,MAAM,gBAAgB,EAAE,qBAM9B,CAAA"}
@@ -2,6 +2,7 @@ import { getChainKind } from '@vultisig/core-chain/ChainKind';
2
2
  import { getCardanoTokenMetadata } from './resolvers/cardano.js';
3
3
  import { getCosmosTokenMetadata } from './resolvers/cosmos.js';
4
4
  import { getEvmTokenMetadata } from './resolvers/evm.js';
5
+ import { getRippleTokenMetadata } from './resolvers/ripple.js';
5
6
  import { getSolanaTokenMetadata } from './resolvers/solana.js';
6
7
  import { getSuiTokenMetadata } from './resolvers/sui.js';
7
8
  import { getTonTokenMetadata } from './resolvers/ton.js';
@@ -14,6 +15,7 @@ const resolvers = {
14
15
  tron: getTronTokenMetadata,
15
16
  cardano: getCardanoTokenMetadata,
16
17
  sui: getSuiTokenMetadata,
18
+ ripple: getRippleTokenMetadata,
17
19
  };
18
20
  export const getTokenMetadata = async (input) => {
19
21
  const chainKind = getChainKind(input.chain);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAI7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAEvD,MAAM,SAAS,GAA4E;IACzF,GAAG,EAAE,mBAAmB;IACxB,MAAM,EAAE,sBAAsB;IAC9B,MAAM,EAAE,sBAAsB;IAC9B,GAAG,EAAE,mBAAmB;IACxB,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,uBAAuB;IAChC,GAAG,EAAE,mBAAmB;CACzB,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAA0B,KAAK,EAAC,KAAK,EAAC,EAAE;IACnE,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAE3C,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IAErC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxB,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/coin/token/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAI7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAEvD,MAAM,SAAS,GAA4E;IACzF,GAAG,EAAE,mBAAmB;IACxB,MAAM,EAAE,sBAAsB;IAC9B,MAAM,EAAE,sBAAsB;IAC9B,GAAG,EAAE,mBAAmB;IACxB,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,uBAAuB;IAChC,GAAG,EAAE,mBAAmB;IACxB,MAAM,EAAE,sBAAsB;CAC/B,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAA0B,KAAK,EAAC,KAAK,EAAC,EAAE;IACnE,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAE3C,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IAErC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxB,CAAC,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { OtherChain } from '@vultisig/core-chain/Chain';
2
+ import { TokenMetadataResolver } from '../resolver.js';
3
+ /**
4
+ * Metadata for a custom XRPL issued currency identified by a `currency.issuer`
5
+ * token id.
6
+ *
7
+ * Unlike EVM/Tron tokens, XRPL issued currencies expose no on-ledger metadata
8
+ * call: issued amounts carry no fixed on-chain decimal count (we model them at
9
+ * {@link rippleIssuedCurrencyDecimals}) and the ticker is derived from the
10
+ * currency code itself, so nothing is fetched. When the token is one we curate,
11
+ * its logo and price provider are merged in; an arbitrary token gets neither, so
12
+ * it renders with a generic icon and no price rather than borrowing a known
13
+ * token's identity — two different issuers can share a ticker on XRPL, so the
14
+ * issuer (encoded in the id) is what actually distinguishes them.
15
+ */
16
+ export declare const getRippleTokenMetadata: TokenMetadataResolver<OtherChain.Ripple>;
17
+ //# sourceMappingURL=ripple.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ripple.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/coin/token/metadata/resolvers/ripple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AASvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAiBnD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,EAAE,qBAAqB,CAAC,UAAU,CAAC,MAAM,CAqB3E,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { parseRippleTokenId, rippleIssuedCurrencyDecimals, rippleKnownIssuedTokens, rippleTokenId, } from '@vultisig/core-chain/chains/ripple/issuedCurrency';
2
+ import { areEqualCoins } from '@vultisig/core-chain/coin/Coin';
3
+ /**
4
+ * Human-readable ticker for an XRPL issued currency. Standard 3-character codes
5
+ * (`USD`) are already tickers; the 160-bit hex form encodes ASCII right-padded
6
+ * with zeros, so decoding it recovers the ticker a user recognises (e.g. `RLUSD`).
7
+ */
8
+ const toIssuedCurrencyTicker = (currency) => {
9
+ if (currency.length !== 40) {
10
+ return currency;
11
+ }
12
+ const ascii = Buffer.from(currency, 'hex').toString('ascii').replace(/\0+$/, '');
13
+ return /^[\x20-\x7e]+$/.test(ascii) ? ascii : currency;
14
+ };
15
+ /**
16
+ * Metadata for a custom XRPL issued currency identified by a `currency.issuer`
17
+ * token id.
18
+ *
19
+ * Unlike EVM/Tron tokens, XRPL issued currencies expose no on-ledger metadata
20
+ * call: issued amounts carry no fixed on-chain decimal count (we model them at
21
+ * {@link rippleIssuedCurrencyDecimals}) and the ticker is derived from the
22
+ * currency code itself, so nothing is fetched. When the token is one we curate,
23
+ * its logo and price provider are merged in; an arbitrary token gets neither, so
24
+ * it renders with a generic icon and no price rather than borrowing a known
25
+ * token's identity — two different issuers can share a ticker on XRPL, so the
26
+ * issuer (encoded in the id) is what actually distinguishes them.
27
+ */
28
+ export const getRippleTokenMetadata = async ({ id, chain }) => {
29
+ const { currency, issuer } = parseRippleTokenId(id);
30
+ const metadata = {
31
+ ticker: toIssuedCurrencyTicker(currency),
32
+ decimals: rippleIssuedCurrencyDecimals,
33
+ };
34
+ const knownToken = rippleKnownIssuedTokens.find(token => areEqualCoins(token, { chain, id: rippleTokenId({ currency, issuer }) }));
35
+ if (!knownToken) {
36
+ return metadata;
37
+ }
38
+ return {
39
+ ...metadata,
40
+ logo: knownToken.logo,
41
+ priceProviderId: knownToken.priceProviderId,
42
+ };
43
+ };
44
+ //# sourceMappingURL=ripple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ripple.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/coin/token/metadata/resolvers/ripple.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAClB,4BAA4B,EAC5B,uBAAuB,EACvB,aAAa,GACd,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAgB,MAAM,gCAAgC,CAAA;AAI5E;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,CAAC,QAAgB,EAAU,EAAE;IAC1D,IAAI,QAAQ,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAC3B,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IAEhF,OAAO,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAA;AACxD,CAAC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA6C,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;IACtG,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAA;IAEnD,MAAM,QAAQ,GAAiB;QAC7B,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC;QACxC,QAAQ,EAAE,4BAA4B;KACvC,CAAA;IAED,MAAM,UAAU,GAAG,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CACtD,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CACzE,CAAA;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,OAAO;QACL,GAAG,QAAQ;QACX,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,eAAe,EAAE,UAAU,CAAC,eAAe;KAC5C,CAAA;AACH,CAAC,CAAA"}
@@ -15,5 +15,16 @@ type Input = {
15
15
  * are identified by a composite `currency.issuer` id and validated as such.
16
16
  */
17
17
  export declare const isValidTokenId: ({ chain, id, walletCore }: Input) => boolean;
18
+ /**
19
+ * Canonical form of a custom token id, for storage and equality.
20
+ *
21
+ * Only XRPL needs normalising: a token may be entered by its human ticker
22
+ * (`SOLO.rsoLo…`, as shown on explorers) or by its on-ledger currency code, and
23
+ * both must resolve to the same id so a manually added token dedupes against the
24
+ * same token discovered from the ledger — which always uses the on-ledger code.
25
+ * `rippleTokenId` re-encodes the currency to that canonical form. For every other
26
+ * chain the id is already canonical and returned unchanged.
27
+ */
28
+ export declare const normalizeTokenId: ({ chain, id }: Pick<Input, "chain" | "id">) => string;
18
29
  export {};
19
30
  //# sourceMappingURL=isValidTokenId.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"isValidTokenId.d.ts","sourceRoot":"","sources":["../../../../../packages/core/chain/utils/isValidTokenId.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAMlD,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,KAAK,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,2BAA2B,KAAK,YAgB9D,CAAA"}
1
+ {"version":3,"file":"isValidTokenId.d.ts","sourceRoot":"","sources":["../../../../../packages/core/chain/utils/isValidTokenId.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAWlD,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,KAAK,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,2BAA2B,KAAK,YA0B9D,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,eAAe,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC,KAAG,MAW7E,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { isValidStructTag } from '@mysten/sui/utils';
2
2
  import { Chain } from '@vultisig/core-chain/Chain';
3
- import { isValidXrplCurrencyCode, parseRippleTokenId } from '@vultisig/core-chain/chains/ripple/issuedCurrency';
3
+ import { isValidXrplCurrencyCode, parseRippleTokenId, rippleTokenId, toXrplCurrencyCode, } from '@vultisig/core-chain/chains/ripple/issuedCurrency';
4
4
  import { attempt } from '@vultisig/lib-utils/attempt';
5
5
  import { isValidAddress } from './isValidAddress.js';
6
6
  /**
@@ -22,8 +22,37 @@ export const isValidTokenId = ({ chain, id, walletCore }) => {
22
22
  return false;
23
23
  }
24
24
  const { currency, issuer } = parsed.data;
25
- return isValidXrplCurrencyCode(currency) && isValidAddress({ chain, address: issuer, walletCore });
25
+ // Accept either an on-ledger currency code (`USD`, or the 40-char hex form)
26
+ // or a human ticker (`SOLO`, `RLUSD`) — the latter is how tokens are
27
+ // referenced on explorers like xrpscan. `toXrplCurrencyCode` normalises a
28
+ // ticker to its on-ledger code; anything it cannot encode (e.g. a name
29
+ // longer than 20 bytes) is rejected.
30
+ const currencyCode = attempt(() => toXrplCurrencyCode(currency));
31
+ if ('error' in currencyCode) {
32
+ return false;
33
+ }
34
+ return isValidXrplCurrencyCode(currencyCode.data) && isValidAddress({ chain, address: issuer, walletCore });
26
35
  }
27
36
  return isValidAddress({ chain, address: id, walletCore });
28
37
  };
38
+ /**
39
+ * Canonical form of a custom token id, for storage and equality.
40
+ *
41
+ * Only XRPL needs normalising: a token may be entered by its human ticker
42
+ * (`SOLO.rsoLo…`, as shown on explorers) or by its on-ledger currency code, and
43
+ * both must resolve to the same id so a manually added token dedupes against the
44
+ * same token discovered from the ledger — which always uses the on-ledger code.
45
+ * `rippleTokenId` re-encodes the currency to that canonical form. For every other
46
+ * chain the id is already canonical and returned unchanged.
47
+ */
48
+ export const normalizeTokenId = ({ chain, id }) => {
49
+ if (chain !== Chain.Ripple) {
50
+ return id;
51
+ }
52
+ const canonical = attempt(() => rippleTokenId(parseRippleTokenId(id.trim())));
53
+ if ('error' in canonical) {
54
+ return id;
55
+ }
56
+ return canonical.data;
57
+ };
29
58
  //# sourceMappingURL=isValidTokenId.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"isValidTokenId.js","sourceRoot":"","sources":["../../../../../packages/core/chain/utils/isValidTokenId.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAA;AAC/G,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAQjD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAS,EAAE,EAAE;IACjE,IAAI,KAAK,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC;QACxB,OAAO,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,KAAK,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QAC3D,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAA;QAExC,OAAO,uBAAuB,CAAC,QAAQ,CAAC,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IACpG,CAAC;IAED,OAAO,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;AAC3D,CAAC,CAAA"}
1
+ {"version":3,"file":"isValidTokenId.js","sourceRoot":"","sources":["../../../../../packages/core/chain/utils/isValidTokenId.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,GACnB,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAQjD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAS,EAAE,EAAE;IACjE,IAAI,KAAK,KAAK,KAAK,CAAC,GAAG,EAAE,CAAC;QACxB,OAAO,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,KAAK,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;QAC3D,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAA;QAExC,4EAA4E;QAC5E,qEAAqE;QACrE,0EAA0E;QAC1E,uEAAuE;QACvE,qCAAqC;QACrC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAA;QAChE,IAAI,OAAO,IAAI,YAAY,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,uBAAuB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IAC7G,CAAC;IAED,OAAO,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;AAC3D,CAAC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAA+B,EAAU,EAAE;IACrF,IAAI,KAAK,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAC7E,IAAI,OAAO,IAAI,SAAS,EAAE,CAAC;QACzB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CAAA;AACvB,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/core-chain",
3
- "version": "2.29.0",
3
+ "version": "2.29.1",
4
4
  "description": "Blockchain chain logic shared across Vultisig clients",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1245,6 +1245,11 @@
1245
1245
  "import": "./dist/coin/token/metadata/resolvers/evm.js",
1246
1246
  "default": "./dist/coin/token/metadata/resolvers/evm.js"
1247
1247
  },
1248
+ "./coin/token/metadata/resolvers/ripple": {
1249
+ "types": "./dist/coin/token/metadata/resolvers/ripple.d.ts",
1250
+ "import": "./dist/coin/token/metadata/resolvers/ripple.js",
1251
+ "default": "./dist/coin/token/metadata/resolvers/ripple.js"
1252
+ },
1248
1253
  "./coin/token/metadata/resolvers/solana": {
1249
1254
  "types": "./dist/coin/token/metadata/resolvers/solana.d.ts",
1250
1255
  "import": "./dist/coin/token/metadata/resolvers/solana.js",