near-safe 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- export * from "./tx-manager";
1
+ export * from "./near-safe";
2
2
  export * from "./types";
3
3
  export * from "./util";
4
4
  export { Network, BaseTx, SignRequestData, populateTx } from "near-ca";
package/dist/cjs/index.js CHANGED
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.populateTx = exports.Network = void 0;
18
- __exportStar(require("./tx-manager"), exports);
18
+ __exportStar(require("./near-safe"), exports);
19
19
  __exportStar(require("./types"), exports);
20
20
  __exportStar(require("./util"), exports);
21
21
  var near_ca_1 = require("near-ca");
@@ -34,4 +34,5 @@ export declare class Erc4337Bundler {
34
34
  getUserOpReceipt(userOpHash: Hash): Promise<UserOperationReceipt>;
35
35
  private _getUserOpReceiptInner;
36
36
  }
37
+ export declare function stripApiKey(error: unknown): string;
37
38
  export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Erc4337Bundler = void 0;
4
+ exports.stripApiKey = stripApiKey;
4
5
  const viem_1 = require("viem");
5
6
  const util_1 = require("../util");
6
7
  function bundlerUrl(chainId, apikey) {
@@ -65,18 +66,19 @@ async function handleRequest(clientMethod) {
65
66
  return await clientMethod();
66
67
  }
67
68
  catch (error) {
69
+ const message = stripApiKey(error);
68
70
  if (error instanceof viem_1.HttpRequestError) {
69
71
  if (error.status === 401) {
70
- throw new Error("Unauthorized request. Please check your API key.");
72
+ throw new Error("Unauthorized request. Please check your Pimlico API key.");
71
73
  }
72
74
  else {
73
- console.error(`Request failed with status ${error.status}: ${error.message}`);
75
+ throw new Error(`Pimlico: ${message}`);
74
76
  }
75
77
  }
76
78
  else if (error instanceof viem_1.RpcError) {
77
- throw new Error(`Failed to send user op with: ${error.message}`);
79
+ throw new Error(`Failed to send user op with: ${message}`);
78
80
  }
79
- throw new Error(`Unexpected error ${error instanceof Error ? error.message : String(error)}`);
81
+ throw new Error(`Bundler Request: ${message}`);
80
82
  }
81
83
  }
82
84
  // TODO(bh2smith) Should probably get reasonable estimates here:
@@ -87,3 +89,12 @@ const defaultPaymasterData = (safeNotDeployed) => {
87
89
  preVerificationGas: (0, viem_1.toHex)(100000),
88
90
  };
89
91
  };
92
+ function stripApiKey(error) {
93
+ const message = error instanceof Error ? error.message : String(error);
94
+ return message.replace(/(apikey=)[^\s&]+/, "$1***");
95
+ // Could also do this with slicing.
96
+ // const keyStart = message.indexOf("apikey=") + 7;
97
+ // // If no apikey in the message, return it as is.
98
+ // if (keyStart === -1) return message;
99
+ // return `${message.slice(0, keyStart)}***${message.slice(keyStart + 36)}`;
100
+ }
@@ -1,21 +1,16 @@
1
- import { Address, Hash, Hex, ParseAbi, PublicClient } from "viem";
2
- import { GasPrice, MetaTransaction, UnsignedUserOperation, UserOperation } from "../types";
3
- interface DeploymentData {
4
- abi: unknown[] | ParseAbi<readonly string[]>;
5
- address: `0x${string}`;
6
- }
1
+ import { Address, Hash, Hex, PublicClient } from "viem";
2
+ import { Deployment, GasPrice, MetaTransaction, UnsignedUserOperation, UserOperation } from "../types";
7
3
  /**
8
4
  * All contracts used in account creation & execution
9
5
  */
