@shapeshiftoss/utils 1.1.0 → 1.2.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 (35) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/assetData/baseAssets.d.ts +1 -0
  3. package/dist/cjs/assetData/baseAssets.js +18 -2
  4. package/dist/cjs/assetData/getBaseAsset.js +2 -0
  5. package/dist/cjs/chainIdToFeeAssetId.js +2 -0
  6. package/dist/cjs/getAssetNamespaceFromChainId.js +1 -0
  7. package/dist/cjs/getChainShortName.d.ts +1 -1
  8. package/dist/cjs/getChainShortName.js +2 -0
  9. package/dist/cjs/getNativeFeeAssetReference.js +2 -0
  10. package/dist/cjs/index.d.ts +2 -0
  11. package/dist/cjs/index.js +2 -0
  12. package/dist/cjs/paymentUri/constants.d.ts +3 -0
  13. package/dist/cjs/paymentUri/constants.js +29 -0
  14. package/dist/cjs/paymentUri/paymentUri.d.ts +7 -0
  15. package/dist/cjs/paymentUri/paymentUri.js +96 -0
  16. package/dist/cjs/paymentUri/paymentUri.test.d.ts +1 -0
  17. package/dist/cjs/paymentUri/paymentUri.test.js +95 -0
  18. package/dist/esm/.tsbuildinfo +1 -1
  19. package/dist/esm/assetData/baseAssets.d.ts +1 -0
  20. package/dist/esm/assetData/baseAssets.js +16 -0
  21. package/dist/esm/assetData/getBaseAsset.js +3 -1
  22. package/dist/esm/chainIdToFeeAssetId.js +3 -1
  23. package/dist/esm/getAssetNamespaceFromChainId.js +1 -0
  24. package/dist/esm/getChainShortName.d.ts +1 -1
  25. package/dist/esm/getChainShortName.js +2 -0
  26. package/dist/esm/getNativeFeeAssetReference.js +2 -0
  27. package/dist/esm/index.d.ts +2 -0
  28. package/dist/esm/index.js +2 -0
  29. package/dist/esm/paymentUri/constants.d.ts +3 -0
  30. package/dist/esm/paymentUri/constants.js +26 -0
  31. package/dist/esm/paymentUri/paymentUri.d.ts +7 -0
  32. package/dist/esm/paymentUri/paymentUri.js +92 -0
  33. package/dist/esm/paymentUri/paymentUri.test.d.ts +1 -0
  34. package/dist/esm/paymentUri/paymentUri.test.js +93 -0
  35. package/package.json +3 -3
@@ -1,4 +1,5 @@
1
1
  import type { Asset } from '@shapeshiftoss/types';
2
+ export declare const robinhood: Readonly<Asset>;
2
3
  export declare const abstract: Readonly<Asset>;
3
4
  export declare const ethereum: Readonly<Asset>;
4
5
  export declare const bitcoin: Readonly<Asset>;
@@ -23,9 +23,25 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.near = exports.sui = exports.tron = exports.solana = exports.soneium = exports.modeChain = exports.bobChain = exports.unichainChain = exports.sonic = exports.scroll = exports.linea = exports.sei = exports.hemi = exports.blast = exports.zkSyncEra = exports.story = exports.celo = exports.ethereal = exports.flowEvm = exports.katana = exports.berachain = exports.megaeth = exports.cronos = exports.ink = exports.mantle = exports.worldchain = exports.plume = exports.plasma = exports.hyperevm = exports.monad = exports.base = exports.arbitrum = exports.gnosis = exports.polygon = exports.bnbsmartchain = exports.optimism = exports.mayachain = exports.maya = exports.ruji = exports.tcy = exports.thorchain = exports.avax = exports.atom = exports.zcash = exports.litecoin = exports.dogecoin = exports.bitcoincash = exports.bitcoin = exports.ethereum = exports.abstract = void 0;
27
- exports.ton = exports.starknet = void 0;
26
+ exports.sui = exports.tron = exports.solana = exports.soneium = exports.modeChain = exports.bobChain = exports.unichainChain = exports.sonic = exports.scroll = exports.linea = exports.sei = exports.hemi = exports.blast = exports.zkSyncEra = exports.story = exports.celo = exports.ethereal = exports.flowEvm = exports.katana = exports.berachain = exports.megaeth = exports.cronos = exports.ink = exports.mantle = exports.worldchain = exports.plume = exports.plasma = exports.hyperevm = exports.monad = exports.base = exports.arbitrum = exports.gnosis = exports.polygon = exports.bnbsmartchain = exports.optimism = exports.mayachain = exports.maya = exports.ruji = exports.tcy = exports.thorchain = exports.avax = exports.atom = exports.zcash = exports.litecoin = exports.dogecoin = exports.bitcoincash = exports.bitcoin = exports.ethereum = exports.abstract = exports.robinhood = void 0;
27
+ exports.ton = exports.starknet = exports.near = void 0;
28
28
  const caip = __importStar(require("@shapeshiftoss/caip"));
