near-safe 0.1.0 → 0.2.0
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 +1 -1
- package/dist/cjs/lib/bundler.d.ts +5 -2
- package/dist/cjs/lib/bundler.js +17 -7
- package/dist/cjs/lib/safe.d.ts +8 -7
- package/dist/cjs/lib/safe.js +31 -12
- package/dist/cjs/tx-manager.d.ts +16 -15
- package/dist/cjs/tx-manager.js +47 -37
- package/dist/cjs/types.d.ts +24 -27
- package/dist/cjs/util.d.ts +8 -6
- package/dist/cjs/util.js +10 -10
- package/dist/esm/lib/bundler.d.ts +5 -2
- package/dist/esm/lib/bundler.js +18 -6
- package/dist/esm/lib/safe.d.ts +8 -7
- package/dist/esm/lib/safe.js +31 -12
- package/dist/esm/tx-manager.d.ts +16 -15
- package/dist/esm/tx-manager.js +51 -41
- package/dist/esm/types.d.ts +24 -27
- package/dist/esm/util.d.ts +8 -6
- package/dist/esm/util.js +10 -10
- package/package.json +8 -2
- package/dist/cjs/lib/near.d.ts +0 -3
- package/dist/cjs/lib/near.js +0 -14
- package/dist/esm/lib/near.d.ts +0 -3
- package/dist/esm/lib/near.js +0 -11
package/README.md
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
import { ethers } from "ethers";
|
2
|
-
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
|
2
|
+
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types.js";
|
3
3
|
export declare class Erc4337Bundler {
|
4
4
|
provider: ethers.JsonRpcProvider;
|
5
5
|
entryPointAddress: string;
|
6
|
-
|
6
|
+
apiKey: string;
|
7
|
+
chainId: number;
|
8
|
+
constructor(entryPointAddress: string, apiKey: string, chainId: number);
|
9
|
+
client(chainId: number): ethers.JsonRpcProvider;
|
7
10
|
getPaymasterData(rawUserOp: UnsignedUserOperation, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
|
8
11
|
sendUserOperation(userOp: UserOperation): Promise<string>;
|
9
12
|
getGasPrice(): Promise<GasPrices>;
|
package/dist/cjs/lib/bundler.js
CHANGED
@@ -1,19 +1,29 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.Erc4337Bundler = void 0;
|
4
|
+
// TODO: Ethers dependency is only for Generic HTTP Provider
|
4
5
|
const ethers_1 = require("ethers");
|
5
|
-
const
|
6
|
+
const util_js_1 = require("../util.js");
|
7
|
+
const viem_1 = require("viem");
|
8
|
+
function bundlerUrl(chainId, apikey) {
|
9
|
+
return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apikey}`;
|
10
|
+
}
|
6
11
|
class Erc4337Bundler {
|
7
|
-
constructor(
|
12
|
+
constructor(entryPointAddress, apiKey, chainId) {
|
8
13
|
this.entryPointAddress = entryPointAddress;
|
9
|
-
this.
|
14
|
+
this.apiKey = apiKey;
|
15
|
+
this.chainId = chainId;
|
16
|
+
this.provider = new ethers_1.ethers.JsonRpcProvider(bundlerUrl(chainId, apiKey));
|
17
|
+
}
|
18
|
+
client(chainId) {
|
19
|
+
return new ethers_1.ethers.JsonRpcProvider(bundlerUrl(chainId, this.apiKey));
|
10
20
|
}
|
11
21
|
async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
|
12
22
|
// TODO: Keep this option out of the bundler
|
13
23
|
if (usePaymaster) {
|
14
24
|
console.log("Requesting paymaster data...");
|
15
25
|
const data = this.provider.send("pm_sponsorUserOperation", [
|
16
|
-
{ ...rawUserOp, signature:
|
26
|
+
{ ...rawUserOp, signature: util_js_1.PLACEHOLDER_SIG },
|
17
27
|
this.entryPointAddress,
|
18
28
|
]);
|
19
29
|
return data;
|
@@ -53,8 +63,8 @@ exports.Erc4337Bundler = Erc4337Bundler;
|
|
53
63
|
// TODO(bh2smith) Should probably get reasonable estimates here:
|
54
64
|
const defaultPaymasterData = (safeNotDeployed) => {
|
55
65
|
return {
|
56
|
-
verificationGasLimit:
|
57
|
-
callGasLimit:
|
58
|
-
preVerificationGas:
|
66
|
+
verificationGasLimit: (0, viem_1.toHex)(safeNotDeployed ? 500000 : 100000),
|
67
|
+
callGasLimit: (0, viem_1.toHex)(100000),
|
68
|
+
preVerificationGas: (0, viem_1.toHex)(100000),
|
59
69
|
};
|
60
70
|
};
|
package/dist/cjs/lib/safe.d.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { ethers } from "ethers";
|
2
2
|
import { GasPrice, UnsignedUserOperation, UserOperation } from "../types";
|
3
3
|
import { MetaTransaction } from "ethers-multisend";
|
4
|
+
import { Address, Hash, Hex } from "viem";
|
4
5
|
/**
|
5
6
|
* All contracts used in account creation & execution
|
6
7
|
*/
|
@@ -12,13 +13,13 @@ export declare class ContractSuite {
|
|
12
13
|
moduleSetup: ethers.Contract;
|
13
14
|
entryPoint: ethers.Contract;
|
14
15
|
constructor(provider: ethers.JsonRpcProvider, singleton: ethers.Contract, proxyFactory: ethers.Contract, m4337: ethers.Contract, moduleSetup: ethers.Contract, entryPoint: ethers.Contract);
|
15
|
-
static init(
|
16
|
-
addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<
|
17
|
-
getSetup(owners: string[]): Promise<
|
18
|
-
getOpHash(unsignedUserOp: UserOperation): Promise<
|
16
|
+
static init(): Promise<ContractSuite>;
|
17
|
+
addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<Address>;
|
18
|
+
getSetup(owners: string[]): Promise<Hex>;
|
19
|
+
getOpHash(unsignedUserOp: UserOperation): Promise<Hash>;
|
19
20
|
factoryDataForSetup(safeNotDeployed: boolean, setup: string, safeSaltNonce: string): {
|
20
|
-
factory?:
|
21
|
-
factoryData?:
|
21
|
+
factory?: Address;
|
22
|
+
factoryData?: Hex;
|
22
23
|
};
|
23
|
-
buildUserOp(txData: MetaTransaction, safeAddress:
|
24
|
+
buildUserOp(txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
|
24
25
|
}
|
package/dist/cjs/lib/safe.js
CHANGED
@@ -17,9 +17,13 @@ class ContractSuite {
|
|
17
17
|
this.moduleSetup = moduleSetup;
|
18
18
|
this.entryPoint = entryPoint;
|
19
19
|
}
|
20
|
-
static async init(
|
20
|
+
static async init() {
|
21
|
+
// TODO - this is a cheeky hack.
|
22
|
+
const provider = new ethers_1.ethers.JsonRpcProvider("https://rpc2.sepolia.org");
|
21
23
|
const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
|
22
|
-
const m4337Deployment = (fn) =>
|
24
|
+
const m4337Deployment = async (fn) => {
|
25
|
+
return getDeployment(fn, { provider, version: "0.3.0" });
|
26
|
+
};
|
23
27
|
// Need this first to get entryPoint address
|
24
28
|
const m4337 = await m4337Deployment(safe_modules_deployments_1.getSafe4337ModuleDeployment);
|
25
29
|
const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
|
@@ -29,6 +33,13 @@ class ContractSuite {
|
|
29
33
|
m4337.SUPPORTED_ENTRYPOINT(),
|
30
34
|
]);
|
31
35
|
const entryPoint = new ethers_1.ethers.Contract(supportedEntryPoint, ["function getNonce(address, uint192 key) view returns (uint256 nonce)"], provider);
|
36
|
+
console.log("Initialized ERC4337 & Safe Module Contracts:", {
|
37
|
+
singleton: await singleton.getAddress(),
|
38
|
+
proxyFactory: await proxyFactory.getAddress(),
|
39
|
+
m4337: await m4337.getAddress(),
|
40
|
+
moduleSetup: await moduleSetup.getAddress(),
|
41
|
+
entryPoint: await entryPoint.getAddress(),
|
42
|
+
});
|
32
43
|
return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
|
33
44
|
}
|
34
45
|
async addressForSetup(setup, saltNonce) {
|
@@ -58,16 +69,15 @@ class ContractSuite {
|
|
58
69
|
]);
|
59
70
|
return setup;
|
60
71
|
}
|
61
|
-
async getOpHash(unsignedUserOp
|
62
|
-
|
63
|
-
) {
|
72
|
+
async getOpHash(unsignedUserOp) {
|
73
|
+
const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
|
64
74
|
return this.m4337.getOperationHash({
|
65
75
|
...unsignedUserOp,
|
66
|
-
initCode:
|
67
|
-
? ethers_1.ethers.solidityPacked(["address", "bytes"], [
|
76
|
+
initCode: factory
|
77
|
+
? ethers_1.ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
|
68
78
|
: "0x",
|
69
|
-
accountGasLimits: (0, util_1.packGas)(
|
70
|
-
gasFees: (0, util_1.packGas)(
|
79
|
+
accountGasLimits: (0, util_1.packGas)(verificationGasLimit, callGasLimit),
|
80
|
+
gasFees: (0, util_1.packGas)(maxPriorityFeePerGas, maxFeePerGas),
|
71
81
|
paymasterAndData: (0, util_1.packPaymasterData)(unsignedUserOp),
|
72
82
|
signature: util_1.PLACEHOLDER_SIG,
|
73
83
|
});
|
@@ -101,8 +111,17 @@ exports.ContractSuite = ContractSuite;
|
|
101
111
|
async function getDeployment(fn, { provider, version }) {
|
102
112
|
const { chainId } = await provider.getNetwork();
|
103
113
|
const deployment = fn({ version });
|
104
|
-
if (!deployment
|
105
|
-
throw new Error(`Deployment not found for version ${version}
|
114
|
+
if (!deployment) {
|
115
|
+
throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
|
116
|
+
}
|
117
|
+
let address = deployment.networkAddresses[`${chainId}`];
|
118
|
+
if (!address) {
|
119
|
+
// console.warn(
|
120
|
+
// `Deployment asset ${fn.name} not listed on chainId ${chainId}, using likely fallback. For more info visit https://github.com/safe-global/safe-modules-deployments`
|
121
|
+
// );
|
122
|
+
// TODO: This is a cheeky hack. Real solution proposed in
|
123
|
+
// https://github.com/Mintbase/near-safe/issues/42
|
124
|
+
address = deployment.networkAddresses["11155111"];
|
106
125
|
}
|
107
|
-
return new ethers_1.ethers.Contract(
|
126
|
+
return new ethers_1.ethers.Contract(address, deployment.abi, provider);
|
108
127
|
}
|
package/dist/cjs/tx-manager.d.ts
CHANGED
@@ -1,36 +1,37 @@
|
|
1
|
-
import { ethers } from "ethers";
|
2
1
|
import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
|
3
2
|
import { Erc4337Bundler } from "./lib/bundler";
|
4
3
|
import { UserOperation, UserOperationReceipt } from "./types";
|
5
4
|
import { MetaTransaction } from "ethers-multisend";
|
6
5
|
import { ContractSuite } from "./lib/safe";
|
6
|
+
import { Address, Hash, Hex } from "viem";
|
7
7
|
export declare class TransactionManager {
|
8
|
-
readonly provider: ethers.JsonRpcProvider;
|
9
8
|
readonly nearAdapter: NearEthAdapter;
|
9
|
+
readonly address: Address;
|
10
|
+
readonly entryPointAddress: Address;
|
10
11
|
private safePack;
|
11
|
-
private bundler;
|
12
12
|
private setup;
|
13
|
-
|
13
|
+
private pimlicoKey;
|
14
14
|
private safeSaltNonce;
|
15
|
-
private
|
16
|
-
constructor(
|
15
|
+
private deployedChains;
|
16
|
+
constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
|
17
17
|
static create(config: {
|
18
|
-
|
19
|
-
erc4337BundlerUrl: string;
|
18
|
+
pimlicoKey: string;
|
20
19
|
nearAdapter: NearEthAdapter;
|
21
20
|
safeSaltNonce?: string;
|
22
21
|
}): Promise<TransactionManager>;
|
23
|
-
get
|
24
|
-
|
25
|
-
|
22
|
+
get mpcAddress(): Address;
|
23
|
+
getBalance(chainId: number): Promise<bigint>;
|
24
|
+
bundlerForChainId(chainId: number): Erc4337Bundler;
|
26
25
|
buildTransaction(args: {
|
26
|
+
chainId: number;
|
27
27
|
transactions: MetaTransaction[];
|
28
28
|
usePaymaster: boolean;
|
29
29
|
}): Promise<UserOperation>;
|
30
|
-
signTransaction(safeOpHash:
|
31
|
-
opHash(userOp: UserOperation): Promise<
|
30
|
+
signTransaction(safeOpHash: Hex): Promise<Hex>;
|
31
|
+
opHash(userOp: UserOperation): Promise<Hash>;
|
32
32
|
encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
|
33
|
-
executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
|
33
|
+
executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
|
34
|
+
safeDeployed(chainId: number): Promise<boolean>;
|
34
35
|
addOwnerTx(address: string): MetaTransaction;
|
35
|
-
safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
36
|
+
safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
36
37
|
}
|
package/dist/cjs/tx-manager.js
CHANGED
@@ -1,68 +1,67 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.TransactionManager = void 0;
|
4
|
-
const
|
4
|
+
const near_ca_1 = require("near-ca");
|
5
5
|
const bundler_1 = require("./lib/bundler");
|
6
6
|
const util_1 = require("./util");
|
7
|
-
const near_1 = require("./lib/near");
|
8
7
|
const ethers_multisend_1 = require("ethers-multisend");
|
9
8
|
const safe_1 = require("./lib/safe");
|
10
9
|
class TransactionManager {
|
11
|
-
constructor(
|
12
|
-
this.provider = provider;
|
10
|
+
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
|
13
11
|
this.nearAdapter = nearAdapter;
|
14
12
|
this.safePack = safePack;
|
15
|
-
this.
|
13
|
+
this.pimlicoKey = pimlicoKey;
|
14
|
+
this.entryPointAddress = entryPointAddress;
|
16
15
|
this.setup = setup;
|
17
|
-
this.
|
16
|
+
this.address = safeAddress;
|
18
17
|
this.safeSaltNonce = safeSaltNonce;
|
19
|
-
this.
|
18
|
+
this.deployedChains = new Set();
|
20
19
|
}
|
21
20
|
static async create(config) {
|
22
|
-
const
|
23
|
-
const
|
24
|
-
|
25
|
-
|
26
|
-
const bundler = new bundler_1.Erc4337Bundler(config.erc4337BundlerUrl, await safePack.entryPoint.getAddress());
|
27
|
-
const setup = await safePack.getSetup([adapter.address]);
|
21
|
+
const { nearAdapter, pimlicoKey } = config;
|
22
|
+
const safePack = await safe_1.ContractSuite.init();
|
23
|
+
console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
|
24
|
+
const setup = await safePack.getSetup([nearAdapter.address]);
|
28
25
|
const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
|
29
|
-
const
|
30
|
-
console.log(`Safe Address: ${safeAddress}
|
31
|
-
return new TransactionManager(
|
26
|
+
const entryPointAddress = (await safePack.entryPoint.getAddress());
|
27
|
+
console.log(`Safe Address: ${safeAddress}`);
|
28
|
+
return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, config.safeSaltNonce || "0");
|
32
29
|
}
|
33
|
-
get
|
34
|
-
return this._safeNotDeployed;
|
35
|
-
}
|
36
|
-
get nearEOA() {
|
30
|
+
get mpcAddress() {
|
37
31
|
return this.nearAdapter.address;
|
38
32
|
}
|
39
|
-
async
|
40
|
-
|
33
|
+
async getBalance(chainId) {
|
34
|
+
const provider = near_ca_1.Network.fromChainId(chainId).client;
|
35
|
+
return await provider.getBalance({ address: this.address });
|
36
|
+
}
|
37
|
+
bundlerForChainId(chainId) {
|
38
|
+
return new bundler_1.Erc4337Bundler(this.entryPointAddress, this.pimlicoKey, chainId);
|
41
39
|
}
|
42
40
|
async buildTransaction(args) {
|
43
|
-
const { transactions, usePaymaster } = args;
|
44
|
-
const
|
45
|
-
|
41
|
+
const { transactions, usePaymaster, chainId } = args;
|
42
|
+
const bundler = this.bundlerForChainId(chainId);
|
43
|
+
const gasFees = (await bundler.getGasPrice()).fast;
|
46
44
|
// Build Singular MetaTransaction for Multisend from transaction list.
|
47
45
|
if (transactions.length === 0) {
|
48
46
|
throw new Error("Empty transaction set!");
|
49
47
|
}
|
50
48
|
const tx = transactions.length > 1 ? (0, ethers_multisend_1.encodeMulti)(transactions) : transactions[0];
|
51
|
-
const
|
52
|
-
const
|
49
|
+
const safeNotDeployed = !(await this.safeDeployed(chainId));
|
50
|
+
const rawUserOp = await this.safePack.buildUserOp(tx, this.address, gasFees, this.setup, safeNotDeployed, this.safeSaltNonce);
|
51
|
+
const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed);
|
53
52
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
54
53
|
return unsignedUserOp;
|
55
54
|
}
|
56
55
|
async signTransaction(safeOpHash) {
|
57
|
-
const signature = await
|
56
|
+
const signature = await this.nearAdapter.sign(safeOpHash);
|
58
57
|
return (0, util_1.packSignature)(signature);
|
59
58
|
}
|
60
59
|
async opHash(userOp) {
|
61
60
|
return this.safePack.getOpHash(userOp);
|
62
61
|
}
|
63
62
|
async encodeSignRequest(tx) {
|
64
|
-
// TODO - This is sloppy and ignores ChainId!
|
65
63
|
const unsignedUserOp = await this.buildTransaction({
|
64
|
+
chainId: tx.chainId,
|
66
65
|
transactions: [
|
67
66
|
{
|
68
67
|
to: tx.to,
|
@@ -83,29 +82,40 @@ class TransactionManager {
|
|
83
82
|
evmMessage: JSON.stringify(unsignedUserOp),
|
84
83
|
};
|
85
84
|
}
|
86
|
-
async executeTransaction(userOp) {
|
87
|
-
const
|
85
|
+
async executeTransaction(chainId, userOp) {
|
86
|
+
const bundler = this.bundlerForChainId(chainId);
|
87
|
+
const userOpHash = await bundler.sendUserOperation(userOp);
|
88
88
|
console.log("UserOp Hash", userOpHash);
|
89
|
-
const userOpReceipt = await
|
89
|
+
const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
|
90
90
|
console.log("userOp Receipt", userOpReceipt);
|
91
91
|
// Update safeNotDeployed after the first transaction
|
92
|
-
this.
|
93
|
-
(await this.provider.getCode(this.safeAddress)) === "0x";
|
92
|
+
this.safeDeployed(chainId);
|
94
93
|
return userOpReceipt;
|
95
94
|
}
|
95
|
+
async safeDeployed(chainId) {
|
96
|
+
if (chainId in this.deployedChains) {
|
97
|
+
return true;
|
98
|
+
}
|
99
|
+
const provider = near_ca_1.Network.fromChainId(chainId).client;
|
100
|
+
const deployed = (await provider.getCode({ address: this.address })) !== "0x";
|
101
|
+
if (deployed) {
|
102
|
+
this.deployedChains.add(chainId);
|
103
|
+
}
|
104
|
+
return deployed;
|
105
|
+
}
|
96
106
|
addOwnerTx(address) {
|
97
107
|
return {
|
98
|
-
to: this.
|
108
|
+
to: this.address,
|
99
109
|
value: "0",
|
100
110
|
data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
|
101
111
|
};
|
102
112
|
}
|
103
|
-
async safeSufficientlyFunded(transactions, gasCost) {
|
113
|
+
async safeSufficientlyFunded(chainId, transactions, gasCost) {
|
104
114
|
const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
|
105
115
|
if (txValue + gasCost === 0n) {
|
106
116
|
return true;
|
107
117
|
}
|
108
|
-
const safeBalance = await this.
|
118
|
+
const safeBalance = await this.getBalance(chainId);
|
109
119
|
return txValue + gasCost < safeBalance;
|
110
120
|
}
|
111
121
|
}
|
package/dist/cjs/types.d.ts
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
import {
|
1
|
+
import { Address, Hex } from "viem";
|
2
2
|
export interface UnsignedUserOperation {
|
3
|
-
sender:
|
3
|
+
sender: Address;
|
4
4
|
nonce: string;
|
5
|
-
factory?:
|
6
|
-
factoryData?:
|
7
|
-
callData:
|
8
|
-
maxPriorityFeePerGas:
|
9
|
-
maxFeePerGas:
|
5
|
+
factory?: Address;
|
6
|
+
factoryData?: Hex;
|
7
|
+
callData: Hex;
|
8
|
+
maxPriorityFeePerGas: Hex;
|
9
|
+
maxFeePerGas: Hex;
|
10
10
|
}
|
11
11
|
/**
|
12
12
|
* Supported Representation of UserOperation for EntryPoint v0.7
|
13
13
|
*/
|
14
14
|
export interface UserOperation extends UnsignedUserOperation {
|
15
|
-
verificationGasLimit:
|
16
|
-
callGasLimit:
|
17
|
-
preVerificationGas:
|
18
|
-
signature?:
|
15
|
+
verificationGasLimit: Hex;
|
16
|
+
callGasLimit: Hex;
|
17
|
+
preVerificationGas: Hex;
|
18
|
+
signature?: Hex;
|
19
19
|
}
|
20
20
|
export interface PaymasterData {
|
21
|
-
paymaster?:
|
22
|
-
paymasterData?:
|
23
|
-
paymasterVerificationGasLimit?:
|
24
|
-
paymasterPostOpGasLimit?:
|
25
|
-
verificationGasLimit:
|
26
|
-
callGasLimit:
|
27
|
-
preVerificationGas:
|
21
|
+
paymaster?: Address;
|
22
|
+
paymasterData?: Hex;
|
23
|
+
paymasterVerificationGasLimit?: Hex;
|
24
|
+
paymasterPostOpGasLimit?: Hex;
|
25
|
+
verificationGasLimit: Hex;
|
26
|
+
callGasLimit: Hex;
|
27
|
+
preVerificationGas: Hex;
|
28
28
|
}
|
29
29
|
export interface UserOptions {
|
30
30
|
usePaymaster: boolean;
|
@@ -32,10 +32,7 @@ export interface UserOptions {
|
|
32
32
|
mpcContractId: string;
|
33
33
|
recoveryAddress?: string;
|
34
34
|
}
|
35
|
-
export type
|
36
|
-
export type Address = ethers.AddressLike;
|
37
|
-
export type Hex = `0x${string}`;
|
38
|
-
export type Hash = `0x${string}`;
|
35
|
+
export type TxStatus = "success" | "reverted";
|
39
36
|
interface Log {
|
40
37
|
logIndex: string;
|
41
38
|
transactionIndex: string;
|
@@ -49,19 +46,19 @@ interface Log {
|
|
49
46
|
interface Receipt {
|
50
47
|
transactionHash: Hex;
|
51
48
|
transactionIndex: bigint;
|
52
|
-
blockHash:
|
49
|
+
blockHash: Hex;
|
53
50
|
blockNumber: bigint;
|
54
51
|
from: Address;
|
55
|
-
to
|
52
|
+
to?: Address;
|
56
53
|
cumulativeGasUsed: bigint;
|
57
|
-
status:
|
54
|
+
status: TxStatus;
|
58
55
|
gasUsed: bigint;
|
59
|
-
contractAddress
|
56
|
+
contractAddress?: Address;
|
60
57
|
logsBloom: Hex;
|
61
58
|
effectiveGasPrice: bigint;
|
62
59
|
}
|
63
60
|
export type UserOperationReceipt = {
|
64
|
-
userOpHash:
|
61
|
+
userOpHash: Hex;
|
65
62
|
entryPoint: Address;
|
66
63
|
sender: Address;
|
67
64
|
nonce: bigint;
|
package/dist/cjs/util.d.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
import {
|
2
|
-
import { PaymasterData } from "./types";
|
1
|
+
import { PaymasterData } from "./types.js";
|
3
2
|
import { MetaTransaction } from "ethers-multisend";
|
4
|
-
|
5
|
-
export declare const
|
6
|
-
|
7
|
-
export declare
|
3
|
+
import { Hex } from "viem";
|
4
|
+
export declare const PLACEHOLDER_SIG: `0x${string}`;
|
5
|
+
type IntLike = Hex | bigint | string | number;
|
6
|
+
export declare const packGas: (hi: IntLike, lo: IntLike) => string;
|
7
|
+
export declare function packSignature(signature: `0x${string}`, validFrom?: number, validTo?: number): Hex;
|
8
|
+
export declare function packPaymasterData(data: PaymasterData): Hex;
|
8
9
|
export declare function containsValue(transactions: MetaTransaction[]): boolean;
|
10
|
+
export {};
|
package/dist/cjs/util.js
CHANGED
@@ -4,22 +4,22 @@ exports.packGas = exports.PLACEHOLDER_SIG = void 0;
|
|
4
4
|
exports.packSignature = packSignature;
|
5
5
|
exports.packPaymasterData = packPaymasterData;
|
6
6
|
exports.containsValue = containsValue;
|
7
|
-
const
|
8
|
-
exports.PLACEHOLDER_SIG =
|
9
|
-
const packGas = (hi, lo) =>
|
7
|
+
const viem_1 = require("viem");
|
8
|
+
exports.PLACEHOLDER_SIG = (0, viem_1.encodePacked)(["uint48", "uint48"], [0, 0]);
|
9
|
+
const packGas = (hi, lo) => (0, viem_1.encodePacked)(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
10
10
|
exports.packGas = packGas;
|
11
11
|
function packSignature(signature, validFrom = 0, validTo = 0) {
|
12
|
-
return
|
12
|
+
return (0, viem_1.encodePacked)(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]);
|
13
13
|
}
|
14
14
|
function packPaymasterData(data) {
|
15
|
-
return data.paymaster
|
16
|
-
?
|
15
|
+
return (data.paymaster
|
16
|
+
? (0, viem_1.concatHex)([
|
17
17
|
data.paymaster,
|
18
|
-
|
19
|
-
|
18
|
+
(0, viem_1.toHex)(BigInt(data.paymasterVerificationGasLimit || 0n), { size: 16 }),
|
19
|
+
(0, viem_1.toHex)(BigInt(data.paymasterPostOpGasLimit || 0n), { size: 16 }),
|
20
20
|
data.paymasterData || "0x",
|
21
|
-
])
|
22
|
-
: "0x";
|
21
|
+
])
|
22
|
+
: "0x");
|
23
23
|
}
|
24
24
|
function containsValue(transactions) {
|
25
25
|
return transactions.some((tx) => tx.value !== "0");
|
@@ -1,9 +1,12 @@
|
|
1
1
|
import { ethers } from "ethers";
|
2
|
-
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
|
2
|
+
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types.js";
|
3
3
|
export declare class Erc4337Bundler {
|
4
4
|
provider: ethers.JsonRpcProvider;
|
5
5
|
entryPointAddress: string;
|
6
|
-
|
6
|
+
apiKey: string;
|
7
|
+
chainId: number;
|
8
|
+
constructor(entryPointAddress: string, apiKey: string, chainId: number);
|
9
|
+
client(chainId: number): ethers.JsonRpcProvider;
|
7
10
|
getPaymasterData(rawUserOp: UnsignedUserOperation, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
|
8
11
|
sendUserOperation(userOp: UserOperation): Promise<string>;
|
9
12
|
getGasPrice(): Promise<GasPrices>;
|
package/dist/esm/lib/bundler.js
CHANGED
@@ -1,11 +1,23 @@
|
|
1
|
+
// TODO: Ethers dependency is only for Generic HTTP Provider
|
1
2
|
import { ethers } from "ethers";
|
2
|
-
import { PLACEHOLDER_SIG } from "../util";
|
3
|
+
import { PLACEHOLDER_SIG } from "../util.js";
|
4
|
+
import { toHex } from "viem";
|
5
|
+
function bundlerUrl(chainId, apikey) {
|
6
|
+
return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apikey}`;
|
7
|
+
}
|
3
8
|
export class Erc4337Bundler {
|
4
9
|
provider;
|
5
10
|
entryPointAddress;
|
6
|
-
|
11
|
+
apiKey;
|
12
|
+
chainId;
|
13
|
+
constructor(entryPointAddress, apiKey, chainId) {
|
7
14
|
this.entryPointAddress = entryPointAddress;
|
8
|
-
this.
|
15
|
+
this.apiKey = apiKey;
|
16
|
+
this.chainId = chainId;
|
17
|
+
this.provider = new ethers.JsonRpcProvider(bundlerUrl(chainId, apiKey));
|
18
|
+
}
|
19
|
+
client(chainId) {
|
20
|
+
return new ethers.JsonRpcProvider(bundlerUrl(chainId, this.apiKey));
|
9
21
|
}
|
10
22
|
async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
|
11
23
|
// TODO: Keep this option out of the bundler
|
@@ -51,8 +63,8 @@ export class Erc4337Bundler {
|
|
51
63
|
// TODO(bh2smith) Should probably get reasonable estimates here:
|
52
64
|
const defaultPaymasterData = (safeNotDeployed) => {
|
53
65
|
return {
|
54
|
-
verificationGasLimit:
|
55
|
-
callGasLimit:
|
56
|
-
preVerificationGas:
|
66
|
+
verificationGasLimit: toHex(safeNotDeployed ? 500000 : 100000),
|
67
|
+
callGasLimit: toHex(100000),
|
68
|
+
preVerificationGas: toHex(100000),
|
57
69
|
};
|
58
70
|
};
|
package/dist/esm/lib/safe.d.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { ethers } from "ethers";
|
2
2
|
import { GasPrice, UnsignedUserOperation, UserOperation } from "../types";
|
3
3
|
import { MetaTransaction } from "ethers-multisend";
|
4
|
+
import { Address, Hash, Hex } from "viem";
|
4
5
|
/**
|
5
6
|
* All contracts used in account creation & execution
|
6
7
|
*/
|
@@ -12,13 +13,13 @@ export declare class ContractSuite {
|
|
12
13
|
moduleSetup: ethers.Contract;
|
13
14
|
entryPoint: ethers.Contract;
|
14
15
|
constructor(provider: ethers.JsonRpcProvider, singleton: ethers.Contract, proxyFactory: ethers.Contract, m4337: ethers.Contract, moduleSetup: ethers.Contract, entryPoint: ethers.Contract);
|
15
|
-
static init(
|
16
|
-
addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<
|
17
|
-
getSetup(owners: string[]): Promise<
|
18
|
-
getOpHash(unsignedUserOp: UserOperation): Promise<
|
16
|
+
static init(): Promise<ContractSuite>;
|
17
|
+
addressForSetup(setup: ethers.BytesLike, saltNonce?: string): Promise<Address>;
|
18
|
+
getSetup(owners: string[]): Promise<Hex>;
|
19
|
+
getOpHash(unsignedUserOp: UserOperation): Promise<Hash>;
|
19
20
|
factoryDataForSetup(safeNotDeployed: boolean, setup: string, safeSaltNonce: string): {
|
20
|
-
factory?:
|
21
|
-
factoryData?:
|
21
|
+
factory?: Address;
|
22
|
+
factoryData?: Hex;
|
22
23
|
};
|
23
|
-
buildUserOp(txData: MetaTransaction, safeAddress:
|
24
|
+
buildUserOp(txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
|
24
25
|
}
|
package/dist/esm/lib/safe.js
CHANGED
@@ -20,9 +20,13 @@ export class ContractSuite {
|
|
20
20
|
this.moduleSetup = moduleSetup;
|
21
21
|
this.entryPoint = entryPoint;
|
22
22
|
}
|
23
|
-
static async init(
|
23
|
+
static async init() {
|
24
|
+
// TODO - this is a cheeky hack.
|
25
|
+
const provider = new ethers.JsonRpcProvider("https://rpc2.sepolia.org");
|
24
26
|
const safeDeployment = (fn) => getDeployment(fn, { provider, version: "1.4.1" });
|
25
|
-
const m4337Deployment = (fn) =>
|
27
|
+
const m4337Deployment = async (fn) => {
|
28
|
+
return getDeployment(fn, { provider, version: "0.3.0" });
|
29
|
+
};
|
26
30
|
// Need this first to get entryPoint address
|
27
31
|
const m4337 = await m4337Deployment(getSafe4337ModuleDeployment);
|
28
32
|
const [singleton, proxyFactory, moduleSetup, supportedEntryPoint] = await Promise.all([
|
@@ -32,6 +36,13 @@ export class ContractSuite {
|
|
32
36
|
m4337.SUPPORTED_ENTRYPOINT(),
|
33
37
|
]);
|
34
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(),
|
45
|
+
});
|
35
46
|
return new ContractSuite(provider, singleton, proxyFactory, m4337, moduleSetup, entryPoint);
|
36
47
|
}
|
37
48
|
async addressForSetup(setup, saltNonce) {
|
@@ -61,16 +72,15 @@ export class ContractSuite {
|
|
61
72
|
]);
|
62
73
|
return setup;
|
63
74
|
}
|
64
|
-
async getOpHash(unsignedUserOp
|
65
|
-
|
66
|
-
) {
|
75
|
+
async getOpHash(unsignedUserOp) {
|
76
|
+
const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
|
67
77
|
return this.m4337.getOperationHash({
|
68
78
|
...unsignedUserOp,
|
69
|
-
initCode:
|
70
|
-
? ethers.solidityPacked(["address", "bytes"], [
|
79
|
+
initCode: factory
|
80
|
+
? ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
|
71
81
|
: "0x",
|
72
|
-
accountGasLimits: packGas(
|
73
|
-
gasFees: packGas(
|
82
|
+
accountGasLimits: packGas(verificationGasLimit, callGasLimit),
|
83
|
+
gasFees: packGas(maxPriorityFeePerGas, maxFeePerGas),
|
74
84
|
paymasterAndData: packPaymasterData(unsignedUserOp),
|
75
85
|
signature: PLACEHOLDER_SIG,
|
76
86
|
});
|
@@ -103,8 +113,17 @@ export class ContractSuite {
|
|
103
113
|
async function getDeployment(fn, { provider, version }) {
|
104
114
|
const { chainId } = await provider.getNetwork();
|
105
115
|
const deployment = fn({ version });
|
106
|
-
if (!deployment
|
107
|
-
throw new Error(`Deployment not found for version ${version}
|
116
|
+
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"];
|
108
127
|
}
|
109
|
-
return new ethers.Contract(
|
128
|
+
return new ethers.Contract(address, deployment.abi, provider);
|
110
129
|
}
|
package/dist/esm/tx-manager.d.ts
CHANGED
@@ -1,36 +1,37 @@
|
|
1
|
-
import { ethers } from "ethers";
|
2
1
|
import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
|
3
2
|
import { Erc4337Bundler } from "./lib/bundler";
|
4
3
|
import { UserOperation, UserOperationReceipt } from "./types";
|
5
4
|
import { MetaTransaction } from "ethers-multisend";
|
6
5
|
import { ContractSuite } from "./lib/safe";
|
6
|
+
import { Address, Hash, Hex } from "viem";
|
7
7
|
export declare class TransactionManager {
|
8
|
-
readonly provider: ethers.JsonRpcProvider;
|
9
8
|
readonly nearAdapter: NearEthAdapter;
|
9
|
+
readonly address: Address;
|
10
|
+
readonly entryPointAddress: Address;
|
10
11
|
private safePack;
|
11
|
-
private bundler;
|
12
12
|
private setup;
|
13
|
-
|
13
|
+
private pimlicoKey;
|
14
14
|
private safeSaltNonce;
|
15
|
-
private
|
16
|
-
constructor(
|
15
|
+
private deployedChains;
|
16
|
+
constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
|
17
17
|
static create(config: {
|
18
|
-
|
19
|
-
erc4337BundlerUrl: string;
|
18
|
+
pimlicoKey: string;
|
20
19
|
nearAdapter: NearEthAdapter;
|
21
20
|
safeSaltNonce?: string;
|
22
21
|
}): Promise<TransactionManager>;
|
23
|
-
get
|
24
|
-
|
25
|
-
|
22
|
+
get mpcAddress(): Address;
|
23
|
+
getBalance(chainId: number): Promise<bigint>;
|
24
|
+
bundlerForChainId(chainId: number): Erc4337Bundler;
|
26
25
|
buildTransaction(args: {
|
26
|
+
chainId: number;
|
27
27
|
transactions: MetaTransaction[];
|
28
28
|
usePaymaster: boolean;
|
29
29
|
}): Promise<UserOperation>;
|
30
|
-
signTransaction(safeOpHash:
|
31
|
-
opHash(userOp: UserOperation): Promise<
|
30
|
+
signTransaction(safeOpHash: Hex): Promise<Hex>;
|
31
|
+
opHash(userOp: UserOperation): Promise<Hash>;
|
32
32
|
encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
|
33
|
-
executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
|
33
|
+
executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
|
34
|
+
safeDeployed(chainId: number): Promise<boolean>;
|
34
35
|
addOwnerTx(address: string): MetaTransaction;
|
35
|
-
safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
36
|
+
safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
36
37
|
}
|
package/dist/esm/tx-manager.js
CHANGED
@@ -1,73 +1,72 @@
|
|
1
|
-
import {
|
1
|
+
import { Network } from "near-ca";
|
2
2
|
import { Erc4337Bundler } from "./lib/bundler";
|
3
3
|
import { packSignature } from "./util";
|
4
|
-
import { getNearSignature } from "./lib/near";
|
5
4
|
import { encodeMulti } from "ethers-multisend";
|
6
5
|
import { ContractSuite } from "./lib/safe";
|
7
6
|
export class TransactionManager {
|
8
|
-
provider;
|
9
7
|
nearAdapter;
|
8
|
+
address;
|
9
|
+
entryPointAddress;
|
10
10
|
safePack;
|
11
|
-
bundler;
|
12
11
|
setup;
|
13
|
-
|
12
|
+
pimlicoKey;
|
14
13
|
safeSaltNonce;
|
15
|
-
|
16
|
-
constructor(
|
17
|
-
this.provider = provider;
|
14
|
+
deployedChains;
|
15
|
+
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
|
18
16
|
this.nearAdapter = nearAdapter;
|
19
17
|
this.safePack = safePack;
|
20
|
-
this.
|
18
|
+
this.pimlicoKey = pimlicoKey;
|
19
|
+
this.entryPointAddress = entryPointAddress;
|
21
20
|
this.setup = setup;
|
22
|
-
this.
|
21
|
+
this.address = safeAddress;
|
23
22
|
this.safeSaltNonce = safeSaltNonce;
|
24
|
-
this.
|
23
|
+
this.deployedChains = new Set();
|
25
24
|
}
|
26
25
|
static async create(config) {
|
27
|
-
const
|
28
|
-
const
|
29
|
-
|
30
|
-
|
31
|
-
const bundler = new Erc4337Bundler(config.erc4337BundlerUrl, await safePack.entryPoint.getAddress());
|
32
|
-
const setup = await safePack.getSetup([adapter.address]);
|
26
|
+
const { nearAdapter, pimlicoKey } = config;
|
27
|
+
const safePack = await ContractSuite.init();
|
28
|
+
console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
|
29
|
+
const setup = await safePack.getSetup([nearAdapter.address]);
|
33
30
|
const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
|
34
|
-
const
|
35
|
-
console.log(`Safe Address: ${safeAddress}
|
36
|
-
return new TransactionManager(
|
31
|
+
const entryPointAddress = (await safePack.entryPoint.getAddress());
|
32
|
+
console.log(`Safe Address: ${safeAddress}`);
|
33
|
+
return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, config.safeSaltNonce || "0");
|
37
34
|
}
|
38
|
-
get
|
39
|
-
return this._safeNotDeployed;
|
40
|
-
}
|
41
|
-
get nearEOA() {
|
35
|
+
get mpcAddress() {
|
42
36
|
return this.nearAdapter.address;
|
43
37
|
}
|
44
|
-
async
|
45
|
-
|
38
|
+
async getBalance(chainId) {
|
39
|
+
const provider = Network.fromChainId(chainId).client;
|
40
|
+
return await provider.getBalance({ address: this.address });
|
41
|
+
}
|
42
|
+
bundlerForChainId(chainId) {
|
43
|
+
return new Erc4337Bundler(this.entryPointAddress, this.pimlicoKey, chainId);
|
46
44
|
}
|
47
45
|
async buildTransaction(args) {
|
48
|
-
const { transactions, usePaymaster } = args;
|
49
|
-
const
|
50
|
-
|
46
|
+
const { transactions, usePaymaster, chainId } = args;
|
47
|
+
const bundler = this.bundlerForChainId(chainId);
|
48
|
+
const gasFees = (await bundler.getGasPrice()).fast;
|
51
49
|
// Build Singular MetaTransaction for Multisend from transaction list.
|
52
50
|
if (transactions.length === 0) {
|
53
51
|
throw new Error("Empty transaction set!");
|
54
52
|
}
|
55
53
|
const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
|
56
|
-
const
|
57
|
-
const
|
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);
|
58
57
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
59
58
|
return unsignedUserOp;
|
60
59
|
}
|
61
60
|
async signTransaction(safeOpHash) {
|
62
|
-
const signature = await
|
61
|
+
const signature = await this.nearAdapter.sign(safeOpHash);
|
63
62
|
return packSignature(signature);
|
64
63
|
}
|
65
64
|
async opHash(userOp) {
|
66
65
|
return this.safePack.getOpHash(userOp);
|
67
66
|
}
|
68
67
|
async encodeSignRequest(tx) {
|
69
|
-
// TODO - This is sloppy and ignores ChainId!
|
70
68
|
const unsignedUserOp = await this.buildTransaction({
|
69
|
+
chainId: tx.chainId,
|
71
70
|
transactions: [
|
72
71
|
{
|
73
72
|
to: tx.to,
|
@@ -88,29 +87,40 @@ export class TransactionManager {
|
|
88
87
|
evmMessage: JSON.stringify(unsignedUserOp),
|
89
88
|
};
|
90
89
|
}
|
91
|
-
async executeTransaction(userOp) {
|
92
|
-
const
|
90
|
+
async executeTransaction(chainId, userOp) {
|
91
|
+
const bundler = this.bundlerForChainId(chainId);
|
92
|
+
const userOpHash = await bundler.sendUserOperation(userOp);
|
93
93
|
console.log("UserOp Hash", userOpHash);
|
94
|
-
const userOpReceipt = await
|
94
|
+
const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
|
95
95
|
console.log("userOp Receipt", userOpReceipt);
|
96
96
|
// Update safeNotDeployed after the first transaction
|
97
|
-
this.
|
98
|
-
(await this.provider.getCode(this.safeAddress)) === "0x";
|
97
|
+
this.safeDeployed(chainId);
|
99
98
|
return userOpReceipt;
|
100
99
|
}
|
100
|
+
async safeDeployed(chainId) {
|
101
|
+
if (chainId in this.deployedChains) {
|
102
|
+
return true;
|
103
|
+
}
|
104
|
+
const provider = Network.fromChainId(chainId).client;
|
105
|
+
const deployed = (await provider.getCode({ address: this.address })) !== "0x";
|
106
|
+
if (deployed) {
|
107
|
+
this.deployedChains.add(chainId);
|
108
|
+
}
|
109
|
+
return deployed;
|
110
|
+
}
|
101
111
|
addOwnerTx(address) {
|
102
112
|
return {
|
103
|
-
to: this.
|
113
|
+
to: this.address,
|
104
114
|
value: "0",
|
105
115
|
data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
|
106
116
|
};
|
107
117
|
}
|
108
|
-
async safeSufficientlyFunded(transactions, gasCost) {
|
118
|
+
async safeSufficientlyFunded(chainId, transactions, gasCost) {
|
109
119
|
const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
|
110
120
|
if (txValue + gasCost === 0n) {
|
111
121
|
return true;
|
112
122
|
}
|
113
|
-
const safeBalance = await this.
|
123
|
+
const safeBalance = await this.getBalance(chainId);
|
114
124
|
return txValue + gasCost < safeBalance;
|
115
125
|
}
|
116
126
|
}
|
package/dist/esm/types.d.ts
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
import {
|
1
|
+
import { Address, Hex } from "viem";
|
2
2
|
export interface UnsignedUserOperation {
|
3
|
-
sender:
|
3
|
+
sender: Address;
|
4
4
|
nonce: string;
|
5
|
-
factory?:
|
6
|
-
factoryData?:
|
7
|
-
callData:
|
8
|
-
maxPriorityFeePerGas:
|
9
|
-
maxFeePerGas:
|
5
|
+
factory?: Address;
|
6
|
+
factoryData?: Hex;
|
7
|
+
callData: Hex;
|
8
|
+
maxPriorityFeePerGas: Hex;
|
9
|
+
maxFeePerGas: Hex;
|
10
10
|
}
|
11
11
|
/**
|
12
12
|
* Supported Representation of UserOperation for EntryPoint v0.7
|
13
13
|
*/
|
14
14
|
export interface UserOperation extends UnsignedUserOperation {
|
15
|
-
verificationGasLimit:
|
16
|
-
callGasLimit:
|
17
|
-
preVerificationGas:
|
18
|
-
signature?:
|
15
|
+
verificationGasLimit: Hex;
|
16
|
+
callGasLimit: Hex;
|
17
|
+
preVerificationGas: Hex;
|
18
|
+
signature?: Hex;
|
19
19
|
}
|
20
20
|
export interface PaymasterData {
|
21
|
-
paymaster?:
|
22
|
-
paymasterData?:
|
23
|
-
paymasterVerificationGasLimit?:
|
24
|
-
paymasterPostOpGasLimit?:
|
25
|
-
verificationGasLimit:
|
26
|
-
callGasLimit:
|
27
|
-
preVerificationGas:
|
21
|
+
paymaster?: Address;
|
22
|
+
paymasterData?: Hex;
|
23
|
+
paymasterVerificationGasLimit?: Hex;
|
24
|
+
paymasterPostOpGasLimit?: Hex;
|
25
|
+
verificationGasLimit: Hex;
|
26
|
+
callGasLimit: Hex;
|
27
|
+
preVerificationGas: Hex;
|
28
28
|
}
|
29
29
|
export interface UserOptions {
|
30
30
|
usePaymaster: boolean;
|
@@ -32,10 +32,7 @@ export interface UserOptions {
|
|
32
32
|
mpcContractId: string;
|
33
33
|
recoveryAddress?: string;
|
34
34
|
}
|
35
|
-
export type
|
36
|
-
export type Address = ethers.AddressLike;
|
37
|
-
export type Hex = `0x${string}`;
|
38
|
-
export type Hash = `0x${string}`;
|
35
|
+
export type TxStatus = "success" | "reverted";
|
39
36
|
interface Log {
|
40
37
|
logIndex: string;
|
41
38
|
transactionIndex: string;
|
@@ -49,19 +46,19 @@ interface Log {
|
|
49
46
|
interface Receipt {
|
50
47
|
transactionHash: Hex;
|
51
48
|
transactionIndex: bigint;
|
52
|
-
blockHash:
|
49
|
+
blockHash: Hex;
|
53
50
|
blockNumber: bigint;
|
54
51
|
from: Address;
|
55
|
-
to
|
52
|
+
to?: Address;
|
56
53
|
cumulativeGasUsed: bigint;
|
57
|
-
status:
|
54
|
+
status: TxStatus;
|
58
55
|
gasUsed: bigint;
|
59
|
-
contractAddress
|
56
|
+
contractAddress?: Address;
|
60
57
|
logsBloom: Hex;
|
61
58
|
effectiveGasPrice: bigint;
|
62
59
|
}
|
63
60
|
export type UserOperationReceipt = {
|
64
|
-
userOpHash:
|
61
|
+
userOpHash: Hex;
|
65
62
|
entryPoint: Address;
|
66
63
|
sender: Address;
|
67
64
|
nonce: bigint;
|
package/dist/esm/util.d.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
import {
|
2
|
-
import { PaymasterData } from "./types";
|
1
|
+
import { PaymasterData } from "./types.js";
|
3
2
|
import { MetaTransaction } from "ethers-multisend";
|
4
|
-
|
5
|
-
export declare const
|
6
|
-
|
7
|
-
export declare
|
3
|
+
import { Hex } from "viem";
|
4
|
+
export declare const PLACEHOLDER_SIG: `0x${string}`;
|
5
|
+
type IntLike = Hex | bigint | string | number;
|
6
|
+
export declare const packGas: (hi: IntLike, lo: IntLike) => string;
|
7
|
+
export declare function packSignature(signature: `0x${string}`, validFrom?: number, validTo?: number): Hex;
|
8
|
+
export declare function packPaymasterData(data: PaymasterData): Hex;
|
8
9
|
export declare function containsValue(transactions: MetaTransaction[]): boolean;
|
10
|
+
export {};
|
package/dist/esm/util.js
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
import {
|
2
|
-
export const PLACEHOLDER_SIG =
|
3
|
-
export const packGas = (hi, lo) =>
|
1
|
+
import { concatHex, encodePacked, toHex } from "viem";
|
2
|
+
export const PLACEHOLDER_SIG = encodePacked(["uint48", "uint48"], [0, 0]);
|
3
|
+
export const packGas = (hi, lo) => encodePacked(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
4
4
|
export function packSignature(signature, validFrom = 0, validTo = 0) {
|
5
|
-
return
|
5
|
+
return encodePacked(["uint48", "uint48", "bytes"], [validFrom, validTo, signature]);
|
6
6
|
}
|
7
7
|
export function packPaymasterData(data) {
|
8
|
-
return data.paymaster
|
9
|
-
?
|
8
|
+
return (data.paymaster
|
9
|
+
? concatHex([
|
10
10
|
data.paymaster,
|
11
|
-
|
12
|
-
|
11
|
+
toHex(BigInt(data.paymasterVerificationGasLimit || 0n), { size: 16 }),
|
12
|
+
toHex(BigInt(data.paymasterPostOpGasLimit || 0n), { size: 16 }),
|
13
13
|
data.paymasterData || "0x",
|
14
|
-
])
|
15
|
-
: "0x";
|
14
|
+
])
|
15
|
+
: "0x");
|
16
16
|
}
|
17
17
|
export function containsValue(transactions) {
|
18
18
|
return transactions.some((tx) => tx.value !== "0");
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "near-safe",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.2.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",
|
@@ -35,6 +35,7 @@
|
|
35
35
|
"start": "yarn example",
|
36
36
|
"example": "tsx examples/send-tx.ts",
|
37
37
|
"lint": "eslint . --ignore-pattern dist/",
|
38
|
+
"test": "jest",
|
38
39
|
"fmt": "prettier --write '{src,examples,tests}/**/*.{js,jsx,ts,tsx}'",
|
39
40
|
"all": "yarn fmt && yarn lint && yarn build"
|
40
41
|
},
|
@@ -44,16 +45,21 @@
|
|
44
45
|
"ethers": "^6.13.1",
|
45
46
|
"ethers-multisend": "^3.1.0",
|
46
47
|
"near-api-js": "^5.0.0",
|
47
|
-
"near-ca": "^0.5.2"
|
48
|
+
"near-ca": "^0.5.2",
|
49
|
+
"viem": "^2.16.5",
|
50
|
+
"yargs": "^17.7.2"
|
48
51
|
},
|
49
52
|
"devDependencies": {
|
53
|
+
"@types/jest": "^29.5.12",
|
50
54
|
"@types/node": "^22.3.0",
|
51
55
|
"@types/yargs": "^17.0.32",
|
52
56
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
53
57
|
"@typescript-eslint/parser": "^8.1.0",
|
54
58
|
"dotenv": "^16.4.5",
|
55
59
|
"eslint": "^9.6.0",
|
60
|
+
"jest": "^29.7.0",
|
56
61
|
"prettier": "^3.3.2",
|
62
|
+
"ts-jest": "^29.1.5",
|
57
63
|
"tsx": "^4.16.0",
|
58
64
|
"typescript": "^5.5.2",
|
59
65
|
"yargs": "^17.7.2"
|
package/dist/cjs/lib/near.d.ts
DELETED
package/dist/cjs/lib/near.js
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.getNearSignature = getNearSignature;
|
4
|
-
const ethers_1 = require("ethers");
|
5
|
-
async function getNearSignature(adapter, hash) {
|
6
|
-
const viemHash = typeof hash === "string" ? hash : hash;
|
7
|
-
// MPC Contract produces two possible signatures.
|
8
|
-
const signature = await adapter.sign(viemHash);
|
9
|
-
if (ethers_1.ethers.recoverAddress(hash, signature).toLocaleLowerCase() ===
|
10
|
-
adapter.address.toLocaleLowerCase()) {
|
11
|
-
return signature;
|
12
|
-
}
|
13
|
-
throw new Error("Invalid signature!");
|
14
|
-
}
|
package/dist/esm/lib/near.d.ts
DELETED
package/dist/esm/lib/near.js
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
import { ethers } from "ethers";
|
2
|
-
export async function getNearSignature(adapter, hash) {
|
3
|
-
const viemHash = typeof hash === "string" ? hash : hash;
|
4
|
-
// MPC Contract produces two possible signatures.
|
5
|
-
const signature = await adapter.sign(viemHash);
|
6
|
-
if (ethers.recoverAddress(hash, signature).toLocaleLowerCase() ===
|
7
|
-
adapter.address.toLocaleLowerCase()) {
|
8
|
-
return signature;
|
9
|
-
}
|
10
|
-
throw new Error("Invalid signature!");
|
11
|
-
}
|