Skip to content

Commit

Permalink
Add NearConfig to Near Safe Constructor (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith authored Sep 22, 2024
1 parent e87ce9e commit b7bc54c
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 93 deletions.
6 changes: 3 additions & 3 deletions examples/send-tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ethers } from "ethers";
import { isAddress } from "viem";

import { loadArgs, loadEnv } from "./cli";
import { TransactionManager } from "../src";
import { NearSafe } from "../src";

dotenv.config();

Expand All @@ -13,7 +13,7 @@ async function main(): Promise<void> {
{ mpcContractId, recoveryAddress, usePaymaster },
] = await Promise.all([loadEnv(), loadArgs()]);
const chainId = 11155111;
const txManager = await TransactionManager.create({
const txManager = await NearSafe.create({
accountId: nearAccountId,
mpcContractId,
pimlicoKey,
Expand Down Expand Up @@ -52,7 +52,7 @@ async function main(): Promise<void> {
// TODO: Evaluate gas cost (in ETH)
const gasCost = ethers.parseEther("0.01");
// Whenever not using paymaster, or on value transfer, the Safe must be funded.
const sufficientFunded = await txManager.safeSufficientlyFunded(
const sufficientFunded = await txManager.sufficientlyFunded(
chainId,
transactions,
usePaymaster ? 0n : gasCost
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./tx-manager";
export * from "./near-safe";
export * from "./types";
export * from "./util";

Expand Down
6 changes: 3 additions & 3 deletions src/lib/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface DeploymentData {
/**
* All contracts used in account creation & execution
*/
export class ContractSuite {
export class SafeContractSuite {
// Used only for stateless contract reads.
dummyClient: PublicClient;
singleton: DeploymentData;
Expand All @@ -67,7 +67,7 @@ export class ContractSuite {
this.entryPoint = entryPoint;
}

static async init(): Promise<ContractSuite> {
static async init(): Promise<SafeContractSuite> {
// TODO - this is a cheeky hack.
const client = getClient(11155111);
const safeDeployment = (fn: DeploymentFunction): Promise<DeploymentData> =>
Expand All @@ -92,7 +92,7 @@ export class ContractSuite {
// moduleSetup: await moduleSetup.getAddress(),
// entryPoint: await entryPoint.getAddress(),
// });
return new ContractSuite(
return new SafeContractSuite(
client,
singleton,
proxyFactory,
Expand Down
164 changes: 79 additions & 85 deletions src/tx-manager.ts → src/near-safe.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NearConfig } from "near-api-js/lib/near";
import { FinalExecutionOutcome } from "near-api-js/lib/providers";
import {
NearEthAdapter,
Expand All @@ -12,7 +13,7 @@ import { Address, Hash, Hex, serializeSignature } from "viem";

import { Erc4337Bundler } from "./lib/bundler";
import { encodeMulti } from "./lib/multisend";
import { ContractSuite } from "./lib/safe";
import { SafeContractSuite } from "./lib/safe";
import { decodeSafeMessage } from "./lib/safe-message";
import {
EncodedTxData,
Expand All @@ -27,62 +28,64 @@ import {
packSignature,
} from "./util";

export class TransactionManager {
export interface NearSafeConfig {
accountId: string;
mpcContractId: string;
pimlicoKey: string;
nearConfig?: NearConfig;
privateKey?: string;
safeSaltNonce?: string;
}

export class NearSafe {
readonly nearAdapter: NearEthAdapter;
readonly address: Address;

private safePack: ContractSuite;
private safePack: SafeContractSuite;
private setup: string;
private pimlicoKey: string;
private safeSaltNonce: string;
private deployedChains: Set<number>;

static async create(config: NearSafeConfig): Promise<NearSafe> {
const { pimlicoKey, safeSaltNonce } = config;
const [nearAdapter, safePack] = await Promise.all([
setupAdapter({ ...config }),
SafeContractSuite.init(),
]);

const setup = safePack.getSetup([nearAdapter.address]);
const safeAddress = await safePack.addressForSetup(setup, safeSaltNonce);
console.log(`
Near Adapter:
Near Account ID: ${nearAdapter.nearAccountId()}
MPC EOA: ${nearAdapter.address}
Safe: ${safeAddress}
`);
return new NearSafe(
nearAdapter,
safePack,
pimlicoKey,
setup,
safeAddress,
safeSaltNonce || "0"
);
}

constructor(
nearAdapter: NearEthAdapter,
safePack: ContractSuite,
safePack: SafeContractSuite,
pimlicoKey: string,
setup: string,
safeAddress: Address,
safeSaltNonce: string
) {
this.nearAdapter = nearAdapter;
this.address = safeAddress;

this.setup = setup;
this.safePack = safePack;
this.pimlicoKey = pimlicoKey;
this.setup = setup;
this.address = safeAddress;
this.safeSaltNonce = safeSaltNonce;
this.deployedChains = new Set();
}

static async create(config: {
accountId: string;
mpcContractId: string;
pimlicoKey: string;
privateKey?: string;
safeSaltNonce?: string;
}): Promise<TransactionManager> {
const { pimlicoKey } = config;
const [nearAdapter, safePack] = await Promise.all([
setupAdapter({ ...config }),
ContractSuite.init(),
]);
console.log(
`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`
);
const setup = safePack.getSetup([nearAdapter.address]);
const safeAddress = await safePack.addressForSetup(
setup,
config.safeSaltNonce
);
console.log(`Safe Address: ${safeAddress}`);
return new TransactionManager(
nearAdapter,
safePack,
pimlicoKey,
setup,
safeAddress,
config.safeSaltNonce || "0"
);
}

get mpcAddress(): Address {
Expand All @@ -97,14 +100,6 @@ export class TransactionManager {
return await getClient(chainId).getBalance({ address: this.address });
}

bundlerForChainId(chainId: number): Erc4337Bundler {
return new Erc4337Bundler(
this.safePack.entryPoint.address,
this.pimlicoKey,
chainId
);
}

async buildTransaction(args: {
chainId: number;
transactions: MetaTransaction[];
Expand Down Expand Up @@ -175,6 +170,28 @@ export class TransactionManager {
},
};
}
async broadcastEvm(
chainId: number,
outcome: FinalExecutionOutcome,
unsignedUserOp: UserOperation
): Promise<{ signature: Hex; receipt: UserOperationReceipt }> {
const signature = packSignature(
serializeSignature(signatureFromOutcome(outcome))
);
try {
return {
signature,
receipt: await this.executeTransaction(chainId, {
...unsignedUserOp,
signature,
}),
};
} catch (error: unknown) {
throw new Error(
`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`
);
}
}

async executeTransaction(
chainId: number,
Expand All @@ -193,26 +210,10 @@ export class TransactionManager {
}

async safeDeployed(chainId: number): Promise<boolean> {
// Early exit if already known.
if (chainId in this.deployedChains) {
return true;
}
const deployed = await isContract(this.address, chainId);
if (deployed) {
this.deployedChains.add(chainId);
}
return deployed;
return isContract(this.address, chainId);
}

addOwnerTx(address: Address): MetaTransaction {
return {
to: this.address,
value: "0",
data: this.safePack.addOwnerData(address),
};
}

async safeSufficientlyFunded(
async sufficientlyFunded(
chainId: number,
transactions: MetaTransaction[],
gasCost: bigint
Expand All @@ -228,27 +229,20 @@ export class TransactionManager {
return txValue + gasCost < safeBalance;
}

async broadcastEvm(
chainId: number,
outcome: FinalExecutionOutcome,
unsignedUserOp: UserOperation
): Promise<{ signature: Hex; receipt: UserOperationReceipt }> {
const signature = packSignature(
serializeSignature(signatureFromOutcome(outcome))
addOwnerTx(address: Address): MetaTransaction {
return {
to: this.address,
value: "0",
data: this.safePack.addOwnerData(address),
};
}

private bundlerForChainId(chainId: number): Erc4337Bundler {
return new Erc4337Bundler(
this.safePack.entryPoint.address,
this.pimlicoKey,
chainId
);
try {
return {
signature,
receipt: await this.executeTransaction(chainId, {
...unsignedUserOp,
signature,
}),
};
} catch (error: unknown) {
throw new Error(
`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`
);
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/safe.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { zeroAddress } from "viem";

import { ContractSuite as ViemPack } from "../../src/lib/safe";
import { SafeContractSuite as ViemPack } from "../../src/lib/safe";
import { ContractSuite as EthPack } from "../ethers-safe";

describe("Safe Pack", () => {
Expand Down

0 comments on commit b7bc54c

Please sign in to comment.