Skip to content

Commit

Permalink
TxManager.fromChainId (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith authored Sep 14, 2024
1 parent 8fe5fe8 commit 98a00a0
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 58 deletions.
28 changes: 12 additions & 16 deletions examples/load-manager.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
import dotenv from "dotenv";
import { managerForChainId, TransactionManager } from "../src";
import { MpcContract, nearAccountFromAccountId, NearEthAdapter } from "near-ca";
import { TransactionManager } from "../src";
import { setupAdapter } from "near-ca";

dotenv.config();

async function main(): Promise<void> {
const nearNetwork = {
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
const nearAccount = await nearAccountFromAccountId(
process.env.NEAR_ACCOUNT_ID!,
nearNetwork
);
const nearAdapter = await NearEthAdapter.fromConfig({
mpcContract: new MpcContract(nearAccount, "v1.signer-prod.testnet"),
const nearAdapter = await setupAdapter({
accountId: "farmface.testnet",
mpcContractId: "v1.signer-prod.testnet",
});

const managers = new Map<number, TransactionManager>();
console.log("Start");

for (const chainId of [100, 137, 11155111]) {
managers.set(
chainId,
await managerForChainId(nearAdapter, chainId, process.env.PIMLICO_KEY!)
await TransactionManager.fromChainId({
nearAdapter,
chainId,
pimlicoKey: process.env.PIMLICO_KEY!,
})
);
}

console.log("Done");
}

main().catch((err) => {
Expand Down
41 changes: 16 additions & 25 deletions examples/send-tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,24 @@ import dotenv from "dotenv";
import { ethers } from "ethers";
import { loadArgs } from "./cli";
import { TransactionManager } from "../src";
import { MpcContract, nearAccountFromKeyPair, NearEthAdapter } from "near-ca";
import { KeyPair } from "near-api-js";
import { KeyPairString } from "near-api-js/lib/utils";
import { setupAdapter } from "near-ca";

dotenv.config();

async function main(): Promise<void> {
const options = await loadArgs();
const nearAccount = await nearAccountFromKeyPair({
accountId: process.env.NEAR_ACCOUNT_ID!,
keyPair: KeyPair.fromString(
process.env.NEAR_ACCOUNT_PRIVATE_KEY! as KeyPairString
),
network: {
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
},
});
const nearAdapter = await NearEthAdapter.fromConfig({
mpcContract: new MpcContract(nearAccount, options.mpcContractId),
});
const txManager = await TransactionManager.create({
ethRpc: process.env.ETH_RPC!,
const { mpcContractId, recoveryAddress, usePaymaster } = await loadArgs();

const txManager = await TransactionManager.fromChainId({
chainId: 11155111,
nearAdapter: await setupAdapter({
accountId: process.env.NEAR_ACCOUNT_ID!,
// This must be set to transact. Otherwise, instance will be read-only
privateKey: process.env.NEAR_ACCOUNT_PRIVATE_KEY,
mpcContractId,
}),
pimlicoKey: process.env.PIMLICO_KEY!,
nearAdapter,
safeSaltNonce: options.safeSaltNonce,
});

const transactions = [
// TODO: Replace dummy transaction with real user transaction.
{
Expand All @@ -38,15 +29,15 @@ async function main(): Promise<void> {
},
];
// Add Recovery if safe not deployed & recoveryAddress was provided.
if (txManager.safeNotDeployed && options.recoveryAddress) {
const recoveryTx = txManager.addOwnerTx(options.recoveryAddress);
if (txManager.safeNotDeployed && recoveryAddress) {
const recoveryTx = txManager.addOwnerTx(recoveryAddress);
// This would happen (sequentially) after the userTx, but all executed in a single
transactions.push(recoveryTx);
}

const unsignedUserOp = await txManager.buildTransaction({
transactions,
usePaymaster: options.usePaymaster,
usePaymaster,
});
console.log("Unsigned UserOp", unsignedUserOp);
const safeOpHash = await txManager.opHash(unsignedUserOp);
Expand All @@ -57,7 +48,7 @@ async function main(): Promise<void> {
// Whenever not using paymaster, or on value transfer, the Safe must be funded.
const sufficientFunded = await txManager.safeSufficientlyFunded(
transactions,
options.usePaymaster ? 0n : gasCost
usePaymaster ? 0n : gasCost
);
if (!sufficientFunded) {
console.warn(
Expand Down
7 changes: 7 additions & 0 deletions src/lib/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ export class ContractSuite {
["function getNonce(address, uint192 key) view returns (uint256 nonce)"],
provider
);
console.log("Initialized ERC4337 & Safe Module Contracts:", {
singleton: await singleton.getAddress(),
proxyFactory: await proxyFactory.getAddress(),
m4337: await m4337.getAddress(),
moduleSetup: await moduleSetup.getAddress(),
entryPoint: await entryPoint.getAddress(),
});
return new ContractSuite(
provider,
singleton,
Expand Down
39 changes: 22 additions & 17 deletions src/tx-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,14 @@ import { UserOperation, UserOperationReceipt } from "./types";
import { MetaTransaction, encodeMulti } from "ethers-multisend";
import { ContractSuite } from "./lib/safe";

export async function managerForChainId(
nearAdapter: NearEthAdapter,
chainId: number,
pimlicoKey: string
): Promise<TransactionManager> {
return TransactionManager.create({
ethRpc: Network.fromChainId(chainId).rpcUrl,
pimlicoKey,
nearAdapter,
});
}

export class TransactionManager {
readonly provider: ethers.JsonRpcProvider;
readonly nearAdapter: NearEthAdapter;
private safePack: ContractSuite;
private bundler: Erc4337Bundler;
private setup: string;
readonly address: string;
readonly chainId: number;
private safeSaltNonce: string;
private _safeNotDeployed: boolean;

Expand All @@ -35,6 +24,7 @@ export class TransactionManager {
safePack: ContractSuite,
bundler: Erc4337Bundler,
setup: string,
chainId: number,
safeAddress: string,
safeSaltNonce: string,
safeNotDeployed: boolean
Expand All @@ -44,6 +34,7 @@ export class TransactionManager {
this.safePack = safePack;
this.bundler = bundler;
this.setup = setup;
this.chainId = chainId;
this.address = safeAddress;
this.safeSaltNonce = safeSaltNonce;
this._safeNotDeployed = safeNotDeployed;
Expand Down Expand Up @@ -79,12 +70,26 @@ export class TransactionManager {
safePack,
bundler,
setup,
parseInt(chainId.toString()),
safeAddress,
config.safeSaltNonce || "0",
safeNotDeployed
);
}

static async fromChainId(args: {
chainId: number;
nearAdapter: NearEthAdapter;
pimlicoKey: string;
}): Promise<TransactionManager> {
const { pimlicoKey, nearAdapter } = args;
return TransactionManager.create({
ethRpc: Network.fromChainId(args.chainId).rpcUrl,
pimlicoKey,
nearAdapter,
});
}

get safeNotDeployed(): boolean {
return this._safeNotDeployed;
}
Expand All @@ -93,11 +98,6 @@ export class TransactionManager {
return this.nearAdapter.address;
}

async chainId(): Promise<number> {
const network = await this.provider.getNetwork();
return parseInt(network.chainId.toString());
}

async getSafeBalance(): Promise<bigint> {
return await this.provider.getBalance(this.address);
}
Expand Down Expand Up @@ -145,6 +145,11 @@ export class TransactionManager {
}
async encodeSignRequest(tx: BaseTx): Promise<NearEthTxData> {
// TODO - This is sloppy and ignores ChainId!
if (tx.chainId !== this.chainId) {
throw new Error(
`Transaciton request for invalid ChainId ${tx.chainId} != ${this.chainId}`
);
}
const unsignedUserOp = await this.buildTransaction({
transactions: [
{
Expand Down

0 comments on commit 98a00a0

Please sign in to comment.