near-safe 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,20 @@
1
- import { ethers } from "ethers";
2
1
  import { getProxyFactoryDeployment, getSafeL2SingletonDeployment, } from "@safe-global/safe-deployments";
3
2
  import { getSafe4337ModuleDeployment, getSafeModuleSetupDeployment, } from "@safe-global/safe-modules-deployments";
4
- import { PLACEHOLDER_SIG, packGas, packPaymasterData } from "../util";
3
+ import { encodeFunctionData, encodePacked, getCreate2Address, keccak256, parseAbi, toHex, zeroAddress, } from "viem";
4
+ import { PLACEHOLDER_SIG, getClient, packGas, packPaymasterData, } from "../util";
5
5
  /**
6
6
  * All contracts used in account creation & execution
7
7
  */
8
8
  export class ContractSuite {
9
- provider;
9
+ // Used only for stateless contract reads.
10
+ dummyClient;
10
11
  singleton;
11
12
  proxyFactory;
12
13
  m4337;
13
14
  moduleSetup;
14
15
  entryPoint;
15
- constructor(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint) {
16
- this.provider = provider;
16
+ constructor(client, singleton, proxyFactory, m4337, moduleSetup, entryPoint) {
17
+ this.dummyClient = client;
17
18
  this.singleton = singleton;
18
19
  this.proxyFactory = proxyFactory;
19
20
  this.m4337 = m4337;
@@ -22,108 +23,154 @@ export class ContractSuite {
22
23
  }
23
24
  static async init() {
24
25
  // TODO - this is a cheeky hack.
25
- const provider = new ethers.JsonRpcProvider("https://rpc2.sepolia.org");
26
- const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
26
+ const client = getClient(11155111);
27
+ const safeDeployment = (fn) => getDeployment(fn, { version: "1.4.1" });
27
28
  const m4337Deployment = async (fn) => {
28
- return getDeployment(fn, { provider, version: "0.3.0" });
29
+ return getDeployment(fn, { version: "0.3.0" });
29
30
  };
30
- // Need this first to get entryPoint address
31
- const m4337 = await m4337Deployment(getSafe4337ModuleDeployment);
32
- const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
31
+ const [singleton, proxyFactory, moduleSetup, m4337] = await Promise.all([
33
32
  safeDeployment(getSafeL2SingletonDeployment),
34
33
  safeDeployment(getProxyFactoryDeployment),
35
34
  m4337Deployment(getSafeModuleSetupDeployment),
36
- m4337.SUPPORTED_ENTRYPOINT(),
35
+ m4337Deployment(getSafe4337ModuleDeployment),
37
36
  ]);
38
- const entryPoint = new ethers.Contract(supportedEntryPoint, ["function getNonce(address, uint192 key) view returns (uint256 nonce)"], provider);
39
- console.log("Initialized ERC4337 & Safe Module Contracts:", {
40
- singleton: await singleton.getAddress(),
41
- proxyFactory: await proxyFactory.getAddress(),
42
- m4337: await m4337.getAddress(),
43
- moduleSetup: await moduleSetup.getAddress(),
44
- entryPoint: await entryPoint.getAddress(),
37
+ // console.log("Initialized ERC4337 & Safe Module Contracts:", {
38
+ // singleton: await singleton.getAddress(),
39
+ // proxyFactory: await proxyFactory.getAddress(),
40
+ // m4337: await m4337.getAddress(),
41
+ // moduleSetup: await moduleSetup.getAddress(),
42
+ // entryPoint: await entryPoint.getAddress(),
43
+ // });
44
+ return new ContractSuite(client, singleton, proxyFactory, m4337, moduleSetup,
45
+ // EntryPoint:
46
+ {
47
+ address: (await client.readContract({
48
+ address: m4337.address,
49
+ abi: m4337.abi,
50
+ functionName: "SUPPORTED_ENTRYPOINT",
51
+ })),
52
+ abi: parseAbi([
53
+ "function getNonce(address, uint192 key) view returns (uint256 nonce)",
54
+ ]),
45
55
  });
46
- return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
47
56
  }
48
57
  async addressForSetup(setup, saltNonce) {
49
58
  // bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
50
59
  // cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L58
51
- const salt = ethers.keccak256(ethers.solidityPacked(["bytes32", "uint256"], [ethers.keccak256(setup), saltNonce || 0]));
60
+ const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(setup), BigInt(saltNonce || "0")]));
52
61
  // abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton)));
53
62
  // cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L29
54
- const initCode = ethers.solidityPacked(["bytes", "uint256"], [
55
- await this.proxyFactory.proxyCreationCode(),
56
- await this.singleton.getAddress(),
63
+ const initCode = encodePacked(["bytes", "uint256"], [
64
+ (await this.dummyClient.readContract({
65
+ address: this.proxyFactory.address,
66
+ abi: this.proxyFactory.abi,
67
+ functionName: "proxyCreationCode",
68
+ })),
69
+ BigInt(this.singleton.address),
57
70
  ]);
58
- return ethers.getCreate2Address(await this.proxyFactory.getAddress(), salt, ethers.keccak256(initCode));
71
+ return getCreate2Address({
72
+ from: this.proxyFactory.address,
73
+ salt,
74
+ bytecodeHash: keccak256(initCode),
75
+ });
59
76
  }
60
- async getSetup(owners) {
61
- const setup = await this.singleton.interface.encodeFunctionData("setup", [
62
- owners,
63
- 1, // We use sign threshold of 1.
64
- this.moduleSetup.target,
65
- this.moduleSetup.interface.encodeFunctionData("enableModules", [
66
- [this.m4337.target],
67
- ]),
68
- this.m4337.target,
69
- ethers.ZeroAddress,
70
- 0,
71
- ethers.ZeroAddress,
72
- ]);
73
- return setup;
77
+ getSetup(owners) {
78
+ return encodeFunctionData({
79
+ abi: this.singleton.abi,
80
+ functionName: "setup",
81
+ args: [
82
+ owners,
83
+ 1, // We use sign threshold of 1.
84
+ this.moduleSetup.address,
85
+ encodeFunctionData({
86
+ abi: this.moduleSetup.abi,
87
+ functionName: "enableModules",
88
+ args: [[this.m4337.address]],
89
+ }),
90
+ this.m4337.address,
91
+ zeroAddress,
92
+ 0,
93
+ zeroAddress,
94
+ ],
95
+ });
96
+ }
97
+ addOwnerData(newOwner) {
98
+ return encodeFunctionData({
99
+ abi: this.singleton.abi,
100
+ functionName: "addOwnerWithThreshold",
101
+ args: [newOwner, 1],
102
+ });
74
103
  }
75
104
  async getOpHash(unsignedUserOp) {
76
105
  const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
77
- return this.m4337.getOperationHash({
78
- ...unsignedUserOp,
79
- initCode: factory
80
- ? ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
81
- : "0x",
82
- accountGasLimits: packGas(verificationGasLimit, callGasLimit),
83
- gasFees: packGas(maxPriorityFeePerGas, maxFeePerGas),
84
- paymasterAndData: packPaymasterData(unsignedUserOp),
85
- signature: PLACEHOLDER_SIG,
106
+ const opHash = await this.dummyClient.readContract({
107
+ address: this.m4337.address,
108
+ abi: this.m4337.abi,
109
+ functionName: "getOperationHash",
110
+ args: [
111
+ {
112
+ ...unsignedUserOp,
113
+ initCode: factory
114
+ ? encodePacked(["address", "bytes"], [factory, factoryData])
115
+ : "0x",
116
+ accountGasLimits: packGas(verificationGasLimit, callGasLimit),
117
+ gasFees: packGas(maxPriorityFeePerGas, maxFeePerGas),
118
+ paymasterAndData: packPaymasterData(unsignedUserOp),
119
+ signature: PLACEHOLDER_SIG,
120
+ },
121
+ ],
86
122
  });
123
+ return opHash;
87
124
  }
88
125
  factoryDataForSetup(safeNotDeployed, setup, safeSaltNonce) {
89
126
  return safeNotDeployed
90
127
  ? {
91
- factory: this.proxyFactory.target,
92
- factoryData: this.proxyFactory.interface.encodeFunctionData("createProxyWithNonce", [this.singleton.target, setup, safeSaltNonce]),
128
+ factory: this.proxyFactory.address,
129
+ factoryData: encodeFunctionData({
130
+ abi: this.proxyFactory.abi,
131
+ functionName: "createProxyWithNonce",
132
+ args: [this.singleton.address, setup, safeSaltNonce],
133
+ }),
93
134
  }
94
135
  : {};
95
136
  }
96
- async buildUserOp(txData, safeAddress, feeData, setup, safeNotDeployed, safeSaltNonce) {
97
- const rawUserOp = {
137
+ async buildUserOp(nonce, txData, safeAddress, feeData, setup, safeNotDeployed, safeSaltNonce) {
138
+ return {
98
139
  sender: safeAddress,
99
- nonce: ethers.toBeHex(await this.entryPoint.getNonce(safeAddress, 0)),
140
+ nonce: toHex(nonce),
100
141
  ...this.factoryDataForSetup(safeNotDeployed, setup, safeSaltNonce),
101
142
  // <https://github.com/safe-global/safe-modules/blob/9a18245f546bf2a8ed9bdc2b04aae44f949ec7a0/modules/4337/contracts/Safe4337Module.sol#L172>
102
- callData: this.m4337.interface.encodeFunctionData("executeUserOp", [
103
- txData.to,
104
- BigInt(txData.value),
105
- txData.data,
106
- txData.operation || 0,
107
- ]),
143
+ callData: encodeFunctionData({
144
+ abi: this.m4337.abi,
145
+ functionName: "executeUserOp",
146
+ args: [
147
+ txData.to,
148
+ BigInt(txData.value),
149
+ txData.data,
150
+ txData.operation || 0,
151
+ ],
152
+ }),
108
153
  ...feeData,
109
154
  };
110
- return rawUserOp;
155
+ }
156
+ async getNonce(address, chainId) {
157
+ const nonce = (await getClient(chainId).readContract({
158
+ abi: this.entryPoint.abi,
159
+ address: this.entryPoint.address,
160
+ functionName: "getNonce",
161
+ args: [address, 0],
162
+ }));
163
+ return nonce;
111
164
  }
112
165
  }
113
- async function getDeployment(fn, { provider, version }) {
114
- const { chainId } = await provider.getNetwork();
166
+ async function getDeployment(fn, { version }) {
115
167
  const deployment = fn({ version });
116
168
  if (!deployment) {
117
- throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
118
- }
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"];
169
+ throw new Error(`Deployment not found for ${fn.name} version ${version}`);
127
170
  }
128
- return new ethers.Contract(address, deployment.abi, provider);
171
+ // TODO: maybe call parseAbi on deployment.abi here.
172
+ return {
173
+ address: deployment.networkAddresses["11155111"],
174
+ abi: deployment.abi,
175
+ };
129
176
  }
@@ -1,25 +1,27 @@
1
+ import { FinalExecutionOutcome } from "near-api-js/lib/providers";
1
2
  import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
3
+ import { Address, Hash, Hex } from "viem";
2
4
  import { Erc4337Bundler } from "./lib/bundler";
3
- import { UserOperation, UserOperationReceipt } from "./types";
4
- import { MetaTransaction } from "ethers-multisend";
5
5
  import { ContractSuite } from "./lib/safe";
6
- import { Address, Hash, Hex } from "viem";
6
+ import { MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
7
7
  export declare class TransactionManager {
8
8
  readonly nearAdapter: NearEthAdapter;
9
9
  readonly address: Address;
10
- readonly entryPointAddress: Address;
11
10
  private safePack;
12
11
  private setup;
13
12
  private pimlicoKey;
14
13
  private safeSaltNonce;
15
14
  private deployedChains;
16
- constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
15
+ constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, safeSaltNonce: string);
17
16
  static create(config: {
17
+ accountId: string;
18
+ mpcContractId: string;
18
19
  pimlicoKey: string;
19
- nearAdapter: NearEthAdapter;
20
+ privateKey?: string;
20
21
  safeSaltNonce?: string;
21
22
  }): Promise<TransactionManager>;
22
23
  get mpcAddress(): Address;
24
+ get mpcContractId(): string;
23
25
  getBalance(chainId: number): Promise<bigint>;
24
26
  bundlerForChainId(chainId: number): Erc4337Bundler;
25
27
  buildTransaction(args: {
@@ -32,6 +34,10 @@ export declare class TransactionManager {
32
34
  encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
33
35
  executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
34
36
  safeDeployed(chainId: number): Promise<boolean>;
35
- addOwnerTx(address: string): MetaTransaction;
37
+ addOwnerTx(address: Address): MetaTransaction;
36
38
  safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
39
+ broadcastEvm(chainId: number, outcome: FinalExecutionOutcome, unsignedUserOp: UserOperation): Promise<{
40
+ signature: Hex;
41
+ receipt: UserOperationReceipt;
42
+ }>;
37
43
  }
@@ -1,59 +1,65 @@
1
- import { Network } from "near-ca";
1
+ import { setupAdapter, signatureFromOutcome, } from "near-ca";
2
+ import { serializeSignature } from "viem";
2
3
  import { Erc4337Bundler } from "./lib/bundler";
3
- import { packSignature } from "./util";
4
- import { encodeMulti } from "ethers-multisend";
4
+ import { encodeMulti } from "./lib/multisend";
5
5
  import { ContractSuite } from "./lib/safe";
6
+ import { getClient, isContract, packSignature } from "./util";
6
7
  export class TransactionManager {
7
8
  nearAdapter;
8
9
  address;
9
- entryPointAddress;
10
10
  safePack;
11
11
  setup;
12
12
  pimlicoKey;
13
13
  safeSaltNonce;
14
14
  deployedChains;
15
- constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
15
+ constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce) {
16
16
  this.nearAdapter = nearAdapter;
17
17
  this.safePack = safePack;
18
18
  this.pimlicoKey = pimlicoKey;
19
- this.entryPointAddress = entryPointAddress;
20
19
  this.setup = setup;
21
20
  this.address = safeAddress;
22
21
  this.safeSaltNonce = safeSaltNonce;
23
22
  this.deployedChains = new Set();
24
23
  }
25
24
  static async create(config) {
26
- const { nearAdapter, pimlicoKey } = config;
27
- const safePack = await ContractSuite.init();
25
+ const { pimlicoKey } = config;
26
+ const [nearAdapter, safePack] = await Promise.all([
27
+ setupAdapter({ ...config }),
28
+ ContractSuite.init(),
29
+ ]);
28
30
  console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
29
- const setup = await safePack.getSetup([nearAdapter.address]);
31
+ const setup = safePack.getSetup([nearAdapter.address]);
30
32
  const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
31
- const entryPointAddress = (await safePack.entryPoint.getAddress());
32
33
  console.log(`Safe Address: ${safeAddress}`);
33
- return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, config.safeSaltNonce || "0");
34
+ return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, config.safeSaltNonce || "0");
34
35
  }
35
36
  get mpcAddress() {
36
37
  return this.nearAdapter.address;
37
38
  }
39
+ get mpcContractId() {
40
+ return this.nearAdapter.mpcContract.contract.contractId;
41
+ }
38
42
  async getBalance(chainId) {
39
- const provider = Network.fromChainId(chainId).client;
40
- return await provider.getBalance({ address: this.address });
43
+ return await getClient(chainId).getBalance({ address: this.address });
41
44
  }
42
45
  bundlerForChainId(chainId) {
43
- return new Erc4337Bundler(this.entryPointAddress, this.pimlicoKey, chainId);
46
+ return new Erc4337Bundler(this.safePack.entryPoint.address, this.pimlicoKey, chainId);
44
47
  }
45
48
  async buildTransaction(args) {
46
49
  const { transactions, usePaymaster, chainId } = args;
47
- const bundler = this.bundlerForChainId(chainId);
48
- const gasFees = (await bundler.getGasPrice()).fast;
49
- // Build Singular MetaTransaction for Multisend from transaction list.
50
50
  if (transactions.length === 0) {
51
51
  throw new Error("Empty transaction set!");
52
52
  }
53
+ const bundler = this.bundlerForChainId(chainId);
54
+ const [gasFees, nonce, safeDeployed] = await Promise.all([
55
+ bundler.getGasPrice(),
56
+ this.safePack.getNonce(this.address, chainId),
57
+ this.safeDeployed(chainId),
58
+ ]);
59
+ // Build Singular MetaTransaction for Multisend from transaction list.
53
60
  const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
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);
61
+ const rawUserOp = await this.safePack.buildUserOp(nonce, tx, this.address, gasFees.fast, this.setup, !safeDeployed, this.safeSaltNonce);
62
+ const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, !safeDeployed);
57
63
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
58
64
  return unsignedUserOp;
59
65
  }
@@ -98,11 +104,11 @@ export class TransactionManager {
98
104
  return userOpReceipt;
99
105
  }
100
106
  async safeDeployed(chainId) {
107
+ // Early exit if already known.
101
108
  if (chainId in this.deployedChains) {
102
109
  return true;
103
110
  }
104
- const provider = Network.fromChainId(chainId).client;
105
- const deployed = (await provider.getCode({ address: this.address })) !== "0x";
111
+ const deployed = await isContract(this.address, chainId);
106
112
  if (deployed) {
107
113
  this.deployedChains.add(chainId);
108
114
  }
@@ -112,7 +118,7 @@ export class TransactionManager {
112
118
  return {
113
119
  to: this.address,
114
120
  value: "0",
115
- data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
121
+ data: this.safePack.addOwnerData(address),
116
122
  };
117
123
  }
118
124
  async safeSufficientlyFunded(chainId, transactions, gasCost) {
@@ -123,4 +129,19 @@ export class TransactionManager {
123
129
  const safeBalance = await this.getBalance(chainId);
124
130
  return txValue + gasCost < safeBalance;
125
131
  }
132
+ async broadcastEvm(chainId, outcome, unsignedUserOp) {
133
+ const signature = packSignature(serializeSignature(signatureFromOutcome(outcome)));
134
+ try {
135
+ return {
136
+ signature,
137
+ receipt: await this.executeTransaction(chainId, {
138
+ ...unsignedUserOp,
139
+ signature,
140
+ }),
141
+ };
142
+ }
143
+ catch (error) {
144
+ throw new Error(`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`);
145
+ }
146
+ }
126
147
  }
@@ -79,4 +79,14 @@ export interface GasPrice {
79
79
  maxFeePerGas: Hex;
80
80
  maxPriorityFeePerGas: Hex;
81
81
  }
82
+ export declare enum OperationType {
83
+ Call = 0,
84
+ DelegateCall = 1
85
+ }
86
+ export interface MetaTransaction {
87
+ readonly to: string;
88
+ readonly value: string;
89
+ readonly data: string;
90
+ readonly operation?: OperationType;
91
+ }
82
92
  export {};
package/dist/esm/types.js CHANGED
@@ -1 +1,5 @@
1
- export {};
1
+ export var OperationType;
2
+ (function (OperationType) {
3
+ OperationType[OperationType["Call"] = 0] = "Call";
4
+ OperationType[OperationType["DelegateCall"] = 1] = "DelegateCall";
5
+ })(OperationType || (OperationType = {}));
@@ -1,10 +1,11 @@
1
- import { PaymasterData } from "./types.js";
2
- import { MetaTransaction } from "ethers-multisend";
3
- import { Hex } from "viem";
1
+ import { Address, Hex, PublicClient } from "viem";
2
+ import { PaymasterData, MetaTransaction } from "./types";
4
3
  export declare const PLACEHOLDER_SIG: `0x${string}`;
5
4
  type IntLike = Hex | bigint | string | number;
6
5
  export declare const packGas: (hi: IntLike, lo: IntLike) => string;
7
6
  export declare function packSignature(signature: `0x${string}`, validFrom?: number, validTo?: number): Hex;
8
7
  export declare function packPaymasterData(data: PaymasterData): Hex;
9
8
  export declare function containsValue(transactions: MetaTransaction[]): boolean;
9
+ export declare function isContract(address: Address, chainId: number): Promise<boolean>;
10
+ export declare function getClient(chainId: number): PublicClient;
10
11
  export {};
package/dist/esm/util.js CHANGED
@@ -1,4 +1,5 @@
1
- import { concatHex, encodePacked, toHex } from "viem";
1
+ import { Network } from "near-ca";
2
+ import { concatHex, encodePacked, toHex, } from "viem";
2
3
  export const PLACEHOLDER_SIG = encodePacked(["uint48", "uint48"], [0, 0]);
3
4
  export const packGas = (hi, lo) => encodePacked(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
4
5
  export function packSignature(signature, validFrom = 0, validTo = 0) {
@@ -17,3 +18,9 @@ export function packPaymasterData(data) {
17
18
  export function containsValue(transactions) {
18
19
  return transactions.some((tx) => tx.value !== "0");
19
20
  }
21
+ export async function isContract(address, chainId) {
22
+ return (await getClient(chainId).getCode({ address })) !== undefined;
23
+ }
24
+ export function getClient(chainId) {
25
+ return Network.fromChainId(chainId).client;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "near-safe",
3
- "version": "0.2.0",
3
+ "version": "0.3.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",
@@ -36,18 +36,15 @@
36
36
  "example": "tsx examples/send-tx.ts",
37
37
  "lint": "eslint . --ignore-pattern dist/",
38
38
  "test": "jest",
39
- "fmt": "prettier --write '{src,examples,tests}/**/*.{js,jsx,ts,tsx}'",
39
+ "fmt": "prettier --write '{src,examples,tests}/**/*.{js,jsx,ts,tsx}' && yarn lint --fix",
40
40
  "all": "yarn fmt && yarn lint && yarn build"
41
41
  },
42
42
  "dependencies": {
43
43
  "@safe-global/safe-deployments": "^1.37.0",
44
44
  "@safe-global/safe-modules-deployments": "^2.2.0",
45
- "ethers": "^6.13.1",
46
- "ethers-multisend": "^3.1.0",
47
45
  "near-api-js": "^5.0.0",
48
46
  "near-ca": "^0.5.2",
49
- "viem": "^2.16.5",
50
- "yargs": "^17.7.2"
47
+ "viem": "^2.16.5"
51
48
  },
52
49
  "devDependencies": {
53
50
  "@types/jest": "^29.5.12",
@@ -57,11 +54,17 @@
57
54
  "@typescript-eslint/parser": "^8.1.0",
58
55
  "dotenv": "^16.4.5",
59
56
  "eslint": "^9.6.0",
57
+ "eslint-plugin-import": "^2.30.0",
58
+ "ethers": "^6.13.1",
60
59
  "jest": "^29.7.0",
61
60
  "prettier": "^3.3.2",
62
61
  "ts-jest": "^29.1.5",
63
62
  "tsx": "^4.16.0",
64
63
  "typescript": "^5.5.2",
65
64
  "yargs": "^17.7.2"
65
+ },
66
+ "resolutions": {
67
+ "glob": "^9.0.0",
68
+ "base-x": "^3.0.0"
66
69
  }
67
70
  }