near-safe 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,16 +17,12 @@ 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
24
  const m4337Deployment = async (fn) => {
23
- try {
24
- return await getDeployment(fn, { provider, version: "0.3.0" });
25
- }
26
- catch (error) {
27
- console.warn(error.message, "using v0.2.0");
28
- return getDeployment(fn, { provider, version: "0.2.0" });
29
- }
25
+ return getDeployment(fn, { provider, version: "0.3.0" });
30
26
  };
31
27
  // Need this first to get entryPoint address
32
28
  const m4337 = await m4337Deployment(safe_modules_deployments_1.getSafe4337ModuleDeployment);
@@ -73,16 +69,15 @@ class ContractSuite {
73
69
  ]);
74
70
  return setup;
75
71
  }
76
- async getOpHash(unsignedUserOp
77
- // paymasterData: PaymasterData
78
- ) {
72
+ async getOpHash(unsignedUserOp) {
73
+ const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
79
74
  return this.m4337.getOperationHash({
80
75
  ...unsignedUserOp,
81
- initCode: unsignedUserOp.factory
82
- ? ethers_1.ethers.solidityPacked(["address", "bytes"], [unsignedUserOp.factory, unsignedUserOp.factoryData])
76
+ initCode: factory
77
+ ? ethers_1.ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
83
78
  : "0x",
84
- accountGasLimits: (0, util_1.packGas)(unsignedUserOp.verificationGasLimit, unsignedUserOp.callGasLimit),
85
- 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),
86
81
  paymasterAndData: (0, util_1.packPaymasterData)(unsignedUserOp),
87
82
  signature: util_1.PLACEHOLDER_SIG,
88
83
  });
@@ -116,8 +111,17 @@ exports.ContractSuite = ContractSuite;
116
111
  async function getDeployment(fn, { provider, version }) {
117
112
  const { chainId } = await provider.getNetwork();
118
113
  const deployment = fn({ version });
119
- if (!deployment || !deployment.networkAddresses[`${chainId}`]) {
114
+ if (!deployment) {
120
115
  throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
121
116
  }
122
- return new ethers_1.ethers.Contract(deployment.networkAddresses[`${chainId}`], deployment.abi, provider);
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"];
125
+ }
126
+ return new ethers_1.ethers.Contract(address, deployment.abi, provider);
123
127
  }
@@ -1,42 +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 address: string;
14
- readonly chainId: number;
13
+ private pimlicoKey;
15
14
  private safeSaltNonce;
16
- private _safeNotDeployed;
17
- constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, chainId: number, 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);
18
17
  static create(config: {
19
- ethRpc: string;
20
18
  pimlicoKey: string;
21
19
  nearAdapter: NearEthAdapter;
22
20
  safeSaltNonce?: string;
23
21
  }): Promise<TransactionManager>;
24
- static fromChainId(args: {
25
- chainId: number;
26
- nearAdapter: NearEthAdapter;
27
- pimlicoKey: string;
28
- }): Promise<TransactionManager>;
29
- get safeNotDeployed(): boolean;
30
- get mpcAddress(): `0x${string}`;
31
- getSafeBalance(): Promise<bigint>;
22
+ get mpcAddress(): Address;
23
+ getBalance(chainId: number): Promise<bigint>;
24
+ bundlerForChainId(chainId: number): Erc4337Bundler;
32
25
  buildTransaction(args: {
26
+ chainId: number;
33
27
  transactions: MetaTransaction[];
34
28
  usePaymaster: boolean;
35
29
  }): Promise<UserOperation>;
36
- signTransaction(safeOpHash: string): Promise<string>;
37
- opHash(userOp: UserOperation): Promise<string>;
30
+ signTransaction(safeOpHash: Hex): Promise<Hex>;
31
+ opHash(userOp: UserOperation): Promise<Hash>;
38
32
  encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
39
- executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
33
+ executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
34
+ safeDeployed(chainId: number): Promise<boolean>;
40
35
  addOwnerTx(address: string): MetaTransaction;
