near-safe 0.1.0 → 0.2.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 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
- }