@pushchain/core 6.0.0 → 6.0.1

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": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "homepage": "https://push.org",
5
5
  "keywords": [
6
6
  "pushchain",
@@ -96,7 +96,7 @@ export declare const UNIVERSAL_GATEWAY_PC: readonly [{
96
96
  readonly inputs: readonly [{
97
97
  readonly components: readonly [{
98
98
  readonly internalType: "bytes";
99
- readonly name: "target";
99
+ readonly name: "recipient";
100
100
  readonly type: "bytes";
101
101
  }, {
102
102
  readonly internalType: "address";
@@ -42,7 +42,7 @@ exports.UNIVERSAL_GATEWAY_PC = [
42
42
  inputs: [
43
43
  {
44
44
  components: [
45
- { internalType: 'bytes', name: 'target', type: 'bytes' },
45
+ { internalType: 'bytes', name: 'recipient', type: 'bytes' },
46
46
  { internalType: 'address', name: 'token', type: 'address' },
47
47
  { internalType: 'uint256', name: 'amount', type: 'uint256' },
48
48
  { internalType: 'uint256', name: 'gasLimit', type: 'uint256' },
@@ -37,6 +37,8 @@ export declare const SYNTHETIC_PUSH_ERC20: Record<PUSH_NETWORK, {
37
37
  USDT_SOL: `0x${string}`;
38
38
  USDC_SOL: `0x${string}`;
39
39
  USDT_BNB: `0x${string}`;
40
+ USDC_BSC: `0x${string}`;
41
+ /** @deprecated Use USDC_BSC instead. */
40
42
  USDC_BNB: `0x${string}`;
41
43
  USDT_BASE: `0x${string}`;
42
44
  USDC_BASE: `0x${string}`;
@@ -63,6 +63,7 @@ exports.SYNTHETIC_PUSH_ERC20 = {
63
63
  USDT_SOL: '0x4f1A3D22d170a2F4Bddb37845a962322e24f4e34',
64
64
  USDC_SOL: '0x04B8F634ABC7C879763F623e0f0550a4b5c4426F',
65
65
  USDT_BNB: '0x731aF1Da5365259d27528557EE4aFBA4baC90ef2',
66
+ USDC_BSC: '0x120EBf25Dad7D6a09Ad2316f23f9Be95DBb90639',
66
67
  USDC_BNB: '0x120EBf25Dad7D6a09Ad2316f23f9Be95DBb90639',
67
68
  USDT_BASE: '0x148823809B853e1db187BC09A9ac909BC42F971a',
68
69
  USDC_BASE: '0xD7C6cA1e2c0CE260BE0c0AD39C1540de460e3Be1',
@@ -83,6 +84,7 @@ exports.SYNTHETIC_PUSH_ERC20 = {
83
84
  USDT_SOL: '0x4f1A3D22d170a2F4Bddb37845a962322e24f4e34',
84
85
  USDC_SOL: '0x04B8F634ABC7C879763F623e0f0550a4b5c4426F',
85
86
  USDT_BNB: '0x731aF1Da5365259d27528557EE4aFBA4baC90ef2',
87
+ USDC_BSC: '0x120EBf25Dad7D6a09Ad2316f23f9Be95DBb90639',
86
88
  USDC_BNB: '0x120EBf25Dad7D6a09Ad2316f23f9Be95DBb90639',
87
89
  USDT_BASE: '0x148823809B853e1db187BC09A9ac909BC42F971a',
88
90
  USDC_BASE: '0xD7C6cA1e2c0CE260BE0c0AD39C1540de460e3Be1',
@@ -103,6 +105,7 @@ exports.SYNTHETIC_PUSH_ERC20 = {
103
105
  USDT_SOL: '0xTBD',
104
106
  USDC_SOL: '0xTBD',
105
107
  USDT_BNB: '0xTBD',
108
+ USDC_BSC: '0xTBD',
106
109
  USDC_BNB: '0xTBD',
107
110
  USDT_BASE: '0xTBD',
108
111
  USDC_BASE: '0xTBD',
@@ -123,6 +126,7 @@ exports.SYNTHETIC_PUSH_ERC20 = {
123
126
  USDT_SOL: '0xTBD',
124
127
  USDC_SOL: '0xTBD',
125
128
  USDT_BNB: '0xTBD',
129
+ USDC_BSC: '0xTBD',
126
130
  USDC_BNB: '0xTBD',
127
131
  USDT_BASE: '0xTBD',
128
132
  USDC_BASE: '0xTBD',
@@ -1,6 +1,6 @@
1
1
  import { CHAIN, LIBRARY, PUSH_NETWORK } from './enums';
2
2
  import { TypedDataDomain, TypedData } from '../universal/signer/signer.types';
3
- export type { MoveableToken, PayableToken, PushChainMoveableToken, ChainSuffixAccessor, PushChainMoveableTokenAccessor, MoveableTokenConstantsMap, PayableTokenConstantsMap, } from './tokens';
3
+ export type { MoveableToken, PayableToken, PushChainMoveableToken, ChainSuffixAccessor, UsdcChainSuffixAccessor, PushChainMoveableTokenAccessor, MoveableTokenConstantsMap, PayableTokenConstantsMap, } from './tokens';
4
4
  export declare const CONSTANTS: {
5
5
  PUSH_NETWORK: typeof PUSH_NETWORK;
6
6
  CHAIN: typeof CHAIN;
@@ -76,6 +76,15 @@ export interface ChainSuffixAccessor {
76
76
  readonly bnb: PushChainMoveableToken;
77
77
  readonly sol: PushChainMoveableToken;
78
78
  }
79
+ export interface UsdcChainSuffixAccessor {
80
+ readonly eth: PushChainMoveableToken;
81
+ readonly arb: PushChainMoveableToken;
82
+ readonly base: PushChainMoveableToken;
83
+ readonly bsc: PushChainMoveableToken;
84
+ /** @deprecated Use `bsc` instead. */
85
+ readonly bnb: PushChainMoveableToken;
86
+ readonly sol: PushChainMoveableToken;
87
+ }
79
88
  export interface PushChainMoveableTokenAccessor {
80
89
  readonly pEth: PushChainMoveableToken;
81
90
  readonly pEthArb: PushChainMoveableToken;
@@ -86,7 +95,7 @@ export interface PushChainMoveableTokenAccessor {
86
95
  readonly pStEth: PushChainMoveableToken;
87
96
  readonly pDai: PushChainMoveableToken;
88
97
  readonly USDT: ChainSuffixAccessor;
89
- readonly USDC: ChainSuffixAccessor;
98
+ readonly USDC: UsdcChainSuffixAccessor;
90
99
  }
91
100
  export type MoveableTokenConstantsMap = {
92
101
  ETHEREUM_SEPOLIA: MoveableTokenAccessor;
@@ -270,7 +270,7 @@ function buildPushChainMoveableTokenList() {
270
270
  mk('USDC', 6, s.USDC_ETH, enums_1.CHAIN.ETHEREUM_SEPOLIA),
271
271
  mk('USDC', 6, s.USDC_ARB, enums_1.CHAIN.ARBITRUM_SEPOLIA),
272
272
  mk('USDC', 6, s.USDC_BASE, enums_1.CHAIN.BASE_SEPOLIA),
273
- mk('USDC', 6, s.USDC_BNB, enums_1.CHAIN.BNB_TESTNET),
273
+ mk('USDC', 6, s.USDC_BSC, enums_1.CHAIN.BNB_TESTNET),
274
274
  mk('USDC', 6, s.USDC_SOL, enums_1.CHAIN.SOLANA_DEVNET),
275
275
  mk('WETH', 18, s.WETH_ETH, enums_1.CHAIN.ETHEREUM_SEPOLIA),
276
276
  mk('stETH', 18, s.stETH_ETH, enums_1.CHAIN.ETHEREUM_SEPOLIA),
@@ -365,6 +365,7 @@ function buildPushChainMoveableTokenAccessor() {
365
365
  sourceChain,
366
366
  prc20Address: address,
367
367
  });
368
+ const usdcBsc = mk('USDC', 6, s.USDC_BSC, enums_1.CHAIN.BNB_TESTNET);
368
369
  return {
369
370
  pEth: mk('pETH', 18, s.pETH, enums_1.CHAIN.ETHEREUM_SEPOLIA),
370
371
  pEthArb: mk('pETH_ARB', 18, s.pETH_ARB, enums_1.CHAIN.ARBITRUM_SEPOLIA),
@@ -385,7 +386,8 @@ function buildPushChainMoveableTokenAccessor() {
385
386
  eth: mk('USDC', 6, s.USDC_ETH, enums_1.CHAIN.ETHEREUM_SEPOLIA),
386
387
  arb: mk('USDC', 6, s.USDC_ARB, enums_1.CHAIN.ARBITRUM_SEPOLIA),
387
388
  base: mk('USDC', 6, s.USDC_BASE, enums_1.CHAIN.BASE_SEPOLIA),
388
- bnb: mk('USDC', 6, s.USDC_BNB, enums_1.CHAIN.BNB_TESTNET),
389
+ bsc: usdcBsc,
390
+ bnb: usdcBsc,
389
391
  sol: mk('USDC', 6, s.USDC_SOL, enums_1.CHAIN.SOLANA_DEVNET),
390
392
  },
391
393
  };
@@ -41,7 +41,9 @@ export interface ComposeCascadeResult {
41
41
  multicalls: MultiCall[];
42
42
  requiredNativeValue: bigint;
43
43
  }
44
- export declare function composeCascadeDetailed(ctx: OrchestratorContext, segments: CascadeSegment[], ueaAddress: `0x${string}`, ueaBalance?: bigint, ueaNonce?: bigint): Promise<ComposeCascadeResult>;
44
+ export declare function composeCascadeDetailed(ctx: OrchestratorContext, segments: CascadeSegment[], ueaAddress: `0x${string}`, ueaBalance?: bigint, ueaNonce?: bigint, options?: {
45
+ enforceGasCheck?: boolean;
46
+ }): Promise<ComposeCascadeResult>;
45
47
  export declare function composeCascade(ctx: OrchestratorContext, segments: CascadeSegment[], ueaAddress: `0x${string}`, ueaBalance?: bigint, ueaNonce?: bigint): Promise<MultiCall[]>;
46
48
  export declare function createCascadedBuilder(ctx: OrchestratorContext, preparedTxs: PreparedUniversalTx[], callbacks: CascadeCallbacks, defaultEventHook?: (event: import('../../progress-hook/progress-hook.types').ProgressEvent) => void): {
47
49
  send: () => Promise<CascadedTxResponse>;
@@ -753,7 +753,7 @@ function getInboundFundingForBurnToken(segments, beforeSegmentIndex, burnToken,
753
753
  }
754
754
  return total;
755
755
  }
756
- function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce) {
756
+ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce, options) {
757
757
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
758
758
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
759
759
  let accumulatedPushMulticalls = [];
@@ -762,6 +762,8 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
762
762
  const currentUeaBalance = ueaBalance !== null && ueaBalance !== void 0 ? ueaBalance : BigInt(0);
763
763
  const cascadeNativeSeed = getCascadeNativeSeed(segments);
764
764
  const effectiveUeaBalance = currentUeaBalance + cascadeNativeSeed;
765
+ const enforceGasCheck = (options === null || options === void 0 ? void 0 : options.enforceGasCheck) === true ||
766
+ segments.some((segment) => segment.hops.some((hop) => { var _a; return ((_a = hop.params.options) === null || _a === void 0 ? void 0 : _a.enforceGasCheck) === true; }));
765
767
  if (cascadeNativeSeed > BigInt(0)) {
766
768
  (0, context_1.printLog)(ctx, `composeCascade — counting cascade native seed ${cascadeNativeSeed.toString()} for cascade sizing (current UEA balance ${currentUeaBalance.toString()})`);
767
769
  }
@@ -773,8 +775,8 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
773
775
  // Cascade pre-flight: catches the zero-balance silent-segment-zero failure
774
776
  // mode. When `ueaBalance <= CASCADE_GAS_RESERVE`, `perOutboundNativeValue`
775
777
  // is left undefined and downstream segments silently allocate value=0,
776
- // producing under-funded swaps that revert inside Uniswap. Throw early
777
- // with a structured error so the caller knows which path / hop failed.
778
+ // producing under-funded swaps that revert inside Uniswap. Warn by default
779
+ // and throw only when enforceGasCheck=true.
778
780
  if (numOutbounds > 0) {
779
781
  (0, preflight_1.runPreflight)({
780
782
  ctx,
@@ -785,6 +787,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
785
787
  requiredValue: BigInt(1),
786
788
  gasReserve: CASCADE_GAS_RESERVE,
787
789
  pathTag: 'CASCADE',
790
+ enforceGasCheck,
788
791
  });
789
792
  }
790
793
  let perOutboundNativeValue;
@@ -867,6 +870,17 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
867
870
  // knows which hop's pool is misbehaving.
868
871
  if (estimated > BigInt(0) && value < estimated) {
869
872
  const shortfall = estimated - value;
873
+ (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03, estimated, value, false, ueaAddress, 'CASCADE', {
874
+ kind: 'NATIVE',
875
+ segmentIndex: cascadeIdx,
876
+ enforceGasCheck,
877
+ shortfall,
878
+ });
879
+ if (!enforceGasCheck) {
880
+ (0, context_1.printLog)(ctx, `composeCascade — SVM outbound segment ${cascadeIdx} has native shortfall ${shortfall.toString()}; proceeding because enforceGasCheck=false`);
881
+ svmNativeValueBySegment.set(seg, value);
882
+ continue;
883
+ }
870
884
  (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04, estimated, value, shortfall, ueaAddress, 'CASCADE', { kind: 'NATIVE', segmentIndex: cascadeIdx });
871
885
  throw new errors_1.InsufficientUEABalanceError({
872
886
  required: estimated,
@@ -940,7 +954,13 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
940
954
  args: [ueaAddress],
941
955
  });
942
956
  const sufficient = onHand >= segBurnAmount;
943
- (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03, segBurnAmount, onHand, sufficient, ueaAddress, 'CASCADE', { kind: 'PRC20', burnToken: segBurnToken, segmentIndex: segIdxLoop });
957
+ (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03, segBurnAmount, onHand, sufficient, ueaAddress, 'CASCADE', {
958
+ kind: 'PRC20',
959
+ burnToken: segBurnToken,
960
+ segmentIndex: segIdxLoop,
961
+ enforceGasCheck,
962
+ shortfall: sufficient ? BigInt(0) : segBurnAmount - onHand,
963
+ });
944
964
  const priorPushExecutionMayFundBurn = segments
945
965
  .slice(0, segIdxLoop)
946
966
  .some((s) => s.type === 'PUSH_EXECUTION');
@@ -951,21 +971,26 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce)
951
971
  !priorInboundMayFundBurn) {
952
972
  const projectedOnHand = onHand + priorInboundFundingForBurn;
953
973
  const shortfall = segBurnAmount - projectedOnHand;
954
- (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04, segBurnAmount, projectedOnHand, shortfall, ueaAddress, 'CASCADE', {
955
- kind: 'PRC20',
956
- burnToken: segBurnToken,
957
- segmentIndex: segIdxLoop,
958
- });
959
- throw new errors_1.InsufficientUEABalanceError({
960
- required: segBurnAmount,
961
- available: projectedOnHand,
962
- shortfall,
963
- ueaAddress,
964
- pathTag: 'CASCADE',
965
- reason: 'PRC20',
966
- burnToken: segBurnToken,
967
- segmentIndex: segIdxLoop,
968
- });
974
+ if (!enforceGasCheck) {
975
+ (0, context_1.printLog)(ctx, `composeCascade — PRC-20 shortfall ${shortfall.toString()} for segment ${segIdxLoop}; proceeding because enforceGasCheck=false`);
976
+ }
977
+ else {
978
+ (0, context_2.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04, segBurnAmount, projectedOnHand, shortfall, ueaAddress, 'CASCADE', {
979
+ kind: 'PRC20',
980
+ burnToken: segBurnToken,
981
+ segmentIndex: segIdxLoop,
982
+ });
983
+ throw new errors_1.InsufficientUEABalanceError({
984
+ required: segBurnAmount,
985
+ available: projectedOnHand,
986
+ shortfall,
987
+ ueaAddress,
988
+ pathTag: 'CASCADE',
989
+ reason: 'PRC20',
990
+ burnToken: segBurnToken,
991
+ segmentIndex: segIdxLoop,
992
+ });
993
+ }
969
994
  }
970
995
  else if (!sufficient && priorPushExecutionMayFundBurn) {
971
996
  (0, context_1.printLog)(ctx, `composeCascade — skipping PRC-20 preflight failure for segment ${segIdxLoop}; earlier Push execution may fund ${segBurnToken} before this outbound runs`);
@@ -1252,11 +1277,14 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1252
1277
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
1253
1278
  // Extract HopDescriptors
1254
1279
  const hops = preparedTxs.map((tx) => tx._hop);
1280
+ const enforceGasCheck = hops.some((hop) => { var _a; return ((_a = hop.params.options) === null || _a === void 0 ? void 0 : _a.enforceGasCheck) === true; });
1281
+ const withCascadeOptions = (params) => enforceGasCheck
1282
+ ? Object.assign(Object.assign({}, params), { options: Object.assign(Object.assign({}, params.options), { enforceGasCheck: true }) }) : params;
1255
1283
  // Classify into segments
1256
1284
  const segments = classifyIntoSegments(hops);
1257
1285
  // Check if this is a single-hop Route 1 (no composition needed)
1258
1286
  if (preparedTxs.length === 1 && preparedTxs[0].route === 'UOA_TO_PUSH') {
1259
- const response = yield callbacks.executeFn(hops[0].params);
1287
+ const response = yield callbacks.executeFn(withCascadeOptions(hops[0].params));
1260
1288
  const singleRoute1Result = {
1261
1289
  initialTxHash: response.hash,
1262
1290
  initialTxResponse: response,
@@ -1290,7 +1318,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1290
1318
  }
1291
1319
  // Check if single-hop Route 2 (just execute directly)
1292
1320
  if (preparedTxs.length === 1 && preparedTxs[0].route === 'UOA_TO_CEA') {
1293
- const response = yield callbacks.executeFn(hops[0].params);
1321
+ const response = yield callbacks.executeFn(withCascadeOptions(hops[0].params));
1294
1322
  const targetChain = hops[0].targetChain || (0, helpers_1.getPushChainForNetwork)(ctx.pushNetwork);
1295
1323
  const singleRoute2Result = {
1296
1324
  initialTxHash: response.hash,
@@ -1335,7 +1363,7 @@ function createCascadedBuilder(ctx, preparedTxs, callbacks, defaultEventHook) {
1335
1363
  let composedMulticalls;
1336
1364
  let cascadeRequiredNativeValue = BigInt(0);
1337
1365
  try {
1338
- const composed = yield composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonceCascade);
1366
+ const composed = yield composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonceCascade, { enforceGasCheck });
1339
1367
  composedMulticalls = composed.multicalls;
1340
1368
  cascadeRequiredNativeValue = composed.requiredNativeValue;
1341
1369
  }
@@ -146,6 +146,7 @@ function toExecuteParams(params) {
146
146
  maxFeePerGas: params.maxFeePerGas,
147
147
  maxPriorityFeePerGas: params.maxPriorityFeePerGas,
148
148
  deadline: params.deadline,
149
+ options: params.options,
149
150
  payGasWith: params.payGasWith,
150
151
  feeLockTxHash: params.feeLockTxHash,
151
152
  };
@@ -1,6 +1,6 @@
1
1
  import { CHAIN } from '../../constants/enums';
2
2
  import type { OrchestratorContext } from './context';
3
- type StableKey = 'USDT_ETH' | 'USDT_ARB' | 'USDT_BASE' | 'USDT_BNB' | 'USDC_BNB' | 'USDT_SOL';
3
+ type StableKey = 'USDT_ETH' | 'USDT_ARB' | 'USDT_BASE' | 'USDT_BNB' | 'USDC_BSC' | 'USDT_SOL';
4
4
  /**
5
5
  * Get $PC/USD price (8 decimals) for the given origin chain's route.
6
6
  * Returns 0 on any failure — caller is expected to fall back to
@@ -8,12 +8,13 @@
8
8
  * with `Error("STF")`, fees are burned, and the user sees an opaque
9
9
  * library string with no balance signal.
10
10
  *
11
- * `runPreflight` replaces that branch: it checks the **pre-clamp**
12
- * buffered pool quote against UEA balance (and optionally a PRC-20 burn
13
- * balance) and either:
14
- * - emits `SEND-TX-203-03` (INFO, sufficient) and lets the caller proceed; or
11
+ * `runPreflight` checks the **pre-clamp** buffered pool quote against UEA
12
+ * balance (and optionally a PRC-20 burn balance) and either:
13
+ * - emits `SEND-TX-203-03` (INFO, sufficient) and lets the caller proceed;
14
+ * - emits `SEND-TX-203-03` (WARNING, insufficient) and proceeds when
15
+ * enforceGasCheck is false/omitted; or
15
16
  * - emits `SEND-TX-203-03` (ERROR, insufficient) + `SEND-TX-203-04` and
16
- * throws `InsufficientUEABalanceError`.
17
+ * throws `InsufficientUEABalanceError` when enforceGasCheck is true.
17
18
  *
18
19
  * The helper is intentionally not "pure" — it emits hooks and may throw.
19
20
  * Callers fetch all balances (so the helper itself does no I/O).
@@ -34,6 +35,8 @@ export interface RunPreflightOpts {
34
35
  burnToken?: `0x${string}`;
35
36
  burnAmount?: bigint;
36
37
  prc20Balance?: bigint;
38
+ /** Defaults to false: warn on shortfall and proceed. */
39
+ enforceGasCheck?: boolean;
37
40
  /** Cascade-only: identifies which segment failed. */
38
41
  segmentIndex?: number;
39
42
  }
@@ -9,12 +9,13 @@
9
9
  * with `Error("STF")`, fees are burned, and the user sees an opaque
10
10
  * library string with no balance signal.
11
11
  *
12
- * `runPreflight` replaces that branch: it checks the **pre-clamp**
13
- * buffered pool quote against UEA balance (and optionally a PRC-20 burn
14
- * balance) and either:
15
- * - emits `SEND-TX-203-03` (INFO, sufficient) and lets the caller proceed; or
12
+ * `runPreflight` checks the **pre-clamp** buffered pool quote against UEA
13
+ * balance (and optionally a PRC-20 burn balance) and either:
14
+ * - emits `SEND-TX-203-03` (INFO, sufficient) and lets the caller proceed;
15
+ * - emits `SEND-TX-203-03` (WARNING, insufficient) and proceeds when
16
+ * enforceGasCheck is false/omitted; or
16
17
  * - emits `SEND-TX-203-03` (ERROR, insufficient) + `SEND-TX-203-04` and
17
- * throws `InsufficientUEABalanceError`.
18
+ * throws `InsufficientUEABalanceError` when enforceGasCheck is true.
18
19
  *
19
20
  * The helper is intentionally not "pure" — it emits hooks and may throw.
20
21
  * Callers fetch all balances (so the helper itself does no I/O).
@@ -32,7 +33,7 @@ const errors_1 = require("./errors");
32
33
  * sees the actionable PRC-20 shortfall instead of a stale native PC error.
33
34
  */
34
35
  function runPreflight(opts) {
35
- const { ctx, ueaAddress, ueaBalance, requiredValue, gasReserve, pathTag, burnToken, burnAmount, prc20Balance, segmentIndex, } = opts;
36
+ const { ctx, ueaAddress, ueaBalance, requiredValue, gasReserve, pathTag, burnToken, burnAmount, prc20Balance, enforceGasCheck = false, segmentIndex, } = opts;
36
37
  // Each route bucket gets its own preflight IDs so hook streams stay
37
38
  // self-consistent within their convention (R2 → 203-xx, R3 → 303-xx,
38
39
  // cascade → 003-xx). The three hook formatters share an identical
@@ -54,9 +55,15 @@ function runPreflight(opts) {
54
55
  if (burnAmount && burnAmount > BigInt(0) && burnToken) {
55
56
  const onHand = prc20Balance !== null && prc20Balance !== void 0 ? prc20Balance : BigInt(0);
56
57
  const sufficient = onHand >= burnAmount;
57
- (0, context_1.fireProgressHook)(ctx, HOOK_PRE_INFO, burnAmount, onHand, sufficient, ueaAddress, pathTag, { kind: 'PRC20', burnToken, segmentIndex });
58
- if (!sufficient) {
59
- const shortfall = burnAmount - onHand;
58
+ const shortfall = sufficient ? BigInt(0) : burnAmount - onHand;
59
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_INFO, burnAmount, onHand, sufficient, ueaAddress, pathTag, {
60
+ kind: 'PRC20',
61
+ burnToken,
62
+ segmentIndex,
63
+ enforceGasCheck,
64
+ shortfall,
65
+ });
66
+ if (!sufficient && enforceGasCheck) {
60
67
  (0, context_1.fireProgressHook)(ctx, HOOK_PRE_FAIL, burnAmount, onHand, shortfall, ueaAddress, pathTag, { kind: 'PRC20', burnToken, segmentIndex });
61
68
  throw new errors_1.InsufficientUEABalanceError({
62
69
  required: burnAmount,
@@ -69,25 +76,32 @@ function runPreflight(opts) {
69
76
  segmentIndex,
70
77
  });
71
78
  }
79
+ else if (!sufficient) {
80
+ (0, context_1.printLog)(ctx, `[preflight ${pathTag}] PRC-20 shortfall ${shortfall.toString()} for ${burnToken}; proceeding because enforceGasCheck=false`);
81
+ }
72
82
  }
73
83
  // 2. Native (UPC) balance check.
74
84
  const totalRequired = requiredValue + gasReserve;
75
85
  const sufficient = ueaBalance >= totalRequired;
76
- (0, context_1.fireProgressHook)(ctx, HOOK_PRE_INFO, totalRequired, ueaBalance, sufficient, ueaAddress, pathTag, { kind: 'NATIVE', segmentIndex });
86
+ const shortfall = sufficient ? BigInt(0) : totalRequired - ueaBalance;
87
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_INFO, totalRequired, ueaBalance, sufficient, ueaAddress, pathTag, { kind: 'NATIVE', segmentIndex, enforceGasCheck, shortfall });
77
88
  if (sufficient) {
78
89
  return { ok: true, adjustedValue: requiredValue };
79
90
  }
80
- const shortfall = totalRequired - ueaBalance;
81
- (0, context_1.fireProgressHook)(ctx, HOOK_PRE_FAIL, totalRequired, ueaBalance, shortfall, ueaAddress, pathTag, { kind: 'NATIVE', segmentIndex });
82
- throw new errors_1.InsufficientUEABalanceError({
83
- required: totalRequired,
84
- available: ueaBalance,
85
- shortfall,
86
- ueaAddress,
87
- pathTag,
88
- reason: 'NATIVE',
89
- segmentIndex,
90
- });
91
+ if (enforceGasCheck) {
92
+ (0, context_1.fireProgressHook)(ctx, HOOK_PRE_FAIL, totalRequired, ueaBalance, shortfall, ueaAddress, pathTag, { kind: 'NATIVE', segmentIndex });
93
+ throw new errors_1.InsufficientUEABalanceError({
94
+ required: totalRequired,
95
+ available: ueaBalance,
96
+ shortfall,
97
+ ueaAddress,
98
+ pathTag,
99
+ reason: 'NATIVE',
100
+ segmentIndex,
101
+ });
102
+ }
103
+ (0, context_1.printLog)(ctx, `[preflight ${pathTag}] native shortfall ${shortfall.toString()}; proceeding because enforceGasCheck=false`);
104
+ return { ok: true, adjustedValue: requiredValue };
91
105
  }
92
106
  /**
93
107
  * SVM-only telemetry threshold. Buffered pool quotes above this value
@@ -312,7 +312,7 @@ function executeMultiChain(ctx, params, executeFn) {
312
312
  */
313
313
  function executeUoaToCea(ctx, params, executeFn) {
314
314
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
315
- var _a, _b, _c, _d;
315
+ var _a, _b, _c, _d, _e;
316
316
  const target = params.to;
317
317
  const targetChain = target.chain;
318
318
  const targetAddress = target.address;
@@ -418,7 +418,7 @@ function executeUoaToCea(ctx, params, executeFn) {
418
418
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, gasLimitForQuery, ceaPayload, ueaAddress, // revert recipient is the UEA
419
419
  (_d = params.maxPCForGas) !== null && _d !== void 0 ? _d : BigInt(0), outboundGasPrice);
420
420
  (0, context_1.printLog)(ctx, `executeUoaToCea — outbound request: ${JSON.stringify({
421
- target: outboundReq.target,
421
+ recipient: outboundReq.recipient,
422
422
  token: outboundReq.token,
423
423
  amount: outboundReq.amount.toString(),
424
424
  gasLimit: outboundReq.gasLimit.toString(),
@@ -480,10 +480,9 @@ function executeUoaToCea(ctx, params, executeFn) {
480
480
  (0, context_1.printLog)(ctx, `executeUoaToCea — capping nativeValueForGas at 200 UPC ceiling (was ${nativeValueForGas.toString()})`);
481
481
  nativeValueForGas = EVM_NATIVE_VALUE_SAFETY_CAP;
482
482
  }
483
- // Pre-flight: throws InsufficientUEABalanceError when UEA cannot cover
484
- // (nativeValueForGas + gasReserve) or the PRC-20 burn amount. Replaces the
485
- // legacy silent-clamp that previously submitted under-funded swaps and
486
- // bubbled up as Uniswap STF reverts. PRC-20 check fires whenever
483
+ // Pre-flight: warns by default when UEA cannot cover (nativeValueForGas +
484
+ // gasReserve) or the PRC-20 burn amount; throws only when
485
+ // options.enforceGasCheck=true. PRC-20 check fires whenever
487
486
  // burnAmount > 0 — user must pre-hold the burn token, even when it equals
488
487
  // the gas token (the gateway does transferFrom BEFORE the gas swap).
489
488
  let prc20BalanceR2Evm;
@@ -505,6 +504,7 @@ function executeUoaToCea(ctx, params, executeFn) {
505
504
  burnToken: prc20Token,
506
505
  burnAmount,
507
506
  prc20Balance: prc20BalanceR2Evm,
507
+ enforceGasCheck: ((_e = params.options) === null || _e === void 0 ? void 0 : _e.enforceGasCheck) === true,
508
508
  });
509
509
  // Preflight success means nativeValueForGas already covers required + reserve; no adjust needed.
510
510
  // Build outbound multicalls (approve burn + sendUniversalTxOutbound with native value)
@@ -568,7 +568,7 @@ function executeUoaToCea(ctx, params, executeFn) {
568
568
  */
569
569
  function executeUoaToCeaSvm(ctx, params, target, executeFn) {
570
570
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
571
- var _a, _b, _c, _d, _e, _f, _g, _h;
571
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
572
572
  const targetChain = target.chain;
573
573
  const targetAddress = target.address; // 0x-prefixed, 32 bytes
574
574
  const ueaAddress = (0, uea_manager_1.computeUEAOffchain)(ctx);
@@ -656,7 +656,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
656
656
  const outboundReq = (0, payload_builders_1.buildOutboundRequest)(targetBytes, prc20Token, burnAmount, effectiveGasLimit, svmPayload, ueaAddress, // revert recipient is the UEA
657
657
  (_f = params.maxPCForGas) !== null && _f !== void 0 ? _f : BigInt(0), outboundGasPriceSvm);
658
658
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — outbound request: ${JSON.stringify({
659
- target: outboundReq.target,
659
+ recipient: outboundReq.recipient,
660
660
  token: outboundReq.token,
661
661
  amount: outboundReq.amount.toString(),
662
662
  gasLimit: outboundReq.gasLimit.toString(),
@@ -710,10 +710,9 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
710
710
  (0, context_1.printLog)(ctx, `executeUoaToCeaSvm — nativeValueForGas=${nativeValueForGas.toString()} (includes 2.2x buffer)`);
711
711
  (0, preflight_1.maybeFireSvmWarnThreshold)(ctx, nativeValueForGas, gasToken, 'R2_SVM');
712
712
  }
713
- // Pre-flight: throws InsufficientUEABalanceError when UEA cannot cover
714
- // (nativeValueForGas + SVM_GAS_RESERVE) or the PRC-20 burn amount.
715
- // Replaces the legacy silent-clamp that previously submitted under-funded
716
- // swaps and bubbled up as Uniswap STF reverts.
713
+ // Pre-flight: warns by default when UEA cannot cover (nativeValueForGas +
714
+ // SVM_GAS_RESERVE) or the PRC-20 burn amount; throws only when
715
+ // options.enforceGasCheck=true.
717
716
  // PRC-20 burn-balance pre-check fires whenever `burnAmount > 0`, regardless
718
717
  // of whether `prc20Token` happens to equal `gasToken`. The gateway's
719
718
  // `sendUniversalTxOutbound` does `transferFrom(user, gateway, burnAmount)`
@@ -739,6 +738,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
739
738
  burnToken: prc20Token,
740
739
  burnAmount,
741
740
  prc20Balance: prc20BalanceR2Svm,
741
+ enforceGasCheck: ((_j = params.options) === null || _j === void 0 ? void 0 : _j.enforceGasCheck) === true,
742
742
  });
743
743
  // --- Build Push Chain multicalls (approve + sendUniversalTxOutbound) ---
744
744
  // Reuse the same builder as EVM — this part is identical
@@ -804,7 +804,7 @@ function executeUoaToCeaSvm(ctx, params, target, executeFn) {
804
804
  */
805
805
  function executeCeaToPush(ctx, params, executeFn) {
806
806
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
807
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
807
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
808
808
  // 1. Validate and extract source chain
809
809
  if (!((_a = params.from) === null || _a === void 0 ? void 0 : _a.chain)) {
810
810
  throw new Error('Route 3 (CEA -> Push) requires from.chain to specify the source CEA chain');
@@ -936,15 +936,15 @@ function executeCeaToPush(ctx, params, executeFn) {
936
936
  const outboundGasLimit = (_f = params.gasLimit) !== null && _f !== void 0 ? _f : selectors_1.DEFAULT_CEA_TO_PUSH_GAS_LIMIT;
937
937
  (0, context_1.printLog)(ctx, `executeCeaToPush — prc20Token: ${prc20Token}, burnAmount: ${burnAmount.toString()}`);
938
938
  // 9. Build outbound request (same structure as Route 2)
939
- // target = CEA address (for self-execution), value = 0 in payload
940
- let outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // target: CEA address (to=CEA for self-execution)
939
+ // recipient = CEA address (for self-execution), value = 0 in payload
940
+ let outboundReq = (0, payload_builders_1.buildOutboundRequest)(ceaAddress, // recipient: CEA address (to=CEA for self-execution)
941
941
  prc20Token, // token: native PRC-20 for source chain (for namespace lookup)
942
942
  burnAmount, // amount: 0 (payload-only, CEA uses its own balance)
943
943
  outboundGasLimit, ceaPayload, // payload: ABI-encoded CEA multicall
944
944
  ueaAddress, // revertRecipient: UEA
945
945
  (_g = params.maxPCForGas) !== null && _g !== void 0 ? _g : BigInt(0));
946
946
  (0, context_1.printLog)(ctx, `executeCeaToPush — outbound request: ${JSON.stringify({
947
- target: outboundReq.target,
947
+ recipient: outboundReq.recipient,
948
948
  token: outboundReq.token,
949
949
  amount: outboundReq.amount.toString(),
950
950
  gasLimit: outboundReq.gasLimit.toString(),
@@ -1047,7 +1047,8 @@ function executeCeaToPush(ctx, params, executeFn) {
1047
1047
  }
1048
1048
  // Pre-flight (R3 EVM): no PRC-20 burn check — `burnAmount = 0` makes this
1049
1049
  // payload-only (UEA never holds the source-chain PRC-20; see plan §9 #4).
1050
- // Native UPC check still applies for the gas swap.
1050
+ // Native UPC check still applies for the gas swap. It warns by default and
1051
+ // throws only when options.enforceGasCheck=true.
1051
1052
  (0, preflight_1.runPreflight)({
1052
1053
  ctx,
1053
1054
  ueaAddress,
@@ -1055,6 +1056,7 @@ function executeCeaToPush(ctx, params, executeFn) {
1055
1056
  requiredValue: nativeValueForGas,
1056
1057
  gasReserve: OUTBOUND_GAS_RESERVE_R3,
1057
1058
  pathTag: 'R3_EVM',
1059
+ enforceGasCheck: ((_l = params.options) === null || _l === void 0 ? void 0 : _l.enforceGasCheck) === true,
1058
1060
  });
1059
1061
  // 12. Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
1060
1062
  const pushChainMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
@@ -1120,7 +1122,7 @@ function executeCeaToPush(ctx, params, executeFn) {
1120
1122
  */
1121
1123
  function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1122
1124
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
1123
- var _a, _b, _c, _d, _e;
1125
+ var _a, _b, _c, _d, _e, _f;
1124
1126
  if (typeof params.to !== 'string') {
1125
1127
  throw new Error('Route 3 SVM (CEA -> Push): to must be a Push Chain address (string), not a ChainTarget');
1126
1128
  }
@@ -1170,9 +1172,9 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1170
1172
  // on the cascade path, and the same precompile is used here.
1171
1173
  const burnAmount = BigInt(0);
1172
1174
  let effectiveGasLimit = (_a = params.gasLimit) !== null && _a !== void 0 ? _a : BigInt(0);
1173
- // Build outbound request: target = gateway program (self-call)
1175
+ // Build outbound request: recipient = gateway program (self-call)
1174
1176
  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}`);
1177
+ (0, context_1.printLog)(ctx, `executeCeaToPushSvm — outbound request: recipient=${outboundReq.recipient.slice(0, 20)}..., token=${outboundReq.token}, maxPCForGas=${outboundReq.maxPCForGas}`);
1176
1178
  // Query gas fees
1177
1179
  let gasFee = BigInt(0);
1178
1180
  let protocolFeeR3Svm = BigInt(0);
@@ -1245,13 +1247,13 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1245
1247
  : UPWARD_BASE_CEILING_R3_SVM;
1246
1248
  // Upward-only allocation: when balance has headroom over the calibrated
1247
1249
  // value, consume more of it (up to the ceiling above). Down-clamp +
1248
- // zero-balance throws live in pre-flight.
1250
+ // zero-balance handling lives in pre-flight.
1249
1251
  // Ordering note: pre-flight runs AFTER the upward adjust below; the
1250
1252
  // semantics are equivalent to running it before — when upward adjust
1251
1253
  // applies it sets nativeValueForGas to balance-reserve (capped), and the
1252
1254
  // post-adjust pre-flight check is always satisfied. When upward adjust is
1253
1255
  // skipped (balance too low), pre-flight runs against the calibrated value
1254
- // and throws cleanly on shortfall.
1256
+ // and either warns or throws based on options.enforceGasCheck.
1255
1257
  if (currentBalance > OUTBOUND_GAS_RESERVE_R3_SVM &&
1256
1258
  currentBalance - OUTBOUND_GAS_RESERVE_R3_SVM > nativeValueForGas) {
1257
1259
  const balanceDriven = currentBalance - OUTBOUND_GAS_RESERVE_R3_SVM;
@@ -1273,6 +1275,7 @@ function executeCeaToPushSvm(ctx, params, sourceChain, executeFn) {
1273
1275
  requiredValue: nativeValueForGas,
1274
1276
  gasReserve: OUTBOUND_GAS_RESERVE_R3_SVM,
1275
1277
  pathTag: 'R3_SVM',
1278
+ enforceGasCheck: ((_f = params.options) === null || _f === void 0 ? void 0 : _f.enforceGasCheck) === true,
1276
1279
  });
1277
1280
  // Build Push Chain multicalls (approvals + sendUniversalTxOutbound)
1278
1281
  const pushChainMulticalls = (0, payload_builders_1.buildOutboundApprovalAndCall)({
@@ -97,6 +97,14 @@ function gasLimitForSvmGasFeeBudget(gasFeeBudget, gasPrice) {
97
97
  }
98
98
  return (gasFeeBudget + gasPrice - BigInt(1)) / gasPrice;
99
99
  }
100
+ function resolveSvmGasLimitUsed(quote) {
101
+ if (quote.gasLimitUsed > BigInt(0) ||
102
+ quote.gasFee <= BigInt(0) ||
103
+ quote.gasPrice <= BigInt(0)) {
104
+ return quote;
105
+ }
106
+ return Object.assign(Object.assign({}, quote), { gasLimitUsed: gasLimitForSvmGasFeeBudget(quote.gasFee, quote.gasPrice) });
107
+ }
100
108
  function ensureSvmFinalizeGasBudgetQuote(_a) {
101
109
  return tslib_1.__awaiter(this, arguments, void 0, function* ({ ctx, ueaAddress, targetChain, prc20Token, quote, splMintBase58, burnAmount, pathTag, }) {
102
110
  const minGasFee = yield getSvmFinalizeGasBudget({
@@ -107,9 +115,10 @@ function ensureSvmFinalizeGasBudgetQuote(_a) {
107
115
  burnAmount,
108
116
  });
109
117
  if (quote.gasFee >= minGasFee) {
110
- (0, context_1.printLog)(ctx, `${pathTag} — SVM finalize gas budget ok: gasFee=${quote.gasFee.toString()}, ` +
111
- `minimum=${minGasFee.toString()}, gasLimit=${quote.gasLimitUsed.toString()}`);
112
- return quote;
118
+ const resolvedQuote = resolveSvmGasLimitUsed(quote);
119
+ (0, context_1.printLog)(ctx, `${pathTag} — SVM finalize gas budget ok: gasFee=${resolvedQuote.gasFee.toString()}, ` +
120
+ `minimum=${minGasFee.toString()}, gasLimit=${resolvedQuote.gasLimitUsed.toString()}`);
121
+ return resolvedQuote;
113
122
  }
114
123
  const bumpedGasLimit = gasLimitForSvmGasFeeBudget(minGasFee, quote.gasPrice);
115
124
  (0, context_1.printLog)(ctx, `${pathTag} — bumping SVM gasLimit ${quote.gasLimitUsed.toString()} → ${bumpedGasLimit.toString()}; ` +
@@ -119,7 +128,7 @@ function ensureSvmFinalizeGasBudgetQuote(_a) {
119
128
  (0, context_1.printLog)(ctx, `${pathTag} — WARNING: bumped SVM gasFee=${bumpedQuote.gasFee.toString()} ` +
120
129
  `still below finalize minimum=${minGasFee.toString()}`);
121
130
  }
122
- return bumpedQuote;
131
+ return resolveSvmGasLimitUsed(bumpedQuote);
123
132
  });
124
133
  }
125
134
  /**
@@ -4,7 +4,7 @@ import type { AccountStatus, UniversalTxResponse } from './orchestrator.types';
4
4
  import { ProgressEvent } from '../progress-hook/progress-hook.types';
5
5
  import { PushClient } from '../push-client/push-client';
6
6
  import type { UniversalAccount, UniversalSigner } from '../universal/universal.types';
7
- import { ExecuteParams, UniversalExecuteParams, PreparedUniversalTx, CascadedTxResponse, RescueFundsParams } from './orchestrator.types';
7
+ import { ExecuteParams, UniversalExecuteParams, PreparedUniversalTx, CascadedTxResponse, CascadeExecutionOptions, TransactionExecutionOptions, RescueFundsParams } from './orchestrator.types';
8
8
  import { TransactionRoute } from './route-detector';
9
9
  export declare class Orchestrator {
10
10
  private readonly universalSigner;
@@ -71,15 +71,16 @@ export declare class Orchestrator {
71
71
  * Routes 2-4 (multi-chain) delegate to prepareTransaction().send().
72
72
  * Route 1 (Push Chain) dispatches to extracted sub-flow modules.
73
73
  */
74
- execute(params: ExecuteParams | UniversalExecuteParams): Promise<UniversalTxResponse>;
74
+ execute(params: ExecuteParams | UniversalExecuteParams, options?: TransactionExecutionOptions): Promise<UniversalTxResponse>;
75
75
  /**
76
76
  * Prepare a universal transaction without executing it.
77
77
  * Returns a PreparedUniversalTx that can be chained with thenOn() or sent with send().
78
78
  *
79
79
  * @param params - Universal execution parameters
80
+ * @param options - Optional transaction behavior switches.
80
81
  * @returns PreparedUniversalTx with chaining capabilities
81
82
  */
82
- prepareTransaction(params: UniversalExecuteParams): Promise<PreparedUniversalTx>;
83
+ prepareTransaction(params: UniversalExecuteParams, options?: TransactionExecutionOptions): Promise<PreparedUniversalTx>;
83
84
  /**
84
85
  * Creates a cascaded transaction builder for nested multi-chain execution.
85
86
  * The cascade composes all hops bottom-to-top into a single Push Chain tx.
@@ -91,9 +92,7 @@ export declare class Orchestrator {
91
92
  * tracking). Dedups by reference if it matches the init-time hook.
92
93
  * @returns Object with send() method
93
94
  */
94
- createCascadedBuilder(preparedTxs: PreparedUniversalTx[], options?: {
95
- progressHook?: (event: ProgressEvent) => void;
96
- }): {
95
+ createCascadedBuilder(preparedTxs: PreparedUniversalTx[], options?: CascadeExecutionOptions): {
97
96
  send: () => Promise<CascadedTxResponse>;
98
97
  };
99
98
  /**
@@ -129,9 +129,12 @@ class Orchestrator {
129
129
  * Routes 2-4 (multi-chain) delegate to prepareTransaction().send().
130
130
  * Route 1 (Push Chain) dispatches to extracted sub-flow modules.
131
131
  */
132
- execute(params) {
132
+ execute(params, options) {
133
133
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
134
134
  var _a, _b, _c, _d;
135
+ if (options) {
136
+ params = Object.assign(Object.assign({}, params), { options: Object.assign(Object.assign({}, params.options), options) });
137
+ }
135
138
  // Snapshot the caller's active route — `executeMultiChain` recurses into
136
139
  // this same method for R2/R3's inner Push-side execution, and without
137
140
  // snapshot/restore the recursive `detectRoute(params)` would clobber the
@@ -285,10 +288,14 @@ class Orchestrator {
285
288
  * Returns a PreparedUniversalTx that can be chained with thenOn() or sent with send().
286
289
  *
287
290
  * @param params - Universal execution parameters
291
+ * @param options - Optional transaction behavior switches.
288
292
  * @returns PreparedUniversalTx with chaining capabilities
289
293
  */
290
- prepareTransaction(params) {
294
+ prepareTransaction(params, options) {
291
295
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
296
+ if (options) {
297
+ params = Object.assign(Object.assign({}, params), { options: Object.assign(Object.assign({}, params.options), options) });
298
+ }
292
299
  return (0, internals_1.prepareTransaction)(this.ctx, params, this._getCascadeCallbacks());
293
300
  });
294
301
  }
@@ -306,7 +313,7 @@ class Orchestrator {
306
313
  createCascadedBuilder(preparedTxs, options) {
307
314
  const perCallHook = options === null || options === void 0 ? void 0 : options.progressHook;
308
315
  if (!perCallHook) {
309
- return (0, internals_1.createCascadedBuilder)(this.ctx, preparedTxs, this._getCascadeCallbacks());
316
+ return (0, internals_1.createCascadedBuilder)(this.ctx, preparedTxs, this._getCascadeCallbacks(), undefined);
310
317
  }
311
318
  // Wrap ctx.progressHook for the synchronous send() phase — captures every
312
319
  // fireProgressHook(ctx, ...) call during pre-flight, composeCascade, and
@@ -31,6 +31,14 @@ export type UniversalTo = `0x${string}` | ChainTarget;
31
31
  * Transaction route identifiers
32
32
  */
33
33
  export type TransactionRouteType = 'UOA_TO_PUSH' | 'UOA_TO_CEA' | 'CEA_TO_PUSH' | 'CEA_TO_CEA';
34
+ export interface TransactionExecutionOptions {
35
+ /**
36
+ * Enforce the SDK pre-flight gas/balance check.
37
+ * - false/omitted: emit a warning on shortfall and continue
38
+ * - true: emit the failure hook, throw InsufficientUEABalanceError, and stop
39
+ */
40
+ enforceGasCheck?: boolean;
41
+ }
34
42
  export type ExecuteParams = {
35
43
  /**
36
44
  * The target contract or account on Push Chain.
@@ -80,6 +88,10 @@ export type ExecuteParams = {
80
88
  * Optional for signature expiry
81
89
  */
82
90
  deadline?: bigint;
91
+ /**
92
+ * Optional transaction behavior switches.
93
+ */
94
+ options?: TransactionExecutionOptions;
83
95
  /**
84
96
  * Optional: pay gas in a specific token. If not provided, use `token` (bridge token) when present; otherwise, native token.
85
97
  */
@@ -432,7 +444,7 @@ export interface SvmExecutePayloadFields {
432
444
  * Request structure for sendUniversalTxOutbound on Push Chain
433
445
  * Used for Routes 2, 3, 4 (outbound from Push)
434
446
  *
435
- * NOTE: The `target` field is raw recipient bytes for the destination chain.
447
+ * NOTE: The `recipient` field is raw recipient bytes for the destination chain.
436
448
  * For EVM CEA funds-parking, use the zero recipient when `payload` is empty.
437
449
  */
438
450
  export interface UniversalOutboundTxRequest {
@@ -440,7 +452,7 @@ export interface UniversalOutboundTxRequest {
440
452
  * Raw destination recipient bytes. EVM routes usually pass a 20-byte address;
441
453
  * SVM routes pass a 32-byte program/account address.
442
454
  */
443
- target: `0x${string}`;
455
+ recipient: `0x${string}`;
444
456
  /** PRC20 token address on Push Chain to burn */
445
457
  token: `0x${string}`;
446
458
  /** Amount to burn (0 for no-burn, use existing CEA balance) */
@@ -662,6 +674,10 @@ export interface CascadeTrackOptions {
662
674
  */
663
675
  eventHook?: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void;
664
676
  }
677
+ export interface CascadeExecutionOptions {
678
+ /** Per-call progress callback. */
679
+ progressHook?: (event: import('../progress-hook/progress-hook.types').ProgressEvent) => void;
680
+ }
665
681
  /**
666
682
  * Progress event emitted during cascade tracking
667
683
  */
@@ -70,8 +70,8 @@ export declare function buildSendUniversalTxToUEA(ceaAddress: `0x${string}`, tok
70
70
  /**
71
71
  * Build UniversalOutboundTxRequest for Push Chain outbound
72
72
  *
73
- * @param target - Raw recipient bytes for the destination chain. For EVM CEA
74
- * funds-parking, use the zero recipient when payload is empty.
73
+ * @param recipient - Raw recipient bytes for the destination chain. For EVM CEA
74
+ * funds-parking, use the zero recipient when payload is empty.
75
75
  * @param prc20Token - PRC20 token address to burn (or address(0) for native)
76
76
  * @param amount - Amount to burn
77
77
  * @param gasLimit - Gas limit for fee calculation
@@ -81,7 +81,7 @@ export declare function buildSendUniversalTxToUEA(ceaAddress: `0x${string}`, tok
81
81
  * @param gasPrice - Destination-chain gas price override (0 = UniversalCore default)
82
82
  * @returns UniversalOutboundTxRequest object
83
83
  */
84
- export declare function buildOutboundRequest(target: `0x${string}`, prc20Token: `0x${string}`, amount: bigint, gasLimit: bigint, payload: `0x${string}`, revertRecipient: `0x${string}`, maxPCForGas?: bigint, gasPrice?: bigint): UniversalOutboundTxRequest;
84
+ export declare function buildOutboundRequest(recipient: `0x${string}`, prc20Token: `0x${string}`, amount: bigint, gasLimit: bigint, payload: `0x${string}`, revertRecipient: `0x${string}`, maxPCForGas?: bigint, gasPrice?: bigint): UniversalOutboundTxRequest;
85
85
  export declare function assertCeaFundsParkingInvariant(recipient: `0x${string}`, payload: `0x${string}`): void;
86
86
  /**
87
87
  * Build ERC20 transfer multicall
@@ -226,8 +226,8 @@ function buildSendUniversalTxToUEA(ceaAddress, token, amount, payload, revertRec
226
226
  /**
227
227
  * Build UniversalOutboundTxRequest for Push Chain outbound
228
228
  *
229
- * @param target - Raw recipient bytes for the destination chain. For EVM CEA
230
- * funds-parking, use the zero recipient when payload is empty.
229
+ * @param recipient - Raw recipient bytes for the destination chain. For EVM CEA
230
+ * funds-parking, use the zero recipient when payload is empty.
231
231
  * @param prc20Token - PRC20 token address to burn (or address(0) for native)
232
232
  * @param amount - Amount to burn
233
233
  * @param gasLimit - Gas limit for fee calculation
@@ -237,9 +237,9 @@ function buildSendUniversalTxToUEA(ceaAddress, token, amount, payload, revertRec
237
237
  * @param gasPrice - Destination-chain gas price override (0 = UniversalCore default)
238
238
  * @returns UniversalOutboundTxRequest object
239
239
  */
240
- function buildOutboundRequest(target, prc20Token, amount, gasLimit, payload, revertRecipient, maxPCForGas = BigInt(0), gasPrice = BigInt(0)) {
240
+ function buildOutboundRequest(recipient, prc20Token, amount, gasLimit, payload, revertRecipient, maxPCForGas = BigInt(0), gasPrice = BigInt(0)) {
241
241
  return {
242
- target,
242
+ recipient,
243
243
  token: prc20Token,
244
244
  amount,
245
245
  gasLimit,
@@ -23,6 +23,18 @@ const friendlyChain = (chainOrNs) => {
23
23
  const key = utils_1.Utils.chains.getChainName(asString);
24
24
  return key ? toTitleCase(key) : asString;
25
25
  };
26
+ function preflightLevel(sufficient, enforceGasCheck) {
27
+ if (sufficient)
28
+ return 'INFO';
29
+ return enforceGasCheck === false ? 'WARNING' : 'ERROR';
30
+ }
31
+ function preflightSuffix(sufficient, pathTag, enforceGasCheck) {
32
+ if (sufficient)
33
+ return `sufficient (${pathTag})`;
34
+ return enforceGasCheck === false
35
+ ? `INSUFFICIENT (${pathTag}); warning only, proceeding`
36
+ : `INSUFFICIENT (${pathTag})`;
37
+ }
26
38
  /**
27
39
  * Classify a sign-time error as either a true wallet user-decline
28
40
  * (viem `UserRejectedRequestError`, ethers `ACTION_REJECTED`, EIP-1193 4001,
@@ -307,15 +319,16 @@ const RAW_HOOKS_R2 = {
307
319
  level: 'SUCCESS',
308
320
  }),
309
321
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_03]: (required, available, sufficient, ueaAddress, pathTag, extra) => {
310
- var _a, _b, _c;
322
+ var _a, _b, _c, _d;
311
323
  const kind = (_a = extra === null || extra === void 0 ? void 0 : extra.kind) !== null && _a !== void 0 ? _a : 'NATIVE';
312
324
  const availableStr = kind === 'PRC20' ? `${available} units` : (0, formatters_1.formatPc)(available, 4);
313
325
  const requiredStr = kind === 'PRC20' ? `${required} units` : (0, formatters_1.formatPc)(required, 4);
326
+ const enforceGasCheck = extra === null || extra === void 0 ? void 0 : extra.enforceGasCheck;
314
327
  return {
315
328
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_03,
316
329
  title: 'Pre-flight Balance Check',
317
330
  message: `UEA balance: ${availableStr}; required: ${requiredStr}; ` +
318
- `${sufficient ? 'sufficient' : 'INSUFFICIENT'} (${pathTag})`,
331
+ preflightSuffix(sufficient, pathTag, enforceGasCheck),
319
332
  response: {
320
333
  required,
321
334
  available,
@@ -325,8 +338,11 @@ const RAW_HOOKS_R2 = {
325
338
  kind,
326
339
  burnToken: (_b = extra === null || extra === void 0 ? void 0 : extra.burnToken) !== null && _b !== void 0 ? _b : null,
327
340
  segmentIndex: (_c = extra === null || extra === void 0 ? void 0 : extra.segmentIndex) !== null && _c !== void 0 ? _c : null,
341
+ enforceGasCheck: enforceGasCheck !== null && enforceGasCheck !== void 0 ? enforceGasCheck : null,
342
+ warningOnly: !sufficient && enforceGasCheck === false,
343
+ shortfall: (_d = extra === null || extra === void 0 ? void 0 : extra.shortfall) !== null && _d !== void 0 ? _d : null,
328
344
  },
329
- level: sufficient ? 'INFO' : 'ERROR',
345
+ level: preflightLevel(sufficient, enforceGasCheck),
330
346
  };
331
347
  },
332
348
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_203_04]: (required, available, shortfall, ueaAddress, pathTag, extra) => {
@@ -521,15 +537,16 @@ const RAW_HOOKS_R3 = {
521
537
  }),
522
538
  // R3 single-route pre-flight balance check. Same payload shape as 203-03/04/05.
523
539
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_04]: (required, available, sufficient, ueaAddress, pathTag, extra) => {
524
- var _a, _b, _c;
540
+ var _a, _b, _c, _d;
525
541
  const kind = (_a = extra === null || extra === void 0 ? void 0 : extra.kind) !== null && _a !== void 0 ? _a : 'NATIVE';
526
542
  const availableStr = kind === 'PRC20' ? `${available} units` : (0, formatters_1.formatPc)(available, 4);
527
543
  const requiredStr = kind === 'PRC20' ? `${required} units` : (0, formatters_1.formatPc)(required, 4);
544
+ const enforceGasCheck = extra === null || extra === void 0 ? void 0 : extra.enforceGasCheck;
528
545
  return {
529
546
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_04,
530
547
  title: 'Pre-flight Balance Check',
531
548
  message: `UEA balance: ${availableStr}; required: ${requiredStr}; ` +
532
- `${sufficient ? 'sufficient' : 'INSUFFICIENT'} (${pathTag})`,
549
+ preflightSuffix(sufficient, pathTag, enforceGasCheck),
533
550
  response: {
534
551
  required,
535
552
  available,
@@ -539,8 +556,11 @@ const RAW_HOOKS_R3 = {
539
556
  kind,
540
557
  burnToken: (_b = extra === null || extra === void 0 ? void 0 : extra.burnToken) !== null && _b !== void 0 ? _b : null,
541
558
  segmentIndex: (_c = extra === null || extra === void 0 ? void 0 : extra.segmentIndex) !== null && _c !== void 0 ? _c : null,
559
+ enforceGasCheck: enforceGasCheck !== null && enforceGasCheck !== void 0 ? enforceGasCheck : null,
560
+ warningOnly: !sufficient && enforceGasCheck === false,
561
+ shortfall: (_d = extra === null || extra === void 0 ? void 0 : extra.shortfall) !== null && _d !== void 0 ? _d : null,
542
562
  },
543
- level: sufficient ? 'INFO' : 'ERROR',
563
+ level: preflightLevel(sufficient, enforceGasCheck),
544
564
  };
545
565
  },
546
566
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_303_05]: (required, available, shortfall, ueaAddress, pathTag, extra) => {
@@ -785,15 +805,16 @@ const RAW_HOOKS_MULTICHAIN = {
785
805
  // streams remain self-consistent. `runPreflight` switches between
786
806
  // 203-xx and 003-xx based on pathTag === 'CASCADE'.
787
807
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03]: (required, available, sufficient, ueaAddress, pathTag, extra) => {
788
- var _a, _b, _c;
808
+ var _a, _b, _c, _d;
789
809
  const kind = (_a = extra === null || extra === void 0 ? void 0 : extra.kind) !== null && _a !== void 0 ? _a : 'NATIVE';
790
810
  const availableStr = kind === 'PRC20' ? `${available} units` : (0, formatters_1.formatPc)(available, 4);
791
811
  const requiredStr = kind === 'PRC20' ? `${required} units` : (0, formatters_1.formatPc)(required, 4);
812
+ const enforceGasCheck = extra === null || extra === void 0 ? void 0 : extra.enforceGasCheck;
792
813
  return {
793
814
  id: progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_03,
794
815
  title: 'Cascade Pre-flight Balance Check',
795
816
  message: `UEA balance: ${availableStr}; required: ${requiredStr}; ` +
796
- `${sufficient ? 'sufficient' : 'INSUFFICIENT'} (${pathTag})`,
817
+ preflightSuffix(sufficient, pathTag, enforceGasCheck),
797
818
  response: {
798
819
  required,
799
820
  available,
@@ -803,8 +824,11 @@ const RAW_HOOKS_MULTICHAIN = {
803
824
  kind,
804
825
  burnToken: (_b = extra === null || extra === void 0 ? void 0 : extra.burnToken) !== null && _b !== void 0 ? _b : null,
805
826
  segmentIndex: (_c = extra === null || extra === void 0 ? void 0 : extra.segmentIndex) !== null && _c !== void 0 ? _c : null,
827
+ enforceGasCheck: enforceGasCheck !== null && enforceGasCheck !== void 0 ? enforceGasCheck : null,
828
+ warningOnly: !sufficient && enforceGasCheck === false,
829
+ shortfall: (_d = extra === null || extra === void 0 ? void 0 : extra.shortfall) !== null && _d !== void 0 ? _d : null,
806
830
  },
807
- level: sufficient ? 'INFO' : 'ERROR',
831
+ level: preflightLevel(sufficient, enforceGasCheck),
808
832
  };
809
833
  },
810
834
  [progress_hook_types_1.PROGRESS_HOOK.SEND_TX_003_04]: (required, available, shortfall, ueaAddress, pathTag, extra) => {
@@ -7,7 +7,7 @@ export type ProgressEvent = {
7
7
  id: string;
8
8
  title: string;
9
9
  message: string;
10
- level: 'INFO' | 'SUCCESS' | 'ERROR';
10
+ level: 'INFO' | 'SUCCESS' | 'WARNING' | 'ERROR';
11
11
  response: object | null;
12
12
  timestamp: string;
13
13
  };
@@ -68,7 +68,7 @@ export declare enum PROGRESS_HOOK {
68
68
  SEND_TX_303_03_01 = "SEND-TX-303-03-01",// Sizer: Case A (< $1, padded)
69
69
  SEND_TX_303_03_02 = "SEND-TX-303-03-02",// Sizer: Case B ($1–$10, happy path)
70
70
  SEND_TX_303_03_03 = "SEND-TX-303-03-03",// Sizer: Case C (> $10, split + overflow bridge)
71
- SEND_TX_303_04 = "SEND-TX-303-04",// R3 pre-flight balance check (INFO when sufficient, ERROR when insufficient)
71
+ SEND_TX_303_04 = "SEND-TX-303-04",// R3 pre-flight balance check (INFO/WARNING/ERROR per enforceGasCheck)
72
72
  SEND_TX_303_05 = "SEND-TX-303-05",// R3 pre-flight FAILED (ERROR, terminal precursor before 399-02)
73
73
  SEND_TX_303_06 = "SEND-TX-303-06",// R3 SVM warn-threshold tripped (INFO, no action)
74
74
  SEND_TX_304_01 = "SEND-TX-304-01",
@@ -87,7 +87,7 @@ export declare enum PROGRESS_HOOK {
87
87
  SEND_TX_001 = "SEND-TX-001",
88
88
  SEND_TX_002_01 = "SEND-TX-002-01",
89
89
  SEND_TX_002_99_99 = "SEND-TX-002-99-99",
90
- SEND_TX_003_03 = "SEND-TX-003-03",// Cascade pre-flight balance check (INFO when sufficient, ERROR when insufficient)
90
+ SEND_TX_003_03 = "SEND-TX-003-03",// Cascade pre-flight balance check (INFO/WARNING/ERROR per enforceGasCheck)
91
91
  SEND_TX_003_04 = "SEND-TX-003-04",// Cascade pre-flight FAILED (ERROR, terminal precursor before 999-02)
92
92
  SEND_TX_003_05 = "SEND-TX-003-05",// Cascade SVM warn-threshold tripped (INFO, no action)
93
93
  SEND_TX_999_01 = "SEND-TX-999-01",
@@ -37,8 +37,10 @@ var PROGRESS_HOOK;
37
37
  PROGRESS_HOOK["SEND_TX_202_02"] = "SEND-TX-202-02";
38
38
  PROGRESS_HOOK["SEND_TX_203_01"] = "SEND-TX-203-01";
39
39
  PROGRESS_HOOK["SEND_TX_203_02"] = "SEND-TX-203-02";
40
- // Pre-flight balance check (203-03 INFO when sufficient or burn-token check passes;
41
- // 203-04 ERROR terminal precursor on shortfall; 203-05 INFO SVM warn-threshold tripped).
40
+ // Pre-flight balance check (203-03 INFO when sufficient, WARNING when
41
+ // short but enforceGasCheck=false, ERROR when enforceGasCheck=true;
42
+ // 203-04 ERROR terminal precursor on enforced shortfall;
43
+ // 203-05 INFO SVM warn-threshold tripped).
42
44
  // 203 bucket choice is temporal (sits between 203-02 CEA-resolved and 204-01 sign),
43
45
  // not strictly semantic — UEA balance is on Push Chain, not the destination CEA.
44
46
  PROGRESS_HOOK["SEND_TX_203_03"] = "SEND-TX-203-03";
@@ -6,7 +6,7 @@ import { TypedData, TypedDataDomain } from 'viem';
6
6
  import { ProgressEvent } from '../progress-hook/progress-hook.types';
7
7
  import type { AccountStatus } from '../orchestrator/orchestrator.types';
8
8
  import { MoveableToken, PayableToken, ConversionQuote, MoveableTokenAccessor, PayableTokenAccessor } from '../constants/tokens';
9
- import type { PreparedUniversalTx, CascadedTxResponse } from '../orchestrator/orchestrator.types';
9
+ import type { PreparedUniversalTx, CascadedTxResponse, CascadeExecutionOptions } from '../orchestrator/orchestrator.types';
10
10
  /**
11
11
  * @class PushChain
12
12
  *
@@ -67,11 +67,19 @@ export declare class PushChain {
67
67
  * to: { address: '0x...', chain: CHAIN.BNB_TESTNET },
68
68
  * value: parseEther('0.001')
69
69
  * });
70
+ *
71
+ * @param options.enforceGasCheck - Defaults to false. When true, the
72
+ * SDK stops on pre-flight gas/balance shortfall instead of warning
73
+ * and proceeding.
70
74
  */
71
75
  sendTransaction: Orchestrator['execute'];
72
76
  /**
73
77
  * Prepare a universal transaction without executing it.
74
78
  * Returns a PreparedUniversalTx that can be passed to executeTransactions.
79
+ *
80
+ * @param options.enforceGasCheck - Defaults to false. When true, the
81
+ * prepared transaction carries strict pre-flight gas/balance enforcement
82
+ * into executeTransactions.
75
83
  */
76
84
  prepareTransaction: Orchestrator['prepareTransaction'];
77
85
  /**
@@ -86,9 +94,7 @@ export declare class PushChain {
86
94
  * if it matches the init-time hook. Note: concurrent calls on the
87
95
  * same instance share the hook channel during the broadcast phase.
88
96
  */
89
- executeTransactions: (txs: PreparedUniversalTx[], options?: {
90
- progressHook?: (event: ProgressEvent) => void;
91
- }) => Promise<CascadedTxResponse>;
97
+ executeTransactions: (txs: PreparedUniversalTx[], options?: CascadeExecutionOptions) => Promise<CascadedTxResponse>;
92
98
  /**
93
99
  * Tracks a transaction by hash on Push Chain
94
100
  */
@@ -81,8 +81,8 @@ class PushChain {
81
81
  }
82
82
  return orchestrator.execute.bind(orchestrator)(...args);
83
83
  },
84
- prepareTransaction: (params) => {
85
- return orchestrator.prepareTransaction.bind(orchestrator)(params);
84
+ prepareTransaction: (params, options) => {
85
+ return orchestrator.prepareTransaction.bind(orchestrator)(params, options);
86
86
  },
87
87
  executeTransactions: (txs, options) => tslib_1.__awaiter(this, void 0, void 0, function* () {
88
88
  if (this.isReadMode) {
package/src/lib/utils.js CHANGED
@@ -711,7 +711,7 @@ Utils.tokens = {
711
711
  else if (isBaseFamily)
712
712
  key = 'USDC_BASE';
713
713
  else if (isBnbFamily)
714
- key = 'USDC_BNB';
714
+ key = 'USDC_BSC';
715
715
  else if (isSolFamily)
716
716
  key = 'USDC_SOL';
717
717
  else