@pushchain/core 5.1.18 → 6.0.0

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/lib/constants/abi/cea.evm.d.ts +52 -0
  3. package/src/lib/constants/abi/cea.evm.js +66 -1
  4. package/src/lib/constants/abi/prc20.evm.d.ts +117 -11
  5. package/src/lib/constants/abi/prc20.evm.js +38 -11
  6. package/src/lib/constants/abi/uea.evm.js +41 -0
  7. package/src/lib/constants/abi/universalGatewayPC.evm.d.ts +117 -1
  8. package/src/lib/constants/abi/universalGatewayPC.evm.js +40 -1
  9. package/src/lib/constants/abi/universalGatewayV0.json +1 -1
  10. package/src/lib/constants/chain.js +14 -14
  11. package/src/lib/constants/tokens.js +1 -0
  12. package/src/lib/formatters.d.ts +6 -0
  13. package/src/lib/formatters.js +14 -0
  14. package/src/lib/index.d.ts +2 -0
  15. package/src/lib/index.js +7 -1
  16. package/src/lib/orchestrator/internals/cascade.js +139 -81
  17. package/src/lib/orchestrator/internals/errors.js +1 -1
  18. package/src/lib/orchestrator/internals/execute-funds-only.js +3 -2
  19. package/src/lib/orchestrator/internals/execute-funds-payload.js +2 -1
  20. package/src/lib/orchestrator/internals/execute-standard.js +4 -4
  21. package/src/lib/orchestrator/internals/gas-calculator.d.ts +1 -0
  22. package/src/lib/orchestrator/internals/gas-calculator.js +13 -11
  23. package/src/lib/orchestrator/internals/helpers.js +1 -0
  24. package/src/lib/orchestrator/internals/inbound-tracker.js +4 -2
  25. package/src/lib/orchestrator/internals/outbound-sync.js +73 -60
  26. package/src/lib/orchestrator/internals/pc-usd-oracle.d.ts +1 -1
  27. package/src/lib/orchestrator/internals/pc-usd-oracle.js +4 -3
  28. package/src/lib/orchestrator/internals/push-chain-tx.js +5 -1
  29. package/src/lib/orchestrator/internals/route-handlers.js +155 -185
  30. package/src/lib/orchestrator/internals/signing.js +7 -2
  31. package/src/lib/orchestrator/internals/svm-helpers.js +3 -3
  32. package/src/lib/orchestrator/internals/svm-rent.d.ts +42 -9
  33. package/src/lib/orchestrator/internals/svm-rent.js +148 -14
  34. package/src/lib/orchestrator/internals/tx-transformer.js +5 -2
  35. package/src/lib/orchestrator/max-pc-for-gas.d.ts +60 -0
  36. package/src/lib/orchestrator/max-pc-for-gas.js +66 -0
  37. package/src/lib/orchestrator/orchestrator.d.ts +1 -0
  38. package/src/lib/orchestrator/orchestrator.types.d.ts +31 -9
  39. package/src/lib/orchestrator/payload-builders.d.ts +7 -6
  40. package/src/lib/orchestrator/payload-builders.js +18 -6
  41. package/src/lib/progress-hook/progress-hook.js +26 -18
  42. package/src/lib/universal-tx-detector/events.d.ts +3 -1
  43. package/src/lib/universal-tx-detector/events.js +3 -1
  44. package/src/lib/universal-tx-detector/svm-events.d.ts +1 -1
  45. package/src/lib/universal-tx-detector/svm-events.js +1 -1
@@ -112,6 +112,37 @@ function resolveR2Prc20TokenSvm(params, targetChain, pushNetwork, hasSvmExecute)
112
112
  }
113
113
  return { prc20Token, burnAmount };
114
114
  }
115
+ function resolveR3SvmDrain(params, sourceChain, pushNetwork) {
116
+ var _a;
117
+ let drainAmount = BigInt(0);
118
+ let tokenMintHex;
119
+ let prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, pushNetwork);
120
+ if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
121
+ drainAmount = params.funds.amount;
122
+ const token = params.funds.token;
123
+ if ((token === null || token === void 0 ? void 0 : token.mechanism) === 'approve') {
124
+ const mintPk = new web3_js_1.PublicKey(token.address);
125
+ tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
126
+ prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
127
+ }
128
+ }
129
+ else if (params.value && params.value > BigInt(0)) {
130
+ drainAmount = params.value;
131
+ }
132
+ return { prc20Token, drainAmount, tokenMintHex };
133
+ }
134
+ function buildR3SvmExtraPayload(ctx, params, ueaAddress, sourceChain, inboundNonce) {
135
+ if (!params.data) {
136
+ return undefined;
137
+ }
138
+ const executeParams = (0, helpers_1.toExecuteParams)(params);
139
+ const multicallCtx = Object.assign(Object.assign({}, ctx), { universalSigner: Object.assign(Object.assign({}, ctx.universalSigner), { account: Object.assign(Object.assign({}, ctx.universalSigner.account), { chain: sourceChain }) }) });
140
+ const multicallPayload = (0, payload_builder_1.buildMulticallPayloadData)(multicallCtx, executeParams.to, (0, payload_builders_1.buildExecuteMulticall)({ execute: executeParams, ueaAddress }));
141
+ const inboundPayload = (0, payload_builders_1.buildInboundUniversalPayload)(multicallPayload, {
142
+ nonce: inboundNonce !== null && inboundNonce !== void 0 ? inboundNonce : BigInt(0),
143
+ });
144
+ return (0, viem_1.hexToBytes)(inboundPayload);
145
+ }
115
146
  /**
116
147
  * Build the CEA-side multicall payload for a Route 2 EVM outbound. Needs the
117
148
  * resolved `ceaAddress` because the native-value self-call branch skips the
@@ -281,7 +312,7 @@ function executeMultiChain(ctx, params, executeFn) {
281
312
  */
