near-safe 0.0.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -33,7 +33,7 @@ NEAR_ACCOUNT_ID=
33
33
  NEAR_ACCOUNT_PRIVATE_KEY=
34
34
 
35
35
  # Head to https://www.pimlico.io/ for an API key
36
- ERC4337_BUNDLER_URL=
36
+ PIMLICO_KEY=
37
37
  ```
38
38
 
39
39
 
@@ -19,7 +19,15 @@ class ContractSuite {
19
19
  }
20
20
  static async init(provider) {
21
21
  const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
22
- const m4337Deployment = (fn) => getDeployment(fn, { provider, version: "0.3.0" });
22
+ 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
+ }
30
+ };
23
31
  // Need this first to get entryPoint address
24
32
  const m4337 = await m4337Deployment(safe_modules_deployments_1.getSafe4337ModuleDeployment);
25
33
  const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
@@ -29,6 +37,13 @@ class ContractSuite {
29
37
  m4337.SUPPORTED_ENTRYPOINT(),
30
38
  ]);
31
39
  const entryPoint = new ethers_1.ethers.Contract(supportedEntryPoint, ["function getNonce(address, uint192 key) view returns (uint256 nonce)"], provider);
40
+ console.log("Initialized ERC4337 & Safe Module Contracts:", {
41
+ singleton: await singleton.getAddress(),
42
+ proxyFactory: await proxyFactory.getAddress(),
43
+ m4337: await m4337.getAddress(),
44
+ moduleSetup: await moduleSetup.getAddress(),
45
+ entryPoint: await entryPoint.getAddress(),
46
+ });
32
47
  return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
33
48
  }
34
49
  async addressForSetup(setup, saltNonce) {
@@ -102,7 +117,7 @@ async function getDeployment(fn, { provider, version }) {
102
117
  const { chainId } = await provider.getNetwork();
103
118
  const deployment = fn({ version });
104
119
  if (!deployment || !deployment.networkAddresses[`${chainId}`]) {
105
- throw new Error(`Deployment not found for version ${version} and chainId ${chainId}`);
120
+ throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
106
121
  }
107
122
  return new ethers_1.ethers.Contract(deployment.networkAddresses[`${chainId}`], deployment.abi, provider);
108
123
  }
@@ -4,26 +4,30 @@ import { Erc4337Bundler } from "./lib/bundler";
4
4
  import { UserOperation, UserOperationReceipt } from "./types";
5
5
  import { MetaTransaction } from "ethers-multisend";
6
6
  import { ContractSuite } from "./lib/safe";
7
- import { Account } from "near-api-js";
8
7
  export declare class TransactionManager {
9
8
  readonly provider: ethers.JsonRpcProvider;
10
9
  readonly nearAdapter: NearEthAdapter;
11
10
  private safePack;
12
11
  private bundler;
13
12
  private setup;
14
- readonly safeAddress: string;
13
+ readonly address: string;
14
+ readonly chainId: number;
15
15
  private safeSaltNonce;
16
16
  private _safeNotDeployed;
17
- constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, safeAddress: string, safeSaltNonce: string, safeNotDeployed: boolean);
17
+ constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, chainId: number, safeAddress: string, safeSaltNonce: string, safeNotDeployed: boolean);
18
18
  static create(config: {
19
19
  ethRpc: string;
20
- erc4337BundlerUrl: string;
21
- nearAccount: Account;
22
- mpcContractId: string;
20
+ pimlicoKey: string;
21
+ nearAdapter: NearEthAdapter;
23
22
  safeSaltNonce?: string;
24
23
  }): Promise<TransactionManager>;
24
+ static fromChainId(args: {
25
+ chainId: number;
26
+ nearAdapter: NearEthAdapter;
27
+ pimlicoKey: string;
28
+ }): Promise<TransactionManager>;
25
29
  get safeNotDeployed(): boolean;
26
- get nearEOA(): `0x${string}`;
30
+ get mpcAddress(): `0x${string}`;
27
31
  getSafeBalance(): Promise<bigint>;
28
32
  buildTransaction(args: {
29
33
  transactions: MetaTransaction[];
@@ -9,40 +9,46 @@ const near_1 = require("./lib/near");
9
9
  const ethers_multisend_1 = require("ethers-multisend");
10
10
  const safe_1 = require("./lib/safe");
11
11
  class TransactionManager {
12
- constructor(provider, nearAdapter, safePack, bundler, setup, safeAddress, safeSaltNonce, safeNotDeployed) {
12
+ constructor(provider, nearAdapter, safePack, bundler, setup, chainId, safeAddress, safeSaltNonce, safeNotDeployed) {
13
13
  this.provider = provider;
14
14
  this.nearAdapter = nearAdapter;
15
15
  this.safePack = safePack;
16
16
  this.bundler = bundler;
17
17
  this.setup = setup;
18
- this.safeAddress = safeAddress;
18
+ this.chainId = chainId;
19
+ this.address = safeAddress;
19
20
  this.safeSaltNonce = safeSaltNonce;
20
21
  this._safeNotDeployed = safeNotDeployed;
21
22
  }
22
23
  static async create(config) {
24
+ const { nearAdapter, pimlicoKey } = config;
23
25
  const provider = new ethers_1.ethers.JsonRpcProvider(config.ethRpc);
24
- const [nearAdapter, safePack] = await Promise.all([
25
- near_ca_1.NearEthAdapter.fromConfig({
26
- mpcContract: new near_ca_1.MpcContract(config.nearAccount, config.mpcContractId),
27
- }),
28
- safe_1.ContractSuite.init(provider),
29
- ]);
26
+ const chainId = (await provider.getNetwork()).chainId;
27
+ const safePack = await safe_1.ContractSuite.init(provider);
30
28
  console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
31
- const bundler = new bundler_1.Erc4337Bundler(config.erc4337BundlerUrl, await safePack.entryPoint.getAddress());
29
+ const bundler = new bundler_1.Erc4337Bundler(`https://api.pimlico.io/v2/${chainId}/rpc?apikey=${pimlicoKey}`, await safePack.entryPoint.getAddress());
32
30
  const setup = await safePack.getSetup([nearAdapter.address]);
