@pushchain/core 2.0.3 → 2.0.6
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/package.json +1 -1
- package/src/lib/constants/abi/pushsolanagateway.json +1129 -253
- package/src/lib/constants/abi/universalGatewayV0.evm.js +2 -2
- package/src/lib/constants/abi/universalGatewayV0.evm.js.map +1 -1
- package/src/lib/constants/tokens.js +6 -1
- package/src/lib/constants/tokens.js.map +1 -1
- package/src/lib/orchestrator/orchestrator.d.ts +2 -1
- package/src/lib/orchestrator/orchestrator.js +203 -119
- package/src/lib/orchestrator/orchestrator.js.map +1 -1
- package/src/lib/progress-hook/progress-hook.js +4 -11
- package/src/lib/progress-hook/progress-hook.js.map +1 -1
- package/src/lib/progress-hook/progress-hook.types.d.ts +0 -1
- package/src/lib/progress-hook/progress-hook.types.js +0 -1
- package/src/lib/progress-hook/progress-hook.types.js.map +1 -1
- package/src/lib/push-chain/push-chain.d.ts +3 -1
- package/src/lib/push-chain/push-chain.js +1 -1
- package/src/lib/push-chain/push-chain.js.map +1 -1
- package/src/lib/utils.d.ts +75 -29
- package/src/lib/utils.js +148 -71
- package/src/lib/utils.js.map +1 -1
- package/src/lib/vm-client/svm-client.js +83 -4
- package/src/lib/vm-client/svm-client.js.map +1 -1
|
@@ -79,8 +79,8 @@ class Orchestrator {
|
|
|
79
79
|
}
|
|
80
80
|
const chain = this.universalSigner.account.chain;
|
|
81
81
|
const { vm } = chain_1.CHAIN_INFO[chain];
|
|
82
|
-
if (!(chain === enums_1.CHAIN.ETHEREUM_SEPOLIA)) {
|
|
83
|
-
throw new Error('Funds bridging is only supported on Ethereum Sepolia for now');
|
|
82
|
+
if (!(chain === enums_1.CHAIN.ETHEREUM_SEPOLIA || chain === enums_1.CHAIN.SOLANA_DEVNET)) {
|
|
83
|
+
throw new Error('Funds bridging is only supported on Ethereum Sepolia and Solana Devnet for now');
|
|
84
84
|
}
|
|
85
85
|
// Progress: Origin chain detected
|
|
86
86
|
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_01, chain);
|
|
@@ -153,92 +153,50 @@ class Orchestrator {
|
|
|
153
153
|
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txHash, bridgeAmount, execute.funds.token.decimals, symbol);
|
|
154
154
|
yield this.waitForEvmConfirmationsWithCountdown(evmClient, txHash, 14, 210000);
|
|
155
155
|
// After origin confirmations, query Push Chain for UniversalTx status
|
|
156
|
-
|
|
157
|
-
const receipt = yield evmClient.publicClient.getTransactionReceipt({
|
|
158
|
-
hash: txHash,
|
|
159
|
-
});
|
|
160
|
-
const gatewayLogs = (receipt.logs || []).filter((l) => (l.address || '').toLowerCase() ===
|
|
161
|
-
gatewayAddress.toLowerCase());
|
|
162
|
-
const firstLog = (gatewayLogs[0] || ((_a = receipt.logs) === null || _a === void 0 ? void 0 : _a[0]));
|
|
163
|
-
const logIndexVal = (_b = firstLog === null || firstLog === void 0 ? void 0 : firstLog.logIndex) !== null && _b !== void 0 ? _b : 0;
|
|
164
|
-
const logIndexStr = typeof logIndexVal === 'bigint'
|
|
165
|
-
? logIndexVal.toString()
|
|
166
|
-
: String(logIndexVal);
|
|
167
|
-
const sourceChain = `${chain_1.VM_NAMESPACE[vm]}:${chain_1.CHAIN_INFO[this.universalSigner.account.chain].chainId}`;
|
|
168
|
-
// ID = sha256("${sourceChain}:${txHash}:${logIndex}") as hex string (no 0x)
|
|
169
|
-
const idInput = `${sourceChain}:${txHash}:${logIndexStr}`;
|
|
170
|
-
const idHex = (0, viem_1.sha256)((0, viem_1.stringToBytes)(idInput)).slice(2);
|
|
171
|
-
// Fetch UniversalTx via gRPC with a brief retry window
|
|
172
|
-
let universalTxObj;
|
|
173
|
-
for (let attempt = 0; attempt < 10; attempt++) {
|
|
174
|
-
try {
|
|
175
|
-
const universalTxResp = yield this.pushClient.getUniversalTxById(idHex);
|
|
176
|
-
universalTxObj = universalTxResp === null || universalTxResp === void 0 ? void 0 : universalTxResp.universalTx;
|
|
177
|
-
if (universalTxObj)
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
catch (_d) {
|
|
181
|
-
// ignore and retry
|
|
182
|
-
}
|
|
183
|
-
yield new Promise((r) => setTimeout(r, 1500));
|
|
184
|
-
}
|
|
185
|
-
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06, (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universalStatus) ||
|
|
186
|
-
(universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universal_status));
|
|
187
|
-
}
|
|
188
|
-
catch (_e) {
|
|
189
|
-
// Best-effort; do not fail flow if PC query is unavailable
|
|
190
|
-
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
|
|
191
|
-
}
|
|
156
|
+
yield this.queryUniversalTxStatusFromEvmGatewayTx(evmClient, gatewayAddress, txHash, 'sendFunds');
|
|
192
157
|
const tx = yield evmClient.getTransaction(txHash);
|
|
193
158
|
return yield this.transformToUniversalTxResponse(tx);
|
|
194
159
|
}
|
|
195
160
|
else {
|
|
196
161
|
// SVM path (Solana Devnet)
|
|
197
162
|
const svmClient = new svm_client_1.SvmClient({ rpcUrls });
|
|
198
|
-
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.
|
|
163
|
+
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
|
|
199
164
|
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
200
165
|
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
166
|
+
const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
201
167
|
const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
|
|
202
168
|
let txSignature;
|
|
203
169
|
// SVM-specific RevertSettings: bytes must be a Buffer
|
|
204
170
|
const revertSvm = {
|
|
205
171
|
fundRecipient: userPk,
|
|
206
|
-
revertMsg: Buffer.
|
|
172
|
+
revertMsg: Buffer.from([]),
|
|
207
173
|
};
|
|
174
|
+
// New gateway expects EVM recipient as [u8; 20]
|
|
175
|
+
const recipientEvm20 = Array.from(Buffer.from(execute.to.slice(2).padStart(40, '0'), 'hex').subarray(0, 20));
|
|
208
176
|
if (execute.funds.token.mechanism === 'native') {
|
|
209
177
|
// Native SOL funds-only
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
console.log('bridgeAmount', bridgeAmount);
|
|
213
|
-
console.log('userPk', userPk);
|
|
214
|
-
console.log('configPda', configPda);
|
|
215
|
-
console.log('vaultPda', vaultPda);
|
|
216
|
-
console.log('systemProgram', web3_js_1.SystemProgram.programId);
|
|
217
|
-
console.log('programId', programId.toBase58());
|
|
218
|
-
console.log('SVM_GATEWAY_IDL', abi_1.SVM_GATEWAY_IDL);
|
|
219
|
-
console.log('sendFundsNative', 'sendFundsNative');
|
|
220
|
-
console.log('args', [
|
|
221
|
-
recipientPk,
|
|
222
|
-
bridgeAmount,
|
|
223
|
-
{ fundRecipient: userPk, revertMsg: new Uint8Array([]) },
|
|
224
|
-
]);
|
|
225
|
-
console.log('signer', this.universalSigner);
|
|
226
|
-
console.log('accounts', {
|
|
227
|
-
config: configPda,
|
|
228
|
-
vault: vaultPda,
|
|
229
|
-
user: userPk,
|
|
230
|
-
systemProgram: web3_js_1.SystemProgram.programId,
|
|
231
|
-
});
|
|
178
|
+
// Compute a local whitelist PDA to avoid TS scope issues
|
|
179
|
+
const [whitelistPdaLocal] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
232
180
|
txSignature = yield svmClient.writeContract({
|
|
233
181
|
abi: abi_1.SVM_GATEWAY_IDL,
|
|
234
182
|
address: programId.toBase58(),
|
|
235
|
-
functionName: '
|
|
236
|
-
args: [
|
|
183
|
+
functionName: 'sendFunds', // -> unified sendFunds(recipient, bridge_token, bridge_amount, revert_cfg)
|
|
184
|
+
args: [
|
|
185
|
+
recipientEvm20,
|
|
186
|
+
web3_js_1.PublicKey.default,
|
|
187
|
+
bridgeAmount,
|
|
188
|
+
revertSvm,
|
|
189
|
+
],
|
|
237
190
|
signer: this.universalSigner,
|
|
238
191
|
accounts: {
|
|
239
192
|
config: configPda,
|
|
240
193
|
vault: vaultPda,
|
|
241
194
|
user: userPk,
|
|
195
|
+
tokenWhitelist: whitelistPdaLocal,
|
|
196
|
+
userTokenAccount: userPk,
|
|
197
|
+
gatewayTokenAccount: vaultPda,
|
|
198
|
+
bridgeToken: web3_js_1.PublicKey.default,
|
|
199
|
+
tokenProgram: new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
|
|
242
200
|
systemProgram: web3_js_1.SystemProgram.programId,
|
|
243
201
|
},
|
|
244
202
|
});
|
|
@@ -246,7 +204,6 @@ class Orchestrator {
|
|
|
246
204
|
else if (execute.funds.token.mechanism === 'approve') {
|
|
247
205
|
// SPL token funds-only (requires pre-existing ATAs)
|
|
248
206
|
const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
|
|
249
|
-
const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
250
207
|
// Associated Token Accounts
|
|
251
208
|
const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
252
209
|
const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
|
|
@@ -260,12 +217,11 @@ class Orchestrator {
|
|
|
260
217
|
TOKEN_PROGRAM_ID.toBuffer(),
|
|
261
218
|
mintPk.toBuffer(),
|
|
262
219
|
], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
263
|
-
const recipientPk = userPk; // must be non-default per program checks
|
|
264
220
|
txSignature = yield svmClient.writeContract({
|
|
265
221
|
abi: abi_1.SVM_GATEWAY_IDL,
|
|
266
222
|
address: programId.toBase58(),
|
|
267
223
|
functionName: 'sendFunds',
|
|
268
|
-
args: [
|
|
224
|
+
args: [recipientEvm20, mintPk, bridgeAmount, revertSvm],
|
|
269
225
|
signer: this.universalSigner,
|
|
270
226
|
accounts: {
|
|
271
227
|
config: configPda,
|
|
@@ -350,7 +306,8 @@ class Orchestrator {
|
|
|
350
306
|
// Bridge funds + execute payload. Support:
|
|
351
307
|
// - EVM (Sepolia): ERC-20 approve path + native gas via msg.value
|
|
352
308
|
// - SVM (Solana Devnet): SPL or native SOL with gas_amount
|
|
353
|
-
const { chain, evmClient, gatewayAddress } = this.
|
|
309
|
+
const { chain, evmClient, gatewayAddress } = this.getOriginGatewayContext();
|
|
310
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_01, chain);
|
|
354
311
|
// Default token to native ETH if none provided
|
|
355
312
|
if (!execute.funds.token) {
|
|
356
313
|
const available = tokens_1.MOVEABLE_TOKENS[chain] || [];
|
|
@@ -367,16 +324,19 @@ class Orchestrator {
|
|
|
367
324
|
const mechanism = execute.funds.token.mechanism;
|
|
368
325
|
const { deployed, nonce } = yield this.getUeaStatusAndNonce();
|
|
369
326
|
const { payload: universalPayload } = yield this.buildGatewayPayloadAndGas(execute, nonce);
|
|
327
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_01);
|
|
370
328
|
// Compute required gas funding on Push Chain and current UEA balance
|
|
371
329
|
const gasEstimate = execute.gasLimit || BigInt(1e7);
|
|
372
330
|
const gasPrice = yield this.pushClient.getGasPrice();
|
|
373
331
|
const requiredGasFee = gasEstimate * gasPrice;
|
|
374
|
-
const payloadValue = (
|
|
332
|
+
const payloadValue = (_a = execute.value) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
375
333
|
const requiredFunds = requiredGasFee + payloadValue;
|
|
376
334
|
const ueaAddress = this.computeUEAOffchain();
|
|
377
335
|
const [ueaBalance] = yield Promise.all([
|
|
378
336
|
this.pushClient.getBalance(ueaAddress),
|
|
379
337
|
]);
|
|
338
|
+
// UEA resolved (address, deployment status, balance, nonce)
|
|
339
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, ueaAddress, deployed);
|
|
380
340
|
// Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$10
|
|
381
341
|
const oneUsd = push_chain_1.PushChain.utils.helpers.parseUnits('1', 8);
|
|
382
342
|
const tenUsd = push_chain_1.PushChain.utils.helpers.parseUnits('10', 8);
|
|
@@ -386,10 +346,48 @@ class Orchestrator {
|
|
|
386
346
|
depositUsd = oneUsd;
|
|
387
347
|
if (depositUsd > tenUsd)
|
|
388
348
|
throw new Error('Deposit value exceeds max $10 worth of native token');
|
|
389
|
-
|
|
349
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_02, depositUsd);
|
|
350
|
+
// If SVM, clamp depositUsd to on-chain Config caps
|
|
351
|
+
if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
|
|
352
|
+
const svmClient = new svm_client_1.SvmClient({
|
|
353
|
+
rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
354
|
+
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
355
|
+
});
|
|
356
|
+
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
|
|
357
|
+
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
358
|
+
try {
|
|
359
|
+
const cfg = yield svmClient.readContract({
|
|
360
|
+
abi: abi_1.SVM_GATEWAY_IDL,
|
|
361
|
+
address: abi_1.SVM_GATEWAY_IDL.address,
|
|
362
|
+
functionName: 'config',
|
|
363
|
+
args: [configPda.toBase58()],
|
|
364
|
+
});
|
|
365
|
+
const minField = (_b = cfg.minCapUniversalTxUsd) !== null && _b !== void 0 ? _b : cfg.min_cap_universal_tx_usd;
|
|
366
|
+
const maxField = (_c = cfg.maxCapUniversalTxUsd) !== null && _c !== void 0 ? _c : cfg.max_cap_universal_tx_usd;
|
|
367
|
+
const minCapUsd = BigInt(minField.toString());
|
|
368
|
+
const maxCapUsd = BigInt(maxField.toString());
|
|
369
|
+
if (depositUsd < minCapUsd)
|
|
370
|
+
depositUsd = minCapUsd;
|
|
371
|
+
// Add 20% safety margin to avoid BelowMinCap due to price drift
|
|
372
|
+
const withMargin = (minCapUsd * BigInt(12)) / BigInt(10);
|
|
373
|
+
if (depositUsd < withMargin)
|
|
374
|
+
depositUsd = withMargin;
|
|
375
|
+
if (depositUsd > maxCapUsd)
|
|
376
|
+
depositUsd = maxCapUsd;
|
|
377
|
+
}
|
|
378
|
+
catch (_d) {
|
|
379
|
+
// best-effort; fallback to previous bounds if read fails
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Convert USD(8) -> native units using pricing path
|
|
390
383
|
const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(this.rpcUrls).getPrice(chain); // 8 decimals
|
|
391
|
-
const
|
|
392
|
-
const
|
|
384
|
+
const nativeDecimals = chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM ? 9 : 18;
|
|
385
|
+
const oneNativeUnit = push_chain_1.PushChain.utils.helpers.parseUnits('1', nativeDecimals);
|
|
386
|
+
// Ceil division to avoid rounding below min USD on-chain
|
|
387
|
+
let nativeAmount = (depositUsd * oneNativeUnit + (nativeTokenUsdPrice - BigInt(1))) /
|
|
388
|
+
nativeTokenUsdPrice;
|
|
389
|
+
// Add 1 unit safety to avoid BelowMinCap from rounding differences
|
|
390
|
+
nativeAmount = nativeAmount + BigInt(1);
|
|
393
391
|
const revertCFG = {
|
|
394
392
|
fundRecipient: this.universalSigner.account
|
|
395
393
|
.address,
|
|
@@ -404,31 +402,37 @@ class Orchestrator {
|
|
|
404
402
|
if (mechanism !== 'approve') {
|
|
405
403
|
throw new Error('Only ERC-20 tokens are supported for funds+payload on EVM; native and permit2 are not supported yet');
|
|
406
404
|
}
|
|
407
|
-
|
|
405
|
+
const evmClientEvm = evmClient;
|
|
406
|
+
const gatewayAddressEvm = gatewayAddress;
|
|
407
|
+
yield this.ensureErc20Allowance(evmClientEvm, tokenAddr, gatewayAddressEvm, bridgeAmount);
|
|
408
408
|
}
|
|
409
409
|
let txHash;
|
|
410
410
|
try {
|
|
411
411
|
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
412
412
|
const tokenAddr = execute.funds.token.address;
|
|
413
413
|
// Compute EIP-712 signature for the universal payload and hash to bytes32
|
|
414
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_01);
|
|
414
415
|
const ueaAddress = this.computeUEAOffchain();
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
416
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, ueaAddress, deployed);
|
|
417
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_01);
|
|
418
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_02);
|
|
419
|
+
const eip712Signature = yield this.signUniversalPayload(universalPayload, ueaAddress);
|
|
420
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
|
|
418
421
|
const eip712SignatureHex = typeof eip712Signature === 'string'
|
|
419
422
|
? eip712Signature
|
|
420
423
|
: (0, viem_1.bytesToHex)(eip712Signature);
|
|
421
|
-
const
|
|
422
|
-
|
|
424
|
+
const evmClientEvm = evmClient;
|
|
425
|
+
const gatewayAddressEvm = gatewayAddress;
|
|
426
|
+
txHash = yield evmClientEvm.writeContract({
|
|
423
427
|
abi: abi_1.UNIVERSAL_GATEWAY_V0,
|
|
424
|
-
address:
|
|
428
|
+
address: gatewayAddressEvm,
|
|
425
429
|
functionName: 'sendTxWithFunds',
|
|
426
430
|
args: [
|
|
427
431
|
tokenAddr,
|
|
428
432
|
bridgeAmount,
|
|
429
433
|
universalPayload,
|
|
430
434
|
revertCFG,
|
|
431
|
-
|
|
435
|
+
eip712SignatureHex,
|
|
432
436
|
],
|
|
433
437
|
signer: this.universalSigner,
|
|
434
438
|
value: nativeAmount,
|
|
@@ -440,19 +444,23 @@ class Orchestrator {
|
|
|
440
444
|
rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
441
445
|
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
442
446
|
});
|
|
443
|
-
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.
|
|
447
|
+
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
|
|
444
448
|
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
445
449
|
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
446
|
-
|
|
450
|
+
// whitelistPda already computed above
|
|
447
451
|
const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
|
|
448
452
|
const priceUpdatePk = new web3_js_1.PublicKey('7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE');
|
|
449
453
|
const isNative = mechanism === 'native' || execute.funds.token.symbol === 'SOL';
|
|
450
454
|
const revertSvm2 = {
|
|
451
455
|
fundRecipient: userPk,
|
|
452
|
-
revertMsg: Buffer.
|
|
456
|
+
revertMsg: Buffer.from([]),
|
|
453
457
|
};
|
|
458
|
+
// Compute signature for universal payload on SVM
|
|
459
|
+
const ueaAddressSvm = this.computeUEAOffchain();
|
|
460
|
+
const svmSignature = yield this.signUniversalPayload(universalPayload, ueaAddressSvm);
|
|
454
461
|
if (isNative) {
|
|
455
462
|
// Native SOL as bridge + gas
|
|
463
|
+
const [whitelistPdaLocal] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
456
464
|
txHash = yield svmClient.writeContract({
|
|
457
465
|
abi: abi_1.SVM_GATEWAY_IDL,
|
|
458
466
|
address: programId.toBase58(),
|
|
@@ -463,15 +471,15 @@ class Orchestrator {
|
|
|
463
471
|
universalPayload,
|
|
464
472
|
revertSvm2,
|
|
465
473
|
nativeAmount,
|
|
466
|
-
Buffer.
|
|
474
|
+
Buffer.from(svmSignature),
|
|
467
475
|
],
|
|
468
476
|
signer: this.universalSigner,
|
|
469
477
|
accounts: {
|
|
470
478
|
config: configPda,
|
|
471
479
|
vault: vaultPda,
|
|
472
|
-
tokenWhitelist:
|
|
473
|
-
userTokenAccount:
|
|
474
|
-
gatewayTokenAccount:
|
|
480
|
+
tokenWhitelist: whitelistPdaLocal,
|
|
481
|
+
userTokenAccount: userPk, // for native SOL, can be any valid account
|
|
482
|
+
gatewayTokenAccount: vaultPda, // for native SOL, can be any valid account
|
|
475
483
|
user: userPk,
|
|
476
484
|
priceUpdate: priceUpdatePk,
|
|
477
485
|
bridgeToken: web3_js_1.PublicKey.default,
|
|
@@ -495,6 +503,7 @@ class Orchestrator {
|
|
|
495
503
|
TOKEN_PROGRAM_ID.toBuffer(),
|
|
496
504
|
mintPk.toBuffer(),
|
|
497
505
|
], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
506
|
+
const [whitelistPdaLocal] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
498
507
|
txHash = yield svmClient.writeContract({
|
|
499
508
|
abi: abi_1.SVM_GATEWAY_IDL,
|
|
500
509
|
address: programId.toBase58(),
|
|
@@ -505,13 +514,13 @@ class Orchestrator {
|
|
|
505
514
|
universalPayload,
|
|
506
515
|
revertSvm2,
|
|
507
516
|
nativeAmount,
|
|
508
|
-
Buffer.
|
|
517
|
+
Buffer.from(svmSignature),
|
|
509
518
|
],
|
|
510
519
|
signer: this.universalSigner,
|
|
511
520
|
accounts: {
|
|
512
521
|
config: configPda,
|
|
513
522
|
vault: vaultPda,
|
|
514
|
-
tokenWhitelist:
|
|
523
|
+
tokenWhitelist: whitelistPdaLocal,
|
|
515
524
|
userTokenAccount: userAta,
|
|
516
525
|
gatewayTokenAccount: vaultAta,
|
|
517
526
|
user: userPk,
|
|
@@ -534,7 +543,8 @@ class Orchestrator {
|
|
|
534
543
|
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txHash, bridgeAmount, execute.funds.token.decimals, symbol);
|
|
535
544
|
// Awaiting confirmations
|
|
536
545
|
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
537
|
-
|
|
546
|
+
const evmClientEvm = evmClient;
|
|
547
|
+
yield this.waitForEvmConfirmationsWithCountdown(evmClientEvm, txHash, 14, 300000);
|
|
538
548
|
}
|
|
539
549
|
else {
|
|
540
550
|
const svmClient = new svm_client_1.SvmClient({
|
|
@@ -548,11 +558,26 @@ class Orchestrator {
|
|
|
548
558
|
});
|
|
549
559
|
}
|
|
550
560
|
// Funds Flow: Confirmed on origin
|
|
561
|
+
let feeLockTxHash = txHash;
|
|
562
|
+
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.SVM) {
|
|
563
|
+
if (feeLockTxHash && !feeLockTxHash.startsWith('0x')) {
|
|
564
|
+
// decode svm base58
|
|
565
|
+
const decoded = anchor_1.utils.bytes.bs58.decode(feeLockTxHash);
|
|
566
|
+
feeLockTxHash = (0, viem_1.bytesToHex)(new Uint8Array(decoded));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
yield this.sendUniversalTx(deployed, feeLockTxHash);
|
|
551
570
|
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
|
|
552
|
-
//
|
|
553
|
-
|
|
571
|
+
// After sending Cosmos tx to Push Chain, query UniversalTx status (Sepolia only)
|
|
572
|
+
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
573
|
+
const evmClientEvm = evmClient;
|
|
574
|
+
const gatewayAddressEvm = gatewayAddress;
|
|
575
|
+
yield this.queryUniversalTxStatusFromEvmGatewayTx(evmClientEvm, gatewayAddressEvm, txHash, 'sendTxWithFunds');
|
|
576
|
+
}
|
|
554
577
|
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
555
|
-
const
|
|
578
|
+
const evmClientEvm = evmClient;
|
|
579
|
+
const evmTx = yield evmClientEvm.getTransaction(txHash);
|
|
580
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_07, bridgeAmount, execute.funds.token.decimals, symbol);
|
|
556
581
|
return yield this.transformToUniversalTxResponse(evmTx);
|
|
557
582
|
}
|
|
558
583
|
else {
|
|
@@ -652,7 +677,7 @@ class Orchestrator {
|
|
|
652
677
|
]);
|
|
653
678
|
const isUEADeployed = code !== undefined;
|
|
654
679
|
const nonce = isUEADeployed ? yield this.getUEANonce(UEA) : BigInt(0);
|
|
655
|
-
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, UEA, isUEADeployed
|
|
680
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, UEA, isUEADeployed);
|
|
656
681
|
/**
|
|
657
682
|
* Compute Universal Payload Hash
|
|
658
683
|
*/
|
|
@@ -660,7 +685,7 @@ class Orchestrator {
|
|
|
660
685
|
if (feeLockTxHash && !feeLockTxHash.startsWith('0x')) {
|
|
661
686
|
// decode svm base58
|
|
662
687
|
const decoded = anchor_1.utils.bytes.bs58.decode(feeLockTxHash);
|
|
663
|
-
feeLockTxHash = (0, viem_1.bytesToHex)(Uint8Array
|
|
688
|
+
feeLockTxHash = (0, viem_1.bytesToHex)(new Uint8Array(decoded));
|
|
664
689
|
}
|
|
665
690
|
// Fee locking is required if UEA is not deployed OR insufficient funds
|
|
666
691
|
const feeLockingRequired = (!isUEADeployed || funds < requiredFunds) && !feeLockTxHash;
|
|
@@ -672,10 +697,12 @@ class Orchestrator {
|
|
|
672
697
|
if (!allowedChains.includes(this.universalSigner.account.chain)) {
|
|
673
698
|
throw new Error('Multicall is only enabled for Ethereum Sepolia and Solana Devnet');
|
|
674
699
|
}
|
|
675
|
-
// For multicall
|
|
676
|
-
//
|
|
677
|
-
|
|
678
|
-
|
|
700
|
+
// For multicall, `to` must be the executor account (UEA) of the sender
|
|
701
|
+
// i.e., PushChain.universal.account
|
|
702
|
+
const expectedUea = this.computeUEAOffchain();
|
|
703
|
+
const toAddr = (0, viem_1.getAddress)(execute.to);
|
|
704
|
+
if (toAddr !== (0, viem_1.getAddress)(expectedUea)) {
|
|
705
|
+
throw new Error('Multicall requires `to` to be the executor account (UEA) of the sender.');
|
|
679
706
|
}
|
|
680
707
|
// Normalize and validate calls
|
|
681
708
|
const normalizedCalls = execute.data.map((c) => ({
|
|
@@ -702,10 +729,8 @@ class Orchestrator {
|
|
|
702
729
|
else {
|
|
703
730
|
payloadData = (execute.data || '0x');
|
|
704
731
|
}
|
|
705
|
-
// Determine payload `to` value. For multicall
|
|
706
|
-
const payloadTo =
|
|
707
|
-
? '0x0000000000000000000000000000000000000000'
|
|
708
|
-
: execute.to;
|
|
732
|
+
// Determine payload `to` value. For multicall, `to` must be UEA, pass-through as-is.
|
|
733
|
+
const payloadTo = execute.to;
|
|
709
734
|
const universalPayload = JSON.parse(JSON.stringify({
|
|
710
735
|
to: payloadTo,
|
|
711
736
|
value: execute.value,
|
|
@@ -847,7 +872,7 @@ class Orchestrator {
|
|
|
847
872
|
systemProgram: web3_js_1.SystemProgram.programId,
|
|
848
873
|
},
|
|
849
874
|
});
|
|
850
|
-
return anchor_1.utils.bytes.bs58.decode(txHash);
|
|
875
|
+
return new Uint8Array(anchor_1.utils.bytes.bs58.decode(txHash));
|
|
851
876
|
}
|
|
852
877
|
default:
|
|
853
878
|
throw new Error(`Unsupported VM type: ${vm}`);
|
|
@@ -914,7 +939,7 @@ class Orchestrator {
|
|
|
914
939
|
owner: vm === enums_1.VM.EVM
|
|
915
940
|
? address
|
|
916
941
|
: vm === enums_1.VM.SVM
|
|
917
|
-
? (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address))
|
|
942
|
+
? (0, viem_1.bytesToHex)(new Uint8Array(anchor_1.utils.bytes.bs58.decode(address)))
|
|
918
943
|
: address,
|
|
919
944
|
};
|
|
920
945
|
const { cosmosAddress: signer } = this.pushClient.getSignerAddress();
|
|
@@ -1080,7 +1105,7 @@ class Orchestrator {
|
|
|
1080
1105
|
owner: vm === enums_1.VM.EVM
|
|
1081
1106
|
? address
|
|
1082
1107
|
: vm === enums_1.VM.SVM
|
|
1083
|
-
? (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address))
|
|
1108
|
+
? (0, viem_1.bytesToHex)(new Uint8Array(anchor_1.utils.bytes.bs58.decode(address)))
|
|
1084
1109
|
: address,
|
|
1085
1110
|
},
|
|
1086
1111
|
],
|
|
@@ -1104,7 +1129,7 @@ class Orchestrator {
|
|
|
1104
1129
|
ownerKey = address;
|
|
1105
1130
|
}
|
|
1106
1131
|
else if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
|
|
1107
|
-
ownerKey = (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address));
|
|
1132
|
+
ownerKey = (0, viem_1.bytesToHex)(new Uint8Array(anchor_1.utils.bytes.bs58.decode(address)));
|
|
1108
1133
|
}
|
|
1109
1134
|
else {
|
|
1110
1135
|
throw new Error(`Unsupported VM type: ${chain_1.CHAIN_INFO[chain].vm}`);
|
|
@@ -1262,19 +1287,24 @@ class Orchestrator {
|
|
|
1262
1287
|
/**
|
|
1263
1288
|
* Ensures we're on Sepolia, returns EVM client and gateway address.
|
|
1264
1289
|
*/
|
|
1265
|
-
|
|
1290
|
+
getOriginGatewayContext() {
|
|
1266
1291
|
const chain = this.universalSigner.account.chain;
|
|
1267
|
-
if (chain !== enums_1.CHAIN.ETHEREUM_SEPOLIA) {
|
|
1268
|
-
throw new Error('Funds + payload bridging is only supported on Ethereum Sepolia for now');
|
|
1292
|
+
if (chain !== enums_1.CHAIN.ETHEREUM_SEPOLIA && chain !== enums_1.CHAIN.SOLANA_DEVNET) {
|
|
1293
|
+
throw new Error('Funds + payload bridging is only supported on Ethereum Sepolia and Solana Devnet for now');
|
|
1269
1294
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1295
|
+
// For EVM (Sepolia), return client and gateway address. For SVM (Solana Devnet), only chain is needed here.
|
|
1296
|
+
if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.EVM) {
|
|
1297
|
+
const { defaultRPC, lockerContract } = chain_1.CHAIN_INFO[chain];
|
|
1298
|
+
const rpcUrls = this.rpcUrls[chain] || defaultRPC;
|
|
1299
|
+
const evmClient = new evm_client_1.EvmClient({ rpcUrls });
|
|
1300
|
+
const gatewayAddress = lockerContract;
|
|
1301
|
+
if (!gatewayAddress) {
|
|
1302
|
+
throw new Error('Universal Gateway address not configured');
|
|
1303
|
+
}
|
|
1304
|
+
return { chain, evmClient, gatewayAddress };
|
|
1276
1305
|
}
|
|
1277
|
-
|
|
1306
|
+
// SVM path (Solana Devnet) does not require evmClient/gatewayAddress
|
|
1307
|
+
return { chain };
|
|
1278
1308
|
}
|
|
1279
1309
|
/**
|
|
1280
1310
|
* Computes UEA and fetches its nonce if deployed; returns 0 otherwise.
|
|
@@ -1320,7 +1350,7 @@ class Orchestrator {
|
|
|
1320
1350
|
maxPriorityFeePerGas: execute.maxPriorityFeePerGas || BigInt(0),
|
|
1321
1351
|
nonce,
|
|
1322
1352
|
deadline: execute.deadline || BigInt(9999999999),
|
|
1323
|
-
vType: tx_1.VerificationType.
|
|
1353
|
+
vType: tx_1.VerificationType.signedVerification,
|
|
1324
1354
|
};
|
|
1325
1355
|
return { payload: universalPayload, gasAmount };
|
|
1326
1356
|
});
|
|
@@ -1545,6 +1575,59 @@ class Orchestrator {
|
|
|
1545
1575
|
// invoke the user-provided callback
|
|
1546
1576
|
this.progressHook(hookPayload);
|
|
1547
1577
|
}
|
|
1578
|
+
// Query Push Chain for UniversalTx status given an EVM gateway tx
|
|
1579
|
+
queryUniversalTxStatusFromEvmGatewayTx(evmClient, gatewayAddress, txHash, fromBranch) {
|
|
1580
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1581
|
+
var _a, _b;
|
|
1582
|
+
try {
|
|
1583
|
+
const receipt = yield evmClient.publicClient.getTransactionReceipt({
|
|
1584
|
+
hash: txHash,
|
|
1585
|
+
});
|
|
1586
|
+
const gatewayLogs = (receipt.logs || []).filter((l) => (l.address || '').toLowerCase() === gatewayAddress.toLowerCase());
|
|
1587
|
+
const logIndexToUse = fromBranch === 'sendTxWithFunds' ? 1 : 0;
|
|
1588
|
+
const firstLog = (gatewayLogs[logIndexToUse] ||
|
|
1589
|
+
((_a = receipt.logs) === null || _a === void 0 ? void 0 : _a[logIndexToUse]));
|
|
1590
|
+
const logIndexVal = (_b = firstLog === null || firstLog === void 0 ? void 0 : firstLog.logIndex) !== null && _b !== void 0 ? _b : 0;
|
|
1591
|
+
const logIndexStr = typeof logIndexVal === 'bigint'
|
|
1592
|
+
? logIndexVal.toString()
|
|
1593
|
+
: String(logIndexVal);
|
|
1594
|
+
const chain = this.universalSigner.account.chain;
|
|
1595
|
+
const { vm } = chain_1.CHAIN_INFO[chain];
|
|
1596
|
+
const sourceChain = `${chain_1.VM_NAMESPACE[vm]}:${chain_1.CHAIN_INFO[chain].chainId}`;
|
|
1597
|
+
// ID = sha256("${sourceChain}:${txHash}:${logIndex}") as hex string (no 0x)
|
|
1598
|
+
const idInput = `${sourceChain}:${txHash}:${logIndexStr}`;
|
|
1599
|
+
const idHex = (0, viem_1.sha256)((0, viem_1.stringToBytes)(idInput)).slice(2);
|
|
1600
|
+
// Fetch UniversalTx via gRPC with a brief retry window
|
|
1601
|
+
let universalTxObj;
|
|
1602
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
1603
|
+
try {
|
|
1604
|
+
const universalTxResp = yield this.pushClient.getUniversalTxById(idHex);
|
|
1605
|
+
universalTxObj = universalTxResp === null || universalTxResp === void 0 ? void 0 : universalTxResp.universalTx;
|
|
1606
|
+
if (universalTxObj)
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
catch (error) {
|
|
1610
|
+
// ignore and retry
|
|
1611
|
+
console.log(error);
|
|
1612
|
+
}
|
|
1613
|
+
yield new Promise((r) => setTimeout(r, 1500));
|
|
1614
|
+
}
|
|
1615
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06, (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universalStatus) || (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universal_status));
|
|
1616
|
+
this.printLog(`UniversalTx fetched via gRPC: ${JSON.stringify({
|
|
1617
|
+
gatewayTx: txHash,
|
|
1618
|
+
id: idHex,
|
|
1619
|
+
status: (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universalStatus) ||
|
|
1620
|
+
(universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universal_status),
|
|
1621
|
+
}, this.bigintReplacer, 2)}`);
|
|
1622
|
+
return universalTxObj;
|
|
1623
|
+
}
|
|
1624
|
+
catch (_c) {
|
|
1625
|
+
// Best-effort; do not fail flow if PC query is unavailable
|
|
1626
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
|
|
1627
|
+
return undefined;
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1548
1631
|
// Emit countdown updates while waiting for EVM confirmations
|
|
1549
1632
|
waitForEvmConfirmationsWithCountdown(evmClient, txHash, confirmations, timeoutMs) {
|
|
1550
1633
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
@@ -1563,7 +1646,8 @@ class Orchestrator {
|
|
|
1563
1646
|
if (currentBlock >= targetBlock)
|
|
1564
1647
|
return;
|
|
1565
1648
|
const remaining = Number(targetBlock - currentBlock);
|
|
1566
|
-
|
|
1649
|
+
const completed = Math.max(1, confirmations - remaining + 1);
|
|
1650
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_04, completed, confirmations);
|
|
1567
1651
|
if (Date.now() - start > timeoutMs) {
|
|
1568
1652
|
throw new Error(`Timeout: transaction ${txHash} not confirmed with ${confirmations} confirmations within ${timeoutMs} ms`);
|
|
1569
1653
|
}
|