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.
@@ -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);
@@ -1,9 +1,9 @@
1
1
  import { FinalExecutionOutcome } from "near-api-js/lib/providers";
2
- import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
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(tx: BaseTx): Promise<NearEthTxData>;
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
  }
@@ -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(tx) {
70
- const unsignedUserOp = await this.buildTransaction({
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
- ...signRequest,
89
- evmMessage: JSON.stringify(unsignedUserOp),
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;
@@ -1,4 +1,5 @@
1
- import { Address, Hex } from "viem";
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 {};
@@ -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);
@@ -1,9 +1,9 @@
1
1
  import { FinalExecutionOutcome } from "near-api-js/lib/providers";
2
- import { NearEthAdapter, NearEthTxData, BaseTx } from "near-ca";
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(tx: BaseTx): Promise<NearEthTxData>;
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
  }
@@ -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 { getClient, isContract, packSignature } from "./util";
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(tx) {
74
- const unsignedUserOp = await this.buildTransaction({
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
- ...signRequest,
93
- evmMessage: JSON.stringify(unsignedUserOp),
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
  }
@@ -1,4 +1,5 @@
1
- import { Address, Hex } from "viem";
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 {};
@@ -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.0",
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.2",
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",