@pushchain/core 6.0.16 → 6.0.18
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/CHANGELOG.md +137 -1
- package/package.json +1 -1
- package/src/lib/orchestrator/internals/cascade.js +1 -1
- package/src/lib/orchestrator/internals/execute-funds-payload.js +1 -1
- package/src/lib/orchestrator/internals/gas-calculator.d.ts +17 -0
- package/src/lib/orchestrator/internals/gas-calculator.js +22 -5
- package/src/lib/orchestrator/internals/signing.js +25 -11
- package/src/lib/orchestrator/payload-builders.d.ts +5 -5
- package/src/lib/orchestrator/payload-builders.js +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,140 @@
|
|
|
1
|
-
@pushchain/core@6.0.
|
|
1
|
+
@pushchain/core@6.0.17 (2026-06-09)
|
|
2
|
+
|
|
3
|
+
- fix: make SVM payload encoding browser-safe
|
|
4
|
+
- fix: prevent underfunded outbound gas swaps
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
@pushchain/core@6.0.17 (unreleased)
|
|
9
|
+
|
|
10
|
+
- Fixed outbound (UOA_TO_CEA) transactions reverting on-chain with Uniswap `STF`
|
|
11
|
+
when the UEA held a modest amount of native PC - e.g. pETH -> Ethereum ETH.
|
|
12
|
+
Destination gas is funded by swapping the UEA's PC into the gas token, and
|
|
13
|
+
`capSwapEstimate` was sizing `nativeValueForGas` down to `balance - reserve`
|
|
14
|
+
even when that fell below the swap's actual requirement, so the swap reverted
|
|
15
|
+
with `STF` while the native pre-flight stayed silent (its check was
|
|
16
|
+
self-satisfying). The estimator now keeps the full requirement when the
|
|
17
|
+
affordable amount can't cover the swap floor, so the pre-flight reports the
|
|
18
|
+
real PC shortfall instead of emitting a cryptic on-chain `STF`.
|
|
19
|
+
|
|
20
|
+
- Fixed SVM payload encoding in browser environments whose `Buffer` polyfill
|
|
21
|
+
does not implement Node's `writeBigUInt64LE` / `writeBigInt64LE` methods.
|
|
22
|
+
The encoder now writes u64/i64 fields with `DataView`, preserving the same
|
|
23
|
+
little-endian wire format without crashing during Solana transaction builds.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
@pushchain/core@6.0.16 (2026-06-05)
|
|
28
|
+
|
|
29
|
+
- Fixed SVM domain-separator chainId encoding to match the pre-EIP-712 UEA_SVM
|
|
30
|
+
behavior on Donut (raw string encoding), restoring correct SVM signature
|
|
31
|
+
validation.
|
|
32
|
+
|
|
33
|
+
- Fixed Route 1 deposit-sizing progress-hook ordering: the `103-03-04` event is
|
|
34
|
+
now emitted after UEA resolution so it matches the funds-payload path sequence.
|
|
35
|
+
|
|
36
|
+
- Additional cascade execution test coverage and validation fixes.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
@pushchain/core@6.0.15 (2026-06-02)
|
|
41
|
+
|
|
42
|
+
- Internal fixes to response-building logic.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
@pushchain/core@6.0.14 (2026-06-02)
|
|
47
|
+
|
|
48
|
+
- `trackTransaction` now resolves the originating source chain correctly even when
|
|
49
|
+
the tracked hash points to Push Chain, with explicit chain-support checks.
|
|
50
|
+
|
|
51
|
+
- Added `finalTxHash` to `UniversalTxResponse` — a single hash that resolves across
|
|
52
|
+
all routes (R1 → Push tx, R2 → external tx, R3 → Push inbound tx).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
@pushchain/core@6.0.13 (2026-05-26)
|
|
57
|
+
|
|
58
|
+
- Maintenance release (version bump only; no code changes).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
@pushchain/core@6.0.12 (2026-05-26)
|
|
63
|
+
|
|
64
|
+
- Maintenance release (version bump only; no code changes).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
@pushchain/core@6.0.11 (2026-05-25)
|
|
69
|
+
|
|
70
|
+
- Added transaction-indexing polling for SVM multihop: the SDK now retries with
|
|
71
|
+
backoff (1s polling, 30s timeout) before fetching transaction details, fixing
|
|
72
|
+
failures when querying immediately after confirmation.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
@pushchain/core@6.0.10 (2026-05-23)
|
|
77
|
+
|
|
78
|
+
- Added Borsh-encoded SVM-origin inbound relay payload building for Route 3
|
|
79
|
+
(CEA → Push).
|
|
80
|
+
|
|
81
|
+
- Improved SVM child-inbound detection using log index for more reliable
|
|
82
|
+
transaction identification.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
@pushchain/core@6.0.9 (2026-05-19)
|
|
87
|
+
|
|
88
|
+
- Added a decoder for the `GasPriceBelowBase` (0x05aab006) error with an
|
|
89
|
+
actionable hint about gasPrice overrides.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
@pushchain/core@6.0.8 (2026-05-19)
|
|
94
|
+
|
|
95
|
+
- Maintenance release (version bump only; no code changes).
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
@pushchain/core@6.0.7 (2026-05-19)
|
|
100
|
+
|
|
101
|
+
- Fixed corner-case bugs in cascade execution and funds-payload handling for
|
|
102
|
+
cross-chain transactions.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
@pushchain/core@6.0.6 (2026-05-18)
|
|
107
|
+
|
|
108
|
+
- Clarified progress-event message text (no functional changes).
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
@pushchain/core@6.0.5 (2026-05-18)
|
|
113
|
+
|
|
114
|
+
- Added multihop progress markers: `SEND_TX_199_03` / `SEND_TX_199_99` (Route 1
|
|
115
|
+
intermediate success) and `SEND_TX_399_99` (Route 3 round-trip completion), with
|
|
116
|
+
improved cascade event sequencing.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
@pushchain/core@6.0.4 (2026-05-18)
|
|
121
|
+
|
|
122
|
+
- Fixed payload-builder detection of executable data vs. value-only transfers,
|
|
123
|
+
including multicall empty-array handling.
|
|
124
|
+
|
|
125
|
+
- Fixed progress-hook wiring for funds-only execution paths.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
@pushchain/core@6.0.3 (2026-05-16)
|
|
130
|
+
|
|
131
|
+
- Added per-call `progressHook` support: `sendTransaction(params, { progressHook })`
|
|
132
|
+
is additive with the init-time hook, auto-registers on the returned response for
|
|
133
|
+
wait-phase progress, and dedupes by reference to avoid double-firing.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
@pushchain/core@6.0.2 (2026-05-16)
|
|
2
138
|
|
|
3
139
|
- Token constants now expose the missing `USDT.bsc` chain-suffix accessor,
|
|
4
140
|
while keeping `USDT.bnb` as a deprecated alias for backward compatibility.
|
package/package.json
CHANGED
|
@@ -1107,7 +1107,7 @@ function composeCascadeDetailed(ctx, segments, ueaAddress, ueaBalance, ueaNonce,
|
|
|
1107
1107
|
else {
|
|
1108
1108
|
evmFallbackValue = segGasFee * BigInt(1000000);
|
|
1109
1109
|
}
|
|
1110
|
-
|
|
1110
|
+
const nativeValueForGas = (_h = (_g = svmOverride !== null && svmOverride !== void 0 ? svmOverride : evmNativeValueBySegment.get(segment)) !== null && _g !== void 0 ? _g : perOutboundNativeValue) !== null && _h !== void 0 ? _h : evmFallbackValue;
|
|
1111
1111
|
// SDK 5.2 Case C: when the segment's sizing is category C, compose
|
|
1112
1112
|
// R2/OUTBOUND_TO_CEA segments no longer apply Case A/B/C sizing
|
|
1113
1113
|
// (scoped to R1 + R3 only). segment.sizing may still be populated
|
|
@@ -158,7 +158,7 @@ function executeFundsWithPayload(ctx, execute, eventBuffer, getResponseCallbacks
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
// Final USD(8) -> native conversion reusing the cached price fetched above.
|
|
161
|
-
|
|
161
|
+
const nativeAmount = usdToNative(depositUsd);
|
|
162
162
|
(0, context_1.fireProgressHook)(ctx, progress_hook_types_1.PROGRESS_HOOK.SEND_TX_103_03_04, nativeAmount, depositUsd, chain);
|
|
163
163
|
const bridgeAmount = execute.funds.amount;
|
|
164
164
|
const symbol = execute.funds.token.symbol;
|
|
@@ -25,6 +25,23 @@ export declare function queryOutboundGasFee(ctx: OrchestratorContext, prc20Token
|
|
|
25
25
|
* Falls back to a percentage of accountBalance if any on-chain query fails.
|
|
26
26
|
*/
|
|
27
27
|
export declare function estimateNativeValueForSwap(ctx: OrchestratorContext, universalCoreAddress: `0x${string}`, gasToken: `0x${string}`, gasFee: bigint, accountBalance: bigint): Promise<bigint>;
|
|
28
|
+
/**
|
|
29
|
+
* Bound the buffered swap estimate by the UEA's affordable PC.
|
|
30
|
+
*
|
|
31
|
+
* `result` is the 2.2x-buffered WPC needed for the gas swap; `minNeeded` is the
|
|
32
|
+
* un-buffered minimum the WPC->gasToken `exactOutput` swap actually consumes.
|
|
33
|
+
*
|
|
34
|
+
* Reducing the buffer to fit a tight balance is fine - but ONLY while the
|
|
35
|
+
* affordable amount (`accountBalance - reserve`) still covers `minNeeded`.
|
|
36
|
+
* Below that floor the swap cannot complete and reverts on-chain with Uniswap
|
|
37
|
+
* `STF`. The previous logic returned `accountBalance - reserve` unconditionally,
|
|
38
|
+
* which (a) under-funded `msg.value` into a doomed swap and (b) made the native
|
|
39
|
+
* preflight self-satisfying (`balance >= (balance - reserve) + reserve`), so the
|
|
40
|
+
* shortfall went undetected. When the balance can't cover the floor we now
|
|
41
|
+
* return the full `result` so the caller's preflight reports the real shortfall
|
|
42
|
+
* instead of emitting a cryptic STF.
|
|
43
|
+
*/
|
|
44
|
+
export declare function capSwapEstimate(result: bigint, accountBalance: bigint, reserve: bigint, minNeeded?: bigint): bigint;
|
|
28
45
|
export declare function queryRescueGasFee(ctx: OrchestratorContext, prc20Token: `0x${string}`, destinationChain?: CHAIN): Promise<{
|
|
29
46
|
gasToken: `0x${string}`;
|
|
30
47
|
gasFee: bigint;
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.ensureErc20Allowance = ensureErc20Allowance;
|
|
8
8
|
exports.queryOutboundGasFee = queryOutboundGasFee;
|
|
9
9
|
exports.estimateNativeValueForSwap = estimateNativeValueForSwap;
|
|
10
|
+
exports.capSwapEstimate = capSwapEstimate;
|
|
10
11
|
exports.queryRescueGasFee = queryRescueGasFee;
|
|
11
12
|
exports.calculateNativeAmountForDeposit = calculateNativeAmountForDeposit;
|
|
12
13
|
exports.calculateGasAmountFromAmountOutMinETH = calculateGasAmountFromAmountOutMinETH;
|
|
@@ -309,7 +310,7 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
|
|
|
309
310
|
const result = (exactWpcNeeded * SWAP_BUFFER_NUM) / SWAP_BUFFER_DEN;
|
|
310
311
|
(0, context_1.printLog)(ctx, `estimateNativeValueForSwap — exactOutput quoter=${quoterAddress}, ` +
|
|
311
312
|
`wpcNeeded=${exactWpcNeeded}, withBuffer(2.2x)=${result}`);
|
|
312
|
-
return capSwapEstimate(result, accountBalance, GAS_RESERVE);
|
|
313
|
+
return capSwapEstimate(result, accountBalance, GAS_RESERVE, exactWpcNeeded);
|
|
313
314
|
}
|
|
314
315
|
catch (quoteErr) {
|
|
315
316
|
(0, context_1.printLog)(ctx, `estimateNativeValueForSwap — exactOutput quote failed: ${quoteErr instanceof Error ? quoteErr.message : String(quoteErr)}, falling back to slot0 price`);
|
|
@@ -355,7 +356,7 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
|
|
|
355
356
|
const result = (wpcNeeded * SWAP_BUFFER_NUM) / SWAP_BUFFER_DEN;
|
|
356
357
|
(0, context_1.printLog)(ctx, `estimateNativeValueForSwap — pool=${poolAddress}, sqrtPriceX96=${sqrtPriceX96}, ` +
|
|
357
358
|
`wpcNeeded=${wpcNeeded}, withBuffer(2.2x)=${result}`);
|
|
358
|
-
return capSwapEstimate(result, accountBalance, GAS_RESERVE);
|
|
359
|
+
return capSwapEstimate(result, accountBalance, GAS_RESERVE, wpcNeeded);
|
|
359
360
|
}
|
|
360
361
|
catch (err) {
|
|
361
362
|
(0, context_1.printLog)(ctx, `estimateNativeValueForSwap — failed: ${err instanceof Error ? err.message : String(err)}, using balance fallback`);
|
|
@@ -363,14 +364,30 @@ function estimateNativeValueForSwap(ctx, universalCoreAddress, gasToken, gasFee,
|
|
|
363
364
|
}
|
|
364
365
|
});
|
|
365
366
|
}
|
|
366
|
-
|
|
367
|
+
/**
|
|
368
|
+
* Bound the buffered swap estimate by the UEA's affordable PC.
|
|
369
|
+
*
|
|
370
|
+
* `result` is the 2.2x-buffered WPC needed for the gas swap; `minNeeded` is the
|
|
371
|
+
* un-buffered minimum the WPC->gasToken `exactOutput` swap actually consumes.
|
|
372
|
+
*
|
|
373
|
+
* Reducing the buffer to fit a tight balance is fine - but ONLY while the
|
|
374
|
+
* affordable amount (`accountBalance - reserve`) still covers `minNeeded`.
|
|
375
|
+
* Below that floor the swap cannot complete and reverts on-chain with Uniswap
|
|
376
|
+
* `STF`. The previous logic returned `accountBalance - reserve` unconditionally,
|
|
377
|
+
* which (a) under-funded `msg.value` into a doomed swap and (b) made the native
|
|
378
|
+
* preflight self-satisfying (`balance >= (balance - reserve) + reserve`), so the
|
|
379
|
+
* shortfall went undetected. When the balance can't cover the floor we now
|
|
380
|
+
* return the full `result` so the caller's preflight reports the real shortfall
|
|
381
|
+
* instead of emitting a cryptic STF.
|
|
382
|
+
*/
|
|
383
|
+
function capSwapEstimate(result, accountBalance, reserve, minNeeded = BigInt(0)) {
|
|
367
384
|
if (accountBalance > result + reserve) {
|
|
368
385
|
return result;
|
|
369
386
|
}
|
|
370
|
-
if (accountBalance > reserve) {
|
|
387
|
+
if (accountBalance > reserve && accountBalance - reserve >= minNeeded) {
|
|
371
388
|
return accountBalance - reserve;
|
|
372
389
|
}
|
|
373
|
-
return result; //
|
|
390
|
+
return result; // balance can't cover the swap floor - surface true need to preflight
|
|
374
391
|
}
|
|
375
392
|
function balanceFallback(balance, divisor, reserve) {
|
|
376
393
|
const fraction = balance / divisor;
|
|
@@ -213,14 +213,28 @@ function encodeUniversalPayload(payload) {
|
|
|
213
213
|
]);
|
|
214
214
|
}
|
|
215
215
|
function encodeUniversalPayloadSvm(payload) {
|
|
216
|
-
const
|
|
216
|
+
const U64_MAX = (BigInt(1) << BigInt(64)) - BigInt(1);
|
|
217
|
+
const I64_MIN = -(BigInt(1) << BigInt(63));
|
|
218
|
+
const I64_MAX = (BigInt(1) << BigInt(63)) - BigInt(1);
|
|
219
|
+
const viewFor = (buf) => new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
220
|
+
const assertRange = (field, val, min, max) => {
|
|
221
|
+
if (val < min || val > max) {
|
|
222
|
+
throw new RangeError(`SVM payload field ${field} out of range: ${val.toString()} ` +
|
|
223
|
+
`(expected ${min.toString()}..${max.toString()})`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const writeU64 = (field, val) => {
|
|
227
|
+
const n = BigInt(val);
|
|
228
|
+
assertRange(field, n, BigInt(0), U64_MAX);
|
|
217
229
|
const b = Buffer.alloc(8);
|
|
218
|
-
b.
|
|
230
|
+
viewFor(b).setBigUint64(0, n, true);
|
|
219
231
|
return b;
|
|
220
232
|
};
|
|
221
|
-
const writeI64 = (val) => {
|
|
233
|
+
const writeI64 = (field, val) => {
|
|
234
|
+
const n = BigInt(val);
|
|
235
|
+
assertRange(field, n, I64_MIN, I64_MAX);
|
|
222
236
|
const b = Buffer.alloc(8);
|
|
223
|
-
b.
|
|
237
|
+
viewFor(b).setBigInt64(0, n, true);
|
|
224
238
|
return b;
|
|
225
239
|
};
|
|
226
240
|
const writeVecU8 = (val) => {
|
|
@@ -228,7 +242,7 @@ function encodeUniversalPayloadSvm(payload) {
|
|
|
228
242
|
? val
|
|
229
243
|
: Buffer.from(val);
|
|
230
244
|
const len = Buffer.alloc(4);
|
|
231
|
-
len.
|
|
245
|
+
viewFor(len).setUint32(0, bytes.length, true);
|
|
232
246
|
return Buffer.concat([len, bytes]);
|
|
233
247
|
};
|
|
234
248
|
const writeU8 = (val) => Buffer.from([val]);
|
|
@@ -237,18 +251,18 @@ function encodeUniversalPayloadSvm(payload) {
|
|
|
237
251
|
const hex = to.slice(2).padStart(40, '0');
|
|
238
252
|
return Buffer.from(hex, 'hex');
|
|
239
253
|
})();
|
|
240
|
-
const valueBytes = writeU64(payload.value);
|
|
254
|
+
const valueBytes = writeU64('value', payload.value);
|
|
241
255
|
const dataBytes = (() => {
|
|
242
256
|
const data = payload.data;
|
|
243
257
|
const hex = data.slice(2);
|
|
244
258
|
const buf = hex.length ? Buffer.from(hex, 'hex') : Buffer.alloc(0);
|
|
245
259
|
return writeVecU8(buf);
|
|
246
260
|
})();
|
|
247
|
-
const gasLimitBytes = writeU64(payload.gasLimit);
|
|
248
|
-
const maxFeePerGasBytes = writeU64(payload.maxFeePerGas);
|
|
249
|
-
const maxPriorityFeePerGasBytes = writeU64(payload.maxPriorityFeePerGas);
|
|
250
|
-
const nonceBytes = writeU64(payload.nonce);
|
|
251
|
-
const deadlineBytes = writeI64(payload.deadline);
|
|
261
|
+
const gasLimitBytes = writeU64('gasLimit', payload.gasLimit);
|
|
262
|
+
const maxFeePerGasBytes = writeU64('maxFeePerGas', payload.maxFeePerGas);
|
|
263
|
+
const maxPriorityFeePerGasBytes = writeU64('maxPriorityFeePerGas', payload.maxPriorityFeePerGas);
|
|
264
|
+
const nonceBytes = writeU64('nonce', payload.nonce);
|
|
265
|
+
const deadlineBytes = writeI64('deadline', payload.deadline);
|
|
252
266
|
const vTypeVal = (() => {
|
|
253
267
|
const v = payload.vType;
|
|
254
268
|
if (typeof v === 'number')
|
|
@@ -127,14 +127,14 @@ export declare function buildMigrationPayload(): `0x${string}`;
|
|
|
127
127
|
* Build approval multicalls + sendUniversalTxOutbound call for Push Chain.
|
|
128
128
|
*
|
|
129
129
|
* Extracted from executeUoaToCea to be reusable in cascade composition.
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
130
|
+
* The PRC-20 approval covers only the burn amount. Destination gas is funded
|
|
131
|
+
* through native PC msg.value on sendUniversalTxOutbound; UniversalCore swaps
|
|
132
|
+
* that native value into the route gas token inside swapAndBurnGas.
|
|
133
133
|
*
|
|
134
134
|
* @param opts.prc20Token - PRC-20 token to burn
|
|
135
|
-
* @param opts.gasToken - Gas token for fee payment
|
|
135
|
+
* @param opts.gasToken - Gas token for fee payment, used by the outbound request
|
|
136
136
|
* @param opts.burnAmount - Amount to burn
|
|
137
|
-
* @param opts.gasFee - Gas fee amount
|
|
137
|
+
* @param opts.gasFee - Gas fee amount, used only for fallback native value sizing
|
|
138
138
|
* @param opts.gatewayPcAddress - UniversalGatewayPC precompile address
|
|
139
139
|
* @param opts.outboundRequest - The outbound request struct
|
|
140
140
|
* @returns Array of MultiCall operations (approvals + outbound call)
|
|
@@ -342,14 +342,14 @@ function buildMigrationPayload() {
|
|
|
342
342
|
* Build approval multicalls + sendUniversalTxOutbound call for Push Chain.
|
|
343
343
|
*
|
|
344
344
|
* Extracted from executeUoaToCea to be reusable in cascade composition.
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
345
|
+
* The PRC-20 approval covers only the burn amount. Destination gas is funded
|
|
346
|
+
* through native PC msg.value on sendUniversalTxOutbound; UniversalCore swaps
|
|
347
|
+
* that native value into the route gas token inside swapAndBurnGas.
|
|
348
348
|
*
|
|
349
349
|
* @param opts.prc20Token - PRC-20 token to burn
|
|
350
|
-
* @param opts.gasToken - Gas token for fee payment
|
|
350
|
+
* @param opts.gasToken - Gas token for fee payment, used by the outbound request
|
|
351
351
|
* @param opts.burnAmount - Amount to burn
|
|
352
|
-
* @param opts.gasFee - Gas fee amount
|
|
352
|
+
* @param opts.gasFee - Gas fee amount, used only for fallback native value sizing
|
|
353
353
|
* @param opts.gatewayPcAddress - UniversalGatewayPC precompile address
|
|
354
354
|
* @param opts.outboundRequest - The outbound request struct
|
|
355
355
|
* @returns Array of MultiCall operations (approvals + outbound call)
|