29
+ exports.robinhood = Object.freeze({
30
+ assetId: caip.robinhoodAssetId,
31
+ chainId: caip.robinhoodChainId,
32
+ name: 'Ethereum',
33
+ networkName: 'Robinhood',
34
+ symbol: 'ETH',
35
+ precision: 18,
36
+ color: '#00C805',
37
+ networkColor: '#00C805',
38
+ icon: 'https://assets.coingecko.com/coins/images/279/large/ethereum.png',
39
+ networkIcon: 'https://assets.relay.link/icons/4663/light.png',
40
+ explorer: 'https://robinhoodchain.blockscout.com',
41
+ explorerAddressLink: 'https://robinhoodchain.blockscout.com/address/',
42
+ explorerTxLink: 'https://robinhoodchain.blockscout.com/tx/',
43
+ relatedAssetKey: 'eip155:1/slip44:60',
44
+ });
29
45
  exports.abstract = Object.freeze({
30
46
  assetId: caip.abstractAssetId,
31
47
  chainId: caip.abstractChainId,
@@ -105,6 +105,8 @@ const getBaseAsset = (chainId) => {
105
105
  return baseAssets_1.ton;
106
106
  case types_1.KnownChainIds.AbstractMainnet:
107
107
  return baseAssets_1.abstract;
108
+ case types_1.KnownChainIds.RobinhoodMainnet:
109
+ return baseAssets_1.robinhood;
108
110
  default:
109
111
  return (0, assertUnreachable_1.assertUnreachable)(knownChainId);
110
112
  }
@@ -105,6 +105,8 @@ const chainIdToFeeAssetId = (_chainId) => {
105
105
  return caip_1.tonAssetId;
106
106
  case types_1.KnownChainIds.AbstractMainnet:
107
107
  return caip_1.abstractAssetId;
108
+ case types_1.KnownChainIds.RobinhoodMainnet:
109
+ return caip_1.robinhoodAssetId;
108
110
  default:
109
111
  return (0, assertUnreachable_1.assertUnreachable)(chainId);
110
112
  }
@@ -49,6 +49,7 @@ const getAssetNamespaceFromChainId = (chainId) => {
49
49
  case types_1.KnownChainIds.FlowEvmMainnet:
50
50
  case types_1.KnownChainIds.CeloMainnet:
51
51
  case types_1.KnownChainIds.AbstractMainnet:
52
+ case types_1.KnownChainIds.RobinhoodMainnet:
52
53
  return caip_1.ASSET_NAMESPACE.erc20;
53
54
  case types_1.KnownChainIds.StarknetMainnet:
54
55
  return caip_1.ASSET_NAMESPACE.starknetToken;
@@ -1,2 +1,2 @@
1
1
  import { KnownChainIds } from '@shapeshiftoss/types';
2
- export declare const getChainShortName: (chainId: KnownChainIds) => "AVA" | "OP" | "ETH" | "POLY" | "GNO" | "BNB" | "ARB" | "BAS" | "BTC" | "BCH" | "COSM" | "THOR" | "MAYA" | "DOGE" | "LTC" | "SOL" | "TRX" | "SUI" | "STRK" | "MON" | "HYPE" | "XPL" | "MNT" | "INK" | "MEGA" | "BERA" | "CRO" | "KAT" | "ETRL" | "FLOW" | "CELO" | "PLUME" | "STORY" | "ZKS" | "BLAST" | "WLD" | "HEM" | "SEI" | "LIN" | "SCR" | "S" | "UNI" | "BOB" | "MODE" | "SON" | "ZEC" | "NEAR" | "TON" | "ABS" | undefined;
2
+ export declare const getChainShortName: (chainId: KnownChainIds) => "AVA" | "OP" | "ETH" | "POLY" | "GNO" | "BNB" | "ARB" | "BAS" | "BTC" | "BCH" | "COSM" | "THOR" | "MAYA" | "DOGE" | "LTC" | "SOL" | "TRX" | "SUI" | "STRK" | "MON" | "HYPE" | "XPL" | "MNT" | "INK" | "MEGA" | "BERA" | "CRO" | "KAT" | "ETRL" | "FLOW" | "CELO" | "PLUME" | "STORY" | "ZKS" | "BLAST" | "WLD" | "HEM" | "SEI" | "LIN" | "SCR" | "S" | "UNI" | "BOB" | "MODE" | "SON" | "ZEC" | "NEAR" | "TON" | "ABS" | "RH" | undefined;
@@ -103,6 +103,8 @@ const getChainShortName = (chainId) => {
103
103
  return 'TON';
104
104
  case types_1.KnownChainIds.AbstractMainnet:
105
105
  return 'ABS';
106
+ case types_1.KnownChainIds.RobinhoodMainnet:
107
+ return 'RH';
106
108
  default: {
107
109
  (0, assertUnreachable_1.assertUnreachable)(chainId);
108
110
  }
@@ -92,6 +92,8 @@ const getNativeFeeAssetReference = (chainNamespace, chainReference) => {
92
92
  return caip_1.ASSET_REFERENCE.Mode;
93
93
  case caip_1.CHAIN_REFERENCE.SoneiumMainnet:
94
94
  return caip_1.ASSET_REFERENCE.Soneium;
95
+ case caip_1.CHAIN_REFERENCE.RobinhoodMainnet:
96
+ return caip_1.ASSET_REFERENCE.Robinhood;
95
97
  default:
96
98
  throw new Error(`Chain namespace ${chainNamespace} on ${chainReference} not supported.`);
97
99
  }
@@ -15,6 +15,8 @@ export * from './getChainShortName';
15
15
  export * from './getNativeFeeAssetReference';
16
16
  export * from './historyTimeframe';
17
17
  export * from './makeAsset/makeAsset';
18
+ export * from './paymentUri/constants';
19
+ export * from './paymentUri/paymentUri';
18
20
  export * from './promises';
19
21
  export * from './sha256';
20
22
  export * from './timeout';
package/dist/cjs/index.js CHANGED
@@ -33,6 +33,8 @@ __exportStar(require("./getChainShortName"), exports);
33
33
  __exportStar(require("./getNativeFeeAssetReference"), exports);
34
34
  __exportStar(require("./historyTimeframe"), exports);
35
35
  __exportStar(require("./makeAsset/makeAsset"), exports);
36
+ __exportStar(require("./paymentUri/constants"), exports);
37
+ __exportStar(require("./paymentUri/paymentUri"), exports);
36
38
  __exportStar(require("./promises"), exports);
37
39
  __exportStar(require("./sha256"), exports);
38
40
  __exportStar(require("./timeout"), exports);
@@ -0,0 +1,3 @@
1
+ import type { ChainId } from '@shapeshiftoss/caip';
2
+ export declare const CHAIN_ID_TO_URN_SCHEME: Partial<Record<ChainId, string>>;
3
+ export declare const URN_SCHEME_TO_CHAIN_ID: Partial<Record<string, ChainId>>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.URN_SCHEME_TO_CHAIN_ID = exports.CHAIN_ID_TO_URN_SCHEME = void 0;
4
+ const caip_1 = require("@shapeshiftoss/caip");
5
+ exports.CHAIN_ID_TO_URN_SCHEME = {
6
+ [caip_1.ethChainId]: 'ethereum',
7
+ [caip_1.arbitrumChainId]: 'arbitrum',
8
+ [caip_1.optimismChainId]: 'optimism',
9
+ [caip_1.polygonChainId]: 'polygon',
10
+ [caip_1.bscChainId]: 'smartchain',
11
+ [caip_1.avalancheChainId]: 'avalanchec',
12
+ [caip_1.baseChainId]: 'base',
13
+ [caip_1.gnosisChainId]: 'xdai',
14
+ [caip_1.btcChainId]: 'bitcoin',
15
+ [caip_1.bchChainId]: 'bitcoincash',
16
+ [caip_1.dogeChainId]: 'dogecoin',
17
+ [caip_1.ltcChainId]: 'litecoin',
18
+ [caip_1.zecChainId]: 'zcash',
19
+ [caip_1.thorchainChainId]: 'thorchain',
20
+ [caip_1.cosmosChainId]: 'cosmos',
21
+ [caip_1.mayachainChainId]: 'mayachain',
22
+ [caip_1.solanaChainId]: 'solana',
23
+ [caip_1.tronChainId]: 'tron',
24
+ };
25
+ exports.URN_SCHEME_TO_CHAIN_ID = {
26
+ ...Object.fromEntries(Object.entries(exports.CHAIN_ID_TO_URN_SCHEME).map(([chainId, scheme]) => [scheme, chainId])),
27
+ // The app emitted doge: QR codes for a year before adopting Dogecoin Core's scheme
28
+ doge: caip_1.dogeChainId,
29
+ };
@@ -0,0 +1,7 @@
1
+ import type { Asset } from '@shapeshiftoss/types';
2
+ export type BuildPaymentUriArgs = {
3
+ address: string;
4
+ asset: Pick<Asset, 'assetId' | 'chainId' | 'precision'>;
5
+ amountCryptoPrecision?: string;
6
+ };
7
+ export declare const buildPaymentUri: (args: BuildPaymentUriArgs) => string;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPaymentUri = void 0;
4
+ const caip_1 = require("@shapeshiftoss/caip");
5
+ const bigAmount_1 = require("../bigAmount/bigAmount");
6
+ const bignumber_1 = require("../bignumber/bignumber");
7
+ const constants_1 = require("./constants");
8
+ const toBaseUnit = (asset, amountCryptoPrecision) => bigAmount_1.BigAmount.fromPrecision({ value: amountCryptoPrecision, precision: asset.precision }).toBaseUnit();
9
+ // EIP-681 encourages scientific notation, and writes it without the exponent's plus sign
10
+ const toEip681Amount = (amountCryptoBaseUnit) => (0, bignumber_1.bn)(amountCryptoBaseUnit).toExponential().replace('+', '').replace('e0', '');
11
+ const buildEvmUri = ({ address, asset, amountCryptoPrecision }) => {
12
+ const target = `@${Number((0, caip_1.fromChainId)(asset.chainId).chainReference)}`;
13
+ const { assetNamespace, assetReference } = (0, caip_1.fromAssetId)(asset.assetId);
14
+ // transfer(address,uint256) is erc20's alone, so erc721 and erc1155 get no amount
15
+ const takesAmount = assetNamespace === caip_1.ASSET_NAMESPACE.slip44 || assetNamespace === caip_1.ASSET_NAMESPACE.erc20;
16
+ if (!amountCryptoPrecision || !takesAmount)
17
+ return `ethereum:${address}${target}`;
18
+ const amount = toEip681Amount(toBaseUnit(asset, amountCryptoPrecision));
19
+ return assetNamespace === caip_1.ASSET_NAMESPACE.erc20
20
+ ? `ethereum:${assetReference}${target}/transfer?address=${address}&uint256=${amount}`
21
+ : `ethereum:${address}${target}?value=${amount}`;
22
+ };
23
+ const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
24
+ // The same check as web3.js's PublicKey: base58 that decodes to exactly 32 bytes
25
+ const isSolanaPublicKey = (address) => {
26
+ let value = 0n;
27
+ for (const char of address) {
28
+ const digit = BASE58_ALPHABET.indexOf(char);
29
+ if (digit === -1)
30
+ return false;
31
+ value = value * 58n + BigInt(digit);
32
+ }
33
+ const leadingZeroBytes = address.length - address.replace(/^1+/, '').length;
34
+ const significantBytes = value === 0n ? 0 : Math.ceil(value.toString(16).length / 2);
35
+ return leadingZeroBytes + significantBytes === 32;
36
+ };
37
+ const buildSolanaUri = ({ address, asset, amountCryptoPrecision }) => {
38
+ if (!amountCryptoPrecision)
39
+ return address;
40
+ if (!isSolanaPublicKey(address))
41
+ throw new Error(`Invalid Solana address: ${address}`);
42
+ // Solana Pay normalises the amount rather than passing it through verbatim
43
+ const amount = (0, bignumber_1.bn)(amountCryptoPrecision).toFixed();
44
+ const { assetNamespace, assetReference } = (0, caip_1.fromAssetId)(asset.assetId);
45
+ if (assetNamespace === caip_1.ASSET_NAMESPACE.splToken) {
46
+ // Solana Pay takes decimal ui units, and the recipient is the native account, not its ATA
47
+ return `solana:${address}?amount=${amount}&spl-token=${assetReference}`;
48
+ }
49
+ return `solana:${address}?amount=${amount}`;
50
+ };
51
+ const buildTonUri = ({ address, asset, amountCryptoPrecision }) => {
52
+ if (!amountCryptoPrecision)
53
+ return address;
54
+ const { assetNamespace, assetReference } = (0, caip_1.fromAssetId)(asset.assetId);
55
+ const amount = toBaseUnit(asset, amountCryptoPrecision);
56
+ // A jetton transfer names its master contract, and its amount is in the jetton's own units
57
+ if (assetNamespace === caip_1.ASSET_NAMESPACE.jetton) {
58
+ return `ton://transfer/${address}?jetton=${assetReference}&amount=${amount}`;
59
+ }
60
+ return `ton://transfer/${address}?amount=${amount}`;
61
+ };
62
+ // BIP-21 takes decimal coin units
63
+ const buildBip21Uri = ({ address, asset, amountCryptoPrecision }) => {
64
+ const scheme = constants_1.CHAIN_ID_TO_URN_SCHEME[asset.chainId];
65
+ if (!amountCryptoPrecision || !scheme)
66
+ return address;
67
+ // CashAddr already carries its scheme
68
+ const target = address.startsWith(`${scheme}:`) ? address.slice(scheme.length + 1) : address;
69
+ return `${scheme}:${target}?amount=${(0, bignumber_1.bn)(amountCryptoPrecision).toFixed()}`;
70
+ };
71
+ const buildPaymentUri = (args) => {
72
+ const { asset, amountCryptoPrecision } = args;
73
+ if (amountCryptoPrecision) {
74
+ const amount = (0, bignumber_1.bn)(amountCryptoPrecision);
75
+ if (!amount.isFinite() || amount.isNegative()) {
76
+ throw new Error(`Invalid payment amount: ${amountCryptoPrecision}`);
77
+ }
78
+ if ((amount.decimalPlaces() ?? 0) > asset.precision) {
79
+ throw new Error(`Payment amount ${amountCryptoPrecision} exceeds ${asset.precision} decimals`);
80
+ }
81
+ }
82
+ switch ((0, caip_1.fromChainId)(args.asset.chainId).chainNamespace) {
83
+ case caip_1.CHAIN_NAMESPACE.Utxo:
84
+ case caip_1.CHAIN_NAMESPACE.CosmosSdk:
85
+ return buildBip21Uri(args);
86
+ case caip_1.CHAIN_NAMESPACE.Evm:
87
+ return buildEvmUri(args);
88
+ case caip_1.CHAIN_NAMESPACE.Solana:
89
+ return buildSolanaUri(args);
90
+ case caip_1.CHAIN_NAMESPACE.Ton:
91
+ return buildTonUri(args);
92
+ default:
93
+ return args.address;
94
+ }
95
+ };
96
+ exports.buildPaymentUri = buildPaymentUri;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const paymentUri_1 = require("./paymentUri");
5
+ const asset = (assetId, chainId, precision) => ({
6
+ assetId,
7
+ chainId,
8
+ precision,
9
+ });
10
+ const BTC = asset('bip122:000000000019d6689c085ae165831e93/slip44:0', 'bip122:000000000019d6689c085ae165831e93', 8);
11
+ const BCH = asset('bip122:000000000000000000651ef99cb9fcbe/slip44:145', 'bip122:000000000000000000651ef99cb9fcbe', 8);
12
+ const ETH = asset('eip155:1/slip44:60', 'eip155:1', 18);
13
+ const BASE_ETH = asset('eip155:8453/slip44:60', 'eip155:8453', 18);
14
+ const USDC = asset('eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 'eip155:1', 6);
15
+ const FOX = asset('eip155:1/erc20:0xc770eefad204b5180df6a14ee197d99d808ee52d', 'eip155:1', 18);
16
+ const SOL = asset('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', 9);
17
+ const WIF = asset('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', 6);
18
+ const TON = asset('ton:mainnet/slip44:607', 'ton:mainnet', 9);
19
+ const TON_USDT = asset('ton:mainnet/jetton:EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs', 'ton:mainnet', 6);
20
+ const ATOM = asset('cosmos:cosmoshub-4/slip44:118', 'cosmos:cosmoshub-4', 6);
21
+ const BTC_ADDRESS = 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzz';
22
+ const EVM_ADDRESS = '0xAbC0000000000000000000000000000000000001';
23
+ const SOL_ADDRESS = 'GsbwXfJraMomNxBcjYLcG3mxkBUiyWXAB32fGbSMQRdW';
24
+ const TON_ADDRESS = 'UQBFtnj3-yYK4p7XqXsyC5LNxfL2Rm5m0m3F0pvfDy7YQ9tk';
25
+ (0, vitest_1.describe)('buildPaymentUri', () => {
26
+ (0, vitest_1.it)('builds a BIP-21 uri with a decimal coin amount', () => {
27
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: BTC_ADDRESS, asset: BTC, amountCryptoPrecision: '0.05' })).toBe(`bitcoin:${BTC_ADDRESS}?amount=0.05`);
28
+ });
29
+ (0, vitest_1.it)('does not double-prefix a cashaddr that already carries its scheme', () => {
30
+ const address = 'bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y';
31
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address, asset: BCH, amountCryptoPrecision: '1' })).toBe('bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y?amount=1');
32
+ });
33
+ (0, vitest_1.it)('carries the chain id so an l2 deposit cannot be paid on mainnet', () => {
34
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: BASE_ETH, amountCryptoPrecision: '2' })).toBe(`ethereum:${EVM_ADDRESS}@8453?value=2e18`);
35
+ });
36
+ (0, vitest_1.it)('targets the contract and moves the destination into the call for an erc20', () => {
37
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: USDC, amountCryptoPrecision: '1.5' })).toBe(`ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=${EVM_ADDRESS}&uint256=1.5e6`);
38
+ });
39
+ (0, vitest_1.it)('keeps every digit of a full-precision erc20 amount', () => {
40
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({
41
+ address: EVM_ADDRESS,
42
+ asset: FOX,
43
+ amountCryptoPrecision: '123.123456789012345678',
44
+ })).toBe(`ethereum:0xc770eefad204b5180df6a14ee197d99d808ee52d@1/transfer?address=${EVM_ADDRESS}&uint256=1.23123456789012345678e20`);
45
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: USDC, amountCryptoPrecision: '0.000001' })).toBe(`ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48@1/transfer?address=${EVM_ADDRESS}&uint256=1`);
46
+ });
47
+ (0, vitest_1.it)('builds a Solana Pay uri with ui units', () => {
48
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: SOL_ADDRESS, asset: SOL, amountCryptoPrecision: '1.5' })).toBe(`solana:${SOL_ADDRESS}?amount=1.5`);
49
+ });
50
+ (0, vitest_1.it)('names the mint and keeps the native account as recipient for an spl token', () => {
51
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: SOL_ADDRESS, asset: WIF, amountCryptoPrecision: '0.1' })).toBe(`solana:${SOL_ADDRESS}?amount=0.1&spl-token=EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm`);
52
+ });
53
+ (0, vitest_1.it)('builds a ton transfer link in nanotons', () => {
54
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: TON_ADDRESS, asset: TON, amountCryptoPrecision: '1.5' })).toBe(`ton://transfer/${TON_ADDRESS}?amount=1500000000`);
55
+ });
56
+ (0, vitest_1.it)('names the jetton master and uses the jetton units for a jetton', () => {
57
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: TON_ADDRESS, asset: TON_USDT, amountCryptoPrecision: '5' })).toBe(`ton://transfer/${TON_ADDRESS}?jetton=EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs&amount=5000000`);
58
+ });
59
+ (0, vitest_1.it)('uses the cosmos sdk scheme', () => {
60
+ const address = 'cosmos1zqf0dq3nl3fhr8xk3pd2yq2sxwv0h2gd8qzptc';
61
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address, asset: ATOM, amountCryptoPrecision: '1' })).toBe(`cosmos:${address}?amount=1`);
62
+ });
63
+ (0, vitest_1.it)('falls back to the bare address on a chain with no scheme', () => {
64
+ const address = '0x04a1b2c3';
65
+ const starknet = asset('starknet:SN_MAIN/slip44:9004', 'starknet:SN_MAIN', 18);
66
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address, asset: starknet, amountCryptoPrecision: '1' })).toBe(address);
67
+ });
68
+ (0, vitest_1.it)('omits the amount from schemes that need one, but keeps the evm chain id', () => {
69
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: BTC_ADDRESS, asset: BTC })).toBe(BTC_ADDRESS);
70
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: SOL_ADDRESS, asset: SOL })).toBe(SOL_ADDRESS);
71
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: TON_ADDRESS, asset: TON_USDT })).toBe(TON_ADDRESS);
72
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: BASE_ETH })).toBe(`ethereum:${EVM_ADDRESS}@8453`);
73
+ });
74
+ (0, vitest_1.it)('passes a zero amount through', () => {
75
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: BTC_ADDRESS, asset: BTC, amountCryptoPrecision: '0' })).toBe(`bitcoin:${BTC_ADDRESS}?amount=0`);
76
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: ETH, amountCryptoPrecision: '0' })).toBe(`ethereum:${EVM_ADDRESS}@1?value=0`);
77
+ });
78
+ (0, vitest_1.it)('throws on a Solana address that is not a public key, as Solana Pay did', () => {
79
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: 'not-a-pubkey', asset: SOL })).toBe('not-a-pubkey');
80
+ (0, vitest_1.expect)(() => (0, paymentUri_1.buildPaymentUri)({ address: 'not-a-pubkey', asset: SOL, amountCryptoPrecision: '1' })).toThrow('Invalid Solana address');
81
+ (0, vitest_1.expect)(() => (0, paymentUri_1.buildPaymentUri)({ address: `${SOL_ADDRESS}1`, asset: SOL, amountCryptoPrecision: '1' })).toThrow('Invalid Solana address');
82
+ });
83
+ (0, vitest_1.it)('writes a scientific-notation amount as a plain decimal', () => {
84
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: BTC_ADDRESS, asset: BTC, amountCryptoPrecision: '1e-8' })).toBe(`bitcoin:${BTC_ADDRESS}?amount=0.00000001`);
85
+ (0, vitest_1.expect)((0, paymentUri_1.buildPaymentUri)({ address: SOL_ADDRESS, asset: SOL, amountCryptoPrecision: '1e-9' })).toBe(`solana:${SOL_ADDRESS}?amount=0.000000001`);
86
+ });
87
+ (0, vitest_1.it)('throws on an amount finer than the asset', () => {
88
+ (0, vitest_1.expect)(() => (0, paymentUri_1.buildPaymentUri)({ address: BTC_ADDRESS, asset: BTC, amountCryptoPrecision: '0.000000001' })).toThrow('exceeds 8 decimals');
89
+ (0, vitest_1.expect)(() => (0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: USDC, amountCryptoPrecision: '0.0000001' })).toThrow('exceeds 6 decimals');
90
+ });
91
+ (0, vitest_1.it)('throws on an amount no wallet could pay', () => {
92
+ (0, vitest_1.expect)(() => (0, paymentUri_1.buildPaymentUri)({ address: BTC_ADDRESS, asset: BTC, amountCryptoPrecision: '-1' })).toThrow('Invalid payment amount');
93
+ (0, vitest_1.expect)(() => (0, paymentUri_1.buildPaymentUri)({ address: EVM_ADDRESS, asset: ETH, amountCryptoPrecision: 'abc' })).toThrow('Invalid payment amount');
94
+ });
95
+ });