Skip to content

Commit 8f3a408

Browse files
authored
Split Execute Transasction (#68)
1 parent bc311ff commit 8f3a408

File tree

3 files changed

+138
-16
lines changed

3 files changed

+138
-16
lines changed

examples/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export async function loadArgs(): Promise<UserOptions> {
4747
.option("mpcContractId", {
4848
type: "string",
4949
description: "Address of the mpc (signing) contract",
50-
default: "v1.signer-dev.testnet",
50+
default: "v1.signer-prod.testnet",
5151
})
5252
.help()
5353
.alias("help", "h").argv;

examples/send-tx.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,14 @@ async function main(): Promise<void> {
6868
const signature = await txManager.signTransaction(safeOpHash);
6969

7070
console.log("Executing UserOp...");
71-
const userOpReceipt = await txManager.executeTransaction(chainId, {
71+
const userOpHash = await txManager.executeTransaction(chainId, {
7272
...unsignedUserOp,
7373
signature,
7474
});
75-
console.log("userOp Receipt", userOpReceipt);
75+
console.log("userOpHash:", userOpHash);
76+
77+
const userOpReceipt = await txManager.getOpReceipt(chainId, userOpHash);
78+
console.log("userOpReceipt:", userOpReceipt);
7679
}
7780

7881
main().catch((err) => {

src/near-safe.ts

Lines changed: 132 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export class NearSafe {
5555
private pimlicoKey: string;
5656
private safeSaltNonce: string;
5757

58+
/**
59+
* Creates a new instance of the `NearSafe` class using the provided configuration.
60+
*
61+
* @param {NearSafeConfig} config - The configuration object required to initialize the `NearSafe` instance, including the Pimlico key and safe salt nonce.
62+
* @returns {Promise<NearSafe>} - A promise that resolves to a new `NearSafe` instance.
63+
*/
5864
static async create(config: NearSafeConfig): Promise<NearSafe> {
5965
const { pimlicoKey, safeSaltNonce } = config;
6066
const nearAdapter = await setupAdapter({ ...config });
@@ -78,6 +84,16 @@ export class NearSafe {
7884
);
7985
}
8086

87+
/**
88+
* Constructs a new `NearSafe` object with the provided parameters.
89+
*
90+
* @param {NearEthAdapter} nearAdapter - The NEAR adapter used for interacting with the NEAR blockchain.
91+
* @param {SafeContractSuite} safePack - A suite of contracts and utilities for managing the Safe contract.
92+
* @param {string} pimlicoKey - A key required for authenticating with the Pimlico service.
93+
* @param {string} setup - The setup string generated for the Safe contract.
94+
* @param {Address} safeAddress - The address of the deployed Safe contract.
95+
* @param {string} safeSaltNonce - A unique nonce used to differentiate the Safe setup.
96+
*/
8197
constructor(
8298
nearAdapter: NearEthAdapter,
8399
safePack: SafeContractSuite,
@@ -95,18 +111,45 @@ export class NearSafe {
95111
this.safeSaltNonce = safeSaltNonce;
96112
}
97113

114+
/**
115+
* Retrieves the MPC (Multi-Party Computation) address associated with the NEAR adapter.
116+
*
117+
* @returns {Address} - The MPC address of the NEAR adapter.
118+
*/
98119
get mpcAddress(): Address {
99120
return this.nearAdapter.address;
100121
}
101122

123+
/**
124+
* Retrieves the contract ID of the MPC contract associated with the NEAR adapter.
125+
*
126+
* @returns {string} - The contract ID of the MPC contract.
127+
*/
102128
get mpcContractId(): string {
103129
return this.nearAdapter.mpcContract.contract.contractId;
104130
}
105131

132+
/**
133+
* Retrieves the balance of the Safe account on the specified EVM chain.
134+
*
135+
* @param {number} chainId - The ID of the blockchain network where the Safe account is located.
136+
* @returns {Promise<bigint>} - A promise that resolves to the balance of the Safe account in wei.
137+
*/
106138
async getBalance(chainId: number): Promise<bigint> {
107139
return await getClient(chainId).getBalance({ address: this.address });
108140
}
109141

142+
/**
143+
* Constructs a user operation for the specified chain, including necessary gas fees, nonce, and paymaster data.
144+
* Warning: Uses a private ethRPC with sensitive Pimlico API key (should be run server side).
145+
*
146+
* @param {Object} args - The arguments for building the transaction.
147+
* @param {number} args.chainId - The ID of the blockchain network where the transaction will be executed.
148+
* @param {MetaTransaction[]} args.transactions - A list of meta-transactions to be included in the user operation.
149+
* @param {boolean} args.usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
150+
* @returns {Promise<UserOperation>} - A promise that resolves to a complete `UserOperation` object, including gas fees, nonce, and paymaster data.
151+
* @throws {Error} - Throws an error if the transaction set is empty or if any operation fails during the building process.
152+
*/
110153
async buildTransaction(args: {
111154
chainId: number;
112155
transactions: MetaTransaction[];
@@ -147,15 +190,34 @@ export class NearSafe {
147190
return unsignedUserOp;
148191
}
149192

193+
/**
194+
* Signs a transaction with the NEAR adapter using the provided operation hash.
195+
*
196+
* @param {Hex} safeOpHash - The hash of the user operation that needs to be signed.
197+
* @returns {Promise<Hex>} - A promise that resolves to the packed signature in hexadecimal format.
198+
*/
150199
async signTransaction(safeOpHash: Hex): Promise<Hex> {
151200
const signature = await this.nearAdapter.sign(safeOpHash);
152201
return packSignature(signature);
153202
}
154203

204+
/**
205+
* Computes the operation hash for a given user operation.
206+
*
207+
* @param {UserOperation} userOp - The user operation for which the hash needs to be computed.
208+
* @returns {Promise<Hash>} - A promise that resolves to the hash of the provided user operation.
209+
*/
155210
async opHash(userOp: UserOperation): Promise<Hash> {
156211
return this.safePack.getOpHash(userOp);
157212
}
158213

214+
/**
215+
* Encodes a request to sign a transaction using either a paymaster or the user's own funds.
216+
*
217+
* @param {SignRequestData} signRequest - The data required to create the signature request. This includes information such as the chain ID and other necessary fields for the transaction.
218+
* @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
219+
* @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
220+
*/
159221
async encodeSignRequest(
160222
signRequest: SignRequestData,
161223
usePaymaster: boolean
@@ -177,18 +239,29 @@ export class NearSafe {
177239
},
178240
};
179241
}
242+
243+
/**
244+
* Broadcasts a user operation to the EVM network with a provided signature.
245+
* Warning: Uses a private ethRPC with sensitive Pimlico API key (should be run server side).
246+
*
247+
* @param {number} chainId - The ID of the EVM network to which the transaction should be broadcasted.
248+
* @param {FinalExecutionOutcome} outcome - The result of the NEAR transaction execution, which contains the necessary data to construct an EVM signature.
249+
* @param {UserOperation} unsignedUserOp - The unsigned user operation to be broadcasted. This includes transaction data such as the destination address and data payload.
250+
* @returns {Promise<{ signature: Hex; opHash: Hash }>} - A promise that resolves to an object containing the signature used and the hash of the executed user operation.
251+
* @throws {Error} - Throws an error if the EVM broadcast fails, including the error message for debugging.
252+
*/
180253
async broadcastEvm(
181254
chainId: number,
182255
outcome: FinalExecutionOutcome,
183256
unsignedUserOp: UserOperation
184-
): Promise<{ signature: Hex; receipt: UserOperationReceipt }> {
257+
): Promise<{ signature: Hex; opHash: Hash }> {
185258
const signature = packSignature(
186259
serializeSignature(signatureFromOutcome(outcome))
187260
);
188261
try {
189262
return {
190263
signature,
191-
receipt: await this.executeTransaction(chainId, {
264+
opHash: await this.executeTransaction(chainId, {
192265
...unsignedUserOp,
193266
signature,
194267
}),
@@ -200,26 +273,54 @@ export class NearSafe {
200273
}
201274
}
202275

276+
/**
277+
* Executes a user operation on the specified blockchain network.
278+
* Warning: Uses a private ethRPC with sensitive Pimlico API key (should be run server side).
279+
*
280+
* @param {number} chainId - The ID of the blockchain network on which to execute the transaction.
281+
* @param {UserOperation} userOp - The user operation to be executed, typically includes the data and signatures necessary for the transaction.
282+
* @returns {Promise<Hash>} - A promise that resolves to the hash of the executed transaction.
283+
*/
203284
async executeTransaction(
204285
chainId: number,
205286
userOp: UserOperation
206-
): Promise<UserOperationReceipt> {
207-
const bundler = this.bundlerForChainId(chainId);
208-
const userOpHash = await bundler.sendUserOperation(userOp);
209-
console.log("UserOp Hash", userOpHash);
210-
211-
const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
212-
console.log("userOp Receipt", userOpReceipt);
287+
): Promise<Hash> {
288+
return this.bundlerForChainId(chainId).sendUserOperation(userOp);
289+
}
213290

214-
// Update safeNotDeployed after the first transaction
215-
this.safeDeployed(chainId);
216-
return userOpReceipt;
291+
/**
292+
* Retrieves the receipt of a previously executed user operation.
293+
* Warning: Uses a private ethRPC with sensitive Pimlico API key (should be run server side).
294+
*
295+
* @param {number} chainId - The ID of the blockchain network where the operation was executed.
296+
* @param {Hash} userOpHash - The hash of the user operation for which to retrieve the receipt.
297+
* @returns {Promise<UserOperationReceipt>} - A promise that resolves to the receipt of the user operation, which includes status and logs.
298+
*/
299+
async getOpReceipt(
300+
chainId: number,
301+
userOpHash: Hash
302+
): Promise<UserOperationReceipt> {
303+
return this.bundlerForChainId(chainId).getUserOpReceipt(userOpHash);
217304
}
218305

306+
/**
307+
* Checks if the Safe contract is deployed on the specified chain.
308+
*
309+
* @param {number} chainId - The ID of the blockchain network where the Safe contract should be checked.
310+
* @returns {Promise<boolean>} - A promise that resolves to `true` if the Safe contract is deployed, otherwise `false`.
311+
*/
219312
async safeDeployed(chainId: number): Promise<boolean> {
220313
return isContract(this.address, chainId);
221314
}
222315

316+
/**
317+
* Determines if the Safe account has sufficient funds to cover the transaction costs.
318+
*
319+
* @param {number} chainId - The ID of the blockchain network where the Safe account is located.
320+
* @param {MetaTransaction[]} transactions - A list of meta-transactions to be evaluated for funding.
321+
* @param {bigint} gasCost - The estimated gas cost of executing the transactions.
322+
* @returns {Promise<boolean>} - A promise that resolves to `true` if the Safe account has sufficient funds, otherwise `false`.
323+
*/
223324
async sufficientlyFunded(
224325
chainId: number,
225326
transactions: MetaTransaction[],
@@ -236,6 +337,12 @@ export class NearSafe {
236337
return txValue + gasCost < safeBalance;
237338
}
238339

340+
/**
341+
* Creates a meta-transaction for adding a new owner to the Safe contract.
342+
*
343+
* @param {Address} address - The address of the new owner to be added.
344+
* @returns {MetaTransaction} - A meta-transaction object for adding the new owner.
345+
*/
239346
addOwnerTx(address: Address): MetaTransaction {
240347
return {
241348
to: this.address,
@@ -244,6 +351,12 @@ export class NearSafe {
244351
};
245352
}
246353

354+
/**
355+
* Creates and returns a new `Erc4337Bundler` instance for the specified chain.
356+
*
357+
* @param {number} chainId - The ID of the blockchain network for which the bundler is to be created.
358+
* @returns {Erc4337Bundler} - A new instance of the `Erc4337Bundler` class configured for the specified chain.
359+
*/
247360
private bundlerForChainId(chainId: number): Erc4337Bundler {
248361
return new Erc4337Bundler(
249362
this.safePack.entryPoint.address,
@@ -252,6 +365,12 @@ export class NearSafe {
252365
);
253366
}
254367

368+
/**
369+
* Decodes transaction data for a given EVM transaction and extracts relevant details.
370+
*
371+
* @param {EvmTransactionData} data - The raw transaction data to be decoded.
372+
* @returns {{ chainId: number; costEstimate: string; transactions: MetaTransaction[] }} - An object containing the chain ID, estimated cost, and a list of decoded meta-transactions.
373+
*/
255374
decodeTxData(data: EvmTransactionData): {
256375
chainId: number;
257376
costEstimate: string;
@@ -266,7 +385,7 @@ export class NearSafe {
266385
data: userOp.callData,
267386
});
268387

269-
// Determine if sungular or double!
388+
// Determine if singular or double!
270389
const transactions = isMultisendTx(args)
271390
? decodeMulti(args[2] as string)
272391
: [

0 commit comments

Comments
 (0)