near-safe 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -33,7 +33,7 @@ NEAR_ACCOUNT_ID=
33
33
  NEAR_ACCOUNT_PRIVATE_KEY=
34
34
 
35
35
  # Head to https://www.pimlico.io/ for an API key
36
- ERC4337_BUNDLER_URL=
36
+ PIMLICO_KEY=
37
37
  ```
38
38
 
39
39
 
@@ -1,9 +1,12 @@
1
1
  import { ethers } from "ethers";
2
- import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
2
+ import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types.js";
3
3
  export declare class Erc4337Bundler {
4
4
  provider: ethers.JsonRpcProvider;
5
5
  entryPointAddress: string;
6
- constructor(bundlerUrl: string, entryPointAddress: string);
6
+ apiKey: string;
7
+ chainId: number;
8
+ constructor(entryPointAddress: string, apiKey: string, chainId: number);
9
+ client(chainId: number): ethers.JsonRpcProvider;
7
10
  getPaymasterData(rawUserOp: UnsignedUserOperation, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
8
11
  sendUserOperation(userOp: UserOperation): Promise<string>;
9
12
  getGasPrice(): Promise<GasPrices>;
@@ -1,19 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Erc4337Bundler = void 0;
4
+ // TODO: Ethers dependency is only for Generic HTTP Provider
4
5
  const ethers_1 = require("ethers");
5
- const util_1 = require("../util");
6
+ const util_js_1 = require("../util.js");
7
+ const viem_1 = require("viem");
8
+ function bundlerUrl(chainId, apikey) {
9
+ return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apikey}`;
10
+ }
6
11
  class Erc4337Bundler {
7
- constructor(bundlerUrl, entryPointAddress) {
12
+ constructor(entryPointAddress, apiKey, chainId) {
8
13
  this.entryPointAddress = entryPointAddress;
9
- this.provider = new ethers_1.ethers.JsonRpcProvider(bundlerUrl);
14
+ this.apiKey = apiKey;
15
+ this.chainId = chainId;
16
+ this.provider = new ethers_1.ethers.JsonRpcProvider(bundlerUrl(chainId, apiKey));
17
+ }
18
+ client(chainId) {
19
+ return new ethers_1.ethers.JsonRpcProvider(bundlerUrl(chainId, this.apiKey));
10
20
  }
11
21
  async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
12
22
  // TODO: Keep this option out of the bundler
13
23
  if (usePaymaster) {
14
24
  console.log("Requesting paymaster data...");
15
25
  const data = this.provider.send("pm_sponsorUserOperation", [
16
- { ...rawUserOp, signature: util_1.PLACEHOLDER_SIG },
26
+ { ...rawUserOp, signature: util_js_1.PLACEHOLDER_SIG },
17
27
  this.entryPointAddress,
18
28
  ]);
19
29
  return data;
@@ -53,8 +63,8 @@ exports.Erc4337Bundler = Erc4337Bundler;
53
63
  // TODO(bh2smith) Should probably get reasonable estimates here:
54
64
  const defaultPaymasterData = (safeNotDeployed) => {
55
65
  return {
56
- verificationGasLimit: ethers_1.ethers.toBeHex(safeNotDeployed ? 500000 : 100000),
57
- callGasLimit: ethers_1.ethers.toBeHex(100000),
58
- preVerificationGas: ethers_1.ethers.toBeHex(100000),
66
+ verificationGasLimit: (0, viem_1.toHex)(safeNotDeployed ? 500000 : 100000),
67
+ callGasLimit: (0, viem_1.toHex)(100000),
68
+ preVerificationGas: (0, viem_1.toHex)(100000),
59
69
  };
60
70
  };
@@ -1,6 +1,7 @@
1
1
  import { ethers } from "ethers";
2
2
  import { GasPrice, UnsignedUserOperation, UserOperation } from "../types";
3
3
  import { MetaTransaction } from "ethers-multisend";
4
+ import { Address, Hash, Hex } from "viem";
4
5
  /**
5
6
  * All contracts used in account creation & execution
6
7
  */
@@ -12,13 +13,13 @@ export declare class ContractSuite {
12
13
  moduleSetup: ethers.Contract;
13
14
  entryPoint: ethers.Contract;
14
15
  constructor(provider: ethers.JsonRpcProvider, singleton: ethers.Contract, proxyFactory: ethers.Contract, m4337: ethers.Contract, moduleSetup: ethers.Contract, entryPoint: ethers.Contract);
15
- static init(provider: ethers.JsonRpcProvider): Promise<ContractSuite>;
16
- addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<string>;
17
- getSetup(owners: string[]): Promise<string>;
18
- getOpHash(unsignedUserOp: UserOperation): Promise<string>;
16
+ static init(): Promise<ContractSuite>;
17
+ addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<Address>;
18
+ getSetup(owners: string[]): Promise<Hex>;
19
+ getOpHash(unsignedUserOp: UserOperation): Promise<Hash>;
19
20
  factoryDataForSetup(safeNotDeployed: boolean, setup: string, safeSaltNonce: string): {
20
- factory?: ethers.AddressLike;
21
- factoryData?: string;
21
+ factory?: Address;
22
+ factoryData?: Hex;
22
23
  };
23
- buildUserOp(txData: MetaTransaction, safeAddress: ethers.AddressLike, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
24
+ buildUserOp(txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
24
25
  }
@@ -17,9 +17,13 @@ class ContractSuite {
17
17
  this.moduleSetup = moduleSetup;
18
18
  this.entryPoint = entryPoint;
19
19
  }
20
- static async init(provider) {
20
+ static async init() {
21
+ // TODO - this is a cheeky hack.
22
+ const provider = new ethers_1.ethers.JsonRpcProvider("https://rpc2.sepolia.org");
21
23
  const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
22
- const m4337Deployment = (fn) => getDeployment(fn, { provider, version: "0.3.0" });
24
+ const m4337Deployment = async (fn) => {
25
+ return getDeployment(fn, { provider, version: "0.3.0" });
26
+ };
23
27
  // Need this first to get entryPoint address
24
28
  const m4337 = await m4337Deployment(safe_modules_deployments_1.getSafe4337ModuleDeployment);
25
29
  const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
@@ -29,6 +33,13 @@ class ContractSuite {
29
33
  m4337.SUPPORTED_ENTRYPOINT(),
30
34
  ]);
31
35
  const entryPoint = new ethers_1.ethers.Contract(supportedEntryPoint, ["function getNonce(address, uint192 key) view returns (uint256 nonce)"], provider);
36
+ console.log("Initialized ERC4337 & Safe Module Contracts:", {
37
+ singleton: await singleton.getAddress(),
38
+ proxyFactory: await proxyFactory.getAddress(),
39
+ m4337: await m4337.getAddress(),
40
+ moduleSetup: await moduleSetup.getAddress(),
41
+ entryPoint: await entryPoint.getAddress(),
42
+ });
32
43
  return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
33
44
  }
34
45
  async addressForSetup(setup, saltNonce) {
@@ -58,16 +69,15 @@ class ContractSuite {
58
69
  ]);
59
70
  return setup;
60
71
  }
61
- async getOpHash(unsignedUserOp
62
- // paymasterData: PaymasterData
63
- ) {
72
+ async getOpHash(unsignedUserOp) {
73
+ const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
64
74
  return this.m4337.getOperationHash({
65
75
  ...unsignedUserOp,
66
- initCode: unsignedUserOp.factory
67
- ? ethers_1.ethers.solidityPacked(["address", "bytes"], [unsignedUserOp.factory, unsignedUserOp.factoryData])
76
+ initCode: factory
77
+ ? ethers_1.ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
68
78
  : "0x",
69
- accountGasLimits: (0, util_1.packGas)(unsignedUserOp.verificationGasLimit, unsignedUserOp.callGasLimit),
70
- gasFees: (0, util_1.packGas)(unsignedUserOp.maxPriorityFeePerGas, unsignedUserOp.maxFeePerGas),
79
+ accountGasLimits: (0, util_1.packGas)(verificationGasLimit, callGasLimit),
80
+ gasFees: (0, util_1.packGas)(maxPriorityFeePerGas, maxFeePerGas),
71
81
  paymasterAndData: (0, util_1.packPaymasterData)(unsignedUserOp),
72
82
  signature: util_1.PLACEHOLDER_SIG,
73
83
  });
@@ -101,8 +111,17 @@ exports.ContractSuite = ContractSuite;
101
111
  async function getDeployment(fn, { provider, version }) {
102
112
  const { chainId } = await provider.getNetwork();
103
113
  const deployment = fn({ version });
104
- if (!deployment || !deployment.networkAddresses[`${chainId}`]) {
105
- throw new Error(`Deployment not found for version ${version} and chainId ${chainId}`);
114
+ if (!deployment) {
115
+ throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
116
+ }
117
+ let address = deployment.networkAddresses[`${chainId}`];
118
+ if (!address) {
119
+ // console.warn(
120
+ // `Deployment asset ${fn.name} not listed on chainId ${chainId}, using likely fallback. For more info visit https://github.com/safe-global/safe-modules-deployments`
121
+ // );
122
+ // TODO: This is a cheeky hack. Real solution proposed in
123
+ // https://github.com/Mintbase/near-safe/issues/42
124
+ address = deployment.networkAddresses["11155111"];
106
125
  }
107
- return new ethers_1.ethers.Contract(deployment.networkAddresses[`${chainId}`], deployment.abi, provider);
126
+ return new ethers_1.ethers.Contract(address, deployment.abi, provider);
108
127
  }
@@ -1,36 +1,37 @@
1
- import { ethers } from "ethers";
2
1
  import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
3
2
  import { Erc4337Bundler } from "./lib/bundler";
4
3
  import { UserOperation, UserOperationReceipt } from "./types";
5
4
  import { MetaTransaction } from "ethers-multisend";
6
5
  import { ContractSuite } from "./lib/safe";
6
+ import { Address, Hash, Hex } from "viem";
7
7
  export declare class TransactionManager {
8
- readonly provider: ethers.JsonRpcProvider;
9
8
  readonly nearAdapter: NearEthAdapter;
9
+ readonly address: Address;
10
+ readonly entryPointAddress: Address;
10
11
  private safePack;
11
- private bundler;
12
12
  private setup;
13
- readonly safeAddress: string;
13
+ private pimlicoKey;
14
14
  private safeSaltNonce;
15
- private _safeNotDeployed;
16
- constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, safeAddress: string, safeSaltNonce: string, safeNotDeployed: boolean);
15
+ private deployedChains;
16
+ constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
17
17
  static create(config: {
18
- ethRpc: string;
19
- erc4337BundlerUrl: string;
18
+ pimlicoKey: string;
20
19
  nearAdapter: NearEthAdapter;
21
20
  safeSaltNonce?: string;
22
21
  }): Promise<TransactionManager>;
23
- get safeNotDeployed(): boolean;
24
- get nearEOA(): `0x${string}`;
25
- getSafeBalance(): Promise<bigint>;
22
+ get mpcAddress(): Address;
23
+ getBalance(chainId: number): Promise<bigint>;
24
+ bundlerForChainId(chainId: number): Erc4337Bundler;
26
25
  buildTransaction(args: {
26
+ chainId: number;
27
27
  transactions: MetaTransaction[];
28
28
  usePaymaster: boolean;
29
29
  }): Promise<UserOperation>;
30
- signTransaction(safeOpHash: string): Promise<string>;
31
- opHash(userOp: UserOperation): Promise<string>;
30
+ signTransaction(safeOpHash: Hex): Promise<Hex>;
31
+ opHash(userOp: UserOperation): Promise<Hash>;
32
32
  encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
33
- executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
33
+ executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
34
+ safeDeployed(chainId: number): Promise<boolean>;
34
35
  addOwnerTx(address: string): MetaTransaction;
35
- safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
36
+ safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
36
37
  }
@@ -1,68 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TransactionManager = void 0;
4
- const ethers_1 = require("ethers");
4
+ const near_ca_1 = require("near-ca");
5
5
  const bundler_1 = require("./lib/bundler");
6
6
  const util_1 = require("./util");
7
- const near_1 = require("./lib/near");
8
7
  const ethers_multisend_1 = require("ethers-multisend");
9
8
  const safe_1 = require("./lib/safe");
10
9
  class TransactionManager {
11
- constructor(provider, nearAdapter, safePack, bundler, setup, safeAddress, safeSaltNonce, safeNotDeployed) {
12
- this.provider = provider;
10
+ constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
13
11
  this.nearAdapter = nearAdapter;
14
12
  this.safePack = safePack;
15
- this.bundler = bundler;
13
+ this.pimlicoKey = pimlicoKey;
14
+ this.entryPointAddress = entryPointAddress;
16
15
  this.setup = setup;
17
- this.safeAddress = safeAddress;
16
+ this.address = safeAddress;
18
17
  this.safeSaltNonce = safeSaltNonce;
19
- this._safeNotDeployed = safeNotDeployed;
18
+ this.deployedChains = new Set();
20
19
  }
21
20
  static async create(config) {
22
- const adapter = config.nearAdapter;
23
- const provider = new ethers_1.ethers.JsonRpcProvider(config.ethRpc);
24
- const safePack = await safe_1.ContractSuite.init(provider);
25
- console.log(`Near Adapter: ${adapter.nearAccountId()} <> ${adapter.address}`);
26
- const bundler = new bundler_1.Erc4337Bundler(config.erc4337BundlerUrl, await safePack.entryPoint.getAddress());
27
- const setup = await safePack.getSetup([adapter.address]);
21
+ const { nearAdapter, pimlicoKey } = config;
22
+ const safePack = await safe_1.ContractSuite.init();
23
+ console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
24
+ const setup = await safePack.getSetup([nearAdapter.address]);
28
25
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
29
- const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
30
- console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
31
- return new TransactionManager(provider, adapter, safePack, bundler, setup, safeAddress, config.safeSaltNonce || "0", safeNotDeployed);
26
+ const entryPointAddress = (await safePack.entryPoint.getAddress());
27
+ console.log(`Safe Address: ${safeAddress}`);
28
+ return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, config.safeSaltNonce || "0");
32
29
  }
33
- get safeNotDeployed() {
34
- return this._safeNotDeployed;
35
- }
36
- get nearEOA() {
30
+ get mpcAddress() {
37
31
  return this.nearAdapter.address;
38
32
  }
39
- async getSafeBalance() {
40
- return await this.provider.getBalance(this.safeAddress);
33
+ async getBalance(chainId) {
34
+ const provider = near_ca_1.Network.fromChainId(chainId).client;
35
+ return await provider.getBalance({ address: this.address });
36
+ }
37
+ bundlerForChainId(chainId) {
38
+ return new bundler_1.Erc4337Bundler(this.entryPointAddress, this.pimlicoKey, chainId);
41
39
  }
42
40
  async buildTransaction(args) {
43
- const { transactions, usePaymaster } = args;
44
- const gasFees = (await this.bundler.getGasPrice()).fast;
45
- // const gasFees = await this.provider.getFeeData();
41
+ const { transactions, usePaymaster, chainId } = args;
42
+ const bundler = this.bundlerForChainId(chainId);
43
+ const gasFees = (await bundler.getGasPrice()).fast;
46
44
  // Build Singular MetaTransaction for Multisend from transaction list.
47
45
  if (transactions.length === 0) {
48
46
  throw new Error("Empty transaction set!");
49
47
  }
50
48
  const tx = transactions.length > 1 ? (0, ethers_multisend_1.encodeMulti)(transactions) : transactions[0];
51
- const rawUserOp = await this.safePack.buildUserOp(tx, this.safeAddress, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
52
- const paymasterData = await this.bundler.getPaymasterData(rawUserOp, usePaymaster, this.safeNotDeployed);
49
+ const safeNotDeployed = !(await this.safeDeployed(chainId));
50
+ const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, safeNotDeployed, this.safeSaltNonce);
51
+ const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed);
53
52
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
54
53
  return unsignedUserOp;
55
54
  }
56
55
  async signTransaction(safeOpHash) {
57
- const signature = await (0, near_1.getNearSignature)(this.nearAdapter, safeOpHash);
56
+ const signature = await this.nearAdapter.sign(safeOpHash);
58
57
  return (0, util_1.packSignature)(signature);
59
58
  }
60
59
  async opHash(userOp) {
61
60
  return this.safePack.getOpHash(userOp);
62
61
  }
63
62
  async encodeSignRequest(tx) {
64
- // TODO - This is sloppy and ignores ChainId!
65
63
  const unsignedUserOp = await this.buildTransaction({
64
+ chainId: tx.chainId,
66
65
  transactions: [
67
66
  {
68
67
  to: tx.to,
@@ -83,29 +82,40 @@ class TransactionManager {
83
82
  evmMessage: JSON.stringify(unsignedUserOp),
84
83
  };
85
84
  }
86
- async executeTransaction(userOp) {
87
- const userOpHash = await this.bundler.sendUserOperation(userOp);
85
+ async executeTransaction(chainId, userOp) {
86
+ const bundler = this.bundlerForChainId(chainId);
87
+ const userOpHash = await bundler.sendUserOperation(userOp);
88
88
  console.log("UserOp Hash", userOpHash);
89
- const userOpReceipt = await this.bundler.getUserOpReceipt(userOpHash);
89
+ const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
90
90
  console.log("userOp Receipt", userOpReceipt);
91
91
  // Update safeNotDeployed after the first transaction
92
- this._safeNotDeployed =
93
- (await this.provider.getCode(this.safeAddress)) === "0x";
92
+ this.safeDeployed(chainId);
94
93
  return userOpReceipt;
95
94
  }
95
+ async safeDeployed(chainId) {
96
+ if (chainId in this.deployedChains) {
97
+ return true;
98
+ }
99
+ const provider = near_ca_1.Network.fromChainId(chainId).client;
100
+ const deployed = (await provider.getCode({ address: this.address })) !== "0x";
101
+ if (deployed) {
102
+ this.deployedChains.add(chainId);
103
+ }
104
+ return deployed;
105
+ }
96
106
  addOwnerTx(address) {
97
107
  return {
98
- to: this.safeAddress,
108
+ to: this.address,
99
109
  value: "0",
100
110
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
101
111
  };
102
112
  }
103
- async safeSufficientlyFunded(transactions, gasCost) {
113
+ async safeSufficientlyFunded(chainId, transactions, gasCost) {
104
114
  const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
105
115
  if (txValue + gasCost === 0n) {
106
116
  return true;
107
117
  }
108
- const safeBalance = await this.getSafeBalance();
118
+ const safeBalance = await this.getBalance(chainId);
109
119
  return txValue + gasCost < safeBalance;
110
120
  }
111
121
  }
@@ -1,30 +1,30 @@
1
- import { ethers } from "ethers";
1
+ import { Address, Hex } from "viem";
2
2
  export interface UnsignedUserOperation {
3
- sender: ethers.AddressLike;
3
+ sender: Address;
4
4
  nonce: string;
5
- factory?: ethers.AddressLike;
6
- factoryData?: ethers.BytesLike;
7
- callData: string;
8
- maxPriorityFeePerGas: string;
9
- maxFeePerGas: string;
5
+ factory?: Address;
6
+ factoryData?: Hex;
7
+ callData: Hex;
8
+ maxPriorityFeePerGas: Hex;
9
+ maxFeePerGas: Hex;
10
10
  }
11
11
  /**
12
12
  * Supported Representation of UserOperation for EntryPoint v0.7
13
13
  */
14
14
  export interface UserOperation extends UnsignedUserOperation {
15
- verificationGasLimit: string;
16
- callGasLimit: string;
17
- preVerificationGas: string;
18
- signature?: string;
15
+ verificationGasLimit: Hex;
16
+ callGasLimit: Hex;
17
+ preVerificationGas: Hex;
18
+ signature?: Hex;
19
19
  }
20
20
  export interface PaymasterData {
21
- paymaster?: string;
22
- paymasterData?: string;
23
- paymasterVerificationGasLimit?: string;
24
- paymasterPostOpGasLimit?: string;
25
- verificationGasLimit: string;
26
- callGasLimit: string;
27
- preVerificationGas: string;
21
+ paymaster?: Address;
22
+ paymasterData?: Hex;
23
+ paymasterVerificationGasLimit?: Hex;
24
+ paymasterPostOpGasLimit?: Hex;
25
+ verificationGasLimit: Hex;
26
+ callGasLimit: Hex;
27
+ preVerificationGas: Hex;
28
28
  }
29
29
  export interface UserOptions {
30
30
  usePaymaster: boolean;
@@ -32,10 +32,7 @@ export interface UserOptions {
32
32
  mpcContractId: string;
33
33
  recoveryAddress?: string;
34
34
  }
35
- export type TStatus = "success" | "reverted";
36
- export type Address = ethers.AddressLike;
37
- export type Hex = `0x${string}`;
38
- export type Hash = `0x${string}`;
35
+ export type TxStatus = "success" | "reverted";
39
36
  interface Log {
40
37
  logIndex: string;
41
38
  transactionIndex: string;
@@ -49,19 +46,19 @@ interface Log {
49
46
  interface Receipt {
50
47
  transactionHash: Hex;
51
48
  transactionIndex: bigint;
52
- blockHash: Hash;
49
+ blockHash: Hex;
53
50
  blockNumber: bigint;
54
51
  from: Address;
55
- to: Address | null;
52
+ to?: Address;
56
53
  cumulativeGasUsed: bigint;
57
- status: TStatus;
54
+ status: TxStatus;
58
55
  gasUsed: bigint;
59
- contractAddress: Address | null;
56
+ contractAddress?: Address;
60
57
  logsBloom: Hex;
61
58
  effectiveGasPrice: bigint;
62
59
  }
63
60
  export type UserOperationReceipt = {
64
- userOpHash: Hash;
61
+ userOpHash: Hex;
65
62
  entryPoint: Address;
66
63
  sender: Address;
67
64
  nonce: bigint;
@@ -1,8 +1,10 @@
1
- import { ethers } from "ethers";
2
- import { PaymasterData } from "./types";
1
+ import { PaymasterData } from "./types.js";
3
2
  import { MetaTransaction } from "ethers-multisend";
4
- export declare const PLACEHOLDER_SIG: string;
5
- export declare const packGas: (hi: ethers.BigNumberish, lo: ethers.BigNumberish) => string;
6
- export declare function packSignature(signature: string, validFrom?: number, validTo?: number): string;
7
- export declare function packPaymasterData(data: PaymasterData): string;
3
+ import { Hex } from "viem";
4
+ export declare const PLACEHOLDER_SIG: `0x${string}`;
5
+ type IntLike = Hex | bigint | string | number;
6
+ export declare const packGas: (hi: IntLike, lo: IntLike) => string;
7
+ export declare function packSignature(signature: `0x${string}`, validFrom?: number, validTo?: number): Hex;
8
+ export declare function packPaymasterData(data: PaymasterData): Hex;
8
9
  export declare function containsValue(transactions: MetaTransaction[]): boolean;
10
+ export {};
package/dist/cjs/util.js CHANGED
@@ -4,22 +4,22 @@ exports.packGas = exports.PLACEHOLDER_SIG = void 0;
4
4
  exports.packSignature = packSignature;
5
5
  exports.packPaymasterData = packPaymasterData;
6
6
  exports.containsValue = containsValue;
7
- const ethers_1 = require("ethers");
8
- exports.PLACEHOLDER_SIG = ethers_1.ethers.solidityPacked(["uint48", "uint48"], [0, 0]);
9
- const packGas = (hi, lo) => ethers_1.ethers.solidityPacked(["uint128", "uint128"], [hi, lo]);
7
+ const viem_1 = require("viem");
8
+ exports.PLACEHOLDER_SIG = (0, viem_1.encodePacked)(["uint48", "uint48"], [0, 0]);
9
+ const packGas = (hi, lo) => (0, viem_1.encodePacked)(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
10
10
  exports.packGas = packGas;
11
11
  function packSignature(signature, validFrom = 0, validTo = 0) {
12
- return ethers_1.ethers.solidityPacked(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]);
12
+ return (0, viem_1.encodePacked)(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]);
13
13
  }
14
14
  function packPaymasterData(data) {
15
- return data.paymaster
16
- ? ethers_1.ethers.hexlify(ethers_1.ethers.concat([
15
+ return (data.paymaster
16
+ ? (0, viem_1.concatHex)([
17
17
  data.paymaster,
18
- ethers_1.ethers.toBeHex(data.paymasterVerificationGasLimit || "0x", 16),
19
- ethers_1.ethers.toBeHex(data.paymasterPostOpGasLimit || "0x", 16),
18
+ (0, viem_1.toHex)(BigInt(data.paymasterVerificationGasLimit || 0n), { size: 16 }),
19
+ (0, viem_1.toHex)(BigInt(data.paymasterPostOpGasLimit || 0n), { size: 16 }),
20
20
  data.paymasterData || "0x",
21
- ]))
22
- : "0x";
21
+ ])
22
+ : "0x");
23
23
  }
24
24
  function containsValue(transactions) {
25
25
  return transactions.some((tx) => tx.value !== "0");
@@ -1,9 +1,12 @@
1
1
  import { ethers } from "ethers";
2
- import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
2
+ import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types.js";
3
3
  export declare class Erc4337Bundler {
4
4
  provider: ethers.JsonRpcProvider;
5
5
  entryPointAddress: string;
6
- constructor(bundlerUrl: string, entryPointAddress: string);
6
+ apiKey: string;
7
+ chainId: number;
8
+ constructor(entryPointAddress: string, apiKey: string, chainId: number);
9
+ client(chainId: number): ethers.JsonRpcProvider;
7
10
  getPaymasterData(rawUserOp: UnsignedUserOperation, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
8
11
  sendUserOperation(userOp: UserOperation): Promise<string>;
9
12
  getGasPrice(): Promise<GasPrices>;
@@ -1,11 +1,23 @@
1
+ // TODO: Ethers dependency is only for Generic HTTP Provider
1
2
  import { ethers } from "ethers";
2
- import { PLACEHOLDER_SIG } from "../util";
3
+ import { PLACEHOLDER_SIG } from "../util.js";
4
+ import { toHex } from "viem";
5
+ function bundlerUrl(chainId, apikey) {
6
+ return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apikey}`;
7
+ }
3
8
  export class Erc4337Bundler {
4
9
  provider;
5
10
  entryPointAddress;
6
- constructor(bundlerUrl, entryPointAddress) {
11
+ apiKey;
12
+ chainId;
13
+ constructor(entryPointAddress, apiKey, chainId) {
7
14
  this.entryPointAddress = entryPointAddress;
8
- this.provider = new ethers.JsonRpcProvider(bundlerUrl);
15
+ this.apiKey = apiKey;
16
+ this.chainId = chainId;
17
+ this.provider = new ethers.JsonRpcProvider(bundlerUrl(chainId, apiKey));
18
+ }
19
+ client(chainId) {
20
+ return new ethers.JsonRpcProvider(bundlerUrl(chainId, this.apiKey));
9
21
  }
10
22
  async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
11
23
  // TODO: Keep this option out of the bundler
@@ -51,8 +63,8 @@ export class Erc4337Bundler {
51
63
  // TODO(bh2smith) Should probably get reasonable estimates here:
52
64
  const defaultPaymasterData = (safeNotDeployed) => {
53
65
  return {
54
- verificationGasLimit: ethers.toBeHex(safeNotDeployed ? 500000 : 100000),
55
- callGasLimit: ethers.toBeHex(100000),
56
- preVerificationGas: ethers.toBeHex(100000),
66
+ verificationGasLimit: toHex(safeNotDeployed ? 500000 : 100000),
67
+ callGasLimit: toHex(100000),
68
+ preVerificationGas: toHex(100000),
57
69
  };
58
70
  };
@@ -1,6 +1,7 @@
1
1
  import { ethers } from "ethers";
2
2
  import { GasPrice, UnsignedUserOperation, UserOperation } from "../types";
3
3
  import { MetaTransaction } from "ethers-multisend";
4
+ import { Address, Hash, Hex } from "viem";
4
5
  /**
5
6
  * All contracts used in account creation & execution
6
7
  */
@@ -12,13 +13,13 @@ export declare class ContractSuite {
12
13
  moduleSetup: ethers.Contract;
13
14
  entryPoint: ethers.Contract;
14
15
  constructor(provider: ethers.JsonRpcProvider, singleton: ethers.Contract, proxyFactory: ethers.Contract, m4337: ethers.Contract, moduleSetup: ethers.Contract, entryPoint: ethers.Contract);
15
- static init(provider: ethers.JsonRpcProvider): Promise<ContractSuite>;
16
- addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<string>;
17
- getSetup(owners: string[]): Promise<string>;
18
- getOpHash(unsignedUserOp: UserOperation): Promise<string>;
16
+ static init(): Promise<ContractSuite>;
17
+ addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<Address>;
18
+ getSetup(owners: string[]): Promise<Hex>;
19
+ getOpHash(unsignedUserOp: UserOperation): Promise<Hash>;
19
20
  factoryDataForSetup(safeNotDeployed: boolean, setup: string, safeSaltNonce: string): {
20
- factory?: ethers.AddressLike;
21
- factoryData?: string;
21
+ factory?: Address;
22
+ factoryData?: Hex;
22
23
  };
23
- buildUserOp(txData: MetaTransaction, safeAddress: ethers.AddressLike, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
24
+ buildUserOp(txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
24
25
  }
@@ -20,9 +20,13 @@ export class ContractSuite {
20
20
  this.moduleSetup = moduleSetup;
21
21
  this.entryPoint = entryPoint;
22
22
  }
23
- static async init(provider) {
23
+ static async init() {
24
+ // TODO - this is a cheeky hack.
25
+ const provider = new ethers.JsonRpcProvider("https://rpc2.sepolia.org");
24
26
  const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
25
- const m4337Deployment = (fn) => getDeployment(fn, { provider, version: "0.3.0" });
27
+ const m4337Deployment = async (fn) => {
28
+ return getDeployment(fn, { provider, version: "0.3.0" });
29
+ };
26
30
  // Need this first to get entryPoint address
27
31
  const m4337 = await m4337Deployment(getSafe4337ModuleDeployment);
28
32
  const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
@@ -32,6 +36,13 @@ export class ContractSuite {
32
36
  m4337.SUPPORTED_ENTRYPOINT(),
33
37
  ]);
34
38
  const entryPoint = new ethers.Contract(supportedEntryPoint, ["function getNonce(address, uint192 key) view returns (uint256 nonce)"], provider);
39
+ console.log("Initialized ERC4337 & Safe Module Contracts:", {
40
+ singleton: await singleton.getAddress(),
41
+ proxyFactory: await proxyFactory.getAddress(),
42
+ m4337: await m4337.getAddress(),
43
+ moduleSetup: await moduleSetup.getAddress(),
44
+ entryPoint: await entryPoint.getAddress(),
45
+ });
35
46
  return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
36
47
  }
37
48
  async addressForSetup(setup, saltNonce) {
@@ -61,16 +72,15 @@ export class ContractSuite {
61
72
  ]);
62
73
  return setup;
63
74
  }
64
- async getOpHash(unsignedUserOp
65
- // paymasterData: PaymasterData
66
- ) {
75
+ async getOpHash(unsignedUserOp) {
76
+ const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
67
77
  return this.m4337.getOperationHash({
68
78
  ...unsignedUserOp,
69
- initCode: unsignedUserOp.factory
70
- ? ethers.solidityPacked(["address", "bytes"], [unsignedUserOp.factory, unsignedUserOp.factoryData])
79
+ initCode: factory
80
+ ? ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
71
81
  : "0x",
72
- accountGasLimits: packGas(unsignedUserOp.verificationGasLimit, unsignedUserOp.callGasLimit),
73
- gasFees: packGas(unsignedUserOp.maxPriorityFeePerGas, unsignedUserOp.maxFeePerGas),
82
+ accountGasLimits: packGas(verificationGasLimit, callGasLimit),
83
+ gasFees: packGas(maxPriorityFeePerGas, maxFeePerGas),
74
84
  paymasterAndData: packPaymasterData(unsignedUserOp),
75
85
  signature: PLACEHOLDER_SIG,
76
86
  });
@@ -103,8 +113,17 @@ export class ContractSuite {
103
113
  async function getDeployment(fn, { provider, version }) {
104
114
  const { chainId } = await provider.getNetwork();
105
115
  const deployment = fn({ version });
106
- if (!deployment || !deployment.networkAddresses[`${chainId}`]) {
107
- throw new Error(`Deployment not found for version ${version} and chainId ${chainId}`);
116
+ if (!deployment) {
117
+ throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
118
+ }
119
+ let address = deployment.networkAddresses[`${chainId}`];
120
+ if (!address) {
121
+ // console.warn(
122
+ // `Deployment asset ${fn.name} not listed on chainId ${chainId}, using likely fallback. For more info visit https://github.com/safe-global/safe-modules-deployments`
123
+ // );
124
+ // TODO: This is a cheeky hack. Real solution proposed in
125
+ // https://github.com/Mintbase/near-safe/issues/42
126
+ address = deployment.networkAddresses["11155111"];
108
127
  }
109
- return new ethers.Contract(deployment.networkAddresses[`${chainId}`], deployment.abi, provider);
128
+ return new ethers.Contract(address, deployment.abi, provider);
110
129
  }
@@ -1,36 +1,37 @@
1
- import { ethers } from "ethers";
2
1
  import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
3
2
  import { Erc4337Bundler } from "./lib/bundler";
4
3
  import { UserOperation, UserOperationReceipt } from "./types";
5
4
  import { MetaTransaction } from "ethers-multisend";
6
5
  import { ContractSuite } from "./lib/safe";
6
+ import { Address, Hash, Hex } from "viem";
7
7
  export declare class TransactionManager {
8
- readonly provider: ethers.JsonRpcProvider;
9
8
  readonly nearAdapter: NearEthAdapter;
9
+ readonly address: Address;
10
+ readonly entryPointAddress: Address;
10
11
  private safePack;
11
- private bundler;
12
12
  private setup;
13
- readonly safeAddress: string;
13
+ private pimlicoKey;
14
14
  private safeSaltNonce;
15
- private _safeNotDeployed;
16
- constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, safeAddress: string, safeSaltNonce: string, safeNotDeployed: boolean);
15
+ private deployedChains;
16
+ constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
17
17
  static create(config: {
18
- ethRpc: string;
19
- erc4337BundlerUrl: string;
18
+ pimlicoKey: string;
20
19
  nearAdapter: NearEthAdapter;
21
20
  safeSaltNonce?: string;
22
21
  }): Promise<TransactionManager>;
23
- get safeNotDeployed(): boolean;
24
- get nearEOA(): `0x${string}`;
25
- getSafeBalance(): Promise<bigint>;
22
+ get mpcAddress(): Address;
23
+ getBalance(chainId: number): Promise<bigint>;
24
+ bundlerForChainId(chainId: number): Erc4337Bundler;
26
25
  buildTransaction(args: {
26
+ chainId: number;
27
27
  transactions: MetaTransaction[];
28
28
  usePaymaster: boolean;
29
29
  }): Promise<UserOperation>;
30
- signTransaction(safeOpHash: string): Promise<string>;
31
- opHash(userOp: UserOperation): Promise<string>;
30
+ signTransaction(safeOpHash: Hex): Promise<Hex>;
31
+ opHash(userOp: UserOperation): Promise<Hash>;
32
32
  encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
33
- executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
33
+ executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
34
+ safeDeployed(chainId: number): Promise<boolean>;
34
35
  addOwnerTx(address: string): MetaTransaction;
35
- safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
36
+ safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
36
37
  }
@@ -1,73 +1,72 @@
1
- import { ethers } from "ethers";
1
+ import { Network } from "near-ca";
2
2
  import { Erc4337Bundler } from "./lib/bundler";
3
3
  import { packSignature } from "./util";
4
- import { getNearSignature } from "./lib/near";
5
4
  import { encodeMulti } from "ethers-multisend";
6
5
  import { ContractSuite } from "./lib/safe";
7
6
  export class TransactionManager {
8
- provider;
9
7
  nearAdapter;
8
+ address;
9
+ entryPointAddress;
10
10
  safePack;
11
- bundler;
12
11
  setup;
13
- safeAddress;
12
+ pimlicoKey;
14
13
  safeSaltNonce;
15
- _safeNotDeployed;
16
- constructor(provider, nearAdapter, safePack, bundler, setup, safeAddress, safeSaltNonce, safeNotDeployed) {
17
- this.provider = provider;
14
+ deployedChains;
15
+ constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
18
16
  this.nearAdapter = nearAdapter;
19
17
  this.safePack = safePack;
20
- this.bundler = bundler;
18
+ this.pimlicoKey = pimlicoKey;
19
+ this.entryPointAddress = entryPointAddress;
21
20
  this.setup = setup;
22
- this.safeAddress = safeAddress;
21
+ this.address = safeAddress;
23
22
  this.safeSaltNonce = safeSaltNonce;
24
- this._safeNotDeployed = safeNotDeployed;
23
+ this.deployedChains = new Set();
25
24
  }
26
25
  static async create(config) {
27
- const adapter = config.nearAdapter;
28
- const provider = new ethers.JsonRpcProvider(config.ethRpc);
29
- const safePack = await ContractSuite.init(provider);
30
- console.log(`Near Adapter: ${adapter.nearAccountId()} <> ${adapter.address}`);
31
- const bundler = new Erc4337Bundler(config.erc4337BundlerUrl, await safePack.entryPoint.getAddress());
32
- const setup = await safePack.getSetup([adapter.address]);
26
+ const { nearAdapter, pimlicoKey } = config;
27
+ const safePack = await ContractSuite.init();
28
+ console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
29
+ const setup = await safePack.getSetup([nearAdapter.address]);
33
30
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
34
- const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
35
- console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
36
- return new TransactionManager(provider, adapter, safePack, bundler, setup, safeAddress, config.safeSaltNonce || "0", safeNotDeployed);
31
+ const entryPointAddress = (await safePack.entryPoint.getAddress());
32
+ console.log(`Safe Address: ${safeAddress}`);
33
+ return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, config.safeSaltNonce || "0");
37
34
  }
38
- get safeNotDeployed() {
39
- return this._safeNotDeployed;
40
- }
41
- get nearEOA() {
35
+ get mpcAddress() {
42
36
  return this.nearAdapter.address;
43
37
  }
44
- async getSafeBalance() {
45
- return await this.provider.getBalance(this.safeAddress);
38
+ async getBalance(chainId) {
39
+ const provider = Network.fromChainId(chainId).client;
40
+ return await provider.getBalance({ address: this.address });
41
+ }
42
+ bundlerForChainId(chainId) {
43
+ return new Erc4337Bundler(this.entryPointAddress, this.pimlicoKey, chainId);
46
44
  }
47
45
  async buildTransaction(args) {
48
- const { transactions, usePaymaster } = args;
49
- const gasFees = (await this.bundler.getGasPrice()).fast;
50
- // const gasFees = await this.provider.getFeeData();
46
+ const { transactions, usePaymaster, chainId } = args;
47
+ const bundler = this.bundlerForChainId(chainId);
48
+ const gasFees = (await bundler.getGasPrice()).fast;
51
49
  // Build Singular MetaTransaction for Multisend from transaction list.
52
50
  if (transactions.length === 0) {
53
51
  throw new Error("Empty transaction set!");
54
52
  }
55
53
  const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
56
- const rawUserOp = await this.safePack.buildUserOp(tx, this.safeAddress, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
57
- const paymasterData = await this.bundler.getPaymasterData(rawUserOp, usePaymaster, this.safeNotDeployed);
54
+ const safeNotDeployed = !(await this.safeDeployed(chainId));
55
+ const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, safeNotDeployed, this.safeSaltNonce);
56
+ const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed);
58
57
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
59
58
  return unsignedUserOp;
60
59
  }
61
60
  async signTransaction(safeOpHash) {
62
- const signature = await getNearSignature(this.nearAdapter, safeOpHash);
61
+ const signature = await this.nearAdapter.sign(safeOpHash);
63
62
  return packSignature(signature);
64
63
  }
65
64
  async opHash(userOp) {
66
65
  return this.safePack.getOpHash(userOp);
67
66
  }
68
67
  async encodeSignRequest(tx) {
69
- // TODO - This is sloppy and ignores ChainId!
70
68
  const unsignedUserOp = await this.buildTransaction({
69
+ chainId: tx.chainId,
71
70
  transactions: [
72
71
  {
73
72
  to: tx.to,
@@ -88,29 +87,40 @@ export class TransactionManager {
88
87
  evmMessage: JSON.stringify(unsignedUserOp),
89
88
  };
90
89
  }
91
- async executeTransaction(userOp) {
92
- const userOpHash = await this.bundler.sendUserOperation(userOp);
90
+ async executeTransaction(chainId, userOp) {
91
+ const bundler = this.bundlerForChainId(chainId);
92
+ const userOpHash = await bundler.sendUserOperation(userOp);
93
93
  console.log("UserOp Hash", userOpHash);
94
- const userOpReceipt = await this.bundler.getUserOpReceipt(userOpHash);
94
+ const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
95
95
  console.log("userOp Receipt", userOpReceipt);
96
96
  // Update safeNotDeployed after the first transaction
97
- this._safeNotDeployed =
98
- (await this.provider.getCode(this.safeAddress)) === "0x";
97
+ this.safeDeployed(chainId);
99
98
  return userOpReceipt;
100
99
  }
100
+ async safeDeployed(chainId) {
101
+ if (chainId in this.deployedChains) {
102
+ return true;
103
+ }
104
+ const provider = Network.fromChainId(chainId).client;
105
+ const deployed = (await provider.getCode({ address: this.address })) !== "0x";
106
+ if (deployed) {
107
+ this.deployedChains.add(chainId);
108
+ }
109
+ return deployed;
110
+ }
101
111
  addOwnerTx(address) {
102
112
  return {
103
- to: this.safeAddress,
113
+ to: this.address,
104
114
  value: "0",
105
115
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
106
116
  };
107
117
  }
108
- async safeSufficientlyFunded(transactions, gasCost) {
118
+ async safeSufficientlyFunded(chainId, transactions, gasCost) {
109
119
  const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
110
120
  if (txValue + gasCost === 0n) {
111
121
  return true;
112
122
  }
113
- const safeBalance = await this.getSafeBalance();
123
+ const safeBalance = await this.getBalance(chainId);
114
124
  return txValue + gasCost < safeBalance;
115
125
  }
116
126
  }
@@ -1,30 +1,30 @@
1
- import { ethers } from "ethers";
1
+ import { Address, Hex } from "viem";
2
2
  export interface UnsignedUserOperation {
3
- sender: ethers.AddressLike;
3
+ sender: Address;
4
4
  nonce: string;
5
- factory?: ethers.AddressLike;
6
- factoryData?: ethers.BytesLike;
7
- callData: string;
8
- maxPriorityFeePerGas: string;
9
- maxFeePerGas: string;
5
+ factory?: Address;
6
+ factoryData?: Hex;
7
+ callData: Hex;
8
+ maxPriorityFeePerGas: Hex;
9
+ maxFeePerGas: Hex;
10
10
  }
11
11
  /**
12
12
  * Supported Representation of UserOperation for EntryPoint v0.7
13
13
  */
14
14
  export interface UserOperation extends UnsignedUserOperation {
15
- verificationGasLimit: string;
16
- callGasLimit: string;
17
- preVerificationGas: string;
18
- signature?: string;
15
+ verificationGasLimit: Hex;
16
+ callGasLimit: Hex;
17
+ preVerificationGas: Hex;
18
+ signature?: Hex;
19
19
  }
20
20
  export interface PaymasterData {
21
- paymaster?: string;
22
- paymasterData?: string;
23
- paymasterVerificationGasLimit?: string;
24
- paymasterPostOpGasLimit?: string;
25
- verificationGasLimit: string;
26
- callGasLimit: string;
27
- preVerificationGas: string;
21
+ paymaster?: Address;
22
+ paymasterData?: Hex;
23
+ paymasterVerificationGasLimit?: Hex;
24
+ paymasterPostOpGasLimit?: Hex;
25
+ verificationGasLimit: Hex;
26
+ callGasLimit: Hex;
27
+ preVerificationGas: Hex;
28
28
  }
29
29
  export interface UserOptions {
30
30
  usePaymaster: boolean;
@@ -32,10 +32,7 @@ export interface UserOptions {
32
32
  mpcContractId: string;
33
33
  recoveryAddress?: string;
34
34
  }
35
- export type TStatus = "success" | "reverted";
36
- export type Address = ethers.AddressLike;
37
- export type Hex = `0x${string}`;
38
- export type Hash = `0x${string}`;
35
+ export type TxStatus = "success" | "reverted";
39
36
  interface Log {
40
37
  logIndex: string;
41
38
  transactionIndex: string;
@@ -49,19 +46,19 @@ interface Log {
49
46
  interface Receipt {
50
47
  transactionHash: Hex;
51
48
  transactionIndex: bigint;
52
- blockHash: Hash;
49
+ blockHash: Hex;
53
50
  blockNumber: bigint;
54
51
  from: Address;
55
- to: Address | null;
52
+ to?: Address;
56
53
  cumulativeGasUsed: bigint;
57
- status: TStatus;
54
+ status: TxStatus;
58
55
  gasUsed: bigint;
59
- contractAddress: Address | null;
56
+ contractAddress?: Address;
60
57
  logsBloom: Hex;
61
58
  effectiveGasPrice: bigint;
62
59
  }
63
60
  export type UserOperationReceipt = {
64
- userOpHash: Hash;
61
+ userOpHash: Hex;
65
62
  entryPoint: Address;
66
63
  sender: Address;
67
64
  nonce: bigint;
@@ -1,8 +1,10 @@
1
- import { ethers } from "ethers";
2
- import { PaymasterData } from "./types";
1
+ import { PaymasterData } from "./types.js";
3
2
  import { MetaTransaction } from "ethers-multisend";
4
- export declare const PLACEHOLDER_SIG: string;
5
- export declare const packGas: (hi: ethers.BigNumberish, lo: ethers.BigNumberish) => string;
6
- export declare function packSignature(signature: string, validFrom?: number, validTo?: number): string;
7
- export declare function packPaymasterData(data: PaymasterData): string;
3
+ import { Hex } from "viem";
4
+ export declare const PLACEHOLDER_SIG: `0x${string}`;
5
+ type IntLike = Hex | bigint | string | number;
6
+ export declare const packGas: (hi: IntLike, lo: IntLike) => string;
7
+ export declare function packSignature(signature: `0x${string}`, validFrom?: number, validTo?: number): Hex;
8
+ export declare function packPaymasterData(data: PaymasterData): Hex;
8
9
  export declare function containsValue(transactions: MetaTransaction[]): boolean;
10
+ export {};
package/dist/esm/util.js CHANGED
@@ -1,18 +1,18 @@
1
- import { ethers } from "ethers";
2
- export const PLACEHOLDER_SIG = ethers.solidityPacked(["uint48", "uint48"], [0, 0]);
3
- export const packGas = (hi, lo) => ethers.solidityPacked(["uint128", "uint128"], [hi, lo]);
1
+ import { concatHex, encodePacked, toHex } from "viem";
2
+ export const PLACEHOLDER_SIG = encodePacked(["uint48", "uint48"], [0, 0]);
3
+ export const packGas = (hi, lo) => encodePacked(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
4
4
  export function packSignature(signature, validFrom = 0, validTo = 0) {
5
- return ethers.solidityPacked(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]);
5
+ return encodePacked(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]);
6
6
  }
7
7
  export function packPaymasterData(data) {
8
- return data.paymaster
9
- ? ethers.hexlify(ethers.concat([
8
+ return (data.paymaster
9
+ ? concatHex([
10
10
  data.paymaster,
11
- ethers.toBeHex(data.paymasterVerificationGasLimit || "0x", 16),
12
- ethers.toBeHex(data.paymasterPostOpGasLimit || "0x", 16),
11
+ toHex(BigInt(data.paymasterVerificationGasLimit || 0n), { size: 16 }),
12
+ toHex(BigInt(data.paymasterPostOpGasLimit || 0n), { size: 16 }),
13
13
  data.paymasterData || "0x",
14
- ]))
15
- : "0x";
14
+ ])
15
+ : "0x");
16
16
  }
17
17
  export function containsValue(transactions) {
18
18
  return transactions.some((tx) => tx.value !== "0");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "near-safe",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.",
6
6
  "author": "bh2smith",
@@ -35,6 +35,7 @@
35
35
  "start": "yarn example",
36
36
  "example": "tsx examples/send-tx.ts",
37
37
  "lint": "eslint . --ignore-pattern dist/",
38
+ "test": "jest",
38
39
  "fmt": "prettier --write '{src,examples,tests}/**/*.{js,jsx,ts,tsx}'",
39
40
  "all": "yarn fmt && yarn lint && yarn build"
40
41
  },
@@ -44,16 +45,21 @@
44
45
  "ethers": "^6.13.1",
45
46
  "ethers-multisend": "^3.1.0",
46
47
  "near-api-js": "^5.0.0",
47
- "near-ca": "^0.5.2"
48
+ "near-ca": "^0.5.2",
49
+ "viem": "^2.16.5",
50
+ "yargs": "^17.7.2"
48
51
  },
49
52
  "devDependencies": {
53
+ "@types/jest": "^29.5.12",
50
54
  "@types/node": "^22.3.0",
51
55
  "@types/yargs": "^17.0.32",
52
56
  "@typescript-eslint/eslint-plugin": "^8.1.0",
53
57
  "@typescript-eslint/parser": "^8.1.0",
54
58
  "dotenv": "^16.4.5",
55
59
  "eslint": "^9.6.0",
60
+ "jest": "^29.7.0",
56
61
  "prettier": "^3.3.2",
62
+ "ts-jest": "^29.1.5",
57
63
  "tsx": "^4.16.0",
58
64
  "typescript": "^5.5.2",
59
65
  "yargs": "^17.7.2"
@@ -1,3 +0,0 @@
1
- import { ethers } from "ethers";
2
- import { NearEthAdapter } from "near-ca";
3
- export declare function getNearSignature(adapter: NearEthAdapter, hash: ethers.BytesLike): Promise<string>;
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNearSignature = getNearSignature;
4
- const ethers_1 = require("ethers");
5
- async function getNearSignature(adapter, hash) {
6
- const viemHash = typeof hash === "string" ? hash : hash;
7
- // MPC Contract produces two possible signatures.
8
- const signature = await adapter.sign(viemHash);
9
- if (ethers_1.ethers.recoverAddress(hash, signature).toLocaleLowerCase() ===
10
- adapter.address.toLocaleLowerCase()) {
11
- return signature;
12
- }
13
- throw new Error("Invalid signature!");
14
- }
@@ -1,3 +0,0 @@
1
- import { ethers } from "ethers";
2
- import { NearEthAdapter } from "near-ca";
3
- export declare function getNearSignature(adapter: NearEthAdapter, hash: ethers.BytesLike): Promise<string>;
@@ -1,11 +0,0 @@
1
- import { ethers } from "ethers";
2
- export async function getNearSignature(adapter, hash) {
3
- const viemHash = typeof hash === "string" ? hash : hash;
4
- // MPC Contract produces two possible signatures.
5
- const signature = await adapter.sign(viemHash);
6
- if (ethers.recoverAddress(hash, signature).toLocaleLowerCase() ===
7
- adapter.address.toLocaleLowerCase()) {
8
- return signature;
9
- }
10
- throw new Error("Invalid signature!");
11
- }