282
313
  function executeUoaToCea(ctx, params, executeFn) {
283
314
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
284
- var _a, _b, _c;
315
+ var _a, _b, _c, _d;
285
316
  const target = params.to;
286
317
  const targetChain = target.chain;
287
318
  const targetAddress = target.address;
@@ -337,6 +368,7 @@ function executeUoaToCea(ctx, params, executeFn) {
337
368
  // --- 202: Gas estimation (spec-ordered before 203) ---
338
369
  let gasFee = BigInt(0);
339
370
  let protocolFee = BigInt(0);
371
+ let outboundGasPrice = BigInt(0);
340
372
  let gasToken = selectors_1.ZERO_ADDRESS;
341
373
  let universalCoreAddress;
342
374
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
@@ -345,6 +377,7 @@ function executeUoaToCea(ctx, params, executeFn) {
345
377
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, gasLimitForQuery, targetChain);
346
378
  gasFee = result.gasFee;
347
379
  protocolFee = result.protocolFee;
380
+ outboundGasPrice = result.gasPrice;
348
381
  gasToken = result.gasToken;
349
382
  universalCoreAddress = result.universalCoreAddress;
350
383
  (0, context_1.printLog)(ctx, `executeUoaToCea — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}`);
@@ -376,19 +409,20 @@ function executeUoaToCea(ctx, params, executeFn) {
376
409
  (0, context_1.printLog)(ctx, `executeUoaToCea — MIGRATION: using raw MIGRATION_SELECTOR payload (${ceaPayload}), native PRC-20 ${prc20Token}`);
377
410
  }
378
411
  // Build outbound request struct for the gateway
379
- // NOTE: `target` is a LEGACY/DUMMY parameter for contract compatibility.
380
- // The deployed UniversalGatewayPC still expects this field, but the relay does NOT use it
381
- // to determine the actual destination. The relay determines destination from the PRC-20 token's
382
- // SOURCE_CHAIN_NAMESPACE. We pass the CEA address as a non-zero placeholder.
383
- // This field will be removed in future contract upgrades.
384
- const targetBytes = ceaAddress; // Dummy value - any non-zero address works
385
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, gasLimitForQuery, ceaPayload, ueaAddress // revert recipient is the UEA
386
- );
412
+ // Empty CEA payloads intentionally use zero recipient to park funds in the
413
+ // CEA under the post-audit single-call guard.
414
+ const targetBytes = ceaPayload === '0x'
415
+ ? selectors_1.ZERO_ADDRESS
416
+ : ceaAddress;
417
+ (0, payload_builders_1.assertCeaFundsParkingInvariant)(targetBytes, ceaPayload);
418
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, gasLimitForQuery, ceaPayload, ueaAddress, // revert recipient is the UEA
419
+ (_d = params.maxPCForGas) !== null && _d !== void 0 ? _d : BigInt(0), outboundGasPrice);
387
420
  (0, context_1.printLog)(ctx, `executeUoaToCea — outbound request: ${JSON.stringify({
388
421
  target: outboundReq.target,
389
422
  token: outboundReq.token,
390
423
  amount: outboundReq.amount.toString(),
391
424
  gasLimit: outboundReq.gasLimit.toString(),
425
+ maxPCForGas: outboundReq.maxPCForGas.toString(),
392
426
  payloadLength: outboundReq.payload.length,
393
427
  revertRecipient: outboundReq.revertRecipient,
394
428
  }, null, 2)}`);
@@ -561,44 +595,49 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
561
595
  // --- 202: Gas estimation (spec-ordered before 203) ---
562
596
  let gasFee = BigInt(0);
563
597
  let protocolFeeSvm = BigInt(0);
598
+ let outboundGasPriceSvm = BigInt(0);
564
599
  let gasToken = selectors_1.ZERO_ADDRESS;
565
600
  let universalCoreAddress;
566
601
  // Effective gas limit seeded from the user param. When the user omits
567
- // gasLimit, it'll be derived from the gasFee/gasPrice response so the
568
- // on-chain record carries a non-zero compute budget the relay can use.
602
+ // gasLimit, it'll be derived from the gas quote so the on-chain record
603
+ // carries a non-zero compute budget the relay can use.
569
604
  let effectiveGasLimit = (_d = params.gasLimit) !== null && _d !== void 0 ? _d : BigInt(0);
570
- // Conditional CEA-ATA rent bump: when the SPL outbound's destination ATA
571
- // does not yet exist on Solana, the gateway needs an extra ~2.04M lamports
572
- // for ATA-create rent. The on-chain quote does NOT include this, so bump
573
- // effectiveGasLimit BEFORE queryOutboundGasFee so the returned gasFee
574
- // already covers the rent.
575
- const splMintBase58 = burnAmount > BigInt(0)
576
- ? (_f = (_e = params.funds) === null || _e === void 0 ? void 0 : _e.token) === null || _f === void 0 ? void 0 : _f.address
605
+ // SVM finalize budget: the Solana gateway requires gas_fee to cover the
606
+ // relayer signature fee, ExecutedSubTx rent, a compute buffer, and
607
+ // conditionally CEA ATA rent for SPL outbounds. The on-chain quote can be
608
+ // below that floor, so we compare the quoted fee in lamports and re-query
609
+ // with a bumped gasLimit when required.
610
+ const svmFundsToken = burnAmount > BigInt(0)
611
+ ? (_e = params.funds) === null || _e === void 0 ? void 0 : _e.token
577
612
  : undefined;