41
- safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
36
+ safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
42
37
  }
@@ -1,82 +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");
5
4
  const near_ca_1 = require("near-ca");
6
5
  const bundler_1 = require("./lib/bundler");
7
6
  const util_1 = require("./util");
8
- const near_1 = require("./lib/near");
9
7
  const ethers_multisend_1 = require("ethers-multisend");
10
8
  const safe_1 = require("./lib/safe");
11
9
  class TransactionManager {
12
- constructor(provider, nearAdapter, safePack, bundler, setup, chainId, safeAddress, safeSaltNonce, safeNotDeployed) {
13
- this.provider = provider;
10
+ constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
14
11
  this.nearAdapter = nearAdapter;
15
12
  this.safePack = safePack;
16
- this.bundler = bundler;
13
+ this.pimlicoKey = pimlicoKey;
14
+ this.entryPointAddress = entryPointAddress;
17
15
  this.setup = setup;
18
- this.chainId = chainId;
19
16
  this.address = safeAddress;
20
17
  this.safeSaltNonce = safeSaltNonce;
21
- this._safeNotDeployed = safeNotDeployed;
18
+ this.deployedChains = new Set();
22
19
  }
23
20
  static async create(config) {
24
21
  const { nearAdapter, pimlicoKey } = config;
25
- const provider = new ethers_1.ethers.JsonRpcProvider(config.ethRpc);
26
- const chainId = (await provider.getNetwork()).chainId;
27
- const safePack = await safe_1.ContractSuite.init(provider);
22
+ const safePack = await safe_1.ContractSuite.init();
28
23
  console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
29
- const bundler = new bundler_1.Erc4337Bundler(`https://api.pimlico.io/v2/${chainId}/rpc?apikey=${pimlicoKey}`, await safePack.entryPoint.getAddress());
30
24
  const setup = await safePack.getSetup([nearAdapter.address]);
31
25
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
32
- const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
33
- console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
34
- return new TransactionManager(provider, nearAdapter, safePack, bundler, setup, parseInt(chainId.toString()), safeAddress, config.safeSaltNonce || "0", safeNotDeployed);
35
- }
36
- static async fromChainId(args) {
37
- const { pimlicoKey, nearAdapter } = args;
38
- return TransactionManager.create({
39
- ethRpc: near_ca_1.Network.fromChainId(args.chainId).rpcUrl,
40
- pimlicoKey,
41
- nearAdapter,
42
- });
43
- }
44
- get safeNotDeployed() {
45
- return this._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");
46
29
  }
47
30
  get mpcAddress() {
48
31
  return this.nearAdapter.address;
49
32
  }
50
- async getSafeBalance() {
51
- return await this.provider.getBalance(this.address);
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);
52
39
  }
53
40
  async buildTransaction(args) {
54
- const { transactions, usePaymaster } = args;
55
- const gasFees = (await this.bundler.getGasPrice()).fast;
56
- // 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;
57
44
  // Build Singular MetaTransaction for Multisend from transaction list.
58
45
  if (transactions.length === 0) {
59
46
  throw new Error("Empty transaction set!");
60
47
  }
61
48
  const tx = transactions.length > 1 ? (0, ethers_multisend_1.encodeMulti)(transactions) : transactions[0];
62
- const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
63
- 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);
64
52
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
65
53
  return unsignedUserOp;
66
54
  }
67
55
  async signTransaction(safeOpHash) {
68
- const signature = await (0, near_1.getNearSignature)(this.nearAdapter, safeOpHash);
56
+ const signature = await this.nearAdapter.sign(safeOpHash);
69
57
  return (0, util_1.packSignature)(signature);
70
58
  }
71
59
  async opHash(userOp) {
72
60
  return this.safePack.getOpHash(userOp);
73
61
  }
