near-safe 0.1.1 → 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/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 +21 -17
- package/dist/cjs/tx-manager.d.ts +15 -20
- package/dist/cjs/tx-manager.js +40 -44
- 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 +21 -17
- package/dist/esm/tx-manager.d.ts +15 -20
- package/dist/esm/tx-manager.js +44 -49
- 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
@@ -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,16 +17,12 @@ 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
24
|
const m4337Deployment = async (fn) => {
|
23
|
-
|
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
|
-
}
|
25
|
+
return getDeployment(fn, { provider, version: "0.3.0" });
|
30
26
|
};
|
31
27
|
// Need this first to get entryPoint address
|
32
28
|
const m4337 = await m4337Deployment(safe_modules_deployments_1.getSafe4337ModuleDeployment);
|
@@ -73,16 +69,15 @@ class ContractSuite {
|
|
73
69
|
]);
|
74
70
|
return setup;
|
75
71
|
}
|
76
|
-
async getOpHash(unsignedUserOp
|
77
|
-
|
78
|
-
) {
|
72
|
+
async getOpHash(unsignedUserOp) {
|
73
|
+
const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
|
79
74
|
return this.m4337.getOperationHash({
|
80
75
|
...unsignedUserOp,
|
81
|
-
initCode:
|
82
|
-
? ethers_1.ethers.solidityPacked(["address", "bytes"], [
|
76
|
+
initCode: factory
|
77
|
+
? ethers_1.ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
|
83
78
|
: "0x",
|
84
|
-
accountGasLimits: (0, util_1.packGas)(
|
85
|
-
gasFees: (0, util_1.packGas)(
|
79
|
+
accountGasLimits: (0, util_1.packGas)(verificationGasLimit, callGasLimit),
|
80
|
+
gasFees: (0, util_1.packGas)(maxPriorityFeePerGas, maxFeePerGas),
|
86
81
|
paymasterAndData: (0, util_1.packPaymasterData)(unsignedUserOp),
|
87
82
|
signature: util_1.PLACEHOLDER_SIG,
|
88
83
|
});
|
@@ -116,8 +111,17 @@ exports.ContractSuite = ContractSuite;
|
|
116
111
|
async function getDeployment(fn, { provider, version }) {
|
117
112
|
const { chainId } = await provider.getNetwork();
|
118
113
|
const deployment = fn({ version });
|
119
|
-
if (!deployment
|
114
|
+
if (!deployment) {
|
120
115
|
throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
|
121
116
|
}
|
122
|
-
|
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"];
|
125
|
+
}
|
126
|
+
return new ethers_1.ethers.Contract(address, deployment.abi, provider);
|
123
127
|
}
|
package/dist/cjs/tx-manager.d.ts
CHANGED
@@ -1,42 +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
|
-
|
14
|
-
readonly chainId: number;
|
13
|
+
private pimlicoKey;
|
15
14
|
private safeSaltNonce;
|
16
|
-
private
|
17
|
-
constructor(
|
15
|
+
private deployedChains;
|
16
|
+
constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
|
18
17
|
static create(config: {
|
19
|
-
ethRpc: string;
|
20
18
|
pimlicoKey: string;
|
21
19
|
nearAdapter: NearEthAdapter;
|
22
20
|
safeSaltNonce?: string;
|
23
21
|
}): Promise<TransactionManager>;
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
pimlicoKey: string;
|
28
|
-
}): Promise<TransactionManager>;
|
29
|
-
get safeNotDeployed(): boolean;
|
30
|
-
get mpcAddress(): `0x${string}`;
|
31
|
-
getSafeBalance(): Promise<bigint>;
|
22
|
+
get mpcAddress(): Address;
|
23
|
+
getBalance(chainId: number): Promise<bigint>;
|
24
|
+
bundlerForChainId(chainId: number): Erc4337Bundler;
|
32
25
|
buildTransaction(args: {
|
26
|
+
chainId: number;
|
33
27
|
transactions: MetaTransaction[];
|
34
28
|
usePaymaster: boolean;
|
35
29
|
}): Promise<UserOperation>;
|
36
|
-
signTransaction(safeOpHash:
|
37
|
-
opHash(userOp: UserOperation): Promise<
|
30
|
+
signTransaction(safeOpHash: Hex): Promise<Hex>;
|
31
|
+
opHash(userOp: UserOperation): Promise<Hash>;
|
38
32
|
encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
|
39
|
-
executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
|
33
|
+
executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
|
34
|
+
safeDeployed(chainId: number): Promise<boolean>;
|
40
35
|
addOwnerTx(address: string): MetaTransaction;
|
41
|
-
safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
36
|
+
safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
42
37
|
}
|
package/dist/cjs/tx-manager.js
CHANGED
@@ -1,82 +1,67 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.TransactionManager = void 0;
|
4
|
-
const ethers_1 = require("ethers");
|
5
4
|
const near_ca_1 = require("near-ca");
|
6
5
|
const bundler_1 = require("./lib/bundler");
|
7
6
|
const util_1 = require("./util");
|
8
|
-
const near_1 = require("./lib/near");
|
9
7
|
const ethers_multisend_1 = require("ethers-multisend");
|
10
8
|
const safe_1 = require("./lib/safe");
|
11
9
|
class TransactionManager {
|
12
|
-
constructor(
|
13
|
-
this.provider = provider;
|
10
|
+
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
|
14
11
|
this.nearAdapter = nearAdapter;
|
15
12
|
this.safePack = safePack;
|
16
|
-
this.
|
13
|
+
this.pimlicoKey = pimlicoKey;
|
14
|
+
this.entryPointAddress = entryPointAddress;
|
17
15
|
this.setup = setup;
|
18
|
-
this.chainId = chainId;
|
19
16
|
this.address = safeAddress;
|
20
17
|
this.safeSaltNonce = safeSaltNonce;
|
21
|
-
this.
|
18
|
+
this.deployedChains = new Set();
|
22
19
|
}
|
23
20
|
static async create(config) {
|
24
21
|
const { nearAdapter, pimlicoKey } = config;
|
25
|
-
const
|
26
|
-
const chainId = (await provider.getNetwork()).chainId;
|
27
|
-
const safePack = await safe_1.ContractSuite.init(provider);
|
22
|
+
const safePack = await safe_1.ContractSuite.init();
|
28
23
|
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
24
|
const setup = await safePack.getSetup([nearAdapter.address]);
|
31
25
|
const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
|
32
|
-
const
|
33
|
-
console.log(`Safe Address: ${safeAddress}
|
34
|
-
return new TransactionManager(
|
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
|
-
});
|
43
|
-
}
|
44
|
-
get safeNotDeployed() {
|
45
|
-
return this._safeNotDeployed;
|
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");
|
46
29
|
}
|
47
30
|
get mpcAddress() {
|
48
31
|
return this.nearAdapter.address;
|
49
32
|
}
|
50
|
-
async
|
51
|
-
|
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);
|
52
39
|
}
|
53
40
|
async buildTransaction(args) {
|
54
|
-
const { transactions, usePaymaster } = args;
|
55
|
-
const
|
56
|
-
|
41
|
+
const { transactions, usePaymaster, chainId } = args;
|
42
|
+
const bundler = this.bundlerForChainId(chainId);
|
43
|
+
const gasFees = (await bundler.getGasPrice()).fast;
|
57
44
|
// Build Singular MetaTransaction for Multisend from transaction list.
|
58
45
|
if (transactions.length === 0) {
|
59
46
|
throw new Error("Empty transaction set!");
|
60
47
|
}
|
61
48
|
const tx = transactions.length > 1 ? (0, ethers_multisend_1.encodeMulti)(transactions) : transactions[0];
|
62
|
-
const
|
63
|
-
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);
|
64
52
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
65
53
|
return unsignedUserOp;
|
66
54
|
}
|
67
55
|
async signTransaction(safeOpHash) {
|
68
|
-
const signature = await
|
56
|
+
const signature = await this.nearAdapter.sign(safeOpHash);
|
69
57
|
return (0, util_1.packSignature)(signature);
|
70
58
|
}
|
71
59
|
async opHash(userOp) {
|
72
60
|
return this.safePack.getOpHash(userOp);
|
73
61
|
}
|
74
62
|
async encodeSignRequest(tx) {
|
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
|
-
}
|
79
63
|
const unsignedUserOp = await this.buildTransaction({
|
64
|
+
chainId: tx.chainId,
|
80
65
|
transactions: [
|
81
66
|
{
|
82
67
|
to: tx.to,
|
@@ -97,16 +82,27 @@ class TransactionManager {
|
|
97
82
|
evmMessage: JSON.stringify(unsignedUserOp),
|
98
83
|
};
|
99
84
|
}
|
100
|
-
async executeTransaction(userOp) {
|
101
|
-
const
|
85
|
+
async executeTransaction(chainId, userOp) {
|
86
|
+
const bundler = this.bundlerForChainId(chainId);
|
87
|
+
const userOpHash = await bundler.sendUserOperation(userOp);
|
102
88
|
console.log("UserOp Hash", userOpHash);
|
103
|
-
const userOpReceipt = await
|
89
|
+
const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
|
104
90
|
console.log("userOp Receipt", userOpReceipt);
|
105
91
|
// Update safeNotDeployed after the first transaction
|
106
|
-
this.
|
107
|
-
(await this.provider.getCode(this.address)) === "0x";
|
92
|
+
this.safeDeployed(chainId);
|
108
93
|
return userOpReceipt;
|
109
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
|
+
}
|
110
106
|
addOwnerTx(address) {
|
111
107
|
return {
|
112
108
|
to: this.address,
|
@@ -114,12 +110,12 @@ class TransactionManager {
|
|
114
110
|
data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
|
115
111
|
};
|
116
112
|
}
|
117
|
-
async safeSufficientlyFunded(transactions, gasCost) {
|
113
|
+
async safeSufficientlyFunded(chainId, transactions, gasCost) {
|
118
114
|
const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
|
119
115
|
if (txValue + gasCost === 0n) {
|
120
116
|
return true;
|
121
117
|
}
|
122
|
-
const safeBalance = await this.
|
118
|
+
const safeBalance = await this.getBalance(chainId);
|
123
119
|
return txValue + gasCost < safeBalance;
|
124
120
|
}
|
125
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,16 +20,12 @@ 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
27
|
const m4337Deployment = async (fn) => {
|
26
|
-
|
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
|
-
}
|
28
|
+
return getDeployment(fn, { provider, version: "0.3.0" });
|
33
29
|
};
|
34
30
|
// Need this first to get entryPoint address
|
35
31
|
const m4337 = await m4337Deployment(getSafe4337ModuleDeployment);
|
@@ -76,16 +72,15 @@ export class ContractSuite {
|
|
76
72
|
]);
|
77
73
|
return setup;
|
78
74
|
}
|
79
|
-
async getOpHash(unsignedUserOp
|
80
|
-
|
81
|
-
) {
|
75
|
+
async getOpHash(unsignedUserOp) {
|
76
|
+
const { factory, factoryData, verificationGasLimit, callGasLimit, maxPriorityFeePerGas, maxFeePerGas, } = unsignedUserOp;
|
82
77
|
return this.m4337.getOperationHash({
|
83
78
|
...unsignedUserOp,
|
84
|
-
initCode:
|
85
|
-
? ethers.solidityPacked(["address", "bytes"], [
|
79
|
+
initCode: factory
|
80
|
+
? ethers.solidityPacked(["address", "bytes"], [factory, factoryData])
|
86
81
|
: "0x",
|
87
|
-
accountGasLimits: packGas(
|
88
|
-
gasFees: packGas(
|
82
|
+
accountGasLimits: packGas(verificationGasLimit, callGasLimit),
|
83
|
+
gasFees: packGas(maxPriorityFeePerGas, maxFeePerGas),
|
89
84
|
paymasterAndData: packPaymasterData(unsignedUserOp),
|
90
85
|
signature: PLACEHOLDER_SIG,
|
91
86
|
});
|
@@ -118,8 +113,17 @@ export class ContractSuite {
|
|
118
113
|
async function getDeployment(fn, { provider, version }) {
|
119
114
|
const { chainId } = await provider.getNetwork();
|
120
115
|
const deployment = fn({ version });
|
121
|
-
if (!deployment
|
116
|
+
if (!deployment) {
|
122
117
|
throw new Error(`Deployment not found for ${fn.name} version ${version} on chainId ${chainId}`);
|
123
118
|
}
|
124
|
-
|
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"];
|
127
|
+
}
|
128
|
+
return new ethers.Contract(address, deployment.abi, provider);
|
125
129
|
}
|
package/dist/esm/tx-manager.d.ts
CHANGED
@@ -1,42 +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
|
-
|
14
|
-
readonly chainId: number;
|
13
|
+
private pimlicoKey;
|
15
14
|
private safeSaltNonce;
|
16
|
-
private
|
17
|
-
constructor(
|
15
|
+
private deployedChains;
|
16
|
+
constructor(nearAdapter: NearEthAdapter, safePack: ContractSuite, pimlicoKey: string, setup: string, safeAddress: Address, entryPointAddress: Address, safeSaltNonce: string);
|
18
17
|
static create(config: {
|
19
|
-
ethRpc: string;
|
20
18
|
pimlicoKey: string;
|
21
19
|
nearAdapter: NearEthAdapter;
|
22
20
|
safeSaltNonce?: string;
|
23
21
|
}): Promise<TransactionManager>;
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
pimlicoKey: string;
|
28
|
-
}): Promise<TransactionManager>;
|
29
|
-
get safeNotDeployed(): boolean;
|
30
|
-
get mpcAddress(): `0x${string}`;
|
31
|
-
getSafeBalance(): Promise<bigint>;
|
22
|
+
get mpcAddress(): Address;
|
23
|
+
getBalance(chainId: number): Promise<bigint>;
|
24
|
+
bundlerForChainId(chainId: number): Erc4337Bundler;
|
32
25
|
buildTransaction(args: {
|
26
|
+
chainId: number;
|
33
27
|
transactions: MetaTransaction[];
|
34
28
|
usePaymaster: boolean;
|
35
29
|
}): Promise<UserOperation>;
|
36
|
-
signTransaction(safeOpHash:
|
37
|
-
opHash(userOp: UserOperation): Promise<
|
30
|
+
signTransaction(safeOpHash: Hex): Promise<Hex>;
|
31
|
+
opHash(userOp: UserOperation): Promise<Hash>;
|
38
32
|
encodeSignRequest(tx: BaseTx): Promise<NearEthTxData>;
|
39
|
-
executeTransaction(userOp: UserOperation): Promise<UserOperationReceipt>;
|
33
|
+
executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
|
34
|
+
safeDeployed(chainId: number): Promise<boolean>;
|
40
35
|
addOwnerTx(address: string): MetaTransaction;
|
41
|
-
safeSufficientlyFunded(transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
36
|
+
safeSufficientlyFunded(chainId: number, transactions: MetaTransaction[], gasCost: bigint): Promise<boolean>;
|
42
37
|
}
|
package/dist/esm/tx-manager.js
CHANGED
@@ -1,88 +1,72 @@
|
|
1
|
-
import { ethers } from "ethers";
|
2
1
|
import { Network } from "near-ca";
|
3
2
|
import { Erc4337Bundler } from "./lib/bundler";
|
4
3
|
import { packSignature } from "./util";
|
5
|
-
import { getNearSignature } from "./lib/near";
|
6
4
|
import { encodeMulti } from "ethers-multisend";
|
7
5
|
import { ContractSuite } from "./lib/safe";
|
8
6
|
export class TransactionManager {
|
9
|
-
provider;
|
10
7
|
nearAdapter;
|
8
|
+
address;
|
9
|
+
entryPointAddress;
|
11
10
|
safePack;
|
12
|
-
bundler;
|
13
11
|
setup;
|
14
|
-
|
15
|
-
chainId;
|
12
|
+
pimlicoKey;
|
16
13
|
safeSaltNonce;
|
17
|
-
|
18
|
-
constructor(
|
19
|
-
this.provider = provider;
|
14
|
+
deployedChains;
|
15
|
+
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, entryPointAddress, safeSaltNonce) {
|
20
16
|
this.nearAdapter = nearAdapter;
|
21
17
|
this.safePack = safePack;
|
22
|
-
this.
|
18
|
+
this.pimlicoKey = pimlicoKey;
|
19
|
+
this.entryPointAddress = entryPointAddress;
|
23
20
|
this.setup = setup;
|
24
|
-
this.chainId = chainId;
|
25
21
|
this.address = safeAddress;
|
26
22
|
this.safeSaltNonce = safeSaltNonce;
|
27
|
-
this.
|
23
|
+
this.deployedChains = new Set();
|
28
24
|
}
|
29
25
|
static async create(config) {
|
30
26
|
const { nearAdapter, pimlicoKey } = config;
|
31
|
-
const
|
32
|
-
const chainId = (await provider.getNetwork()).chainId;
|
33
|
-
const safePack = await ContractSuite.init(provider);
|
27
|
+
const safePack = await ContractSuite.init();
|
34
28
|
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
29
|
const setup = await safePack.getSetup([nearAdapter.address]);
|
37
30
|
const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
|
38
|
-
const
|
39
|
-
console.log(`Safe Address: ${safeAddress}
|
40
|
-
return new TransactionManager(
|
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
|
-
});
|
49
|
-
}
|
50
|
-
get safeNotDeployed() {
|
51
|
-
return this._safeNotDeployed;
|
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");
|
52
34
|
}
|
53
35
|
get mpcAddress() {
|
54
36
|
return this.nearAdapter.address;
|
55
37
|
}
|
56
|
-
async
|
57
|
-
|
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);
|
58
44
|
}
|
59
45
|
async buildTransaction(args) {
|
60
|
-
const { transactions, usePaymaster } = args;
|
61
|
-
const
|
62
|
-
|
46
|
+
const { transactions, usePaymaster, chainId } = args;
|
47
|
+
const bundler = this.bundlerForChainId(chainId);
|
48
|
+
const gasFees = (await bundler.getGasPrice()).fast;
|
63
49
|
// Build Singular MetaTransaction for Multisend from transaction list.
|
64
50
|
if (transactions.length === 0) {
|
65
51
|
throw new Error("Empty transaction set!");
|
66
52
|
}
|
67
53
|
const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
|
68
|
-
const
|
69
|
-
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);
|
70
57
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
71
58
|
return unsignedUserOp;
|
72
59
|
}
|
73
60
|
async signTransaction(safeOpHash) {
|
74
|
-
const signature = await
|
61
|
+
const signature = await this.nearAdapter.sign(safeOpHash);
|
75
62
|
return packSignature(signature);
|
76
63
|
}
|
77
64
|
async opHash(userOp) {
|
78
65
|
return this.safePack.getOpHash(userOp);
|
79
66
|
}
|
80
67
|
async encodeSignRequest(tx) {
|
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
|
-
}
|
85
68
|
const unsignedUserOp = await this.buildTransaction({
|
69
|
+
chainId: tx.chainId,
|
86
70
|
transactions: [
|
87
71
|
{
|
88
72
|
to: tx.to,
|
@@ -103,16 +87,27 @@ export class TransactionManager {
|
|
103
87
|
evmMessage: JSON.stringify(unsignedUserOp),
|
104
88
|
};
|
105
89
|
}
|
106
|
-
async executeTransaction(userOp) {
|
107
|
-
const
|
90
|
+
async executeTransaction(chainId, userOp) {
|
91
|
+
const bundler = this.bundlerForChainId(chainId);
|
92
|
+
const userOpHash = await bundler.sendUserOperation(userOp);
|
108
93
|
console.log("UserOp Hash", userOpHash);
|
109
|
-
const userOpReceipt = await
|
94
|
+
const userOpReceipt = await bundler.getUserOpReceipt(userOpHash);
|
110
95
|
console.log("userOp Receipt", userOpReceipt);
|
111
96
|
// Update safeNotDeployed after the first transaction
|
112
|
-
this.
|
113
|
-
(await this.provider.getCode(this.address)) === "0x";
|
97
|
+
this.safeDeployed(chainId);
|
114
98
|
return userOpReceipt;
|
115
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
|
+
}
|
116
111
|
addOwnerTx(address) {
|
117
112
|
return {
|
118
113
|
to: this.address,
|
@@ -120,12 +115,12 @@ export class TransactionManager {
|
|
120
115
|
data: this.safePack.singleton.interface.encodeFunctionData("addOwnerWithThreshold", [address, 1]),
|
121
116
|
};
|
122
117
|
}
|
123
|
-
async safeSufficientlyFunded(transactions, gasCost) {
|
118
|
+
async safeSufficientlyFunded(chainId, transactions, gasCost) {
|
124
119
|
const txValue = transactions.reduce((acc, tx) => acc + BigInt(tx.value), 0n);
|
125
120
|
if (txValue + gasCost === 0n) {
|
126
121
|
return true;
|
127
122
|
}
|
128
|
-
const safeBalance = await this.
|
123
|
+
const safeBalance = await this.getBalance(chainId);
|
129
124
|
return txValue + gasCost < safeBalance;
|
130
125
|
}
|
131
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
|
-
}
|