@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.
@@ -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
- try {
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.metadata.address);
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.alloc(0),
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
- const recipientPk = userPk; // must be non-default per program checks
211
- console.log('recipientPk', recipientPk);
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: 'sendFundsNative',
236
- args: [recipientPk, bridgeAmount, revertSvm],
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: [recipientPk, mintPk, bridgeAmount, revertSvm],
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.ensureSepoliaGateway();
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 = (_c = execute.value) !== null && _c !== void 0 ? _c : BigInt(0);
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
- // Convert USD(8) -> native wei using the same pricing path as fee locking
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 oneEthWei = push_chain_1.PushChain.utils.helpers.parseUnits('1', 18);
392
- const nativeAmount = (depositUsd * oneEthWei) / nativeTokenUsdPrice;
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
- yield this.ensureErc20Allowance(evmClient, tokenAddr, gatewayAddress, bridgeAmount);
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
- const eip712Signature = yield this.signUniversalPayload(universalPayload,
416
- // execute.to
417
- ueaAddress);
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 signatureData = (0, viem_1.keccak256)(eip712SignatureHex);
422
- txHash = yield evmClient.writeContract({
424
+ const evmClientEvm = evmClient;
425
+ const gatewayAddressEvm = gatewayAddress;
426
+ txHash = yield evmClientEvm.writeContract({
423
427
  abi: abi_1.UNIVERSAL_GATEWAY_V0,
424
- address: gatewayAddress,
428
+ address: gatewayAddressEvm,
425
429
  functionName: 'sendTxWithFunds',
426
430
  args: [
427
431
  tokenAddr,
428
432
  bridgeAmount,
429
433
  universalPayload,
430
434
  revertCFG,
431
- signatureData,
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.metadata.address);
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
- const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
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.alloc(0),
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.alloc(32),
474
+ Buffer.from(svmSignature),
467
475
  ],
468
476
  signer: this.universalSigner,
469
477
  accounts: {
470
478
  config: configPda,
471
479
  vault: vaultPda,
472
- tokenWhitelist: whitelistPda,
473
- userTokenAccount: web3_js_1.PublicKey.default, // not used for native
474
- gatewayTokenAccount: web3_js_1.PublicKey.default, // not used for native
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.alloc(32),
517
+ Buffer.from(svmSignature),
509
518
  ],
510
519
  signer: this.universalSigner,
511
520
  accounts: {
512
521
  config: configPda,
513
522
  vault: vaultPda,
514
- tokenWhitelist: whitelistPda,
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
- yield this.waitForEvmConfirmationsWithCountdown(evmClient, txHash, 14, 300000);
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
- // If UEA is not deployed yet, deploy it now using the gateway tx hash
553
- yield this.sendUniversalTx(deployed, txHash);
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 evmTx = yield evmClient.getTransaction(txHash);
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, funds, nonce);
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.from(decoded));
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 payloads, ExecuteParams.to must be the sentinel '0x'
676
- // Note: This is distinct from each call's `to` inside MulticallCall[]
677
- if (execute.to !== '0x') {
678
- throw new Error('When using multicall, "to" must be a 0x-prefixed address');
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 sentinel '0x', encode as zero address.
706
- const payloadTo = Array.isArray(execute.data)
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
- ensureSepoliaGateway() {
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
- const { defaultRPC, lockerContract } = chain_1.CHAIN_INFO[chain];
1271
- const rpcUrls = this.rpcUrls[chain] || defaultRPC;
1272
- const evmClient = new evm_client_1.EvmClient({ rpcUrls });
1273
- const gatewayAddress = lockerContract;
1274
- if (!gatewayAddress) {
1275
- throw new Error('Universal Gateway address not configured');
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
- return { chain, evmClient, gatewayAddress };
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.universalTxVerification,
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
- this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_03, remaining);
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
  }