74
62
  async encodeSignRequest(tx) {
75
- // TODO - This is sloppy and ignores ChainId!
76
- if (tx.chainId !== this.chainId) {
77
- throw new Error(`Transaciton request for invalid ChainId ${tx.chainId} != ${this.chainId}`);
78
- }
79
63
  const unsignedUserOp = await this.buildTransaction({
64
+ chainId: tx.chainId,
80
65
  transactions: [
81
66
  {
82
67
  to: tx.to,
@@ -97,16 +82,27 @@ class TransactionManager {
97
82
  evmMessage: JSON.stringify(unsignedUserOp),
98
83
  };
99
84
  }
100
- async executeTransaction(userOp) {
101
- 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);
102
88
  console.log("UserOp Hash", userOpHash);
103
- const userOpReceipt = await this.bundler.getUserOpReceipt(userOpHash);
89
+ const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
104
90
  console.log("userOp Receipt", userOpReceipt);
105
91
  // Update safeNotDeployed after the first transaction
106
- this._safeNotDeployed =
107
- (await this.provider.getCode(this.address)) === "0x";
92
+ this.safeDeployed(chainId);
108
93
  return userOpReceipt;
109
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
+ }
110
106
  addOwnerTx(address) {
111
107
  return {
112
108
  to: this.address,
@@ -114,12 +110,12 @@ class TransactionManager {
114
110
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
115
111
  };
116
112
  }
117
- async safeSufficientlyFunded(transactions, gasCost) {
113
+ async safeSufficientlyFunded(chainId, transactions, gasCost) {
118
114
  const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
119
115
  if (txValue + gasCost === 0n) {
120
116
  return true;
121
117
  }
122
- const safeBalance = await this.getSafeBalance();
118
+ const safeBalance = await this.getBalance(chainId);
123
119
  return txValue + gasCost < safeBalance;
124
120
  }
125
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,16 +20,12 @@ 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
27
  const m4337Deployment = async (fn) => {
26
- try {
27
- return await getDeployment(fn, { provider, version: "0.3.0" });
28
- }
29
- catch (error) {
30
- console.warn(error.message, "using v0.2.0");
31
- return getDeployment(fn, { provider, version: "0.2.0" });
32
- }
28
+ return getDeployment(fn, { provider, version: "0.3.0" });
33
29
  };
34
30
  // Need this first to get entryPoint address
35
31
  const m4337 = await m4337Deployment(getSafe4337ModuleDeployment);
@@ -76,16 +72,15 @@ export class ContractSuite {
76
72
  ]);
77
73
  return setup;
78
74
  }
79
- async getOpHash(unsignedUserOp
80
- // paymasterData: PaymasterData
81
- ) {
75
+ async getOpHash(unsignedUserOp) {
76
+ const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
82
77
  return this.m4337.getOperationHash({
83
78
  ...unsignedUserOp,
84
- initCode: unsignedUserOp.factory
85
- ? ethers.solidityPacked(["address", "bytes"], [unsignedUserOp.factory, unsignedUserOp.factoryData])
79
+ initCode: factory
80
+ ? ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
86
81
  : "0x",
87
- accountGasLimits: packGas(unsignedUserOp.verificationGasLimit, unsignedUserOp.callGasLimit),
88
- gasFees: packGas(unsignedUserOp.maxPriorityFeePerGas, unsignedUserOp.maxFeePerGas),
82
+ accountGasLimits: packGas(verificationGasLimit, callGasLimit),
83
+ gasFees: packGas(maxPriorityFeePerGas, maxFeePerGas),
89
84
  paymasterAndData: packPaymasterData(unsignedUserOp),
90
85
  signature: PLACEHOLDER_SIG,
91
86
  });
@@ -118,8 +113,17 @@ export class ContractSuite {
118
113
  async function getDeployment(fn, { provider, version }) {
119
114
  const { chainId } = await provider.getNetwork();
120
115
  const deployment = fn({ version });
121
- if (!deployment || !deployment.networkAddresses[`${chainId}`]) {
116
+ if (!deployment) {
122
117
  throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
123
118
  }
124
- return new ethers.Contract(deployment.networkAddresses[`${chainId}`], deployment.abi, provider);
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"];
127
+ }
128
+ return new ethers.Contract(address, deployment.abi, provider);
125
129
  }
@@ -1,42 +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 address: string;
14
- readonly chainId: number;
13
+ private pimlicoKey;
15
14
  private safeSaltNonce;
16
- private _safeNotDeployed;
17
- constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, chainId: number, 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);
18
17
  static create(config: {
19
- ethRpc: string;
20
18
  pimlicoKey: string;
21
19
  nearAdapter: NearEthAdapter;
22
20
  safeSaltNonce?: string;
23
21
  }): Promise<TransactionManager>;
24
- static fromChainId(args: {
25
- chainId: number;
26
- nearAdapter: NearEthAdapter;
27
- pimlicoKey: string;
28
- }): Promise<TransactionManager>;
29
- get safeNotDeployed(): boolean;
30
- get mpcAddress(): `0x${string}`;
31
- getSafeBalance(): Promise<bigint>;
22
+ get mpcAddress(): Address;
23
+ getBalance(chainId: number): Promise<bigint>;
24
+ bundlerForChainId(chainId: number): Erc4337Bundler;
32
25
  buildTransaction(args: {
26
+ chainId: number;
33
27
  transactions: MetaTransaction[];
34
28
  usePaymaster: boolean;
35
29
  }): Promise<UserOperation>;
36
- signTransaction(safeOpHash: string): Promise<string>;
37
- opHash(userOp: UserOperation): Promise<string>;
30
+ signTransaction(safeOpHash: Hex): Promise<Hex>;
31
+ opHash(userOp: UserOperation): Promise<Hash>;
38
32
  encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
39
- executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
33
+ executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
34
+ safeDeployed(chainId: number): Promise<boolean>;
40
35
  addOwnerTx(address: string): MetaTransaction;
41
- safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
36
+ safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
42
37
  }
@@ -1,88 +1,72 @@
1
- import { ethers } from "ethers";
2
1
  import { Network } from "near-ca";
3
2
  import { Erc4337Bundler } from "./lib/bundler";
4
3
  import { packSignature } from "./util";
5
- import { getNearSignature } from "./lib/near";
6
4
  import { encodeMulti } from "ethers-multisend";
7
5
  import { ContractSuite } from "./lib/safe";
8
6
  export class TransactionManager {
9
- provider;
10
7
  nearAdapter;
8
+ address;
9
+ entryPointAddress;
11
10
  safePack;
12
- bundler;
13
11
  setup;
14
- address;
15
- chainId;
12
+ pimlicoKey;
16
13
  safeSaltNonce;
17
- _safeNotDeployed;
18
- constructor(provider, nearAdapter, safePack, bundler, setup, chainId, safeAddress, safeSaltNonce, safeNotDeployed) {
19
- this.provider = provider;
14
+ deployedChains;
15
+ constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
20
16
  this.nearAdapter = nearAdapter;
21
17
  this.safePack = safePack;
22
- this.bundler = bundler;
18
+ this.pimlicoKey = pimlicoKey;
19
+ this.entryPointAddress = entryPointAddress;
23
20
  this.setup = setup;
24
- this.chainId = chainId;
25
21
  this.address = safeAddress;
26
22
  this.safeSaltNonce = safeSaltNonce;
27
- this._safeNotDeployed = safeNotDeployed;
23
+ this.deployedChains = new Set();
28
24
  }
29
25
  static async create(config) {
30
26
  const { nearAdapter, pimlicoKey } = config;
31
- const provider = new ethers.JsonRpcProvider(config.ethRpc);
32
- const chainId = (await provider.getNetwork()).chainId;
33
- const safePack = await ContractSuite.init(provider);
27
+ const safePack = await ContractSuite.init();
34
28
  console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
35
- const bundler = new Erc4337Bundler(`https://api.pimlico.io/v2/${chainId}/rpc?apikey=${pimlicoKey}`, await safePack.entryPoint.getAddress());
36
29
  const setup = await safePack.getSetup([nearAdapter.address]);
37
30
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
38
- const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
39
- console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
40
- return new TransactionManager(provider, nearAdapter, safePack, bundler, setup, parseInt(chainId.toString()), safeAddress, config.safeSaltNonce || "0", safeNotDeployed);
41
- }
42
- static async fromChainId(args) {
43
- const { pimlicoKey, nearAdapter } = args;
44
- return TransactionManager.create({
45
- ethRpc: Network.fromChainId(args.chainId).rpcUrl,
46
- pimlicoKey,
47
- nearAdapter,
48
- });
49
- }
50
- get safeNotDeployed() {
51
- return this._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");
52
34
  }
53
35
  get mpcAddress() {
54
36
  return this.nearAdapter.address;
55
37
  }
56
- async getSafeBalance() {
57
- return await this.provider.getBalance(this.address);
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);
58
44
  }
59
45
  async buildTransaction(args) {
60
- const { transactions, usePaymaster } = args;
61
- const gasFees = (await this.bundler.getGasPrice()).fast;
62
- // 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;
63
49
  // Build Singular MetaTransaction for Multisend from transaction list.
64
50
  if (transactions.length === 0) {
65
51
  throw new Error("Empty transaction set!");
66
52
  }
67
53
  const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
68
- const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
69
- 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);
70
57
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
71
58
  return unsignedUserOp;
72
59
  }
73
60
  async signTransaction(safeOpHash) {
74
- const signature = await getNearSignature(this.nearAdapter, safeOpHash);
61
+ const signature = await this.nearAdapter.sign(safeOpHash);
75
62
  return packSignature(signature);
76
63
  }
77
64
  async opHash(userOp) {
78
65
  return this.safePack.getOpHash(userOp);
79
66
  }
80
67
  async encodeSignRequest(tx) {
81
- // TODO - This is sloppy and ignores ChainId!
82
- if (tx.chainId !== this.chainId) {
83
- throw new Error(`Transaciton request for invalid ChainId ${tx.chainId} != ${this.chainId}`);
84
- }
85
68
  const unsignedUserOp = await this.buildTransaction({
69
+ chainId: tx.chainId,
86
70
  transactions: [
87
71
  {
88
72
  to: tx.to,
@@ -103,16 +87,27 @@ export class TransactionManager {
103
87
  evmMessage: JSON.stringify(unsignedUserOp),
104
88
  };
105
89
  }
106
- async executeTransaction(userOp) {
107
- 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);
108
93
  console.log("UserOp Hash", userOpHash);
109
- const userOpReceipt = await this.bundler.getUserOpReceipt(userOpHash);
94
+ const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
110
95
  console.log("userOp Receipt", userOpReceipt);
111
96
  // Update safeNotDeployed after the first transaction
112
- this._safeNotDeployed =
113
- (await this.provider.getCode(this.address)) === "0x";
97
+ this.safeDeployed(chainId);
114
98
  return userOpReceipt;
115
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
+ }
116
111
  addOwnerTx(address) {
117
112
  return {
118
113
  to: this.address,
@@ -120,12 +115,12 @@ export class TransactionManager {
120
115
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
121
116
  };
122
117
  }
123
- async safeSufficientlyFunded(transactions, gasCost) {
118
+ async safeSufficientlyFunded(chainId, transactions, gasCost) {
124
119
  const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
125
120
  if (txValue + gasCost === 0n) {
126
121
  return true;
127
122
  }
128
- const safeBalance = await this.getSafeBalance();
123
+ const safeBalance = await this.getBalance(chainId);
129
124
  return txValue + gasCost < safeBalance;
130
125
  }
131
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.1",
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
- }