578
- effectiveGasLimit = yield (0, svm_rent_1.maybeBumpForCeaAtaRent)({
579
- ctx,
580
- ueaAddress,
581
- targetChain,
582
- splMintBase58,
583
- burnAmount,
584
- effectiveGasLimit,
585
- });
613
+ const splMintBase58 = (svmFundsToken === null || svmFundsToken === void 0 ? void 0 : svmFundsToken.mechanism) === 'approve' ? svmFundsToken.address : undefined;
586
614
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
587
615
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_202_01, targetChain);
588
616
  try {
589
- const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, effectiveGasLimit, targetChain);
617
+ let result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, effectiveGasLimit, targetChain);
618
+ result = yield (0, svm_rent_1.ensureSvmFinalizeGasBudgetQuote)({
619
+ ctx,
620
+ ueaAddress,
621
+ targetChain,
622
+ prc20Token,
623
+ quote: result,
624
+ splMintBase58,
625
+ burnAmount,
626
+ pathTag: 'executeUoaToCeaSvm',
627
+ });
590
628
  gasFee = result.gasFee;
591
629
  protocolFeeSvm = result.protocolFee;
630
+ outboundGasPriceSvm = result.gasPrice;
592
631
  gasToken = result.gasToken;
593
632
  universalCoreAddress = result.universalCoreAddress;
594
- // When user omits gasLimit (sent as 0), the contract computes fees using its internal
595
- // baseGasLimitByChainNamespace. But the relay reads the stored gasLimit=0 from the
596
- // on-chain outbound record and uses it as the Solana compute budget — 0 CU means the
597
- // relay cannot execute the tx. Derive the effective limit from gasFee/gasPrice so
598
- // the stored record has a non-zero compute budget the relay can use.
599
- if (!params.gasLimit && result.gasPrice > BigInt(0)) {
600
- effectiveGasLimit = result.gasFee / result.gasPrice;
601
- (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — derived effectiveGasLimit: ${effectiveGasLimit} (gasFee=${result.gasFee} / gasPrice=${result.gasPrice})`);
633
+ // When user omits gasLimit (sent as 0), the contract computes fees using
634
+ // its internal baseGasLimitByChainNamespace. But the relay reads the
635
+ // stored gasLimit from the outbound record. Store the resolved value from
636
+ // UniversalCore so the relayer has a non-zero SVM budget.
637
+ effectiveGasLimit = result.gasLimitUsed;
638
+ if (!params.gasLimit) {
639
+ (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — resolved effectiveGasLimit: ${effectiveGasLimit} ` +
640
+ `(gasFee=${result.gasFee}, gasPrice=${result.gasPrice})`);
602
641
  }
603
642
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}`);
604
643
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_202_02, targetChain, protocolFeeSvm, gasFee);
@@ -614,13 +653,14 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
614
653
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_02, ueaAddress, targetAddress, targetChain, true);
615
654
  // --- Build outbound request ---
616
655
  // targetBytes: for execute = program id (resolver output); for withdraw = to.address
617
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress // revert recipient is the UEA
618
- );
656
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, // revert recipient is the UEA
657
+ (_f = params.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0), outboundGasPriceSvm);
619
658
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
620
659
  target: outboundReq.target,
621
660
  token: outboundReq.token,
622
661
  amount: outboundReq.amount.toString(),
623
662
  gasLimit: outboundReq.gasLimit.toString(),
663
+ maxPCForGas: outboundReq.maxPCForGas.toString(),
624
664
  payloadLength: (outboundReq.payload.length - 2) / 2,
625
665
  revertRecipient: outboundReq.revertRecipient,
626
666
  }, null, 2)}`);
@@ -764,7 +804,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
764
804
  */