10
- export declare class ContractSuite {
6
+ export declare class SafeContractSuite {
11
7
  dummyClient: PublicClient;
12
- singleton: DeploymentData;
13
- proxyFactory: DeploymentData;
14
- m4337: DeploymentData;
15
- moduleSetup: DeploymentData;
16
- entryPoint: DeploymentData;
17
- constructor(client: PublicClient, singleton: DeploymentData, proxyFactory: DeploymentData, m4337: DeploymentData, moduleSetup: DeploymentData, entryPoint: DeploymentData);
18
- static init(): Promise<ContractSuite>;
8
+ singleton: Deployment;
9
+ proxyFactory: Deployment;
10
+ m4337: Deployment;
11
+ moduleSetup: Deployment;
12
+ entryPoint: Deployment;
13
+ constructor();
19
14
  addressForSetup(setup: Hex, saltNonce?: string): Promise<Address>;
20
15
  getSetup(owners: string[]): Hex;
21
16
  addOwnerData(newOwner: Address): Hex;
@@ -24,4 +19,3 @@ export declare class ContractSuite {
24
19
  buildUserOp(nonce: bigint, txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
25
20
  getNonce(address: Address, chainId: number): Promise<bigint>;
26
21
  }
27
- export {};
@@ -1,54 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ContractSuite = void 0;
4
- const safe_deployments_1 = require("@safe-global/safe-deployments");
5
- const safe_modules_deployments_1 = require("@safe-global/safe-modules-deployments");
3
+ exports.SafeContractSuite = void 0;
6
4
  const viem_1 = require("viem");
5
+ const deployments_1 = require("../_gen/deployments");
7
6
  const util_1 = require("../util");
8
7
  /**
9
8
  * All contracts used in account creation & execution
10
9
  */
11
- class ContractSuite {
12
- constructor(client, singleton, proxyFactory, m4337, moduleSetup, entryPoint) {
13
- this.dummyClient = client;
14
- this.singleton = singleton;
15
- this.proxyFactory = proxyFactory;
16
- this.m4337 = m4337;
17
- this.moduleSetup = moduleSetup;
18
- this.entryPoint = entryPoint;
19
- }
20
- static async init() {
21
- // TODO - this is a cheeky hack.
22
- const client = (0, util_1.getClient)(11155111);
23
- const safeDeployment = (fn) => getDeployment(fn, { version: "1.4.1" });
24
- const m4337Deployment = async (fn) => {
25
- return getDeployment(fn, { version: "0.3.0" });
26
- };
27
- const [singleton, proxyFactory, moduleSetup, m4337] = await Promise.all([
28
- safeDeployment(safe_deployments_1.getSafeL2SingletonDeployment),
29
- safeDeployment(safe_deployments_1.getProxyFactoryDeployment),
30
- m4337Deployment(safe_modules_deployments_1.getSafeModuleSetupDeployment),
31
- m4337Deployment(safe_modules_deployments_1.getSafe4337ModuleDeployment),
32
- ]);
33
- // console.log("Initialized ERC4337 & Safe Module Contracts:", {
34
- // singleton: await singleton.getAddress(),
35
- // proxyFactory: await proxyFactory.getAddress(),
36
- // m4337: await m4337.getAddress(),
37
- // moduleSetup: await moduleSetup.getAddress(),
38
- // entryPoint: await entryPoint.getAddress(),
39
- // });
40
- return new ContractSuite(client, singleton, proxyFactory, m4337, moduleSetup,
41
- // EntryPoint:
42
- {
43
- address: (await client.readContract({
44
- address: m4337.address,
45
- abi: m4337.abi,
46
- functionName: "SUPPORTED_ENTRYPOINT",
47
- })),
48
- abi: (0, viem_1.parseAbi)([
49
- "function getNonce(address, uint192 key) view returns (uint256 nonce)",
50
- ]),
51
- });
10
+ class SafeContractSuite {
11
+ constructor() {
12
+ this.dummyClient = (0, util_1.getClient)(11155111);
13
+ const deployments = deployments_1.SAFE_DEPLOYMENTS;
14
+ this.singleton = deployments.singleton;
15
+ this.proxyFactory = deployments.proxyFactory;
16
+ this.m4337 = deployments.m4337;
17
+ this.moduleSetup = deployments.moduleSetup;
18
+ this.entryPoint = deployments.entryPoint;
52
19
  }
53
20
  async addressForSetup(setup, saltNonce) {
54
21
  // bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
@@ -159,15 +126,4 @@ class ContractSuite {
159
126
  return nonce;
160
127
  }
161
128
  }
162
- exports.ContractSuite = ContractSuite;
163
- async function getDeployment(fn, { version }) {
164
- const deployment = fn({ version });
165
- if (!deployment) {
166
- throw new Error(`Deployment not found for ${fn.name} version ${version}`);
167
- }
168
- // TODO: maybe call parseAbi on deployment.abi here.
169
- return {
170
- address: deployment.networkAddresses["11155111"],
171
- abi: deployment.abi,
172
- };
173
- }
129
+ exports.SafeContractSuite = SafeContractSuite;
@@ -1,29 +1,29 @@
1
+ import { NearConfig } from "near-api-js/lib/near";
1
2
  import { FinalExecutionOutcome } from "near-api-js/lib/providers";
2
3
  import { NearEthAdapter, SignRequestData } from "near-ca";
3
4
  import { Address, Hash, Hex } from "viem";
4
- import { Erc4337Bundler } from "./lib/bundler";
5
- import { ContractSuite } from "./lib/safe";
5
+ import { SafeContractSuite } from "./lib/safe";
6
6
  import { EncodedTxData, MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
7
- export declare class TransactionManager {
7
+ export interface NearSafeConfig {
8
+ accountId: string;
9
+ mpcContractId: string;
10
+ pimlicoKey: string;
11
+ nearConfig?: NearConfig;
12
+ privateKey?: string;
13
+ safeSaltNonce?: string;
14
+ }
15
+ export declare class NearSafe {
8
16
  readonly nearAdapter: NearEthAdapter;
9
17
  readonly address: Address;
10
18
  private safePack;
11
19
  private setup;
12
20
  private pimlicoKey;
13
21
  private safeSaltNonce;
14
- private deployedChains;
15
- constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, safeSaltNonce: string);
16
- static create(config: {
17
- accountId: string;
18
- mpcContractId: string;
19
- pimlicoKey: string;
20
- privateKey?: string;
21
- safeSaltNonce?: string;
22
- }): Promise<TransactionManager>;
22
+ static create(config: NearSafeConfig): Promise<NearSafe>;
23
+ constructor(nearAdapter: NearEthAdapter, safePack: SafeContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, safeSaltNonce: string);
23
24
  get mpcAddress(): Address;
24
25
  get mpcContractId(): string;
25
26
  getBalance(chainId: number): Promise<bigint>;
26
- bundlerForChainId(chainId: number): Erc4337Bundler;
27
27
  buildTransaction(args: {
28
28
  chainId: number;
29
29
  transactions: MetaTransaction[];
@@ -32,14 +32,15 @@ export declare class TransactionManager {
32
32
  signTransaction(safeOpHash: Hex): Promise<Hex>;
33
33
  opHash(userOp: UserOperation): Promise<Hash>;
34
34
  encodeSignRequest(signRequest: SignRequestData, usePaymaster: boolean): Promise<EncodedTxData>;
35
- executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
36
- safeDeployed(chainId: number): Promise<boolean>;
37
- addOwnerTx(address: Address): MetaTransaction;
38
- safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
39
35
  broadcastEvm(chainId: number, outcome: FinalExecutionOutcome, unsignedUserOp: UserOperation): Promise<{
40
36
  signature: Hex;
41
37
  receipt: UserOperationReceipt;
42
38
  }>;
39
+ executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
40
+ safeDeployed(chainId: number): Promise<boolean>;
41
+ sufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
42
+ addOwnerTx(address: Address): MetaTransaction;
43
+ private bundlerForChainId;
43
44
  /**
44
45
  * Handles routing of signature requests based on the provided method, chain ID, and parameters.
45
46
  *
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TransactionManager = void 0;
3
+ exports.NearSafe = void 0;
4
4
  const near_ca_1 = require("near-ca");
5
5
  const viem_1 = require("viem");
6
6
  const bundler_1 = require("./lib/bundler");
@@ -8,27 +8,28 @@ const multisend_1 = require("./lib/multisend");
8
8
  const safe_1 = require("./lib/safe");
9
9
  const safe_message_1 = require("./lib/safe-message");
10
10
  const util_1 = require("./util");
11
- class TransactionManager {
11
+ class NearSafe {
12
+ static async create(config) {
13
+ const { pimlicoKey, safeSaltNonce } = config;
14
+ const nearAdapter = await (0, near_ca_1.setupAdapter)({ ...config });
15
+ const safePack = new safe_1.SafeContractSuite();
16
+ const setup = safePack.getSetup([nearAdapter.address]);
17
+ const safeAddress = await safePack.addressForSetup(setup, safeSaltNonce);
18
+ console.log(`
19
+ Near Adapter:
20
+ Near Account ID: ${nearAdapter.nearAccountId()}
21
+ MPC EOA: ${nearAdapter.address}
22
+ Safe: ${safeAddress}
23
+ `);
24
+ return new NearSafe(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce || "0");
25
+ }
12
26
  constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce) {
13
27
  this.nearAdapter = nearAdapter;
28
+ this.address = safeAddress;
29
+ this.setup = setup;
14
30
  this.safePack = safePack;
15
31
  this.pimlicoKey = pimlicoKey;
16
- this.setup = setup;
17
- this.address = safeAddress;
18
32
  this.safeSaltNonce = safeSaltNonce;
19
- this.deployedChains = new Set();
20
- }
21
- static async create(config) {
22
- const { pimlicoKey } = config;
23
- const [nearAdapter, safePack] = await Promise.all([
24
- (0, near_ca_1.setupAdapter)({ ...config }),
25
- safe_1.ContractSuite.init(),
26
- ]);
27
- console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
28
- const setup = safePack.getSetup([nearAdapter.address]);
29
- const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
30
- console.log(`Safe Address: ${safeAddress}`);
31
- return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, config.safeSaltNonce || "0");
32
33
  }
33
34
  get mpcAddress() {
34
35
  return this.nearAdapter.address;
@@ -39,9 +40,6 @@ class TransactionManager {
39
40
  async getBalance(chainId) {
40
41
  return await (0, util_1.getClient)(chainId).getBalance({ address: this.address });
41
42
  }
42
- bundlerForChainId(chainId) {
43
- return new bundler_1.Erc4337Bundler(this.safePack.entryPoint.address, this.pimlicoKey, chainId);
44
- }
45
43
  async buildTransaction(args) {
46
44
  const { transactions, usePaymaster, chainId } = args;
47
45
  if (transactions.length === 0) {
@@ -82,6 +80,21 @@ class TransactionManager {
82
80
  },
83
81
  };
84
82
  }
83
+ async broadcastEvm(chainId, outcome, unsignedUserOp) {
84
+ const signature = (0, util_1.packSignature)((0, viem_1.serializeSignature)((0, near_ca_1.signatureFromOutcome)(outcome)));
85
+ try {
86
+ return {
87
+ signature,
88
+ receipt: await this.executeTransaction(chainId, {
89
+ ...unsignedUserOp,
90
+ signature,
91
+ }),
92
+ };
93
+ }
94
+ catch (error) {
95
+ throw new Error(`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`);
96
+ }
97
+ }
85
98
  async executeTransaction(chainId, userOp) {
86
99
  const bundler = this.bundlerForChainId(chainId);
87
100
  const userOpHash = await bundler.sendUserOperation(userOp);
@@ -93,15 +106,15 @@ class TransactionManager {
93
106
  return userOpReceipt;
94
107
  }
95
108
  async safeDeployed(chainId) {
96
- // Early exit if already known.
97
- if (chainId in this.deployedChains) {
109
+ return (0, util_1.isContract)(this.address, chainId);
110
+ }
111
+ async sufficientlyFunded(chainId, transactions, gasCost) {
112
+ const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
113
+ if (txValue + gasCost === 0n) {
98
114
  return true;
99
115
  }
100
- const deployed = await (0, util_1.isContract)(this.address, chainId);
101
- if (deployed) {
102
- this.deployedChains.add(chainId);
103
- }
104
- return deployed;
116
+ const safeBalance = await this.getBalance(chainId);
117
+ return txValue + gasCost < safeBalance;
105
118
  }
106
119
  addOwnerTx(address) {
107
120
  return {
@@ -110,28 +123,8 @@ class TransactionManager {
110
123
  data: this.safePack.addOwnerData(address),
111
124
  };
112
125
  }
113
- async safeSufficientlyFunded(chainId, transactions, gasCost) {
114
- const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
115
- if (txValue + gasCost === 0n) {
116
- return true;
117
- }
118
- const safeBalance = await this.getBalance(chainId);
119
- return txValue + gasCost < safeBalance;
120
- }
121
- async broadcastEvm(chainId, outcome, unsignedUserOp) {
122
- const signature = (0, util_1.packSignature)((0, viem_1.serializeSignature)((0, near_ca_1.signatureFromOutcome)(outcome)));
123
- try {
124
- return {
125
- signature,
126
- receipt: await this.executeTransaction(chainId, {
127
- ...unsignedUserOp,
128
- signature,
129
- }),
130
- };
131
- }
132
- catch (error) {
133
- throw new Error(`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`);
134
- }
126
+ bundlerForChainId(chainId) {
127
+ return new bundler_1.Erc4337Bundler(this.safePack.entryPoint.address, this.pimlicoKey, chainId);
135
128
  }
136
129
  /**
137
130
  * Handles routing of signature requests based on the provided method, chain ID, and parameters.
@@ -190,4 +183,4 @@ class TransactionManager {
190
183
  }
191
184
  }
192
185
  }
193
- exports.TransactionManager = TransactionManager;
186
+ exports.NearSafe = NearSafe;
@@ -1,5 +1,16 @@
1
1
  import { FunctionCallTransaction, SignArgs } from "near-ca";
2
- import { Address, Hash, Hex, TransactionSerializable } from "viem";
2
+ import { Address, Hash, Hex, ParseAbi, TransactionSerializable } from "viem";
3
+ export type SafeDeployments = {
4
+ singleton: Deployment;
5
+ proxyFactory: Deployment;
6
+ moduleSetup: Deployment;
7
+ m4337: Deployment;
8
+ entryPoint: Deployment;
9
+ };
10
+ export interface Deployment {
11
+ abi: unknown[] | ParseAbi<readonly string[]>;
12
+ address: Address;
13
+ }
3
14
  export interface UnsignedUserOperation {
4
15
  sender: Address;
5
16
  nonce: string;
@@ -0,0 +1,2 @@
1
+ import { SafeDeployments } from "../types";
2
+ export declare const SAFE_DEPLOYMENTS: SafeDeployments;