@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
|
@@ -13,6 +13,126 @@ export type ChainTarget = {
|
|
|
13
13
|
address: string;
|
|
14
14
|
chain: CHAIN;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* A dynamic PC20 token, identified by chain and address alone.
|
|
18
|
+
*
|
|
19
|
+
* PC20 mappings live in UniversalCore's on-chain registry, not in the SDK's
|
|
20
|
+
* static token tables, so there is nothing to look a symbol up in — identity is
|
|
21
|
+
* address-and-chain, always.
|
|
22
|
+
*
|
|
23
|
+
* `chain` is where the token *is*, never where it is going:
|
|
24
|
+
* - external wrapper → Push: the external chain the wrapper is deployed on
|
|
25
|
+
* - Push → external: the Push chain the canonical token lives on
|
|
26
|
+
* `to.chain` is the destination and is a separate field.
|
|
27
|
+
*
|
|
28
|
+
* The canonical form is `{ chain, address }` — nothing else. Discrimination
|
|
29
|
+
* from `MoveableToken` relies on what a PC20 reference does NOT have:
|
|
30
|
+
* `MoveableToken` requires `symbol` and `mechanism`, so their absence is the
|
|
31
|
+
* discriminator (see {@link isPC20Reference}, the single predicate). This
|
|
32
|
+
* stays sound even if `MoveableToken` grows optional fields, because the
|
|
33
|
+
* check keys on its required ones.
|
|
34
|
+
*
|
|
35
|
+
* Do NOT add `symbol` to a PC20 reference — a present `symbol` makes the
|
|
36
|
+
* object a `MoveableToken` and routes it down the static-table path (the
|
|
37
|
+
* route validator names this mistake explicitly when it happens).
|
|
38
|
+
*/
|
|
39
|
+
export type PC20TokenReference = {
|
|
40
|
+
chain: CHAIN;
|
|
41
|
+
/** Chain-native: checksummed hex on EVM, base58 on Solana. */
|
|
42
|
+
address: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Token accepted by `funds.token`.
|
|
46
|
+
*
|
|
47
|
+
* `MoveableToken` continues through the PRC20/native path unchanged;
|
|
48
|
+
* `PC20TokenReference` routes through the registry resolver.
|
|
49
|
+
*/
|
|
50
|
+
export type FundsToken = import('../constants').MoveableToken | PC20TokenReference;
|
|
51
|
+
/**
|
|
52
|
+
* The single runtime discriminator for {@link FundsToken}.
|
|
53
|
+
*
|
|
54
|
+
* Every module must use this rather than re-deriving the check by inspecting
|
|
55
|
+
* fields — a second copy is how the two branches drift apart.
|
|
56
|
+
*/
|
|
57
|
+
export declare function isPC20Reference(token: FundsToken | undefined): token is PC20TokenReference;
|
|
58
|
+
/**
|
|
59
|
+
* `ExecuteParams` after the PC20 gate has run.
|
|
60
|
+
*
|
|
61
|
+
* The public `funds.token` accepts either a `MoveableToken` or a
|
|
62
|
+
* `PC20TokenReference`. The legacy PRC20/native execution path only ever
|
|
63
|
+
* handles the former, so it types against this narrowed shape and a single
|
|
64
|
+
* gate (`resolveFundsToken` in the orchestrator) decides which branch a
|
|
65
|
+
* transaction takes.
|
|
66
|
+
*
|
|
67
|
+
* Narrowing here rather than at each of the ~24 `execute.funds!.token.symbol`
|
|
68
|
+
* call sites keeps the existing path provably untouched: if a PC20 reference
|
|
69
|
+
* ever reached it, that would be a type error, not a runtime `undefined`.
|
|
70
|
+
*
|
|
71
|
+
* Phase 2 adds the PC20 branch alongside this; it does not widen this type.
|
|
72
|
+
*/
|
|
73
|
+
export type LegacyExecuteParams = Omit<ExecuteParams, 'funds'> & {
|
|
74
|
+
funds?: {
|
|
75
|
+
amount: bigint;
|
|
76
|
+
token?: InternalFundsToken;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Set by the PC20 gate when `funds.token` was a `PC20TokenReference`.
|
|
80
|
+
* Authoritative for every PC20 behavioral decision — the `funds.token` view
|
|
81
|
+
* above only carries the display/encoding fields the shared path reads.
|
|
82
|
+
*/
|
|
83
|
+
_pc20?: import('./internals/pc20/resolver').ResolvedPC20;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Internal view of a funds token inside the execution path.
|
|
87
|
+
*
|
|
88
|
+
* A PC20 gets a `'pc20-burn'` mechanism rather than being flattened into a fake
|
|
89
|
+
* `MoveableToken`. The distinction matters at three points, and a fake would be
|
|
90
|
+
* wrong at all of them:
|
|
91
|
+
* - `'approve'` would make the SDK approve a wrapper that the gateway's
|
|
92
|
+
* factory burns directly via `burnFrom` — an approval that is never used.
|
|
93
|
+
* - `'native'` would attach the bridge amount as msg.value.
|
|
94
|
+
* - the Push-side transfer must target the resolved PC20, not
|
|
95
|
+
* `getPRC20Address`.
|
|
96
|
+
*/
|
|
97
|
+
export type InternalFundsToken = import('../constants').MoveableToken | {
|
|
98
|
+
symbol: string;
|
|
99
|
+
decimals: number;
|
|
100
|
+
address: string;
|
|
101
|
+
mechanism: 'pc20-burn';
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* One known address of a PC20 token — an external wrapper deployment, or the
|
|
105
|
+
* canonical Push-native token itself.
|
|
106
|
+
*
|
|
107
|
+
* Shape matches `getMoveableTokens`' entries (`chain` + `chainName`) so both
|
|
108
|
+
* token APIs read the same way.
|
|
109
|
+
*/
|
|
110
|
+
export type PC20RegistryEntry = {
|
|
111
|
+
/** Chain-native: checksummed hex on EVM, base58 on Solana. */
|
|
112
|
+
address: string;
|
|
113
|
+
/** CAIP-2 chain id, e.g. `'eip155:11155111'`. */
|
|
114
|
+
chain: CHAIN;
|
|
115
|
+
/** Human-readable chain key, e.g. `'ETHEREUM_SEPOLIA'`. */
|
|
116
|
+
chainName: string;
|
|
117
|
+
};
|
|
118
|
+
/** Result of `PushChain.utils.tokens.getPC20Address`. */
|
|
119
|
+
export type PC20AddressResult = {
|
|
120
|
+
/** Canonical Push-native PC20 address. */
|
|
121
|
+
address: `0x${string}`;
|
|
122
|
+
name: string;
|
|
123
|
+
symbol: string;
|
|
124
|
+
decimals: number;
|
|
125
|
+
network: import('../constants/enums').PUSH_NETWORK;
|
|
126
|
+
/**
|
|
127
|
+
* Every confirmed address for this token: external wrapper deployments in
|
|
128
|
+
* SDK chain order, then the Push-native entry last.
|
|
129
|
+
*
|
|
130
|
+
* Confirmed only. A predicted first-export wrapper address never appears
|
|
131
|
+
* here — that stays internal to the outbound builder, because publishing an
|
|
132
|
+
* address that does not exist yet invites callers to send to it.
|
|
133
|
+
*/
|
|
134
|
+
registry: PC20RegistryEntry[];
|
|
135
|
+
};
|
|
16
136
|
/**
|
|
17
137
|
* Source chain for CEA-originated transactions (Routes 3, 4)
|
|
18
138
|
* Specifies which CEA domain submits the transaction
|
|
@@ -126,7 +246,7 @@ export type ExecuteParams = {
|
|
|
126
246
|
*/
|
|
127
247
|
funds?: {
|
|
128
248
|
amount: bigint;
|
|
129
|
-
token?:
|
|
249
|
+
token?: FundsToken;
|
|
130
250
|
};
|
|
131
251
|
/**
|
|
132
252
|
* Internal: Pre-fetched UEA status to avoid redundant RPC calls.
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPC20Reference = isPC20Reference;
|
|
3
4
|
exports.parseUEAVersion = parseUEAVersion;
|
|
5
|
+
/**
|
|
6
|
+
* The single runtime discriminator for {@link FundsToken}.
|
|
7
|
+
*
|
|
8
|
+
* Every module must use this rather than re-deriving the check by inspecting
|
|
9
|
+
* fields — a second copy is how the two branches drift apart.
|
|
10
|
+
*/
|
|
11
|
+
function isPC20Reference(token) {
|
|
12
|
+
if (!token)
|
|
13
|
+
return false;
|
|
14
|
+
const candidate = token;
|
|
15
|
+
// chain + address, and none of MoveableToken's required fields. Keying on
|
|
16
|
+
// the ABSENCE of required MoveableToken fields keeps this sound even if
|
|
17
|
+
// MoveableToken grows optional fields later.
|
|
18
|
+
return (typeof candidate.chain === 'string' &&
|
|
19
|
+
typeof candidate.address === 'string' &&
|
|
20
|
+
candidate.symbol === undefined &&
|
|
21
|
+
candidate.mechanism === undefined);
|
|
22
|
+
}
|
|
4
23
|
// ============================================================================
|
|
5
24
|
// UEA Version Utilities
|
|
6
25
|
// ============================================================================
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CHAIN } from '../constants/enums';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LegacyExecuteParams, MultiCall, UniversalOutboundTxRequest, SvmExecutePayloadFields } from './orchestrator.types';
|
|
3
3
|
export declare function buildExecuteMulticall({ execute, ueaAddress, logger, allowSelfValueCall, }: {
|
|
4
|
-
execute:
|
|
4
|
+
execute: LegacyExecuteParams;
|
|
5
5
|
ueaAddress: `0x${string}`;
|
|
6
6
|
logger?: (msg: string) => void;
|
|
7
7
|
allowSelfValueCall?: boolean;
|
|
@@ -30,23 +30,22 @@ const data_utils_1 = require("./data-utils");
|
|
|
30
30
|
const tx_1 = require("../generated/v1/tx");
|
|
31
31
|
const signing_1 = require("./internals/signing");
|
|
32
32
|
function buildExecuteMulticall({ execute, ueaAddress, logger, allowSelfValueCall = false, }) {
|
|
33
|
-
|
|
34
|
-
const log = (msg) => logger === null || logger === void 0 ? void 0 : logger(msg);
|
|
33
|
+
const log = (msg) => logger?.(msg);
|
|
35
34
|
const hasData = (0, data_utils_1.hasExecutablePayloadData)(execute.data);
|
|
36
35
|
log('buildExecuteMulticall — input: ' + JSON.stringify({
|
|
37
36
|
to: execute.to,
|
|
38
|
-
value:
|
|
37
|
+
value: execute.value?.toString() ?? 'undefined',
|
|
39
38
|
data: execute.data ? (Array.isArray(execute.data) ? `Array(${execute.data.length})` : execute.data.slice(0, 20) + '...') : 'undefined',
|
|
40
39
|
hasData,
|
|
41
|
-
fundsAmount:
|
|
42
|
-
fundsTokenSymbol:
|
|
43
|
-
fundsTokenMechanism:
|
|
40
|
+
fundsAmount: execute.funds?.amount?.toString() ?? 'undefined',
|
|
41
|
+
fundsTokenSymbol: execute.funds?.token?.symbol ?? 'undefined',
|
|
42
|
+
fundsTokenMechanism: execute.funds?.token?.mechanism ?? 'undefined',
|
|
44
43
|
ueaAddress,
|
|
45
44
|
}, null, 2));
|
|
46
45
|
const multicallData = [];
|
|
47
46
|
// *** We will pass the value alongside with the data in a single message now ***
|
|
48
47
|
const branch1 = !hasData && execute.value;
|
|
49
|
-
log(`buildExecuteMulticall — Branch 1 (!data && value): ${branch1} | !hasData: ${!hasData} | execute.value: ${
|
|
48
|
+
log(`buildExecuteMulticall — Branch 1 (!data && value): ${branch1} | !hasData: ${!hasData} | execute.value: ${execute.value?.toString() ?? 'undefined'}`);
|
|
50
49
|
if (!hasData && execute.value) {
|
|
51
50
|
const isValueToSelf = execute.to.toLowerCase() === ueaAddress.toLowerCase();
|
|
52
51
|
if (isValueToSelf && !allowSelfValueCall) {
|
|
@@ -61,7 +60,7 @@ function buildExecuteMulticall({ execute, ueaAddress, logger, allowSelfValueCall
|
|
|
61
60
|
log(`buildExecuteMulticall — Branch 1 ENTERED: pushed native value transfer to ${execute.to}`);
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
|
-
if (
|
|
63
|
+
if (execute.funds?.amount) {
|
|
65
64
|
const token = execute.funds.token;
|
|
66
65
|
const isArrayMulticall = Array.isArray(execute.data) && execute.data.length > 0;
|
|
67
66
|
const isNative = token.mechanism === 'native';
|
|
@@ -80,9 +79,16 @@ function buildExecuteMulticall({ execute, ueaAddress, logger, allowSelfValueCall
|
|
|
80
79
|
const erc20Transfer = (0, viem_1.encodeFunctionData)({
|
|
81
80
|
abi: erc20_evm_1.ERC20_EVM,
|
|
82
81
|
functionName: 'transfer',
|
|
83
|
-
args: [execute.to,
|
|
82
|
+
args: [execute.to, execute.funds?.amount],
|
|
84
83
|
});
|
|
85
|
-
|
|
84
|
+
// A PC20 unlocks as its canonical Push-native token, which the resolver
|
|
85
|
+
// already established. `getPRC20Address` must not run for it — there is
|
|
86
|
+
// no static synthetic mapping to look up, and the symbol carries no
|
|
87
|
+
// authority.
|
|
88
|
+
const pc20 = execute._pc20;
|
|
89
|
+
const pushChainTo = pc20
|
|
90
|
+
? pc20.pushAddress
|
|
91
|
+
: push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
|
|
86
92
|
multicallData.push({
|
|
87
93
|
to: pushChainTo,
|
|
88
94
|
value: BigInt(0),
|
|
@@ -181,7 +187,6 @@ function buildCeaMulticallPayload(multicalls) {
|
|
|
181
187
|
* @returns ABI-encoded UniversalPayload struct
|
|
182
188
|
*/
|
|
183
189
|
function buildInboundUniversalPayload(multicallData, opts) {
|
|
184
|
-
var _a, _b, _c;
|
|
185
190
|
return (0, viem_1.encodeAbiParameters)([
|
|
186
191
|
{
|
|
187
192
|
type: 'tuple',
|
|
@@ -202,11 +207,11 @@ function buildInboundUniversalPayload(multicallData, opts) {
|
|
|
202
207
|
to: selectors_1.ZERO_ADDRESS,
|
|
203
208
|
value: BigInt(0),
|
|
204
209
|
data: multicallData,
|
|
205
|
-
gasLimit:
|
|
210
|
+
gasLimit: opts?.gasLimit ?? BigInt(10e7),
|
|
206
211
|
maxFeePerGas: BigInt(1e10),
|
|
207
212
|
maxPriorityFeePerGas: BigInt(0),
|
|
208
|
-
nonce:
|
|
209
|
-
deadline:
|
|
213
|
+
nonce: opts?.nonce ?? BigInt(0),
|
|
214
|
+
deadline: opts?.deadline ?? BigInt(9999999999),
|
|
210
215
|
vType: 1, // universalTxVerification
|
|
211
216
|
},
|
|
212
217
|
]);
|
|
@@ -218,16 +223,15 @@ function buildInboundUniversalPayload(multicallData, opts) {
|
|
|
218
223
|
* DecodeUniversalPayloadSolana, not the EVM ABI decoder used for EVM origins.
|
|
219
224
|
*/
|
|
220
225
|
function buildInboundUniversalPayloadSvm(multicallData, opts) {
|
|
221
|
-
var _a, _b, _c;
|
|
222
226
|
const payload = {
|
|
223
227
|
to: selectors_1.ZERO_ADDRESS,
|
|
224
228
|
value: BigInt(0).toString(),
|
|
225
229
|
data: multicallData,
|
|
226
|
-
gasLimit: (
|
|
227
|
-
maxFeePerGas: BigInt(
|
|
230
|
+
gasLimit: (opts?.gasLimit ?? BigInt(100_000_000)).toString(),
|
|
231
|
+
maxFeePerGas: BigInt(10_000_000_000).toString(),
|
|
228
232
|
maxPriorityFeePerGas: BigInt(0).toString(),
|
|
229
|
-
nonce: (
|
|
230
|
-
deadline: (
|
|
233
|
+
nonce: (opts?.nonce ?? BigInt(0)).toString(),
|
|
234
|
+
deadline: (opts?.deadline ?? BigInt(9_999_999_999)).toString(),
|
|
231
235
|
vType: tx_1.VerificationType.universalTxVerification,
|
|
232
236
|
};
|
|
233
237
|
return `0x${(0, signing_1.encodeUniversalPayloadSvm)(payload).toString('hex')}`;
|
|
@@ -355,7 +359,6 @@ function buildMigrationPayload() {
|
|
|
355
359
|
* @returns Array of MultiCall operations (approvals + outbound call)
|
|
356
360
|
*/
|
|
357
361
|
function buildOutboundApprovalAndCall(opts) {
|
|
358
|
-
var _a;
|
|
359
362
|
const { prc20Token, gasFee, gatewayPcAddress, outboundRequest } = opts;
|
|
360
363
|
const multicalls = [];
|
|
361
364
|
// ERC20 approve for burn amount (contract calls transferFrom for PRC20 burn)
|
|
@@ -390,7 +393,7 @@ function buildOutboundApprovalAndCall(opts) {
|
|
|
390
393
|
args: [outboundRequest],
|
|
391
394
|
});
|
|
392
395
|
// Use pre-computed nativeValueForGas (from Uniswap quoter) or fallback to 5x gasFee
|
|
393
|
-
const nativeValue =
|
|
396
|
+
const nativeValue = opts.nativeValueForGas ?? (gasFee * BigInt(5));
|
|
394
397
|
multicalls.push({
|
|
395
398
|
to: gatewayPcAddress,
|
|
396
399
|
value: nativeValue,
|
|
@@ -405,8 +408,7 @@ function buildOutboundApprovalAndCall(opts) {
|
|
|
405
408
|
* Check if a chain targets the SVM (Solana) virtual machine
|
|
406
409
|
*/
|
|
407
410
|
function isSvmChain(chain) {
|
|
408
|
-
|
|
409
|
-
return ((_a = chain_1.CHAIN_INFO[chain]) === null || _a === void 0 ? void 0 : _a.vm) === enums_1.VM.SVM;
|
|
411
|
+
return chain_1.CHAIN_INFO[chain]?.vm === enums_1.VM.SVM;
|
|
410
412
|
}
|
|
411
413
|
/**
|
|
412
414
|
* Validate a Solana address in 0x-prefixed hex format.
|
|
@@ -528,7 +530,7 @@ function encodeSvmCeaToUeaPayload({ gatewayProgramHex, drainAmount, tokenMintHex
|
|
|
528
530
|
const amountView = new DataView(amountBuf.buffer);
|
|
529
531
|
amountView.setBigUint64(0, drainAmount, true); // LE
|
|
530
532
|
// Extra payload (Vec<u8>: u32 LE length + bytes)
|
|
531
|
-
const payloadData = extraPayload
|
|
533
|
+
const payloadData = extraPayload ?? new Uint8Array(0);
|
|
532
534
|
const payloadLenBuf = new Uint8Array(4);
|
|
533
535
|
const payloadLenView = new DataView(payloadLenBuf.buffer);
|
|
534
536
|
payloadLenView.setUint32(0, payloadData.length, true); // LE
|
|
@@ -20,6 +20,7 @@ exports.validateRouteParams = validateRouteParams;
|
|
|
20
20
|
exports.getRouteInfo = getRouteInfo;
|
|
21
21
|
const enums_1 = require("../constants/enums");
|
|
22
22
|
const tokens_1 = require("../constants/tokens");
|
|
23
|
+
const orchestrator_types_1 = require("./orchestrator.types");
|
|
23
24
|
const payload_builders_1 = require("./payload-builders");
|
|
24
25
|
const data_utils_1 = require("./data-utils");
|
|
25
26
|
const normalize_address_1 = require("./svm-idl/normalize-address");
|
|
@@ -57,6 +58,7 @@ exports.RouteValidationError = RouteValidationError;
|
|
|
57
58
|
* Error thrown when a chain is not supported for CEA operations
|
|
58
59
|
*/
|
|
59
60
|
class ChainNotSupportedError extends Error {
|
|
61
|
+
chain;
|
|
60
62
|
constructor(chain) {
|
|
61
63
|
super(`Chain ${chain} is not supported for CEA operations`);
|
|
62
64
|
this.chain = chain;
|
|
@@ -128,7 +130,7 @@ function chainEnumToName(chain) {
|
|
|
128
130
|
*/
|
|
129
131
|
function findTokenChain(token) {
|
|
130
132
|
for (const [chain, tokens] of Object.entries(tokens_1.MOVEABLE_TOKENS)) {
|
|
131
|
-
if (tokens
|
|
133
|
+
if (tokens?.some(t => t.address.toLowerCase() === token.address.toLowerCase() && t.symbol === token.symbol)) {
|
|
132
134
|
return chain;
|
|
133
135
|
}
|
|
134
136
|
}
|
|
@@ -166,8 +168,7 @@ function isChainTarget(to) {
|
|
|
166
168
|
* @throws RouteValidationError if parameters are invalid
|
|
167
169
|
*/
|
|
168
170
|
function detectRoute(params) {
|
|
169
|
-
|
|
170
|
-
const hasFromChain = ((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain) !== undefined;
|
|
171
|
+
const hasFromChain = params.from?.chain !== undefined;
|
|
171
172
|
const toIsChainTarget = isChainTarget(params.to);
|
|
172
173
|
// Case 1: No from.chain, to is simple string → Route 1 (UOA → Push)
|
|
173
174
|
if (!hasFromChain && !toIsChainTarget) {
|
|
@@ -213,10 +214,9 @@ function detectRoute(params) {
|
|
|
213
214
|
* @throws ChainNotSupportedError if chain is not supported
|
|
214
215
|
*/
|
|
215
216
|
function validateRouteParams(params, context) {
|
|
216
|
-
var _a, _b, _c;
|
|
217
217
|
const route = detectRoute(params);
|
|
218
218
|
// Validate from.chain is supported external chain (Routes 3, 4)
|
|
219
|
-
if (
|
|
219
|
+
if (params.from?.chain) {
|
|
220
220
|
if (!isSupportedExternalChain(params.from.chain)) {
|
|
221
221
|
throw new ChainNotSupportedError(params.from.chain);
|
|
222
222
|
}
|
|
@@ -261,9 +261,18 @@ function validateRouteParams(params, context) {
|
|
|
261
261
|
// - Route 3/4 (CEA_TO_PUSH, CEA_TO_CEA): validate against from.chain.
|
|
262
262
|
// For a Push-registered PRC-20 the token's own `sourceChain` must match; for a
|
|
263
263
|
// non-Push token, the token's symbol must be registered on the chain.
|
|
264
|
-
|
|
264
|
+
// PC20 funds skip the moveable-token validation (and only that — the address
|
|
265
|
+
// checks below still run). The static table is symbol-keyed and PC20 identity
|
|
266
|
+
// is never symbol-based; the PC20 gate validates chain ownership against the
|
|
267
|
+
// live registry, which is strictly stronger than anything this block can
|
|
268
|
+
// check. Two shapes qualify: an unresolved `PC20TokenReference`
|
|
269
|
+
// (validateRouteParams can run before the gate, e.g. in prepareTransaction)
|
|
270
|
+
// and the gate's resolved output (`_pc20` descriptor present).
|
|
271
|
+
const isPc20Funds = params._pc20 !== undefined ||
|
|
272
|
+
(0, orchestrator_types_1.isPC20Reference)(params.funds?.token);
|
|
273
|
+
if (params.funds?.token && !isPc20Funds) {
|
|
265
274
|
const token = params.funds.token;
|
|
266
|
-
const clientLabel =
|
|
275
|
+
const clientLabel = context?.clientChain
|
|
267
276
|
? chainEnumToName(context.clientChain)
|
|
268
277
|
: 'unknown';
|
|
269
278
|
const registeredOn = findTokenChain(token);
|
|
@@ -278,7 +287,8 @@ function validateRouteParams(params, context) {
|
|
|
278
287
|
? (tokens_1.MOVEABLE_TOKENS[registeredOn] || []).find(t => t.address.toLowerCase() === token.address.toLowerCase() &&
|
|
279
288
|
t.symbol === token.symbol)
|
|
280
289
|
: undefined;
|
|
281
|
-
const effectiveSourceChain =
|
|
290
|
+
const effectiveSourceChain = token.sourceChain ??
|
|
291
|
+
registrySourceChain?.sourceChain;
|
|
282
292
|
const validateAgainst = (chain, direction) => {
|
|
283
293
|
if (isPushChain(chain))
|
|
284
294
|
return;
|
|
@@ -286,10 +296,17 @@ function validateRouteParams(params, context) {
|
|
|
286
296
|
? effectiveSourceChain === chain
|
|
287
297
|
: (tokens_1.MOVEABLE_TOKENS[chain] || []).some(t => t.symbol === token.symbol);
|
|
288
298
|
if (!ok) {
|
|
299
|
+
// A PC20 reference is { chain, address } ONLY. If a caller added a
|
|
300
|
+
// symbol to one, it classified as a MoveableToken and landed here —
|
|
301
|
+
// say so instead of leaving them to puzzle over the static table.
|
|
302
|
+
const pc20Hint = 'chain' in token
|
|
303
|
+
? '\nIf this is a PC20 token, pass { chain, address } only — no symbol or mechanism.'
|
|
304
|
+
: '';
|
|
289
305
|
throw new RouteValidationError(`Unsupported moveable token for current client and route:\n` +
|
|
290
306
|
`token=${tokenLabel}\n` +
|
|
291
307
|
`clientChain=${clientLabel}\n` +
|
|
292
|
-
`${direction}=${chainEnumToName(chain)}`
|
|
308
|
+
`${direction}=${chainEnumToName(chain)}` +
|
|
309
|
+
pc20Hint);
|
|
293
310
|
}
|
|
294
311
|
};
|
|
295
312
|
switch (route) {
|
|
@@ -6,10 +6,9 @@ const anchor_1 = require("@coral-xyz/anchor");
|
|
|
6
6
|
const web3_js_1 = require("@solana/web3.js");
|
|
7
7
|
const buffer_1 = require("buffer");
|
|
8
8
|
function encodeAnchorIxData(idl, functionName, positionalArgs = []) {
|
|
9
|
-
var _a, _b;
|
|
10
9
|
const ix = findInstruction(idl, functionName);
|
|
11
10
|
if (!ix) {
|
|
12
|
-
throw new Error(`Instruction '${functionName}' not found in IDL '${
|
|
11
|
+
throw new Error(`Instruction '${functionName}' not found in IDL '${idl.metadata?.name ?? 'unknown'}'`);
|
|
13
12
|
}
|
|
14
13
|
if (positionalArgs.length !== ix.args.length) {
|
|
15
14
|
throw new Error(`Arg count mismatch for '${ix.name}': IDL expects ${ix.args.length}, got ${positionalArgs.length}`);
|
|
@@ -26,7 +26,7 @@ function toSvmHexAddress(input) {
|
|
|
26
26
|
try {
|
|
27
27
|
decoded = bs58_1.bs58.decode(input);
|
|
28
28
|
}
|
|
29
|
-
catch
|
|
29
|
+
catch {
|
|
30
30
|
throw new Error(`SVM address '${input}' is not valid base58 and does not start with 0x. ` +
|
|
31
31
|
`Expected base58 (e.g. 'So11111111111111111111111111111111111111112') ` +
|
|
32
32
|
`or 0x-prefixed 32-byte hex.`);
|
|
@@ -49,7 +49,7 @@ function isValidSvmAddress(input) {
|
|
|
49
49
|
toSvmHexAddress(input);
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
52
|
-
catch
|
|
52
|
+
catch {
|
|
53
53
|
return false;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -13,7 +13,6 @@ const CEA_AUTHORITY_NAMES = new Set([
|
|
|
13
13
|
'ceaSigner',
|
|
14
14
|
]);
|
|
15
15
|
function resolveSvmCall(input) {
|
|
16
|
-
var _a, _b;
|
|
17
16
|
const idl = (0, registry_1.getIdl)(input.programAddress);
|
|
18
17
|
if (!idl) {
|
|
19
18
|
throw new Error(`resolveSvmCall: no IDL found for ${input.programAddress}. ` +
|
|
@@ -28,7 +27,7 @@ function resolveSvmCall(input) {
|
|
|
28
27
|
const disc = input.data.slice(0, 8);
|
|
29
28
|
const ix = idl.instructions.find((i) => buffer_1.Buffer.from(i.discriminator).equals(buffer_1.Buffer.from(disc)));
|
|
30
29
|
if (!ix) {
|
|
31
|
-
throw new Error(`resolveSvmCall: no instruction in IDL '${
|
|
30
|
+
throw new Error(`resolveSvmCall: no instruction in IDL '${idl.metadata?.name ?? idl.address}' matches discriminator [${Array.from(disc).join(',')}]`);
|
|
32
31
|
}
|
|
33
32
|
const programPk = new web3_js_1.PublicKey(buffer_1.Buffer.from(input.programAddress.slice(2), 'hex'));
|
|
34
33
|
const { address: ceaPdaHex } = (0, account_1.deriveSvmCeaPda)(input.senderUea, input.targetChain, true);
|
|
@@ -13,84 +13,83 @@ const web3_js_2 = require("@solana/web3.js");
|
|
|
13
13
|
const anchor_2 = require("@coral-xyz/anchor");
|
|
14
14
|
// Module-level price cache — survives across PriceFetch instances.
|
|
15
15
|
// Chainlink feeds update every 1-2h or on 0.5% price move; 30s TTL is safe.
|
|
16
|
-
const PRICE_CACHE_TTL_MS =
|
|
16
|
+
const PRICE_CACHE_TTL_MS = 30_000;
|
|
17
17
|
const priceCache = new Map();
|
|
18
18
|
class PriceFetch {
|
|
19
|
+
rpcUrls;
|
|
19
20
|
constructor(rpcUrls = {}) {
|
|
20
21
|
this.rpcUrls = rpcUrls;
|
|
21
22
|
}
|
|
22
|
-
getPrice(chain) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
async getPrice(chain) {
|
|
24
|
+
const cached = priceCache.get(chain);
|
|
25
|
+
if (cached && Date.now() < cached.expiry) {
|
|
26
|
+
return cached.price;
|
|
27
|
+
}
|
|
28
|
+
const rpcUrls = this.rpcUrls[chain] || chain_1.CHAIN_INFO[chain].defaultRPC;
|
|
29
|
+
const vm = chain_1.CHAIN_INFO[chain].vm;
|
|
30
|
+
const { lockerContract } = chain_1.CHAIN_INFO[chain];
|
|
31
|
+
if (!lockerContract) {
|
|
32
|
+
throw new Error(`Locker contract not configured for chain: ${chain}`);
|
|
33
|
+
}
|
|
34
|
+
let price;
|
|
35
|
+
switch (vm) {
|
|
36
|
+
case enums_1.VM.EVM: {
|
|
37
|
+
const evmClient = new evm_client_1.EvmClient({ rpcUrls });
|
|
38
|
+
const result = await evmClient.readContract({
|
|
39
|
+
abi: feeLocker_evm_1.FEE_LOCKER_EVM,
|
|
40
|
+
address: lockerContract,
|
|
41
|
+
functionName: 'getEthUsdPrice',
|
|
42
|
+
});
|
|
43
|
+
// getEthUsdPrice returns price scaled to 1e18 and the chainlink feed decimals.
|
|
44
|
+
// Downstream lockFee math expects the price in 8 decimals, so scale down from 1e18 to 1e8.
|
|
45
|
+
const [price1e18] = result;
|
|
46
|
+
price = price1e18 / BigInt(10 ** 10); // 1e18 -> 1e8
|
|
47
|
+
break;
|
|
27
48
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// getEthUsdPrice returns price scaled to 1e18 and the chainlink feed decimals.
|
|
44
|
-
// Downstream lockFee math expects the price in 8 decimals, so scale down from 1e18 to 1e8.
|
|
45
|
-
const [price1e18] = result;
|
|
46
|
-
price = price1e18 / BigInt(Math.pow(10, 10)); // 1e18 -> 1e8
|
|
47
|
-
break;
|
|
49
|
+
case enums_1.VM.SVM: {
|
|
50
|
+
const PRICE_ACCOUNT = new web3_js_2.PublicKey('7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE');
|
|
51
|
+
const connection = new web3_js_1.Connection(rpcUrls[0], 'confirmed');
|
|
52
|
+
const provider = new anchor_2.AnchorProvider(connection, {
|
|
53
|
+
publicKey: new web3_js_2.PublicKey('EfQYRThwBu4MsU7Lf3D2e68tCtdwfYj6f66ot1e2HNrq'),
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
}, { commitment: 'confirmed' });
|
|
56
|
+
const program = new anchor_1.Program(feeLocker_json_1.default, provider);
|
|
57
|
+
let result;
|
|
58
|
+
try {
|
|
59
|
+
result = await program.methods['getSolPrice']()
|
|
60
|
+
.accounts({
|
|
61
|
+
priceUpdate: PRICE_ACCOUNT,
|
|
62
|
+
})
|
|
63
|
+
.view();
|
|
48
64
|
}
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
.view();
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
const e = err;
|
|
67
|
-
console.error('[price-fetch][SVM getSolPrice] FAILED');
|
|
68
|
-
console.error(' rpc:', rpcUrls[0]);
|
|
69
|
-
console.error(' locker:', lockerContract);
|
|
70
|
-
console.error(' priceAccount:', PRICE_ACCOUNT.toBase58());
|
|
71
|
-
console.error(' message:', e === null || e === void 0 ? void 0 : e.message);
|
|
72
|
-
if (e === null || e === void 0 ? void 0 : e.logs)
|
|
73
|
-
console.error(' logs:', e.logs);
|
|
74
|
-
if (e === null || e === void 0 ? void 0 : e.simulationResponse)
|
|
75
|
-
console.error(' simulationResponse:', JSON.stringify(e.simulationResponse, null, 2));
|
|
76
|
-
if (e === null || e === void 0 ? void 0 : e.programErrorStack)
|
|
77
|
-
console.error(' programErrorStack:', e.programErrorStack);
|
|
78
|
-
throw err;
|
|
79
|
-
}
|
|
80
|
-
if (!result || !result.price) {
|
|
81
|
-
throw new Error('Invalid price data returned');
|
|
82
|
-
}
|
|
83
|
-
// Exponent on this function is always NEGATIVE
|
|
84
|
-
price = BigInt(result.price.toNumber());
|
|
85
|
-
break;
|
|
65
|
+
catch (err) {
|
|
66
|
+
const e = err;
|
|
67
|
+
console.error('[price-fetch][SVM getSolPrice] FAILED');
|
|
68
|
+
console.error(' rpc:', rpcUrls[0]);
|
|
69
|
+
console.error(' locker:', lockerContract);
|
|
70
|
+
console.error(' priceAccount:', PRICE_ACCOUNT.toBase58());
|
|
71
|
+
console.error(' message:', e?.message);
|
|
72
|
+
if (e?.logs)
|
|
73
|
+
console.error(' logs:', e.logs);
|
|
74
|
+
if (e?.simulationResponse)
|
|
75
|
+
console.error(' simulationResponse:', JSON.stringify(e.simulationResponse, null, 2));
|
|
76
|
+
if (e?.programErrorStack)
|
|
77
|
+
console.error(' programErrorStack:', e.programErrorStack);
|
|
78
|
+
throw err;
|
|
86
79
|
}
|
|
87
|
-
|
|
88
|
-
throw new Error(
|
|
80
|
+
if (!result || !result.price) {
|
|
81
|
+
throw new Error('Invalid price data returned');
|
|
89
82
|
}
|
|
83
|
+
// Exponent on this function is always NEGATIVE
|
|
84
|
+
price = BigInt(result.price.toNumber());
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
default: {
|
|
88
|
+
throw new Error(`Unsupported VM ${vm}`);
|
|
90
89
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
}
|
|
91
|
+
priceCache.set(chain, { price, expiry: Date.now() + PRICE_CACHE_TTL_MS });
|
|
92
|
+
return price;
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
exports.PriceFetch = PriceFetch;
|