@pushchain/core 6.0.19 → 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 +54 -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 +4 -1
- 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 -331
- 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.d.ts +2 -1
- package/src/lib/universal/signer/signer.js +315 -299
- 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
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PC20 registry resolver — the one module that reads PC20 state.
|
|
4
|
+
*
|
|
5
|
+
* Used by `sendTransaction`, `prepareTransaction`, cascade construction,
|
|
6
|
+
* `getPC20Address`, and the outbound tracking fallback.
|
|
7
|
+
* Nothing else may read the registry directly; a second reader is how the
|
|
8
|
+
* validation tiers and the cache policy drift apart.
|
|
9
|
+
*
|
|
10
|
+
* ## Read budget
|
|
11
|
+
*
|
|
12
|
+
* Validation is tiered so a send does not pay for the full identity sweep:
|
|
13
|
+
*
|
|
14
|
+
* Tier A (every send) — `getPC20Source` then `getPC20Wrapper`. Forward and
|
|
15
|
+
* reverse agreement proves the address is registered
|
|
16
|
+
* for that exact namespace, which is what defeats a
|
|
17
|
+
* wrapper address copied onto the wrong chain.
|
|
18
|
+
* Tier B (prepare-time, first use per session, or strict mode) — live gateway
|
|
19
|
+
* factory identity, `isPC20Wrapper`, `wrapperToSource`,
|
|
20
|
+
* `SOURCE_ASSET`, deployed bytecode. Largely redundant
|
|
21
|
+
* once Tier A holds, but it is what catches a
|
|
22
|
+
* misconfigured deployment where the gateway would not
|
|
23
|
+
* take the PC20 burn path at all.
|
|
24
|
+
*
|
|
25
|
+
* Each tier is one multicall against one chain. Budget: at most 2 sequential
|
|
26
|
+
* round trips warm, 4 cold. `__readCount` exists so tests can assert that.
|
|
27
|
+
*
|
|
28
|
+
* ## Cache policy
|
|
29
|
+
*
|
|
30
|
+
* Positive results are cached for the client's lifetime, keyed by
|
|
31
|
+
* `(pushNetwork, chain, address)`. Registered mappings are immutable in
|
|
32
|
+
* practice — a factory reconfiguration is a redeploy-level event.
|
|
33
|
+
*
|
|
34
|
+
* Negative results are NEVER cached. The first successful export creates a
|
|
35
|
+
* mapping that did not previously exist, and a cached "not registered" would
|
|
36
|
+
* make the SDK permanently wrong about a token that now works.
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.__readCount = void 0;
|
|
40
|
+
exports.getUniversalCoreAddress = getUniversalCoreAddress;
|
|
41
|
+
exports.__clearPC20Caches = __clearPC20Caches;
|
|
42
|
+
exports.resolveWrapperToSource = resolveWrapperToSource;
|
|
43
|
+
exports.verifyEvmWrapperIdentity = verifyEvmWrapperIdentity;
|
|
44
|
+
exports.readPushPC20Metadata = readPushPC20Metadata;
|
|
45
|
+
exports.listPC20Deployments = listPC20Deployments;
|
|
46
|
+
exports.getRegisteredWrapper = getRegisteredWrapper;
|
|
47
|
+
exports.discoverPC20Chain = discoverPC20Chain;
|
|
48
|
+
exports.resolvePC20Token = resolvePC20Token;
|
|
49
|
+
const viem_1 = require("viem");
|
|
50
|
+
const enums_1 = require("../../../constants/enums");
|
|
51
|
+
const chain_1 = require("../../../constants/chain");
|
|
52
|
+
const abi_1 = require("../../../constants/abi");
|
|
53
|
+
const helpers_1 = require("../helpers");
|
|
54
|
+
const chain_namespace_1 = require("./chain-namespace");
|
|
55
|
+
const address_codec_1 = require("./address-codec");
|
|
56
|
+
const errors_1 = require("./errors");
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Client plumbing
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/**
|
|
61
|
+
* Instrumentation for the round-trip budget assertion. Incremented once per
|
|
62
|
+
* network round trip (a batch counts as one, which is the whole point).
|
|
63
|
+
*/
|
|
64
|
+
exports.__readCount = { n: 0 };
|
|
65
|
+
const clientCache = new Map();
|
|
66
|
+
/**
|
|
67
|
+
* Why JSON-RPC batching rather than Multicall3.
|
|
68
|
+
*
|
|
69
|
+
* Every PC20 registry read targets UniversalCore on Push Chain, so batching is
|
|
70
|
+
* what keeps `getPC20Address` at one round trip instead of one per chain. The
|
|
71
|
+
* obvious tool is `client.multicall()` — but **Multicall3 is not deployed on
|
|
72
|
+
* Push Chain**, so that path fails outright with "multicallAddress is
|
|
73
|
+
* required". Verified against Donut: no code at
|
|
74
|
+
* `0xcA11bde05977b3631167028862bE2a173976CA11`.
|
|
75
|
+
*
|
|
76
|
+
* viem's transport-level `batch` option achieves the same thing a layer lower:
|
|
77
|
+
* concurrent `eth_call`s issued in the same tick are coalesced into a single
|
|
78
|
+
* JSON-RPC batch request over one HTTP round trip. No contract required, and it
|
|
79
|
+
* works identically on chains that do have Multicall3.
|
|
80
|
+
*/
|
|
81
|
+
const BATCH_TRANSPORT_OPTS = { batch: { wait: 0 } };
|
|
82
|
+
function rpcFor(chain, opts) {
|
|
83
|
+
const override = opts.rpcUrls?.[chain]?.[0];
|
|
84
|
+
const fallback = chain_1.CHAIN_INFO[chain]?.defaultRPC?.[0];
|
|
85
|
+
const url = override ?? fallback;
|
|
86
|
+
if (!url) {
|
|
87
|
+
throw new errors_1.UnsupportedPC20DestinationError({
|
|
88
|
+
chain: String(chain),
|
|
89
|
+
hint: 'No RPC URL configured for this chain. Pass one via `rpcUrls`.',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return url;
|
|
93
|
+
}
|
|
94
|
+
function clientFor(chain, opts) {
|
|
95
|
+
const url = rpcFor(chain, opts);
|
|
96
|
+
const key = `${chain}:${url}`;
|
|
97
|
+
let client = clientCache.get(key);
|
|
98
|
+
if (!client) {
|
|
99
|
+
client = (0, viem_1.createPublicClient)({
|
|
100
|
+
transport: (0, viem_1.http)(url, BATCH_TRANSPORT_OPTS),
|
|
101
|
+
});
|
|
102
|
+
clientCache.set(key, client);
|
|
103
|
+
}
|
|
104
|
+
return client;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Read several contract calls in one network round trip.
|
|
108
|
+
*
|
|
109
|
+
* Individual `readContract` calls issued together in the same tick are coalesced
|
|
110
|
+
* by the batching transport (see {@link BATCH_TRANSPORT_OPTS}). `Promise.all`
|
|
111
|
+
* dispatches them synchronously, which is what puts them in the same batch
|
|
112
|
+
* window — awaiting them one at a time would silently become N round trips.
|
|
113
|
+
*
|
|
114
|
+
* Failures are captured per call rather than thrown, because several PC20 probes
|
|
115
|
+
* are *expected* to revert: `pc20Metadata()` reverting on a plain ERC20 is the
|
|
116
|
+
* primary discriminator, not an error condition. Callers interpret each result
|
|
117
|
+
* individually via {@link unwrap}.
|
|
118
|
+
*/
|
|
119
|
+
async function batchRead(client, contracts) {
|
|
120
|
+
exports.__readCount.n += 1;
|
|
121
|
+
return Promise.all(contracts.map((c) => client
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
|
+
.readContract(c)
|
|
124
|
+
.then((result) => ({ status: 'success', result }))
|
|
125
|
+
.catch((error) => ({ status: 'failure', error }))));
|
|
126
|
+
}
|
|
127
|
+
function unwrap(entry) {
|
|
128
|
+
return entry?.status === 'success' ? entry.result : undefined;
|
|
129
|
+
}
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// UniversalCore address
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
const universalCoreCache = new Map();
|
|
134
|
+
/**
|
|
135
|
+
* Resolve UniversalCore through the UGPC precompile, matching how
|
|
136
|
+
* `gas-calculator.queryOutboundGasFee` does it. Cached per network — the
|
|
137
|
+
* precompile's `universalCore` pointer does not move within a session.
|
|
138
|
+
*/
|
|
139
|
+
async function getUniversalCoreAddress(opts) {
|
|
140
|
+
const cached = universalCoreCache.get(opts.network);
|
|
141
|
+
if (cached)
|
|
142
|
+
return cached;
|
|
143
|
+
const pushChain = (0, helpers_1.getPushChainForNetwork)(opts.network);
|
|
144
|
+
const client = clientFor(pushChain, opts);
|
|
145
|
+
exports.__readCount.n += 1;
|
|
146
|
+
const address = (await client.readContract({
|
|
147
|
+
address: (0, helpers_1.getUniversalGatewayPCAddress)(),
|
|
148
|
+
abi: abi_1.UNIVERSAL_GATEWAY_PC,
|
|
149
|
+
functionName: 'universalCore',
|
|
150
|
+
args: [],
|
|
151
|
+
}));
|
|
152
|
+
universalCoreCache.set(opts.network, address);
|
|
153
|
+
return address;
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Positive-only caches
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
/** wrapper → Push source. Positive entries only; see the cache policy above. */
|
|
159
|
+
const wrapperToSourceCache = new Map();
|
|
160
|
+
/** Push token → validated metadata. Positive entries only. */
|
|
161
|
+
const metadataCache = new Map();
|
|
162
|
+
/** `(network, chain, address)` pairs that have passed Tier B this session. */
|
|
163
|
+
const tierBVerified = new Set();
|
|
164
|
+
const cacheKey = (network, chain, address) => `${network}:${chain}:${address.toLowerCase()}`;
|
|
165
|
+
/** Test seam. Never call from product code — it would drop verified Tier B state. */
|
|
166
|
+
function __clearPC20Caches() {
|
|
167
|
+
wrapperToSourceCache.clear();
|
|
168
|
+
metadataCache.clear();
|
|
169
|
+
tierBVerified.clear();
|
|
170
|
+
universalCoreCache.clear();
|
|
171
|
+
clientCache.clear();
|
|
172
|
+
exports.__readCount.n = 0;
|
|
173
|
+
}
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Tier A — registry forward/reverse agreement
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
/**
|
|
178
|
+
* Resolve an external wrapper to its canonical Push-native source.
|
|
179
|
+
*
|
|
180
|
+
* One multicall: `getPC20Source(wrapper, ns)` and, in the same batch, the
|
|
181
|
+
* reverse `getPC20Wrapper` cannot be issued because it depends on the source.
|
|
182
|
+
* The reverse read is therefore a second round trip — this is the "2 sequential
|
|
183
|
+
* round trips" in the budget, and it is not collapsible: the registry has no
|
|
184
|
+
* combined accessor.
|
|
185
|
+
*
|
|
186
|
+
* @throws {PC20WrapperNotRegisteredError} when the wrapper is unknown.
|
|
187
|
+
* @throws {PC20RegistryMismatchError} when forward and reverse disagree.
|
|
188
|
+
*/
|
|
189
|
+
async function resolveWrapperToSource(chain, address, opts) {
|
|
190
|
+
const namespace = (0, chain_namespace_1.chainToNamespace)(chain);
|
|
191
|
+
const rawWrapper = (0, address_codec_1.pc20AddressToBytes32)(chain, address);
|
|
192
|
+
const key = cacheKey(opts.network, chain, address);
|
|
193
|
+
const cached = wrapperToSourceCache.get(key);
|
|
194
|
+
if (cached && !opts.bypassCache) {
|
|
195
|
+
return { pushAddress: cached, chainNamespace: namespace, rawWrapper };
|
|
196
|
+
}
|
|
197
|
+
const core = await getUniversalCoreAddress(opts);
|
|
198
|
+
const pushClient = clientFor((0, helpers_1.getPushChainForNetwork)(opts.network), opts);
|
|
199
|
+
const [sourceEntry] = await batchRead(pushClient, [
|
|
200
|
+
{
|
|
201
|
+
address: core,
|
|
202
|
+
abi: abi_1.UNIVERSAL_CORE_EVM,
|
|
203
|
+
functionName: 'getPC20Source',
|
|
204
|
+
args: [rawWrapper, namespace],
|
|
205
|
+
},
|
|
206
|
+
]);
|
|
207
|
+
const source = unwrap(sourceEntry);
|
|
208
|
+
if (!source || !source[1] || source[0] === '0x0000000000000000000000000000000000000000') {
|
|
209
|
+
throw new errors_1.PC20WrapperNotRegisteredError({
|
|
210
|
+
chain: String(chain),
|
|
211
|
+
address,
|
|
212
|
+
chainNamespace: namespace,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const pushAddress = (0, viem_1.getAddress)(source[0]);
|
|
216
|
+
// Reverse check. A stale or half-written entry would otherwise unlock the
|
|
217
|
+
// wrong Push token on burn.
|
|
218
|
+
const [wrapperEntry] = await batchRead(pushClient, [
|
|
219
|
+
{
|
|
220
|
+
address: core,
|
|
221
|
+
abi: abi_1.UNIVERSAL_CORE_EVM,
|
|
222
|
+
functionName: 'getPC20Wrapper',
|
|
223
|
+
args: [pushAddress, namespace],
|
|
224
|
+
},
|
|
225
|
+
]);
|
|
226
|
+
const reverse = unwrap(wrapperEntry);
|
|
227
|
+
if (!reverse || (0, address_codec_1.isZeroBytes32)(reverse[0]) || reverse[0].toLowerCase() !== rawWrapper.toLowerCase()) {
|
|
228
|
+
throw new errors_1.PC20RegistryMismatchError({
|
|
229
|
+
chain: String(chain),
|
|
230
|
+
address,
|
|
231
|
+
chainNamespace: namespace,
|
|
232
|
+
resolvedWrapper: reverse && !(0, address_codec_1.isZeroBytes32)(reverse[0])
|
|
233
|
+
? (0, address_codec_1.pc20Bytes32ToAddress)(chain, reverse[0])
|
|
234
|
+
: undefined,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
wrapperToSourceCache.set(key, pushAddress);
|
|
238
|
+
return { pushAddress, chainNamespace: namespace, rawWrapper };
|
|
239
|
+
}
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Tier B — live factory identity (EVM)
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
/**
|
|
244
|
+
* Verify the live external gateway will actually take the PC20 burn path for
|
|
245
|
+
* this wrapper, and that its factory is the one UniversalCore knows about.
|
|
246
|
+
*
|
|
247
|
+
* Two multicalls: the gateway's factory pointer must be read before the
|
|
248
|
+
* factory-scoped calls can be addressed.
|
|
249
|
+
*
|
|
250
|
+
* @throws {PC20FactoryMismatchError} on any identity disagreement.
|
|
251
|
+
*/
|
|
252
|
+
async function verifyEvmWrapperIdentity(chain, address, pushAddress, opts) {
|
|
253
|
+
const key = cacheKey(opts.network, chain, address);
|
|
254
|
+
if (!opts.strict && tierBVerified.has(key))
|
|
255
|
+
return;
|
|
256
|
+
const namespace = (0, chain_namespace_1.chainToNamespace)(chain);
|
|
257
|
+
const gateway = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[chain];
|
|
258
|
+
if (!gateway) {
|
|
259
|
+
throw new errors_1.UnsupportedPC20DestinationError({
|
|
260
|
+
chain: String(chain),
|
|
261
|
+
chainNamespace: namespace,
|
|
262
|
+
hint: 'No universal gateway address is configured for this chain.',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const core = await getUniversalCoreAddress(opts);
|
|
266
|
+
const pushClient = clientFor((0, helpers_1.getPushChainForNetwork)(opts.network), opts);
|
|
267
|
+
const extClient = clientFor(chain, opts);
|
|
268
|
+
// Reference factory to compare the gateway against. The deployed
|
|
269
|
+
// UniversalCore does not expose `pc20FactoryByChain` (chain-team decision:
|
|
270
|
+
// the token mappings are the supported registry surface), so the standing
|
|
271
|
+
// reference is the destination Vault's own pointer — the Vault is what
|
|
272
|
+
// deploys and mints wrappers (`Vault.sol:327`), so gateway-vs-Vault
|
|
273
|
+
// agreement is the identity that actually matters at settlement. The
|
|
274
|
+
// registry read stays first, failure-tolerant, for forward compatibility
|
|
275
|
+
// should a future upgrade add the accessor.
|
|
276
|
+
const [registryFactoryEntry] = await batchRead(pushClient, [
|
|
277
|
+
{
|
|
278
|
+
address: core,
|
|
279
|
+
abi: abi_1.UNIVERSAL_CORE_EVM,
|
|
280
|
+
functionName: 'pc20FactoryByChain',
|
|
281
|
+
args: [namespace],
|
|
282
|
+
},
|
|
283
|
+
]);
|
|
284
|
+
const registryFactoryRaw = unwrap(registryFactoryEntry);
|
|
285
|
+
let referenceFactory = registryFactoryRaw && !(0, address_codec_1.isZeroBytes32)(registryFactoryRaw)
|
|
286
|
+
? (0, address_codec_1.pc20Bytes32ToAddress)(chain, registryFactoryRaw)
|
|
287
|
+
: undefined;
|
|
288
|
+
if (!referenceFactory) {
|
|
289
|
+
const vault = chain_1.VAULT_ADDRESSES[chain];
|
|
290
|
+
if (vault) {
|
|
291
|
+
const [vaultFactoryEntry] = await batchRead(extClient, [
|
|
292
|
+
{
|
|
293
|
+
address: (0, viem_1.getAddress)(vault),
|
|
294
|
+
abi: abi_1.VAULT_PC20_FACTORY_EVM,
|
|
295
|
+
functionName: 'pc20Factory',
|
|
296
|
+
args: [],
|
|
297
|
+
},
|
|
298
|
+
]);
|
|
299
|
+
const vaultFactory = unwrap(vaultFactoryEntry);
|
|
300
|
+
if (vaultFactory && vaultFactory !== '0x0000000000000000000000000000000000000000') {
|
|
301
|
+
referenceFactory = (0, viem_1.getAddress)(vaultFactory);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (!referenceFactory) {
|
|
306
|
+
throw new errors_1.UnsupportedPC20DestinationError({
|
|
307
|
+
chain: String(chain),
|
|
308
|
+
chainNamespace: namespace,
|
|
309
|
+
hint: 'Neither UniversalCore nor the destination Vault has a PC20 factory ' +
|
|
310
|
+
'registered for this chain.',
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
const [gatewayFactoryEntry, codeEntry] = await Promise.all([
|
|
314
|
+
batchRead(extClient, [
|
|
315
|
+
{ address: gateway, abi: abi_1.EXTERNAL_GATEWAY_PC20_EVM, functionName: 'pc20Factory', args: [] },
|
|
316
|
+
]).then((r) => r[0]),
|
|
317
|
+
(async () => {
|
|
318
|
+
exports.__readCount.n += 1;
|
|
319
|
+
const code = await extClient.getCode({ address: (0, viem_1.getAddress)(address) });
|
|
320
|
+
return { status: 'success', result: code };
|
|
321
|
+
})(),
|
|
322
|
+
]);
|
|
323
|
+
const gatewayFactory = unwrap(gatewayFactoryEntry);
|
|
324
|
+
if (!gatewayFactory || (0, viem_1.getAddress)(gatewayFactory) !== (0, viem_1.getAddress)(referenceFactory)) {
|
|
325
|
+
throw new errors_1.PC20FactoryMismatchError({
|
|
326
|
+
chain: String(chain),
|
|
327
|
+
address,
|
|
328
|
+
chainNamespace: namespace,
|
|
329
|
+
gatewayFactory,
|
|
330
|
+
registryFactory: referenceFactory,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
const code = unwrap(codeEntry);
|
|
334
|
+
if (!code || code === '0x') {
|
|
335
|
+
throw new errors_1.PC20FactoryMismatchError({
|
|
336
|
+
chain: String(chain),
|
|
337
|
+
address,
|
|
338
|
+
chainNamespace: namespace,
|
|
339
|
+
hint: 'No contract is deployed at this wrapper address.',
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
// Factory-scoped identity, batched into one round trip.
|
|
343
|
+
const factory = (0, viem_1.getAddress)(gatewayFactory);
|
|
344
|
+
const wrapper = (0, viem_1.getAddress)(address);
|
|
345
|
+
const [isWrapperEntry, wrapperSourceEntry, sourceAssetEntry] = await batchRead(extClient, [
|
|
346
|
+
{ address: factory, abi: abi_1.PC20_FACTORY_EVM, functionName: 'isPC20Wrapper', args: [wrapper] },
|
|
347
|
+
{ address: factory, abi: abi_1.PC20_FACTORY_EVM, functionName: 'wrapperToSource', args: [wrapper] },
|
|
348
|
+
{ address: wrapper, abi: abi_1.PC20_WRAPPER_EVM, functionName: 'SOURCE_ASSET', args: [] },
|
|
349
|
+
]);
|
|
350
|
+
if (unwrap(isWrapperEntry) !== true) {
|
|
351
|
+
throw new errors_1.PC20FactoryMismatchError({
|
|
352
|
+
chain: String(chain),
|
|
353
|
+
address,
|
|
354
|
+
chainNamespace: namespace,
|
|
355
|
+
hint: 'The live factory does not recognize this address as a PC20 wrapper.',
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
const factorySource = unwrap(wrapperSourceEntry);
|
|
359
|
+
const immutableSource = unwrap(sourceAssetEntry);
|
|
360
|
+
for (const [label, value] of [
|
|
361
|
+
['factory.wrapperToSource', factorySource],
|
|
362
|
+
['wrapper.SOURCE_ASSET', immutableSource],
|
|
363
|
+
]) {
|
|
364
|
+
if (!value || (0, viem_1.getAddress)(value) !== (0, viem_1.getAddress)(pushAddress)) {
|
|
365
|
+
throw new errors_1.PC20FactoryMismatchError({
|
|
366
|
+
chain: String(chain),
|
|
367
|
+
address,
|
|
368
|
+
chainNamespace: namespace,
|
|
369
|
+
hint: `${label} does not match the Push source UniversalCore returned.`,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
tierBVerified.add(key);
|
|
374
|
+
}
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
// Push-native metadata
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
/**
|
|
379
|
+
* Read and validate `pc20Metadata()` on a Push-native token.
|
|
380
|
+
*
|
|
381
|
+
* The absence of `pc20Metadata` is the discriminator between a PC20 and any
|
|
382
|
+
* other ERC20 — never the symbol. A synthetic PRC20 is detected separately so
|
|
383
|
+
* it gets an error that points at the right API rather than reading as
|
|
384
|
+
* "your token is broken".
|
|
385
|
+
*/
|
|
386
|
+
async function readPushPC20Metadata(pushAddress, opts) {
|
|
387
|
+
const pushChain = (0, helpers_1.getPushChainForNetwork)(opts.network);
|
|
388
|
+
const key = cacheKey(opts.network, pushChain, pushAddress);
|
|
389
|
+
const cached = metadataCache.get(key);
|
|
390
|
+
if (cached)
|
|
391
|
+
return cached;
|
|
392
|
+
const client = clientFor(pushChain, opts);
|
|
393
|
+
const [metaEntry, codeEntry] = await Promise.all([
|
|
394
|
+
batchRead(client, [
|
|
395
|
+
{ address: pushAddress, abi: abi_1.IPC20_EVM, functionName: 'pc20Metadata', args: [] },
|
|
396
|
+
]).then((r) => r[0]),
|
|
397
|
+
(async () => {
|
|
398
|
+
exports.__readCount.n += 1;
|
|
399
|
+
return { status: 'success', result: await client.getCode({ address: pushAddress }) };
|
|
400
|
+
})(),
|
|
401
|
+
]);
|
|
402
|
+
const code = unwrap(codeEntry);
|
|
403
|
+
if (!code || code === '0x') {
|
|
404
|
+
throw new errors_1.InvalidPC20MetadataError('No contract is deployed at this address.', {
|
|
405
|
+
chain: String(pushChain),
|
|
406
|
+
address: pushAddress,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
const meta = unwrap(metaEntry);
|
|
410
|
+
if (!meta) {
|
|
411
|
+
if (await isSyntheticPRC20(pushAddress, opts)) {
|
|
412
|
+
throw new errors_1.PC20ExpectedButPRC20Error({
|
|
413
|
+
chain: String(pushChain),
|
|
414
|
+
address: pushAddress,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
throw new errors_1.InvalidPC20MetadataError('Token does not implement pc20Metadata() and is not a Push-native PC20.', { chain: String(pushChain), address: pushAddress });
|
|
418
|
+
}
|
|
419
|
+
const [name, symbol, decimals, originAddress] = meta;
|
|
420
|
+
// The origin binding is what makes the metadata trustworthy: a contract can
|
|
421
|
+
// return any name it likes, but claiming to originate at a different address
|
|
422
|
+
// means it is not the canonical source for this token.
|
|
423
|
+
if ((0, viem_1.getAddress)(originAddress) !== (0, viem_1.getAddress)(pushAddress)) {
|
|
424
|
+
throw new errors_1.InvalidPC20MetadataError(`pc20Metadata().originAddress (${originAddress}) does not match the token address.`, { chain: String(pushChain), address: pushAddress });
|
|
425
|
+
}
|
|
426
|
+
if (!name || !symbol) {
|
|
427
|
+
throw new errors_1.InvalidPC20MetadataError('PC20 name and symbol must be non-empty.', {
|
|
428
|
+
chain: String(pushChain),
|
|
429
|
+
address: pushAddress,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
const validated = {
|
|
433
|
+
name,
|
|
434
|
+
symbol,
|
|
435
|
+
decimals: Number(decimals),
|
|
436
|
+
originAddress: (0, viem_1.getAddress)(originAddress),
|
|
437
|
+
};
|
|
438
|
+
metadataCache.set(key, validated);
|
|
439
|
+
return validated;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Distinguish a synthetic PRC20 from an arbitrary ERC20.
|
|
443
|
+
*
|
|
444
|
+
* PRC20s expose the origin chain they wrap; PC20s do not. Checked only after
|
|
445
|
+
* `pc20Metadata()` has already failed, so it costs nothing on the happy path.
|
|
446
|
+
*/
|
|
447
|
+
async function isSyntheticPRC20(address, opts) {
|
|
448
|
+
const client = clientFor((0, helpers_1.getPushChainForNetwork)(opts.network), opts);
|
|
449
|
+
const [entry] = await batchRead(client, [
|
|
450
|
+
{
|
|
451
|
+
address,
|
|
452
|
+
abi: [
|
|
453
|
+
{
|
|
454
|
+
type: 'function',
|
|
455
|
+
name: 'CHAIN_NAMESPACE',
|
|
456
|
+
inputs: [],
|
|
457
|
+
outputs: [{ name: '', type: 'string' }],
|
|
458
|
+
stateMutability: 'view',
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
functionName: 'CHAIN_NAMESPACE',
|
|
462
|
+
args: [],
|
|
463
|
+
},
|
|
464
|
+
]);
|
|
465
|
+
return typeof unwrap(entry) === 'string';
|
|
466
|
+
}
|
|
467
|
+
// ---------------------------------------------------------------------------
|
|
468
|
+
// Destination-side lookups
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
/**
|
|
471
|
+
* Confirmed wrapper deployments for a Push-native PC20.
|
|
472
|
+
*
|
|
473
|
+
* One multicall regardless of chain count — every `getPC20Wrapper` read targets
|
|
474
|
+
* UniversalCore on Push Chain, so fanning out per chain would be N round trips
|
|
475
|
+
* to the same endpoint for no reason.
|
|
476
|
+
*
|
|
477
|
+
* Returns confirmed deployments only. A predicted first-export address is the
|
|
478
|
+
* outbound builder's concern, not discovery's.
|
|
479
|
+
*/
|
|
480
|
+
async function listPC20Deployments(pushAddress, opts) {
|
|
481
|
+
const targets = (opts.chains ?? (0, chain_namespace_1.allExternalChains)()).filter((c) => !(0, chain_namespace_1.isPushChain)(c));
|
|
482
|
+
if (targets.length === 0)
|
|
483
|
+
return [];
|
|
484
|
+
const core = await getUniversalCoreAddress(opts);
|
|
485
|
+
const client = clientFor((0, helpers_1.getPushChainForNetwork)(opts.network), opts);
|
|
486
|
+
const namespaces = targets.map((chain) => (0, chain_namespace_1.chainToNamespace)(chain));
|
|
487
|
+
const results = await batchRead(client, targets.map((_, i) => ({
|
|
488
|
+
address: core,
|
|
489
|
+
abi: abi_1.UNIVERSAL_CORE_EVM,
|
|
490
|
+
functionName: 'getPC20Wrapper',
|
|
491
|
+
args: [pushAddress, namespaces[i]],
|
|
492
|
+
})));
|
|
493
|
+
const deployments = [];
|
|
494
|
+
targets.forEach((chain, i) => {
|
|
495
|
+
const value = unwrap(results[i]);
|
|
496
|
+
if (!value)
|
|
497
|
+
return;
|
|
498
|
+
const [rawAddress, deployed] = value;
|
|
499
|
+
if (!deployed || (0, address_codec_1.isZeroBytes32)(rawAddress))
|
|
500
|
+
return;
|
|
501
|
+
deployments.push({
|
|
502
|
+
chain,
|
|
503
|
+
chainNamespace: namespaces[i],
|
|
504
|
+
vm: (0, chain_namespace_1.vmForChain)(chain),
|
|
505
|
+
address: (0, address_codec_1.pc20Bytes32ToAddress)(chain, rawAddress),
|
|
506
|
+
rawAddress,
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
return deployments;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Registered wrapper for a Push PC20 on one destination chain.
|
|
513
|
+
*
|
|
514
|
+
* `undefined` means not yet deployed — a legitimate state before a first
|
|
515
|
+
* export, not an error. Callers that need an address for a first export must go
|
|
516
|
+
* through the prediction path, which verifies against the live factory.
|
|
517
|
+
*/
|
|
518
|
+
async function getRegisteredWrapper(pushAddress, destinationChain, opts) {
|
|
519
|
+
const [found] = await listPC20Deployments(pushAddress, {
|
|
520
|
+
...opts,
|
|
521
|
+
chains: [destinationChain],
|
|
522
|
+
});
|
|
523
|
+
return found;
|
|
524
|
+
}
|
|
525
|
+
// ---------------------------------------------------------------------------
|
|
526
|
+
// Chain discovery (lookup utility only)
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
/**
|
|
529
|
+
* Work out which chain an address belongs to, when the caller did not say.
|
|
530
|
+
*
|
|
531
|
+
* Used ONLY by the read-only `getPC20Address` utility. The transaction path
|
|
532
|
+
* never calls this — `funds.token` requires an explicit `chain`, and
|
|
533
|
+
* `gate.ts` rejects one that disagrees with where the funds actually are. A
|
|
534
|
+
* wrapper address copied from the wrong chain still fails on send; it merely
|
|
535
|
+
* resolves informationally on lookup.
|
|
536
|
+
*
|
|
537
|
+
* Order is cheapest-first:
|
|
538
|
+
* 1. narrow candidates by address format (local, free);
|
|
539
|
+
* 2. try Push-native, which short-circuits with no registry read at all;
|
|
540
|
+
* 3. probe every remaining namespace in one multicall.
|
|
541
|
+
*
|
|
542
|
+
* @throws {PC20AmbiguousAddressError} when several chains claim the address
|
|
543
|
+
* with different Push sources.
|
|
544
|
+
*/
|
|
545
|
+
async function discoverPC20Chain(address, opts) {
|
|
546
|
+
const pushChain = (0, helpers_1.getPushChainForNetwork)(opts.network);
|
|
547
|
+
const looksEvm = /^0x[0-9a-fA-F]{40}$/.test(address);
|
|
548
|
+
// 2. Push-native short-circuit. Only an EVM-shaped address can be one.
|
|
549
|
+
if (looksEvm) {
|
|
550
|
+
try {
|
|
551
|
+
const pushAddress = (0, viem_1.getAddress)(address);
|
|
552
|
+
await readPushPC20Metadata(pushAddress, opts);
|
|
553
|
+
return { chain: pushChain, pushAddress };
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
// Not a Push-native PC20 — fall through to the external probe.
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
// 1. Format narrowing. A hex address cannot be a Solana mint and vice versa,
|
|
560
|
+
// so probing the other VM's namespaces would be pure waste.
|
|
561
|
+
const candidates = (0, chain_namespace_1.allExternalChains)().filter((chain) => looksEvm ? (0, chain_namespace_1.vmForChain)(chain) === enums_1.VM.EVM : (0, chain_namespace_1.vmForChain)(chain) === enums_1.VM.SVM);
|
|
562
|
+
if (candidates.length === 0)
|
|
563
|
+
return null;
|
|
564
|
+
// 3. One multicall across every candidate namespace — all these reads hit
|
|
565
|
+
// UniversalCore on Push, so chain count costs nothing.
|
|
566
|
+
const core = await getUniversalCoreAddress(opts);
|
|
567
|
+
const client = clientFor(pushChain, opts);
|
|
568
|
+
const probes = candidates.flatMap((chain) => {
|
|
569
|
+
try {
|
|
570
|
+
return [
|
|
571
|
+
{
|
|
572
|
+
chain,
|
|
573
|
+
namespace: (0, chain_namespace_1.chainToNamespace)(chain),
|
|
574
|
+
raw: (0, address_codec_1.pc20AddressToBytes32)(chain, address),
|
|
575
|
+
},
|
|
576
|
+
];
|
|
577
|
+
}
|
|
578
|
+
catch {
|
|
579
|
+
// Address is malformed for this chain's VM — not a candidate.
|
|
580
|
+
return [];
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
if (probes.length === 0)
|
|
584
|
+
return null;
|
|
585
|
+
const results = await batchRead(client, probes.map((p) => ({
|
|
586
|
+
address: core,
|
|
587
|
+
abi: abi_1.UNIVERSAL_CORE_EVM,
|
|
588
|
+
functionName: 'getPC20Source',
|
|
589
|
+
args: [p.raw, p.namespace],
|
|
590
|
+
})));
|
|
591
|
+
const matches = [];
|
|
592
|
+
probes.forEach((p, i) => {
|
|
593
|
+
const value = unwrap(results[i]);
|
|
594
|
+
if (!value)
|
|
595
|
+
return;
|
|
596
|
+
const [source, known] = value;
|
|
597
|
+
if (!known || source === '0x0000000000000000000000000000000000000000')
|
|
598
|
+
return;
|
|
599
|
+
matches.push({ chain: p.chain, pushAddress: (0, viem_1.getAddress)(source) });
|
|
600
|
+
});
|
|
601
|
+
if (matches.length === 0)
|
|
602
|
+
return null;
|
|
603
|
+
// Several chains can legitimately hold the same wrapper address only if they
|
|
604
|
+
// resolve to the same token. Different sources means we cannot answer.
|
|
605
|
+
const distinct = new Set(matches.map((m) => m.pushAddress.toLowerCase()));
|
|
606
|
+
if (distinct.size > 1) {
|
|
607
|
+
throw new errors_1.PC20AmbiguousAddressError(address, matches.map((m) => ({ chain: String(m.chain), pushAddress: m.pushAddress })));
|
|
608
|
+
}
|
|
609
|
+
return matches[0];
|
|
610
|
+
}
|
|
611
|
+
// ---------------------------------------------------------------------------
|
|
612
|
+
// Top-level resolution
|
|
613
|
+
// ---------------------------------------------------------------------------
|
|
614
|
+
/**
|
|
615
|
+
* Resolve a `{ chain, address }` PC20 reference into everything the execution
|
|
616
|
+
* path needs, exactly once per transaction.
|
|
617
|
+
*
|
|
618
|
+
* `expectedChain` is where the funds actually are — the signer's chain for a
|
|
619
|
+
* direct external route, `params.from.chain` for a CEA-origin route, the Push
|
|
620
|
+
* chain for an export. Chain-ownership validation is the caller's job because
|
|
621
|
+
* only the caller knows which of those applies; this function assumes it has
|
|
622
|
+
* already passed.
|
|
623
|
+
*/
|
|
624
|
+
async function resolvePC20Token(chain, address, opts) {
|
|
625
|
+
const pushChain = (0, helpers_1.getPushChainForNetwork)(opts.network);
|
|
626
|
+
// Export: the caller named a Push-native token.
|
|
627
|
+
if ((0, chain_namespace_1.isPushChain)(chain)) {
|
|
628
|
+
const pushAddress = (0, viem_1.getAddress)(address);
|
|
629
|
+
const meta = await readPushPC20Metadata(pushAddress, opts);
|
|
630
|
+
return {
|
|
631
|
+
direction: 'export',
|
|
632
|
+
originChain: pushChain,
|
|
633
|
+
originAddress: pushAddress,
|
|
634
|
+
pushAddress,
|
|
635
|
+
name: meta.name,
|
|
636
|
+
symbol: meta.symbol,
|
|
637
|
+
decimals: meta.decimals,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
// Import: the caller named an external wrapper.
|
|
641
|
+
const { pushAddress, chainNamespace } = await resolveWrapperToSource(chain, address, opts);
|
|
642
|
+
if ((0, chain_namespace_1.vmForChain)(chain) === enums_1.VM.EVM && (opts.tierB || opts.strict)) {
|
|
643
|
+
await verifyEvmWrapperIdentity(chain, address, pushAddress, opts);
|
|
644
|
+
}
|
|
645
|
+
const meta = await readPushPC20Metadata(pushAddress, opts);
|
|
646
|
+
return {
|
|
647
|
+
direction: 'import',
|
|
648
|
+
originChain: chain,
|
|
649
|
+
originAddress: (0, chain_namespace_1.vmForChain)(chain) === enums_1.VM.EVM ? (0, viem_1.getAddress)(address) : address,
|
|
650
|
+
pushAddress,
|
|
651
|
+
name: meta.name,
|
|
652
|
+
symbol: meta.symbol,
|
|
653
|
+
decimals: meta.decimals,
|
|
654
|
+
wrapperAddress: (0, chain_namespace_1.vmForChain)(chain) === enums_1.VM.EVM ? (0, viem_1.getAddress)(address) : address,
|
|
655
|
+
chainNamespace,
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
//# sourceMappingURL=resolver.js.map
|