@pushchain/core 5.1.4-alpha.11 → 5.1.4-alpha.12

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushchain/core",
3
- "version": "5.1.4-alpha.11",
3
+ "version": "5.1.4-alpha.12",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -28,6 +28,9 @@ const confirmation_1 = require("./confirmation");
28
28
  const gateway_client_1 = require("./gateway-client");
29
29
  const response_builder_1 = require("./response-builder");
30
30
  const push_chain_tx_1 = require("./push-chain-tx");
31
+ const gas_calculator_1 = require("./gas-calculator");
32
+ const helpers_2 = require("./helpers");
33
+ const price_fetch_1 = require("../../price-fetch/price-fetch");
31
34
  function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks) {
32
35
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
33
36
  var _a, _b;
@@ -202,6 +205,52 @@ function executeStandardPayload(ctx, execute, eventBuffer, getResponseCallbacks)
202
205
  const effectiveLockAmountInUSD = execute._minimumDepositUsd && execute._minimumDepositUsd > lockAmountInUSD
203
206
  ? execute._minimumDepositUsd
204
207
  : lockAmountInUSD;
208
+ // Pre-flight: predict the actual $PC the UEA will receive after the
209
+ // on-chain pETH → WPC swap and validate it covers requiredFunds.
210
+ // Uses the same Uniswap V3 quoter the chain uses (execute_inbound_gas.go).
211
+ // Mirrors the pattern in route-handlers.ts (Route 2/3).
212
+ const { vm: originVm } = chain_1.CHAIN_INFO[chain];
213
+ if (originVm === enums_1.VM.EVM) {
214
+ try {
215
+ // Replicate lockFee's USD → native conversion (with its $1–$10 clamp)
216
+ const oneUsd = utils_1.Utils.helpers.parseUnits('1', 8);
217
+ const tenUsd = utils_1.Utils.helpers.parseUnits('10', 8);
218
+ let depositUsd = effectiveLockAmountInUSD < oneUsd ? oneUsd : effectiveLockAmountInUSD;
219
+ if (depositUsd > tenUsd)
220
+ depositUsd = tenUsd;
221
+ const ethPrice = yield new price_fetch_1.PriceFetch(ctx.rpcUrls).getPrice(chain);
222
+ if (ethPrice > BigInt(0)) {
223
+ const nativeAmountETH = (depositUsd * BigInt(1e18) + (ethPrice - BigInt(1))) / ethPrice + BigInt(1);
224
+ const originPrc20 = (0, helpers_2.getNativePRC20ForChain)(chain, ctx.pushNetwork);
225
+ const predictedUPC = yield (0, gas_calculator_1.estimateDepositFromLockedNative)(ctx, nativeAmountETH, originPrc20);
226
+ if (predictedUPC > BigInt(0)) {
227
+ // 10% slippage margin (same as route-handlers.ts)
228
+ const safeUPC = (predictedUPC * BigInt(90)) / BigInt(100);
229
+ const totalAvailable = safeUPC + funds;
230
+ (0, context_1.printLog)(ctx, `Fee-lock pre-flight: deposit ${depositUsd.toString()} USD → ` +
231
+ `~${nativeAmountETH.toString()} wei ETH → ~${predictedUPC.toString()} UPC ` +
232
+ `(safe: ${safeUPC.toString()}), existing: ${funds.toString()}, ` +
233
+ `total: ${totalAvailable.toString()}, required: ${requiredFunds.toString()}`);
234
+ if (totalAvailable < requiredFunds) {
235
+ const shortfall = requiredFunds - totalAvailable;
236
+ throw new Error(`Insufficient deposit: the fee-lock will deposit ~${(Number(safeUPC) / 1e18).toFixed(4)} $PC ` +
237
+ `but the transaction requires ~${(Number(requiredFunds) / 1e18).toFixed(4)} $PC ` +
238
+ `(shortfall: ~${(Number(shortfall) / 1e18).toFixed(4)} $PC). ` +
239
+ `The fee-lock deposit is capped at $10 USD. Reduce the transfer value or ` +
240
+ `pre-fund the UEA (${UEA}) on Push Chain.`);
241
+ }
242
+ }
243
+ }
244
+ }
245
+ catch (err) {
246
+ // If the error is our own insufficient-deposit error, rethrow it
247
+ if (err instanceof Error && err.message.startsWith('Insufficient deposit')) {
248
+ throw err;
249
+ }
250
+ // Otherwise quoter failed — log and continue (same fallback as route-handlers)
251
+ (0, context_1.printLog)(ctx, `Fee-lock pre-flight quote failed, proceeding without validation: ${err instanceof Error ? err.message : String(err)}`);
252
+ }
253
+ }
205
254
  const feeLockTxHashBytes = yield (0, gateway_client_1.lockFee)(ctx, effectiveLockAmountInUSD, universalPayload, req);
206
255
  feeLockTxHash = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
207
256
  verificationData = (0, viem_1.bytesToHex)(feeLockTxHashBytes);
@@ -195,10 +195,24 @@ function extractPcTxAndTransform(ctx, pushChainUniversalTx, gatewayTxHash, event
195
195
  const lastPcTransaction = pushChainUniversalTx.pcTx.at(-1);
196
196
  (0, context_1.printLog)(ctx, `${label} — pcTx: ` + JSON.stringify(pushChainUniversalTx.pcTx.map((p) => ({ txHash: p.txHash, status: p.status, errorMsg: p.errorMsg })), null, 2));
197
197
  (0, context_1.printLog)(ctx, `${label} — using lastPcTransaction: ` + JSON.stringify(lastPcTransaction, null, 2));
198
- if (!(lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.txHash)) {
199
- const failedPcTx = pushChainUniversalTx.pcTx.find((pcTx) => pcTx.status === 'FAILED' && pcTx.errorMsg);
198
+ if (!(lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.txHash) || lastPcTransaction.status === 'FAILED') {
199
+ const failedPcTx = (lastPcTransaction === null || lastPcTransaction === void 0 ? void 0 : lastPcTransaction.status) === 'FAILED'
200
+ ? lastPcTransaction
201
+ : pushChainUniversalTx.pcTx.find((pcTx) => pcTx.status === 'FAILED' && pcTx.errorMsg);
200
202
  const errorDetails = (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg) ? `: ${failedPcTx.errorMsg}` : '';
201
- throw new Error(`No transaction hash found in Push Chain response for gateway tx: ${gatewayTxHash}${errorDetails}`);
203
+ // Parse known UEA error selectors for actionable hints
204
+ let hint = '';
205
+ if (failedPcTx === null || failedPcTx === void 0 ? void 0 : failedPcTx.errorMsg) {
206
+ if (failedPcTx.errorMsg.includes('0xacfdb444')) {
207
+ hint = ' [ExecutionFailed: the subcall to the target contract reverted. ' +
208
+ 'Check that the target address can receive native tokens (has receive/fallback) ' +
209
+ 'and that calldata matches a valid function signature.]';
210
+ }
211
+ else if (failedPcTx.errorMsg.includes('0x179a867c')) {
212
+ hint = ' [ExpiredDeadline: the transaction deadline has passed.]';
213
+ }
214
+ }
215
+ throw new Error(`Push Chain transaction failed for gateway tx: ${gatewayTxHash}${errorDetails}${hint}`);
202
216
  }
203
217
  const tx = yield ctx.pushClient.getTransaction(lastPcTransaction.txHash);
204
218
  return transformFn(tx, eventBuffer);
@@ -256,7 +256,7 @@ function trackTransaction(ctx, txHash, options, callbacks) {
256
256
  function transformToUniversalTxResponse(ctx_1, tx_1) {
257
257
  return tslib_1.__awaiter(this, arguments, void 0, function* (ctx, tx, eventBuffer = [], callbacks) {
258
258
  const chain = ctx.universalSigner.account.chain;
259
- const { vm, chainId } = chain_1.CHAIN_INFO[chain];
259
+ let { vm, chainId } = chain_1.CHAIN_INFO[chain];
260
260
  let from;
261
261
  let to;
262
262
  let value;
@@ -269,6 +269,13 @@ function transformToUniversalTxResponse(ctx_1, tx_1) {
269
269
  throw new Error('UEA origin account is null');
270
270
  }
271
271
  originAddress = ueaOrigin.account.address;
272
+ // Use the resolved origin chain for vm/chainId so that the `origin`
273
+ // field reflects the transaction's actual origin, not the tracker's.
274
+ const resolvedChainInfo = chain_1.CHAIN_INFO[ueaOrigin.account.chain];
275
+ if (resolvedChainInfo) {
276
+ vm = resolvedChainInfo.vm;
277
+ chainId = resolvedChainInfo.chainId;
278
+ }
272
279
  from = (0, viem_1.getAddress)(tx.to);
273
280
  let decoded;
274
281
  if (tx.input !== '0x') {
@@ -565,7 +565,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
565
565
  */
566
566
  function executeCeaToPush(ctx, params, executeFn) {
567
567
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
568
- var _a, _b, _c, _d, _e, _f, _g;
568
+ var _a, _b, _c, _d, _e, _f;
569
569
  // 1. Validate and extract source chain
570
570
  if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
571
571
  throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
@@ -700,27 +700,24 @@ function executeCeaToPush(ctx, params, executeFn) {
700
700
  // CEA will self-execute this multicall (to=CEA, value=0)
701
701
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
702
702
  (0, context_1.printLog)(ctx, `executeCeaToPush — CEA payload (first 100 chars): ${ceaPayload.slice(0, 100)}...`);
703
- // 8. Determine PRC-20 token for the outbound burn on Push Chain.
704
- // For ERC20 flows (params.funds with token), use the token's PRC-20 (e.g. pUSDT)
705
- // so the Vault deposits ERC20 to CEA. For native flows, use the chain's native PRC-20 (e.g. pBNB).
706
- let prc20Token;
707
- if (((_f = params.funds) === null || _f === void 0 ? void 0 : _f.amount) && params.funds.token) {
708
- const token = params.funds.token;
709
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
710
- }
711
- else {
712
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
713
- }
714
- // burnAmount = PRC20 to burn on Push Chain (NOT the bridge amount).
715
- // Vault deposits burnAmount to CEA. CEA uses burnAmount + pre-existing balance for the bridge.
716
- const burnAmount = amount;
703
+ // 8. Determine PRC-20 token for the outbound relay.
704
+ // Route 3: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
705
+ // The CEA uses its OWN pre-existing balance on the external chain to bridge
706
+ // tokens back — no PRC-20 burn is needed on Push Chain.
707
+ // burnAmount = 0 makes this a payload-only outbound relay.
708
+ //
709
+ // Previously this burned pUSDT/pToken equal to the bridge amount, which failed
710
+ // because the UEA never holds pUSDT — the whole point of Route 3 is to get
711
+ // tokens FROM the CEA back to Push Chain, not to round-trip them.
712
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
713
+ const burnAmount = BigInt(0);
717
714
  (0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
718
715
  // 9. Build outbound request (same structure as Route 2)
719
716
  // target = CEA address (for self-execution), value = 0 in payload
720
717
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
721
718
  prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
722
- burnAmount, // amount: 1 wei (precompile workaround)
723
- (_g = params.gasLimit) !== null && _g !== void 0 ? _g : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
719
+ burnAmount, // amount: 0 (payload-only, CEA uses its own balance)
720
+ (_f = params.gasLimit) !== null && _f !== void 0 ? _f : BigInt(0), ceaPayload, // payload: ABI-encoded CEA multicall
724
721
  ueaAddress // revertRecipient: UEA
725
722
  );
726
723
  (0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
@@ -852,7 +849,6 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
852
849
  // Determine drain amount and token
853
850
  let drainAmount = BigInt(0);
854
851
  let tokenMintHex;
855
- let prc20Token;
856
852
  if (((_a = params.funds) === null || _a === void 0 ? void 0 : _a.amount) && params.funds.amount > BigInt(0)) {
857
853
  // SPL token drain
858
854
  drainAmount = params.funds.amount;
@@ -861,22 +857,15 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
861
857
  // Convert SPL mint address to 32-byte hex
862
858
  const mintPk = new web3_js_1.PublicKey(token.address);
863
859
  tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
864
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
865
- }
866
- else {
867
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
868
860
  }
869
861
  }
870
862
  else if (params.value && params.value > BigInt(0)) {
871
863
  // Native SOL drain
872
864
  drainAmount = params.value;
873
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
874
- }
875
- else {
876
- // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
877
- drainAmount = BigInt(0);
878
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
879
865
  }
866
+ // Route 3 SVM: ALWAYS use native PRC-20 for chain namespace lookup + gas fees.
867
+ // CEA uses its own pre-existing balance — no PRC-20 burn needed on Push Chain.
868
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
880
869
  // Build the SVM CPI payload (send_universal_tx_to_uea wrapped in execute)
881
870
  // If params.data is provided, pass it as extraPayload for Push Chain execution
882
871
  let extraPayload;
@@ -1100,28 +1089,19 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1100
1089
  const gatewayProgramHex = ('0x' + Buffer.from(programPk.toBytes()).toString('hex'));
1101
1090
  let drainAmount = BigInt(0);
1102
1091
  let tokenMintHex;
1103
- let prc20Token;
1104
1092
  if (((_j = params.funds) === null || _j === void 0 ? void 0 : _j.amount) && params.funds.amount > BigInt(0)) {
1105
1093
  drainAmount = params.funds.amount;
1106
1094
  const token = params.funds.token;
1107
1095
  if (token && token.address) {
1108
1096
  const mintPk = new web3_js_1.PublicKey(token.address);
1109
1097
  tokenMintHex = ('0x' + Buffer.from(mintPk.toBytes()).toString('hex'));
1110
- prc20Token = push_chain_1.PushChain.utils.tokens.getPRC20Address(token);
1111
- }
1112
- else {
1113
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1114
1098
  }
1115
1099
  }
1116
1100
  else if (params.value && params.value > BigInt(0)) {
1117
1101
  drainAmount = params.value;
1118
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1119
- }
1120
- else {
1121
- // Payload-only Route 3 SVM: no funds to drain, just execute data on Push Chain
1122
- drainAmount = BigInt(0);
1123
- prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1124
1102
  }
1103
+ // Route 3 SVM: ALWAYS use native PRC-20, CEA uses its own balance.
1104
+ const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1125
1105
  // Derive CEA PDA as revert recipient
1126
1106
  const ueaBytes2 = Buffer.from(ueaAddress.slice(2), 'hex');
1127
1107
  const [ceaPda2] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from('push_identity'), ueaBytes2], programPk);
@@ -1209,9 +1189,9 @@ function buildPayloadForRoute(ctx, params, route, nonce) {
1209
1189
  ceaMulticalls.push(sendCall);
1210
1190
  const ceaPayload = (0, payload_builders_1.buildCeaMulticallPayload)(ceaMulticalls);
1211
1191
  const prc20Token = (0, helpers_1.getNativePRC20ForChain)(sourceChain, ctx.pushNetwork);
1212
- // burnAmount = actual amount needed by CEA. Vault deposits this as msg.value.
1213
- // Fallback to BigInt(1) for payload-only outbound (precompile rejects amount=0).
1214
- const burnAmount = amount > BigInt(0) ? amount : BigInt(1);
1192
+ // Route 3: CEA uses its own pre-existing balance — no PRC-20 burn needed.
1193
+ // burnAmount = 0 makes this a payload-only outbound relay.
1194
+ const burnAmount = BigInt(0);
1215
1195
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, prc20Token, burnAmount, (_o = params.gasLimit) !== null && _o !== void 0 ? _o : BigInt(0), ceaPayload, ueaAddress);
1216
1196
  return { payload: ceaPayload, gatewayRequest: outboundReq };
1217
1197
  }