@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.
- package/CHANGELOG.md +48 -0
- package/package.json +1 -1
- package/src/lib/cache/cache.js +2 -0
- package/src/lib/constants/abi/index.d.ts +1 -0
- package/src/lib/constants/abi/index.js +8 -1
- package/src/lib/constants/abi/pc20.evm.d.ts +217 -0
- package/src/lib/constants/abi/pc20.evm.js +150 -0
- package/src/lib/constants/abi/prc20.evm.d.ts +172 -0
- package/src/lib/constants/abi/prc20.evm.js +97 -0
- package/src/lib/constants/abi/universalGatewayV0.json +1 -1
- package/src/lib/constants/chain.js +38 -8
- package/src/lib/constants/selectors.js +2 -2
- package/src/lib/constants/tokens.js +4 -3
- package/src/lib/formatters.js +16 -6
- package/src/lib/generated/uexecutor/v1/query.js +2 -4
- package/src/lib/generated/uexecutor/v1/types.d.ts +14 -0
- package/src/lib/generated/uexecutor/v1/types.js +90 -47
- package/src/lib/generated/uexecutor/v2/query.js +2 -4
- package/src/lib/generated/uexecutor/v2/types.d.ts +20 -0
- package/src/lib/generated/uexecutor/v2/types.js +49 -33
- package/src/lib/generated/v1/tx.js +30 -37
- package/src/lib/index.d.ts +3 -0
- package/src/lib/index.js +21 -3
- package/src/lib/internal/buffer.js +1 -2
- package/src/lib/orchestrator/cea-utils.js +73 -83
- package/src/lib/orchestrator/internals/account-manager.js +130 -139
- package/src/lib/orchestrator/internals/cascade.d.ts +0 -6
- package/src/lib/orchestrator/internals/cascade.js +1184 -1059
- package/src/lib/orchestrator/internals/confirmation.js +138 -148
- package/src/lib/orchestrator/internals/error-decoder.js +1 -1
- package/src/lib/orchestrator/internals/errors.js +12 -3
- package/src/lib/orchestrator/internals/execute-funds-only.d.ts +2 -2
- package/src/lib/orchestrator/internals/execute-funds-only.js +297 -259
- package/src/lib/orchestrator/internals/execute-funds-payload.d.ts +2 -2
- package/src/lib/orchestrator/internals/execute-funds-payload.js +220 -220
- package/src/lib/orchestrator/internals/execute-standard.d.ts +0 -8
- package/src/lib/orchestrator/internals/execute-standard.js +339 -342
- package/src/lib/orchestrator/internals/gas-calculator.js +497 -520
- package/src/lib/orchestrator/internals/gas-usd-sizer.js +101 -110
- package/src/lib/orchestrator/internals/gateway-client.js +102 -103
- package/src/lib/orchestrator/internals/helpers.js +30 -27
- package/src/lib/orchestrator/internals/inbound-tracker.js +234 -238
- package/src/lib/orchestrator/internals/outbound-sync.js +463 -452
- package/src/lib/orchestrator/internals/outbound-tracker.js +27 -34
- package/src/lib/orchestrator/internals/payload-builder.d.ts +2 -2
- package/src/lib/orchestrator/internals/payload-builder.js +119 -100
- package/src/lib/orchestrator/internals/pc-usd-oracle.js +121 -129
- package/src/lib/orchestrator/internals/pc20/address-codec.d.ts +31 -0
- package/src/lib/orchestrator/internals/pc20/address-codec.js +124 -0
- package/src/lib/orchestrator/internals/pc20/chain-namespace.d.ts +68 -0
- package/src/lib/orchestrator/internals/pc20/chain-namespace.js +136 -0
- package/src/lib/orchestrator/internals/pc20/errors.d.ts +160 -0
- package/src/lib/orchestrator/internals/pc20/errors.js +236 -0
- package/src/lib/orchestrator/internals/pc20/export.d.ts +172 -0
- package/src/lib/orchestrator/internals/pc20/export.js +412 -0
- package/src/lib/orchestrator/internals/pc20/gate.d.ts +67 -0
- package/src/lib/orchestrator/internals/pc20/gate.js +184 -0
- package/src/lib/orchestrator/internals/pc20/resolver.d.ts +187 -0
- package/src/lib/orchestrator/internals/pc20/resolver.js +658 -0
- package/src/lib/orchestrator/internals/pc20/svm.d.ts +189 -0
- package/src/lib/orchestrator/internals/pc20/svm.js +286 -0
- package/src/lib/orchestrator/internals/pc20/tracking.d.ts +46 -0
- package/src/lib/orchestrator/internals/pc20/tracking.js +109 -0
- package/src/lib/orchestrator/internals/preflight.js +1 -1
- package/src/lib/orchestrator/internals/progress-route-hooks.js +2 -2
- package/src/lib/orchestrator/internals/push-chain-tx.js +319 -329
- package/src/lib/orchestrator/internals/quote.js +90 -94
- package/src/lib/orchestrator/internals/rescue.js +76 -79
- package/src/lib/orchestrator/internals/response-builder.d.ts +28 -1
- package/src/lib/orchestrator/internals/response-builder.js +817 -701
- package/src/lib/orchestrator/internals/route-handlers.d.ts +7 -15
- package/src/lib/orchestrator/internals/route-handlers.js +1512 -1178
- package/src/lib/orchestrator/internals/signing.js +78 -83
- package/src/lib/orchestrator/internals/svm-bridge.d.ts +2 -2
- package/src/lib/orchestrator/internals/svm-bridge.js +161 -93
- package/src/lib/orchestrator/internals/svm-helpers.d.ts +15 -1
- package/src/lib/orchestrator/internals/svm-helpers.js +68 -24
- package/src/lib/orchestrator/internals/svm-rent.d.ts +19 -1
- package/src/lib/orchestrator/internals/svm-rent.js +163 -141
- package/src/lib/orchestrator/internals/tx-transformer.js +26 -23
- package/src/lib/orchestrator/internals/uea-manager.js +89 -86
- package/src/lib/orchestrator/max-pc-for-gas.js +5 -6
- package/src/lib/orchestrator/orchestrator.js +217 -200
- package/src/lib/orchestrator/orchestrator.types.d.ts +121 -1
- package/src/lib/orchestrator/orchestrator.types.js +19 -0
- package/src/lib/orchestrator/payload-builders.d.ts +2 -2
- package/src/lib/orchestrator/payload-builders.js +26 -24
- package/src/lib/orchestrator/route-detector.js +26 -9
- package/src/lib/orchestrator/svm-idl/ix-encoder.js +1 -2
- package/src/lib/orchestrator/svm-idl/normalize-address.js +2 -2
- package/src/lib/orchestrator/svm-idl/resolve.js +1 -2
- package/src/lib/price-fetch/price-fetch.js +67 -68
- package/src/lib/progress-hook/progress-hook.js +124 -72
- package/src/lib/push-chain/push-chain.js +171 -153
- package/src/lib/push-client/push-client.js +223 -237
- package/src/lib/universal/account/account.js +349 -365
- package/src/lib/universal/signer/signer.js +306 -294
- package/src/lib/universal-tx-detector/auto-detect.js +55 -60
- package/src/lib/universal-tx-detector/cascade.js +160 -172
- package/src/lib/universal-tx-detector/child-inbounds.js +67 -75
- package/src/lib/universal-tx-detector/classify.js +2 -3
- package/src/lib/universal-tx-detector/detector-svm.js +139 -147
- package/src/lib/universal-tx-detector/detector.js +145 -157
- package/src/lib/universal-tx-detector/normalize.js +7 -7
- package/src/lib/utils/external-tx-hash.js +1 -2
- package/src/lib/utils.d.ts +44 -0
- package/src/lib/utils.js +762 -683
- package/src/lib/vm-client/evm-client.js +186 -202
- package/src/lib/vm-client/svm-client.d.ts +1 -1
- package/src/lib/vm-client/svm-client.js +220 -224
- 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
|
-
*
|
|
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
|
-
*
|
|
55
|
-
*
|
|
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
|
-
*
|
|
67
|
-
*
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
enums_1.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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 (!
|
|
430
|
-
throw new Error('
|
|
232
|
+
if (!Array.isArray(args)) {
|
|
233
|
+
throw new Error('Arguments must be an array');
|
|
431
234
|
}
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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:
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
-
|
|
653
|
-
|
|
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
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
decimals: tokenDecimals,
|
|
729
|
-
network,
|
|
730
|
-
};
|
|
731
|
-
},
|
|
732
|
-
};
|
|
808
|
+
return undefined;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
exports.Utils = Utils;
|
|
733
812
|
//# sourceMappingURL=utils.js.map
|