765
805
  function executeCeaToPush(ctx, params, executeFn) {
766
806
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
767
- var _a, _b, _c, _d, _e, _f, _g, _h;
807
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
768
808
  // 1. Validate and extract source chain
769
809
  if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
770
810
  throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
@@ -801,11 +841,6 @@ function executeCeaToPush(ctx, params, executeFn) {
801
841
  (0, context_1.printLog)(ctx, `executeCeaToPush — sourceChain: ${sourceChain}, CEA: ${ceaAddress}, deployed: ${ceaDeployed}`);
802
842
  // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
803
843
  // if CEA doesn't exist yet. No SDK-side blocking needed.
804
- // 5. Get UniversalGateway address on source chain
805
- const gatewayAddress = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
806
- if (!gatewayAddress) {
807
- throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
808
- }
809
844
  // 6. Build multicall for CEA to execute on source chain (self-calls via sendUniversalTxToUEA)
810
845
  const ceaMulticalls = [];
811
846
  // Determine token and amount for sendUniversalTxToUEA
@@ -821,7 +856,7 @@ function executeCeaToPush(ctx, params, executeFn) {
821
856
  amount = params.funds.amount;
822
857
  }
823
858
  else {
824
- // ERC20 token - need approval for gateway before sendUniversalTxToUEA
859
+ // ERC20 token. CEA handles gateway approval internally around sendUniversalTxToUEA.
825
860
  tokenAddress = token.address;
826
861
  amount = params.funds.amount;
827
862
  }
@@ -838,33 +873,7 @@ function executeCeaToPush(ctx, params, executeFn) {
838
873
  // query and relay execution, causing sendUniversalTxToUEA to revert with
839
874
  // InsufficientBalance. Pre-existing CEA funds remain parked and can be swept separately.
840
875
  const bridgeAmount = amount;
841
- // Note: CEA contract may reject amount=0 in sendUniversalTxToUEA.
842
- // Keeping bridgeAmount as-is (0) for payload-only to test precompile behavior.
843
- // For ERC20 tokens, add approve call for the bridge amount
844
- // (CEA approves gateway to spend the Vault-deposited amount)
845
- // Reset to 0 first for USDT-style tokens that revert on non-zero to non-zero approve.
846
- if (tokenAddress !== selectors_1.ZERO_ADDRESS && bridgeAmount > BigInt(0)) {
847
- const approveZeroData = (0, viem_1.encodeFunctionData)({
848
- abi: erc20_evm_1.ERC20_EVM,
849
- functionName: 'approve',
850
- args: [gatewayAddress, BigInt(0)],
851
- });
852
- ceaMulticalls.push({
853
- to: tokenAddress,
854
- value: BigInt(0),
855
- data: approveZeroData,
856
- });
857
- const approveData = (0, viem_1.encodeFunctionData)({
858
- abi: erc20_evm_1.ERC20_EVM,
859
- functionName: 'approve',
860
- args: [gatewayAddress, bridgeAmount],
861
- });
862
- ceaMulticalls.push({
863
- to: tokenAddress,
864
- value: BigInt(0),
865
- data: approveData,
866
- });
867
- }
876
+ // The post-audit CEA allows amount=0 for payload-only sendUniversalTxToUEA.
868
877
  // Pre-fetch UEA nonce — needed for the inbound UniversalPayload struct
869
878
  // Skip getCode if accountStatusCache already confirmed deployment
870
879
  let isUEADeployed;
@@ -928,17 +937,18 @@ function executeCeaToPush(ctx, params, executeFn) {
928
937
  (0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
929
938
  // 9. Build outbound request (same structure as Route 2)
930
939
  // target = CEA address (for self-execution), value = 0 in payload
931
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
940
+ let outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
932
941
  prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
933
942
  burnAmount, // amount: 0 (payload-only, CEA uses its own balance)
934
943
  outboundGasLimit, ceaPayload, // payload: ABI-encoded CEA multicall
935
- ueaAddress // revertRecipient: UEA
936
- );
944
+ ueaAddress, // revertRecipient: UEA
945
+ (_g = params.maxPCForGas) !== null && _g !== void 0 ? _g : BigInt(0));
937
946
  (0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
938
947
  target: outboundReq.target,
939
948
  token: outboundReq.token,
940
949
  amount: outboundReq.amount.toString(),
941
950
  gasLimit: outboundReq.gasLimit.toString(),
951
+ maxPCForGas: outboundReq.maxPCForGas.toString(),
942
952
  payloadLength: outboundReq.payload.length,
943
953
  revertRecipient: outboundReq.revertRecipient,
944
954
  }, null, 2)}`);
@@ -949,6 +959,7 @@ function executeCeaToPush(ctx, params, executeFn) {
949
959
  // 11. Query gas fees from UniversalCore
950
960
  let gasFee = BigInt(0);
951
961
  let protocolFeeR3 = BigInt(0);
962
+ let outboundGasPriceR3 = BigInt(0);
952
963
  let nativeValueForGas = BigInt(0);
953
964
  let gasToken = selectors_1.ZERO_ADDRESS;
954
965
  let sizingDecisionR3;
@@ -958,10 +969,12 @@ function executeCeaToPush(ctx, params, executeFn) {
958
969
  const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundGasLimit, sourceChain);
959
970
  gasToken = result.gasToken;
960
971
  gasFee = result.gasFee;
972
+ outboundGasPriceR3 = result.gasPrice;
961
973
  protocolFeeR3 = result.protocolFee;
962
974
  nativeValueForGas = result.nativeValueForGas;
963
975
  sizingDecisionR3 = result.sizing;
964
- (0, context_1.printLog)(ctx, `executeCeaToPush — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_h = (_g = result.sizing) === null || _g === void 0 ? void 0 : _g.category) !== null && _h !== void 0 ? _h : 'legacy'}`);
976
+ outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress, (_h = params.maxPCForGas) !== null && _h !== void 0 ? _h : BigInt(0), outboundGasPriceR3);
977
+ (0, context_1.printLog)(ctx, `executeCeaToPush — queried gas fee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_k = (_j = result.sizing) === null || _j === void 0 ? void 0 : _j.category) !== null && _k !== void 0 ? _k : 'legacy'}`);
965
978
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_02, sourceChain, protocolFeeR3, gasFee);
966
979
  }
967
980
  catch (err) {
@@ -1107,7 +1120,7 @@ function executeCeaToPush(ctx, params, executeFn) {
1107
1120
  */
1108
1121
  function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1109
1122
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
1110
- var _a, _b, _c, _d;
1123
+ var _a, _b, _c, _d, _e;
1111
1124
  if (typeof params.to !== 'string') {
1112
1125
  throw new Error('Route 3 SVM (CEA -> Push): to must be a Push Chain address (string), not a ChainTarget');
1113
1126
  }
@@ -1121,41 +1134,31 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1121
1134
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1122
1135
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_301, sourceChain, lockerContract);
1123
1136
  (0, context_1.printLog)(ctx, `executeCeaToPushSvm — sourceChain: ${sourceChain}, gateway: ${lockerContract}`);
1124
- // Determine drain amount and token
1125
- let drainAmount = BigInt(0);
1126
- let tokenMintHex;
1127
- if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
1128
- // SPL token drain
1129
- drainAmount = params.funds.amount;
1130
- const token = params.funds.token;
1131
- if (token && token.address) {
1132
- // Convert SPL mint address to 32-byte hex
1133
- const mintPk = new web3_js_1.PublicKey(token.address);
1134
- tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
1135
- }
1136
- }
1137
- else if (params.value && params.value > BigInt(0)) {
1138
- // Native SOL drain
1139
- drainAmount = params.value;
1140
- }
1141
- // Route 3 SVM: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
1142
- // CEA uses its own pre-existing balance — no PRC-20 burn needed on Push Chain.
1143
- const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1144
- // Build the SVM CPI payload (send_universal_tx_to_uea wrapped in execute)
1145
- // If params.data is provided, pass it as extraPayload for Push Chain execution
1146
- let extraPayload;
1147
- if (params.data && typeof params.data === 'string') {
1148
- extraPayload = (0, viem_1.hexToBytes)(params.data);
1149
- }
1137
+ // Route 3 SVM: CEA uses its own pre-existing balance — no PRC-20 burn
1138
+ // needed on Push Chain. The outer request token must still match the source
1139
+ // asset so the relayer supplies the same Solana mint that the payload
1140
+ // encodes; otherwise the gateway rejects with InvalidMint.
1141
+ const { prc20Token, drainAmount, tokenMintHex } = resolveR3SvmDrain(params, sourceChain, ctx.pushNetwork);
1150
1142
  // Derive CEA PDA as revert recipient: ["push_identity", ueaAddress_20_bytes]
1151
1143
  const ueaBytes = Buffer.from(ueaAddress.slice(2), 'hex'); // 20 bytes
1152
1144
  const [ceaPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes], programPk);
1153
1145
  const ceaPdaHex = ('0x' + Buffer.from(ceaPda.toBytes()).toString('hex'));
1146
+ // Pre-fetch UEA status before payload construction. The inbound
1147
+ // UniversalPayload must use the nonce after this outbound execution, because
1148
+ // the Push-side execute() below consumes one UEA nonce before the Solana
1149
+ // round-trip payload returns.
1150
+ const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
1151
+ const [ueaCode, ueaBalance] = yield Promise.all([
1152
+ ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
1153
+ ctx.pushClient.getBalance(ueaAddress),
1154
+ ]);
1155
+ const isUEADeployed = ueaCode !== undefined;
1156
+ const ueaNonce = isUEADeployed ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress) : BigInt(0);
1154
1157
  const svmPayload = (0, payload_builders_1.encodeSvmCeaToUeaPayload)({
1155
1158
  gatewayProgramHex,
1156
1159
  drainAmount,
1157
1160
  tokenMintHex,
1158
- extraPayload,
1161
+ extraPayload: buildR3SvmExtraPayload(ctx, params, ueaAddress, sourceChain, ueaNonce + BigInt(1)),
1159
1162
  revertRecipientHex: ceaPdaHex,
1160
1163
  });
1161
1164
  (0, context_1.printLog)(ctx, `executeCeaToPushSvm — drainAmount: ${drainAmount.toString()}, payload length: ${(svmPayload.length - 2) / 2} bytes`);
@@ -1166,33 +1169,42 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1166
1169
  // (e.g. pSOL). The UGPC upgrade post-2026-03-19 already accepts amount = 0
1167
1170
  // on the cascade path, and the same precompile is used here.
1168
1171
  const burnAmount = BigInt(0);
1172
+ let effectiveGasLimit = (_a = params.gasLimit) !== null && _a !== void 0 ? _a : BigInt(0);
1169
1173
  // Build outbound request: target = gateway program (self-call)
1170
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), svmPayload, ueaAddress);
1171
- (0, context_1.printLog)(ctx, `executeCeaToPushSvm — outbound request: target=${outboundReq.target.slice(0, 20)}..., token=${outboundReq.token}`);
1172
- // Pre-fetch UEA status early — balance is needed for gas value calculation
1173
- const gatewayPcAddress = (0, helpers_1.getUniversalGatewayPCAddress)();
1174
- const [ueaCode, ueaBalance] = yield Promise.all([
1175
- ctx.pushClient.publicClient.getCode({ address: ueaAddress }),
1176
- ctx.pushClient.getBalance(ueaAddress),
1177
- ]);
1178
- const isUEADeployed = ueaCode !== undefined;
1179
- const ueaNonce = isUEADeployed ? yield (0, uea_manager_1.getUEANonce)(ctx, ueaAddress) : BigInt(0);
1174
+ let outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, (_b = params.maxPCForGas) !== null && _b !== void 0 ? _b : BigInt(0));
1175
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — outbound request: target=${outboundReq.target.slice(0, 20)}..., token=${outboundReq.token}, maxPCForGas=${outboundReq.maxPCForGas}`);
1180
1176
  // Query gas fees
