near-safe 0.1.0 → 0.1.1

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
 
@@ -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",