near-safe 0.1.0 → 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
  }
@@ -10,18 +10,24 @@ export declare class TransactionManager {
10
10
  private safePack;
11
11
  private bundler;
12
12
  private setup;
13
- readonly safeAddress: string;
13
+ readonly address: string;
14
+ readonly chainId: number;
14
15
  private safeSaltNonce;
15
16
  private _safeNotDeployed;
16
- 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);
17
18
  static create(config: {
18
19
  ethRpc: string;
19
- erc4337BundlerUrl: string;
20
+ pimlicoKey: string;
20
21
  nearAdapter: NearEthAdapter;
21
22
  safeSaltNonce?: string;
22
23
  }): Promise<TransactionManager>;
24
+ static fromChainId(args: {
25
+ chainId: number;
26
+ nearAdapter: NearEthAdapter;
27
+ pimlicoKey: string;
28
+ }): Promise<TransactionManager>;
23
29
  get safeNotDeployed(): boolean;
24
- get nearEOA(): `0x${string}`;
30
+ get mpcAddress(): `0x${string}`;
25
31
  getSafeBalance(): Promise<bigint>;
26
32
  buildTransaction(args: {
27
33
  transactions: MetaTransaction[];
@@ -2,42 +2,53 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TransactionManager = void 0;
4
4
  const ethers_1 = require("ethers");
5
+ const near_ca_1 = require("near-ca");
5
6
  const bundler_1 = require("./lib/bundler");
6
7
  const util_1 = require("./util");
7
8
  const near_1 = require("./lib/near");
8
9
  const ethers_multisend_1 = require("ethers-multisend");
9
10
  const safe_1 = require("./lib/safe");
10
11
  class TransactionManager {
11
- constructor(provider, nearAdapter, safePack, bundler, setup, safeAddress, safeSaltNonce, safeNotDeployed) {
12
+ constructor(provider, nearAdapter, safePack, bundler, setup, chainId, safeAddress, safeSaltNonce, safeNotDeployed) {
12
13
  this.provider = provider;
13
14
  this.nearAdapter = nearAdapter;
14
15
  this.safePack = safePack;
15
16
  this.bundler = bundler;
16
17
  this.setup = setup;
17
- this.safeAddress = safeAddress;
18
+ this.chainId = chainId;
19
+ this.address = safeAddress;
18
20
  this.safeSaltNonce = safeSaltNonce;
19
21
  this._safeNotDeployed = safeNotDeployed;
20
22
  }
21
23
  static async create(config) {
22
- const adapter = config.nearAdapter;
24
+ const { nearAdapter, pimlicoKey } = config;
23
25
  const provider = new ethers_1.ethers.JsonRpcProvider(config.ethRpc);
26
+ const chainId = (await provider.getNetwork()).chainId;
24
27
  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]);
28
+ 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
+ const setup = await safePack.getSetup([nearAdapter.address]);
28
31
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
29
32
  const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
30
33
  console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
31
- return new TransactionManager(provider, adapter, 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
+ });
32
43
  }
33
44
  get safeNotDeployed() {
34
45
  return this._safeNotDeployed;
35
46
  }
36
- get nearEOA() {
47
+ get mpcAddress() {
37
48
  return this.nearAdapter.address;
38
49
  }
39
50
  async getSafeBalance() {
40
- return await this.provider.getBalance(this.safeAddress);
51
+ return await this.provider.getBalance(this.address);
41
52
  }
42
53
  async buildTransaction(args) {
43
54
  const { transactions, usePaymaster } = args;
@@ -48,7 +59,7 @@ class TransactionManager {
48
59
  throw new Error("Empty transaction set!");
49
60
  }
50
61
  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);
62
+ const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
52
63
  const paymasterData = await this.bundler.getPaymasterData(rawUserOp, usePaymaster, this.safeNotDeployed);
53
64
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
54
65
  return unsignedUserOp;
@@ -62,6 +73,9 @@ class TransactionManager {
62
73
  }
63
74
  async encodeSignRequest(tx) {
64
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
+ }
65
79
  const unsignedUserOp = await this.buildTransaction({
66
80
  transactions: [
67
81
  {
@@ -90,12 +104,12 @@ class TransactionManager {
90
104
  console.log("userOp Receipt", userOpReceipt);
91
105
  // Update safeNotDeployed after the first transaction
92
106
  this._safeNotDeployed =
93
- (await this.provider.getCode(this.safeAddress)) === "0x";
107
+ (await this.provider.getCode(this.address)) === "0x";
94
108
  return userOpReceipt;
95
109
  }
96
110
  addOwnerTx(address) {
97
111
  return {
98
- to: this.safeAddress,
112
+ to: this.address,
99
113
  value: "0",
100
114
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
101
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
  }
@@ -10,18 +10,24 @@ export declare class TransactionManager {
10
10
  private safePack;
11
11
  private bundler;
12
12
  private setup;
13
- readonly safeAddress: string;
13
+ readonly address: string;
14
+ readonly chainId: number;
14
15
  private safeSaltNonce;
15
16
  private _safeNotDeployed;
16
- 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);
17
18
  static create(config: {
18
19
  ethRpc: string;
19
- erc4337BundlerUrl: string;
20
+ pimlicoKey: string;
20
21
  nearAdapter: NearEthAdapter;
21
22
  safeSaltNonce?: string;
22
23
  }): Promise<TransactionManager>;
24
+ static fromChainId(args: {
25
+ chainId: number;
26
+ nearAdapter: NearEthAdapter;
27
+ pimlicoKey: string;
28
+ }): Promise<TransactionManager>;
23
29
  get safeNotDeployed(): boolean;
24
- get nearEOA(): `0x${string}`;
30
+ get mpcAddress(): `0x${string}`;
25
31
  getSafeBalance(): Promise<bigint>;
26
32
  buildTransaction(args: {
27
33
  transactions: MetaTransaction[];
@@ -1,4 +1,5 @@
1
1
  import { ethers } from "ethers";
2
+ import { Network } from "near-ca";
2
3
  import { Erc4337Bundler } from "./lib/bundler";
3
4
  import { packSignature } from "./util";
4
5
  import { getNearSignature } from "./lib/near";
@@ -10,39 +11,50 @@ export class TransactionManager {
10
11
  safePack;
11
12
  bundler;
12
13
  setup;
13
- safeAddress;
14
+ address;
15
+ chainId;
14
16
  safeSaltNonce;
15
17
  _safeNotDeployed;
16
- constructor(provider, nearAdapter, safePack, bundler, setup, safeAddress, safeSaltNonce, safeNotDeployed) {
18
+ constructor(provider, nearAdapter, safePack, bundler, setup, chainId, safeAddress, safeSaltNonce, safeNotDeployed) {
17
19
  this.provider = provider;
18
20
  this.nearAdapter = nearAdapter;
19
21
  this.safePack = safePack;
20
22
  this.bundler = bundler;
21
23
  this.setup = setup;
22
- this.safeAddress = safeAddress;
24
+ this.chainId = chainId;
25
+ this.address = safeAddress;
23
26
  this.safeSaltNonce = safeSaltNonce;
24
27
  this._safeNotDeployed = safeNotDeployed;
25
28
  }
26
29
  static async create(config) {
27
- const adapter = config.nearAdapter;
30
+ const { nearAdapter, pimlicoKey } = config;
28
31
  const provider = new ethers.JsonRpcProvider(config.ethRpc);
32
+ const chainId = (await provider.getNetwork()).chainId;
29
33
  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]);
34
+ 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
+ const setup = await safePack.getSetup([nearAdapter.address]);
33
37
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
34
38
  const safeNotDeployed = (await provider.getCode(safeAddress)) === "0x";
35
39
  console.log(`Safe Address: ${safeAddress} - deployed? ${!safeNotDeployed}`);
36
- return new TransactionManager(provider, adapter, 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
+ });
37
49
  }
38
50
  get safeNotDeployed() {
39
51
  return this._safeNotDeployed;
40
52
  }
41
- get nearEOA() {
53
+ get mpcAddress() {
42
54
  return this.nearAdapter.address;
43
55
  }
44
56
  async getSafeBalance() {
45
- return await this.provider.getBalance(this.safeAddress);
57
+ return await this.provider.getBalance(this.address);
46
58
  }
47
59
  async buildTransaction(args) {
48
60
  const { transactions, usePaymaster } = args;
@@ -53,7 +65,7 @@ export class TransactionManager {
53
65
  throw new Error("Empty transaction set!");
54
66
  }
55
67
  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);
68
+ const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, this.safeNotDeployed, this.safeSaltNonce);
57
69
  const paymasterData = await this.bundler.getPaymasterData(rawUserOp, usePaymaster, this.safeNotDeployed);
58
70
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
59
71
  return unsignedUserOp;
@@ -67,6 +79,9 @@ export class TransactionManager {
67
79
  }
68
80
  async encodeSignRequest(tx) {
69
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
+ }
70
85
  const unsignedUserOp = await this.buildTransaction({
71
86
  transactions: [
72
87
  {
@@ -95,12 +110,12 @@ export class TransactionManager {
95
110
  console.log("userOp Receipt", userOpReceipt);
96
111
  // Update safeNotDeployed after the first transaction
97
112
  this._safeNotDeployed =
98
- (await this.provider.getCode(this.safeAddress)) === "0x";
113
+ (await this.provider.getCode(this.address)) === "0x";
99
114
  return userOpReceipt;
100
115
  }
101
116
  addOwnerTx(address) {
102
117
  return {
103
- to: this.safeAddress,
118
+ to: this.address,
104
119
  value: "0",
105
120
  data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
106
121
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "near-safe",
3
- "version": "0.1.0",
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",