@pushchain/core 6.0.18 → 6.0.20
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 +14 -0
- package/package.json +1 -1
- package/src/lib/constants/chain.d.ts +16 -0
- package/src/lib/constants/chain.js +32 -4
- package/src/lib/formatters.d.ts +1 -0
- package/src/lib/formatters.js +66 -1
- package/src/lib/index.d.ts +1 -1
- package/src/lib/orchestrator/internals/cascade.js +64 -46
- package/src/lib/orchestrator/internals/confirmation.js +8 -8
- package/src/lib/orchestrator/internals/error-decoder.d.ts +1 -0
- package/src/lib/orchestrator/internals/error-decoder.js +10 -0
- package/src/lib/orchestrator/internals/errors.js +1 -1
- package/src/lib/orchestrator/internals/execute-funds-only.js +8 -6
- package/src/lib/orchestrator/internals/execute-funds-payload.js +6 -4
- package/src/lib/orchestrator/internals/execute-standard.js +10 -8
- package/src/lib/orchestrator/internals/gateway-client.js +34 -1
- package/src/lib/orchestrator/internals/inbound-tracker.js +4 -3
- package/src/lib/orchestrator/internals/progress-route-hooks.d.ts +8 -4
- package/src/lib/orchestrator/internals/progress-route-hooks.js +8 -8
- package/src/lib/orchestrator/internals/push-chain-tx.js +71 -17
- package/src/lib/orchestrator/internals/response-builder.js +25 -16
- package/src/lib/orchestrator/internals/tx-transformer.js +11 -6
- package/src/lib/orchestrator/orchestrator.js +3 -17
- package/src/lib/orchestrator/orchestrator.types.d.ts +7 -0
- package/src/lib/progress-hook/progress-hook.js +37 -40
- package/src/lib/push-client/push-client.d.ts +39 -0
- package/src/lib/push-client/push-client.js +130 -12
- package/src/lib/universal/signer/signer.d.ts +3 -2
- package/src/lib/universal/signer/signer.js +92 -8
- package/src/lib/universal/universal.types.d.ts +37 -0
- package/src/lib/vm-client/evm-client.d.ts +43 -0
- package/src/lib/vm-client/evm-client.js +154 -1
|
@@ -21,10 +21,75 @@ const bs58_1 = require("../../internal/bs58");
|
|
|
21
21
|
/**
|
|
22
22
|
* Converts viem's parsed transaction to an ethers-compatible TransactionRequest.
|
|
23
23
|
* viem uses `gas` while ethers expects `gasLimit`.
|
|
24
|
+
*
|
|
25
|
+
* For EIP-7702 (type-4) transactions the `authorizationList` entries also need
|
|
26
|
+
* reshaping: viem represents the signature inline (`r`/`s`/`yParity`), whereas
|
|
27
|
+
* ethers expects a nested `signature`. Without this conversion ethers normalises
|
|
28
|
+
* the entry to a zero signature and the delegation is silently dropped.
|
|
24
29
|
*/
|
|
25
30
|
function toEthersTxRequest(viemTx) {
|
|
26
|
-
const _a = viemTx, { gas, type } = _a, rest = tslib_1.__rest(_a, ["gas", "type"]);
|
|
27
|
-
|
|
31
|
+
const _a = viemTx, { gas, type, authorizationList } = _a, rest = tslib_1.__rest(_a, ["gas", "type", "authorizationList"]);
|
|
32
|
+
const out = Object.assign(Object.assign({}, rest), { gasLimit: gas });
|
|
33
|
+
if (Array.isArray(authorizationList) && authorizationList.length > 0) {
|
|
34
|
+
out['type'] = 4;
|
|
35
|
+
out['authorizationList'] = authorizationList.map((a) => {
|
|
36
|
+
var _a;
|
|
37
|
+
return ({
|
|
38
|
+
address: (_a = a.address) !== null && _a !== void 0 ? _a : a.contractAddress,
|
|
39
|
+
chainId: a.chainId,
|
|
40
|
+
nonce: a.nonce,
|
|
41
|
+
signature: { r: a.r, s: a.s, yParity: a.yParity },
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds an EIP-7702 `signAuthorization` for a viem WalletClient, but ONLY when
|
|
49
|
+
* the underlying account can sign offline (a local account). viem exposes the
|
|
50
|
+
* `signAuthorization` action on every client — including JSON-RPC accounts that
|
|
51
|
+
* cannot actually produce one — so we gate on `account.signAuthorization`
|
|
52
|
+
* (present on local accounts only). Returns `undefined` otherwise so callers
|
|
53
|
+
* fall back to non-7702 execution.
|
|
54
|
+
*/
|
|
55
|
+
function viemSignAuthorization(wallet, address) {
|
|
56
|
+
const account = wallet === null || wallet === void 0 ? void 0 : wallet.account;
|
|
57
|
+
if (!account || typeof account.signAuthorization !== 'function') {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return (_a) => tslib_1.__awaiter(this, [_a], void 0, function* ({ contractAddress, chainId, nonce, executor }) {
|
|
61
|
+
const auth = yield wallet.signAuthorization(Object.assign(Object.assign(Object.assign({ account: account || address, contractAddress }, (chainId !== undefined ? { chainId } : {})), (nonce !== undefined ? { nonce } : {})), (executor ? { executor } : {})));
|
|
62
|
+
return auth;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Builds an EIP-7702 `signAuthorization` for an ethers v6 signer. Presence of
|
|
67
|
+
* `authorize` is not a reliable capability signal (AbstractSigner's default
|
|
68
|
+
* throws), so callers MUST also handle a runtime failure and fall back — see
|
|
69
|
+
* `EIP7702NotSupportedError` in evm-client. Returns `undefined` when the method
|
|
70
|
+
* is entirely absent (e.g. ethers v5).
|
|
71
|
+
*/
|
|
72
|
+
function ethersSignAuthorization(wallet, chain) {
|
|
73
|
+
if (typeof (wallet === null || wallet === void 0 ? void 0 : wallet.authorize) !== 'function')
|
|
74
|
+
return undefined;
|
|
75
|
+
return (_a) => tslib_1.__awaiter(this, [_a], void 0, function* ({ contractAddress, chainId, nonce }) {
|
|
76
|
+
var _b, _c, _d;
|
|
77
|
+
const cid = chainId !== null && chainId !== void 0 ? chainId : Number(chain.split(':')[1]);
|
|
78
|
+
const auth = yield wallet.authorize({
|
|
79
|
+
address: contractAddress,
|
|
80
|
+
chainId: cid,
|
|
81
|
+
nonce: nonce !== null && nonce !== void 0 ? nonce : 0,
|
|
82
|
+
});
|
|
83
|
+
const sig = auth.signature;
|
|
84
|
+
return {
|
|
85
|
+
address: contractAddress,
|
|
86
|
+
chainId: Number((_b = auth.chainId) !== null && _b !== void 0 ? _b : cid),
|
|
87
|
+
nonce: Number((_d = (_c = auth.nonce) !== null && _c !== void 0 ? _c : nonce) !== null && _d !== void 0 ? _d : 0),
|
|
88
|
+
r: sig.r,
|
|
89
|
+
s: sig.s,
|
|
90
|
+
yParity: Number(sig.yParity),
|
|
91
|
+
};
|
|
92
|
+
});
|
|
28
93
|
}
|
|
29
94
|
/**
|
|
30
95
|
* Maps an EVM numeric chainId to the corresponding CHAIN enum.
|
|
@@ -72,12 +137,13 @@ function chainIdToChain(chainId) {
|
|
|
72
137
|
* signAndSendTransaction: async (data) => signRawTx(data),
|
|
73
138
|
* });
|
|
74
139
|
*/
|
|
75
|
-
function createUniversalSigner({ account, signMessage, signAndSendTransaction, signTypedData, }) {
|
|
140
|
+
function createUniversalSigner({ account, signMessage, signAndSendTransaction, signTypedData, signAuthorization, }) {
|
|
76
141
|
return {
|
|
77
142
|
account,
|
|
78
143
|
signMessage,
|
|
79
144
|
signAndSendTransaction,
|
|
80
145
|
signTypedData,
|
|
146
|
+
signAuthorization,
|
|
81
147
|
};
|
|
82
148
|
}
|
|
83
149
|
/**
|
|
@@ -93,6 +159,10 @@ function toUniversalFromKeypair(clientOrAccount_1, _a) {
|
|
|
93
159
|
let signMessage;
|
|
94
160
|
let signAndSendTransaction;
|
|
95
161
|
let signTypedData;
|
|
162
|
+
// Optional: only set for EVM wallets that support EIP-7702 authorization
|
|
163
|
+
// signing. Left undefined for Solana and wallets without 7702 support, in
|
|
164
|
+
// which case callers fall back to a non-7702 path.
|
|
165
|
+
let signAuthorization;
|
|
96
166
|
// Check if signer has UID='custom', then we take signMessage, signAndSendTransaction, signTypedData, chain and address from the CustomUniversalSigner.
|
|
97
167
|
// If ViemSigner, convert ViemSigner to UniversalSigner.
|
|
98
168
|
switch (library) {
|
|
@@ -129,6 +199,9 @@ function toUniversalFromKeypair(clientOrAccount_1, _a) {
|
|
|
129
199
|
const sigHex = yield wallet.signTypedData(domain, types, message);
|
|
130
200
|
return (0, viem_1.hexToBytes)(sigHex);
|
|
131
201
|
});
|
|
202
|
+
// EIP-7702 authorization signing (ethers v6). Backed by a runtime
|
|
203
|
+
// fallback in evm-client since `authorize` presence isn't a guarantee.
|
|
204
|
+
signAuthorization = ethersSignAuthorization(wallet, chain);
|
|
132
205
|
break;
|
|
133
206
|
}
|
|
134
207
|
case enums_1.LIBRARY.ETHEREUM_VIEM: {
|
|
@@ -157,6 +230,9 @@ function toUniversalFromKeypair(clientOrAccount_1, _a) {
|
|
|
157
230
|
});
|
|
158
231
|
return (0, viem_1.hexToBytes)(hexSig);
|
|
159
232
|
});
|
|
233
|
+
// EIP-7702 authorization signing — only for local viem accounts that can
|
|
234
|
+
// sign offline (gated inside the helper); JSON-RPC accounts fall back.
|
|
235
|
+
signAuthorization = viemSignAuthorization(wallet, address);
|
|
160
236
|
break;
|
|
161
237
|
}
|
|
162
238
|
case enums_1.LIBRARY.SOLANA_WEB3JS: {
|
|
@@ -225,25 +301,30 @@ function toUniversalFromKeypair(clientOrAccount_1, _a) {
|
|
|
225
301
|
signMessage,
|
|
226
302
|
signAndSendTransaction,
|
|
227
303
|
signTypedData,
|
|
304
|
+
signAuthorization,
|
|
228
305
|
};
|
|
229
306
|
return createUniversalSigner(universalSigner);
|
|
230
307
|
});
|
|
231
308
|
}
|
|
232
309
|
// `signTypedData` is only mandatory for EVM Signers. For Solana, this is not necessary.
|
|
233
310
|
function construct(account, options) {
|
|
234
|
-
const { signMessage, signAndSendTransaction, signTypedData } = options;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
311
|
+
const { signMessage, signAndSendTransaction, signTypedData, signAuthorization, } = options;
|
|
312
|
+
const isSolana = account.chain === enums_1.CHAIN.SOLANA_MAINNET ||
|
|
313
|
+
account.chain === enums_1.CHAIN.SOLANA_TESTNET ||
|
|
314
|
+
account.chain === enums_1.CHAIN.SOLANA_DEVNET;
|
|
315
|
+
if (signTypedData && isSolana) {
|
|
239
316
|
throw new Error('Typed data signing is not supported for Solana');
|
|
240
317
|
}
|
|
318
|
+
if (signAuthorization && isSolana) {
|
|
319
|
+
throw new Error('EIP-7702 authorization is not supported for Solana');
|
|
320
|
+
}
|
|
241
321
|
return {
|
|
242
322
|
signerId: 'CustomGeneratedSigner',
|
|
243
323
|
account,
|
|
244
324
|
signMessage,
|
|
245
325
|
signAndSendTransaction,
|
|
246
326
|
signTypedData,
|
|
327
|
+
signAuthorization,
|
|
247
328
|
};
|
|
248
329
|
}
|
|
249
330
|
function toUniversal(signer) {
|
|
@@ -320,6 +401,7 @@ function generateSkeletonFromEthersV6(signer) {
|
|
|
320
401
|
const sigHex = yield signer.signTypedData(domain, types, message);
|
|
321
402
|
return (0, viem_1.hexToBytes)(sigHex);
|
|
322
403
|
}),
|
|
404
|
+
signAuthorization: ethersSignAuthorization(signer, chain),
|
|
323
405
|
};
|
|
324
406
|
});
|
|
325
407
|
}
|
|
@@ -369,6 +451,8 @@ function generateSkeletonFromViem(signer) {
|
|
|
369
451
|
});
|
|
370
452
|
return (0, viem_1.hexToBytes)(hexSig);
|
|
371
453
|
}),
|
|
454
|
+
// Gated on a local account inside the helper (JSON-RPC accounts → undefined).
|
|
455
|
+
signAuthorization: viemSignAuthorization(signer, address),
|
|
372
456
|
};
|
|
373
457
|
});
|
|
374
458
|
}
|
|
@@ -48,6 +48,35 @@ export interface EthersV6SignerType {
|
|
|
48
48
|
signTypedData: (domain: TypedDataDomain, types: Record<string, Array<any>>, value: Record<string, any>) => Promise<string>;
|
|
49
49
|
provider?: any;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Parameters for signing an EIP-7702 authorization (delegating an EOA's code to
|
|
53
|
+
* `contractAddress`).
|
|
54
|
+
*/
|
|
55
|
+
export type SignAuthorizationParams = {
|
|
56
|
+
/** Address of the contract to delegate the EOA's code to. */
|
|
57
|
+
contractAddress: `0x${string}`;
|
|
58
|
+
/** Chain id for the delegation. Defaults to the signer's chain. */
|
|
59
|
+
chainId?: number;
|
|
60
|
+
/** Authorization nonce. Omit to let the wallet fill it. */
|
|
61
|
+
nonce?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Set `'self'` when the signing EOA also submits the type-4 transaction; the
|
|
64
|
+
* wallet then bumps the authorization nonce by one. Prefer passing an explicit
|
|
65
|
+
* `nonce` for deterministic self-execution.
|
|
66
|
+
*/
|
|
67
|
+
executor?: 'self';
|
|
68
|
+
};
|
|
69
|
+
/** A signed EIP-7702 authorization (viem-compatible shape). */
|
|
70
|
+
export type SignedAuthorization = {
|
|
71
|
+
/** Address of the contract the EOA's code is delegated to. */
|
|
72
|
+
address: `0x${string}`;
|
|
73
|
+
chainId: number;
|
|
74
|
+
nonce: number;
|
|
75
|
+
r: `0x${string}`;
|
|
76
|
+
s: `0x${string}`;
|
|
77
|
+
yParity: number;
|
|
78
|
+
v?: bigint;
|
|
79
|
+
};
|
|
51
80
|
/**
|
|
52
81
|
* A signer capable of signing messages for a specific chain.
|
|
53
82
|
* Used to abstract away signing across multiple VM types.
|
|
@@ -84,12 +113,20 @@ export interface UniversalSigner {
|
|
|
84
113
|
* Used for sending on-chain transactions.
|
|
85
114
|
*/
|
|
86
115
|
signAndSendTransaction: (unsignedTx: Uint8Array) => Promise<Uint8Array>;
|
|
116
|
+
/**
|
|
117
|
+
* Signs an EIP-7702 authorization, delegating the EOA's code to
|
|
118
|
+
* `contractAddress`. Optional — only EVM signers whose underlying wallet
|
|
119
|
+
* supports EIP-7702 implement it. When absent, callers must fall back to a
|
|
120
|
+
* non-7702 path (e.g. sequential single calls).
|
|
121
|
+
*/
|
|
122
|
+
signAuthorization?: (params: SignAuthorizationParams) => Promise<SignedAuthorization>;
|
|
87
123
|
}
|
|
88
124
|
export interface UniversalSignerSkeleton {
|
|
89
125
|
signerId: string;
|
|
90
126
|
account: UniversalAccount;
|
|
91
127
|
signMessage: (data: Uint8Array) => Promise<Uint8Array>;
|
|
92
128
|
signAndSendTransaction: (unsignedTx: Uint8Array) => Promise<Uint8Array>;
|
|
129
|
+
signAuthorization?: (params: SignAuthorizationParams) => Promise<SignedAuthorization>;
|
|
93
130
|
signTypedData?: ({ domain, types, primaryType, message, }: {
|
|
94
131
|
domain: TypedDataDomain;
|
|
95
132
|
types: TypedData;
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { UniversalSigner } from '../universal/universal.types';
|
|
2
2
|
import { ClientOptions, ReadContractParams, TxResponse, WriteContractParams } from './vm-client.types';
|
|
3
3
|
import { PublicClient, Hex } from 'viem';
|
|
4
|
+
/** A single call in a 7702 batch. */
|
|
5
|
+
export type BatchCall = {
|
|
6
|
+
to: `0x${string}`;
|
|
7
|
+
value: bigint;
|
|
8
|
+
data: `0x${string}`;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Thrown by {@link EvmClient.sendBatch7702} when the signer cannot actually
|
|
12
|
+
* produce an EIP-7702 authorization — either the capability is absent, or the
|
|
13
|
+
* wallet exposes a `signAuthorization`/`authorize` method that throws at call
|
|
14
|
+
* time (e.g. viem JSON-RPC accounts, ethers' default `AbstractSigner.authorize`).
|
|
15
|
+
*
|
|
16
|
+
* It is raised strictly BEFORE any transaction is broadcast, so callers may
|
|
17
|
+
* safely fall back to a non-7702 path without risk of double execution.
|
|
18
|
+
*/
|
|
19
|
+
export declare class EIP7702NotSupportedError extends Error {
|
|
20
|
+
readonly reason?: unknown;
|
|
21
|
+
constructor(reason?: unknown);
|
|
22
|
+
}
|
|
4
23
|
/**
|
|
5
24
|
* EVM client for reading and writing to Ethereum-compatible chains
|
|
6
25
|
*
|
|
@@ -126,6 +145,30 @@ export declare class EvmClient {
|
|
|
126
145
|
nonce?: number;
|
|
127
146
|
gas?: bigint;
|
|
128
147
|
}): Promise<Hex>;
|
|
148
|
+
/**
|
|
149
|
+
* Executes a batch of calls atomically from a native EOA using EIP-7702.
|
|
150
|
+
*
|
|
151
|
+
* The EOA signs an authorization delegating its code to `executor`
|
|
152
|
+
* (`PushBatchExecutor`, an ERC-7821 executor) and submits a single type-4
|
|
153
|
+
* transaction calling `execute(mode, executionData)` on itself. Because the
|
|
154
|
+
* authority equals the transaction sender, ERC-7821's self-call check
|
|
155
|
+
* authorizes the batch — no per-call signature is needed. All calls succeed
|
|
156
|
+
* or the whole tx reverts.
|
|
157
|
+
*
|
|
158
|
+
* Requires `signer.signAuthorization` (EIP-7702 capable wallet). Callers
|
|
159
|
+
* should check for it and fall back to sequential execution when absent.
|
|
160
|
+
*
|
|
161
|
+
* Rejects a zero `to` address: ERC-7579/7821 reinterprets `target == 0` as
|
|
162
|
+
* `address(this)`, which would silently differ from the sequential fallback
|
|
163
|
+
* (which sends to the zero address). Disallow it so behaviour is consistent.
|
|
164
|
+
*
|
|
165
|
+
* @returns The transaction hash.
|
|
166
|
+
*/
|
|
167
|
+
sendBatch7702({ executor, calls, signer, }: {
|
|
168
|
+
executor: `0x${string}`;
|
|
169
|
+
calls: BatchCall[];
|
|
170
|
+
signer: UniversalSigner;
|
|
171
|
+
}): Promise<Hex>;
|
|
129
172
|
/**
|
|
130
173
|
* Estimates the gas required for a transaction.
|
|
131
174
|
*
|
|
@@ -1,8 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EvmClient = void 0;
|
|
3
|
+
exports.EvmClient = exports.EIP7702NotSupportedError = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const viem_1 = require("viem");
|
|
6
|
+
/**
|
|
7
|
+
* ABI of the ERC-7821 `execute(bytes32 mode, bytes executionData)` entrypoint —
|
|
8
|
+
* invoked by a native Push EOA on itself after it delegates its code to the
|
|
9
|
+
* executor (`PushBatchExecutor is ERC7821`) via EIP-7702. ERC-7821's default
|
|
10
|
+
* authorization (`caller == address(this)`) is exactly that self-call.
|
|
11
|
+
*/
|
|
12
|
+
const PUSH_BATCH_EXECUTOR_ABI = (0, viem_1.parseAbi)([
|
|
13
|
+
'function execute(bytes32 mode, bytes executionData) payable',
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* ERC-7821 single-batch execution mode: callType `0x01` (batch) in the top byte,
|
|
17
|
+
* default exec type and zero mode-selector — i.e. `0x01` followed by 31 zero
|
|
18
|
+
* bytes. This is the only mode the executor supports.
|
|
19
|
+
*/
|
|
20
|
+
const ERC7821_BATCH_MODE = '0x0100000000000000000000000000000000000000000000000000000000000000';
|
|
21
|
+
/**
|
|
22
|
+
* ABI tuple for an ERC-7821 / ERC-7579 `Execution`. `executionData` for a batch
|
|
23
|
+
* is `abi.encode(Execution[])`.
|
|
24
|
+
*/
|
|
25
|
+
const ERC7821_EXECUTION_TUPLE = {
|
|
26
|
+
type: 'tuple[]',
|
|
27
|
+
components: [
|
|
28
|
+
{ name: 'target', type: 'address' },
|
|
29
|
+
{ name: 'value', type: 'uint256' },
|
|
30
|
+
{ name: 'callData', type: 'bytes' },
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Thrown by {@link EvmClient.sendBatch7702} when the signer cannot actually
|
|
35
|
+
* produce an EIP-7702 authorization — either the capability is absent, or the
|
|
36
|
+
* wallet exposes a `signAuthorization`/`authorize` method that throws at call
|
|
37
|
+
* time (e.g. viem JSON-RPC accounts, ethers' default `AbstractSigner.authorize`).
|
|
38
|
+
*
|
|
39
|
+
* It is raised strictly BEFORE any transaction is broadcast, so callers may
|
|
40
|
+
* safely fall back to a non-7702 path without risk of double execution.
|
|
41
|
+
*/
|
|
42
|
+
class EIP7702NotSupportedError extends Error {
|
|
43
|
+
constructor(reason) {
|
|
44
|
+
super('Signer cannot produce an EIP-7702 authorization');
|
|
45
|
+
this.name = 'EIP7702NotSupportedError';
|
|
46
|
+
this.reason = reason;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.EIP7702NotSupportedError = EIP7702NotSupportedError;
|
|
6
50
|
/**
|
|
7
51
|
* EVM client for reading and writing to Ethereum-compatible chains
|
|
8
52
|
*
|
|
@@ -203,6 +247,115 @@ class EvmClient {
|
|
|
203
247
|
return (0, viem_1.bytesToHex)(txHashBytes);
|
|
204
248
|
});
|
|
205
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Executes a batch of calls atomically from a native EOA using EIP-7702.
|
|
252
|
+
*
|
|
253
|
+
* The EOA signs an authorization delegating its code to `executor`
|
|
254
|
+
* (`PushBatchExecutor`, an ERC-7821 executor) and submits a single type-4
|
|
255
|
+
* transaction calling `execute(mode, executionData)` on itself. Because the
|
|
256
|
+
* authority equals the transaction sender, ERC-7821's self-call check
|
|
257
|
+
* authorizes the batch — no per-call signature is needed. All calls succeed
|
|
258
|
+
* or the whole tx reverts.
|
|
259
|
+
*
|
|
260
|
+
* Requires `signer.signAuthorization` (EIP-7702 capable wallet). Callers
|
|
261
|
+
* should check for it and fall back to sequential execution when absent.
|
|
262
|
+
*
|
|
263
|
+
* Rejects a zero `to` address: ERC-7579/7821 reinterprets `target == 0` as
|
|
264
|
+
* `address(this)`, which would silently differ from the sequential fallback
|
|
265
|
+
* (which sends to the zero address). Disallow it so behaviour is consistent.
|
|
266
|
+
*
|
|
267
|
+
* @returns The transaction hash.
|
|
268
|
+
*/
|
|
269
|
+
sendBatch7702(_a) {
|
|
270
|
+
return tslib_1.__awaiter(this, arguments, void 0, function* ({ executor, calls, signer, }) {
|
|
271
|
+
if (!signer.signAuthorization) {
|
|
272
|
+
// No capability at all → safe pre-broadcast signal to fall back.
|
|
273
|
+
throw new EIP7702NotSupportedError();
|
|
274
|
+
}
|
|
275
|
+
if (!signer.signAndSendTransaction) {
|
|
276
|
+
throw new Error('signer.signAndSendTransaction is undefined');
|
|
277
|
+
}
|
|
278
|
+
const account = signer.account.address;
|
|
279
|
+
// ERC-7821 maps target == address(0) to address(this); reject it so a batch
|
|
280
|
+
// can't silently behave differently here vs. the sequential fallback.
|
|
281
|
+
if (calls.some((c) => /^0x0{40}$/i.test(c.to) || c.to.toLowerCase() === '0x0')) {
|
|
282
|
+
throw new Error('sendBatch7702: a call has a zero `to` address, which ERC-7821 reinterprets ' +
|
|
283
|
+
'as the account itself. Use an explicit target.');
|
|
284
|
+
}
|
|
285
|
+
// ERC-7821: execute(mode, executionData) where executionData = abi.encode(Execution[]).
|
|
286
|
+
const executionData = (0, viem_1.encodeAbiParameters)([ERC7821_EXECUTION_TUPLE], [
|
|
287
|
+
calls.map((c) => ({ target: c.to, value: c.value, callData: c.data })),
|
|
288
|
+
]);
|
|
289
|
+
const data = (0, viem_1.encodeFunctionData)({
|
|
290
|
+
abi: PUSH_BATCH_EXECUTOR_ABI,
|
|
291
|
+
functionName: 'execute',
|
|
292
|
+
args: [ERC7821_BATCH_MODE, executionData],
|
|
293
|
+
});
|
|
294
|
+
const [feePerGas, chainId, nonce] = yield Promise.all([
|
|
295
|
+
this.publicClient.estimateFeesPerGas(),
|
|
296
|
+
this.publicClient.getChainId(),
|
|
297
|
+
this.publicClient.getTransactionCount({
|
|
298
|
+
address: account,
|
|
299
|
+
blockTag: 'pending',
|
|
300
|
+
}),
|
|
301
|
+
]);
|
|
302
|
+
// EIP-7702 self-execution: the transaction consumes `nonce`, so the
|
|
303
|
+
// authorization (validated after the sender's nonce is bumped) must carry
|
|
304
|
+
// `nonce + 1`. This is the only step that can reveal the wallet truly can't
|
|
305
|
+
// sign a 7702 authorization (presence of the method is not a guarantee), so
|
|
306
|
+
// a failure here is surfaced as EIP7702NotSupportedError — pre-broadcast,
|
|
307
|
+
// hence safe for the caller to fall back on.
|
|
308
|
+
let authorization;
|
|
309
|
+
try {
|
|
310
|
+
authorization = yield signer.signAuthorization({
|
|
311
|
+
contractAddress: executor,
|
|
312
|
+
chainId,
|
|
313
|
+
nonce: nonce + 1,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
throw new EIP7702NotSupportedError(err);
|
|
318
|
+
}
|
|
319
|
+
// Estimate gas against the delegated account: override the EOA's code with
|
|
320
|
+
// the 7702 delegation designator (0xef0100 || executor) so estimation runs
|
|
321
|
+
// the executor. Fall back to a safe ceiling if the node rejects overrides.
|
|
322
|
+
let gas;
|
|
323
|
+
try {
|
|
324
|
+
gas = yield this.publicClient.estimateGas({
|
|
325
|
+
account,
|
|
326
|
+
to: account,
|
|
327
|
+
data,
|
|
328
|
+
stateOverride: [
|
|
329
|
+
{
|
|
330
|
+
address: account,
|
|
331
|
+
code: ('0xef0100' + executor.slice(2)),
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
});
|
|
335
|
+
gas = (gas * BigInt(120)) / BigInt(100); // headroom for delegation + auth
|
|
336
|
+
}
|
|
337
|
+
catch (_b) {
|
|
338
|
+
// Estimation unavailable (e.g. node rejects state overrides). Scale the
|
|
339
|
+
// ceiling with the batch size so large batches don't OOG — the legacy
|
|
340
|
+
// per-call loop budgeted ~500k gas per call; mirror that plus overhead.
|
|
341
|
+
gas = BigInt(500000) * BigInt(calls.length) + BigInt(100000);
|
|
342
|
+
}
|
|
343
|
+
const unsignedTx = (0, viem_1.serializeTransaction)({
|
|
344
|
+
chainId,
|
|
345
|
+
type: 'eip7702',
|
|
346
|
+
to: account,
|
|
347
|
+
data,
|
|
348
|
+
gas,
|
|
349
|
+
nonce,
|
|
350
|
+
maxFeePerGas: feePerGas.maxFeePerGas,
|
|
351
|
+
maxPriorityFeePerGas: feePerGas.maxPriorityFeePerGas,
|
|
352
|
+
value: BigInt(0),
|
|
353
|
+
authorizationList: [authorization],
|
|
354
|
+
});
|
|
355
|
+
const txHashBytes = yield signer.signAndSendTransaction((0, viem_1.hexToBytes)(unsignedTx));
|
|
356
|
+
return (0, viem_1.bytesToHex)(txHashBytes);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
206
359
|
/**
|
|
207
360
|
* Estimates the gas required for a transaction.
|
|
208
361
|
*
|