@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
|
@@ -9,7 +9,6 @@ exports.signUniversalPayload = signUniversalPayload;
|
|
|
9
9
|
exports.signMigrationPayload = signMigrationPayload;
|
|
10
10
|
exports.encodeUniversalPayload = encodeUniversalPayload;
|
|
11
11
|
exports.encodeUniversalPayloadSvm = encodeUniversalPayloadSvm;
|
|
12
|
-
const tslib_1 = require("tslib");
|
|
13
12
|
const viem_1 = require("viem");
|
|
14
13
|
const chain_1 = require("../../constants/chain");
|
|
15
14
|
const enums_1 = require("../../constants/enums");
|
|
@@ -96,95 +95,91 @@ function computeMigrationHash(ctx, { verifyingContract, migrationContractAddress
|
|
|
96
95
|
// ============================================================================
|
|
97
96
|
// Payload Signing
|
|
98
97
|
// ============================================================================
|
|
99
|
-
function signUniversalPayload(ctx, universalPayload, verifyingContract, version) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
throw new Error('signTypedData is not defined');
|
|
107
|
-
}
|
|
108
|
-
return ctx.universalSigner.signTypedData({
|
|
109
|
-
domain: {
|
|
110
|
-
version: version || '0.1.0',
|
|
111
|
-
chainId: Number(chainId),
|
|
112
|
-
verifyingContract,
|
|
113
|
-
},
|
|
114
|
-
types: {
|
|
115
|
-
UniversalPayload: [
|
|
116
|
-
{ name: 'to', type: 'address' },
|
|
117
|
-
{ name: 'value', type: 'uint256' },
|
|
118
|
-
{ name: 'data', type: 'bytes' },
|
|
119
|
-
{ name: 'gasLimit', type: 'uint256' },
|
|
120
|
-
{ name: 'maxFeePerGas', type: 'uint256' },
|
|
121
|
-
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
|
|
122
|
-
{ name: 'nonce', type: 'uint256' },
|
|
123
|
-
{ name: 'deadline', type: 'uint256' },
|
|
124
|
-
{ name: 'vType', type: 'uint8' },
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
primaryType: 'UniversalPayload',
|
|
128
|
-
message: universalPayload,
|
|
129
|
-
});
|
|
98
|
+
async function signUniversalPayload(ctx, universalPayload, verifyingContract, version) {
|
|
99
|
+
const chain = ctx.universalSigner.account.chain;
|
|
100
|
+
const { vm, chainId } = chain_1.CHAIN_INFO[chain];
|
|
101
|
+
switch (vm) {
|
|
102
|
+
case enums_1.VM.EVM: {
|
|
103
|
+
if (!ctx.universalSigner.signTypedData) {
|
|
104
|
+
throw new Error('signTypedData is not defined');
|
|
130
105
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
verifyingContract,
|
|
134
|
-
payload: universalPayload,
|
|
106
|
+
return ctx.universalSigner.signTypedData({
|
|
107
|
+
domain: {
|
|
135
108
|
version: version || '0.1.0',
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
109
|
+
chainId: Number(chainId),
|
|
110
|
+
verifyingContract,
|
|
111
|
+
},
|
|
112
|
+
types: {
|
|
113
|
+
UniversalPayload: [
|
|
114
|
+
{ name: 'to', type: 'address' },
|
|
115
|
+
{ name: 'value', type: 'uint256' },
|
|
116
|
+
{ name: 'data', type: 'bytes' },
|
|
117
|
+
{ name: 'gasLimit', type: 'uint256' },
|
|
118
|
+
{ name: 'maxFeePerGas', type: 'uint256' },
|
|
119
|
+
{ name: 'maxPriorityFeePerGas', type: 'uint256' },
|
|
120
|
+
{ name: 'nonce', type: 'uint256' },
|
|
121
|
+
{ name: 'deadline', type: 'uint256' },
|
|
122
|
+
{ name: 'vType', type: 'uint8' },
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
primaryType: 'UniversalPayload',
|
|
126
|
+
message: universalPayload,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
case enums_1.VM.SVM: {
|
|
130
|
+
const digest = computeExecutionHash(ctx, {
|
|
131
|
+
verifyingContract,
|
|
132
|
+
payload: universalPayload,
|
|
133
|
+
version: version || '0.1.0',
|
|
134
|
+
});
|
|
135
|
+
return ctx.universalSigner.signMessage((0, viem_1.stringToBytes)(digest));
|
|
141
136
|
}
|
|
142
|
-
|
|
137
|
+
default:
|
|
138
|
+
throw new Error(`Unsupported VM type: ${vm}`);
|
|
139
|
+
}
|
|
143
140
|
}
|
|
144
|
-
function signMigrationPayload(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
throw new Error('signTypedData is not defined');
|
|
152
|
-
}
|
|
153
|
-
return ctx.universalSigner.signTypedData({
|
|
154
|
-
domain: {
|
|
155
|
-
version: ueaVersion,
|
|
156
|
-
chainId: Number(chainId),
|
|
157
|
-
verifyingContract: ueaAddress,
|
|
158
|
-
},
|
|
159
|
-
types: {
|
|
160
|
-
MigrationPayload: [
|
|
161
|
-
{ name: 'migration', type: 'address' },
|
|
162
|
-
{ name: 'nonce', type: 'uint256' },
|
|
163
|
-
{ name: 'deadline', type: 'uint256' },
|
|
164
|
-
],
|
|
165
|
-
},
|
|
166
|
-
primaryType: 'MigrationPayload',
|
|
167
|
-
message: {
|
|
168
|
-
migration: migrationContractAddress,
|
|
169
|
-
nonce: nonce.toString(),
|
|
170
|
-
deadline: deadline.toString(),
|
|
171
|
-
},
|
|
172
|
-
});
|
|
141
|
+
async function signMigrationPayload(ctx, { migrationContractAddress, nonce, deadline, ueaVersion, ueaAddress, }) {
|
|
142
|
+
const chain = ctx.universalSigner.account.chain;
|
|
143
|
+
const { vm, chainId } = chain_1.CHAIN_INFO[chain];
|
|
144
|
+
switch (vm) {
|
|
145
|
+
case enums_1.VM.EVM: {
|
|
146
|
+
if (!ctx.universalSigner.signTypedData) {
|
|
147
|
+
throw new Error('signTypedData is not defined');
|
|
173
148
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
verifyingContract: ueaAddress,
|
|
177
|
-
migrationContractAddress,
|
|
178
|
-
nonce,
|
|
179
|
-
deadline,
|
|
149
|
+
return ctx.universalSigner.signTypedData({
|
|
150
|
+
domain: {
|
|
180
151
|
version: ueaVersion,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
152
|
+
chainId: Number(chainId),
|
|
153
|
+
verifyingContract: ueaAddress,
|
|
154
|
+
},
|
|
155
|
+
types: {
|
|
156
|
+
MigrationPayload: [
|
|
157
|
+
{ name: 'migration', type: 'address' },
|
|
158
|
+
{ name: 'nonce', type: 'uint256' },
|
|
159
|
+
{ name: 'deadline', type: 'uint256' },
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
primaryType: 'MigrationPayload',
|
|
163
|
+
message: {
|
|
164
|
+
migration: migrationContractAddress,
|
|
165
|
+
nonce: nonce.toString(),
|
|
166
|
+
deadline: deadline.toString(),
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
case enums_1.VM.SVM: {
|
|
171
|
+
const digest = computeMigrationHash(ctx, {
|
|
172
|
+
verifyingContract: ueaAddress,
|
|
173
|
+
migrationContractAddress,
|
|
174
|
+
nonce,
|
|
175
|
+
deadline,
|
|
176
|
+
version: ueaVersion,
|
|
177
|
+
});
|
|
178
|
+
return ctx.universalSigner.signMessage((0, viem_1.stringToBytes)(digest));
|
|
186
179
|
}
|
|
187
|
-
|
|
180
|
+
default:
|
|
181
|
+
throw new Error(`Unsupported VM type for migration: ${vm}`);
|
|
182
|
+
}
|
|
188
183
|
}
|
|
189
184
|
// ============================================================================
|
|
190
185
|
// Payload Encoding
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* Handles both native SOL and SPL token bridging with universal payload signing.
|
|
6
6
|
*/
|
|
7
7
|
import type { UniversalPayload } from '../../generated/v1/tx';
|
|
8
|
-
import type {
|
|
8
|
+
import type { LegacyExecuteParams, UniversalTxRequest } from '../orchestrator.types';
|
|
9
9
|
import type { OrchestratorContext } from './context';
|
|
10
10
|
/**
|
|
11
11
|
* Sends a Solana gateway transaction for the funds+payload bridge path.
|
|
12
12
|
* Handles native SOL and SPL token mechanisms.
|
|
13
13
|
*/
|
|
14
14
|
export declare function sendSVMTxWithFunds(ctx: OrchestratorContext, params: {
|
|
15
|
-
execute:
|
|
15
|
+
execute: LegacyExecuteParams;
|
|
16
16
|
mechanism: 'native' | 'approve' | 'permit2' | string;
|
|
17
17
|
universalPayload: UniversalPayload;
|
|
18
18
|
bridgeAmount: bigint;
|
|
@@ -15,6 +15,7 @@ const chain_1 = require("../../constants/chain");
|
|
|
15
15
|
const enums_1 = require("../../constants/enums");
|
|
16
16
|
const svm_client_1 = require("../../vm-client/svm-client");
|
|
17
17
|
const svm_helpers_1 = require("./svm-helpers");
|
|
18
|
+
const svm_1 = require("./pc20/svm");
|
|
18
19
|
const signing_1 = require("./signing");
|
|
19
20
|
const uea_manager_1 = require("./uea-manager");
|
|
20
21
|
// ============================================================================
|
|
@@ -24,99 +25,166 @@ const uea_manager_1 = require("./uea-manager");
|
|
|
24
25
|
* Sends a Solana gateway transaction for the funds+payload bridge path.
|
|
25
26
|
* Handles native SOL and SPL token mechanisms.
|
|
26
27
|
*/
|
|
27
|
-
function sendSVMTxWithFunds(ctx, params) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
rpcUrls: ctx.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
33
|
-
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
34
|
-
});
|
|
35
|
-
const programId = new web3_js_1.PublicKey(universalGatewayV0_json_1.default.address);
|
|
36
|
-
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
37
|
-
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
38
|
-
const userPk = new web3_js_1.PublicKey(ctx.universalSigner.account.address);
|
|
39
|
-
const priceUpdatePk = new web3_js_1.PublicKey(chain_1.SVM_PYTH_PRICE_FEED);
|
|
40
|
-
const [rateLimitConfigPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit_config')], programId);
|
|
41
|
-
if (execute.payGasWith !== undefined) {
|
|
42
|
-
throw new Error('Pay-with token is not supported on Solana');
|
|
43
|
-
}
|
|
44
|
-
if (!((_b = (_a = execute.funds) === null || _a === void 0 ? void 0 : _a.token) === null || _b === void 0 ? void 0 : _b.address)) {
|
|
45
|
-
throw new Error('Token address is required for bridge path');
|
|
46
|
-
}
|
|
47
|
-
const isNative = mechanism === 'native' || execute.funds.token.symbol === 'SOL';
|
|
48
|
-
const { feeVaultPda, protocolFeeLamports } = yield (0, svm_helpers_1.getSvmProtocolFee)(svmClient, programId);
|
|
49
|
-
const ueaAddressSvm = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
50
|
-
const ueaVersion = yield (0, uea_manager_1.fetchUEAVersion)(ctx);
|
|
51
|
-
const svmSignature = yield (0, signing_1.signUniversalPayload)(ctx, universalPayload, ueaAddressSvm, ueaVersion);
|
|
52
|
-
if (isNative) {
|
|
53
|
-
const [tokenRateLimitPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit'), web3_js_1.PublicKey.default.toBuffer()], programId);
|
|
54
|
-
const nativeReq = (0, svm_helpers_1.buildSvmUniversalTxRequest)({
|
|
55
|
-
recipient: Array.from(Buffer.alloc(20, 0)),
|
|
56
|
-
token: web3_js_1.PublicKey.default,
|
|
57
|
-
amount: bridgeAmount,
|
|
58
|
-
payload: Uint8Array.from((0, signing_1.encodeUniversalPayloadSvm)(universalPayload)),
|
|
59
|
-
revertRecipient: userPk,
|
|
60
|
-
signatureData: svmSignature,
|
|
61
|
-
});
|
|
62
|
-
return yield svmClient.writeContract({
|
|
63
|
-
abi: universalGatewayV0_json_1.default,
|
|
64
|
-
address: programId.toBase58(),
|
|
65
|
-
functionName: 'sendUniversalTx',
|
|
66
|
-
args: [nativeReq, nativeAmount + protocolFeeLamports],
|
|
67
|
-
signer: ctx.universalSigner,
|
|
68
|
-
accounts: {
|
|
69
|
-
config: configPda,
|
|
70
|
-
vault: vaultPda,
|
|
71
|
-
feeVault: feeVaultPda,
|
|
72
|
-
userTokenAccount: vaultPda,
|
|
73
|
-
gatewayTokenAccount: vaultPda,
|
|
74
|
-
user: userPk,
|
|
75
|
-
priceUpdate: priceUpdatePk,
|
|
76
|
-
tokenProgram: new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
|
|
77
|
-
rateLimitConfig: rateLimitConfigPda,
|
|
78
|
-
tokenRateLimit: tokenRateLimitPda,
|
|
79
|
-
systemProgram: web3_js_1.SystemProgram.programId,
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
// Token address already validated above (line 67)
|
|
85
|
-
const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
|
|
86
|
-
const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
87
|
-
const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
|
|
88
|
-
const userAta = web3_js_1.PublicKey.findProgramAddressSync([userPk.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mintPk.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
89
|
-
const vaultAta = web3_js_1.PublicKey.findProgramAddressSync([vaultPda.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mintPk.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
90
|
-
const [tokenRateLimitPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit'), mintPk.toBuffer()], programId);
|
|
91
|
-
const splReq = (0, svm_helpers_1.buildSvmUniversalTxRequest)({
|
|
92
|
-
recipient: Array.from(Buffer.alloc(20, 0)),
|
|
93
|
-
token: mintPk,
|
|
94
|
-
amount: bridgeAmount,
|
|
95
|
-
payload: Uint8Array.from((0, signing_1.encodeUniversalPayloadSvm)(universalPayload)),
|
|
96
|
-
revertRecipient: userPk,
|
|
97
|
-
signatureData: svmSignature,
|
|
98
|
-
});
|
|
99
|
-
return yield svmClient.writeContract({
|
|
100
|
-
abi: universalGatewayV0_json_1.default,
|
|
101
|
-
address: programId.toBase58(),
|
|
102
|
-
functionName: 'sendUniversalTx',
|
|
103
|
-
args: [splReq, nativeAmount + protocolFeeLamports],
|
|
104
|
-
signer: ctx.universalSigner,
|
|
105
|
-
accounts: {
|
|
106
|
-
config: configPda,
|
|
107
|
-
vault: vaultPda,
|
|
108
|
-
feeVault: feeVaultPda,
|
|
109
|
-
userTokenAccount: userAta,
|
|
110
|
-
gatewayTokenAccount: vaultAta,
|
|
111
|
-
user: userPk,
|
|
112
|
-
priceUpdate: priceUpdatePk,
|
|
113
|
-
tokenProgram: TOKEN_PROGRAM_ID,
|
|
114
|
-
rateLimitConfig: rateLimitConfigPda,
|
|
115
|
-
tokenRateLimit: tokenRateLimitPda,
|
|
116
|
-
systemProgram: web3_js_1.SystemProgram.programId,
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
}
|
|
28
|
+
async function sendSVMTxWithFunds(ctx, params) {
|
|
29
|
+
const { execute, mechanism, universalPayload, bridgeAmount, nativeAmount, req } = params;
|
|
30
|
+
const svmClient = new svm_client_1.SvmClient({
|
|
31
|
+
rpcUrls: ctx.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
32
|
+
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
120
33
|
});
|
|
34
|
+
const programId = new web3_js_1.PublicKey(universalGatewayV0_json_1.default.address);
|
|
35
|
+
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
36
|
+
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
37
|
+
const userPk = new web3_js_1.PublicKey(ctx.universalSigner.account.address);
|
|
38
|
+
const priceUpdatePk = new web3_js_1.PublicKey(chain_1.SVM_PYTH_PRICE_FEED);
|
|
39
|
+
const [rateLimitConfigPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit_config')], programId);
|
|
40
|
+
if (execute.payGasWith !== undefined) {
|
|
41
|
+
throw new Error('Pay-with token is not supported on Solana');
|
|
42
|
+
}
|
|
43
|
+
if (!execute.funds?.token?.address) {
|
|
44
|
+
throw new Error('Token address is required for bridge path');
|
|
45
|
+
}
|
|
46
|
+
const isNative = mechanism === 'native' || execute.funds.token.symbol === 'SOL';
|
|
47
|
+
const { feeVaultPda, protocolFeeLamports } = await (0, svm_helpers_1.getSvmProtocolFee)(svmClient, programId);
|
|
48
|
+
const ueaAddressSvm = (0, uea_manager_1.computeUEAOffchain)(ctx);
|
|
49
|
+
const ueaVersion = await (0, uea_manager_1.fetchUEAVersion)(ctx);
|
|
50
|
+
const svmSignature = await (0, signing_1.signUniversalPayload)(ctx, universalPayload, ueaAddressSvm, ueaVersion);
|
|
51
|
+
// --- PC20 wrapper burn -------------------------------------------------
|
|
52
|
+
// route_pc20_universal_tx (deposit.rs) differs from the SPL escrow route in
|
|
53
|
+
// four program-enforced ways, each of which reverts if violated:
|
|
54
|
+
// 1. req.recipient must be the NONZERO 20-byte Push recipient — the chain
|
|
55
|
+
// unlocks the source token directly to it (unlockPC20), unlike SPL
|
|
56
|
+
// where zeros are fine because the UEA payload moves funds.
|
|
57
|
+
// 2. gateway_token_account must be ABSENT — the wrapper is burned via the
|
|
58
|
+
// mint authority, never escrowed.
|
|
59
|
+
// 3. remaining_accounts must be exactly [pc20_state ro, pc20_mint w].
|
|
60
|
+
// 4. token_rate_limit is null for a pure burn; the NATIVE-SOL rate-limit
|
|
61
|
+
// PDA when native value above the inbound fee rides along (that excess
|
|
62
|
+
// is a rate-limited native FUNDS leg).
|
|
63
|
+
// The SDK must NOT prepend the PC20 selector — the gateway does
|
|
64
|
+
// (pc20_prefixed_payload), mirroring the EVM inbound.
|
|
65
|
+
const pc20 = execute._pc20;
|
|
66
|
+
if (pc20?.direction === 'import') {
|
|
67
|
+
const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
|
|
68
|
+
const { remainingAccounts } = (0, svm_1.buildPC20BurnAccounts)({
|
|
69
|
+
programId,
|
|
70
|
+
mint: mintPk,
|
|
71
|
+
});
|
|
72
|
+
const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
73
|
+
const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
|
|
74
|
+
const userAta = web3_js_1.PublicKey.findProgramAddressSync([userPk.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mintPk.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
75
|
+
// A pure PC20 burn consumes no per-token rate limit — pass null. When
|
|
76
|
+
// native value rides along (nativeAmount > 0), the post-fee excess routes
|
|
77
|
+
// as a native FUNDS leg, which IS rate-limited: that leg requires the
|
|
78
|
+
// native-SOL rate-limit PDA (token_mint = Pubkey::default()) — the program
|
|
79
|
+
// rejects a mint-derived one (deposit.rs route_pc20_universal_tx).
|
|
80
|
+
const tokenRateLimit = nativeAmount > BigInt(0)
|
|
81
|
+
? web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit'), web3_js_1.PublicKey.default.toBuffer()], programId)[0]
|
|
82
|
+
: null;
|
|
83
|
+
// The unlock recipient is ALWAYS the signer's UEA, never `execute.to`:
|
|
84
|
+
// the payload multicall transfers the canonical token FROM the UEA to
|
|
85
|
+
// `execute.to` (buildExecuteMulticall Branch 2), so the unlock must land
|
|
86
|
+
// on the UEA for that leg to have funds — EVM parity, and the exact shape
|
|
87
|
+
// the R3 drain verified live (degenerate self-transfer when to === UEA).
|
|
88
|
+
// Unlocking directly to `to` while the payload transfers from the UEA
|
|
89
|
+
// would strand the transfer leg with a zero balance.
|
|
90
|
+
const pushRecipient = ueaAddressSvm;
|
|
91
|
+
const burnReq = (0, svm_helpers_1.buildSvmUniversalTxRequest)({
|
|
92
|
+
recipient: Array.from(Buffer.from(pushRecipient.slice(2), 'hex')),
|
|
93
|
+
token: mintPk,
|
|
94
|
+
amount: bridgeAmount,
|
|
95
|
+
payload: Uint8Array.from((0, signing_1.encodeUniversalPayloadSvm)(universalPayload)),
|
|
96
|
+
revertRecipient: userPk,
|
|
97
|
+
signatureData: svmSignature,
|
|
98
|
+
});
|
|
99
|
+
return await svmClient.writeContract({
|
|
100
|
+
abi: universalGatewayV0_json_1.default,
|
|
101
|
+
address: programId.toBase58(),
|
|
102
|
+
functionName: 'sendUniversalTx',
|
|
103
|
+
args: [burnReq, nativeAmount + protocolFeeLamports],
|
|
104
|
+
signer: ctx.universalSigner,
|
|
105
|
+
accounts: {
|
|
106
|
+
config: configPda,
|
|
107
|
+
vault: vaultPda,
|
|
108
|
+
feeVault: feeVaultPda,
|
|
109
|
+
userTokenAccount: userAta,
|
|
110
|
+
gatewayTokenAccount: null,
|
|
111
|
+
user: userPk,
|
|
112
|
+
priceUpdate: priceUpdatePk,
|
|
113
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
114
|
+
rateLimitConfig: rateLimitConfigPda,
|
|
115
|
+
tokenRateLimit,
|
|
116
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
117
|
+
},
|
|
118
|
+
remainingAccounts,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (isNative) {
|
|
122
|
+
const [tokenRateLimitPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit'), web3_js_1.PublicKey.default.toBuffer()], programId);
|
|
123
|
+
const nativeReq = (0, svm_helpers_1.buildSvmUniversalTxRequest)({
|
|
124
|
+
recipient: Array.from(Buffer.alloc(20, 0)),
|
|
125
|
+
token: web3_js_1.PublicKey.default,
|
|
126
|
+
amount: bridgeAmount,
|
|
127
|
+
payload: Uint8Array.from((0, signing_1.encodeUniversalPayloadSvm)(universalPayload)),
|
|
128
|
+
revertRecipient: userPk,
|
|
129
|
+
signatureData: svmSignature,
|
|
130
|
+
});
|
|
131
|
+
return await svmClient.writeContract({
|
|
132
|
+
abi: universalGatewayV0_json_1.default,
|
|
133
|
+
address: programId.toBase58(),
|
|
134
|
+
functionName: 'sendUniversalTx',
|
|
135
|
+
args: [nativeReq, nativeAmount + protocolFeeLamports],
|
|
136
|
+
signer: ctx.universalSigner,
|
|
137
|
+
accounts: {
|
|
138
|
+
config: configPda,
|
|
139
|
+
vault: vaultPda,
|
|
140
|
+
feeVault: feeVaultPda,
|
|
141
|
+
userTokenAccount: vaultPda,
|
|
142
|
+
gatewayTokenAccount: vaultPda,
|
|
143
|
+
user: userPk,
|
|
144
|
+
priceUpdate: priceUpdatePk,
|
|
145
|
+
tokenProgram: new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
|
|
146
|
+
rateLimitConfig: rateLimitConfigPda,
|
|
147
|
+
tokenRateLimit: tokenRateLimitPda,
|
|
148
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Token address already validated above (line 67)
|
|
154
|
+
const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
|
|
155
|
+
const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
156
|
+
const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
|
|
157
|
+
const userAta = web3_js_1.PublicKey.findProgramAddressSync([userPk.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mintPk.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
158
|
+
const vaultAta = web3_js_1.PublicKey.findProgramAddressSync([vaultPda.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mintPk.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
159
|
+
const [tokenRateLimitPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('rate_limit'), mintPk.toBuffer()], programId);
|
|
160
|
+
const splReq = (0, svm_helpers_1.buildSvmUniversalTxRequest)({
|
|
161
|
+
recipient: Array.from(Buffer.alloc(20, 0)),
|
|
162
|
+
token: mintPk,
|
|
163
|
+
amount: bridgeAmount,
|
|
164
|
+
payload: Uint8Array.from((0, signing_1.encodeUniversalPayloadSvm)(universalPayload)),
|
|
165
|
+
revertRecipient: userPk,
|
|
166
|
+
signatureData: svmSignature,
|
|
167
|
+
});
|
|
168
|
+
return await svmClient.writeContract({
|
|
169
|
+
abi: universalGatewayV0_json_1.default,
|
|
170
|
+
address: programId.toBase58(),
|
|
171
|
+
functionName: 'sendUniversalTx',
|
|
172
|
+
args: [splReq, nativeAmount + protocolFeeLamports],
|
|
173
|
+
signer: ctx.universalSigner,
|
|
174
|
+
accounts: {
|
|
175
|
+
config: configPda,
|
|
176
|
+
vault: vaultPda,
|
|
177
|
+
feeVault: feeVaultPda,
|
|
178
|
+
userTokenAccount: userAta,
|
|
179
|
+
gatewayTokenAccount: vaultAta,
|
|
180
|
+
user: userPk,
|
|
181
|
+
priceUpdate: priceUpdatePk,
|
|
182
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
183
|
+
rateLimitConfig: rateLimitConfigPda,
|
|
184
|
+
tokenRateLimit: tokenRateLimitPda,
|
|
185
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
121
189
|
}
|
|
122
190
|
//# sourceMappingURL=svm-bridge.js.map
|
|
@@ -32,4 +32,18 @@ export declare function getSvmProtocolFee(svmClient: {
|
|
|
32
32
|
feeVaultPda: PublicKey;
|
|
33
33
|
protocolFeeLamports: bigint;
|
|
34
34
|
}>;
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Locate the gateway event whose UTX the caller should track.
|
|
37
|
+
*
|
|
38
|
+
* Default: the second gateway event when there are two (a gas leg followed by
|
|
39
|
+
* the funds leg), else the last one.
|
|
40
|
+
*
|
|
41
|
+
* `preferPC20` inverts that for PC20 wrapper burns. Such a transaction emits
|
|
42
|
+
* the PC20 burn event first and then, when native value rides along, a plain
|
|
43
|
+
* funds event for the gas deposit — so the default would track the deposit
|
|
44
|
+
* leg. A failure crediting that deposit (the known chain-side fee-credit bug)
|
|
45
|
+
* would then be reported as the whole transfer failing, even though the tokens
|
|
46
|
+
* arrived via the first event. Selection is by payload selector rather than
|
|
47
|
+
* position, so it survives a change in emission order.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getSvmGatewayLogIndexFromTx(txResp: any, preferPC20?: boolean): number;
|
|
@@ -74,30 +74,30 @@ function buildSvmUniversalTxRequestFromReq(req, revertRecipient, signatureDataOv
|
|
|
74
74
|
amount: req.amount,
|
|
75
75
|
payload: req.payload,
|
|
76
76
|
revertRecipient,
|
|
77
|
-
signatureData: signatureDataOverride
|
|
77
|
+
signatureData: signatureDataOverride ?? req.signatureData,
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
// ============================================================================
|
|
81
81
|
// SVM Protocol Fee
|
|
82
82
|
// ============================================================================
|
|
83
|
-
function getSvmProtocolFee(svmClient, programId) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
}
|
|
83
|
+
async function getSvmProtocolFee(svmClient, programId) {
|
|
84
|
+
const [feeVaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('fee_vault')], programId);
|
|
85
|
+
try {
|
|
86
|
+
const feeVault = await svmClient.readContract({
|
|
87
|
+
abi: universalGatewayV0_json_1.default,
|
|
88
|
+
address: universalGatewayV0_json_1.default.address,
|
|
89
|
+
functionName: 'feeVault',
|
|
90
|
+
args: [feeVaultPda.toBase58()],
|
|
91
|
+
});
|
|
92
|
+
const protocolFeeLamports = BigInt((feeVault.inboundFeeLamports ??
|
|
93
|
+
feeVault.inbound_fee_lamports ??
|
|
94
|
+
feeVault.protocolFeeLamports ??
|
|
95
|
+
feeVault.protocol_fee_lamports)?.toString() ?? '0');
|
|
96
|
+
return { feeVaultPda, protocolFeeLamports };
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return { feeVaultPda, protocolFeeLamports: BigInt(0) };
|
|
100
|
+
}
|
|
101
101
|
}
|
|
102
102
|
// ============================================================================
|
|
103
103
|
// SVM Log Parsing
|
|
@@ -106,14 +106,51 @@ function getSvmProtocolFee(svmClient, programId) {
|
|
|
106
106
|
const SVM_GATEWAY_EVENT_DISCRIMINATOR = '6c9ad829b5ea1d7c';
|
|
107
107
|
// ============================================================================
|
|
108
108
|
// ============================================================================
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Does this decoded `UniversalTx` event body carry a PC20-selector payload?
|
|
111
|
+
*
|
|
112
|
+
* Borsh layout after the 8-byte discriminator: sender(32) ++ recipient(20) ++
|
|
113
|
+
* token(32) ++ amount(8) ++ payload_len(u32 LE) ++ payload. The gateway stamps
|
|
114
|
+
* the `PC20` selector (ASCII, 0x50433230) on the front of the payload for a
|
|
115
|
+
* PC20 burn, which is what distinguishes that leg from the native funds leg
|
|
116
|
+
* emitted by the same transaction.
|
|
117
|
+
*/
|
|
118
|
+
function isPC20EventBody(decoded) {
|
|
119
|
+
const PAYLOAD_LEN_OFFSET = 8 + 32 + 20 + 32 + 8;
|
|
120
|
+
if (decoded.length < PAYLOAD_LEN_OFFSET + 4)
|
|
121
|
+
return false;
|
|
122
|
+
const view = new DataView(decoded.buffer, decoded.byteOffset, decoded.byteLength);
|
|
123
|
+
const payloadLen = view.getUint32(PAYLOAD_LEN_OFFSET, true);
|
|
124
|
+
const start = PAYLOAD_LEN_OFFSET + 4;
|
|
125
|
+
if (payloadLen < 4 || decoded.length < start + 4)
|
|
126
|
+
return false;
|
|
127
|
+
return (decoded[start] === 0x50 &&
|
|
128
|
+
decoded[start + 1] === 0x43 &&
|
|
129
|
+
decoded[start + 2] === 0x32 &&
|
|
130
|
+
decoded[start + 3] === 0x30);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Locate the gateway event whose UTX the caller should track.
|
|
134
|
+
*
|
|
135
|
+
* Default: the second gateway event when there are two (a gas leg followed by
|
|
136
|
+
* the funds leg), else the last one.
|
|
137
|
+
*
|
|
138
|
+
* `preferPC20` inverts that for PC20 wrapper burns. Such a transaction emits
|
|
139
|
+
* the PC20 burn event first and then, when native value rides along, a plain
|
|
140
|
+
* funds event for the gas deposit — so the default would track the deposit
|
|
141
|
+
* leg. A failure crediting that deposit (the known chain-side fee-credit bug)
|
|
142
|
+
* would then be reported as the whole transfer failing, even though the tokens
|
|
143
|
+
* arrived via the first event. Selection is by payload selector rather than
|
|
144
|
+
* position, so it survives a change in emission order.
|
|
145
|
+
*/
|
|
146
|
+
function getSvmGatewayLogIndexFromTx(txResp, preferPC20 = false) {
|
|
147
|
+
const logs = (txResp?.meta?.logMessages || []);
|
|
112
148
|
if (!Array.isArray(logs) || logs.length === 0)
|
|
113
149
|
return 0;
|
|
114
150
|
const prefix = 'Program data: ';
|
|
115
151
|
let matchCount = 0;
|
|
116
152
|
let lastMatchIndex = -1;
|
|
153
|
+
let pc20MatchIndex = -1;
|
|
117
154
|
for (let i = 0; i < logs.length; i++) {
|
|
118
155
|
const log = logs[i] || '';
|
|
119
156
|
if (!log.startsWith(prefix))
|
|
@@ -123,7 +160,7 @@ function getSvmGatewayLogIndexFromTx(txResp) {
|
|
|
123
160
|
try {
|
|
124
161
|
decoded = new Uint8Array(Buffer.from(base64Data, 'base64'));
|
|
125
162
|
}
|
|
126
|
-
catch
|
|
163
|
+
catch {
|
|
127
164
|
continue;
|
|
128
165
|
}
|
|
129
166
|
if (!decoded || decoded.length < 8)
|
|
@@ -132,10 +169,17 @@ function getSvmGatewayLogIndexFromTx(txResp) {
|
|
|
132
169
|
if (discriminatorHex === SVM_GATEWAY_EVENT_DISCRIMINATOR) {
|
|
133
170
|
matchCount++;
|
|
134
171
|
lastMatchIndex = i;
|
|
135
|
-
if (
|
|
172
|
+
if (preferPC20 && pc20MatchIndex === -1 && isPC20EventBody(decoded)) {
|
|
173
|
+
pc20MatchIndex = i;
|
|
174
|
+
}
|
|
175
|
+
// Default selection returns eagerly on the second event; when looking
|
|
176
|
+
// for the PC20 leg the scan must continue so ordering cannot matter.
|
|
177
|
+
if (!preferPC20 && matchCount === 2)
|
|
136
178
|
return i;
|
|
137
179
|
}
|
|
138
180
|
}
|
|
181
|
+
if (preferPC20 && pc20MatchIndex !== -1)
|
|
182
|
+
return pc20MatchIndex;
|
|
139
183
|
if (lastMatchIndex !== -1)
|
|
140
184
|
return lastMatchIndex;
|
|
141
185
|
return 0;
|