1181
1177
  let gasFee = BigInt(0);
1182
1178
  let protocolFeeR3Svm = BigInt(0);
1179
+ let outboundGasPriceR3Svm = BigInt(0);
1183
1180
  let nativeValueForGas = BigInt(0);
1184
1181
  let gasToken = selectors_1.ZERO_ADDRESS;
1185
1182
  let sizingDecisionR3Svm;
1186
1183
  if (prc20Token !== selectors_1.ZERO_ADDRESS) {
1187
1184
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_01, sourceChain);
1188
1185
  try {
1189
- const result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit, sourceChain);
1186
+ let result = yield (0, gas_calculator_1.queryOutboundGasFee)(ctx, prc20Token, outboundReq.gasLimit, sourceChain);
1187
+ result = yield (0, svm_rent_1.ensureSvmFinalizeGasBudgetQuote)({
1188
+ ctx,
1189
+ ueaAddress,
1190
+ targetChain: sourceChain,
1191
+ prc20Token,
1192
+ quote: result,
1193
+ splMintBase58: undefined,
1194
+ burnAmount: BigInt(0),
1195
+ pathTag: 'executeCeaToPushSvm',
1196
+ });
1190
1197
  gasToken = result.gasToken;
1191
1198
  gasFee = result.gasFee;
