near-safe 0.3.0 → 0.3.2
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/safe-message.d.ts +22 -0
- package/dist/cjs/lib/safe-message.js +95 -0
- package/dist/cjs/tx-manager.d.ts +18 -3
- package/dist/cjs/tx-manager.js +69 -20
- package/dist/cjs/types.d.ts +12 -1
- package/dist/cjs/util.d.ts +2 -0
- package/dist/cjs/util.js +25 -0
- package/dist/esm/lib/safe-message.d.ts +22 -0
- package/dist/esm/lib/safe-message.js +92 -0
- package/dist/esm/tx-manager.d.ts +18 -3
- package/dist/esm/tx-manager.js +71 -22
- package/dist/esm/types.d.ts +12 -1
- package/dist/esm/util.d.ts +2 -0
- package/dist/esm/util.js +25 -1
- package/package.json +5 -2
@@ -0,0 +1,22 @@
|
|
1
|
+
import { type SafeInfo } from "@safe-global/safe-gateway-typescript-sdk";
|
2
|
+
import { EIP712TypedData } from "near-ca";
|
3
|
+
import { 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;
|
@@ -0,0 +1,95 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.decodeSafeMessage = decodeSafeMessage;
|
4
|
+
const semver_1 = require("semver");
|
5
|
+
const viem_1 = require("viem");
|
6
|
+
/*
|
7
|
+
* From v1.3.0, EIP-1271 support was moved to the CompatibilityFallbackHandler.
|
8
|
+
* Also 1.3.0 introduces the chainId in the domain part of the SafeMessage
|
9
|
+
*/
|
10
|
+
const EIP1271_FALLBACK_HANDLER_SUPPORTED_SAFE_VERSION = "1.3.0";
|
11
|
+
const generateSafeMessageMessage = (message) => {
|
12
|
+
return typeof message === "string"
|
13
|
+
? (0, viem_1.hashMessage)(message)
|
14
|
+
: (0, viem_1.hashTypedData)(message);
|
15
|
+
};
|
16
|
+
/**
|
17
|
+
* Generates `SafeMessage` typed data for EIP-712
|
18
|
+
* https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L12
|
19
|
+
* @param safe Safe which will sign the message
|
20
|
+
* @param message Message to sign
|
21
|
+
* @returns `SafeMessage` types for signing
|
22
|
+
*/
|
23
|
+
const generateSafeMessageTypedData = ({ version, chainId, address }, message) => {
|
24
|
+
if (!version) {
|
25
|
+
throw Error("Cannot create SafeMessage without version information");
|
26
|
+
}
|
27
|
+
const isHandledByFallbackHandler = (0, semver_1.gte)(version, EIP1271_FALLBACK_HANDLER_SUPPORTED_SAFE_VERSION);
|
28
|
+
const verifyingContract = address.value;
|
29
|
+
return {
|
30
|
+
domain: isHandledByFallbackHandler
|
31
|
+
? {
|
32
|
+
chainId: Number(BigInt(chainId)),
|
33
|
+
verifyingContract,
|
34
|
+
}
|
35
|
+
: { verifyingContract },
|
36
|
+
types: {
|
37
|
+
SafeMessage: [{ name: "message", type: "bytes" }],
|
38
|
+
},
|
39
|
+
message: {
|
40
|
+
message: generateSafeMessageMessage(message),
|
41
|
+
},
|
42
|
+
primaryType: "SafeMessage",
|
43
|
+
};
|
44
|
+
};
|
45
|
+
const generateSafeMessageHash = (safe, message) => {
|
46
|
+
const typedData = generateSafeMessageTypedData(safe, message);
|
47
|
+
return (0, viem_1.hashTypedData)(typedData);
|
48
|
+
};
|
49
|
+
/**
|
50
|
+
* If message is a hex value and is Utf8 encoded string we decode it, else we return the raw message
|
51
|
+
* @param {string} message raw input message
|
52
|
+
* @returns {string}
|
53
|
+
*/
|
54
|
+
const getDecodedMessage = (message) => {
|
55
|
+
if ((0, viem_1.isHex)(message)) {
|
56
|
+
try {
|
57
|
+
return (0, viem_1.fromHex)(message, "string");
|
58
|
+
}
|
59
|
+
catch (e) {
|
60
|
+
// the hex string is not UTF8 encoding so return the raw message.
|
61
|
+
}
|
62
|
+
}
|
63
|
+
return message;
|
64
|
+
};
|
65
|
+
/**
|
66
|
+
* Returns the decoded message, the hash of the `message` and the hash of the `safeMessage`.
|
67
|
+
* The `safeMessageMessage` is the value inside the SafeMessage and the `safeMessageHash` gets signed if the connected wallet does not support `eth_signTypedData`.
|
68
|
+
*
|
69
|
+
* @param message message as string, UTF-8 encoded hex string or EIP-712 Typed Data
|
70
|
+
* @param safe SafeInfo of the opened Safe
|
71
|
+
* @returns `{
|
72
|
+
* decodedMessage,
|
73
|
+
* safeMessageMessage,
|
74
|
+
* safeMessageHash
|
75
|
+
* }`
|
76
|
+
*/
|
77
|
+
function decodeSafeMessage(message, safe) {
|
78
|
+
const decodedMessage = typeof message === "string" ? getDecodedMessage(message) : message;
|
79
|
+
return {
|
80
|
+
decodedMessage,
|
81
|
+
safeMessageMessage: generateSafeMessageMessage(decodedMessage),
|
82
|
+
safeMessageHash: generateSafeMessageHash(safe, decodedMessage),
|
83
|
+
};
|
84
|
+
}
|
85
|
+
// const isEIP712TypedData = (obj: any): obj is EIP712TypedData => {
|
86
|
+
// return (
|
87
|
+
// typeof obj === "object" &&
|
88
|
+
// obj != null &&
|
89
|
+
// "domain" in obj &&
|
90
|
+
// "types" in obj &&
|
91
|
+
// "message" in obj
|
92
|
+
// );
|
93
|
+
// };
|
94
|
+
// export const isBlindSigningPayload = (obj: EIP712TypedData | string): boolean =>
|
95
|
+
// !isEIP712TypedData(obj) && isHash(obj);
|
package/dist/cjs/tx-manager.d.ts
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import { FinalExecutionOutcome } from "near-api-js/lib/providers";
|
2
|
-
import { NearEthAdapter,
|
2
|
+
import { NearEthAdapter, SignRequestData } from "near-ca";
|
3
3
|
import { Address, Hash, Hex } from "viem";
|
4
4
|
import { Erc4337Bundler } from "./lib/bundler";
|
5
5
|
import { ContractSuite } from "./lib/safe";
|
6
|
-
import { MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
|
6
|
+
import { EncodedTxData, MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
|
7
7
|
export declare class TransactionManager {
|
8
8
|
readonly nearAdapter: NearEthAdapter;
|
9
9
|
readonly address: Address;
|
@@ -31,7 +31,7 @@ export declare class TransactionManager {
|
|
31
31
|
}): Promise<UserOperation>;
|
32
32
|
signTransaction(safeOpHash: Hex): Promise<Hex>;
|
33
33
|
opHash(userOp: UserOperation): Promise<Hash>;
|
34
|
-
encodeSignRequest(
|
34
|
+
encodeSignRequest(signRequest: SignRequestData, usePaymaster: boolean): Promise<EncodedTxData>;
|
35
35
|
executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
|
36
36
|
safeDeployed(chainId: number): Promise<boolean>;
|
37
37
|
addOwnerTx(address: Address): MetaTransaction;
|
@@ -40,4 +40,19 @@ export declare class TransactionManager {
|
|
40
40
|
signature: Hex;
|
41
41
|
receipt: UserOperationReceipt;
|
42
42
|
}>;
|
43
|
+
/**
|
44
|
+
* Handles routing of signature requests based on the provided method, chain ID, and parameters.
|
45
|
+
*
|
46
|
+
* @async
|
47
|
+
* @function requestRouter
|
48
|
+
* @param {SignRequestData} params - An object containing the method, chain ID, and request parameters.
|
49
|
+
* @returns {Promise<{ evmMessage: string; payload: number[]; recoveryData: RecoveryData }>}
|
50
|
+
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
51
|
+
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
52
|
+
*/
|
53
|
+
requestRouter({ method, chainId, params }: SignRequestData, usePaymaster: boolean): Promise<{
|
54
|
+
evmMessage: string;
|
55
|
+
payload: number[];
|
56
|
+
hash: Hash;
|
57
|
+
}>;
|
43
58
|
}
|
package/dist/cjs/tx-manager.js
CHANGED
@@ -6,6 +6,7 @@ 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
12
|
constructor(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce) {
|
@@ -66,27 +67,19 @@ class TransactionManager {
|
|
66
67
|
async opHash(userOp) {
|
67
68
|
return this.safePack.getOpHash(userOp);
|
68
69
|
}
|
69
|
-
async encodeSignRequest(
|
70
|
-
const
|
71
|
-
chainId: tx.chainId,
|
72
|
-
transactions: [
|
73
|
-
{
|
74
|
-
to: tx.to,
|
75
|
-
value: (tx.value || 0n).toString(),
|
76
|
-
data: tx.data || "0x",
|
77
|
-
},
|
78
|
-
],
|
79
|
-
usePaymaster: true,
|
80
|
-
});
|
81
|
-
const safeOpHash = (await this.opHash(unsignedUserOp));
|
82
|
-
const signRequest = await this.nearAdapter.encodeSignRequest({
|
83
|
-
method: "hash",
|
84
|
-
chainId: 0,
|
85
|
-
params: safeOpHash,
|
86
|
-
});
|
70
|
+
async encodeSignRequest(signRequest, usePaymaster) {
|
71
|
+
const { payload, evmMessage, hash } = await this.requestRouter(signRequest, usePaymaster);
|
87
72
|
return {
|
88
|
-
|
89
|
-
|
73
|
+
nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
|
74
|
+
path: this.nearAdapter.derivationPath,
|
75
|
+
payload,
|
76
|
+
key_version: 0,
|
77
|
+
}),
|
78
|
+
evmData: {
|
79
|
+
chainId: signRequest.chainId,
|
80
|
+
data: evmMessage,
|
81
|
+
hash,
|
82
|
+
},
|
90
83
|
};
|
91
84
|
}
|
92
85
|
async executeTransaction(chainId, userOp) {
|
@@ -140,5 +133,61 @@ class TransactionManager {
|
|
140
133
|
throw new Error(`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`);
|
141
134
|
}
|
142
135
|
}
|
136
|
+
/**
|
137
|
+
* Handles routing of signature requests based on the provided method, chain ID, and parameters.
|
138
|
+
*
|
139
|
+
* @async
|
140
|
+
* @function requestRouter
|
141
|
+
* @param {SignRequestData} params - An object containing the method, chain ID, and request parameters.
|
142
|
+
* @returns {Promise<{ evmMessage: string; payload: number[]; recoveryData: RecoveryData }>}
|
143
|
+
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
144
|
+
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
145
|
+
*/
|
146
|
+
async requestRouter({ method, chainId, params }, usePaymaster) {
|
147
|
+
const safeInfo = {
|
148
|
+
address: { value: this.address },
|
149
|
+
chainId: chainId.toString(),
|
150
|
+
// TODO: Should be able to read this from on chain.
|
151
|
+
version: "1.4.1+L2",
|
152
|
+
};
|
153
|
+
// TODO: We are provided with sender in the input, but also expect safeInfo.
|
154
|
+
// We should either confirm they agree or ignore one of the two.
|
155
|
+
switch (method) {
|
156
|
+
case "eth_signTypedData":
|
157
|
+
case "eth_signTypedData_v4":
|
158
|
+
case "eth_sign": {
|
159
|
+
const [_, messageOrData] = params;
|
160
|
+
const message = (0, safe_message_1.decodeSafeMessage)(messageOrData, safeInfo);
|
161
|
+
return {
|
162
|
+
evmMessage: message.safeMessageMessage,
|
163
|
+
payload: (0, near_ca_1.toPayload)(message.safeMessageHash),
|
164
|
+
hash: message.safeMessageHash,
|
165
|
+
};
|
166
|
+
}
|
167
|
+
case "personal_sign": {
|
168
|
+
const [messageHash, _] = params;
|
169
|
+
const message = (0, safe_message_1.decodeSafeMessage)(messageHash, safeInfo);
|
170
|
+
return {
|
171
|
+
evmMessage: message.safeMessageMessage,
|
172
|
+
payload: (0, near_ca_1.toPayload)(message.safeMessageHash),
|
173
|
+
hash: message.safeMessageHash,
|
174
|
+
};
|
175
|
+
}
|
176
|
+
case "eth_sendTransaction": {
|
177
|
+
const transactions = (0, util_1.metaTransactionsFromRequest)(params);
|
178
|
+
const userOp = await this.buildTransaction({
|
179
|
+
chainId,
|
180
|
+
transactions,
|
181
|
+
usePaymaster,
|
182
|
+
});
|
183
|
+
const opHash = await this.opHash(userOp);
|
184
|
+
return {
|
185
|
+
payload: (0, near_ca_1.toPayload)(opHash),
|
186
|
+
evmMessage: JSON.stringify(userOp),
|
187
|
+
hash: await this.opHash(userOp),
|
188
|
+
};
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
143
192
|
}
|
144
193
|
exports.TransactionManager = TransactionManager;
|
package/dist/cjs/types.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { FunctionCallTransaction, SignArgs } from "near-ca";
|
2
|
+
import { Address, Hash, Hex, TransactionSerializable } from "viem";
|
2
3
|
export interface UnsignedUserOperation {
|
3
4
|
sender: Address;
|
4
5
|
nonce: string;
|
@@ -89,4 +90,14 @@ export interface MetaTransaction {
|
|
89
90
|
readonly data: string;
|
90
91
|
readonly operation?: OperationType;
|
91
92
|
}
|
93
|
+
export interface EncodedTxData {
|
94
|
+
evmData: {
|
95
|
+
chainId: number;
|
96
|
+
data: string | TransactionSerializable;
|
97
|
+
hash: Hash;
|
98
|
+
};
|
99
|
+
nearPayload: FunctionCallTransaction<{
|
100
|
+
request: SignArgs;
|
101
|
+
}>;
|
102
|
+
}
|
92
103
|
export {};
|
package/dist/cjs/util.d.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { SessionRequestParams } from "near-ca";
|
1
2
|
import { Address, Hex, PublicClient } from "viem";
|
2
3
|
import { PaymasterData, MetaTransaction } from "./types";
|
3
4
|
export declare const PLACEHOLDER_SIG: `0x${string}`;
|
@@ -8,4 +9,5 @@ export declare function packPaymasterData(data: PaymasterData): Hex;
|
|
8
9
|
export declare function containsValue(transactions: MetaTransaction[]): boolean;
|
9
10
|
export declare function isContract(address: Address, chainId: number): Promise<boolean>;
|
10
11
|
export declare function getClient(chainId: number): PublicClient;
|
12
|
+
export declare function metaTransactionsFromRequest(params: SessionRequestParams): MetaTransaction[];
|
11
13
|
export {};
|
package/dist/cjs/util.js
CHANGED
@@ -6,8 +6,10 @@ exports.packPaymasterData = packPaymasterData;
|
|
6
6
|
exports.containsValue = containsValue;
|
7
7
|
exports.isContract = isContract;
|
8
8
|
exports.getClient = getClient;
|
9
|
+
exports.metaTransactionsFromRequest = metaTransactionsFromRequest;
|
9
10
|
const near_ca_1 = require("near-ca");
|
10
11
|
const viem_1 = require("viem");
|
12
|
+
//
|
11
13
|
exports.PLACEHOLDER_SIG = (0, viem_1.encodePacked)(["uint48", "uint48"], [0, 0]);
|
12
14
|
const packGas = (hi, lo) => (0, viem_1.encodePacked)(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
13
15
|
exports.packGas = packGas;
|
@@ -33,3 +35,26 @@ async function isContract(address, chainId) {
|
|
33
35
|
function getClient(chainId) {
|
34
36
|
return near_ca_1.Network.fromChainId(chainId).client;
|
35
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
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { type SafeInfo } from "@safe-global/safe-gateway-typescript-sdk";
|
2
|
+
import { EIP712TypedData } from "near-ca";
|
3
|
+
import { 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;
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import { gte } from "semver";
|
2
|
+
import { fromHex, hashMessage, hashTypedData, isHex, } from "viem";
|
3
|
+
/*
|
4
|
+
* From v1.3.0, EIP-1271 support was moved to the CompatibilityFallbackHandler.
|
5
|
+
* Also 1.3.0 introduces the chainId in the domain part of the SafeMessage
|
6
|
+
*/
|
7
|
+
const EIP1271_FALLBACK_HANDLER_SUPPORTED_SAFE_VERSION = "1.3.0";
|
8
|
+
const generateSafeMessageMessage = (message) => {
|
9
|
+
return typeof message === "string"
|
10
|
+
? hashMessage(message)
|
11
|
+
: hashTypedData(message);
|
12
|
+
};
|
13
|
+
/**
|
14
|
+
* Generates `SafeMessage` typed data for EIP-712
|
15
|
+
* https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L12
|
16
|
+
* @param safe Safe which will sign the message
|
17
|
+
* @param message Message to sign
|
18
|
+
* @returns `SafeMessage` types for signing
|
19
|
+
*/
|
20
|
+
const generateSafeMessageTypedData = ({ version, chainId, address }, message) => {
|
21
|
+
if (!version) {
|
22
|
+
throw Error("Cannot create SafeMessage without version information");
|
23
|
+
}
|
24
|
+
const isHandledByFallbackHandler = gte(version, EIP1271_FALLBACK_HANDLER_SUPPORTED_SAFE_VERSION);
|
25
|
+
const verifyingContract = address.value;
|
26
|
+
return {
|
27
|
+
domain: isHandledByFallbackHandler
|
28
|
+
? {
|
29
|
+
chainId: Number(BigInt(chainId)),
|
30
|
+
verifyingContract,
|
31
|
+
}
|
32
|
+
: { verifyingContract },
|
33
|
+
types: {
|
34
|
+
SafeMessage: [{ name: "message", type: "bytes" }],
|
35
|
+
},
|
36
|
+
message: {
|
37
|
+
message: generateSafeMessageMessage(message),
|
38
|
+
},
|
39
|
+
primaryType: "SafeMessage",
|
40
|
+
};
|
41
|
+
};
|
42
|
+
const generateSafeMessageHash = (safe, message) => {
|
43
|
+
const typedData = generateSafeMessageTypedData(safe, message);
|
44
|
+
return hashTypedData(typedData);
|
45
|
+
};
|
46
|
+
/**
|
47
|
+
* If message is a hex value and is Utf8 encoded string we decode it, else we return the raw message
|
48
|
+
* @param {string} message raw input message
|
49
|
+
* @returns {string}
|
50
|
+
*/
|
51
|
+
const getDecodedMessage = (message) => {
|
52
|
+
if (isHex(message)) {
|
53
|
+
try {
|
54
|
+
return fromHex(message, "string");
|
55
|
+
}
|
56
|
+
catch (e) {
|
57
|
+
// the hex string is not UTF8 encoding so return the raw message.
|
58
|
+
}
|
59
|
+
}
|
60
|
+
return message;
|
61
|
+
};
|
62
|
+
/**
|
63
|
+
* Returns the decoded message, the hash of the `message` and the hash of the `safeMessage`.
|
64
|
+
* The `safeMessageMessage` is the value inside the SafeMessage and the `safeMessageHash` gets signed if the connected wallet does not support `eth_signTypedData`.
|
65
|
+
*
|
66
|
+
* @param message message as string, UTF-8 encoded hex string or EIP-712 Typed Data
|
67
|
+
* @param safe SafeInfo of the opened Safe
|
68
|
+
* @returns `{
|
69
|
+
* decodedMessage,
|
70
|
+
* safeMessageMessage,
|
71
|
+
* safeMessageHash
|
72
|
+
* }`
|
73
|
+
*/
|
74
|
+
export function decodeSafeMessage(message, safe) {
|
75
|
+
const decodedMessage = typeof message === "string" ? getDecodedMessage(message) : message;
|
76
|
+
return {
|
77
|
+
decodedMessage,
|
78
|
+
safeMessageMessage: generateSafeMessageMessage(decodedMessage),
|
79
|
+
safeMessageHash: generateSafeMessageHash(safe, decodedMessage),
|
80
|
+
};
|
81
|
+
}
|
82
|
+
// const isEIP712TypedData = (obj: any): obj is EIP712TypedData => {
|
83
|
+
// return (
|
84
|
+
// typeof obj === "object" &&
|
85
|
+
// obj != null &&
|
86
|
+
// "domain" in obj &&
|
87
|
+
// "types" in obj &&
|
88
|
+
// "message" in obj
|
89
|
+
// );
|
90
|
+
// };
|
91
|
+
// export const isBlindSigningPayload = (obj: EIP712TypedData | string): boolean =>
|
92
|
+
// !isEIP712TypedData(obj) && isHash(obj);
|
package/dist/esm/tx-manager.d.ts
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import { FinalExecutionOutcome } from "near-api-js/lib/providers";
|
2
|
-
import { NearEthAdapter,
|
2
|
+
import { NearEthAdapter, SignRequestData } from "near-ca";
|
3
3
|
import { Address, Hash, Hex } from "viem";
|
4
4
|
import { Erc4337Bundler } from "./lib/bundler";
|
5
5
|
import { ContractSuite } from "./lib/safe";
|
6
|
-
import { MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
|
6
|
+
import { EncodedTxData, MetaTransaction, UserOperation, UserOperationReceipt } from "./types";
|
7
7
|
export declare class TransactionManager {
|
8
8
|
readonly nearAdapter: NearEthAdapter;
|
9
9
|
readonly address: Address;
|
@@ -31,7 +31,7 @@ export declare class TransactionManager {
|
|
31
31
|
}): Promise<UserOperation>;
|
32
32
|
signTransaction(safeOpHash: Hex): Promise<Hex>;
|
33
33
|
opHash(userOp: UserOperation): Promise<Hash>;
|
34
|
-
encodeSignRequest(
|
34
|
+
encodeSignRequest(signRequest: SignRequestData, usePaymaster: boolean): Promise<EncodedTxData>;
|
35
35
|
executeTransaction(chainId: number, userOp: UserOperation): Promise<UserOperationReceipt>;
|
36
36
|
safeDeployed(chainId: number): Promise<boolean>;
|
37
37
|
addOwnerTx(address: Address): MetaTransaction;
|
@@ -40,4 +40,19 @@ export declare class TransactionManager {
|
|
40
40
|
signature: Hex;
|
41
41
|
receipt: UserOperationReceipt;
|
42
42
|
}>;
|
43
|
+
/**
|
44
|
+
* Handles routing of signature requests based on the provided method, chain ID, and parameters.
|
45
|
+
*
|
46
|
+
* @async
|
47
|
+
* @function requestRouter
|
48
|
+
* @param {SignRequestData} params - An object containing the method, chain ID, and request parameters.
|
49
|
+
* @returns {Promise<{ evmMessage: string; payload: number[]; recoveryData: RecoveryData }>}
|
50
|
+
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
51
|
+
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
52
|
+
*/
|
53
|
+
requestRouter({ method, chainId, params }: SignRequestData, usePaymaster: boolean): Promise<{
|
54
|
+
evmMessage: string;
|
55
|
+
payload: number[];
|
56
|
+
hash: Hash;
|
57
|
+
}>;
|
43
58
|
}
|
package/dist/esm/tx-manager.js
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
import { setupAdapter, signatureFromOutcome, } from "near-ca";
|
1
|
+
import { setupAdapter, signatureFromOutcome, toPayload, } from "near-ca";
|
2
2
|
import { serializeSignature } from "viem";
|
3
3
|
import { Erc4337Bundler } from "./lib/bundler";
|
4
4
|
import { encodeMulti } from "./lib/multisend";
|
5
5
|
import { ContractSuite } from "./lib/safe";
|
6
|
-
import {
|
6
|
+
import { decodeSafeMessage } from "./lib/safe-message";
|
7
|
+
import { getClient, isContract, metaTransactionsFromRequest, packSignature, } from "./util";
|
7
8
|
export class TransactionManager {
|
8
9
|
nearAdapter;
|
9
10
|
address;
|
@@ -70,27 +71,19 @@ export class TransactionManager {
|
|
70
71
|
async opHash(userOp) {
|
71
72
|
return this.safePack.getOpHash(userOp);
|
72
73
|
}
|
73
|
-
async encodeSignRequest(
|
74
|
-
const
|
75
|
-
chainId: tx.chainId,
|
76
|
-
transactions: [
|
77
|
-
{
|
78
|
-
to: tx.to,
|
79
|
-
value: (tx.value || 0n).toString(),
|
80
|
-
data: tx.data || "0x",
|
81
|
-
},
|
82
|
-
],
|
83
|
-
usePaymaster: true,
|
84
|
-
});
|
85
|
-
const safeOpHash = (await this.opHash(unsignedUserOp));
|
86
|
-
const signRequest = await this.nearAdapter.encodeSignRequest({
|
87
|
-
method: "hash",
|
88
|
-
chainId: 0,
|
89
|
-
params: safeOpHash,
|
90
|
-
});
|
74
|
+
async encodeSignRequest(signRequest, usePaymaster) {
|
75
|
+
const { payload, evmMessage, hash } = await this.requestRouter(signRequest, usePaymaster);
|
91
76
|
return {
|
92
|
-
|
93
|
-
|
77
|
+
nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
|
78
|
+
path: this.nearAdapter.derivationPath,
|
79
|
+
payload,
|
80
|
+
key_version: 0,
|
81
|
+
}),
|
82
|
+
evmData: {
|
83
|
+
chainId: signRequest.chainId,
|
84
|
+
data: evmMessage,
|
85
|
+
hash,
|
86
|
+
},
|
94
87
|
};
|
95
88
|
}
|
96
89
|
async executeTransaction(chainId, userOp) {
|
@@ -144,4 +137,60 @@ export class TransactionManager {
|
|
144
137
|
throw new Error(`Failed EVM broadcast: ${error instanceof Error ? error.message : String(error)}`);
|
145
138
|
}
|
146
139
|
}
|
140
|
+
/**
|
141
|
+
* Handles routing of signature requests based on the provided method, chain ID, and parameters.
|
142
|
+
*
|
143
|
+
* @async
|
144
|
+
* @function requestRouter
|
145
|
+
* @param {SignRequestData} params - An object containing the method, chain ID, and request parameters.
|
146
|
+
* @returns {Promise<{ evmMessage: string; payload: number[]; recoveryData: RecoveryData }>}
|
147
|
+
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
148
|
+
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
149
|
+
*/
|
150
|
+
async requestRouter({ method, chainId, params }, usePaymaster) {
|
151
|
+
const safeInfo = {
|
152
|
+
address: { value: this.address },
|
153
|
+
chainId: chainId.toString(),
|
154
|
+
// TODO: Should be able to read this from on chain.
|
155
|
+
version: "1.4.1+L2",
|
156
|
+
};
|
157
|
+
// TODO: We are provided with sender in the input, but also expect safeInfo.
|
158
|
+
// We should either confirm they agree or ignore one of the two.
|
159
|
+
switch (method) {
|
160
|
+
case "eth_signTypedData":
|
161
|
+
case "eth_signTypedData_v4":
|
162
|
+
case "eth_sign": {
|
163
|
+
const [_, messageOrData] = params;
|
164
|
+
const message = decodeSafeMessage(messageOrData, safeInfo);
|
165
|
+
return {
|
166
|
+
evmMessage: message.safeMessageMessage,
|
167
|
+
payload: toPayload(message.safeMessageHash),
|
168
|
+
hash: message.safeMessageHash,
|
169
|
+
};
|
170
|
+
}
|
171
|
+
case "personal_sign": {
|
172
|
+
const [messageHash, _] = params;
|
173
|
+
const message = decodeSafeMessage(messageHash, safeInfo);
|
174
|
+
return {
|
175
|
+
evmMessage: message.safeMessageMessage,
|
176
|
+
payload: toPayload(message.safeMessageHash),
|
177
|
+
hash: message.safeMessageHash,
|
178
|
+
};
|
179
|
+
}
|
180
|
+
case "eth_sendTransaction": {
|
181
|
+
const transactions = metaTransactionsFromRequest(params);
|
182
|
+
const userOp = await this.buildTransaction({
|
183
|
+
chainId,
|
184
|
+
transactions,
|
185
|
+
usePaymaster,
|
186
|
+
});
|
187
|
+
const opHash = await this.opHash(userOp);
|
188
|
+
return {
|
189
|
+
payload: toPayload(opHash),
|
190
|
+
evmMessage: JSON.stringify(userOp),
|
191
|
+
hash: await this.opHash(userOp),
|
192
|
+
};
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
147
196
|
}
|
package/dist/esm/types.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { FunctionCallTransaction, SignArgs } from "near-ca";
|
2
|
+
import { Address, Hash, Hex, TransactionSerializable } from "viem";
|
2
3
|
export interface UnsignedUserOperation {
|
3
4
|
sender: Address;
|
4
5
|
nonce: string;
|
@@ -89,4 +90,14 @@ export interface MetaTransaction {
|
|
89
90
|
readonly data: string;
|
90
91
|
readonly operation?: OperationType;
|
91
92
|
}
|
93
|
+
export interface EncodedTxData {
|
94
|
+
evmData: {
|
95
|
+
chainId: number;
|
96
|
+
data: string | TransactionSerializable;
|
97
|
+
hash: Hash;
|
98
|
+
};
|
99
|
+
nearPayload: FunctionCallTransaction<{
|
100
|
+
request: SignArgs;
|
101
|
+
}>;
|
102
|
+
}
|
92
103
|
export {};
|
package/dist/esm/util.d.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { SessionRequestParams } from "near-ca";
|
1
2
|
import { Address, Hex, PublicClient } from "viem";
|
2
3
|
import { PaymasterData, MetaTransaction } from "./types";
|
3
4
|
export declare const PLACEHOLDER_SIG: `0x${string}`;
|
@@ -8,4 +9,5 @@ export declare function packPaymasterData(data: PaymasterData): Hex;
|
|
8
9
|
export declare function containsValue(transactions: MetaTransaction[]): boolean;
|
9
10
|
export declare function isContract(address: Address, chainId: number): Promise<boolean>;
|
10
11
|
export declare function getClient(chainId: number): PublicClient;
|
12
|
+
export declare function metaTransactionsFromRequest(params: SessionRequestParams): MetaTransaction[];
|
11
13
|
export {};
|
package/dist/esm/util.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Network } from "near-ca";
|
2
|
-
import { concatHex, encodePacked, toHex, } from "viem";
|
2
|
+
import { concatHex, encodePacked, toHex, isHex, parseTransaction, zeroAddress, } from "viem";
|
3
|
+
//
|
3
4
|
export const PLACEHOLDER_SIG = encodePacked(["uint48", "uint48"], [0, 0]);
|
4
5
|
export const packGas = (hi, lo) => encodePacked(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
5
6
|
export function packSignature(signature, validFrom = 0, validTo = 0) {
|
@@ -24,3 +25,26 @@ export async function isContract(address, chainId) {
|
|
24
25
|
export function getClient(chainId) {
|
25
26
|
return Network.fromChainId(chainId).client;
|
26
27
|
}
|
28
|
+
export function metaTransactionsFromRequest(params) {
|
29
|
+
let transactions;
|
30
|
+
if (isHex(params)) {
|
31
|
+
// If RLP hex is given, decode the transaction and build EthTransactionParams
|
32
|
+
const tx = parseTransaction(params);
|
33
|
+
transactions = [
|
34
|
+
{
|
35
|
+
from: zeroAddress, // TODO: This is a hack - but its unused.
|
36
|
+
to: tx.to,
|
37
|
+
value: tx.value ? toHex(tx.value) : "0x00",
|
38
|
+
data: tx.data || "0x",
|
39
|
+
},
|
40
|
+
];
|
41
|
+
}
|
42
|
+
else {
|
43
|
+
transactions = params;
|
44
|
+
}
|
45
|
+
return transactions.map((tx) => ({
|
46
|
+
to: tx.to,
|
47
|
+
value: tx.value || "0x00",
|
48
|
+
data: tx.data || "0x",
|
49
|
+
}));
|
50
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "near-safe",
|
3
|
-
"version": "0.3.
|
3
|
+
"version": "0.3.2",
|
4
4
|
"license": "MIT",
|
5
5
|
"description": "An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.",
|
6
6
|
"author": "bh2smith",
|
@@ -41,14 +41,17 @@
|
|
41
41
|
},
|
42
42
|
"dependencies": {
|
43
43
|
"@safe-global/safe-deployments": "^1.37.0",
|
44
|
+
"@safe-global/safe-gateway-typescript-sdk": "^3.22.2",
|
44
45
|
"@safe-global/safe-modules-deployments": "^2.2.0",
|
45
46
|
"near-api-js": "^5.0.0",
|
46
|
-
"near-ca": "^0.5.
|
47
|
+
"near-ca": "^0.5.6",
|
48
|
+
"semver": "^7.6.3",
|
47
49
|
"viem": "^2.16.5"
|
48
50
|
},
|
49
51
|
"devDependencies": {
|
50
52
|
"@types/jest": "^29.5.12",
|
51
53
|
"@types/node": "^22.3.0",
|
54
|
+
"@types/semver": "^7.5.8",
|
52
55
|
"@types/yargs": "^17.0.32",
|
53
56
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
54
57
|
"@typescript-eslint/parser": "^8.1.0",
|