smart-multisig-engine 0.1.0

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/README.md ADDED
@@ -0,0 +1,409 @@
1
+ # smart-multisig-engine
2
+
3
+ A TypeScript utility library for submitting transactions to Safe multisig wallets and retrieving their `safeTxHash` from the Safe Transaction Service.
4
+
5
+ Built on [wagmi](https://wagmi.sh) and [viem](https://viem.sh).
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add smart-multisig-engine
11
+ ```
12
+
13
+ Peer dependencies: `@wagmi/core`, `viem`.
14
+
15
+ ## Quick Start
16
+
17
+ Use `submitTx` with the generic adapter interface to submit transactions to any supported multisig:
18
+
19
+ ```typescript
20
+ import { submitTx, waitForExecution } from "smart-multisig-engine";
21
+ import { config } from "./wagmi-config";
22
+
23
+ // Submit a transaction
24
+ const { txHash } = await submitTx({
25
+ adapter: "safe",
26
+ config,
27
+ walletAddress: "0xYourSafeAddress",
28
+ address: "0xTargetContract",
29
+ abi: contractAbi,
30
+ functionName: "transfer",
31
+ args: [recipientAddress, amount],
32
+ value: 0n,
33
+ chainId: 1n,
34
+ });
35
+
36
+ console.log("Safe TX Hash:", txHash);
37
+
38
+ // Wait for execution
39
+ const { transactionHash } = await waitForExecution({
40
+ adapter: "safe",
41
+ txHash,
42
+ chainId: 1n,
43
+ });
44
+
45
+ console.log("On-chain TX Hash:", transactionHash);
46
+ ```
47
+
48
+ Or use the Safe adapter directly for more control:
49
+
50
+ ```typescript
51
+ import { safe } from "smart-multisig-engine";
52
+ import { config } from "./wagmi-config";
53
+
54
+ const { safeTxHash, txHash } = await safe.submitAndFindSafeTx({
55
+ config,
56
+ safeAddress: "0xYourSafeAddress",
57
+ address: "0xTargetContract",
58
+ abi: contractAbi,
59
+ functionName: "transfer",
60
+ args: [recipientAddress, amount],
61
+ value: 0n,
62
+ chainId: 1,
63
+ });
64
+
65
+ console.log("Safe TX Hash:", safeTxHash);
66
+ console.log("On-chain TX Hash:", txHash);
67
+ ```
68
+
69
+ ## Architecture
70
+
71
+ The library follows a **core/adapter** pattern:
72
+
73
+ - **Core** — Pure functions with no I/O. Can be used independently for encoding, matching, or URL resolution.
74
+ - **Adapters** — I/O implementations that wrap contract calls and external APIs. Currently supports Safe multisig.
75
+
76
+ ### Generic Adapter Interface
77
+
78
+ The library provides adapter-agnostic entry points that route to the appropriate implementation:
79
+
80
+ ```typescript
81
+ import { submitTx, waitForExecution, fetchPendingTxs, simulate, write } from "smart-multisig-engine";
82
+
83
+ // All functions take an `adapter` parameter to select the implementation
84
+ await submitTx({ adapter: "safe", ... });
85
+ await waitForExecution({ adapter: "safe", ... });
86
+ await fetchPendingTxs({ adapter: "safe", ... });
87
+ await simulate({ adapter: "safe", ... });
88
+ await write({ adapter: "safe", ... });
89
+ ```
90
+
91
+ ### Direct Adapter Access
92
+
93
+ For adapter-specific features, import the adapter namespace directly:
94
+
95
+ ```typescript
96
+ import { safe } from "smart-multisig-engine";
97
+
98
+ // Access Safe-specific functions
99
+ await safe.submitAndFindSafeTx({ ... });
100
+ await safe.waitForExecution({ ... });
101
+ await safe.fetchPendingTransactions({ ... });
102
+ await safe.simulateContractCall(config, { ... });
103
+ await safe.writeContractCall(config, request);
104
+ ```
105
+
106
+ ## API Reference
107
+
108
+ ### Generic Adapter Functions
109
+
110
+ These functions provide a unified interface across all adapters.
111
+
112
+ #### `submitTx(options): Promise<SubmitTxResult>`
113
+
114
+ Submit a transaction through the specified adapter.
115
+
116
+ ```typescript
117
+ interface SubmitTxOptions {
118
+ adapter: "safe"; // Adapter type
119
+ config: Config; // wagmi Config instance
120
+ walletAddress: Address; // The multisig wallet address
121
+ address: Address; // Target contract address
122
+ abi: Abi; // Contract ABI
123
+ functionName: string; // Function to call
124
+ args?: readonly unknown[]; // Function arguments
125
+ value?: bigint; // ETH value (default: 0n)
126
+ chainId?: number; // Chain ID
127
+ txServiceUrl?: string; // Override service URL
128
+ apiKey?: string; // API key for the service
129
+ pollingInterval?: number; // Polling interval in ms
130
+ maxAttempts?: number; // Max poll attempts
131
+ }
132
+
133
+ interface SubmitTxResult {
134
+ txHash: string; // The transaction hash (safeTxHash for Safe)
135
+ }
136
+ ```
137
+
138
+ #### `waitForExecution(options): Promise<WaitForExecutionResult>`
139
+
140
+ Wait for a transaction to be executed on-chain.
141
+
142
+ ```typescript
143
+ interface WaitForExecutionOptions {
144
+ adapter: "safe"; // Adapter type
145
+ txHash: string; // Transaction hash to wait for
146
+ chainId: bigint; // Chain ID
147
+ txServiceUrl?: string; // Override service URL
148
+ apiKey?: string; // API key for the service
149
+ pollingInterval?: number; // Polling interval in ms (default: 5000)
150
+ maxAttempts?: number; // Max poll attempts (default: 60)
151
+ }
152
+
153
+ interface WaitForExecutionResult {
154
+ transactionHash: string; // The on-chain transaction hash
155
+ }
156
+ ```
157
+
158
+ #### `fetchPendingTxs(options): Promise<unknown[]>`
159
+
160
+ Fetch pending transactions for a wallet.
161
+
162
+ ```typescript
163
+ interface FetchPendingOptions {
164
+ adapter: "safe"; // Adapter type
165
+ walletAddress: string; // The multisig wallet address
166
+ chainId: bigint; // Chain ID
167
+ txServiceUrl?: string; // Override service URL
168
+ apiKey?: string; // API key for the service
169
+ }
170
+ ```
171
+
172
+ #### `simulate(options): Promise<unknown>`
173
+
174
+ Simulate a contract call before submission.
175
+
176
+ ```typescript
177
+ interface SimulateOptions {
178
+ adapter: "safe"; // Adapter type
179
+ config: Config; // wagmi Config instance
180
+ address: Address; // Target contract address
181
+ abi: Abi; // Contract ABI
182
+ functionName: string; // Function to call
183
+ args?: readonly unknown[]; // Function arguments
184
+ value?: bigint; // ETH value
185
+ chainId?: number; // Chain ID
186
+ }
187
+ ```
188
+
189
+ #### `write(options): Promise<string>`
190
+
191
+ Write a contract call (submit the transaction).
192
+
193
+ ```typescript
194
+ interface WriteOptions {
195
+ adapter: "safe"; // Adapter type
196
+ config: Config; // wagmi Config instance
197
+ request: unknown; // Request from simulation result
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ### Safe Adapter
204
+
205
+ #### `safe.submitAndFindSafeTx(options): Promise<SubmitAndFindSafeTxResult>`
206
+
207
+ Full flow: simulate → write → poll Safe TX Service → match → return `safeTxHash`.
208
+
209
+ ```typescript
210
+ interface SubmitAndFindSafeTxOptions {
211
+ config: Config; // wagmi Config instance
212
+ safeAddress: Address; // The Safe wallet address to poll
213
+ address: Address; // Target contract address
214
+ abi: Abi; // Contract ABI
215
+ functionName: string; // Function to call
216
+ args?: readonly unknown[]; // Function arguments
217
+ value?: bigint; // ETH value (default: 0n)
218
+ chainId?: number; // Chain ID (auto-detected from config if omitted)
219
+ account?: Address; // Signer address
220
+ serviceUrl?: string; // Override Safe TX Service URL
221
+ pollingInterval?: number; // Polling interval in ms (default: 3000)
222
+ maxAttempts?: number; // Max poll attempts (default: 20)
223
+ }
224
+
225
+ interface SubmitAndFindSafeTxResult {
226
+ safeTxHash: string; // The Safe transaction hash
227
+ txHash: `0x${string}`; // The on-chain transaction hash
228
+ }
229
+ ```
230
+
231
+ #### `safe.waitForExecution(options): Promise<WaitForExecutionResult>`
232
+
233
+ Poll the Safe Transaction Service until a transaction is executed.
234
+
235
+ ```typescript
236
+ interface WaitForExecutionOptions {
237
+ safeTxHash: string; // The Safe transaction hash to monitor
238
+ chainId: bigint; // Chain ID
239
+ txServiceUrl?: string; // Override Safe TX Service URL
240
+ apiKey?: string; // API key for the Safe service
241
+ pollingInterval?: number; // Polling interval in ms (default: 5000)
242
+ maxAttempts?: number; // Max poll attempts (default: 60)
243
+ }
244
+
245
+ interface WaitForExecutionResult {
246
+ transactionHash: string; // The on-chain transaction hash
247
+ }
248
+ ```
249
+
250
+ ---
251
+
252
+ ### Core Utilities
253
+
254
+ #### `extractCallData(params): EncodedCallData`
255
+
256
+ Encodes a contract function call into `{ to, data, value }` using viem's `encodeFunctionData`. Pure function, no network calls.
257
+
258
+ ```typescript
259
+ import { extractCallData } from "smart-multisig-engine";
260
+
261
+ const callData = extractCallData({
262
+ abi: myAbi,
263
+ functionName: "transfer",
264
+ args: [recipient, amount],
265
+ address: "0xContractAddress",
266
+ value: 0n,
267
+ });
268
+ // { to: "0x...", data: "0x...", value: 0n }
269
+ ```
270
+
271
+ #### `matchPendingTransaction(pendingTxs, criteria): SafePendingTransaction | undefined`
272
+
273
+ Finds a pending Safe transaction matching `{ to, value, data }`. Comparison is case-insensitive for addresses and converts `bigint` value to string for matching.
274
+
275
+ ```typescript
276
+ import { matchPendingTransaction } from "smart-multisig-engine";
277
+
278
+ const match = matchPendingTransaction(pendingTransactions, {
279
+ to: "0xContractAddress",
280
+ value: 0n,
281
+ data: "0xEncodedCalldata",
282
+ });
283
+ ```
284
+
285
+ #### `getSafeServiceUrl(chainId): string`
286
+
287
+ Resolves the Safe Transaction Service base URL for a chain ID. Throws if the chain is not in the known list.
288
+
289
+ ```typescript
290
+ import { getSafeServiceUrl } from "smart-multisig-engine";
291
+
292
+ getSafeServiceUrl(1); // "https://safe-transaction-mainnet.safe.global"
293
+ getSafeServiceUrl(11155111); // "https://safe-transaction-sepolia.safe.global"
294
+ ```
295
+
296
+ #### `SAFE_TX_SERVICE_URLS`
297
+
298
+ The `Record<number, string>` map of supported chain IDs:
299
+
300
+ | Chain ID | Network |
301
+ |----------|---------|
302
+ | 1 | Ethereum Mainnet |
303
+ | 10 | Optimism |
304
+ | 56 | BSC |
305
+ | 100 | Gnosis Chain |
306
+ | 137 | Polygon |
307
+ | 8453 | Base |
308
+ | 42161 | Arbitrum |
309
+ | 11155111 | Sepolia |
310
+ | 84532 | Base Sepolia |
311
+
312
+ ---
313
+
314
+ ### Safe Low-Level Utilities
315
+
316
+ #### `safe.simulateContractCall(config, params)`
317
+
318
+ Wraps wagmi's `simulateContract`. Validates the transaction will succeed before submission.
319
+
320
+ ```typescript
321
+ import { safe } from "smart-multisig-engine";
322
+
323
+ const simulation = await safe.simulateContractCall(config, {
324
+ address: "0xContract",
325
+ abi: myAbi,
326
+ functionName: "transfer",
327
+ args: [recipient, amount],
328
+ });
329
+ ```
330
+
331
+ #### `safe.writeContractCall(config, request): Promise<Hex>`
332
+
333
+ Wraps wagmi's `writeContract`. Accepts the `request` from a prior simulation result.
334
+
335
+ ```typescript
336
+ import { safe } from "smart-multisig-engine";
337
+
338
+ const txHash = await safe.writeContractCall(config, simulation.request);
339
+ ```
340
+
341
+ #### `safe.fetchPendingTransactions(options): Promise<SafePendingTransaction[]>`
342
+
343
+ Fetches pending (unexecuted) multisig transactions from the Safe Transaction Service REST API.
344
+
345
+ ```typescript
346
+ import { safe } from "smart-multisig-engine";
347
+
348
+ const pending = await safe.fetchPendingTransactions({
349
+ safeAddress: "0xSafeAddress",
350
+ chainId: 1n,
351
+ apiKey: "optional-api-key",
352
+ });
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Using Individual Bricks
358
+
359
+ The high-level functions are convenience wrappers. You can compose the bricks yourself for custom flows:
360
+
361
+ ```typescript
362
+ import {
363
+ extractCallData,
364
+ matchPendingTransaction,
365
+ safe,
366
+ } from "smart-multisig-engine";
367
+
368
+ // 1. Encode the call data
369
+ const callData = extractCallData({ abi, functionName, args, address, value });
370
+
371
+ // 2. Simulate
372
+ const simulation = await safe.simulateContractCall(config, { abi, functionName, args, address, value });
373
+
374
+ // 3. Submit on-chain
375
+ const txHash = await safe.writeContractCall(config, simulation.request);
376
+
377
+ // 4. Poll for the pending Safe transaction
378
+ const pending = await safe.fetchPendingTransactions({
379
+ safeAddress,
380
+ chainId: 1n,
381
+ });
382
+
383
+ // 5. Match
384
+ const match = matchPendingTransaction(pending, callData);
385
+ console.log(match?.safeTxHash);
386
+
387
+ // 6. Wait for execution
388
+ const result = await safe.waitForExecution({
389
+ safeTxHash: match.safeTxHash,
390
+ chainId: 1n,
391
+ });
392
+ console.log("Executed:", result.transactionHash);
393
+ ```
394
+
395
+ ## Development
396
+
397
+ ```bash
398
+ pnpm install
399
+ pnpm build # Bundle with tsup (ESM + CJS + .d.ts)
400
+ pnpm dev # Watch mode
401
+ pnpm test # Run vitest (watch mode)
402
+ pnpm test --run # Run tests once
403
+ pnpm typecheck # tsc --noEmit
404
+ pnpm clean # Remove dist/
405
+ ```
406
+
407
+ ## License
408
+
409
+ ISC
@@ -0,0 +1,210 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/core/extract-call-data.ts
8
+ import { encodeFunctionData } from "viem";
9
+ function extractCallData(params) {
10
+ const data = encodeFunctionData({
11
+ abi: params.abi,
12
+ functionName: params.functionName,
13
+ args: params.args
14
+ });
15
+ return {
16
+ to: params.address,
17
+ data,
18
+ value: params.value ?? 0n
19
+ };
20
+ }
21
+
22
+ // src/core/match-pending-tx.ts
23
+ function matchPendingTransaction(pendingTxs, criteria) {
24
+ return pendingTxs.find(
25
+ (tx) => tx.to.toLowerCase() === criteria.to.toLowerCase() && tx.value === String(criteria.value) && tx.data?.toLowerCase() === criteria.data.toLowerCase()
26
+ );
27
+ }
28
+
29
+ // src/adapters/safe/index.ts
30
+ var safe_exports = {};
31
+ __export(safe_exports, {
32
+ fetchPendingTransactions: () => fetchPendingTransactions,
33
+ simulateContractCall: () => simulateContractCall,
34
+ submitAndFindSafeTx: () => submitAndFindSafeTx,
35
+ waitForExecution: () => waitForExecution,
36
+ writeContractCall: () => writeContractCall
37
+ });
38
+
39
+ // src/adapters/safe/simulate-contract-call.ts
40
+ import { simulateContract } from "@wagmi/core";
41
+ async function simulateContractCall(config, params) {
42
+ return simulateContract(config, {
43
+ address: params.address,
44
+ abi: params.abi,
45
+ functionName: params.functionName,
46
+ args: params.args,
47
+ value: params.value,
48
+ chainId: params.chainId,
49
+ account: params.account
50
+ });
51
+ }
52
+
53
+ // src/adapters/safe/write-contract-call.ts
54
+ import { writeContract } from "@wagmi/core";
55
+ async function writeContractCall(config, request) {
56
+ return writeContract(config, request);
57
+ }
58
+
59
+ // src/adapters/safe/fetch-pending-transactions.ts
60
+ import SafeApiKit from "@safe-global/api-kit";
61
+ async function fetchPendingTransactions(params) {
62
+ const apiKit = new SafeApiKit({
63
+ chainId: params.chainId,
64
+ ...params.txServiceUrl && { txServiceUrl: params.txServiceUrl },
65
+ apiKey: params.apiKey
66
+ });
67
+ const response = await apiKit.getPendingTransactions(params.safeAddress);
68
+ return response.results;
69
+ }
70
+
71
+ // src/adapters/safe/submit-and-find-safe-tx.ts
72
+ function delay(ms) {
73
+ return new Promise((resolve) => setTimeout(resolve, ms));
74
+ }
75
+ async function submitAndFindSafeTx(options) {
76
+ const {
77
+ config,
78
+ safeAddress,
79
+ txServiceUrl,
80
+ apiKey,
81
+ pollingInterval = 3e3,
82
+ maxAttempts = 20,
83
+ ...callParams
84
+ } = options;
85
+ const callData = extractCallData(callParams);
86
+ const simulation = await simulateContractCall(config, callParams);
87
+ await writeContractCall(config, simulation.request);
88
+ const chainId = BigInt(callParams.chainId ?? await getChainId(config));
89
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
90
+ await delay(pollingInterval);
91
+ const pendingTxs = await fetchPendingTransactions({
92
+ chainId,
93
+ safeAddress,
94
+ txServiceUrl,
95
+ apiKey
96
+ });
97
+ const match = matchPendingTransaction(pendingTxs, callData);
98
+ if (match) {
99
+ return {
100
+ safeTxHash: match.safeTxHash
101
+ };
102
+ }
103
+ }
104
+ throw new Error(
105
+ `Could not find matching Safe transaction after ${maxAttempts} attempts`
106
+ );
107
+ }
108
+ async function getChainId(config) {
109
+ const { getChainId: wagmiGetChainId } = await import("@wagmi/core");
110
+ return wagmiGetChainId(config);
111
+ }
112
+
113
+ // src/adapters/safe/wait-for-execution.ts
114
+ import SafeApiKit2 from "@safe-global/api-kit";
115
+ function delay2(ms) {
116
+ return new Promise((resolve) => setTimeout(resolve, ms));
117
+ }
118
+ async function waitForExecution(options) {
119
+ const {
120
+ safeTxHash,
121
+ chainId,
122
+ txServiceUrl,
123
+ apiKey,
124
+ pollingInterval = 5e3,
125
+ maxAttempts = 60
126
+ } = options;
127
+ const apiKit = new SafeApiKit2({
128
+ chainId,
129
+ ...txServiceUrl && { txServiceUrl },
130
+ apiKey
131
+ });
132
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
133
+ const tx = await apiKit.getTransaction(safeTxHash);
134
+ if (tx.isExecuted && tx.transactionHash) {
135
+ return {
136
+ transactionHash: tx.transactionHash
137
+ };
138
+ }
139
+ await delay2(pollingInterval);
140
+ }
141
+ throw new Error(
142
+ `Safe transaction ${safeTxHash} was not executed after ${maxAttempts} attempts`
143
+ );
144
+ }
145
+
146
+ // src/adapters/registry.ts
147
+ var adapters = {
148
+ safe: {
149
+ submitTx: submitAndFindSafeTx,
150
+ waitForExecution,
151
+ fetchPending: fetchPendingTransactions,
152
+ simulate: simulateContractCall,
153
+ write: writeContractCall
154
+ }
155
+ };
156
+
157
+ // src/adapters/index.ts
158
+ async function submitTx(options) {
159
+ const { adapter, walletAddress, ...rest } = options;
160
+ assertAdapter(adapter);
161
+ const result = await adapters[adapter].submitTx({
162
+ safeAddress: walletAddress,
163
+ ...rest
164
+ });
165
+ return { txHash: result.safeTxHash };
166
+ }
167
+ async function waitForExecution2(options) {
168
+ const { adapter, txHash, ...rest } = options;
169
+ assertAdapter(adapter);
170
+ const result = await adapters[adapter].waitForExecution({
171
+ safeTxHash: txHash,
172
+ ...rest
173
+ });
174
+ return { transactionHash: result.transactionHash };
175
+ }
176
+ async function fetchPendingTxs(options) {
177
+ const { adapter, walletAddress, ...rest } = options;
178
+ assertAdapter(adapter);
179
+ return adapters[adapter].fetchPending({
180
+ safeAddress: walletAddress,
181
+ ...rest
182
+ });
183
+ }
184
+ async function simulate(options) {
185
+ const { adapter, config, ...params } = options;
186
+ assertAdapter(adapter);
187
+ return adapters[adapter].simulate(config, params);
188
+ }
189
+ async function write(options) {
190
+ const { adapter, config, request } = options;
191
+ assertAdapter(adapter);
192
+ return adapters[adapter].write(config, request);
193
+ }
194
+ function assertAdapter(adapter) {
195
+ if (!(adapter in adapters)) {
196
+ throw new Error(`Unknown adapter: ${adapter}. Available: ${Object.keys(adapters).join(", ")}`);
197
+ }
198
+ }
199
+
200
+ export {
201
+ extractCallData,
202
+ matchPendingTransaction,
203
+ safe_exports,
204
+ submitTx,
205
+ waitForExecution2 as waitForExecution,
206
+ fetchPendingTxs,
207
+ simulate,
208
+ write
209
+ };
210
+ //# sourceMappingURL=chunk-YQE6NIEB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/extract-call-data.ts","../src/core/match-pending-tx.ts","../src/adapters/safe/index.ts","../src/adapters/safe/simulate-contract-call.ts","../src/adapters/safe/write-contract-call.ts","../src/adapters/safe/fetch-pending-transactions.ts","../src/adapters/safe/submit-and-find-safe-tx.ts","../src/adapters/safe/wait-for-execution.ts","../src/adapters/registry.ts","../src/adapters/index.ts"],"sourcesContent":["import type { Abi, Address } from \"viem\";\nimport { encodeFunctionData } from \"viem\";\nimport type { EncodedCallData } from \"./types.js\";\n\nexport function extractCallData(params: {\n abi: Abi;\n functionName: string;\n args?: readonly unknown[];\n address: Address;\n value?: bigint;\n}): EncodedCallData {\n const data = encodeFunctionData({\n abi: params.abi,\n functionName: params.functionName,\n args: params.args as unknown[],\n });\n\n return {\n to: params.address,\n data,\n value: params.value ?? 0n,\n };\n}\n","import type { SafeMultisigTransactionResponse } from \"@safe-global/types-kit\";\nimport type { TxMatchCriteria } from \"./types.js\";\n\nexport function matchPendingTransaction(\n pendingTxs: SafeMultisigTransactionResponse[],\n criteria: TxMatchCriteria,\n): SafeMultisigTransactionResponse | undefined {\n return pendingTxs.find(\n (tx) =>\n tx.to.toLowerCase() === criteria.to.toLowerCase() &&\n tx.value === String(criteria.value) &&\n tx.data?.toLowerCase() === criteria.data.toLowerCase(),\n );\n}\n","export * from \"./types.js\";\nexport * from \"./simulate-contract-call.js\";\nexport * from \"./write-contract-call.js\";\nexport * from \"./fetch-pending-transactions.js\";\nexport * from \"./submit-and-find-safe-tx.js\";\nexport * from \"./wait-for-execution.js\";\n","import { simulateContract, type Config } from \"@wagmi/core\";\nimport type { ContractCallParams } from \"../../core/types.js\";\n\nexport async function simulateContractCall(\n config: Config,\n params: ContractCallParams,\n) {\n return simulateContract(config, {\n address: params.address,\n abi: params.abi,\n functionName: params.functionName,\n args: params.args as unknown[],\n value: params.value,\n chainId: params.chainId,\n account: params.account,\n });\n}\n","import { writeContract, type Config } from \"@wagmi/core\";\nimport type { Hex } from \"viem\";\n\nexport async function writeContractCall(\n config: Config,\n request: Parameters<typeof writeContract>[1],\n): Promise<Hex> {\n return writeContract(config, request);\n}\n","import SafeApiKit from \"@safe-global/api-kit\";\nimport type { SafeMultisigTransactionResponse } from \"@safe-global/types-kit\";\n\nexport interface FetchPendingTransactionsParams {\n chainId: bigint;\n safeAddress: string;\n txServiceUrl?: string;\n apiKey?: string;\n}\n\nexport async function fetchPendingTransactions(\n params: FetchPendingTransactionsParams,\n): Promise<SafeMultisigTransactionResponse[]> {\n const apiKit = new SafeApiKit({\n chainId: params.chainId,\n ...(params.txServiceUrl && { txServiceUrl: params.txServiceUrl }),\n apiKey: params.apiKey,\n });\n\n const response = await apiKit.getPendingTransactions(params.safeAddress);\n return response.results;\n}\n","import { extractCallData } from \"../../core/extract-call-data.js\";\nimport { matchPendingTransaction } from \"../../core/match-pending-tx.js\";\nimport { fetchPendingTransactions } from \"./fetch-pending-transactions.js\";\nimport { simulateContractCall } from \"./simulate-contract-call.js\";\nimport { writeContractCall } from \"./write-contract-call.js\";\nimport type {\n SubmitAndFindSafeTxOptions,\n SubmitAndFindSafeTxResult,\n} from \"./types.js\";\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function submitAndFindSafeTx(\n options: SubmitAndFindSafeTxOptions,\n): Promise<SubmitAndFindSafeTxResult> {\n const {\n config,\n safeAddress,\n txServiceUrl,\n apiKey,\n pollingInterval = 3000,\n maxAttempts = 20,\n ...callParams\n } = options;\n\n const callData = extractCallData(callParams);\n\n const simulation = await simulateContractCall(config, callParams);\n\n await writeContractCall(config, simulation.request as Parameters<typeof writeContractCall>[1]);\n\n const chainId = BigInt(callParams.chainId ?? (await getChainId(config)));\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n await delay(pollingInterval);\n\n const pendingTxs = await fetchPendingTransactions({\n chainId,\n safeAddress,\n txServiceUrl,\n apiKey,\n });\n const match = matchPendingTransaction(pendingTxs, callData);\n\n if (match) {\n return {\n safeTxHash: match.safeTxHash,\n };\n }\n }\n\n throw new Error(\n `Could not find matching Safe transaction after ${maxAttempts} attempts`,\n );\n}\n\nasync function getChainId(config: Parameters<typeof simulateContractCall>[0]): Promise<number> {\n const { getChainId: wagmiGetChainId } = await import(\"@wagmi/core\");\n return wagmiGetChainId(config);\n}\n","import SafeApiKit from \"@safe-global/api-kit\";\nimport type {\n WaitForExecutionOptions,\n WaitForExecutionResult,\n} from \"./types.js\";\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function waitForExecution(\n options: WaitForExecutionOptions,\n): Promise<WaitForExecutionResult> {\n const {\n safeTxHash,\n chainId,\n txServiceUrl,\n apiKey,\n pollingInterval = 5000,\n maxAttempts = 60,\n } = options;\n\n const apiKit = new SafeApiKit({\n chainId,\n ...(txServiceUrl && { txServiceUrl }),\n apiKey,\n });\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const tx = await apiKit.getTransaction(safeTxHash);\n\n if (tx.isExecuted && tx.transactionHash) {\n return {\n transactionHash: tx.transactionHash,\n };\n }\n\n await delay(pollingInterval);\n }\n\n throw new Error(\n `Safe transaction ${safeTxHash} was not executed after ${maxAttempts} attempts`,\n );\n}\n","import * as safe from \"./safe/index.js\";\n\nexport const adapters = {\n safe: {\n submitTx: safe.submitAndFindSafeTx,\n waitForExecution: safe.waitForExecution,\n fetchPending: safe.fetchPendingTransactions,\n simulate: safe.simulateContractCall,\n write: safe.writeContractCall,\n },\n} as const;\n\nexport type AdapterRegistry = typeof adapters;\n","import { adapters } from \"./registry.js\";\nimport type {\n AdapterType,\n SubmitTxOptions,\n SubmitTxResult,\n WaitForExecutionOptions,\n WaitForExecutionResult,\n FetchPendingOptions,\n SimulateOptions,\n WriteOptions,\n} from \"./types.js\";\n\n// Re-export types\nexport * from \"./types.js\";\n\n// Re-export Safe adapter for direct access\nexport * as safe from \"./safe/index.js\";\n\n// Generic entry functions\n\nexport async function submitTx(\n options: SubmitTxOptions,\n): Promise<SubmitTxResult> {\n const { adapter, walletAddress, ...rest } = options;\n assertAdapter(adapter);\n\n const result = await adapters[adapter].submitTx({\n safeAddress: walletAddress,\n ...rest,\n });\n\n return { txHash: result.safeTxHash };\n}\n\nexport async function waitForExecution(\n options: WaitForExecutionOptions,\n): Promise<WaitForExecutionResult> {\n const { adapter, txHash, ...rest } = options;\n assertAdapter(adapter);\n\n const result = await adapters[adapter].waitForExecution({\n safeTxHash: txHash,\n ...rest,\n });\n\n return { transactionHash: result.transactionHash };\n}\n\nexport async function fetchPendingTxs(\n options: FetchPendingOptions,\n): Promise<unknown[]> {\n const { adapter, walletAddress, ...rest } = options;\n assertAdapter(adapter);\n\n return adapters[adapter].fetchPending({\n safeAddress: walletAddress,\n ...rest,\n });\n}\n\nexport async function simulate(\n options: SimulateOptions,\n): Promise<unknown> {\n const { adapter, config, ...params } = options;\n assertAdapter(adapter);\n\n return adapters[adapter].simulate(config, params);\n}\n\nexport async function write(\n options: WriteOptions,\n): Promise<string> {\n const { adapter, config, request } = options;\n assertAdapter(adapter);\n\n return adapters[adapter].write(config, request as Parameters<typeof adapters.safe.write>[1]);\n}\n\nfunction assertAdapter(adapter: AdapterType): asserts adapter is keyof typeof adapters {\n if (!(adapter in adapters)) {\n throw new Error(`Unknown adapter: ${adapter}. Available: ${Object.keys(adapters).join(\", \")}`);\n }\n}\n"],"mappings":"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","names":["SafeApiKit","delay","waitForExecution"]}