@pushchain/core 6.0.20 → 6.0.21

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 (111) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/package.json +1 -1
  3. package/src/lib/cache/cache.js +2 -0
  4. package/src/lib/constants/abi/index.d.ts +1 -0
  5. package/src/lib/constants/abi/index.js +8 -1
  6. package/src/lib/constants/abi/pc20.evm.d.ts +217 -0
  7. package/src/lib/constants/abi/pc20.evm.js +150 -0
  8. package/src/lib/constants/abi/prc20.evm.d.ts +172 -0
  9. package/src/lib/constants/abi/prc20.evm.js +97 -0
  10. package/src/lib/constants/abi/universalGatewayV0.json +1 -1
  11. package/src/lib/constants/chain.js +38 -8
  12. package/src/lib/constants/selectors.js +2 -2
  13. package/src/lib/constants/tokens.js +4 -3
  14. package/src/lib/formatters.js +16 -6
  15. package/src/lib/generated/uexecutor/v1/query.js +2 -4
  16. package/src/lib/generated/uexecutor/v1/types.d.ts +14 -0
  17. package/src/lib/generated/uexecutor/v1/types.js +90 -47
  18. package/src/lib/generated/uexecutor/v2/query.js +2 -4
  19. package/src/lib/generated/uexecutor/v2/types.d.ts +20 -0
  20. package/src/lib/generated/uexecutor/v2/types.js +49 -33
  21. package/src/lib/generated/v1/tx.js +30 -37
  22. package/src/lib/index.d.ts +3 -0
  23. package/src/lib/index.js +21 -3
  24. package/src/lib/internal/buffer.js +1 -2
  25. package/src/lib/orchestrator/cea-utils.js +73 -83
  26. package/src/lib/orchestrator/internals/account-manager.js +130 -139
  27. package/src/lib/orchestrator/internals/cascade.d.ts +0 -6
  28. package/src/lib/orchestrator/internals/cascade.js +1184 -1059
  29. package/src/lib/orchestrator/internals/confirmation.js +138 -148
  30. package/src/lib/orchestrator/internals/error-decoder.js +1 -1
  31. package/src/lib/orchestrator/internals/errors.js +12 -3
  32. package/src/lib/orchestrator/internals/execute-funds-only.d.ts +2 -2
  33. package/src/lib/orchestrator/internals/execute-funds-only.js +297 -259
  34. package/src/lib/orchestrator/internals/execute-funds-payload.d.ts +2 -2
  35. package/src/lib/orchestrator/internals/execute-funds-payload.js +220 -220
  36. package/src/lib/orchestrator/internals/execute-standard.d.ts +0 -8
  37. package/src/lib/orchestrator/internals/execute-standard.js +339 -342
  38. package/src/lib/orchestrator/internals/gas-calculator.js +497 -520
  39. package/src/lib/orchestrator/internals/gas-usd-sizer.js +101 -110
  40. package/src/lib/orchestrator/internals/gateway-client.js +102 -103
  41. package/src/lib/orchestrator/internals/helpers.js +30 -27
  42. package/src/lib/orchestrator/internals/inbound-tracker.js +234 -238
  43. package/src/lib/orchestrator/internals/outbound-sync.js +463 -452
  44. package/src/lib/orchestrator/internals/outbound-tracker.js +27 -34
  45. package/src/lib/orchestrator/internals/payload-builder.d.ts +2 -2
  46. package/src/lib/orchestrator/internals/payload-builder.js +119 -100
  47. package/src/lib/orchestrator/internals/pc-usd-oracle.js +121 -129
  48. package/src/lib/orchestrator/internals/pc20/address-codec.d.ts +31 -0
  49. package/src/lib/orchestrator/internals/pc20/address-codec.js +124 -0
  50. package/src/lib/orchestrator/internals/pc20/chain-namespace.d.ts +68 -0
  51. package/src/lib/orchestrator/internals/pc20/chain-namespace.js +136 -0
  52. package/src/lib/orchestrator/internals/pc20/errors.d.ts +160 -0
  53. package/src/lib/orchestrator/internals/pc20/errors.js +236 -0
  54. package/src/lib/orchestrator/internals/pc20/export.d.ts +172 -0
  55. package/src/lib/orchestrator/internals/pc20/export.js +412 -0
  56. package/src/lib/orchestrator/internals/pc20/gate.d.ts +67 -0
  57. package/src/lib/orchestrator/internals/pc20/gate.js +184 -0
  58. package/src/lib/orchestrator/internals/pc20/resolver.d.ts +187 -0
  59. package/src/lib/orchestrator/internals/pc20/resolver.js +658 -0
  60. package/src/lib/orchestrator/internals/pc20/svm.d.ts +189 -0
  61. package/src/lib/orchestrator/internals/pc20/svm.js +286 -0
  62. package/src/lib/orchestrator/internals/pc20/tracking.d.ts +46 -0
  63. package/src/lib/orchestrator/internals/pc20/tracking.js +109 -0
  64. package/src/lib/orchestrator/internals/preflight.js +1 -1
  65. package/src/lib/orchestrator/internals/progress-route-hooks.js +2 -2
  66. package/src/lib/orchestrator/internals/push-chain-tx.js +319 -329
  67. package/src/lib/orchestrator/internals/quote.js +90 -94
  68. package/src/lib/orchestrator/internals/rescue.js +76 -79
  69. package/src/lib/orchestrator/internals/response-builder.d.ts +28 -1
  70. package/src/lib/orchestrator/internals/response-builder.js +817 -701
  71. package/src/lib/orchestrator/internals/route-handlers.d.ts +7 -15
  72. package/src/lib/orchestrator/internals/route-handlers.js +1512 -1178
  73. package/src/lib/orchestrator/internals/signing.js +78 -83
  74. package/src/lib/orchestrator/internals/svm-bridge.d.ts +2 -2
  75. package/src/lib/orchestrator/internals/svm-bridge.js +161 -93
  76. package/src/lib/orchestrator/internals/svm-helpers.d.ts +15 -1
  77. package/src/lib/orchestrator/internals/svm-helpers.js +68 -24
  78. package/src/lib/orchestrator/internals/svm-rent.d.ts +19 -1
  79. package/src/lib/orchestrator/internals/svm-rent.js +163 -141
  80. package/src/lib/orchestrator/internals/tx-transformer.js +26 -23
  81. package/src/lib/orchestrator/internals/uea-manager.js +89 -86
  82. package/src/lib/orchestrator/max-pc-for-gas.js +5 -6
  83. package/src/lib/orchestrator/orchestrator.js +217 -200
  84. package/src/lib/orchestrator/orchestrator.types.d.ts +121 -1
  85. package/src/lib/orchestrator/orchestrator.types.js +19 -0
  86. package/src/lib/orchestrator/payload-builders.d.ts +2 -2
  87. package/src/lib/orchestrator/payload-builders.js +26 -24
  88. package/src/lib/orchestrator/route-detector.js +26 -9
  89. package/src/lib/orchestrator/svm-idl/ix-encoder.js +1 -2
  90. package/src/lib/orchestrator/svm-idl/normalize-address.js +2 -2
  91. package/src/lib/orchestrator/svm-idl/resolve.js +1 -2
  92. package/src/lib/price-fetch/price-fetch.js +67 -68
  93. package/src/lib/progress-hook/progress-hook.js +124 -72
  94. package/src/lib/push-chain/push-chain.js +171 -153
  95. package/src/lib/push-client/push-client.js +223 -237
  96. package/src/lib/universal/account/account.js +349 -365
  97. package/src/lib/universal/signer/signer.js +306 -294
  98. package/src/lib/universal-tx-detector/auto-detect.js +55 -60
  99. package/src/lib/universal-tx-detector/cascade.js +160 -172
  100. package/src/lib/universal-tx-detector/child-inbounds.js +67 -75
  101. package/src/lib/universal-tx-detector/classify.js +2 -3
  102. package/src/lib/universal-tx-detector/detector-svm.js +139 -147
  103. package/src/lib/universal-tx-detector/detector.js +145 -157
  104. package/src/lib/universal-tx-detector/normalize.js +7 -7
  105. package/src/lib/utils/external-tx-hash.js +1 -2
  106. package/src/lib/utils.d.ts +44 -0
  107. package/src/lib/utils.js +762 -683
  108. package/src/lib/vm-client/evm-client.js +186 -202
  109. package/src/lib/vm-client/svm-client.d.ts +1 -1
  110. package/src/lib/vm-client/svm-client.js +220 -224
  111. package/src/lib/vm-client/vm-client.types.d.ts +14 -2
package/src/lib/utils.js CHANGED
@@ -7,6 +7,9 @@ const enums_1 = require("./constants/enums");
7
7
  const tokens_1 = require("./constants/tokens");
8
8
  const chain_1 = require("./constants/chain");
9
9
  const viem_1 = require("viem");
10
+ const resolver_1 = require("./orchestrator/internals/pc20/resolver");
11
+ const errors_1 = require("./orchestrator/internals/pc20/errors");
12
+ const helpers_1 = require("./orchestrator/internals/helpers");
10
13
  const buffer_1 = require("buffer");
11
14
  const ix_encoder_1 = require("./orchestrator/svm-idl/ix-encoder");
12
15
  const registry_1 = require("./orchestrator/svm-idl/registry");
@@ -19,715 +22,791 @@ const registry_1 = require("./orchestrator/svm-idl/registry");
19
22
  * and universal account conversions.
20
23
  */
