@aboutcircles/sdk-runner 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 +106 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +89 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +147349 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +1 -0
- package/dist/safe-browser-runner.d.ts +130 -0
- package/dist/safe-browser-runner.d.ts.map +1 -0
- package/dist/safe-browser-runner.js +265 -0
- package/dist/safe-runner.d.ts +102 -0
- package/dist/safe-runner.d.ts.map +1 -0
- package/dist/safe-runner.js +237 -0
- package/package.json +38 -0
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+B,QAAQ,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAIrG,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { Address, TransactionRequest } from '@aboutcircles/sdk-types';
|
|
2
|
+
import type { ContractRunner, BatchRun } from './runner';
|
|
3
|
+
import type { PublicClient, TransactionReceipt, Chain } from 'viem';
|
|
4
|
+
import type { EIP1193Provider } from 'viem';
|
|
5
|
+
/**
|
|
6
|
+
* Safe browser contract runner implementation using Safe Protocol Kit
|
|
7
|
+
* Executes transactions through a Safe multisig wallet using the browser's Web3 provider
|
|
8
|
+
*
|
|
9
|
+
* This runner is designed for use in browser environments where the user has a Web3 wallet
|
|
10
|
+
* extension installed (e.g., MetaMask, WalletConnect, etc.)
|
|
11
|
+
*/
|
|
12
|
+
export declare class SafeBrowserRunner implements ContractRunner {
|
|
13
|
+
address?: Address;
|
|
14
|
+
publicClient: PublicClient;
|
|
15
|
+
private eip1193Provider;
|
|
16
|
+
private safeAddress?;
|
|
17
|
+
private safe?;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new SafeBrowserRunner
|
|
20
|
+
* @param publicClient - The viem public client for reading blockchain state
|
|
21
|
+
* @param eip1193Provider - The EIP-1193 provider from the browser (e.g., window.ethereum)
|
|
22
|
+
* @param safeAddress - The address of the Safe wallet (optional, can be set in init)
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { createPublicClient, http } from 'viem';
|
|
27
|
+
* import { gnosis } from 'viem/chains';
|
|
28
|
+
* import { SafeBrowserRunner } from '@aboutcircles/sdk-runner';
|
|
29
|
+
*
|
|
30
|
+
* const publicClient = createPublicClient({
|
|
31
|
+
* chain: gnosis,
|
|
32
|
+
* transport: http('https://rpc.gnosischain.com')
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* const runner = new SafeBrowserRunner(
|
|
36
|
+
* publicClient,
|
|
37
|
+
* window.ethereum,
|
|
38
|
+
* '0xYourSafeAddress...'
|
|
39
|
+
* );
|
|
40
|
+
*
|
|
41
|
+
* await runner.init();
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
constructor(publicClient: PublicClient, eip1193Provider: EIP1193Provider, safeAddress?: Address);
|
|
45
|
+
/**
|
|
46
|
+
* Create and initialize a SafeBrowserRunner in one step
|
|
47
|
+
* @param rpcUrl - The RPC URL to connect to
|
|
48
|
+
* @param eip1193Provider - The EIP-1193 provider from the browser (e.g., window.ethereum)
|
|
49
|
+
* @param safeAddress - The address of the Safe wallet
|
|
50
|
+
* @param chain - The viem chain configuration (e.g., gnosis from 'viem/chains')
|
|
51
|
+
* @returns An initialized SafeBrowserRunner instance
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* import { gnosis } from 'viem/chains';
|
|
56
|
+
* import { SafeBrowserRunner } from '@aboutcircles/sdk-runner';
|
|
57
|
+
*
|
|
58
|
+
* const runner = await SafeBrowserRunner.create(
|
|
59
|
+
* 'https://rpc.gnosischain.com',
|
|
60
|
+
* window.ethereum,
|
|
61
|
+
* '0xYourSafeAddress...',
|
|
62
|
+
* gnosis
|
|
63
|
+
* );
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
static create(rpcUrl: string, eip1193Provider: EIP1193Provider, safeAddress: Address, chain: Chain): Promise<SafeBrowserRunner>;
|
|
67
|
+
/**
|
|
68
|
+
* Initialize the runner with a Safe address
|
|
69
|
+
* @param safeAddress - The address of the Safe wallet (optional if provided in constructor)
|
|
70
|
+
* @throws {RunnerError} If no Safe address is provided and no EIP-1193 provider is available
|
|
71
|
+
*/
|
|
72
|
+
init(safeAddress?: Address): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Ensures the Safe is initialized
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
private ensureSafe;
|
|
78
|
+
/**
|
|
79
|
+
* Estimate gas for a transaction
|
|
80
|
+
*/
|
|
81
|
+
estimateGas: (tx: TransactionRequest) => Promise<bigint>;
|
|
82
|
+
/**
|
|
83
|
+
* Call a contract (read-only operation)
|
|
84
|
+
*/
|
|
85
|
+
call: (tx: TransactionRequest) => Promise<string>;
|
|
86
|
+
/**
|
|
87
|
+
* Resolve an ENS name to an address
|
|
88
|
+
*/
|
|
89
|
+
resolveName: (name: string) => Promise<string | null>;
|
|
90
|
+
/**
|
|
91
|
+
* Send one or more transactions through the Safe and wait for confirmation
|
|
92
|
+
* All transactions are batched and executed atomically
|
|
93
|
+
*
|
|
94
|
+
* The user will be prompted to sign the transaction through their Web3 wallet
|
|
95
|
+
*
|
|
96
|
+
* @throws {RunnerError} If transaction reverts or execution fails
|
|
97
|
+
*/
|
|
98
|
+
sendTransaction: (txs: TransactionRequest[]) => Promise<TransactionReceipt>;
|
|
99
|
+
/**
|
|
100
|
+
* Create a batch transaction runner
|
|
101
|
+
* @returns A SafeBrowserBatchRun instance for batching multiple transactions
|
|
102
|
+
*/
|
|
103
|
+
sendBatchTransaction: () => SafeBrowserBatchRun;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Batch transaction runner for Safe browser operations
|
|
107
|
+
* Allows multiple transactions to be batched and executed together
|
|
108
|
+
*/
|
|
109
|
+
export declare class SafeBrowserBatchRun implements BatchRun {
|
|
110
|
+
private readonly safe;
|
|
111
|
+
private readonly publicClient;
|
|
112
|
+
private readonly transactions;
|
|
113
|
+
constructor(safe: any, publicClient: PublicClient);
|
|
114
|
+
/**
|
|
115
|
+
* Add a transaction to the batch
|
|
116
|
+
*/
|
|
117
|
+
addTransaction(tx: TransactionRequest): void;
|
|
118
|
+
/**
|
|
119
|
+
* Get the Safe transaction data for all batched transactions
|
|
120
|
+
*/
|
|
121
|
+
getSafeTransaction(): Promise<any>;
|
|
122
|
+
/**
|
|
123
|
+
* Execute all batched transactions and wait for confirmation
|
|
124
|
+
* The user will be prompted to sign the transaction through their Web3 wallet
|
|
125
|
+
*
|
|
126
|
+
* @throws {RunnerError} If transaction reverts or execution fails
|
|
127
|
+
*/
|
|
128
|
+
run(): Promise<TransactionReceipt>;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=safe-browser-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-browser-runner.d.ts","sourceRoot":"","sources":["../src/safe-browser-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAO,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAY5C;;;;;;GAMG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAElC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAC,CAAU;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAM;IAEnB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;gBAED,YAAY,EAAE,YAAY,EAC1B,eAAe,EAAE,eAAe,EAChC,WAAW,CAAC,EAAE,OAAO;IAOvB;;;;;;;;;;;;;;;;;;;;OAoBG;WACU,MAAM,CACjB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,eAAe,EAChC,WAAW,EAAE,OAAO,EACpB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,iBAAiB,CAAC;IAW7B;;;;OAIG;IACG,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhD;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,WAAW,GAAU,IAAI,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAAC,CAW3D;IAEF;;OAEG;IACH,IAAI,GAAU,IAAI,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAAC,CAapD;IAEF;;OAEG;IACH,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUxD;IAEF;;;;;;;OAOG;IACH,eAAe,GAAU,KAAK,kBAAkB,EAAE,KAAG,OAAO,CAAC,kBAAkB,CAAC,CA0C9E;IAEF;;;OAGG;IACH,oBAAoB,QAAO,mBAAmB,CAG5C;CACH;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,QAAQ;IAIhD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJ/B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA4B;gBAGtC,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,YAAY;IAG7C;;OAEG;IACH,cAAc,CAAC,EAAE,EAAE,kBAAkB;IAIrC;;OAEG;IACG,kBAAkB;IAexB;;;;;OAKG;IACG,GAAG,IAAI,OAAO,CAAC,kBAAkB,CAAC;CA0BzC"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { createPublicClient, http } from 'viem';
|
|
2
|
+
import { OperationType } from '@safe-global/safe-core-sdk-types';
|
|
3
|
+
import { RunnerError } from './errors';
|
|
4
|
+
// Use require for Safe to ensure compatibility with bun's CJS/ESM interop
|
|
5
|
+
// Safe Protocol Kit v5 uses CommonJS exports, so we use require() for proper interop
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
7
|
+
//@todo double check the import format
|
|
8
|
+
const SafeModule = require('@safe-global/protocol-kit');
|
|
9
|
+
const Safe = SafeModule.default || SafeModule;
|
|
10
|
+
/**
|
|
11
|
+
* Safe browser contract runner implementation using Safe Protocol Kit
|
|
12
|
+
* Executes transactions through a Safe multisig wallet using the browser's Web3 provider
|
|
13
|
+
*
|
|
14
|
+
* This runner is designed for use in browser environments where the user has a Web3 wallet
|
|
15
|
+
* extension installed (e.g., MetaMask, WalletConnect, etc.)
|
|
16
|
+
*/
|
|
17
|
+
export class SafeBrowserRunner {
|
|
18
|
+
address;
|
|
19
|
+
publicClient;
|
|
20
|
+
eip1193Provider;
|
|
21
|
+
safeAddress;
|
|
22
|
+
safe;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new SafeBrowserRunner
|
|
25
|
+
* @param publicClient - The viem public client for reading blockchain state
|
|
26
|
+
* @param eip1193Provider - The EIP-1193 provider from the browser (e.g., window.ethereum)
|
|
27
|
+
* @param safeAddress - The address of the Safe wallet (optional, can be set in init)
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { createPublicClient, http } from 'viem';
|
|
32
|
+
* import { gnosis } from 'viem/chains';
|
|
33
|
+
* import { SafeBrowserRunner } from '@aboutcircles/sdk-runner';
|
|
34
|
+
*
|
|
35
|
+
* const publicClient = createPublicClient({
|
|
36
|
+
* chain: gnosis,
|
|
37
|
+
* transport: http('https://rpc.gnosischain.com')
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* const runner = new SafeBrowserRunner(
|
|
41
|
+
* publicClient,
|
|
42
|
+
* window.ethereum,
|
|
43
|
+
* '0xYourSafeAddress...'
|
|
44
|
+
* );
|
|
45
|
+
*
|
|
46
|
+
* await runner.init();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
constructor(publicClient, eip1193Provider, safeAddress) {
|
|
50
|
+
this.publicClient = publicClient;
|
|
51
|
+
this.eip1193Provider = eip1193Provider;
|
|
52
|
+
this.safeAddress = safeAddress;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create and initialize a SafeBrowserRunner in one step
|
|
56
|
+
* @param rpcUrl - The RPC URL to connect to
|
|
57
|
+
* @param eip1193Provider - The EIP-1193 provider from the browser (e.g., window.ethereum)
|
|
58
|
+
* @param safeAddress - The address of the Safe wallet
|
|
59
|
+
* @param chain - The viem chain configuration (e.g., gnosis from 'viem/chains')
|
|
60
|
+
* @returns An initialized SafeBrowserRunner instance
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { gnosis } from 'viem/chains';
|
|
65
|
+
* import { SafeBrowserRunner } from '@aboutcircles/sdk-runner';
|
|
66
|
+
*
|
|
67
|
+
* const runner = await SafeBrowserRunner.create(
|
|
68
|
+
* 'https://rpc.gnosischain.com',
|
|
69
|
+
* window.ethereum,
|
|
70
|
+
* '0xYourSafeAddress...',
|
|
71
|
+
* gnosis
|
|
72
|
+
* );
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
static async create(rpcUrl, eip1193Provider, safeAddress, chain) {
|
|
76
|
+
const publicClient = createPublicClient({
|
|
77
|
+
chain,
|
|
78
|
+
transport: http(rpcUrl),
|
|
79
|
+
});
|
|
80
|
+
const runner = new SafeBrowserRunner(publicClient, eip1193Provider, safeAddress);
|
|
81
|
+
await runner.init();
|
|
82
|
+
return runner;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Initialize the runner with a Safe address
|
|
86
|
+
* @param safeAddress - The address of the Safe wallet (optional if provided in constructor)
|
|
87
|
+
* @throws {RunnerError} If no Safe address is provided and no EIP-1193 provider is available
|
|
88
|
+
*/
|
|
89
|
+
async init(safeAddress) {
|
|
90
|
+
// Use provided address or the one from constructor
|
|
91
|
+
const targetSafeAddress = safeAddress || this.safeAddress;
|
|
92
|
+
if (!targetSafeAddress) {
|
|
93
|
+
throw RunnerError.initializationFailed('SafeBrowserRunner', new Error('Safe address must be provided either in constructor or init()'));
|
|
94
|
+
}
|
|
95
|
+
if (!this.eip1193Provider) {
|
|
96
|
+
throw RunnerError.initializationFailed('SafeBrowserRunner', new Error('No EIP-1193 provider available. Make sure you are in a browser environment with a Web3 wallet extension.'));
|
|
97
|
+
}
|
|
98
|
+
this.safeAddress = targetSafeAddress;
|
|
99
|
+
this.address = targetSafeAddress;
|
|
100
|
+
// Initialize Safe Protocol Kit with browser provider
|
|
101
|
+
this.safe = await Safe.init({
|
|
102
|
+
provider: this.eip1193Provider,
|
|
103
|
+
safeAddress: targetSafeAddress,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Ensures the Safe is initialized
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
ensureSafe() {
|
|
111
|
+
if (!this.safe) {
|
|
112
|
+
throw RunnerError.initializationFailed('SafeBrowserRunner', new Error('SafeBrowserRunner not initialized. Call init() first.'));
|
|
113
|
+
}
|
|
114
|
+
return this.safe;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Estimate gas for a transaction
|
|
118
|
+
*/
|
|
119
|
+
estimateGas = async (tx) => {
|
|
120
|
+
const estimate = await this.publicClient.estimateGas({
|
|
121
|
+
// @ts-expect-error - Address type is compatible with viem's 0x${string}
|
|
122
|
+
account: this.address,
|
|
123
|
+
// @ts-expect-error - Address type is compatible with viem's 0x${string}
|
|
124
|
+
to: tx.to,
|
|
125
|
+
data: tx.data,
|
|
126
|
+
value: tx.value,
|
|
127
|
+
});
|
|
128
|
+
return estimate;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Call a contract (read-only operation)
|
|
132
|
+
*/
|
|
133
|
+
call = async (tx) => {
|
|
134
|
+
const result = await this.publicClient.call({
|
|
135
|
+
// @ts-expect-error - Address type is compatible with viem's 0x${string}
|
|
136
|
+
account: tx.from || this.address,
|
|
137
|
+
// @ts-expect-error - Address type is compatible with viem's 0x${string}
|
|
138
|
+
to: tx.to,
|
|
139
|
+
data: tx.data,
|
|
140
|
+
value: tx.value,
|
|
141
|
+
gas: tx.gas,
|
|
142
|
+
gasPrice: tx.gasPrice,
|
|
143
|
+
});
|
|
144
|
+
return result.data || '0x';
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Resolve an ENS name to an address
|
|
148
|
+
*/
|
|
149
|
+
resolveName = async (name) => {
|
|
150
|
+
try {
|
|
151
|
+
const address = await this.publicClient.getEnsAddress({
|
|
152
|
+
name,
|
|
153
|
+
});
|
|
154
|
+
return address;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
// ENS resolution failed or not supported
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Send one or more transactions through the Safe and wait for confirmation
|
|
163
|
+
* All transactions are batched and executed atomically
|
|
164
|
+
*
|
|
165
|
+
* The user will be prompted to sign the transaction through their Web3 wallet
|
|
166
|
+
*
|
|
167
|
+
* @throws {RunnerError} If transaction reverts or execution fails
|
|
168
|
+
*/
|
|
169
|
+
sendTransaction = async (txs) => {
|
|
170
|
+
const safe = this.ensureSafe();
|
|
171
|
+
if (txs.length === 0) {
|
|
172
|
+
throw RunnerError.executionFailed('No transactions provided');
|
|
173
|
+
}
|
|
174
|
+
const metaTransactions = txs.map((tx) => ({
|
|
175
|
+
operation: OperationType.Call,
|
|
176
|
+
to: tx.to,
|
|
177
|
+
value: (tx.value?.toString() ?? '0'),
|
|
178
|
+
data: tx.data ?? '0x',
|
|
179
|
+
}));
|
|
180
|
+
// Create Safe transaction with all transactions
|
|
181
|
+
const safeTransaction = await safe.createTransaction({
|
|
182
|
+
transactions: metaTransactions,
|
|
183
|
+
});
|
|
184
|
+
// Execute the batched transaction (will prompt user for signature)
|
|
185
|
+
const txResult = await safe.executeTransaction(safeTransaction);
|
|
186
|
+
if (!txResult.hash) {
|
|
187
|
+
throw RunnerError.executionFailed('No transaction hash returned from Safe execution');
|
|
188
|
+
}
|
|
189
|
+
// Wait for transaction receipt
|
|
190
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({
|
|
191
|
+
hash: txResult.hash,
|
|
192
|
+
});
|
|
193
|
+
// Check transaction status and throw if reverted
|
|
194
|
+
if (receipt.status === 'reverted') {
|
|
195
|
+
throw RunnerError.transactionReverted(receipt.transactionHash, receipt.blockNumber, receipt.gasUsed);
|
|
196
|
+
}
|
|
197
|
+
// Return viem's TransactionReceipt directly
|
|
198
|
+
return receipt;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Create a batch transaction runner
|
|
202
|
+
* @returns A SafeBrowserBatchRun instance for batching multiple transactions
|
|
203
|
+
*/
|
|
204
|
+
sendBatchTransaction = () => {
|
|
205
|
+
const safe = this.ensureSafe();
|
|
206
|
+
return new SafeBrowserBatchRun(safe, this.publicClient);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Batch transaction runner for Safe browser operations
|
|
211
|
+
* Allows multiple transactions to be batched and executed together
|
|
212
|
+
*/
|
|
213
|
+
export class SafeBrowserBatchRun {
|
|
214
|
+
safe;
|
|
215
|
+
publicClient;
|
|
216
|
+
transactions = [];
|
|
217
|
+
constructor(safe, publicClient) {
|
|
218
|
+
this.safe = safe;
|
|
219
|
+
this.publicClient = publicClient;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Add a transaction to the batch
|
|
223
|
+
*/
|
|
224
|
+
addTransaction(tx) {
|
|
225
|
+
this.transactions.push(tx);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get the Safe transaction data for all batched transactions
|
|
229
|
+
*/
|
|
230
|
+
async getSafeTransaction() {
|
|
231
|
+
const metaTransactions = this.transactions.map((tx) => ({
|
|
232
|
+
operation: OperationType.Call,
|
|
233
|
+
to: tx.to,
|
|
234
|
+
value: (tx.value?.toString() ?? '0'),
|
|
235
|
+
data: tx.data ?? '0x',
|
|
236
|
+
}));
|
|
237
|
+
const safeTransaction = await this.safe.createTransaction({
|
|
238
|
+
transactions: metaTransactions,
|
|
239
|
+
});
|
|
240
|
+
return safeTransaction;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Execute all batched transactions and wait for confirmation
|
|
244
|
+
* The user will be prompted to sign the transaction through their Web3 wallet
|
|
245
|
+
*
|
|
246
|
+
* @throws {RunnerError} If transaction reverts or execution fails
|
|
247
|
+
*/
|
|
248
|
+
async run() {
|
|
249
|
+
const safeTransaction = await this.getSafeTransaction();
|
|
250
|
+
const txResult = await this.safe.executeTransaction(safeTransaction);
|
|
251
|
+
if (!txResult.hash) {
|
|
252
|
+
throw RunnerError.executionFailed('No transaction hash returned from Safe execution');
|
|
253
|
+
}
|
|
254
|
+
// Wait for transaction receipt
|
|
255
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({
|
|
256
|
+
hash: txResult.hash,
|
|
257
|
+
});
|
|
258
|
+
// Check transaction status and throw if reverted
|
|
259
|
+
if (receipt.status === 'reverted') {
|
|
260
|
+
throw RunnerError.transactionReverted(receipt.transactionHash, receipt.blockNumber, receipt.gasUsed);
|
|
261
|
+
}
|
|
262
|
+
// Return viem's TransactionReceipt directly
|
|
263
|
+
return receipt;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Address, Hex, TransactionRequest } from '@aboutcircles/sdk-types';
|
|
2
|
+
import type { ContractRunner, BatchRun } from './runner';
|
|
3
|
+
import type { PublicClient, TransactionReceipt, Chain } from 'viem';
|
|
4
|
+
/**
|
|
5
|
+
* Batch transaction runner for Safe
|
|
6
|
+
* Allows multiple transactions to be batched and executed together
|
|
7
|
+
*/
|
|
8
|
+
export declare class SafeBatchRun implements BatchRun {
|
|
9
|
+
private readonly safe;
|
|
10
|
+
private readonly publicClient;
|
|
11
|
+
private readonly transactions;
|
|
12
|
+
constructor(safe: any, publicClient: PublicClient);
|
|
13
|
+
/**
|
|
14
|
+
* Add a transaction to the batch
|
|
15
|
+
*/
|
|
16
|
+
addTransaction(tx: TransactionRequest): void;
|
|
17
|
+
/**
|
|
18
|
+
* Get the Safe transaction data for all batched transactions
|
|
19
|
+
*/
|
|
20
|
+
getSafeTransaction(): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Execute all batched transactions and wait for confirmation
|
|
23
|
+
* @throws {RunnerError} If transaction reverts or execution fails
|
|
24
|
+
*/
|
|
25
|
+
run(): Promise<TransactionReceipt>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Safe contract runner implementation using Safe Protocol Kit
|
|
29
|
+
* Executes transactions through a Safe multisig wallet
|
|
30
|
+
*/
|
|
31
|
+
export declare class SafeContractRunner implements ContractRunner {
|
|
32
|
+
address?: Address;
|
|
33
|
+
publicClient: PublicClient;
|
|
34
|
+
private privateKey;
|
|
35
|
+
private rpcUrl;
|
|
36
|
+
private safeAddress?;
|
|
37
|
+
private safe?;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new SafeContractRunner
|
|
40
|
+
* @param publicClient - The viem public client for reading blockchain state
|
|
41
|
+
* @param privateKey - The private key of one of the Safe signers
|
|
42
|
+
* @param rpcUrl - The RPC URL to use for Safe operations
|
|
43
|
+
* @param safeAddress - The address of the Safe wallet (optional, can be set in init)
|
|
44
|
+
*/
|
|
45
|
+
constructor(publicClient: PublicClient, privateKey: Hex, rpcUrl: string, safeAddress?: Address);
|
|
46
|
+
/**
|
|
47
|
+
* Create and initialize a SafeContractRunner in one step
|
|
48
|
+
* @param rpcUrl - The RPC URL to connect to
|
|
49
|
+
* @param privateKey - The private key of one of the Safe signers
|
|
50
|
+
* @param safeAddress - The address of the Safe wallet
|
|
51
|
+
* @param chain - The viem chain configuration (e.g., gnosis from 'viem/chains')
|
|
52
|
+
* @returns An initialized SafeContractRunner instance
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* import { gnosis } from 'viem/chains';
|
|
57
|
+
* import { SafeContractRunner } from '@aboutcircles/sdk-runner';
|
|
58
|
+
*
|
|
59
|
+
* const runner = await SafeContractRunner.create(
|
|
60
|
+
* 'https://rpc.gnosischain.com',
|
|
61
|
+
* '0xYourPrivateKey...',
|
|
62
|
+
* '0xYourSafeAddress...',
|
|
63
|
+
* gnosis
|
|
64
|
+
* );
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
static create(rpcUrl: string, privateKey: Hex, safeAddress: Address, chain: Chain): Promise<SafeContractRunner>;
|
|
68
|
+
/**
|
|
69
|
+
* Initialize the runner with a Safe address
|
|
70
|
+
* @param safeAddress - The address of the Safe wallet (optional if provided in constructor)
|
|
71
|
+
*/
|
|
72
|
+
init(safeAddress?: Address): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Ensures the Safe is initialized
|
|
75
|
+
*/
|
|
76
|
+
private ensureSafe;
|
|
77
|
+
/**
|
|
78
|
+
* Estimate gas for a transaction
|
|
79
|
+
*/
|
|
80
|
+
estimateGas: (tx: TransactionRequest) => Promise<bigint>;
|
|
81
|
+
/**
|
|
82
|
+
* Call a contract (read-only operation)
|
|
83
|
+
*/
|
|
84
|
+
call: (tx: TransactionRequest) => Promise<string>;
|
|
85
|
+
/**
|
|
86
|
+
* Resolve an ENS name to an address
|
|
87
|
+
*/
|
|
88
|
+
resolveName: (name: string) => Promise<string | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Send one or more transactions through the Safe and wait for confirmation
|
|
91
|
+
* All transactions are batched and executed atomically
|
|
92
|
+
*
|
|
93
|
+
* @throws {RunnerError} If transaction reverts or execution fails
|
|
94
|
+
*/
|
|
95
|
+
sendTransaction: (txs: TransactionRequest[]) => Promise<TransactionReceipt>;
|
|
96
|
+
/**
|
|
97
|
+
* Create a batch transaction runner
|
|
98
|
+
* @returns A SafeBatchRun instance for batching multiple transactions
|
|
99
|
+
*/
|
|
100
|
+
sendBatchTransaction: () => SafeBatchRun;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=safe-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-runner.d.ts","sourceRoot":"","sources":["../src/safe-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAYpE;;;GAGG;AACH,qBAAa,YAAa,YAAW,QAAQ;IAIzC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJ/B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA4B;gBAGtC,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,YAAY;IAG7C;;OAEG;IACH,cAAc,CAAC,EAAE,EAAE,kBAAkB;IAIrC;;OAEG;IACG,kBAAkB;IAexB;;;OAGG;IACG,GAAG,IAAI,OAAO,CAAC,kBAAkB,CAAC;CA0BzC;AAED;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAElC,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAC,CAAU;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAM;IAEnB;;;;;;OAMG;gBAGD,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,GAAG,EACf,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,OAAO;IAQvB;;;;;;;;;;;;;;;;;;;;OAoBG;WACU,MAAM,CACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,GAAG,EACf,WAAW,EAAE,OAAO,EACpB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,kBAAkB,CAAC;IAW9B;;;OAGG;IACG,IAAI,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBhD;;OAEG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,WAAW,GAAU,IAAI,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAAC,CAW3D;IAEF;;OAEG;IACH,IAAI,GAAU,IAAI,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAAC,CAapD;IAEF;;OAEG;IACH,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUxD;IAEF;;;;;OAKG;IACH,eAAe,GAAU,KAAK,kBAAkB,EAAE,KAAG,OAAO,CAAC,kBAAkB,CAAC,CA0C9E;IAEF;;;OAGG;IACH,oBAAoB,QAAO,YAAY,CAGrC;CACH"}
|