near-safe 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/lib/bundler.d.ts +30 -8
- package/dist/cjs/lib/bundler.js +45 -26
- package/dist/cjs/lib/safe-message.d.ts +27 -0
- package/dist/cjs/lib/safe-message.js +112 -0
- package/dist/cjs/lib/safe.d.ts +19 -16
- package/dist/cjs/lib/safe.js +117 -71
- package/dist/cjs/tx-manager.d.ts +20 -5
- package/dist/cjs/tx-manager.js +80 -37
- package/dist/cjs/util.d.ts +5 -1
- package/dist/cjs/util.js +34 -0
- package/dist/esm/lib/bundler.d.ts +30 -8
- package/dist/esm/lib/bundler.js +47 -28
- package/dist/esm/lib/safe-message.d.ts +27 -0
- package/dist/esm/lib/safe-message.js +108 -0
- package/dist/esm/lib/safe.d.ts +19 -16
- package/dist/esm/lib/safe.js +120 -73
- package/dist/esm/tx-manager.d.ts +20 -5
- package/dist/esm/tx-manager.js +82 -40
- package/dist/esm/util.d.ts +5 -1
- package/dist/esm/util.js +32 -1
- package/package.json +11 -5
package/dist/cjs/tx-manager.js
CHANGED
@@ -6,13 +6,13 @@ const viem_1 = require("viem");
|
|
6
6
|
const bundler_1 = require("./lib/bundler");
|
7
7
|
const multisend_1 = require("./lib/multisend");
|
8
8
|
const safe_1 = require("./lib/safe");
|
9
|
+
const safe_message_1 = require("./lib/safe-message");
|
9
10
|
const util_1 = require("./util");
|
10
11
|
class TransactionManager {
|
11
|
-
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress,
|
12
|
+
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce) {
|
12
13
|
this.nearAdapter = nearAdapter;
|
13
14
|
this.safePack = safePack;
|
14
15
|
this.pimlicoKey = pimlicoKey;
|
15
|
-
this.entryPointAddress = entryPointAddress;
|
16
16
|
this.setup = setup;
|
17
17
|
this.address = safeAddress;
|
18
18
|
this.safeSaltNonce = safeSaltNonce;
|
@@ -25,34 +25,38 @@ class TransactionManager {
|
|
25
25
|
safe_1.ContractSuite.init(),
|
26
26
|
]);
|
27
27
|
console.log(`Near Adapter: ${nearAdapter.nearAccountId()} <> ${nearAdapter.address}`);
|
28
|
-
const setup =
|
28
|
+
const setup = safePack.getSetup([nearAdapter.address]);
|
29
29
|
const safeAddress = await safePack.addressForSetup(setup, config.safeSaltNonce);
|
30
|
-
const entryPointAddress = (await safePack.entryPoint.getAddress());
|
31
30
|
console.log(`Safe Address: ${safeAddress}`);
|
32
|
-
return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress,
|
31
|
+
return new TransactionManager(nearAdapter, safePack, pimlicoKey, setup, safeAddress, config.safeSaltNonce || "0");
|
33
32
|
}
|
34
33
|
get mpcAddress() {
|
35
34
|
return this.nearAdapter.address;
|
36
35
|
}
|
36
|
+
get mpcContractId() {
|
37
|
+
return this.nearAdapter.mpcContract.contract.contractId;
|
38
|
+
}
|
37
39
|
async getBalance(chainId) {
|
38
|
-
|
39
|
-
return await provider.getBalance({ address: this.address });
|
40
|
+
return await (0, util_1.getClient)(chainId).getBalance({ address: this.address });
|
40
41
|
}
|
41
42
|
bundlerForChainId(chainId) {
|
42
|
-
return new bundler_1.Erc4337Bundler(this.
|
43
|
+
return new bundler_1.Erc4337Bundler(this.safePack.entryPoint.address, this.pimlicoKey, chainId);
|
43
44
|
}
|
44
45
|
async buildTransaction(args) {
|
45
46
|
const { transactions, usePaymaster, chainId } = args;
|
46
|
-
const bundler = this.bundlerForChainId(chainId);
|
47
|
-
const gasFees = (await bundler.getGasPrice()).fast;
|
48
|
-
// Build Singular MetaTransaction for Multisend from transaction list.
|
49
47
|
if (transactions.length === 0) {
|
50
48
|
throw new Error("Empty transaction set!");
|
51
49
|
}
|
50
|
+
const bundler = this.bundlerForChainId(chainId);
|
51
|
+
const [gasFees, nonce, safeDeployed] = await Promise.all([
|
52
|
+
bundler.getGasPrice(),
|
53
|
+
this.safePack.getNonce(this.address, chainId),
|
54
|
+
this.safeDeployed(chainId),
|
55
|
+
]);
|
56
|
+
// Build Singular MetaTransaction for Multisend from transaction list.
|
52
57
|
const tx = transactions.length > 1 ? (0, multisend_1.encodeMulti)(transactions) : transactions[0];
|
53
|
-
const
|
54
|
-
const
|
55
|
-
const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed);
|
58
|
+
const rawUserOp = await this.safePack.buildUserOp(nonce, tx, this.address, gasFees.fast, this.setup, !safeDeployed, this.safeSaltNonce);
|
59
|
+
const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, !safeDeployed);
|
56
60
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
57
61
|
return unsignedUserOp;
|
58
62
|
}
|
@@ -63,27 +67,15 @@ class TransactionManager {
|
|
63
67
|
async opHash(userOp) {
|
64
68
|
return this.safePack.getOpHash(userOp);
|
65
69
|
}
|
66
|
-
async encodeSignRequest(
|
67
|
-
const
|
68
|
-
chainId: tx.chainId,
|
69
|
-
transactions: [
|
70
|
-
{
|
71
|
-
to: tx.to,
|
72
|
-
value: (tx.value || 0n).toString(),
|
73
|
-
data: tx.data || "0x",
|
74
|
-
},
|
75
|
-
],
|
76
|
-
usePaymaster: true,
|
77
|
-
});
|
78
|
-
const safeOpHash = (await this.opHash(unsignedUserOp));
|
79
|
-
const signRequest = await this.nearAdapter.encodeSignRequest({
|
80
|
-
method: "hash",
|
81
|
-
chainId: 0,
|
82
|
-
params: safeOpHash,
|
83
|
-
});
|
70
|
+
async encodeSignRequest(signRequest, usePaymaster) {
|
71
|
+
const data = await this.requestRouter(signRequest, usePaymaster);
|
84
72
|
return {
|
85
|
-
|
86
|
-
|
73
|
+
nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
|
74
|
+
path: this.nearAdapter.derivationPath,
|
75
|
+
payload: data.payload,
|
76
|
+
key_version: 0,
|
77
|
+
}),
|
78
|
+
...data,
|
87
79
|
};
|
88
80
|
}
|
89
81
|
async executeTransaction(chainId, userOp) {
|
@@ -97,11 +89,11 @@ class TransactionManager {
|
|
97
89
|
return userOpReceipt;
|
98
90
|
}
|
99
91
|
async safeDeployed(chainId) {
|
92
|
+
// Early exit if already known.
|
100
93
|
if (chainId in this.deployedChains) {
|
101
94
|
return true;
|
102
95
|
}
|
103
|
-
const
|
104
|
-
const deployed = (await provider.getCode({ address: this.address })) !== "0x";
|
96
|
+
const deployed = await (0, util_1.isContract)(this.address, chainId);
|
105
97
|
if (deployed) {
|
106
98
|
this.deployedChains.add(chainId);
|
107
99
|
}
|
@@ -111,7 +103,7 @@ class TransactionManager {
|
|
111
103
|
return {
|
112
104
|
to: this.address,
|
113
105
|
value: "0",
|
114
|
-
data: this.safePack.
|
106
|
+
data: this.safePack.addOwnerData(address),
|
115
107
|
};
|
116
108
|
}
|
117
109
|
async safeSufficientlyFunded(chainId, transactions, gasCost) {
|
@@ -137,5 +129,56 @@ class TransactionManager {
|
|
137
129
|
throw new Error(`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`);
|
138
130
|
}
|
139
131
|
}
|
132
|
+
/**
|
133
|
+
* Handles routing of signature requests based on the provided method, chain ID, and parameters.
|
134
|
+
*
|
135
|
+
* @async
|
136
|
+
* @function requestRouter
|
137
|
+
* @param {SignRequestData} params - An object containing the method, chain ID, and request parameters.
|
138
|
+
* @returns {Promise<{ evmMessage: string; payload: number[]; recoveryData: RecoveryData }>}
|
139
|
+
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
140
|
+
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
141
|
+
*/
|
142
|
+
async requestRouter({ method, chainId, params }, usePaymaster) {
|
143
|
+
const safeInfo = {
|
144
|
+
address: { value: this.address },
|
145
|
+
chainId: chainId.toString(),
|
146
|
+
// TODO: Should be able to read this from on chain.
|
147
|
+
version: "1.4.1+L2",
|
148
|
+
};
|
149
|
+
// TODO: We are provided with sender in the input, but also expect safeInfo.
|
150
|
+
// We should either confirm they agree or ignore one of the two.
|
151
|
+
switch (method) {
|
152
|
+
case "eth_signTypedData":
|
153
|
+
case "eth_signTypedData_v4":
|
154
|
+
case "eth_sign": {
|
155
|
+
const [sender, messageOrData] = params;
|
156
|
+
return (0, safe_message_1.safeMessageTxData)(method, (0, safe_message_1.decodeSafeMessage)(messageOrData, safeInfo), sender);
|
157
|
+
}
|
158
|
+
case "personal_sign": {
|
159
|
+
const [messageHash, sender] = params;
|
160
|
+
return (0, safe_message_1.safeMessageTxData)(method, (0, safe_message_1.decodeSafeMessage)(messageHash, safeInfo), sender);
|
161
|
+
}
|
162
|
+
case "eth_sendTransaction": {
|
163
|
+
const transactions = (0, util_1.metaTransactionsFromRequest)(params);
|
164
|
+
const userOp = await this.buildTransaction({
|
165
|
+
chainId,
|
166
|
+
transactions,
|
167
|
+
usePaymaster,
|
168
|
+
});
|
169
|
+
const opHash = await this.opHash(userOp);
|
170
|
+
return {
|
171
|
+
payload: (0, near_ca_1.toPayload)(opHash),
|
172
|
+
evmMessage: JSON.stringify(userOp),
|
173
|
+
recoveryData: {
|
174
|
+
type: method,
|
175
|
+
// TODO: Double check that this is sufficient for UI.
|
176
|
+
// We may want to adapt and return the `MetaTransactions` instead.
|
177
|
+
data: opHash,
|
178
|
+
},
|
179
|
+
};
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
140
183
|
}
|
141
184
|
exports.TransactionManager = TransactionManager;
|
package/dist/cjs/util.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { SessionRequestParams } from "near-ca";
|
2
|
+
import { Address, Hex, PublicClient } from "viem";
|
2
3
|
import { PaymasterData, MetaTransaction } from "./types";
|
3
4
|
export declare const PLACEHOLDER_SIG: `0x${string}`;
|
4
5
|
type IntLike = Hex | bigint | string | number;
|
@@ -6,4 +7,7 @@ export declare const packGas: (hi: IntLike, lo: IntLike) => string;
|
|
6
7
|
export declare function packSignature(signature: `0x${string}`, validFrom?: number, validTo?: number): Hex;
|
7
8
|
export declare function packPaymasterData(data: PaymasterData): Hex;
|
8
9
|
export declare function containsValue(transactions: MetaTransaction[]): boolean;
|
10
|
+
export declare function isContract(address: Address, chainId: number): Promise<boolean>;
|
11
|
+
export declare function getClient(chainId: number): PublicClient;
|
12
|
+
export declare function metaTransactionsFromRequest(params: SessionRequestParams): MetaTransaction[];
|
9
13
|
export {};
|
package/dist/cjs/util.js
CHANGED
@@ -4,7 +4,12 @@ exports.packGas = exports.PLACEHOLDER_SIG = void 0;
|
|
4
4
|
exports.packSignature = packSignature;
|
5
5
|
exports.packPaymasterData = packPaymasterData;
|
6
6
|
exports.containsValue = containsValue;
|
7
|
+
exports.isContract = isContract;
|
8
|
+
exports.getClient = getClient;
|
9
|
+
exports.metaTransactionsFromRequest = metaTransactionsFromRequest;
|
10
|
+
const near_ca_1 = require("near-ca");
|
7
11
|
const viem_1 = require("viem");
|
12
|
+
//
|
8
13
|
exports.PLACEHOLDER_SIG = (0, viem_1.encodePacked)(["uint48", "uint48"], [0, 0]);
|
9
14
|
const packGas = (hi, lo) => (0, viem_1.encodePacked)(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
10
15
|
exports.packGas = packGas;
|
@@ -24,3 +29,32 @@ function packPaymasterData(data) {
|
|
24
29
|
function containsValue(transactions) {
|
25
30
|
return transactions.some((tx) => tx.value !== "0");
|
26
31
|
}
|
32
|
+
async function isContract(address, chainId) {
|
33
|
+
return (await getClient(chainId).getCode({ address })) !== undefined;
|
34
|
+
}
|
35
|
+
function getClient(chainId) {
|
36
|
+
return near_ca_1.Network.fromChainId(chainId).client;
|
37
|
+
}
|
38
|
+
function metaTransactionsFromRequest(params) {
|
39
|
+
let transactions;
|
40
|
+
if ((0, viem_1.isHex)(params)) {
|
41
|
+
// If RLP hex is given, decode the transaction and build EthTransactionParams
|
42
|
+
const tx = (0, viem_1.parseTransaction)(params);
|
43
|
+
transactions = [
|
44
|
+
{
|
45
|
+
from: viem_1.zeroAddress, // TODO: This is a hack - but its unused.
|
46
|
+
to: tx.to,
|
47
|
+
value: tx.value ? (0, viem_1.toHex)(tx.value) : "0x00",
|
48
|
+
data: tx.data || "0x",
|
49
|
+
},
|
50
|
+
];
|
51
|
+
}
|
52
|
+
else {
|
53
|
+
transactions = params;
|
54
|
+
}
|
55
|
+
return transactions.map((tx) => ({
|
56
|
+
to: tx.to,
|
57
|
+
value: tx.value || "0x00",
|
58
|
+
data: tx.data || "0x",
|
59
|
+
}));
|
60
|
+
}
|
@@ -1,15 +1,37 @@
|
|
1
|
-
import {
|
1
|
+
import { Address, Hash, PublicClient, Transport } from "viem";
|
2
2
|
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
|
3
|
+
type BundlerRpcSchema = [
|
4
|
+
{
|
5
|
+
Method: "pm_sponsorUserOperation";
|
6
|
+
Parameters: [UnsignedUserOperation, Address];
|
7
|
+
ReturnType: PaymasterData;
|
8
|
+
},
|
9
|
+
{
|
10
|
+
Method: "eth_sendUserOperation";
|
11
|
+
Parameters: [UserOperation, Address];
|
12
|
+
ReturnType: Hash;
|
13
|
+
},
|
14
|
+
{
|
15
|
+
Method: "pimlico_getUserOperationGasPrice";
|
16
|
+
Parameters: [];
|
17
|
+
ReturnType: GasPrices;
|
18
|
+
},
|
19
|
+
{
|
20
|
+
Method: "eth_getUserOperationReceipt";
|
21
|
+
Parameters: [Hash];
|
22
|
+
ReturnType: UserOperationReceipt | null;
|
23
|
+
}
|
24
|
+
];
|
3
25
|
export declare class Erc4337Bundler {
|
4
|
-
|
5
|
-
entryPointAddress:
|
26
|
+
client: PublicClient<Transport, undefined, undefined, BundlerRpcSchema>;
|
27
|
+
entryPointAddress: Address;
|
6
28
|
apiKey: string;
|
7
29
|
chainId: number;
|
8
|
-
constructor(entryPointAddress:
|
9
|
-
client(chainId: number): ethers.JsonRpcProvider;
|
30
|
+
constructor(entryPointAddress: Address, apiKey: string, chainId: number);
|
10
31
|
getPaymasterData(rawUserOp: UnsignedUserOperation, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
|
11
|
-
sendUserOperation(userOp: UserOperation): Promise<
|
32
|
+
sendUserOperation(userOp: UserOperation): Promise<Hash>;
|
12
33
|
getGasPrice(): Promise<GasPrices>;
|
13
|
-
|
14
|
-
|
34
|
+
getUserOpReceipt(userOpHash: Hash): Promise<UserOperationReceipt>;
|
35
|
+
private _getUserOpReceiptInner;
|
15
36
|
}
|
37
|
+
export {};
|
package/dist/esm/lib/bundler.js
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
import { ethers } from "ethers";
|
3
|
-
import { toHex } from "viem";
|
1
|
+
import { createPublicClient, http, rpcSchema, toHex, RpcError, HttpRequestError, } from "viem";
|
4
2
|
import { PLACEHOLDER_SIG } from "../util";
|
5
3
|
function bundlerUrl(chainId, apikey) {
|
6
4
|
return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apikey}`;
|
7
5
|
}
|
8
6
|
export class Erc4337Bundler {
|
9
|
-
|
7
|
+
client;
|
10
8
|
entryPointAddress;
|
11
9
|
apiKey;
|
12
10
|
chainId;
|
@@ -14,41 +12,37 @@ export class Erc4337Bundler {
|
|
14
12
|
this.entryPointAddress = entryPointAddress;
|
15
13
|
this.apiKey = apiKey;
|
16
14
|
this.chainId = chainId;
|
17
|
-
this.
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
this.client = createPublicClient({
|
16
|
+
transport: http(bundlerUrl(chainId, this.apiKey)),
|
17
|
+
rpcSchema: rpcSchema(),
|
18
|
+
});
|
21
19
|
}
|
22
20
|
async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
|
23
21
|
// TODO: Keep this option out of the bundler
|
24
22
|
if (usePaymaster) {
|
25
23
|
console.log("Requesting paymaster data...");
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
return handleRequest(() => this.client.request({
|
25
|
+
method: "pm_sponsorUserOperation",
|
26
|
+
params: [
|
27
|
+
{ ...rawUserOp, signature: PLACEHOLDER_SIG },
|
28
|
+
this.entryPointAddress,
|
29
|
+
],
|
30
|
+
}));
|
31
31
|
}
|
32
32
|
return defaultPaymasterData(safeNotDeployed);
|
33
33
|
}
|
34
34
|
async sendUserOperation(userOp) {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
return userOpHash;
|
41
|
-
}
|
42
|
-
catch (err) {
|
43
|
-
const error = err.error;
|
44
|
-
throw new Error(`Failed to send user op with: ${error.message}`);
|
45
|
-
}
|
35
|
+
return handleRequest(() => this.client.request({
|
36
|
+
method: "eth_sendUserOperation",
|
37
|
+
params: [userOp, this.entryPointAddress],
|
38
|
+
}));
|
39
|
+
// throw new Error(`Failed to send user op with: ${error.message}`);
|
46
40
|
}
|
47
41
|
async getGasPrice() {
|
48
|
-
return this.
|
49
|
-
|
50
|
-
|
51
|
-
|
42
|
+
return handleRequest(() => this.client.request({
|
43
|
+
method: "pimlico_getUserOperationGasPrice",
|
44
|
+
params: [],
|
45
|
+
}));
|
52
46
|
}
|
53
47
|
async getUserOpReceipt(userOpHash) {
|
54
48
|
let userOpReceipt = null;
|
@@ -59,6 +53,31 @@ export class Erc4337Bundler {
|
|
59
53
|
}
|
60
54
|
return userOpReceipt;
|
61
55
|
}
|
56
|
+
async _getUserOpReceiptInner(userOpHash) {
|
57
|
+
return handleRequest(() => this.client.request({
|
58
|
+
method: "eth_getUserOperationReceipt",
|
59
|
+
params: [userOpHash],
|
60
|
+
}));
|
61
|
+
}
|
62
|
+
}
|
63
|
+
async function handleRequest(clientMethod) {
|
64
|
+
try {
|
65
|
+
return await clientMethod();
|
66
|
+
}
|
67
|
+
catch (error) {
|
68
|
+
if (error instanceof HttpRequestError) {
|
69
|
+
if (error.status === 401) {
|
70
|
+
throw new Error("Unauthorized request. Please check your API key.");
|
71
|
+
}
|
72
|
+
else {
|
73
|
+
console.error(`Request failed with status ${error.status}: ${error.message}`);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
else if (error instanceof RpcError) {
|
77
|
+
throw new Error(`Failed to send user op with: ${error.message}`);
|
78
|
+
}
|
79
|
+
throw new Error(`Unexpected error ${error instanceof Error ? error.message : String(error)}`);
|
80
|
+
}
|
62
81
|
}
|
63
82
|
// TODO(bh2smith) Should probably get reasonable estimates here:
|
64
83
|
const defaultPaymasterData = (safeNotDeployed) => {
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { type SafeInfo } from "@safe-global/safe-gateway-typescript-sdk";
|
2
|
+
import { EIP712TypedData, RecoveryData } from "near-ca";
|
3
|
+
import { Address, Hash } from "viem";
|
4
|
+
export type DecodedSafeMessage = {
|
5
|
+
decodedMessage: string | EIP712TypedData;
|
6
|
+
safeMessageMessage: string;
|
7
|
+
safeMessageHash: Hash;
|
8
|
+
};
|
9
|
+
export type MinimalSafeInfo = Pick<SafeInfo, "address" | "version" | "chainId">;
|
10
|
+
/**
|
11
|
+
* Returns the decoded message, the hash of the `message` and the hash of the `safeMessage`.
|
12
|
+
* The `safeMessageMessage` is the value inside the SafeMessage and the `safeMessageHash` gets signed if the connected wallet does not support `eth_signTypedData`.
|
13
|
+
*
|
14
|
+
* @param message message as string, UTF-8 encoded hex string or EIP-712 Typed Data
|
15
|
+
* @param safe SafeInfo of the opened Safe
|
16
|
+
* @returns `{
|
17
|
+
* decodedMessage,
|
18
|
+
* safeMessageMessage,
|
19
|
+
* safeMessageHash
|
20
|
+
* }`
|
21
|
+
*/
|
22
|
+
export declare function decodeSafeMessage(message: string | EIP712TypedData, safe: MinimalSafeInfo): DecodedSafeMessage;
|
23
|
+
export declare function safeMessageTxData(method: string, message: DecodedSafeMessage, sender: Address): {
|
24
|
+
evmMessage: string;
|
25
|
+
payload: number[];
|
26
|
+
recoveryData: RecoveryData;
|
27
|
+
};
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import { toPayload } from "near-ca";
|
2
|
+
import { gte } from "semver";
|
3
|
+
import { fromHex, hashMessage, hashTypedData, isHex, } from "viem";
|
4
|
+
/*
|
5
|
+
* From v1.3.0, EIP-1271 support was moved to the CompatibilityFallbackHandler.
|
6
|
+
* Also 1.3.0 introduces the chainId in the domain part of the SafeMessage
|
7
|
+
*/
|
8
|
+
const EIP1271_FALLBACK_HANDLER_SUPPORTED_SAFE_VERSION = "1.3.0";
|
9
|
+
const generateSafeMessageMessage = (message) => {
|
10
|
+
return typeof message === "string"
|
11
|
+
? hashMessage(message)
|
12
|
+
: hashTypedData(message);
|
13
|
+
};
|
14
|
+
/**
|
15
|
+
* Generates `SafeMessage` typed data for EIP-712
|
16
|
+
* https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L12
|
17
|
+
* @param safe Safe which will sign the message
|
18
|
+
* @param message Message to sign
|
19
|
+
* @returns `SafeMessage` types for signing
|
20
|
+
*/
|
21
|
+
const generateSafeMessageTypedData = ({ version, chainId, address }, message) => {
|
22
|
+
if (!version) {
|
23
|
+
throw Error("Cannot create SafeMessage without version information");
|
24
|
+
}
|
25
|
+
const isHandledByFallbackHandler = gte(version, EIP1271_FALLBACK_HANDLER_SUPPORTED_SAFE_VERSION);
|
26
|
+
const verifyingContract = address.value;
|
27
|
+
return {
|
28
|
+
domain: isHandledByFallbackHandler
|
29
|
+
? {
|
30
|
+
chainId: Number(BigInt(chainId)),
|
31
|
+
verifyingContract,
|
32
|
+
}
|
33
|
+
: { verifyingContract },
|
34
|
+
types: {
|
35
|
+
SafeMessage: [{ name: "message", type: "bytes" }],
|
36
|
+
},
|
37
|
+
message: {
|
38
|
+
message: generateSafeMessageMessage(message),
|
39
|
+
},
|
40
|
+
primaryType: "SafeMessage",
|
41
|
+
};
|
42
|
+
};
|
43
|
+
const generateSafeMessageHash = (safe, message) => {
|
44
|
+
const typedData = generateSafeMessageTypedData(safe, message);
|
45
|
+
return hashTypedData(typedData);
|
46
|
+
};
|
47
|
+
/**
|
48
|
+
* If message is a hex value and is Utf8 encoded string we decode it, else we return the raw message
|
49
|
+
* @param {string} message raw input message
|
50
|
+
* @returns {string}
|
51
|
+
*/
|
52
|
+
const getDecodedMessage = (message) => {
|
53
|
+
if (isHex(message)) {
|
54
|
+
try {
|
55
|
+
return fromHex(message, "string");
|
56
|
+
}
|
57
|
+
catch (e) {
|
58
|
+
// the hex string is not UTF8 encoding so return the raw message.
|
59
|
+
}
|
60
|
+
}
|
61
|
+
return message;
|
62
|
+
};
|
63
|
+
/**
|
64
|
+
* Returns the decoded message, the hash of the `message` and the hash of the `safeMessage`.
|
65
|
+
* The `safeMessageMessage` is the value inside the SafeMessage and the `safeMessageHash` gets signed if the connected wallet does not support `eth_signTypedData`.
|
66
|
+
*
|
67
|
+
* @param message message as string, UTF-8 encoded hex string or EIP-712 Typed Data
|
68
|
+
* @param safe SafeInfo of the opened Safe
|
69
|
+
* @returns `{
|
70
|
+
* decodedMessage,
|
71
|
+
* safeMessageMessage,
|
72
|
+
* safeMessageHash
|
73
|
+
* }`
|
74
|
+
*/
|
75
|
+
export function decodeSafeMessage(message, safe) {
|
76
|
+
const decodedMessage = typeof message === "string" ? getDecodedMessage(message) : message;
|
77
|
+
return {
|
78
|
+
decodedMessage,
|
79
|
+
safeMessageMessage: generateSafeMessageMessage(decodedMessage),
|
80
|
+
safeMessageHash: generateSafeMessageHash(safe, decodedMessage),
|
81
|
+
};
|
82
|
+
}
|
83
|
+
export function safeMessageTxData(method, message, sender) {
|
84
|
+
return {
|
85
|
+
evmMessage: message.safeMessageMessage,
|
86
|
+
payload: toPayload(message.safeMessageHash),
|
87
|
+
recoveryData: {
|
88
|
+
type: method,
|
89
|
+
data: {
|
90
|
+
address: sender,
|
91
|
+
// TODO - Upgrade Signable Message in near-ca
|
92
|
+
// @ts-expect-error: Type 'string | EIP712TypedData' is not assignable to type 'SignableMessage'.
|
93
|
+
message: decodedMessage,
|
94
|
+
},
|
95
|
+
},
|
96
|
+
};
|
97
|
+
}
|
98
|
+
// const isEIP712TypedData = (obj: any): obj is EIP712TypedData => {
|
99
|
+
// return (
|
100
|
+
// typeof obj === "object" &&
|
101
|
+
// obj != null &&
|
102
|
+
// "domain" in obj &&
|
103
|
+
// "types" in obj &&
|
104
|
+
// "message" in obj
|
105
|
+
// );
|
106
|
+
// };
|
107
|
+
// export const isBlindSigningPayload = (obj: EIP712TypedData | string): boolean =>
|
108
|
+
// !isEIP712TypedData(obj) && isHash(obj);
|
package/dist/esm/lib/safe.d.ts
CHANGED
@@ -1,24 +1,27 @@
|
|
1
|
-
import {
|
2
|
-
import { Address, Hash, Hex } from "viem";
|
1
|
+
import { Address, Hash, Hex, ParseAbi, PublicClient } from "viem";
|
3
2
|
import { GasPrice, MetaTransaction, UnsignedUserOperation, UserOperation } from "../types";
|
3
|
+
interface DeploymentData {
|
4
|
+
abi: unknown[] | ParseAbi<readonly string[]>;
|
5
|
+
address: `0x${string}`;
|
6
|
+
}
|
4
7
|
/**
|
5
8
|
* All contracts used in account creation & execution
|
6
9
|
*/
|
7
10
|
export declare class ContractSuite {
|
8
|
-
|
9
|
-
singleton:
|
10
|
-
proxyFactory:
|
11
|
-
m4337:
|
12
|
-
moduleSetup:
|
13
|
-
entryPoint:
|
14
|
-
constructor(
|
11
|
+
dummyClient: PublicClient;
|
12
|
+
singleton: DeploymentData;
|
13
|
+
proxyFactory: DeploymentData;
|
14
|
+
m4337: DeploymentData;
|
15
|
+
moduleSetup: DeploymentData;
|
16
|
+
entryPoint: DeploymentData;
|
17
|
+
constructor(client: PublicClient, singleton: DeploymentData, proxyFactory: DeploymentData, m4337: DeploymentData, moduleSetup: DeploymentData, entryPoint: DeploymentData);
|
15
18
|
static init(): Promise<ContractSuite>;
|
16
|
-
addressForSetup(setup:
|
17
|
-
getSetup(owners: string[]):
|
19
|
+
addressForSetup(setup: Hex, saltNonce?: string): Promise<Address>;
|
20
|
+
getSetup(owners: string[]): Hex;
|
21
|
+
addOwnerData(newOwner: Address): Hex;
|
18
22
|
getOpHash(unsignedUserOp: UserOperation): Promise<Hash>;
|
19
|
-
factoryDataForSetup
|
20
|
-
|
21
|
-
|
22
|
-
};
|
23
|
-
buildUserOp(txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
|
23
|
+
private factoryDataForSetup;
|
24
|
+
buildUserOp(nonce: bigint, txData: MetaTransaction, safeAddress: Address, feeData: GasPrice, setup: string, safeNotDeployed: boolean, safeSaltNonce: string): Promise<UnsignedUserOperation>;
|
25
|
+
getNonce(address: Address, chainId: number): Promise<bigint>;
|
24
26
|
}
|
27
|
+
export {};
|