@baliola/smart-account-sdk 0.3.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.
@@ -0,0 +1,25 @@
1
+ //#region src/auth/types.d.ts
2
+ /** Rejection reasons returned by baliola-auth `/api/api-keys/validate` when `valid: false`. */
3
+ type ApiKeyInvalidReason = "not_found" | "revoked" | "expired" | "wrong_module" | "module_inactive" | "origin_not_allowed" | "quota_exceeded" | "rate_limited";
4
+ interface ApiKeyQuota {
5
+ limitType: "periodic" | "lifetime";
6
+ period: "day" | "month" | "year";
7
+ /** Per-period (or lifetime) limit. `null` when uncapped. */
8
+ callLimit: number | null;
9
+ /** Remaining calls in the current period (or total). `null` when uncapped. */
10
+ callsRemaining: number | null;
11
+ /** ISO 8601 timestamp when the current period resets. */
12
+ periodResetsAt: string;
13
+ }
14
+ /** Validated key info — what the SDK exposes on `client.apiKey` after construction. */
15
+ interface ApiKeyInfo {
16
+ /** UUID of the API key record itself. */
17
+ id: string;
18
+ /** UUID of the baliola account that owns this key. */
19
+ accountId: string;
20
+ /** Module slug the key is minted against (always `"smart-account"` for this SDK). */
21
+ module: string;
22
+ quota: ApiKeyQuota;
23
+ }
24
+ //#endregion
25
+ export { ApiKeyInvalidReason as n, ApiKeyQuota as r, ApiKeyInfo as t };
@@ -0,0 +1,60 @@
1
+ import { Account, Address, Hex, PublicClient } from "viem";
2
+
3
+ //#region ../shared/src/user-op/types.d.ts
4
+ /**
5
+ * IUserOperation in the "unpacked" JSON-RPC wire form (ERC-4337 v0.7).
6
+ * This is what clients submit via eth_sendUserOperation.
7
+ */
8
+ interface IUserOperation {
9
+ sender: Address;
10
+ nonce: bigint;
11
+ factory?: Address;
12
+ factoryData?: Hex;
13
+ callData: Hex;
14
+ callGasLimit: bigint;
15
+ verificationGasLimit: bigint;
16
+ preVerificationGas: bigint;
17
+ maxFeePerGas: bigint;
18
+ maxPriorityFeePerGas: bigint;
19
+ paymaster?: Address;
20
+ paymasterVerificationGasLimit?: bigint;
21
+ paymasterPostOpGasLimit?: bigint;
22
+ paymasterData?: Hex;
23
+ signature: Hex;
24
+ }
25
+ //#endregion
26
+ //#region src/accounts/types.d.ts
27
+ /**
28
+ * Raw call tuple — identical to viem's `sendTransaction` input.
29
+ * `value` defaults to `0n` when omitted. `data` defaults to `"0x"`.
30
+ */
31
+ type Call = {
32
+ to: Address;
33
+ value?: bigint;
34
+ data?: Hex;
35
+ };
36
+ /** Viem `Account` narrowed to require `signMessage` (EIP-191 signing). */
37
+ type SigningAccount = Account & {
38
+ signMessage: NonNullable<Account["signMessage"]>;
39
+ };
40
+ /**
41
+ * A Smart Account instance bundles the counterfactual `sender` address,
42
+ * the first-use `factory` + `factoryData`, and the EIP-191 signing closure
43
+ * over `userOpHash`.
44
+ */
45
+ interface ISmartAccount {
46
+ readonly address: Address;
47
+ readonly owner: SigningAccount;
48
+ readonly factory: Address;
49
+ readonly factoryData: Hex;
50
+ readonly entryPoint: Address;
51
+ readonly nonceKey: bigint;
52
+ readonly chainId: number;
53
+ isDeployed(publicClient: PublicClient): Promise<boolean>;
54
+ markDeployed(): void;
55
+ signUserOperation(op: IUserOperation): Promise<Hex>;
56
+ encodeCalls(calls: Call[]): Hex;
57
+ getNonce(publicClient: PublicClient): Promise<bigint>;
58
+ }
59
+ //#endregion
60
+ export { IUserOperation as i, ISmartAccount as n, SigningAccount as r, Call as t };
@@ -0,0 +1,68 @@
1
+ import { n as BaliolaAuthUnreachableError, t as ApiKeyInvalidError } from "./base-v7RyiDFz.js";
2
+ //#region src/auth/authUrls.ts
3
+ /**
4
+ * Default baliola-auth base URLs per MAC chain. The SDK appends
5
+ * `/api/api-keys/validate` to whatever URL is configured.
6
+ */
7
+ const DEFAULT_AUTH_URLS = {
8
+ macTestnet: "https://baliola-auth.baliola.dev",
9
+ macMainnet: "https://auth.baliola.io"
10
+ };
11
+ /**
12
+ * Resolve the auth base URL for a chain, honoring an optional override
13
+ * (e.g. `http://localhost:8000` during dev). Trailing slashes are stripped
14
+ * so the caller can append a path safely.
15
+ */
16
+ function resolveAuthUrl(chainName, override) {
17
+ return (override ?? DEFAULT_AUTH_URLS[chainName]).replace(/\/+$/, "");
18
+ }
19
+ //#endregion
20
+ //#region src/auth/validateApiKey.ts
21
+ const SDK_SERVICE_LABEL = "smart-account-sdk";
22
+ const MODULE = "smart-account";
23
+ /**
24
+ * Validate a customer API key against `baliola-auth` and return the resolved
25
+ * key info on success. Throws `ApiKeyInvalidError` on a structured rejection
26
+ * (`valid: false` or `401`), and `BaliolaAuthUnreachableError` on network /
27
+ * transport failures (fetch rejection, non-2xx 5xx, unparseable body).
28
+ *
29
+ * One successful call charges one quota unit against the key — callers should
30
+ * cache the resulting client rather than re-invoking the factory.
31
+ */
32
+ async function validateApiKey(params) {
33
+ const url = `${params.authUrl}/api/api-keys/validate`;
34
+ let res;
35
+ try {
36
+ res = await fetch(url, {
37
+ method: "POST",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ Authorization: `Bearer ${params.apiKey}`
41
+ },
42
+ body: JSON.stringify({
43
+ requiredModule: MODULE,
44
+ service: SDK_SERVICE_LABEL
45
+ })
46
+ });
47
+ } catch (cause) {
48
+ throw new BaliolaAuthUnreachableError(url, cause);
49
+ }
50
+ if (res.status === 401) throw new ApiKeyInvalidError("not_found");
51
+ if (!res.ok) throw new BaliolaAuthUnreachableError(url, /* @__PURE__ */ new Error(`HTTP ${res.status}`));
52
+ let body;
53
+ try {
54
+ body = await res.json();
55
+ } catch (cause) {
56
+ throw new BaliolaAuthUnreachableError(url, cause);
57
+ }
58
+ const data = body.data;
59
+ if (!data.valid) throw new ApiKeyInvalidError(data.reason, data.retryAfterSeconds);
60
+ return {
61
+ id: data.apiKeyId,
62
+ accountId: data.accountId,
63
+ module: data.module,
64
+ quota: data.quota
65
+ };
66
+ }
67
+ //#endregion
68
+ export { DEFAULT_AUTH_URLS as n, resolveAuthUrl as r, validateApiKey as t };
@@ -0,0 +1,371 @@
1
+ import { t as entryPointAbi } from "./abi-ziHt832n.js";
2
+ import { a as UserOperationReceiptTimeoutError } from "./base-v7RyiDFz.js";
3
+ import { decodeEventLog, defineChain, encodeFunctionData, getAbiItem, hexToBigInt, numberToHex, toEventSelector } from "viem";
4
+ import "viem/accounts";
5
+ //#region src/actions/buildDraft.ts
6
+ /**
7
+ * Draft gas anchors. MAC's Substrate Frontier EVM runs the bundler's
8
+ * `eth_estimateUserOperationGas` path through `EntryPointSimulations` via
9
+ * `eth_call`, and the simulation fails if the packed `accountGasLimits` are
10
+ * zero. Seeding the draft with generous values lets the simulation complete
11
+ * and come back with precise estimates that replace these on submit.
12
+ *
13
+ * Values mirror `bundler/scripts/demo-userop.ts`, which has validated
14
+ * against live MAC Testnet end-to-end.
15
+ */
16
+ const DRAFT_CALL_GAS_LIMIT = 200000n;
17
+ const DRAFT_VERIFICATION_GAS_LIMIT = 900000n;
18
+ const DRAFT_PRE_VERIFICATION_GAS = 150000n;
19
+ const DRAFT_GAS_ANCHORS = {
20
+ callGasLimit: DRAFT_CALL_GAS_LIMIT,
21
+ verificationGasLimit: DRAFT_VERIFICATION_GAS_LIMIT,
22
+ preVerificationGas: DRAFT_PRE_VERIFICATION_GAS,
23
+ paymasterVerificationGasLimit: 200000n,
24
+ paymasterPostOpGasLimit: 100000n
25
+ };
26
+ /**
27
+ * 65-byte dummy signature used during `eth_estimateUserOperationGas`.
28
+ *
29
+ * SimpleAccount's `_validateSignature` calls OpenZeppelin 5.0.2's
30
+ * `ECDSA.recover(hash, signature)`, which enforces EIP-2 low-`s` (reverts
31
+ * when `s > n/2`) and rejects zero-recovery. A naive dummy like `0xfa * 64`
32
+ * has a high `s` and causes the simulation to revert before the account
33
+ * can return `SIG_VALIDATION_FAILED`.
34
+ *
35
+ * This dummy uses `r = 1`, `s = 1`, `v = 27` — all within OZ's accepted
36
+ * ranges. `ecrecover` returns a valid, non-zero address that almost
37
+ * certainly is not the owner, so the account returns
38
+ * `SIG_VALIDATION_FAILED` cleanly and the simulator produces gas numbers.
39
+ */
40
+ const DUMMY_SIGNATURE = "0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011b";
41
+ /**
42
+ * Build an unsigned, un-paymastered, pre-estimate UserOperation from raw
43
+ * calls. Gas limits are seeded to generous defaults so the bundler's
44
+ * estimator can simulate through MAC's Frontier EVM and return precise
45
+ * values; fee fields default to the chain's `gasPrice` (MAC reports legacy
46
+ * pricing, so `maxFee` and `maxPriorityFee` are equal).
47
+ */
48
+ async function buildDraftUserOp(ctx, calls, overrides = {}) {
49
+ const callData = overrides.callData ?? ctx.account.encodeCalls(calls);
50
+ const [nonce, deployed] = await Promise.all([overrides.nonce !== void 0 ? Promise.resolve(overrides.nonce) : ctx.account.getNonce(ctx.publicClient), ctx.account.isDeployed(ctx.publicClient)]);
51
+ const needsFactory = overrides.factory === void 0 ? !deployed : true;
52
+ const factory = overrides.factory ?? (needsFactory ? ctx.account.factory : void 0);
53
+ const factoryData = factory ? overrides.factoryData ?? ctx.account.factoryData : void 0;
54
+ const gasPrice = overrides.maxFeePerGas ?? overrides.maxPriorityFeePerGas ?? await ctx.publicClient.getGasPrice();
55
+ const draft = {
56
+ sender: ctx.account.address,
57
+ nonce,
58
+ callData,
59
+ callGasLimit: overrides.callGasLimit ?? DRAFT_CALL_GAS_LIMIT,
60
+ verificationGasLimit: overrides.verificationGasLimit ?? DRAFT_VERIFICATION_GAS_LIMIT,
61
+ preVerificationGas: overrides.preVerificationGas ?? DRAFT_PRE_VERIFICATION_GAS,
62
+ maxFeePerGas: overrides.maxFeePerGas ?? gasPrice,
63
+ maxPriorityFeePerGas: overrides.maxPriorityFeePerGas ?? gasPrice,
64
+ signature: overrides.signature ?? "0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000011b"
65
+ };
66
+ if (factory) {
67
+ draft.factory = factory;
68
+ draft.factoryData = factoryData ?? "0x";
69
+ }
70
+ return draft;
71
+ }
72
+ //#endregion
73
+ //#region src/clients/rpcCodec.ts
74
+ function toRpcUserOp(op) {
75
+ const rpc = {
76
+ sender: op.sender,
77
+ nonce: numberToHex(op.nonce),
78
+ callData: op.callData,
79
+ callGasLimit: numberToHex(op.callGasLimit),
80
+ verificationGasLimit: numberToHex(op.verificationGasLimit),
81
+ preVerificationGas: numberToHex(op.preVerificationGas),
82
+ maxFeePerGas: numberToHex(op.maxFeePerGas),
83
+ maxPriorityFeePerGas: numberToHex(op.maxPriorityFeePerGas),
84
+ signature: op.signature
85
+ };
86
+ if (op.factory) {
87
+ rpc.factory = op.factory;
88
+ rpc.factoryData = op.factoryData ?? "0x";
89
+ }
90
+ if (op.paymaster) {
91
+ rpc.paymaster = op.paymaster;
92
+ rpc.paymasterVerificationGasLimit = numberToHex(op.paymasterVerificationGasLimit ?? 0n);
93
+ rpc.paymasterPostOpGasLimit = numberToHex(op.paymasterPostOpGasLimit ?? 0n);
94
+ rpc.paymasterData = op.paymasterData ?? "0x";
95
+ }
96
+ return rpc;
97
+ }
98
+ function fromRpcUserOp(rpc) {
99
+ const op = {
100
+ sender: rpc.sender,
101
+ nonce: hexToBigInt(rpc.nonce),
102
+ callData: rpc.callData,
103
+ callGasLimit: hexToBigInt(rpc.callGasLimit),
104
+ verificationGasLimit: hexToBigInt(rpc.verificationGasLimit),
105
+ preVerificationGas: hexToBigInt(rpc.preVerificationGas),
106
+ maxFeePerGas: hexToBigInt(rpc.maxFeePerGas),
107
+ maxPriorityFeePerGas: hexToBigInt(rpc.maxPriorityFeePerGas),
108
+ signature: rpc.signature
109
+ };
110
+ if (rpc.factory) {
111
+ op.factory = rpc.factory;
112
+ op.factoryData = rpc.factoryData ?? "0x";
113
+ }
114
+ if (rpc.paymaster) {
115
+ op.paymaster = rpc.paymaster;
116
+ op.paymasterVerificationGasLimit = hexToBigInt(rpc.paymasterVerificationGasLimit ?? "0x0");
117
+ op.paymasterPostOpGasLimit = hexToBigInt(rpc.paymasterPostOpGasLimit ?? "0x0");
118
+ op.paymasterData = rpc.paymasterData ?? "0x";
119
+ }
120
+ return op;
121
+ }
122
+ //#endregion
123
+ //#region src/actions/estimateUserOperationGas.ts
124
+ /**
125
+ * Low-level estimate that skips paymaster resolution — callers that want
126
+ * the full `{ calls, overrides }` surface should use the higher-level
127
+ * `estimateUserOperationGas` action below.
128
+ */
129
+ async function estimateUserOpGasRaw(ctx, op) {
130
+ const rpc = toRpcUserOp(op);
131
+ const raw = await ctx.bundler.request("eth_estimateUserOperationGas", [rpc, ctx.entryPoint]);
132
+ const estimate = {
133
+ callGasLimit: hexToBigInt(raw.callGasLimit),
134
+ verificationGasLimit: hexToBigInt(raw.verificationGasLimit),
135
+ preVerificationGas: hexToBigInt(raw.preVerificationGas)
136
+ };
137
+ if (raw.paymasterVerificationGasLimit !== void 0) estimate.paymasterVerificationGasLimit = hexToBigInt(raw.paymasterVerificationGasLimit);
138
+ if (raw.paymasterPostOpGasLimit !== void 0) estimate.paymasterPostOpGasLimit = hexToBigInt(raw.paymasterPostOpGasLimit);
139
+ return estimate;
140
+ }
141
+ async function estimateUserOperationGas(ctx, params) {
142
+ const draft = await buildDraftUserOp(ctx, params.calls, params.overrides);
143
+ return estimateUserOpGasRaw(ctx, params.paymasterFields ? {
144
+ ...draft,
145
+ ...params.paymasterFields
146
+ } : draft);
147
+ }
148
+ defineChain({
149
+ id: 20017,
150
+ name: "MAC Testnet",
151
+ nativeCurrency: {
152
+ name: "Kepeng Testnet",
153
+ symbol: "KPGBT",
154
+ decimals: 18
155
+ },
156
+ rpcUrls: { default: { http: ["https://collator1.baliola.dev"] } },
157
+ testnet: true
158
+ });
159
+ defineChain({
160
+ id: 20016,
161
+ name: "MAC",
162
+ nativeCurrency: {
163
+ name: "Kepeng",
164
+ symbol: "KPGB",
165
+ decimals: 18
166
+ },
167
+ rpcUrls: { default: { http: ["https://collator4-mac.baliola.io"] } },
168
+ testnet: false
169
+ });
170
+ getAbiItem({
171
+ abi: entryPointAbi,
172
+ name: "UserOperationEvent"
173
+ });
174
+ /**
175
+ * Slice receipt logs for one userop's execution frame: all logs between the
176
+ * BeforeExecution marker (or the prior UserOperationEvent) and this op's
177
+ * UserOperationEvent.
178
+ */
179
+ function sliceUserOpLogs(receiptLogs, userOpHash) {
180
+ const beforeExecutionTopic = toEventSelector("BeforeExecution()");
181
+ const userOpEventTopic = toEventSelector("UserOperationEvent(bytes32,address,address,uint256,bool,uint256,uint256)");
182
+ let startIdx = -1;
183
+ for (let i = 0; i < receiptLogs.length; i++) {
184
+ const log = receiptLogs[i];
185
+ if (!log) continue;
186
+ if (log.topics[0] === beforeExecutionTopic) {
187
+ startIdx = i;
188
+ continue;
189
+ }
190
+ if (log.topics[0] === userOpEventTopic) {
191
+ if (log.topics[1] === userOpHash) return receiptLogs.slice(startIdx + 1, i);
192
+ startIdx = i;
193
+ }
194
+ }
195
+ return [];
196
+ }
197
+ //#endregion
198
+ //#region src/actions/getUserOperationReceipt.ts
199
+ async function getUserOperationReceipt(ctx, { userOpHash }) {
200
+ const raw = await ctx.bundler.request("eth_getUserOperationReceipt", [userOpHash]);
201
+ if (!raw) return null;
202
+ return decodeReceipt(raw, userOpHash);
203
+ }
204
+ function decodeReceipt(raw, userOpHash) {
205
+ const tx = raw.receipt;
206
+ const bundleLogs = tx.logs ?? [];
207
+ const preSliced = raw.logs ?? [];
208
+ const logs = preSliced.length > 0 ? preSliced : sliceUserOpLogs(bundleLogs, userOpHash);
209
+ const receipt = {
210
+ userOpHash: raw.userOpHash,
211
+ sender: raw.sender,
212
+ nonce: hexToBigInt(raw.nonce),
213
+ success: raw.success,
214
+ actualGasUsed: hexToBigInt(raw.actualGasUsed),
215
+ actualGasCost: hexToBigInt(raw.actualGasCost),
216
+ txHash: tx.transactionHash,
217
+ blockNumber: typeof tx.blockNumber === "bigint" ? tx.blockNumber : hexToBigInt(tx.blockNumber),
218
+ blockHash: tx.blockHash,
219
+ logs
220
+ };
221
+ if (raw.paymaster) receipt.paymaster = raw.paymaster;
222
+ if (raw.reason) receipt.reason = raw.reason;
223
+ return receipt;
224
+ }
225
+ //#endregion
226
+ //#region src/actions/waitForUserOperationReceipt.ts
227
+ const DEFAULT_TIMEOUT_MS = 6e4;
228
+ const DEFAULT_POLL_MS = 1e3;
229
+ async function waitForUserOperationReceipt(ctx, params) {
230
+ const timeout = params.timeout ?? DEFAULT_TIMEOUT_MS;
231
+ const pollingInterval = params.pollingInterval ?? DEFAULT_POLL_MS;
232
+ const deadline = Date.now() + timeout;
233
+ while (true) {
234
+ if (params.signal?.aborted) throw params.signal.reason ?? /* @__PURE__ */ new Error("aborted");
235
+ const receipt = await getUserOperationReceipt(ctx, { userOpHash: params.userOpHash });
236
+ if (receipt) return receipt;
237
+ if (Date.now() >= deadline) throw new UserOperationReceiptTimeoutError(params.userOpHash, timeout);
238
+ await sleep(pollingInterval, params.signal);
239
+ }
240
+ }
241
+ function sleep(ms, signal) {
242
+ return new Promise((resolve, reject) => {
243
+ const timer = setTimeout(() => {
244
+ signal?.removeEventListener("abort", onAbort);
245
+ resolve();
246
+ }, ms);
247
+ const onAbort = () => {
248
+ clearTimeout(timer);
249
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("aborted"));
250
+ };
251
+ if (signal) signal.addEventListener("abort", onAbort, { once: true });
252
+ });
253
+ }
254
+ //#endregion
255
+ //#region src/actions/watchUserOperations.ts
256
+ const userOperationEventAbiItem = getAbiItem({
257
+ abi: entryPointAbi,
258
+ name: "UserOperationEvent"
259
+ });
260
+ function watchUserOperations(ctx, params) {
261
+ const sender = params.sender ?? (params.userOpHash ? void 0 : ctx.account.address);
262
+ const args = {};
263
+ if (sender) args.sender = sender;
264
+ if (params.userOpHash) args.userOpHash = params.userOpHash;
265
+ return ctx.publicClient.watchEvent({
266
+ address: ctx.entryPoint,
267
+ event: userOperationEventAbiItem,
268
+ args,
269
+ onLogs(logs) {
270
+ for (const log of logs) try {
271
+ const match = decodeMatch(log);
272
+ params.onUserOp(match);
273
+ } catch (err) {
274
+ params.onError?.(err);
275
+ }
276
+ },
277
+ ...params.onError ? { onError: params.onError } : {}
278
+ });
279
+ }
280
+ function decodeMatch(log) {
281
+ const args = decodeEventLog({
282
+ abi: entryPointAbi,
283
+ eventName: "UserOperationEvent",
284
+ data: log.data,
285
+ topics: log.topics
286
+ }).args;
287
+ return {
288
+ userOpHash: args.userOpHash,
289
+ sender: args.sender,
290
+ paymaster: args.paymaster,
291
+ nonce: args.nonce,
292
+ success: args.success,
293
+ actualGasCost: args.actualGasCost,
294
+ actualGasUsed: args.actualGasUsed,
295
+ log
296
+ };
297
+ }
298
+ //#endregion
299
+ //#region src/actions/sendUserOperation.ts
300
+ /**
301
+ * Internal engine: build draft → resolve paymaster → estimate gas → sign → submit.
302
+ * Public callers use `writeContract`; this stays internal so we own the lifecycle.
303
+ */
304
+ async function sendUserOperation(ctx, { calls, overrides }) {
305
+ const draft = await buildDraftUserOp(ctx, calls, overrides);
306
+ const paymasterFields = overrides?.paymasterFields ?? await ctx.paymaster({
307
+ userOp: draft,
308
+ entryPoint: ctx.entryPoint,
309
+ chainId: ctx.account.chainId
310
+ });
311
+ const withPaymaster = {
312
+ ...draft,
313
+ ...paymasterFields,
314
+ paymasterVerificationGasLimit: paymasterFields.paymasterVerificationGasLimit ?? DRAFT_GAS_ANCHORS.paymasterVerificationGasLimit,
315
+ paymasterPostOpGasLimit: paymasterFields.paymasterPostOpGasLimit ?? DRAFT_GAS_ANCHORS.paymasterPostOpGasLimit
316
+ };
317
+ const estimate = await estimateUserOpGasRaw(ctx, withPaymaster);
318
+ const final = {
319
+ ...withPaymaster,
320
+ callGasLimit: overrides?.callGasLimit ?? estimate.callGasLimit,
321
+ verificationGasLimit: overrides?.verificationGasLimit ?? estimate.verificationGasLimit,
322
+ preVerificationGas: overrides?.preVerificationGas ?? estimate.preVerificationGas,
323
+ paymasterVerificationGasLimit: overrides?.paymasterVerificationGasLimit ?? paymasterFields.paymasterVerificationGasLimit ?? estimate.paymasterVerificationGasLimit ?? DRAFT_GAS_ANCHORS.paymasterVerificationGasLimit,
324
+ paymasterPostOpGasLimit: overrides?.paymasterPostOpGasLimit ?? paymasterFields.paymasterPostOpGasLimit ?? estimate.paymasterPostOpGasLimit ?? DRAFT_GAS_ANCHORS.paymasterPostOpGasLimit
325
+ };
326
+ final.signature = await ctx.account.signUserOperation(final);
327
+ return ctx.bundler.request("eth_sendUserOperation", [toRpcUserOp(final), ctx.entryPoint]);
328
+ }
329
+ //#endregion
330
+ //#region src/actions/writeContract.ts
331
+ function isTypedCall(entry) {
332
+ return "abi" in entry;
333
+ }
334
+ function normalizeEntry(entry) {
335
+ if (isTypedCall(entry)) {
336
+ const data = encodeFunctionData({
337
+ abi: entry.abi,
338
+ functionName: entry.functionName,
339
+ args: entry.args
340
+ });
341
+ return entry.value !== void 0 ? {
342
+ to: entry.address,
343
+ value: entry.value,
344
+ data
345
+ } : {
346
+ to: entry.address,
347
+ data
348
+ };
349
+ }
350
+ return entry;
351
+ }
352
+ async function writeContractImpl(ctx, input, overrides) {
353
+ const entries = Array.isArray(input) ? input : [input];
354
+ if (entries.length === 0) throw new Error("writeContract: input array must not be empty");
355
+ return sendUserOperation(ctx, {
356
+ calls: entries.map(normalizeEntry),
357
+ overrides
358
+ });
359
+ }
360
+ function writeContract(ctx, input, overrides) {
361
+ return writeContractImpl(ctx, input, overrides);
362
+ }
363
+ /** Build a context-bound `writeContract` for use on `SmartAccountClient`. */
364
+ function bindWriteContract(ctx) {
365
+ function bound(input, overrides) {
366
+ return writeContractImpl(ctx, input, overrides);
367
+ }
368
+ return bound;
369
+ }
370
+ //#endregion
371
+ export { getUserOperationReceipt as a, estimateUserOperationGas as c, DRAFT_GAS_ANCHORS as d, DUMMY_SIGNATURE as f, waitForUserOperationReceipt as i, fromRpcUserOp as l, writeContract as n, sliceUserOpLogs as o, buildDraftUserOp as p, watchUserOperations as r, estimateUserOpGasRaw as s, bindWriteContract as t, toRpcUserOp as u };