33
31
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
34
32
  const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
35
33
  console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
36
- return new TransactionManager(provider, nearAdapter, safePack, bundler, setup, safeAddress, config.safeSaltNonce || "0", 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
+ });
37
43
  }
38
44
  get safeNotDeployed() {
39
45
  return this._safeNotDeployed;
40
46
  }
41
- get nearEOA() {
47
+ get mpcAddress() {
42
48
  return this.nearAdapter.address;
43
49
  }
44
50
  async getSafeBalance() {
45
- return await this.provider.getBalance(this.safeAddress);
51
+ return await this.provider.getBalance(this.address);
46
52
  }
47
53
  async buildTransaction(args) {
48
54
  const { transactions, usePaymaster } = args;
@@ -53,7 +59,7 @@ class TransactionManager {
53
59
  throw new Error("Empty transaction set!");
54
60
  }
55
61
  const tx = transactions.length > 1 ? (0, ethers_multisend_1.encodeMulti)(transactions) : transactions[0];
56
- const rawUserOp = await this.safePack.buildUserOp(tx, this.safeAddress, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
62
+ const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
57
63
  const paymasterData = await this.bundler.getPaymasterData(rawUserOp, usePaymaster, this.safeNotDeployed);
58
64
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
59
65
  return unsignedUserOp;
@@ -67,6 +73,9 @@ class TransactionManager {
67
73
  }
68
74
  async encodeSignRequest(tx) {
69
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
+ }
70
79
  const unsignedUserOp = await this.buildTransaction({
71
80
  transactions: [
72
81
  {
@@ -95,12 +104,12 @@ class TransactionManager {
95
104
  console.log("userOp Receipt", userOpReceipt);
96
105
  // Update safeNotDeployed after the first transaction
97
106
  this._safeNotDeployed =
98
- (await this.provider.getCode(this.safeAddress)) === "0x";
107
+ (await this.provider.getCode(this.address)) === "0x";
99
108
  return userOpReceipt;
100
109
  }
101
110
  addOwnerTx(address) {
102
111
  return {
103
- to: this.safeAddress,
112
+ to: this.address,
104
113
  value: "0",
105
114
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
106
115
  };
@@ -22,7 +22,15 @@ export class ContractSuite {
22
22
  }
23
23
  static async init(provider) {
24
24
  const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
25
- const m4337Deployment = (fn) => getDeployment(fn, { provider, version: "0.3.0" });
25
+ 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
+ }
33
+ };
26
34
  // Need this first to get entryPoint address
27
35
  const m4337 = await m4337Deployment(getSafe4337ModuleDeployment);
28
36
  const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
@@ -32,6 +40,13 @@ export class ContractSuite {
32
40
  m4337.SUPPORTED_ENTRYPOINT(),
33
41
  ]);
34
42
  const entryPoint = new ethers.Contract(supportedEntryPoint, ["function getNonce(address, uint192 key) view returns (uint256 nonce)"], provider);
43
+ console.log("Initialized ERC4337 & Safe Module Contracts:", {
44
+ singleton: await singleton.getAddress(),
45
+ proxyFactory: await proxyFactory.getAddress(),
46
+ m4337: await m4337.getAddress(),
47
+ moduleSetup: await moduleSetup.getAddress(),
48
+ entryPoint: await entryPoint.getAddress(),
49
+ });
35
50
  return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
36
51
  }
37
52
  async addressForSetup(setup, saltNonce) {
@@ -104,7 +119,7 @@ async function getDeployment(fn, { provider, version }) {
104
119
  const { chainId } = await provider.getNetwork();
105
120
  const deployment = fn({ version });
106
121
  if (!deployment || !deployment.networkAddresses[`${chainId}`]) {
107
- throw new Error(`Deployment not found for version ${version} and chainId ${chainId}`);
122
+ throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
108
123
  }
109
124
  return new ethers.Contract(deployment.networkAddresses[`${chainId}`], deployment.abi, provider);
110
125
  }
@@ -4,26 +4,30 @@ import { Erc4337Bundler } from "./lib/bundler";
4
4
  import { UserOperation, UserOperationReceipt } from "./types";
5
5
  import { MetaTransaction } from "ethers-multisend";
6
6
  import { ContractSuite } from "./lib/safe";
7
- import { Account } from "near-api-js";
8
7
  export declare class TransactionManager {
9
8
  readonly provider: ethers.JsonRpcProvider;
10
9
  readonly nearAdapter: NearEthAdapter;
11
10
  private safePack;
12
11
  private bundler;
13
12
  private setup;
14
- readonly safeAddress: string;
13
+ readonly address: string;
14
+ readonly chainId: number;
15
15
  private safeSaltNonce;
16
16
  private _safeNotDeployed;
17
- constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, safeAddress: string, safeSaltNonce: string, safeNotDeployed: boolean);
17
+ constructor(provider: ethers.JsonRpcProvider, nearAdapter: NearEthAdapter, safePack: ContractSuite, bundler: Erc4337Bundler, setup: string, chainId: number, safeAddress: string, safeSaltNonce: string, safeNotDeployed: boolean);
18
18
  static create(config: {
19
19
  ethRpc: string;
20
- erc4337BundlerUrl: string;
21
- nearAccount: Account;
22
- mpcContractId: string;
20
+ pimlicoKey: string;
21
+ nearAdapter: NearEthAdapter;
23
22
  safeSaltNonce?: string;
24
23
  }): Promise<TransactionManager>;
24
+ static fromChainId(args: {
25
+ chainId: number;
26
+ nearAdapter: NearEthAdapter;
27
+ pimlicoKey: string;
28
+ }): Promise<TransactionManager>;
25
29
  get safeNotDeployed(): boolean;
26
- get nearEOA(): `0x${string}`;
30
+ get mpcAddress(): `0x${string}`;
27
31
  getSafeBalance(): Promise<bigint>;
28
32
  buildTransaction(args: {
29
33
  transactions: MetaTransaction[];
@@ -1,5 +1,5 @@
1
1
  import { ethers } from "ethers";
2
- import { NearEthAdapter, MpcContract } from "near-ca";
2
+ import { Network } from "near-ca";
3
3
  import { Erc4337Bundler } from "./lib/bundler";
4
4
  import { packSignature } from "./util";
5
5
  import { getNearSignature } from "./lib/near";
@@ -11,43 +11,50 @@ export class TransactionManager {
11
11
  safePack;
12
12
  bundler;
13
13
  setup;
14
- safeAddress;
14
+ address;
15
+ chainId;
15
16
  safeSaltNonce;
16
17
  _safeNotDeployed;
17
- constructor(provider, nearAdapter, safePack, bundler, setup, safeAddress, safeSaltNonce, safeNotDeployed) {
18
+ constructor(provider, nearAdapter, safePack, bundler, setup, chainId, safeAddress, safeSaltNonce, safeNotDeployed) {
18
19
  this.provider = provider;
19
20
  this.nearAdapter = nearAdapter;
20
21
  this.safePack = safePack;
21
22
  this.bundler = bundler;
22
23
  this.setup = setup;
23
- this.safeAddress = safeAddress;
24
+ this.chainId = chainId;
25
+ this.address = safeAddress;
24
26
  this.safeSaltNonce = safeSaltNonce;
25
27
  this._safeNotDeployed = safeNotDeployed;
26
28
  }
27
29
  static async create(config) {
30
+ const { nearAdapter, pimlicoKey } = config;
28
31
  const provider = new ethers.JsonRpcProvider(config.ethRpc);
29
- const [nearAdapter, safePack] = await Promise.all([
30
- NearEthAdapter.fromConfig({
31
- mpcContract: new MpcContract(config.nearAccount, config.mpcContractId),
32
- }),
33
- ContractSuite.init(provider),
34
- ]);
32
+ const chainId = (await provider.getNetwork()).chainId;
33
+ const safePack = await ContractSuite.init(provider);
35
34
  console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
36
- const bundler = new Erc4337Bundler(config.erc4337BundlerUrl, await safePack.entryPoint.getAddress());
35
+ const bundler = new Erc4337Bundler(`https://api.pimlico.io/v2/${chainId}/rpc?apikey=${pimlicoKey}`, await safePack.entryPoint.getAddress());
37
36
  const setup = await safePack.getSetup([nearAdapter.address]);
38
37
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
39
38
  const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
40
39
  console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
41
- return new TransactionManager(provider, nearAdapter, safePack, bundler, setup, safeAddress, config.safeSaltNonce || "0", 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
+ });
42
49
  }
43
50
  get safeNotDeployed() {
44
51
  return this._safeNotDeployed;
45
52
  }
46
- get nearEOA() {
53
+ get mpcAddress() {
47
54
  return this.nearAdapter.address;
48
55
  }
49
56
  async getSafeBalance() {
50
- return await this.provider.getBalance(this.safeAddress);
57
+ return await this.provider.getBalance(this.address);
51
58
  }
52
59
  async buildTransaction(args) {
53
60
  const { transactions, usePaymaster } = args;
@@ -58,7 +65,7 @@ export class TransactionManager {
58
65
  throw new Error("Empty transaction set!");
59
66
  }
60
67
  const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
61
- const rawUserOp = await this.safePack.buildUserOp(tx, this.safeAddress, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
68
+ const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
62
69
  const paymasterData = await this.bundler.getPaymasterData(rawUserOp, usePaymaster, this.safeNotDeployed);
63
70
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
64
71
  return unsignedUserOp;
@@ -72,6 +79,9 @@ export class TransactionManager {
72
79
  }
73
80
  async encodeSignRequest(tx) {
74
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
+ }
75
85
  const unsignedUserOp = await this.buildTransaction({
76
86
  transactions: [
77
87
  {
@@ -100,12 +110,12 @@ export class TransactionManager {
100
110
  console.log("userOp Receipt", userOpReceipt);
101
111
  // Update safeNotDeployed after the first transaction
102
112
  this._safeNotDeployed =
103
- (await this.provider.getCode(this.safeAddress)) === "0x";
113
+ (await this.provider.getCode(this.address)) === "0x";
104
114
  return userOpReceipt;
105
115
  }
106
116
  addOwnerTx(address) {
107
117
  return {
108
- to: this.safeAddress,
118
+ to: this.address,
109
119
  value: "0",
110
120
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
111
121
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "near-safe",
3
- "version": "0.0.5",
3
+ "version": "0.1.1",
4
4
  "license": "MIT",
5
5
  "description": "An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.",
6
6
  "author": "bh2smith",