1199
+ outboundGasPriceR3Svm = result.gasPrice;
1192
1200
  protocolFeeR3Svm = result.protocolFee;
1193
1201
  nativeValueForGas = result.nativeValueForGas;
1194
1202
  sizingDecisionR3Svm = result.sizing;
1195
- (0, context_1.printLog)(ctx, `executeCeaToPushSvm — gasFee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_d = (_c = result.sizing) === null || _c === void 0 ? void 0 : _c.category) !== null && _d !== void 0 ? _d : 'legacy'}`);
1203
+ effectiveGasLimit = result.gasLimitUsed;
1204
+ outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, (_c = params.maxPCForGas) !== null && _c !== void 0 ? _c : BigInt(0), outboundGasPriceR3Svm);
1205
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — resolved effectiveGasLimit: ${effectiveGasLimit} ` +
1206
+ `(gasFee=${result.gasFee}, gasPrice=${result.gasPrice})`);
1207
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — gasFee: ${gasFee.toString()}, gasToken: ${gasToken}, nativeValueForGas: ${nativeValueForGas.toString()}, sizing=${(_e = (_d = result.sizing) === null || _d === void 0 ? void 0 : _d.category) !== null && _e !== void 0 ? _e : 'legacy'}`);
1196
1208
  (0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_302_02, sourceChain, protocolFeeR3Svm, gasFee);
1197
1209
  }
1198
1210
  catch (err) {
@@ -1252,9 +1264,8 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1252
1264
  }
1253
1265
  // SVM warn-threshold telemetry (no truncation — pre-flight handles drain protection).
1254
1266
  (0, preflight_1.maybeFireSvmWarnThreshold)(ctx, nativeValueForGas, gasToken, 'R3_SVM');
1255
- // Pre-flight (R3 SVM): no PRC-20 burn check — `burnAmount = 1` is a legacy
1256
- // precompile-validation shim against a token UEAs structurally don't hold
1257
- // (see plan §9 #4 / §10.2 latent-bug ticket). Native UPC check still applies.
1267
+ // Pre-flight (R3 SVM): no PRC-20 burn check. R3 SVM uses burnAmount=0
1268
+ // because the drain amount lives inside the SVM instruction data.
1258
1269
  (0, preflight_1.runPreflight)({
1259
1270
  ctx,
1260
1271
  ueaAddress,
@@ -1299,17 +1310,11 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1299
1310
  response.chain = sourceChain;
1300
1311
  const chainInfo = chain_1.CHAIN_INFO[sourceChain];
1301
1312
  response.chainNamespace = `${chain_1.VM_NAMESPACE[chainInfo.vm]}:${chainInfo.chainId}`;
1302
- // R3 SVM inbound round-trip tracking is disabled.
1303
- //
1304
- // For Route 3 SVM drains (rescue_funds-style flow, txType=RESCUE_FUNDS),
1305
- // empirical check on donut showed the keeper does NOT create a child
1306
- // universalTxId on Push for the Solana-side `UniversalTx(fromCEA=true)`
1307
- // log — the parent utxId's `outboundStatus=OBSERVED` is the terminal state.
1308
- // Polling for a deterministically-derived child id therefore never
1309
- // resolves, and the cosmos `inbound_tx_hash` text-search fallback also
1310
- // returns 0 hits. Receipts now resolve as soon as the Solana outbound is
1311
- // observed instead of timing out at 300s.
1312
- response._expectsInboundRoundTrip = false;
1313
+ // R3 SVM mirrors EVM R3: when funds drain back to Push, the Solana-side
1314
+ // send_universal_tx_to_uea emits an inbound leg whose payload can execute on
1315
+ // Push. Payload-only R3 has no child inbound UTX, so skip round-trip tracking
1316
+ // when no amount is being drained.
1317
+ response._expectsInboundRoundTrip = drainAmount > BigInt(0);
1313
1318
  return response;
1314
1319
  });
1315
1320
  }
@@ -1335,7 +1340,7 @@ function executeCeaToCea(ctx, params, executeFn) {
1335
1340
  */
1336
1341
  function buildPayloadForRoute(ctx, params, route, nonce) {
1337
1342
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
1338
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1343
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
1339
1344
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
1340
1345
  switch (route) {
1341
1346
  case route_detector_1.TransactionRoute.UOA_TO_PUSH: {
@@ -1379,13 +1384,13 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1379
1384
  }
1380
1385
  else if (hasExecute) {
1381
1386
  prc20Token = (0, helpers_1.getNativePRC20ForChain)(target.chain, ctx.pushNetwork);
1382
- burnAmount = BigInt(1);
1387
+ burnAmount = BigInt(0);
1383
1388
  }
1384
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), payload, ueaAddress);
1389
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_b = params.gasLimit) !== null && _b !== void 0 ? _b : BigInt(0), payload, ueaAddress, (_c = params.maxPCForGas) !== null && _c !== void 0 ? _c : BigInt(0));
1385
1390
  return { payload, gatewayRequest: outboundReq };
