near-safe 0.6.2 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/cjs/lib/bundler.d.ts +5 -2
- package/dist/cjs/lib/bundler.js +3 -2
- package/dist/cjs/lib/safe.d.ts +1 -1
- package/dist/cjs/lib/safe.js +9 -9
- package/dist/cjs/near-safe.d.ts +3 -3
- package/dist/cjs/near-safe.js +9 -8
- package/dist/cjs/types.d.ts +1 -1
- package/dist/cjs/util.d.ts +26 -0
- package/dist/cjs/util.js +65 -1
- package/dist/esm/lib/bundler.d.ts +5 -2
- package/dist/esm/lib/bundler.js +3 -2
- package/dist/esm/lib/safe.d.ts +1 -1
- package/dist/esm/lib/safe.js +9 -9
- package/dist/esm/near-safe.d.ts +3 -3
- package/dist/esm/near-safe.js +9 -8
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/util.d.ts +26 -0
- package/dist/esm/util.js +65 -3
- package/package.json +3 -3
@@ -1,9 +1,12 @@
|
|
1
1
|
import { Address, Hash, PublicClient, Transport } from "viem";
|
2
2
|
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
|
3
|
+
type SponsorshipPolicy = {
|
4
|
+
sponsorshipPolicyId: string;
|
5
|
+
};
|
3
6
|
type BundlerRpcSchema = [
|
4
7
|
{
|
5
8
|
Method: "pm_sponsorUserOperation";
|
6
|
-
Parameters: [UnsignedUserOperation, Address];
|
9
|
+
Parameters: [UnsignedUserOperation, Address, SponsorshipPolicy];
|
7
10
|
ReturnType: PaymasterData;
|
8
11
|
},
|
9
12
|
{
|
@@ -28,7 +31,7 @@ export declare class Erc4337Bundler {
|
|
28
31
|
apiKey: string;
|
29
32
|
chainId: number;
|
30
33
|
constructor(entryPointAddress: Address, apiKey: string, chainId: number);
|
31
|
-
getPaymasterData(rawUserOp: UnsignedUserOperation,
|
34
|
+
getPaymasterData(rawUserOp: UnsignedUserOperation, safeNotDeployed: boolean, sponsorshipPolicy?: string): Promise<PaymasterData>;
|
32
35
|
sendUserOperation(userOp: UserOperation): Promise<Hash>;
|
33
36
|
getGasPrice(): Promise<GasPrices>;
|
34
37
|
getUserOpReceipt(userOpHash: Hash): Promise<UserOperationReceipt>;
|
package/dist/cjs/lib/bundler.js
CHANGED
@@ -17,15 +17,16 @@ class Erc4337Bundler {
|
|
17
17
|
rpcSchema: (0, viem_1.rpcSchema)(),
|
18
18
|
});
|
19
19
|
}
|
20
|
-
async getPaymasterData(rawUserOp,
|
20
|
+
async getPaymasterData(rawUserOp, safeNotDeployed, sponsorshipPolicy) {
|
21
21
|
// TODO: Keep this option out of the bundler
|
22
|
-
if (
|
22
|
+
if (sponsorshipPolicy) {
|
23
23
|
console.log("Requesting paymaster data...");
|
24
24
|
return handleRequest(() => this.client.request({
|
25
25
|
method: "pm_sponsorUserOperation",
|
26
26
|
params: [
|
27
27
|
{ ...rawUserOp, signature: util_1.PLACEHOLDER_SIG },
|
28
28
|
this.entryPointAddress,
|
29
|
+
{ sponsorshipPolicyId: sponsorshipPolicy },
|
29
30
|
],
|
30
31
|
}));
|
31
32
|
}
|
package/dist/cjs/lib/safe.d.ts
CHANGED
@@ -11,7 +11,7 @@ export declare class SafeContractSuite {
|
|
11
11
|
moduleSetup: Deployment;
|
12
12
|
entryPoint: Deployment;
|
13
13
|
constructor();
|
14
|
-
addressForSetup(setup: Hex, saltNonce
|
14
|
+
addressForSetup(setup: Hex, saltNonce: string): Promise<Address>;
|
15
15
|
getSetup(owners: string[]): Hex;
|
16
16
|
addOwnerData(newOwner: Address): Hex;
|
17
17
|
getOpHash(chainId: number, unsignedUserOp: UserOperation): Promise<Hash>;
|
package/dist/cjs/lib/safe.js
CHANGED
@@ -21,7 +21,7 @@ class SafeContractSuite {
|
|
21
21
|
async addressForSetup(setup, saltNonce) {
|
22
22
|
// bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
|
23
23
|
// cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L58
|
24
|
-
const salt = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["bytes32", "uint256"], [(0, viem_1.keccak256)(setup), BigInt(saltNonce
|
24
|
+
const salt = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["bytes32", "uint256"], [(0, viem_1.keccak256)(setup), BigInt(saltNonce)]));
|
25
25
|
// abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton)));
|
26
26
|
// cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L29
|
27
27
|
const initCode = (0, viem_1.encodePacked)(["bytes", "uint256"], [
|
@@ -43,18 +43,18 @@ class SafeContractSuite {
|
|
43
43
|
abi: this.singleton.abi,
|
44
44
|
functionName: "setup",
|
45
45
|
args: [
|
46
|
-
owners,
|
47
|
-
1, //
|
48
|
-
this.moduleSetup.address,
|
46
|
+
owners, // _owners
|
47
|
+
1, // _threshold
|
48
|
+
this.moduleSetup.address, // to
|
49
49
|
(0, viem_1.encodeFunctionData)({
|
50
50
|
abi: this.moduleSetup.abi,
|
51
51
|
functionName: "enableModules",
|
52
52
|
args: [[this.m4337.address]],
|
53
|
-
}),
|
54
|
-
this.m4337.address,
|
55
|
-
viem_1.zeroAddress,
|
56
|
-
0,
|
57
|
-
viem_1.zeroAddress,
|
53
|
+
}), // data
|
54
|
+
this.m4337.address, // fallbackHandler
|
55
|
+
viem_1.zeroAddress, // paymentToken
|
56
|
+
0, // payment
|
57
|
+
viem_1.zeroAddress, // paymentReceiver
|
58
58
|
],
|
59
59
|
});
|
60
60
|
}
|
package/dist/cjs/near-safe.d.ts
CHANGED
@@ -70,7 +70,7 @@ export declare class NearSafe {
|
|
70
70
|
buildTransaction(args: {
|
71
71
|
chainId: number;
|
72
72
|
transactions: MetaTransaction[];
|
73
|
-
|
73
|
+
sponsorshipPolicy?: string;
|
74
74
|
}): Promise<UserOperation>;
|
75
75
|
/**
|
76
76
|
* Signs a transaction with the NEAR adapter using the provided operation hash.
|
@@ -93,7 +93,7 @@ export declare class NearSafe {
|
|
93
93
|
* @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
|
94
94
|
* @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
|
95
95
|
*/
|
96
|
-
encodeSignRequest(signRequest: SignRequestData,
|
96
|
+
encodeSignRequest(signRequest: SignRequestData, sponsorshipPolicy?: string): Promise<EncodedTxData>;
|
97
97
|
/**
|
98
98
|
* Broadcasts a user operation to the EVM network with a provided signature.
|
99
99
|
* Warning: Uses a private ethRPC with sensitive Pimlico API key (should be run server side).
|
@@ -173,7 +173,7 @@ export declare class NearSafe {
|
|
173
173
|
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
174
174
|
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
175
175
|
*/
|
176
|
-
requestRouter({ method, chainId, params }: SignRequestData,
|
176
|
+
requestRouter({ method, chainId, params }: SignRequestData, sponsorshipPolicy?: string): Promise<{
|
177
177
|
evmMessage: string;
|
178
178
|
payload: number[];
|
179
179
|
hash: Hash;
|
package/dist/cjs/near-safe.js
CHANGED
@@ -18,7 +18,8 @@ class NearSafe {
|
|
18
18
|
* @returns {Promise<NearSafe>} - A promise that resolves to a new `NearSafe` instance.
|
19
19
|
*/
|
20
20
|
static async create(config) {
|
21
|
-
const { pimlicoKey
|
21
|
+
const { pimlicoKey } = config;
|
22
|
+
const safeSaltNonce = config.safeSaltNonce || constants_1.DEFAULT_SAFE_SALT_NONCE;
|
22
23
|
// const nearAdapter = await mockAdapter();
|
23
24
|
const nearAdapter = await (0, near_ca_1.setupAdapter)({ ...config });
|
24
25
|
const safePack = new safe_1.SafeContractSuite();
|
@@ -30,7 +31,7 @@ class NearSafe {
|
|
30
31
|
MPC EOA: ${nearAdapter.address}
|
31
32
|
Safe: ${safeAddress}
|
32
33
|
`);
|
33
|
-
return new NearSafe(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce
|
34
|
+
return new NearSafe(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce);
|
34
35
|
}
|
35
36
|
/**
|
36
37
|
* Constructs a new `NearSafe` object with the provided parameters.
|
@@ -87,7 +88,7 @@ class NearSafe {
|
|
87
88
|
* @throws {Error} - Throws an error if the transaction set is empty or if any operation fails during the building process.
|
88
89
|
*/
|
89
90
|
async buildTransaction(args) {
|
90
|
-
const { transactions,
|
91
|
+
const { transactions, sponsorshipPolicy, chainId } = args;
|
91
92
|
if (transactions.length === 0) {
|
92
93
|
throw new Error("Empty transaction set!");
|
93
94
|
}
|
@@ -101,7 +102,7 @@ class NearSafe {
|
|
101
102
|
// Build Singular MetaTransaction for Multisend from transaction list.
|
102
103
|
const tx = transactions.length > 1 ? (0, multisend_1.encodeMulti)(transactions) : transactions[0];
|
103
104
|
const rawUserOp = await this.safePack.buildUserOp(nonce, tx, this.address, gasFees.fast, this.setup, !safeDeployed, this.safeSaltNonce);
|
104
|
-
const paymasterData = await bundler.getPaymasterData(rawUserOp,
|
105
|
+
const paymasterData = await bundler.getPaymasterData(rawUserOp, !safeDeployed, sponsorshipPolicy);
|
105
106
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
106
107
|
return unsignedUserOp;
|
107
108
|
}
|
@@ -131,8 +132,8 @@ class NearSafe {
|
|
131
132
|
* @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
|
132
133
|
* @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
|
133
134
|
*/
|
134
|
-
async encodeSignRequest(signRequest,
|
135
|
-
const { payload, evmMessage, hash } = await this.requestRouter(signRequest,
|
135
|
+
async encodeSignRequest(signRequest, sponsorshipPolicy) {
|
136
|
+
const { payload, evmMessage, hash } = await this.requestRouter(signRequest, sponsorshipPolicy);
|
136
137
|
return {
|
137
138
|
nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
|
138
139
|
path: this.nearAdapter.derivationPath,
|
@@ -283,7 +284,7 @@ class NearSafe {
|
|
283
284
|
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
284
285
|
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
285
286
|
*/
|
286
|
-
async requestRouter({ method, chainId, params },
|
287
|
+
async requestRouter({ method, chainId, params }, sponsorshipPolicy) {
|
287
288
|
const safeInfo = {
|
288
289
|
address: { value: this.address },
|
289
290
|
chainId: chainId.toString(),
|
@@ -318,7 +319,7 @@ class NearSafe {
|
|
318
319
|
const userOp = await this.buildTransaction({
|
319
320
|
chainId,
|
320
321
|
transactions,
|
321
|
-
|
322
|
+
...(sponsorshipPolicy ? { sponsorshipPolicy } : {}),
|
322
323
|
});
|
323
324
|
const opHash = await this.opHash(chainId, userOp);
|
324
325
|
return {
|
package/dist/cjs/types.d.ts
CHANGED
@@ -80,7 +80,7 @@ export interface PaymasterData {
|
|
80
80
|
*/
|
81
81
|
export interface UserOptions {
|
82
82
|
/** Whether to use a paymaster for gas fee coverage. */
|
83
|
-
|
83
|
+
sponsorshipPolicy?: string;
|
84
84
|
/** The unique nonce used to differentiate multiple Safe setups. */
|
85
85
|
safeSaltNonce: string;
|
86
86
|
/** The NEAR contract ID for the MPC contract. */
|
package/dist/cjs/util.d.ts
CHANGED
@@ -11,4 +11,30 @@ export declare function isContract(address: Address, chainId: number): Promise<b
|
|
11
11
|
export declare function getClient(chainId: number): PublicClient;
|
12
12
|
export declare function metaTransactionsFromRequest(params: SessionRequestParams): MetaTransaction[];
|
13
13
|
export declare function saltNonceFromMessage(input: string): string;
|
14
|
+
/**
|
15
|
+
* Fetches the signature for a NEAR transaction hash. If an `accountId` is provided,
|
16
|
+
* it fetches the signature from the appropriate network. Otherwise, it races across
|
17
|
+
* both `testnet` and `mainnet`.
|
18
|
+
*
|
19
|
+
* @param {string} txHash - The NEAR transaction hash for which to fetch the signature.
|
20
|
+
* @param {string} [accountId] - (Optional) The account ID associated with the transaction.
|
21
|
+
* Providing this will reduce dangling promises as the network is determined by the account.
|
22
|
+
*
|
23
|
+
* @returns {Promise<Hex>} A promise that resolves to the hex-encoded signature.
|
24
|
+
*
|
25
|
+
* @throws Will throw an error if no signature is found for the given transaction hash.
|
26
|
+
*/
|
27
|
+
export declare function signatureFromTxHash(txHash: string, accountId?: string): Promise<Hex>;
|
28
|
+
/**
|
29
|
+
* Races an array of promises and resolves with the first promise that fulfills.
|
30
|
+
* If all promises reject, the function will reject with an error.
|
31
|
+
*
|
32
|
+
* @template T
|
33
|
+
* @param {Promise<T>[]} promises - An array of promises to race. Each promise should resolve to type `T`.
|
34
|
+
*
|
35
|
+
* @returns {Promise<T>} A promise that resolves to the value of the first successfully resolved promise.
|
36
|
+
*
|
37
|
+
* @throws Will throw an error if all promises reject with the message "All promises rejected".
|
38
|
+
*/
|
39
|
+
export declare function raceToFirstResolve<T>(promises: Promise<T>[]): Promise<T>;
|
14
40
|
export {};
|
package/dist/cjs/util.js
CHANGED
@@ -8,9 +8,10 @@ exports.isContract = isContract;
|
|
8
8
|
exports.getClient = getClient;
|
9
9
|
exports.metaTransactionsFromRequest = metaTransactionsFromRequest;
|
10
10
|
exports.saltNonceFromMessage = saltNonceFromMessage;
|
11
|
+
exports.signatureFromTxHash = signatureFromTxHash;
|
12
|
+
exports.raceToFirstResolve = raceToFirstResolve;
|
11
13
|
const near_ca_1 = require("near-ca");
|
12
14
|
const viem_1 = require("viem");
|
13
|
-
//
|
14
15
|
exports.PLACEHOLDER_SIG = (0, viem_1.encodePacked)(["uint48", "uint48"], [0, 0]);
|
15
16
|
const packGas = (hi, lo) => (0, viem_1.encodePacked)(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
16
17
|
exports.packGas = packGas;
|
@@ -66,3 +67,66 @@ function saltNonceFromMessage(input) {
|
|
66
67
|
// Return string for readability and transport.
|
67
68
|
return BigInt((0, viem_1.keccak256)((0, viem_1.toBytes)(input))).toString();
|
68
69
|
}
|
70
|
+
/**
|
71
|
+
* Fetches the signature for a NEAR transaction hash. If an `accountId` is provided,
|
72
|
+
* it fetches the signature from the appropriate network. Otherwise, it races across
|
73
|
+
* both `testnet` and `mainnet`.
|
74
|
+
*
|
75
|
+
* @param {string} txHash - The NEAR transaction hash for which to fetch the signature.
|
76
|
+
* @param {string} [accountId] - (Optional) The account ID associated with the transaction.
|
77
|
+
* Providing this will reduce dangling promises as the network is determined by the account.
|
78
|
+
*
|
79
|
+
* @returns {Promise<Hex>} A promise that resolves to the hex-encoded signature.
|
80
|
+
*
|
81
|
+
* @throws Will throw an error if no signature is found for the given transaction hash.
|
82
|
+
*/
|
83
|
+
async function signatureFromTxHash(txHash, accountId) {
|
84
|
+
if (accountId) {
|
85
|
+
const signature = await (0, near_ca_1.signatureFromTxHash)(`https://archival-rpc.${(0, near_ca_1.getNetworkId)(accountId)}.near.org`, txHash, accountId);
|
86
|
+
return packSignature((0, viem_1.serializeSignature)(signature));
|
87
|
+
}
|
88
|
+
try {
|
89
|
+
const signature = await raceToFirstResolve(["testnet", "mainnet"].map((network) => (0, near_ca_1.signatureFromTxHash)(archiveNode(network), txHash)));
|
90
|
+
return packSignature((0, viem_1.serializeSignature)(signature));
|
91
|
+
}
|
92
|
+
catch {
|
93
|
+
throw new Error(`No signature found for txHash ${txHash}`);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
/**
|
97
|
+
* Utility function to construct an archive node URL for a given NEAR network.
|
98
|
+
*
|
99
|
+
* @param {string} networkId - The ID of the NEAR network (e.g., 'testnet', 'mainnet').
|
100
|
+
*
|
101
|
+
* @returns {string} The full URL of the archival RPC node for the specified network.
|
102
|
+
*/
|
103
|
+
const archiveNode = (networkId) => `https://archival-rpc.${networkId}.near.org`;
|
104
|
+
/**
|
105
|
+
* Races an array of promises and resolves with the first promise that fulfills.
|
106
|
+
* If all promises reject, the function will reject with an error.
|
107
|
+
*
|
108
|
+
* @template T
|
109
|
+
* @param {Promise<T>[]} promises - An array of promises to race. Each promise should resolve to type `T`.
|
110
|
+
*
|
111
|
+
* @returns {Promise<T>} A promise that resolves to the value of the first successfully resolved promise.
|
112
|
+
*
|
113
|
+
* @throws Will throw an error if all promises reject with the message "All promises rejected".
|
114
|
+
*/
|
115
|
+
async function raceToFirstResolve(promises) {
|
116
|
+
return new Promise((resolve, reject) => {
|
117
|
+
let rejectionCount = 0;
|
118
|
+
const totalPromises = promises.length;
|
119
|
+
promises.forEach((promise) => {
|
120
|
+
// Wrap each promise so it only resolves when fulfilled
|
121
|
+
Promise.resolve(promise)
|
122
|
+
.then(resolve) // Resolve when any promise resolves
|
123
|
+
.catch(() => {
|
124
|
+
rejectionCount++;
|
125
|
+
// If all promises reject, reject the race with an error
|
126
|
+
if (rejectionCount === totalPromises) {
|
127
|
+
reject(new Error("All promises rejected"));
|
128
|
+
}
|
129
|
+
});
|
130
|
+
});
|
131
|
+
});
|
132
|
+
}
|
@@ -1,9 +1,12 @@
|
|
1
1
|
import { Address, Hash, PublicClient, Transport } from "viem";
|
2
2
|
import { GasPrices, PaymasterData, UnsignedUserOperation, UserOperation, UserOperationReceipt } from "../types";
|
3
|
+
type SponsorshipPolicy = {
|
4
|
+
sponsorshipPolicyId: string;
|
5
|
+
};
|
3
6
|
type BundlerRpcSchema = [
|
4
7
|
{
|
5
8
|
Method: "pm_sponsorUserOperation";
|
6
|
-
Parameters: [UnsignedUserOperation, Address];
|
9
|
+
Parameters: [UnsignedUserOperation, Address, SponsorshipPolicy];
|
7
10
|
ReturnType: PaymasterData;
|
8
11
|
},
|
9
12
|
{
|
@@ -28,7 +31,7 @@ export declare class Erc4337Bundler {
|
|
28
31
|
apiKey: string;
|
29
32
|
chainId: number;
|
30
33
|
constructor(entryPointAddress: Address, apiKey: string, chainId: number);
|
31
|
-
getPaymasterData(rawUserOp: UnsignedUserOperation,
|
34
|
+
getPaymasterData(rawUserOp: UnsignedUserOperation, safeNotDeployed: boolean, sponsorshipPolicy?: string): Promise<PaymasterData>;
|
32
35
|
sendUserOperation(userOp: UserOperation): Promise<Hash>;
|
33
36
|
getGasPrice(): Promise<GasPrices>;
|
34
37
|
getUserOpReceipt(userOpHash: Hash): Promise<UserOperationReceipt>;
|
package/dist/esm/lib/bundler.js
CHANGED
@@ -17,15 +17,16 @@ export class Erc4337Bundler {
|
|
17
17
|
rpcSchema: rpcSchema(),
|
18
18
|
});
|
19
19
|
}
|
20
|
-
async getPaymasterData(rawUserOp,
|
20
|
+
async getPaymasterData(rawUserOp, safeNotDeployed, sponsorshipPolicy) {
|
21
21
|
// TODO: Keep this option out of the bundler
|
22
|
-
if (
|
22
|
+
if (sponsorshipPolicy) {
|
23
23
|
console.log("Requesting paymaster data...");
|
24
24
|
return handleRequest(() => this.client.request({
|
25
25
|
method: "pm_sponsorUserOperation",
|
26
26
|
params: [
|
27
27
|
{ ...rawUserOp, signature: PLACEHOLDER_SIG },
|
28
28
|
this.entryPointAddress,
|
29
|
+
{ sponsorshipPolicyId: sponsorshipPolicy },
|
29
30
|
],
|
30
31
|
}));
|
31
32
|
}
|
package/dist/esm/lib/safe.d.ts
CHANGED
@@ -11,7 +11,7 @@ export declare class SafeContractSuite {
|
|
11
11
|
moduleSetup: Deployment;
|
12
12
|
entryPoint: Deployment;
|
13
13
|
constructor();
|
14
|
-
addressForSetup(setup: Hex, saltNonce
|
14
|
+
addressForSetup(setup: Hex, saltNonce: string): Promise<Address>;
|
15
15
|
getSetup(owners: string[]): Hex;
|
16
16
|
addOwnerData(newOwner: Address): Hex;
|
17
17
|
getOpHash(chainId: number, unsignedUserOp: UserOperation): Promise<Hash>;
|
package/dist/esm/lib/safe.js
CHANGED
@@ -25,7 +25,7 @@ export class SafeContractSuite {
|
|
25
25
|
async addressForSetup(setup, saltNonce) {
|
26
26
|
// bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
|
27
27
|
// cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L58
|
28
|
-
const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(setup), BigInt(saltNonce
|
28
|
+
const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(setup), BigInt(saltNonce)]));
|
29
29
|
// abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton)));
|
30
30
|
// cf: https://github.com/safe-global/safe-smart-account/blob/499b17ad0191b575fcadc5cb5b8e3faeae5391ae/contracts/proxies/SafeProxyFactory.sol#L29
|
31
31
|
const initCode = encodePacked(["bytes", "uint256"], [
|
@@ -47,18 +47,18 @@ export class SafeContractSuite {
|
|
47
47
|
abi: this.singleton.abi,
|
48
48
|
functionName: "setup",
|
49
49
|
args: [
|
50
|
-
owners,
|
51
|
-
1, //
|
52
|
-
this.moduleSetup.address,
|
50
|
+
owners, // _owners
|
51
|
+
1, // _threshold
|
52
|
+
this.moduleSetup.address, // to
|
53
53
|
encodeFunctionData({
|
54
54
|
abi: this.moduleSetup.abi,
|
55
55
|
functionName: "enableModules",
|
56
56
|
args: [[this.m4337.address]],
|
57
|
-
}),
|
58
|
-
this.m4337.address,
|
59
|
-
zeroAddress,
|
60
|
-
0,
|
61
|
-
zeroAddress,
|
57
|
+
}), // data
|
58
|
+
this.m4337.address, // fallbackHandler
|
59
|
+
zeroAddress, // paymentToken
|
60
|
+
0, // payment
|
61
|
+
zeroAddress, // paymentReceiver
|
62
62
|
],
|
63
63
|
});
|
64
64
|
}
|
package/dist/esm/near-safe.d.ts
CHANGED
@@ -70,7 +70,7 @@ export declare class NearSafe {
|
|
70
70
|
buildTransaction(args: {
|
71
71
|
chainId: number;
|
72
72
|
transactions: MetaTransaction[];
|
73
|
-
|
73
|
+
sponsorshipPolicy?: string;
|
74
74
|
}): Promise<UserOperation>;
|
75
75
|
/**
|
76
76
|
* Signs a transaction with the NEAR adapter using the provided operation hash.
|
@@ -93,7 +93,7 @@ export declare class NearSafe {
|
|
93
93
|
* @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
|
94
94
|
* @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
|
95
95
|
*/
|
96
|
-
encodeSignRequest(signRequest: SignRequestData,
|
96
|
+
encodeSignRequest(signRequest: SignRequestData, sponsorshipPolicy?: string): Promise<EncodedTxData>;
|
97
97
|
/**
|
98
98
|
* Broadcasts a user operation to the EVM network with a provided signature.
|
99
99
|
* Warning: Uses a private ethRPC with sensitive Pimlico API key (should be run server side).
|
@@ -173,7 +173,7 @@ export declare class NearSafe {
|
|
173
173
|
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
174
174
|
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
175
175
|
*/
|
176
|
-
requestRouter({ method, chainId, params }: SignRequestData,
|
176
|
+
requestRouter({ method, chainId, params }: SignRequestData, sponsorshipPolicy?: string): Promise<{
|
177
177
|
evmMessage: string;
|
178
178
|
payload: number[];
|
179
179
|
hash: Hash;
|
package/dist/esm/near-safe.js
CHANGED
@@ -21,7 +21,8 @@ export class NearSafe {
|
|
21
21
|
* @returns {Promise<NearSafe>} - A promise that resolves to a new `NearSafe` instance.
|
22
22
|
*/
|
23
23
|
static async create(config) {
|
24
|
-
const { pimlicoKey
|
24
|
+
const { pimlicoKey } = config;
|
25
|
+
const safeSaltNonce = config.safeSaltNonce || DEFAULT_SAFE_SALT_NONCE;
|
25
26
|
// const nearAdapter = await mockAdapter();
|
26
27
|
const nearAdapter = await setupAdapter({ ...config });
|
27
28
|
const safePack = new SafeContractSuite();
|
@@ -33,7 +34,7 @@ export class NearSafe {
|
|
33
34
|
MPC EOA: ${nearAdapter.address}
|
34
35
|
Safe: ${safeAddress}
|
35
36
|
`);
|
36
|
-
return new NearSafe(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce
|
37
|
+
return new NearSafe(nearAdapter, safePack, pimlicoKey, setup, safeAddress, safeSaltNonce);
|
37
38
|
}
|
38
39
|
/**
|
39
40
|
* Constructs a new `NearSafe` object with the provided parameters.
|
@@ -90,7 +91,7 @@ export class NearSafe {
|
|
90
91
|
* @throws {Error} - Throws an error if the transaction set is empty or if any operation fails during the building process.
|
91
92
|
*/
|
92
93
|
async buildTransaction(args) {
|
93
|
-
const { transactions,
|
94
|
+
const { transactions, sponsorshipPolicy, chainId } = args;
|
94
95
|
if (transactions.length === 0) {
|
95
96
|
throw new Error("Empty transaction set!");
|
96
97
|
}
|
@@ -104,7 +105,7 @@ export class NearSafe {
|
|
104
105
|
// Build Singular MetaTransaction for Multisend from transaction list.
|
105
106
|
const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
|
106
107
|
const rawUserOp = await this.safePack.buildUserOp(nonce, tx, this.address, gasFees.fast, this.setup, !safeDeployed, this.safeSaltNonce);
|
107
|
-
const paymasterData = await bundler.getPaymasterData(rawUserOp,
|
108
|
+
const paymasterData = await bundler.getPaymasterData(rawUserOp, !safeDeployed, sponsorshipPolicy);
|
108
109
|
const unsignedUserOp = { ...rawUserOp, ...paymasterData };
|
109
110
|
return unsignedUserOp;
|
110
111
|
}
|
@@ -134,8 +135,8 @@ export class NearSafe {
|
|
134
135
|
* @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
|
135
136
|
* @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
|
136
137
|
*/
|
137
|
-
async encodeSignRequest(signRequest,
|
138
|
-
const { payload, evmMessage, hash } = await this.requestRouter(signRequest,
|
138
|
+
async encodeSignRequest(signRequest, sponsorshipPolicy) {
|
139
|
+
const { payload, evmMessage, hash } = await this.requestRouter(signRequest, sponsorshipPolicy);
|
139
140
|
return {
|
140
141
|
nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
|
141
142
|
path: this.nearAdapter.derivationPath,
|
@@ -286,7 +287,7 @@ export class NearSafe {
|
|
286
287
|
* - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
|
287
288
|
* the payload (hashed data), and recovery data needed for reconstructing the signature request.
|
288
289
|
*/
|
289
|
-
async requestRouter({ method, chainId, params },
|
290
|
+
async requestRouter({ method, chainId, params }, sponsorshipPolicy) {
|
290
291
|
const safeInfo = {
|
291
292
|
address: { value: this.address },
|
292
293
|
chainId: chainId.toString(),
|
@@ -321,7 +322,7 @@ export class NearSafe {
|
|
321
322
|
const userOp = await this.buildTransaction({
|
322
323
|
chainId,
|
323
324
|
transactions,
|
324
|
-
|
325
|
+
...(sponsorshipPolicy ? { sponsorshipPolicy } : {}),
|
325
326
|
});
|
326
327
|
const opHash = await this.opHash(chainId, userOp);
|
327
328
|
return {
|
package/dist/esm/types.d.ts
CHANGED
@@ -80,7 +80,7 @@ export interface PaymasterData {
|
|
80
80
|
*/
|
81
81
|
export interface UserOptions {
|
82
82
|
/** Whether to use a paymaster for gas fee coverage. */
|
83
|
-
|
83
|
+
sponsorshipPolicy?: string;
|
84
84
|
/** The unique nonce used to differentiate multiple Safe setups. */
|
85
85
|
safeSaltNonce: string;
|
86
86
|
/** The NEAR contract ID for the MPC contract. */
|
package/dist/esm/util.d.ts
CHANGED
@@ -11,4 +11,30 @@ export declare function isContract(address: Address, chainId: number): Promise<b
|
|
11
11
|
export declare function getClient(chainId: number): PublicClient;
|
12
12
|
export declare function metaTransactionsFromRequest(params: SessionRequestParams): MetaTransaction[];
|
13
13
|
export declare function saltNonceFromMessage(input: string): string;
|
14
|
+
/**
|
15
|
+
* Fetches the signature for a NEAR transaction hash. If an `accountId` is provided,
|
16
|
+
* it fetches the signature from the appropriate network. Otherwise, it races across
|
17
|
+
* both `testnet` and `mainnet`.
|
18
|
+
*
|
19
|
+
* @param {string} txHash - The NEAR transaction hash for which to fetch the signature.
|
20
|
+
* @param {string} [accountId] - (Optional) The account ID associated with the transaction.
|
21
|
+
* Providing this will reduce dangling promises as the network is determined by the account.
|
22
|
+
*
|
23
|
+
* @returns {Promise<Hex>} A promise that resolves to the hex-encoded signature.
|
24
|
+
*
|
25
|
+
* @throws Will throw an error if no signature is found for the given transaction hash.
|
26
|
+
*/
|
27
|
+
export declare function signatureFromTxHash(txHash: string, accountId?: string): Promise<Hex>;
|
28
|
+
/**
|
29
|
+
* Races an array of promises and resolves with the first promise that fulfills.
|
30
|
+
* If all promises reject, the function will reject with an error.
|
31
|
+
*
|
32
|
+
* @template T
|
33
|
+
* @param {Promise<T>[]} promises - An array of promises to race. Each promise should resolve to type `T`.
|
34
|
+
*
|
35
|
+
* @returns {Promise<T>} A promise that resolves to the value of the first successfully resolved promise.
|
36
|
+
*
|
37
|
+
* @throws Will throw an error if all promises reject with the message "All promises rejected".
|
38
|
+
*/
|
39
|
+
export declare function raceToFirstResolve<T>(promises: Promise<T>[]): Promise<T>;
|
14
40
|
export {};
|
package/dist/esm/util.js
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
import { Network } from "near-ca";
|
2
|
-
import { concatHex, encodePacked, toHex, isHex, parseTransaction, zeroAddress, toBytes, keccak256, } from "viem";
|
3
|
-
//
|
1
|
+
import { getNetworkId, Network, signatureFromTxHash as sigFromHash, } from "near-ca";
|
2
|
+
import { concatHex, encodePacked, toHex, isHex, parseTransaction, zeroAddress, toBytes, keccak256, serializeSignature, } from "viem";
|
4
3
|
export const PLACEHOLDER_SIG = encodePacked(["uint48", "uint48"], [0, 0]);
|
5
4
|
export const packGas = (hi, lo) => encodePacked(["uint128", "uint128"], [BigInt(hi), BigInt(lo)]);
|
6
5
|
export function packSignature(signature, validFrom = 0, validTo = 0) {
|
@@ -55,3 +54,66 @@ export function saltNonceFromMessage(input) {
|
|
55
54
|
// Return string for readability and transport.
|
56
55
|
return BigInt(keccak256(toBytes(input))).toString();
|
57
56
|
}
|
57
|
+
/**
|
58
|
+
* Fetches the signature for a NEAR transaction hash. If an `accountId` is provided,
|
59
|
+
* it fetches the signature from the appropriate network. Otherwise, it races across
|
60
|
+
* both `testnet` and `mainnet`.
|
61
|
+
*
|
62
|
+
* @param {string} txHash - The NEAR transaction hash for which to fetch the signature.
|
63
|
+
* @param {string} [accountId] - (Optional) The account ID associated with the transaction.
|
64
|
+
* Providing this will reduce dangling promises as the network is determined by the account.
|
65
|
+
*
|
66
|
+
* @returns {Promise<Hex>} A promise that resolves to the hex-encoded signature.
|
67
|
+
*
|
68
|
+
* @throws Will throw an error if no signature is found for the given transaction hash.
|
69
|
+
*/
|
70
|
+
export async function signatureFromTxHash(txHash, accountId) {
|
71
|
+
if (accountId) {
|
72
|
+
const signature = await sigFromHash(`https://archival-rpc.${getNetworkId(accountId)}.near.org`, txHash, accountId);
|
73
|
+
return packSignature(serializeSignature(signature));
|
74
|
+
}
|
75
|
+
try {
|
76
|
+
const signature = await raceToFirstResolve(["testnet", "mainnet"].map((network) => sigFromHash(archiveNode(network), txHash)));
|
77
|
+
return packSignature(serializeSignature(signature));
|
78
|
+
}
|
79
|
+
catch {
|
80
|
+
throw new Error(`No signature found for txHash ${txHash}`);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
/**
|
84
|
+
* Utility function to construct an archive node URL for a given NEAR network.
|
85
|
+
*
|
86
|
+
* @param {string} networkId - The ID of the NEAR network (e.g., 'testnet', 'mainnet').
|
87
|
+
*
|
88
|
+
* @returns {string} The full URL of the archival RPC node for the specified network.
|
89
|
+
*/
|
90
|
+
const archiveNode = (networkId) => `https://archival-rpc.${networkId}.near.org`;
|
91
|
+
/**
|
92
|
+
* Races an array of promises and resolves with the first promise that fulfills.
|
93
|
+
* If all promises reject, the function will reject with an error.
|
94
|
+
*
|
95
|
+
* @template T
|
96
|
+
* @param {Promise<T>[]} promises - An array of promises to race. Each promise should resolve to type `T`.
|
97
|
+
*
|
98
|
+
* @returns {Promise<T>} A promise that resolves to the value of the first successfully resolved promise.
|
99
|
+
*
|
100
|
+
* @throws Will throw an error if all promises reject with the message "All promises rejected".
|
101
|
+
*/
|
102
|
+
export async function raceToFirstResolve(promises) {
|
103
|
+
return new Promise((resolve, reject) => {
|
104
|
+
let rejectionCount = 0;
|
105
|
+
const totalPromises = promises.length;
|
106
|
+
promises.forEach((promise) => {
|
107
|
+
// Wrap each promise so it only resolves when fulfilled
|
108
|
+
Promise.resolve(promise)
|
109
|
+
.then(resolve) // Resolve when any promise resolves
|
110
|
+
.catch(() => {
|
111
|
+
rejectionCount++;
|
112
|
+
// If all promises reject, reject the race with an error
|
113
|
+
if (rejectionCount === totalPromises) {
|
114
|
+
reject(new Error("All promises rejected"));
|
115
|
+
}
|
116
|
+
});
|
117
|
+
});
|
118
|
+
});
|
119
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "near-safe",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.7.1",
|
4
4
|
"license": "MIT",
|
5
5
|
"description": "An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.",
|
6
6
|
"author": "bh2smith",
|
@@ -36,15 +36,15 @@
|
|
36
36
|
"start": "yarn example",
|
37
37
|
"example": "tsx examples/send-tx.ts",
|
38
38
|
"lint": "eslint . --ignore-pattern dist/",
|
39
|
-
"test": "jest",
|
40
39
|
"fmt": "prettier --write '{src,examples,tests}/**/*.{js,jsx,ts,tsx}' && yarn lint --fix",
|
40
|
+
"test": "jest",
|
41
41
|
"all": "yarn fmt && yarn lint && yarn build"
|
42
42
|
},
|
43
43
|
"dependencies": {
|
44
44
|
"@safe-global/safe-gateway-typescript-sdk": "^3.22.2",
|
45
45
|
"ethers-multisend": "^3.1.0",
|
46
46
|
"near-api-js": "^5.0.0",
|
47
|
-
"near-ca": "^0.5.
|
47
|
+
"near-ca": "^0.5.10",
|
48
48
|
"semver": "^7.6.3",
|
49
49
|
"viem": "^2.16.5"
|
50
50
|
},
|