21
24
  class Utils {
25
+ static account = {
26
+ /*
27
+ * Converts a UniversalAccount into a CAIP-10 style address string.
28
+ *
29
+ * Format: `namespace:chainId:address`
30
+ * Namespace is derived from the chain's VM type using VM_NAMESPACE.
31
+ *
32
+ * @param {UniversalAccount} account - The account to convert.
33
+ * @returns {string} A CAIP-10 formatted string.
34
+ *
35
+ * @example
36
+ * Utils.account.toChainAgnostic({
37
+ * chain: CHAIN.ETHEREUM_SEPOLIA,
38
+ * address: '0xabc'
39
+ * })
40
+ * // → 'eip155:11155111:0xabc'
41
+ */
42
+ toChainAgnostic: account_1.toChainAgnostic,
43
+ toUniversal: account_1.toUniversal,
44
+ /**
45
+ * Converts a CAIP-10 formatted string into a UniversalAccount.
46
+ *
47
+ * @param {string} caip - A CAIP-10 address string (e.g., 'eip155:1:0xabc...').
48
+ * @returns {UniversalAccount} The resolved account.
49
+ * @throws {Error} If the CAIP string is invalid or unsupported.
50
+ *
51
+ * @example
52
+ * Utils.account.fromChainAgnostic('eip155:11155111:0xabc...')
53
+ * // → { chain: CHAIN.ETHEREUM_SEPOLIA, address: '0xabc...' }
54
+ */
55
+ fromChainAgnostic: account_1.fromChainAgnostic,
56
+ convertOriginToExecutor: account_1.convertOriginToExecutor,
57
+ convertExecutorToOriginAccount: account_1.convertExecutorToOriginAccount,
58
+ deriveExecutorAccount: account_1.deriveExecutorAccount,
59
+ resolveControllerAccount: account_1.resolveControllerAccount,
60
+ };
22
61
  /**
23
- * Internal: resolves a CHAIN enum from either a CHAIN value or a PushChain client instance.
24
- */
25
- static resolveChainFromInput(chainOrClient) {
26
- var _a;
27
- if (!chainOrClient)
28
- return undefined;
29
- if (typeof chainOrClient === 'string')
30
- return chainOrClient;
31
- // PushChain client → get origin chain from signer account
32
- try {
33
- const originAccount = (_a = chainOrClient.universal) === null || _a === void 0 ? void 0 : _a.origin;
34
- if (originAccount && originAccount.chain)
35
- return originAccount.chain;
36
- }
37
- catch (_b) {
38
- // ignore
39
- }
40
- return undefined;
41
- }
42
- }
43
- exports.Utils = Utils;
44
- Utils.account = {
45
- /*
46
- * Converts a UniversalAccount into a CAIP-10 style address string.
47
- *
48
- * Format: `namespace:chainId:address`
49
- * Namespace is derived from the chain's VM type using VM_NAMESPACE.
50
- *
51
- * @param {UniversalAccount} account - The account to convert.
52
- * @returns {string} A CAIP-10 formatted string.
62
+ * SVM (Solana) escape-hatch utilities.
53
63
  *
54
- * @example
55
- * Utils.account.toChainAgnostic({
56
- * chain: CHAIN.ETHEREUM_SEPOLIA,
57
- * address: '0xabc'
58
- * })
59
- * // → 'eip155:11155111:0xabc'
60
- */
61
- toChainAgnostic: account_1.toChainAgnostic,
62
- toUniversal: account_1.toUniversal,
63
- /**
64
- * Converts a CAIP-10 formatted string into a UniversalAccount.
64
+ * `PushChain.utils.helpers.encodeTxData({ abi: idl, ... })` auto-registers
65
+ * the Anchor IDL internally, so normal callers never need these.
65
66
  *
66
- * @param {string} caip - A CAIP-10 address string (e.g., 'eip155:1:0xabc...').
67
- * @returns {UniversalAccount} The resolved account.
68
- * @throws {Error} If the CAIP string is invalid or unsupported.
69
- *
70
- * @example
71
- * Utils.account.fromChainAgnostic('eip155:11155111:0xabc...')
72
- * // → { chain: CHAIN.ETHEREUM_SEPOLIA, address: '0xabc...' }
73
- */
74
- fromChainAgnostic: account_1.fromChainAgnostic,
75
- convertOriginToExecutor: account_1.convertOriginToExecutor,
76
- convertExecutorToOriginAccount: account_1.convertExecutorToOriginAccount,
77
- deriveExecutorAccount: account_1.deriveExecutorAccount,
78
- resolveControllerAccount: account_1.resolveControllerAccount,
79
- };
80
- /**
81
- * SVM (Solana) escape-hatch utilities.
82
- *
83
- * `PushChain.utils.helpers.encodeTxData({ abi: idl, ... })` auto-registers
84
- * the Anchor IDL internally, so normal callers never need these.
85
- *
86
- * Use `registerIdl` directly only when you build `data` manually (raw
87
- * Anchor discriminator + Borsh bytes) and bypass `encodeTxData`.
88
- */
89
- Utils.svm = {
90
- registerIdl: registry_1.registerIdl,
91
- getRegisteredIdls: registry_1.getRegisteredIdls,
92
- };
93
- Utils.signer = {
94
- /**
95
- * Converts various signer types (viem, ethers v6, Solana) into a UniversalSigner.
96
- */
97
- toUniversalFromKeypair: signer_1.toUniversalFromKeypair,
98
- /**
99
- * Constructs a UniversalSignerSkeleton from raw signing functions.
100
- */
101
- construct: signer_1.construct,
102
- /**
103
- * Converts a UniversalSignerSkeleton to a UniversalSigner.
67
+ * Use `registerIdl` directly only when you build `data` manually (raw
68
+ * Anchor discriminator + Borsh bytes) and bypass `encodeTxData`.
104
69
  */
105
- toUniversal: signer_1.toUniversal,
106
- };
107
- Utils.chains = {
108
- /**
109
- * Returns the list of supported chains for a given Push network.
110
- * Future-proofed to return an object with a `chains` array.
111
- *
112
- * @param {PUSH_NETWORK} network - The Push network environment.
113
- * @returns {{ chains: CHAIN[] }} Object containing supported chains.
114
- *
115
- * @example
116
- * Utils.chains.getSupportedChains(PushChain.CONSTANTS.PUSH_NETWORK.TESTNET)
117
- * // => { chains: [CHAIN.ETHEREUM_SEPOLIA, CHAIN.SOLANA_DEVNET] }
118
- *
119
- * @example
120
- * Utils.chains.getSupportedChains(PushChain.CONSTANTS.PUSH_NETWORK.MAINNET)
121
- * // => { chains: [] }
122
- */
123
- getSupportedChains: (network) => {
124
- var _a;
125
- // Current support: expose test/dev chains; mainnet returns empty until GA
126
- const mapping = {
127
- [enums_1.PUSH_NETWORK.MAINNET]: [],
128
- [enums_1.PUSH_NETWORK.TESTNET]: [
129
- enums_1.CHAIN.PUSH_TESTNET_DONUT,
130
- enums_1.CHAIN.ETHEREUM_SEPOLIA,
131
- enums_1.CHAIN.ARBITRUM_SEPOLIA,
132
- enums_1.CHAIN.BASE_SEPOLIA,
133
- enums_1.CHAIN.BNB_TESTNET,
134
- enums_1.CHAIN.SOLANA_DEVNET,
135
- ],
136
- [enums_1.PUSH_NETWORK.TESTNET_DONUT]: [
137
- enums_1.CHAIN.PUSH_TESTNET_DONUT,
138
- enums_1.CHAIN.ETHEREUM_SEPOLIA,
139
- enums_1.CHAIN.ARBITRUM_SEPOLIA,
140
- enums_1.CHAIN.BASE_SEPOLIA,
141
- enums_1.CHAIN.BNB_TESTNET,
142
- enums_1.CHAIN.SOLANA_DEVNET,
143
- ],
144
- [enums_1.PUSH_NETWORK.LOCALNET]: [
145
- enums_1.CHAIN.PUSH_TESTNET_DONUT,
146
- enums_1.CHAIN.ETHEREUM_SEPOLIA,
147
- enums_1.CHAIN.ARBITRUM_SEPOLIA,
148
- enums_1.CHAIN.BASE_SEPOLIA,
149
- enums_1.CHAIN.BNB_TESTNET,
150
- enums_1.CHAIN.SOLANA_DEVNET,
151
- ],
152
- };
153
- return { chains: (_a = mapping[network]) !== null && _a !== void 0 ? _a : [] };
154
- },
155
- /**
156
- * Returns the list of supported chain names for a given Push network.
157
- * Similar to getSupportedChains but returns chain names instead of namespaces.
158
- *
159
- * @param {PUSH_NETWORK} network - The Push network environment.
160
- * @returns {{ chains: string[] }} Object containing supported chain names.
161
- *
162
- * @example
163
- * Utils.chains.getSupportedChainsByName(PushChain.CONSTANTS.PUSH_NETWORK.TESTNET)
164
- * // => { chains: ['PUSH_TESTNET_DONUT', 'ETHEREUM_SEPOLIA', 'ARBITRUM_SEPOLIA', ...] }
165
- */
166
- getSupportedChainsByName: (network) => {
167
- const { chains } = Utils.chains.getSupportedChains(network);
168
- return {
169
- chains: chains
170
- .map((chain) => Utils.chains.getChainName(chain))
171
- .filter((name) => name !== undefined),
172
- };
173
- },
174
- getChainName: (chainNamespace) => {
175
- // Special case: prefer PUSH_TESTNET_DONUT over PUSH_TESTNET for 'eip155:42101'
176
- if (chainNamespace === 'eip155:42101') {
177
- return 'PUSH_TESTNET_DONUT';
178
- }
179
- const chainEntries = Object.entries(enums_1.CHAIN);
180
- const foundEntry = chainEntries.find((entry) => entry[1] === chainNamespace);
181
- if (!foundEntry) {
70
+ static svm = {
71
+ registerIdl: registry_1.registerIdl,
72
+ getRegisteredIdls: registry_1.getRegisteredIdls,
73
+ };
74
+ static signer = {
75
+ /**
76
+ * Converts various signer types (viem, ethers v6, Solana) into a UniversalSigner.
77
+ */
78
+ toUniversalFromKeypair: signer_1.toUniversalFromKeypair,
79
+ /**
80
+ * Constructs a UniversalSignerSkeleton from raw signing functions.
81
+ */
82
+ construct: signer_1.construct,
83
+ /**
84
+ * Converts a UniversalSignerSkeleton to a UniversalSigner.
85
+ */
86
+ toUniversal: signer_1.toUniversal,
87
+ };
88
+ static chains = {
89
+ /**
90
+ * Returns the list of supported chains for a given Push network.
91
+ * Future-proofed to return an object with a `chains` array.
92
+ *
93
+ * @param {PUSH_NETWORK} network - The Push network environment.
94
+ * @returns {{ chains: CHAIN[] }} Object containing supported chains.
95
+ *
96
+ * @example
97
+ * Utils.chains.getSupportedChains(PushChain.CONSTANTS.PUSH_NETWORK.TESTNET)
98
+ * // => { chains: [CHAIN.ETHEREUM_SEPOLIA, CHAIN.SOLANA_DEVNET] }
99
+ *
100
+ * @example
101
+ * Utils.chains.getSupportedChains(PushChain.CONSTANTS.PUSH_NETWORK.MAINNET)
102
+ * // => { chains: [] }
103
+ */
104
+ getSupportedChains: (network) => {
105
+ // Current support: expose test/dev chains; mainnet returns empty until GA
106
+ const mapping = {
107
+ [enums_1.PUSH_NETWORK.MAINNET]: [],
108
+ [enums_1.PUSH_NETWORK.TESTNET]: [
109
+ enums_1.CHAIN.PUSH_TESTNET_DONUT,
110
+ enums_1.CHAIN.ETHEREUM_SEPOLIA,
111
+ enums_1.CHAIN.ARBITRUM_SEPOLIA,
112
+ enums_1.CHAIN.BASE_SEPOLIA,
113
+ enums_1.CHAIN.BNB_TESTNET,
114
+ enums_1.CHAIN.SOLANA_DEVNET,
115
+ ],
116
+ [enums_1.PUSH_NETWORK.TESTNET_DONUT]: [
117
+ enums_1.CHAIN.PUSH_TESTNET_DONUT,
118
+ enums_1.CHAIN.ETHEREUM_SEPOLIA,
119
+ enums_1.CHAIN.ARBITRUM_SEPOLIA,
120
+ enums_1.CHAIN.BASE_SEPOLIA,
121
+ enums_1.CHAIN.BNB_TESTNET,
122
+ enums_1.CHAIN.SOLANA_DEVNET,
123
+ ],
124
+ [enums_1.PUSH_NETWORK.LOCALNET]: [
125
+ enums_1.CHAIN.PUSH_TESTNET_DONUT,
126
+ enums_1.CHAIN.ETHEREUM_SEPOLIA,
127
+ enums_1.CHAIN.ARBITRUM_SEPOLIA,
128
+ enums_1.CHAIN.BASE_SEPOLIA,
129
+ enums_1.CHAIN.BNB_TESTNET,
130
+ enums_1.CHAIN.SOLANA_DEVNET,
131
+ ],
132
+ };
133
+ return { chains: mapping[network] ?? [] };
134
+ },
135
+ /**
136
+ * Returns the list of supported chain names for a given Push network.
137
+ * Similar to getSupportedChains but returns chain names instead of namespaces.
138
+ *
139
+ * @param {PUSH_NETWORK} network - The Push network environment.
140
+ * @returns {{ chains: string[] }} Object containing supported chain names.
141
+ *
142
+ * @example
143
+ * Utils.chains.getSupportedChainsByName(PushChain.CONSTANTS.PUSH_NETWORK.TESTNET)
144
+ * // => { chains: ['PUSH_TESTNET_DONUT', 'ETHEREUM_SEPOLIA', 'ARBITRUM_SEPOLIA', ...] }
145
+ */
146
+ getSupportedChainsByName: (network) => {
147
+ const { chains } = Utils.chains.getSupportedChains(network);
148
+ return {
149
+ chains: chains
150
+ .map((chain) => Utils.chains.getChainName(chain))
151
+ .filter((name) => name !== undefined),
152
+ };
153
+ },
154
+ getChainName: (chainNamespace) => {
155
+ // Special case: prefer PUSH_TESTNET_DONUT over PUSH_TESTNET for 'eip155:42101'
156
+ if (chainNamespace === 'eip155:42101') {
157
+ return 'PUSH_TESTNET_DONUT';
158
+ }
159
+ const chainEntries = Object.entries(enums_1.CHAIN);
160
+ const foundEntry = chainEntries.find((entry) => entry[1] === chainNamespace);
161
+ if (!foundEntry) {
162
+ return undefined;
163
+ }
164
+ return foundEntry[0];
165
+ },
166
+ /**
167
+ * Returns the chain namespace (e.g., 'eip155:11155111') for a given chain name.
168
+ * Reverse of getChainName. If input is already a namespace, it is returned.
169
+ *
170
+ * @param {string} chainName - The CHAIN key name (e.g., 'ETHEREUM_SEPOLIA' or 'PUSH_TESTNET_DONUT')
171
+ * or an existing namespace (e.g., 'eip155:11155111').
172
+ * @returns {string | undefined} The chain namespace, or undefined if unsupported.
173
+ */
174
+ getChainNamespace: (chainName) => {
175
+ // If already a valid namespace value, return as-is
176
+ const namespaceValues = Object.values(enums_1.CHAIN);
177
+ if (namespaceValues.includes(chainName)) {
178
+ return chainName;
179
+ }
180
+ // Map enum key -> value
181
+ const namespace = enums_1.CHAIN[chainName];
182
+ if (typeof namespace === 'string') {
183
+ return namespace;
184
+ }
182
185
  return undefined;
183
- }
184
- return foundEntry[0];
185
- },
186
- /**
187
- * Returns the chain namespace (e.g., 'eip155:11155111') for a given chain name.
188
- * Reverse of getChainName. If input is already a namespace, it is returned.
189
- *
190
- * @param {string} chainName - The CHAIN key name (e.g., 'ETHEREUM_SEPOLIA' or 'PUSH_TESTNET_DONUT')
191
- * or an existing namespace (e.g., 'eip155:11155111').
192
- * @returns {string | undefined} The chain namespace, or undefined if unsupported.
193
- */
194
- getChainNamespace: (chainName) => {
195
- // If already a valid namespace value, return as-is
196
- const namespaceValues = Object.values(enums_1.CHAIN);
197
- if (namespaceValues.includes(chainName)) {
198
- return chainName;
199
- }
200
- // Map enum key -> value
201
- const namespace = enums_1.CHAIN[chainName];
202
- if (typeof namespace === 'string') {
203
- return namespace;
204
- }
205
- return undefined;
206
- },
207
- };
208
- Utils.helpers = {
209
- /**
210
- * @deprecated Use PushChain.utils.chains.getChainNamespace(chainName) instead.
211
- * Alias maintained for backwards compatibility. Logs a deprecation warning
212
- * and delegates to Utils.chains.getChainNamespace.
213
- */
214
- getChainName: (() => {
215
- let warned = false;
216
- return (chainName) => {
217
- if (!warned) {
218
- console.warn('[DEPRECATED] PushChain.utils.helper.getChainName is deprecated. ' +
219
- 'Use PushChain.utils.chains.getChainNamespace(chainName) instead.');
220
- warned = true;
221
- }
222
- return Utils.chains.getChainName(chainName);
223
- };
224
- })(),
225
- /**
226
- * Encode calldata for a universal tx.
227
- *
228
- * Pass `abi` for EVM targets, or `idl` for SVM (Anchor) targets. Exactly
229
- * one of the two is required.
230
- *
231
- * @example EVM
232
- * PushChain.utils.helpers.encodeTxData({
233
- * abi: erc20Abi,
234
- * functionName: 'transfer',
235
- * args: [to, amount],
236
- * });
237
- *
238
- * @example SVM (Anchor)
239
- * PushChain.utils.helpers.encodeTxData({
240
- * idl: counterIdl,
241
- * functionName: 'increment',
242
- * args: [],
243
- * });
244
- */
245
- encodeTxData({ abi, idl, functionName, args = [], }) {
246
- if (abi !== undefined && idl !== undefined) {
247
- throw new Error("encodeTxData: pass either 'abi' (EVM) or 'idl' (SVM), not both");
248
- }
249
- if (abi === undefined && idl === undefined) {
250
- throw new Error("encodeTxData: either 'abi' (EVM) or 'idl' (SVM) must be provided");
251
- }
252
- if (!Array.isArray(args)) {
253
- throw new Error('Arguments must be an array');
254
- }
255
- // SVM path: explicit idl, or legacy abi-as-IDL for backward compat.
256
- const svmIdl = idl !== null && idl !== void 0 ? idl : ((0, ix_encoder_1.isAnchorIdl)(abi) ? abi : undefined);
257
- if (svmIdl) {
258
- (0, registry_1.registerIdl)(svmIdl);
259
- const bytes = (0, ix_encoder_1.encodeAnchorIxData)(svmIdl, functionName, args);
260
- return `0x${buffer_1.Buffer.from(bytes).toString('hex')}`;
261
- }
262
- // EVM path
263
- if (!Array.isArray(abi)) {
264
- throw new Error('ABI must be an array');
265
- }
266
- const functionAbi = abi.find((f) => f.name === functionName);
267
- if (!functionAbi) {
268
- throw new Error(`Function '${functionName}' not found in ABI`);
269
- }
270
- try {
271
- const data = (0, viem_1.encodeFunctionData)({ abi, functionName, args });
272
- return data;
273
- }
274
- catch (error) {
275
- throw new Error(`Failed to encode function '${functionName}': ${error instanceof Error ? error.message : 'Unknown error'}`);
276
- }
277
- },
278
- /**
279
- * Multiplies a string representation of a number by a given exponent of base 10 (10^exponent).
280
- *
281
- * This is commonly used for converting human-readable token amounts to their on-chain representation.
282
- * For example, converting "1.5" ETH to wei (18 decimals) would be parseUnits("1.5", 18).
283
- *
284
- * @param {string} value - The string representation of the number to multiply.
285
- * @param {number | {decimals: number}} exponent - The exponent (number of decimal places) or an object with decimals property.
286
- * @returns {bigint} The result as a bigint.
287
- *
288
- * @example
289
- * Utils.helpers.parseUnits('420', 9)
290
- * // → 420000000000n
291
- *
292
- * @example
293
- * Utils.helpers.parseUnits('1.5', 18)
294
- * // → 1500000000000000000n
295
- *
296
- * @example
297
- * Utils.helpers.parseUnits('1.5', {decimals: 18})
298
- * // → 1500000000000000000n
299
- */
300
- parseUnits(value, exponent) {
301
- // Validate inputs
302
- if (typeof value !== 'string') {
303
- throw new Error('Value must be a string');
304
- }
305
- // Extract the actual exponent value from either number or object
306
- let actualExponent;
307
- if (typeof exponent === 'number') {
308
- actualExponent = exponent;
309
- }
310
- else if (typeof exponent === 'object' &&
311
- exponent !== null &&
312
- 'decimals' in exponent) {
313
- actualExponent = exponent.decimals;
314
- }
315
- else {
316
- throw new Error('Exponent must be a number or an object with decimals property');
317
- }
318
- if (typeof actualExponent !== 'number') {
319
- throw new Error('Exponent must be a number');
320
- }
321
- if (!Number.isInteger(actualExponent)) {
322
- throw new Error('Exponent must be an integer');
323
- }
324
- if (actualExponent < 0) {
325
- throw new Error('Exponent must be non-negative');
326
- }
327
- // Handle empty string
328
- if (value.trim() === '') {
329
- throw new Error('Value cannot be empty');
330
- }
331
- // Remove any whitespace
332
- const trimmedValue = value.trim();
333
- // Check for valid number format
334
- if (!/^-?\d*\.?\d*$/.test(trimmedValue)) {
335
- throw new Error('Value must be a valid number string');
336
- }
337
- // Handle case where value is just a decimal point
338
- if (trimmedValue === '.' ||
339
- trimmedValue === '-.' ||
340
- trimmedValue === '') {
341
- throw new Error('Value must be a valid number string');
342
- }
343
- try {
344
- // Split on decimal point to handle fractional values
345
- const parts = trimmedValue.split('.');
346
- const integerPart = parts[0] || '0';
347
- const fractionalPart = parts[1] || '';
348
- // Check if fractional part has more digits than the exponent allows
349
- if (fractionalPart.length > actualExponent) {
350
- throw new Error(`Value has more decimal places (${fractionalPart.length}) than exponent allows (${actualExponent})`);
351
- }
352
- // Pad fractional part with zeros to match exponent
353
- const paddedFractionalPart = fractionalPart.padEnd(actualExponent, '0');
354
- // Combine integer and fractional parts
355
- const combinedValue = integerPart + paddedFractionalPart;
356
- // Convert to bigint
357
- return BigInt(combinedValue);
358
- }
359
- catch (error) {
360
- if (error instanceof Error &&
361
- error.message.includes('decimal places')) {
362
- throw error;
186
+ },
187
+ };
188
+ static helpers = {
189
+ /**
190
+ * @deprecated Use PushChain.utils.chains.getChainNamespace(chainName) instead.
191
+ * Alias maintained for backwards compatibility. Logs a deprecation warning
192
+ * and delegates to Utils.chains.getChainNamespace.
193
+ */
194
+ getChainName: (() => {
195
+ let warned = false;
196
+ return (chainName) => {
197
+ if (!warned) {
198
+ console.warn('[DEPRECATED] PushChain.utils.helper.getChainName is deprecated. ' +
199
+ 'Use PushChain.utils.chains.getChainNamespace(chainName) instead.');
200
+ warned = true;
201
+ }
202
+ return Utils.chains.getChainName(chainName);
203
+ };
204
+ })(),
205
+ /**
206
+ * Encode calldata for a universal tx.
207
+ *
208
+ * Pass `abi` for EVM targets, or `idl` for SVM (Anchor) targets. Exactly
209
+ * one of the two is required.
210
+ *
211
+ * @example EVM
212
+ * PushChain.utils.helpers.encodeTxData({
213
+ * abi: erc20Abi,
214
+ * functionName: 'transfer',
215
+ * args: [to, amount],
216
+ * });
217
+ *
218
+ * @example SVM (Anchor)
219
+ * PushChain.utils.helpers.encodeTxData({
220
+ * idl: counterIdl,
221
+ * functionName: 'increment',
222
+ * args: [],
223
+ * });
224
+ */
225
+ encodeTxData({ abi, idl, functionName, args = [], }) {
226
+ if (abi !== undefined && idl !== undefined) {
227
+ throw new Error("encodeTxData: pass either 'abi' (EVM) or 'idl' (SVM), not both");
363
228
  }
364
- throw new Error(`Failed to parse value '${value}': ${error instanceof Error ? error.message : 'Invalid number format'}`);
365
- }
366
- },
367
- /**
368
- * Formats a value from smallest units to human-readable string.
369
- *
370
- * Supports both EVM-style (like ethers/viem) and Push-style (options object) usage patterns.
371
- * Always returns a string for UI safety.
372
- *
373
- * @param {bigint | string} value - The value in smallest units (e.g., "1500000" or 1500000000000000000n).
374
- * @param {number | {decimals: number; precision?: number}} decimalsOrOptions - Token decimals or options object.
375
- * @returns {string} Human-readable string (e.g., "1.5").
376
- *
377
- * @example
378
- * // EVM-style usage
379
- * Utils.helpers.formatUnits(1500000000000000000n, 18)
380
- * // → "1.5"
381
- *
382
- * @example
383
- * // Push-style usage
384
- * Utils.helpers.formatUnits("1500000", { decimals: 6 })
385
- * // → "1.5"
386
- *
387
- * @example
388
- * // With precision (truncate after 2 decimals)
389
- * Utils.helpers.formatUnits("1234567", { decimals: 6, precision: 2 })
390
- * // → "1.23"
391
- */
392
- formatUnits(value, decimalsOrOptions) {
393
- // Validate inputs
394
- if (typeof value !== 'bigint' && typeof value !== 'string') {
395
- throw new Error('Value must be a bigint or string');
396
- }
397
- // Extract decimals and precision from the second parameter
398
- let decimals;
399
- let precision;
400
- if (typeof decimalsOrOptions === 'number') {
401
- // EVM-style: formatUnits(value, decimals)
402
- decimals = decimalsOrOptions;
403
- }
404
- else if (typeof decimalsOrOptions === 'object' &&
405
- decimalsOrOptions !== null &&
406
- 'decimals' in decimalsOrOptions) {
407
- // Push-style: formatUnits(value, { decimals, precision? })
408
- decimals = decimalsOrOptions.decimals;
409
- precision = decimalsOrOptions.precision;
410
- }
411
- else {
412
- throw new Error('Second parameter must be a number (decimals) or an object with decimals property');
413
- }
414
- // Validate decimals
415
- if (typeof decimals !== 'number') {
416
- throw new Error('Decimals must be a number');
417
- }
418
- if (!Number.isInteger(decimals)) {
419
- throw new Error('Decimals must be an integer');
420
- }
421
- if (decimals < 0) {
422
- throw new Error('Decimals must be non-negative');
423
- }
424
- // Validate precision if provided
425
- if (precision !== undefined) {
426
- if (typeof precision !== 'number') {
427
- throw new Error('Precision must be a number');
229
+ if (abi === undefined && idl === undefined) {
230
+ throw new Error("encodeTxData: either 'abi' (EVM) or 'idl' (SVM) must be provided");
428
231
  }
429
- if (!Number.isInteger(precision)) {
430
- throw new Error('Precision must be an integer');
232
+ if (!Array.isArray(args)) {
233
+ throw new Error('Arguments must be an array');
431
234
  }
432
- if (precision < 0) {
433
- throw new Error('Precision must be non-negative');
235
+ // SVM path: explicit idl, or legacy abi-as-IDL for backward compat.
236
+ const svmIdl = idl ?? ((0, ix_encoder_1.isAnchorIdl)(abi) ? abi : undefined);
237
+ if (svmIdl) {
238
+ (0, registry_1.registerIdl)(svmIdl);
239
+ const bytes = (0, ix_encoder_1.encodeAnchorIxData)(svmIdl, functionName, args);
240
+ return `0x${buffer_1.Buffer.from(bytes).toString('hex')}`;
434
241
  }
435
- }
436
- try {
437
- // Convert string to bigint if needed
438
- const bigintValue = typeof value === 'string' ? BigInt(value) : value;
439
- let formatted = (0, viem_1.formatUnits)(bigintValue, decimals);
440
- // Ensure at least one decimal place for backward compatibility
441
- // (only when decimals > 0, matching prior ethers behavior)
442
- if (decimals > 0 && !formatted.includes('.')) {
443
- formatted = formatted + '.0';
444
- }
445
- // Apply precision if specified
242
+ // EVM path
243
+ if (!Array.isArray(abi)) {
244
+ throw new Error('ABI must be an array');
245
+ }
246
+ const functionAbi = abi.find((f) => f.name === functionName);
247
+ if (!functionAbi) {
248
+ throw new Error(`Function '${functionName}' not found in ABI`);
249
+ }
250
+ try {
251
+ const data = (0, viem_1.encodeFunctionData)({ abi, functionName, args });
252
+ return data;
253
+ }
254
+ catch (error) {
255
+ throw new Error(`Failed to encode function '${functionName}': ${error instanceof Error ? error.message : 'Unknown error'}`);
256
+ }
257
+ },
258
+ /**
259
+ * Multiplies a string representation of a number by a given exponent of base 10 (10^exponent).
260
+ *
261
+ * This is commonly used for converting human-readable token amounts to their on-chain representation.
262
+ * For example, converting "1.5" ETH to wei (18 decimals) would be parseUnits("1.5", 18).
263
+ *
264
+ * @param {string} value - The string representation of the number to multiply.
265
+ * @param {number | {decimals: number}} exponent - The exponent (number of decimal places) or an object with decimals property.
266
+ * @returns {bigint} The result as a bigint.
267
+ *
268
+ * @example
269
+ * Utils.helpers.parseUnits('420', 9)
270
+ * // → 420000000000n
271
+ *
272
+ * @example
273
+ * Utils.helpers.parseUnits('1.5', 18)
274
+ * // → 1500000000000000000n
275
+ *
276
+ * @example
277
+ * Utils.helpers.parseUnits('1.5', {decimals: 18})
278
+ * // → 1500000000000000000n
279
+ */
280
+ parseUnits(value, exponent) {
281
+ // Validate inputs
282
+ if (typeof value !== 'string') {
283
+ throw new Error('Value must be a string');
284
+ }
285
+ // Extract the actual exponent value from either number or object
286
+ let actualExponent;
287
+ if (typeof exponent === 'number') {
288
+ actualExponent = exponent;
289
+ }
290
+ else if (typeof exponent === 'object' &&
291
+ exponent !== null &&
292
+ 'decimals' in exponent) {
293
+ actualExponent = exponent.decimals;
294
+ }
295
+ else {
296
+ throw new Error('Exponent must be a number or an object with decimals property');
297
+ }
298
+ if (typeof actualExponent !== 'number') {
299
+ throw new Error('Exponent must be a number');
300
+ }
301
+ if (!Number.isInteger(actualExponent)) {
302
+ throw new Error('Exponent must be an integer');
303
+ }
304
+ if (actualExponent < 0) {
305
+ throw new Error('Exponent must be non-negative');
306
+ }
307
+ // Handle empty string
308
+ if (value.trim() === '') {
309
+ throw new Error('Value cannot be empty');
310
+ }
311
+ // Remove any whitespace
312
+ const trimmedValue = value.trim();
313
+ // Check for valid number format
314
+ if (!/^-?\d*\.?\d*$/.test(trimmedValue)) {
315
+ throw new Error('Value must be a valid number string');
316
+ }
317
+ // Handle case where value is just a decimal point
318
+ if (trimmedValue === '.' ||
319
+ trimmedValue === '-.' ||
320
+ trimmedValue === '') {
321
+ throw new Error('Value must be a valid number string');
322
+ }
323
+ try {
324
+ // Split on decimal point to handle fractional values
325
+ const parts = trimmedValue.split('.');
326
+ const integerPart = parts[0] || '0';
327
+ const fractionalPart = parts[1] || '';
328
+ // Check if fractional part has more digits than the exponent allows
329
+ if (fractionalPart.length > actualExponent) {
330
+ throw new Error(`Value has more decimal places (${fractionalPart.length}) than exponent allows (${actualExponent})`);
331
+ }
332
+ // Pad fractional part with zeros to match exponent
333
+ const paddedFractionalPart = fractionalPart.padEnd(actualExponent, '0');
334
+ // Combine integer and fractional parts
335
+ const combinedValue = integerPart + paddedFractionalPart;
336
+ // Convert to bigint
337
+ return BigInt(combinedValue);
338
+ }
339
+ catch (error) {
340
+ if (error instanceof Error &&
341
+ error.message.includes('decimal places')) {
342
+ throw error;
343
+ }
344
+ throw new Error(`Failed to parse value '${value}': ${error instanceof Error ? error.message : 'Invalid number format'}`);
345
+ }
346
+ },
347
+ /**
348
+ * Formats a value from smallest units to human-readable string.
349
+ *
350
+ * Supports both EVM-style (like ethers/viem) and Push-style (options object) usage patterns.
351
+ * Always returns a string for UI safety.
352
+ *
353
+ * @param {bigint | string} value - The value in smallest units (e.g., "1500000" or 1500000000000000000n).
354
+ * @param {number | {decimals: number; precision?: number}} decimalsOrOptions - Token decimals or options object.
355
+ * @returns {string} Human-readable string (e.g., "1.5").
356
+ *
357
+ * @example
358
+ * // EVM-style usage
359
+ * Utils.helpers.formatUnits(1500000000000000000n, 18)
360
+ * // → "1.5"
361
+ *
362
+ * @example
363
+ * // Push-style usage
364
+ * Utils.helpers.formatUnits("1500000", { decimals: 6 })
365
+ * // → "1.5"
366
+ *
367
+ * @example
368
+ * // With precision (truncate after 2 decimals)
369
+ * Utils.helpers.formatUnits("1234567", { decimals: 6, precision: 2 })
370
+ * // → "1.23"
371
+ */
372
+ formatUnits(value, decimalsOrOptions) {
373
+ // Validate inputs
374
+ if (typeof value !== 'bigint' && typeof value !== 'string') {
375
+ throw new Error('Value must be a bigint or string');
376
+ }
377
+ // Extract decimals and precision from the second parameter
378
+ let decimals;
379
+ let precision;
380
+ if (typeof decimalsOrOptions === 'number') {
381
+ // EVM-style: formatUnits(value, decimals)
382
+ decimals = decimalsOrOptions;
383
+ }
384
+ else if (typeof decimalsOrOptions === 'object' &&
385
+ decimalsOrOptions !== null &&
386
+ 'decimals' in decimalsOrOptions) {
387
+ // Push-style: formatUnits(value, { decimals, precision? })
388
+ decimals = decimalsOrOptions.decimals;
389
+ precision = decimalsOrOptions.precision;
390
+ }
391
+ else {
392
+ throw new Error('Second parameter must be a number (decimals) or an object with decimals property');
393
+ }
394
+ // Validate decimals
395
+ if (typeof decimals !== 'number') {
396
+ throw new Error('Decimals must be a number');
397
+ }
398
+ if (!Number.isInteger(decimals)) {
399
+ throw new Error('Decimals must be an integer');
400
+ }
401
+ if (decimals < 0) {
402
+ throw new Error('Decimals must be non-negative');
403
+ }
404
+ // Validate precision if provided
446
405
  if (precision !== undefined) {
447
- const num = parseFloat(formatted);
448
- const factor = Math.pow(10, precision);
449
- const truncated = Math.floor(num * factor) / factor;
450
- return truncated.toString();
406
+ if (typeof precision !== 'number') {
407
+ throw new Error('Precision must be a number');
408
+ }
409
+ if (!Number.isInteger(precision)) {
410
+ throw new Error('Precision must be an integer');
411
+ }
412
+ if (precision < 0) {
413
+ throw new Error('Precision must be non-negative');
414
+ }
451
415
  }
452
- return formatted;
453
- }
454
- catch (error) {
455
- throw new Error(`Failed to format units: ${error instanceof Error ? error.message : 'Unknown error'}`);
456
- }
457
- },
458
- };
459
- Utils.conversion = {
460
- /**
461
- * Calculates the minimum amount out after applying slippage.
462
- *
463
- * Given an input amount and slippage in basis points, returns the minimum amount
464
- * that should be received after accounting for slippage.
465
- *
466
- * @param {string} amount - The input amount in smallest units (e.g., "100000000" for 100 USDC with 6 decimals)
467
- * @param {object} options - Configuration options
468
- * @param {number} options.slippageBps - Slippage in basis points (100 = 1%, 50 = 0.5%)
469
- * @returns {string} The minimum amount out in smallest units
470
- *
471
- * @example
472
- * // Calculate minimum amount for 100 USDC with 1% slippage
473
- * const amount = PushChain.utils.helpers.parseUnits("100", 6); // "100000000"
474
- * const minOut = PushChain.utils.conversion.slippageToMinAmount(amount, {
475
- * slippageBps: 100, // 1%
476
- * });
477
- * // => "99000000" (99 USDC in smallest units)
478
- *
479
- * @example
480
- * // Simple case with whole numbers
481
- * const minOut = PushChain.utils.conversion.slippageToMinAmount("100", {
482
- * slippageBps: 100, // 1%
483
- * });
484
- * // => "99"
485
- */
486
- slippageToMinAmount(amount, options) {
487
- // Validate inputs
488
- if (typeof amount !== 'string') {
489
- throw new Error('Amount must be a string');
490
- }
491
- if (typeof options.slippageBps !== 'number') {
492
- throw new Error('slippageBps must be a number');
493
- }
494
- if (!Number.isInteger(options.slippageBps)) {
495
- throw new Error('slippageBps must be an integer');
496
- }
497
- if (options.slippageBps < 0) {
498
- throw new Error('slippageBps must be non-negative');
499
- }
500
- if (options.slippageBps > 10000) {
501
- throw new Error('slippageBps cannot exceed 10000 (100%)');
502
- }
503
- // Handle empty string
504
- if (amount.trim() === '') {
505
- throw new Error('Amount cannot be empty');
506
- }
507
- try {
508
- // Convert amount to BigInt for precise calculation
509
- const amountBigInt = BigInt(amount);
510
- // Calculate slippage factor: (10000 - slippageBps) / 10000
511
- // For 1% slippage (100 bps): (10000 - 100) / 10000 = 0.99
512
- const slippageFactor = BigInt(10000 - options.slippageBps);
513
- // Calculate minimum amount: amount * slippageFactor / 10000
514
- const minAmountBigInt = (amountBigInt * slippageFactor) / BigInt(10000);
515
- return minAmountBigInt.toString();
516
- }
517
- catch (error) {
518
- throw new Error(`Failed to calculate slippage: ${error instanceof Error ? error.message : 'Invalid amount format'}`);
519
- }
520
- },
521
- };
522
- Utils.tokens = {
523
- /**
524
- * Returns supported moveable tokens as a flat list with chain info.
525
- * - If a specific chain or a PushChain client is passed, returns only that chain's tokens
526
- * - Otherwise returns tokens across all chains
527
- */
528
- getMoveableTokens(chainOrClient) {
529
- var _a;
530
- const chain = Utils.resolveChainFromInput(chainOrClient);
531
- const map = (k, t) => {
532
- var _a;
533
- const extra = t;
534
- return Object.assign(Object.assign({ chain: k, chainName: (_a = Utils.chains.getChainName(k)) !== null && _a !== void 0 ? _a : k, symbol: t.symbol, decimals: t.decimals, address: t.address, mechanism: t.mechanism }, (extra.sourceChain ? { sourceChain: extra.sourceChain } : {})), (extra.prc20Address ? { prc20Address: extra.prc20Address } : {}));
535
- };
536
- if (chain) {
537
- const list = (_a = tokens_1.MOVEABLE_TOKENS[chain]) !== null && _a !== void 0 ? _a : [];
538
- return { tokens: list.map((t) => map(chain, t)) };
539
- }
540
- const tokens = [];
541
- for (const [key, list] of Object.entries(tokens_1.MOVEABLE_TOKENS)) {
542
- const k = key;
543
- for (const t of list !== null && list !== void 0 ? list : [])
544
- tokens.push(map(k, t));
545
- }
546
- return { tokens };
547
- },
548
- /**
549
- * Returns supported payable tokens as a flat list with chain info.
550
- * - If a specific chain or a PushChain client is passed, returns only that chain's tokens
551
- * - Otherwise returns tokens across all chains
552
- */
553
- getPayableTokens(chainOrClient) {
554
- var _a, _b;
555
- const chain = Utils.resolveChainFromInput(chainOrClient);
556
- if (chain) {
557
- const list = (_a = tokens_1.PAYABLE_TOKENS[chain]) !== null && _a !== void 0 ? _a : [];
558
- return {
559
- tokens: list.map((t) => {
560
- var _a;
561
- return ({
562
- chain,
563
- chainName: (_a = Utils.chains.getChainName(chain)) !== null && _a !== void 0 ? _a : chain,
564
- symbol: t.symbol,
565
- decimals: t.decimals,
566
- address: t.address,
567
- mechanism: t.mechanism,
568
- });
569
- }),
570
- };
571
- }
572
- const tokens = [];
573
- for (const [key, list] of Object.entries(tokens_1.PAYABLE_TOKENS)) {
574
- const k = key;
575
- for (const t of list !== null && list !== void 0 ? list : []) {
576
- tokens.push({
416
+ try {
417
+ // Convert string to bigint if needed
418
+ const bigintValue = typeof value === 'string' ? BigInt(value) : value;
419
+ let formatted = (0, viem_1.formatUnits)(bigintValue, decimals);
420
+ // Ensure at least one decimal place for backward compatibility
421
+ // (only when decimals > 0, matching prior ethers behavior)
422
+ if (decimals > 0 && !formatted.includes('.')) {
423
+ formatted = formatted + '.0';
424
+ }
425
+ // Apply precision if specified
426
+ if (precision !== undefined) {
427
+ const num = parseFloat(formatted);
428
+ const factor = Math.pow(10, precision);
429
+ const truncated = Math.floor(num * factor) / factor;
430
+ return truncated.toString();
431
+ }
432
+ return formatted;
433
+ }
434
+ catch (error) {
435
+ throw new Error(`Failed to format units: ${error instanceof Error ? error.message : 'Unknown error'}`);
436
+ }
437
+ },
438
+ };
439
+ static conversion = {
440
+ /**
441
+ * Calculates the minimum amount out after applying slippage.
442
+ *
443
+ * Given an input amount and slippage in basis points, returns the minimum amount
444
+ * that should be received after accounting for slippage.
445
+ *
446
+ * @param {string} amount - The input amount in smallest units (e.g., "100000000" for 100 USDC with 6 decimals)
447
+ * @param {object} options - Configuration options
448
+ * @param {number} options.slippageBps - Slippage in basis points (100 = 1%, 50 = 0.5%)
449
+ * @returns {string} The minimum amount out in smallest units
450
+ *
451
+ * @example
452
+ * // Calculate minimum amount for 100 USDC with 1% slippage
453
+ * const amount = PushChain.utils.helpers.parseUnits("100", 6); // "100000000"
454
+ * const minOut = PushChain.utils.conversion.slippageToMinAmount(amount, {
455
+ * slippageBps: 100, // 1%
456
+ * });
457
+ * // => "99000000" (99 USDC in smallest units)
458
+ *
459
+ * @example
460
+ * // Simple case with whole numbers
461
+ * const minOut = PushChain.utils.conversion.slippageToMinAmount("100", {
462
+ * slippageBps: 100, // 1%
463
+ * });
464
+ * // => "99"
465
+ */
466
+ slippageToMinAmount(amount, options) {
467
+ // Validate inputs
468
+ if (typeof amount !== 'string') {
469
+ throw new Error('Amount must be a string');
470
+ }
471
+ if (typeof options.slippageBps !== 'number') {
472
+ throw new Error('slippageBps must be a number');
473
+ }
474
+ if (!Number.isInteger(options.slippageBps)) {
475
+ throw new Error('slippageBps must be an integer');
476
+ }
477
+ if (options.slippageBps < 0) {
478
+ throw new Error('slippageBps must be non-negative');
479
+ }
480
+ if (options.slippageBps > 10000) {
481
+ throw new Error('slippageBps cannot exceed 10000 (100%)');
482
+ }
483
+ // Handle empty string
484
+ if (amount.trim() === '') {
485
+ throw new Error('Amount cannot be empty');
486
+ }
487
+ try {
488
+ // Convert amount to BigInt for precise calculation
489
+ const amountBigInt = BigInt(amount);
490
+ // Calculate slippage factor: (10000 - slippageBps) / 10000
491
+ // For 1% slippage (100 bps): (10000 - 100) / 10000 = 0.99
492
+ const slippageFactor = BigInt(10000 - options.slippageBps);
493
+ // Calculate minimum amount: amount * slippageFactor / 10000
494
+ const minAmountBigInt = (amountBigInt * slippageFactor) / BigInt(10000);
495
+ return minAmountBigInt.toString();
496
+ }
497
+ catch (error) {
498
+ throw new Error(`Failed to calculate slippage: ${error instanceof Error ? error.message : 'Invalid amount format'}`);
499
+ }
500
+ },
501
+ };
502
+ static tokens = {
503
+ /**
504
+ * Returns supported moveable tokens as a flat list with chain info.
505
+ * - If a specific chain or a PushChain client is passed, returns only that chain's tokens
506
+ * - Otherwise returns tokens across all chains
507
+ */
508
+ getMoveableTokens(chainOrClient) {
509
+ const chain = Utils.resolveChainFromInput(chainOrClient);
510
+ const map = (k, t) => {
511
+ const extra = t;
512
+ return {
577
513
  chain: k,
578
- chainName: (_b = Utils.chains.getChainName(k)) !== null && _b !== void 0 ? _b : k,
514
+ chainName: Utils.chains.getChainName(k) ?? k,
579
515
  symbol: t.symbol,
580
516
  decimals: t.decimals,
581
517
  address: t.address,
582
518
  mechanism: t.mechanism,
583
- });
584
- }
585
- }
586
- return { tokens };
587
- },
588
- /**
589
- * Convert any supported origin-chain token into its mapped PRC20 token on Push Chain.
590
- *
591
- * @param token - Either a MoveableToken from `pushChainClient.moveable.token.*`
592
- * or an object with the origin chain and token address.
593
- * @returns An object describing the synthetic PRC-20 on Push Chain.
594
- *
595
- * @example
596
- * ```jsx
597
- * const { address, chain, symbol, decimals, network } =
598
- * PushChain.utils.tokens.getPRC20Address({
599
- * chain: CONSTANTS.CHAIN.ETHEREUM_SEPOLIA,
600
- * address: '0x...',
601
- * });
602
- * ```
603
- */
604
- getPRC20Address(token, options) {
605
- var _a, _b, _c, _d;
606
- const network = (_a = options === null || options === void 0 ? void 0 : options.network) !== null && _a !== void 0 ? _a : enums_1.PUSH_NETWORK.TESTNET_DONUT;
607
- const assertDeployed = (addr, ctx) => {
608
- if (!addr || addr === '0xTBD') {
609
- throw new Error(`PRC20 address not available for ${ctx} on network ${network}`);
610
- }
611
- return addr;
612
- };
613
- // PushChainMoveableToken (pETH, pSOL, pUSDT(BNB), …) carries its PRC-20 address
614
- // and origin `sourceChain` directly — no registry lookup needed.
615
- const pcToken = token;
616
- if (pcToken.prc20Address) {
617
- return {
618
- address: assertDeployed(pcToken.prc20Address, `${(_b = pcToken.symbol) !== null && _b !== void 0 ? _b : 'token'} (${(_c = pcToken.sourceChain) !== null && _c !== void 0 ? _c : 'unknown'})`),
619
- chain: pcToken.sourceChain,
620
- symbol: pcToken.symbol,
621
- decimals: pcToken.decimals,
622
- network,
519
+ ...(extra.sourceChain ? { sourceChain: extra.sourceChain } : {}),
520
+ ...(extra.prc20Address ? { prc20Address: extra.prc20Address } : {}),
521
+ };
623
522
  };
624
- }
625
- // Infer origin chain, symbol, decimals by matching against MOVEABLE_TOKENS
626
- let originChain;
627
- let tokenSymbol;
628
- let tokenDecimals;
629
- if ('symbol' in token) {
630
- // MoveableToken path: infer chain by symbol + address
523
+ if (chain) {
524
+ const list = tokens_1.MOVEABLE_TOKENS[chain] ?? [];
525
+ return { tokens: list.map((t) => map(chain, t)) };
526
+ }
527
+ const tokens = [];
631
528
  for (const [key, list] of Object.entries(tokens_1.MOVEABLE_TOKENS)) {
632
529
  const k = key;
633
- const match = (list !== null && list !== void 0 ? list : []).find((t) => t.symbol === token.symbol && t.address === token.address);
530
+ for (const t of list ?? [])
531
+ tokens.push(map(k, t));
532
+ }
533
+ return { tokens };
534
+ },
535
+ /**
536
+ * Returns supported payable tokens as a flat list with chain info.
537
+ * - If a specific chain or a PushChain client is passed, returns only that chain's tokens
538
+ * - Otherwise returns tokens across all chains
539
+ */
540
+ getPayableTokens(chainOrClient) {
541
+ const chain = Utils.resolveChainFromInput(chainOrClient);
542
+ if (chain) {
543
+ const list = tokens_1.PAYABLE_TOKENS[chain] ?? [];
544
+ return {
545
+ tokens: list.map((t) => ({
546
+ chain,
547
+ chainName: Utils.chains.getChainName(chain) ?? chain,
548
+ symbol: t.symbol,
549
+ decimals: t.decimals,
550
+ address: t.address,
551
+ mechanism: t.mechanism,
552
+ })),
553
+ };
554
+ }
555
+ const tokens = [];
556
+ for (const [key, list] of Object.entries(tokens_1.PAYABLE_TOKENS)) {
557
+ const k = key;
558
+ for (const t of list ?? []) {
559
+ tokens.push({
560
+ chain: k,
561
+ chainName: Utils.chains.getChainName(k) ?? k,
562
+ symbol: t.symbol,
563
+ decimals: t.decimals,
564
+ address: t.address,
565
+ mechanism: t.mechanism,
566
+ });
567
+ }
568
+ }
569
+ return { tokens };
570
+ },
571
+ /**
572
+ * Convert any supported origin-chain token into its mapped PRC20 token on Push Chain.
573
+ *
574
+ * @param token - Either a MoveableToken from `pushChainClient.moveable.token.*`
575
+ * or an object with the origin chain and token address.
576
+ * @returns An object describing the synthetic PRC-20 on Push Chain.
577
+ *
578
+ * @example
579
+ * ```jsx
580
+ * const { address, chain, symbol, decimals, network } =
581
+ * PushChain.utils.tokens.getPRC20Address({
582
+ * chain: CONSTANTS.CHAIN.ETHEREUM_SEPOLIA,
583
+ * address: '0x...',
584
+ * });
585
+ * ```
586
+ */
587
+ getPRC20Address(token, options) {
588
+ const network = options?.network ?? enums_1.PUSH_NETWORK.TESTNET_DONUT;
589
+ const assertDeployed = (addr, ctx) => {
590
+ if (!addr || addr === '0xTBD') {
591
+ throw new Error(`PRC20 address not available for ${ctx} on network ${network}`);
592
+ }
593
+ return addr;
594
+ };
595
+ // PushChainMoveableToken (pETH, pSOL, pUSDT(BNB), …) carries its PRC-20 address
596
+ // and origin `sourceChain` directly — no registry lookup needed.
597
+ const pcToken = token;
598
+ if (pcToken.prc20Address) {
599
+ return {
600
+ address: assertDeployed(pcToken.prc20Address, `${pcToken.symbol ?? 'token'} (${pcToken.sourceChain ?? 'unknown'})`),
601
+ chain: pcToken.sourceChain,
602
+ symbol: pcToken.symbol,
603
+ decimals: pcToken.decimals,
604
+ network,
605
+ };
606
+ }
607
+ // Infer origin chain, symbol, decimals by matching against MOVEABLE_TOKENS
608
+ let originChain;
609
+ let tokenSymbol;
610
+ let tokenDecimals;
611
+ if ('symbol' in token) {
612
+ // MoveableToken path: infer chain by symbol + address
613
+ for (const [key, list] of Object.entries(tokens_1.MOVEABLE_TOKENS)) {
614
+ const k = key;
615
+ const match = (list ?? []).find((t) => t.symbol === token.symbol && t.address === token.address);
616
+ if (match) {
617
+ originChain = k;
618
+ tokenSymbol = match.symbol;
619
+ tokenDecimals = match.decimals;
620
+ break;
621
+ }
622
+ }
623
+ }
624
+ else {
625
+ // { chain, address } path: trust the provided chain and resolve symbol via registry
626
+ originChain = token.chain;
627
+ const list = tokens_1.MOVEABLE_TOKENS[originChain] ?? [];
628
+ const match = list.find((t) => t.address === token.address);
634
629
  if (match) {
635
- originChain = k;
636
630
  tokenSymbol = match.symbol;
637
631
  tokenDecimals = match.decimals;
632
+ }
633
+ }
634
+ if (!originChain || !tokenSymbol || tokenDecimals === undefined) {
635
+ throw new Error('Unable to infer origin chain or token symbol for token');
636
+ }
637
+ const map = chain_1.SYNTHETIC_PUSH_ERC20[network];
638
+ if (!map) {
639
+ throw new Error(`No PRC20 address map configured for network: ${network}`);
640
+ }
641
+ // Map token → synthetic key by origin chain family
642
+ const isEthFamily = originChain === enums_1.CHAIN.ETHEREUM_MAINNET ||
643
+ originChain === enums_1.CHAIN.ETHEREUM_SEPOLIA;
644
+ const isArbFamily = originChain === enums_1.CHAIN.ARBITRUM_SEPOLIA;
645
+ const isBaseFamily = originChain === enums_1.CHAIN.BASE_SEPOLIA;
646
+ const isBnbFamily = originChain === enums_1.CHAIN.BNB_TESTNET;
647
+ const isSolFamily = originChain === enums_1.CHAIN.SOLANA_DEVNET;
648
+ let key;
649
+ switch (tokenSymbol) {
650
+ case 'ETH': {
651
+ if (isEthFamily)
652
+ key = 'pETH';
653
+ else if (isArbFamily)
654
+ key = 'pETH_ARB';
655
+ else if (isBaseFamily)
656
+ key = 'pETH_BASE';
657
+ else
658
+ throw new Error('Unsupported ETH origin chain for synthetic mapping');
659
+ break;
660
+ }
661
+ case 'SOL': {
662
+ if (!isSolFamily)
663
+ throw new Error('SOL token provided but origin is not Solana');
664
+ key = 'pSOL';
665
+ break;
666
+ }
667
+ case 'BNB': {
668
+ if (!isBnbFamily)
669
+ throw new Error('BNB token provided but origin is not BNB Testnet');
670
+ key = 'pBNB';
671
+ break;
672
+ }
673
+ case 'USDT': {
674
+ if (isEthFamily)
675
+ key = 'USDT_ETH';
676
+ else if (isArbFamily)
677
+ key = 'USDT_ARB';
678
+ else if (isBaseFamily)
679
+ key = 'USDT_BASE';
680
+ else if (isBnbFamily)
681
+ key = 'USDT_BSC';
682
+ else if (isSolFamily)
683
+ key = 'USDT_SOL';
684
+ else
685
+ throw new Error('Unsupported USDT origin chain for synthetic mapping');
686
+ break;
687
+ }
688
+ case 'USDC': {
689
+ if (isEthFamily)
690
+ key = 'USDC_ETH';
691
+ else if (isArbFamily)
692
+ key = 'USDC_ARB';
693
+ else if (isBaseFamily)
694
+ key = 'USDC_BASE';
695
+ else if (isBnbFamily)
696
+ key = 'USDC_BSC';
697
+ else if (isSolFamily)
698
+ key = 'USDC_SOL';
699
+ else
700
+ throw new Error('Unsupported USDC origin chain for synthetic mapping');
638
701
  break;
639
702
  }
703
+ default:
704
+ throw new Error(`Unsupported token symbol: ${tokenSymbol}`);
640
705
  }
641
- }
642
- else {
643
- // { chain, address } path: trust the provided chain and resolve symbol via registry
644
- originChain = token.chain;
645
- const list = (_d = tokens_1.MOVEABLE_TOKENS[originChain]) !== null && _d !== void 0 ? _d : [];
646
- const match = list.find((t) => t.address === token.address);
647
- if (match) {
648
- tokenSymbol = match.symbol;
649
- tokenDecimals = match.decimals;
706
+ return {
707
+ address: assertDeployed(map[key], `${tokenSymbol} on ${originChain}`),
708
+ chain: originChain,
709
+ symbol: tokenSymbol,
710
+ decimals: tokenDecimals,
711
+ network,
712
+ };
713
+ },
714
+ /**
715
+ * Resolve a PC20 token against UniversalCore's on-chain registry, and list
716
+ * every confirmed address it has across chains.
717
+ *
718
+ * Accepts either end of the mapping — an external wrapper or the canonical
719
+ * Push-native token — and always returns the canonical token at `address`,
720
+ * with every known deployment in `registry`.
721
+ *
722
+ * Unlike `getPRC20Address`, this is **asynchronous**: PC20 mappings are
723
+ * dynamic and live on chain, so there is no static table to consult. It is
724
+ * never symbol-based — identity is address-and-chain only.
725
+ *
726
+ * `chain` is optional. When omitted, the SDK works out which chain the
727
+ * address belongs to. Note the deliberate asymmetry with the transaction
728
+ * path: `funds.token` still *requires* `chain`, and sending with one that
729
+ * disagrees with where the funds actually are is rejected before any
730
+ * approval. Lookup is lenient; spending is not.
731
+ *
732
+ * @example
733
+ * ```ts
734
+ * const token = await PushChain.utils.tokens.getPC20Address('0xWrapper', {
735
+ * chain: CONSTANTS.CHAIN.ETHEREUM_SEPOLIA, // optional
736
+ * network: CONSTANTS.PUSH_NETWORK.TESTNET_DONUT,
737
+ * });
738
+ *
739
+ * token.address // '0x…' canonical Push-native PC20
740
+ * token.registry // [{ address, chain, chainName }, …]
741
+ * ```
742
+ *
743
+ * @throws {PC20WrapperNotRegisteredError} the address is unknown to the registry
744
+ * @throws {PC20RegistryMismatchError} forward/reverse registry disagreement
745
+ * @throws {PC20ExpectedButPRC20Error} a synthetic PRC20 was supplied
746
+ * @throws {PC20AmbiguousAddressError} `chain` omitted and several chains
747
+ * claim the address with different Push sources
748
+ */
749
+ async getPC20Address(address, options) {
750
+ const resolverOpts = {
751
+ network: options.network,
752
+ rpcUrls: options.rpcUrls,
753
+ strict: options.strict,
754
+ };
755
+ let chain = options.chain;
756
+ if (!chain) {
757
+ const discovered = await (0, resolver_1.discoverPC20Chain)(address, resolverOpts);
758
+ if (!discovered) {
759
+ throw new errors_1.PC20WrapperNotRegisteredError({
760
+ address,
761
+ hint: 'No PC20 registration found on any supported chain. If you know ' +
762
+ 'the chain, pass it as `chain` for a more specific error.',
763
+ });
764
+ }
765
+ chain = discovered.chain;
650
766
  }
767
+ const resolved = await (0, resolver_1.resolvePC20Token)(chain, address, resolverOpts);
768
+ // Confirmed deployments across every supported external chain — one
769
+ // multicall, since they all read UniversalCore on Push.
770
+ const deployments = await (0, resolver_1.listPC20Deployments)(resolved.pushAddress, resolverOpts);
771
+ const pushChain = (0, helpers_1.getPushChainForNetwork)(options.network);
772
+ const entry = (addr, c) => ({
773
+ address: addr,
774
+ chain: c,
775
+ chainName: Utils.chains.getChainName(c) ?? c,
776
+ });
777
+ return {
778
+ address: resolved.pushAddress,
779
+ name: resolved.name,
780
+ symbol: resolved.symbol,
781
+ decimals: resolved.decimals,
782
+ network: options.network,
783
+ registry: [
784
+ ...deployments.map((d) => entry(d.address, d.chain)),
785
+ // Canonical token last, matching the reviewed example.
786
+ entry(resolved.pushAddress, pushChain),
787
+ ],
788
+ };
789
+ },
790
+ };
791
+ /**
792
+ * Internal: resolves a CHAIN enum from either a CHAIN value or a PushChain client instance.
793
+ */
794
+ static resolveChainFromInput(chainOrClient) {
795
+ if (!chainOrClient)
796
+ return undefined;
797
+ if (typeof chainOrClient === 'string')
798
+ return chainOrClient;
799
+ // PushChain client → get origin chain from signer account
800
+ try {
801
+ const originAccount = chainOrClient.universal?.origin;
802
+ if (originAccount && originAccount.chain)
803
+ return originAccount.chain;
651
804
  }
652
- if (!originChain || !tokenSymbol || tokenDecimals === undefined) {
653
- throw new Error('Unable to infer origin chain or token symbol for token');
654
- }
655
- const map = chain_1.SYNTHETIC_PUSH_ERC20[network];
656
- if (!map) {
657
- throw new Error(`No PRC20 address map configured for network: ${network}`);
658
- }
659
- // Map token → synthetic key by origin chain family
660
- const isEthFamily = originChain === enums_1.CHAIN.ETHEREUM_MAINNET ||
661
- originChain === enums_1.CHAIN.ETHEREUM_SEPOLIA;
662
- const isArbFamily = originChain === enums_1.CHAIN.ARBITRUM_SEPOLIA;
663
- const isBaseFamily = originChain === enums_1.CHAIN.BASE_SEPOLIA;
664
- const isBnbFamily = originChain === enums_1.CHAIN.BNB_TESTNET;
665
- const isSolFamily = originChain === enums_1.CHAIN.SOLANA_DEVNET;
666
- let key;
667
- switch (tokenSymbol) {
668
- case 'ETH': {
669
- if (isEthFamily)
670
- key = 'pETH';
671
- else if (isArbFamily)
672
- key = 'pETH_ARB';
673
- else if (isBaseFamily)
674
- key = 'pETH_BASE';
675
- else
676
- throw new Error('Unsupported ETH origin chain for synthetic mapping');
677
- break;
678
- }
679
- case 'SOL': {
680
- if (!isSolFamily)
681
- throw new Error('SOL token provided but origin is not Solana');
682
- key = 'pSOL';
683
- break;
684
- }
685
- case 'BNB': {
686
- if (!isBnbFamily)
687
- throw new Error('BNB token provided but origin is not BNB Testnet');
688
- key = 'pBNB';
689
- break;
690
- }
691
- case 'USDT': {
692
- if (isEthFamily)
693
- key = 'USDT_ETH';
694
- else if (isArbFamily)
695
- key = 'USDT_ARB';
696
- else if (isBaseFamily)
697
- key = 'USDT_BASE';
698
- else if (isBnbFamily)
699
- key = 'USDT_BSC';
700
- else if (isSolFamily)
701
- key = 'USDT_SOL';
702
- else
703
- throw new Error('Unsupported USDT origin chain for synthetic mapping');
704
- break;
705
- }
706
- case 'USDC': {
707
- if (isEthFamily)
708
- key = 'USDC_ETH';
709
- else if (isArbFamily)
710
- key = 'USDC_ARB';
711
- else if (isBaseFamily)
712
- key = 'USDC_BASE';
713
- else if (isBnbFamily)
714
- key = 'USDC_BSC';
715
- else if (isSolFamily)
716
- key = 'USDC_SOL';
717
- else
718
- throw new Error('Unsupported USDC origin chain for synthetic mapping');
719
- break;
720
- }
721
- default:
722
- throw new Error(`Unsupported token symbol: ${tokenSymbol}`);
805
+ catch {
806
+ // ignore
723
807
  }
724
- return {
725
- address: assertDeployed(map[key], `${tokenSymbol} on ${originChain}`),
726
- chain: originChain,
727
- symbol: tokenSymbol,
728
- decimals: tokenDecimals,
729
- network,
730
- };
731
- },
732
- };
808
+ return undefined;
809
+ }
810
+ }
811
+ exports.Utils = Utils;
733
812
  //# sourceMappingURL=utils.js.map