1386
1391
  }
1387
1392
  // EVM path: Resolve CEA address first (needed for self-transfer check)
1388
- const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_c = ctx.rpcUrls[target.chain]) === null || _c === void 0 ? void 0 : _c[0]);
1393
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, target.chain, (_d = ctx.rpcUrls[target.chain]) === null || _d === void 0 ? void 0 : _d[0]);
1389
1394
  // Build CEA outbound payload
1390
1395
  const multicalls = [];
1391
1396
  if (params.data) {
@@ -1418,21 +1423,24 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1418
1423
  const payload = (0, payload_builders_1.buildCeaMulticallPayload)(multicalls);
1419
1424
  let prc20Token = selectors_1.ZERO_ADDRESS;
1420
1425
  let burnAmount = BigInt(0);
1421
- if ((_d = params.funds) === null || _d === void 0 ? void 0 : _d.amount) {
1426
+ if ((_e = params.funds) === null || _e === void 0 ? void 0 : _e.amount) {
1422
1427
  const token = params.funds.token;
1423
1428
  if (token) {
1424
1429
  prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token).address;
1425
1430
  burnAmount = params.funds.amount;
1426
1431
  }
1427
1432
  }
1428
- const targetBytes = ceaAddress;
1429
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_e = params.gasLimit) !== null && _e !== void 0 ? _e : BigInt(0), payload, ueaAddress);
1433
+ const targetBytes = payload === '0x'
1434
+ ? selectors_1.ZERO_ADDRESS
1435
+ : ceaAddress;
1436
+ (0, payload_builders_1.assertCeaFundsParkingInvariant)(targetBytes, payload);
1437
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, (_f = params.gasLimit) !== null && _f !== void 0 ? _f : BigInt(0), payload, ueaAddress, (_g = params.maxPCForGas) !== null && _g !== void 0 ? _g : BigInt(0));
1430
1438
  return { payload, gatewayRequest: outboundReq };
1431
1439
  }
1432
1440
  case route_detector_1.TransactionRoute.CEA_TO_PUSH: {
1433
1441
  // Route 3: CEA -> Push Chain
1434
- // Build CEA multicall (approve + sendUniversalTxFromCEA) and wrap in outbound
1435
- if (!((_f = params.from) === null || _f === void 0 ? void 0 : _f.chain)) {
1442
+ // Build CEA multicall (sendUniversalTxToUEA self-call) and wrap in outbound
1443
+ if (!((_h = params.from) === null || _h === void 0 ? void 0 : _h.chain)) {
1436
1444
  throw new Error('Route 3 (CEA -> Push) requires from.chain');
1437
1445
  }
1438
1446
  const sourceChain = params.from.chain;
@@ -1445,21 +1453,7 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1445
1453
  }
1446
1454
  const programPk = new web3_js_1.PublicKey(lockerContract);
1447
1455
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1448
- let drainAmount = BigInt(0);
1449
- let tokenMintHex;
1450
- if (((_g = params.funds) === null || _g === void 0 ? void 0 : _g.amount) && params.funds.amount > BigInt(0)) {
1451
- drainAmount = params.funds.amount;
1452
- const token = params.funds.token;
1453
- if (token && token.address) {
1454
- const mintPk = new web3_js_1.PublicKey(token.address);
1455
- tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
1456
- }
1457
- }
1458
- else if (params.value && params.value > BigInt(0)) {
1459
- drainAmount = params.value;
1460
- }
1461
- // Route 3 SVM: ALWAYS use native PRC-20, CEA uses its own balance.
1462
- const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1456
+ const { prc20Token, drainAmount, tokenMintHex } = resolveR3SvmDrain(params, sourceChain, ctx.pushNetwork);
1463
1457
  // Derive CEA PDA as revert recipient
1464
1458
  const ueaBytes2 = Buffer.from(ueaAddress.slice(2), 'hex');
1465
1459
  const [ceaPda2] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes2], programPk);
@@ -1468,25 +1462,22 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1468
1462
  gatewayProgramHex,
1469
1463
  drainAmount,
1470
1464
  tokenMintHex,
1465
+ extraPayload: buildR3SvmExtraPayload(ctx, params, ueaAddress, sourceChain, nonce + BigInt(1)),
1471
1466
  revertRecipientHex: ceaPdaHex2,
1472
1467
  });
1473
- // burnAmount = 1 (minimum for precompile; drain amount lives inside the ixData)
1474
- const burnAmount = BigInt(1);
1475
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_h = params.gasLimit) !== null && _h !== void 0 ? _h : BigInt(0), svmPayload, ueaAddress);
1468
+ // Payload-only outbound: drain amount lives inside ixData.
1469
+ const burnAmount = BigInt(0);
1470
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(gatewayProgramHex, prc20Token, burnAmount, (_j = params.gasLimit) !== null && _j !== void 0 ? _j : BigInt(0), svmPayload, ueaAddress, (_k = params.maxPCForGas) !== null && _k !== void 0 ? _k : BigInt(0));
1476
1471
  return { payload: svmPayload, gatewayRequest: outboundReq };
1477
1472
  }
1478
- const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_j = ctx.rpcUrls[sourceChain]) === null || _j === void 0 ? void 0 : _j[0]);
1473
+ const { cea: ceaAddress } = yield (0, cea_utils_1.getCEAAddress)(ueaAddress, sourceChain, (_l = ctx.rpcUrls[sourceChain]) === null || _l === void 0 ? void 0 : _l[0]);
1479
1474
  // CEA auto-deploys on-chain: Vault.finalizeUniversalTx calls CEAFactory.deployCEA()
1480
1475
  // if CEA doesn't exist yet. No SDK-side blocking needed.
1481
- const gatewayAddr = chain_1.UNIVERSAL_GATEWAY_ADDRESSES[sourceChain];
1482
- if (!gatewayAddr) {
1483
- throw new Error(`No UniversalGateway address configured for chain ${sourceChain}`);
1484
- }
1485
1476
  // Build CEA multicalls (self-calls via sendUniversalTxToUEA)
1486
1477
  const ceaMulticalls = [];
1487
1478
  let tokenAddress = selectors_1.ZERO_ADDRESS;
1488
1479
  let amount = BigInt(0);
1489
- if ((_k = params.funds) === null || _k === void 0 ? void 0 : _k.amount) {
1480
+ if ((_m = params.funds) === null || _m === void 0 ? void 0 : _m.amount) {
1490
1481
  const token = params.funds.token;
1491
1482
  if (token) {
1492
1483
  if (token.mechanism === 'native') {
@@ -1496,27 +1487,6 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1496
1487
  else {
1497
1488
  tokenAddress = token.address;
1498
1489
  amount = params.funds.amount;
1499
- // Reset allowance to 0 first for USDT-style tokens, then approve
1500
- const approveZeroData = (0, viem_1.encodeFunctionData)({
1501
- abi: erc20_evm_1.ERC20_EVM,
1502
- functionName: 'approve',
1503
- args: [gatewayAddr, BigInt(0)],
1504
- });
1505
- ceaMulticalls.push({
1506
- to: tokenAddress,
1507
- value: BigInt(0),
1508
- data: approveZeroData,
1509
- });
1510
- const approveData = (0, viem_1.encodeFunctionData)({
1511
- abi: erc20_evm_1.ERC20_EVM,
1512
- functionName: 'approve',
1513
- args: [gatewayAddr, amount],
1514
- });
1515
- ceaMulticalls.push({
1516
- to: tokenAddress,
1517
- value: BigInt(0),
1518
- data: approveData,
1519
- });
1520
1490
  }
1521
1491
  }
1522
1492
  }
@@ -1551,8 +1521,8 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1551
1521
  // Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
1552
1522
  // burnAmount = 0 makes this a payload-only outbound relay.
1553
1523
  const burnAmount = BigInt(0);
1554
- const outboundGasLimit = (_l = params.gasLimit) !== null && _l !== void 0 ? _l : selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
1555
- const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress);
1524
+ const outboundGasLimit = (_o = params.gasLimit) !== null && _o !== void 0 ? _o : selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
1525
+ const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, outboundGasLimit, ceaPayload, ueaAddress, (_p = params.maxPCForGas) !== null && _p !== void 0 ? _p : BigInt(0));
1556
1526
  return { payload: ceaPayload, gatewayRequest: outboundReq };
1557
1527
  }
1558
1528
  default:
@@ -16,6 +16,11 @@ const enums_1 = require("../../constants/enums");
16
16
  // ============================================================================
17
17
  // EIP-712 Domain Separator (shared by execution & migration hashes)
18
18
  // ============================================================================
19
+ // NOTE: Push Chain testnet UEA implementation (UEA_VM[EVM] =
20
+ // 0x4C8401F885c79b0e6e0241812f089e8529045d11) still uses the pre-audit
21
+ // 4-field domain (typehash 0x2aef22f9…) — the salted post-audit typehash
22
+ // 0xb90aaffa… in the audit-fixes contracts hasn't been deployed yet. Drop
23
+ // `salt` until the contracts redeploy. See docs/audit-2026-05/sdk-changes-required.md#11.
19
24
  function buildDomainSeparator(vm, chainId, version, verifyingContract) {
20
25
  const domainTypeHash = (0, viem_1.keccak256)((0, viem_1.toBytes)(vm === enums_1.VM.EVM
21
26
  ? 'EIP712Domain(string version,uint256 chainId,address verifyingContract)'
@@ -23,12 +28,12 @@ function buildDomainSeparator(vm, chainId, version, verifyingContract) {
23
28
  return (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([
24
29
  { name: 'typeHash', type: 'bytes32' },
25
30
  { name: 'version', type: 'bytes32' },
26
- { name: 'chainId', type: vm === enums_1.VM.EVM ? 'uint256' : 'string' },
31
+ { name: 'chainId', type: vm === enums_1.VM.EVM ? 'uint256' : 'bytes32' },
27
32
  { name: 'verifyingContract', type: 'address' },
28
33
  ], [
29
34
  domainTypeHash,
30
35
  (0, viem_1.keccak256)((0, viem_1.toBytes)(version)),
31
- vm === enums_1.VM.EVM ? BigInt(chainId) : chainId,
36
+ vm === enums_1.VM.EVM ? BigInt(chainId) : (0, viem_1.keccak256)((0, viem_1.toBytes)(chainId)),
32
37
  verifyingContract,
33
38
  ]));
34
39
  }