near-safe 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
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>;
@@ -17,15 +17,16 @@ class Erc4337Bundler {
17
17
  rpcSchema: (0, viem_1.rpcSchema)(),
18
18
  });
19
19
  }
20
- async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
20
+ async getPaymasterData(rawUserOp, safeNotDeployed, sponsorshipPolicy) {
21
21
  // TODO: Keep this option out of the bundler
22
- if (usePaymaster) {
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
  }
@@ -70,7 +70,7 @@ export declare class NearSafe {
70
70
  buildTransaction(args: {
71
71
  chainId: number;
72
72
  transactions: MetaTransaction[];
73
- usePaymaster: boolean;
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, usePaymaster: boolean): Promise<EncodedTxData>;
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, usePaymaster: boolean): Promise<{
176
+ requestRouter({ method, chainId, params }: SignRequestData, sponsorshipPolicy?: string): Promise<{
177
177
  evmMessage: string;
178
178
  payload: number[];
179
179
  hash: Hash;
@@ -87,7 +87,7 @@ class NearSafe {
87
87
  * @throws {Error} - Throws an error if the transaction set is empty or if any operation fails during the building process.
88
88
  */
89
89
  async buildTransaction(args) {
90
- const { transactions, usePaymaster, chainId } = args;
90
+ const { transactions, sponsorshipPolicy, chainId } = args;
91
91
  if (transactions.length === 0) {
92
92
  throw new Error("Empty transaction set!");
93
93
  }
@@ -101,7 +101,7 @@ class NearSafe {
101
101
  // Build Singular MetaTransaction for Multisend from transaction list.
102
102
  const tx = transactions.length > 1 ? (0, multisend_1.encodeMulti)(transactions) : transactions[0];
103
103
  const rawUserOp = await this.safePack.buildUserOp(nonce, tx, this.address, gasFees.fast, this.setup, !safeDeployed, this.safeSaltNonce);
104
- const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, !safeDeployed);
104
+ const paymasterData = await bundler.getPaymasterData(rawUserOp, !safeDeployed, sponsorshipPolicy);
105
105
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
106
106
  return unsignedUserOp;
107
107
  }
@@ -131,8 +131,8 @@ class NearSafe {
131
131
  * @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
132
132
  * @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
133
133
  */
134
- async encodeSignRequest(signRequest, usePaymaster) {
135
- const { payload, evmMessage, hash } = await this.requestRouter(signRequest, usePaymaster);
134
+ async encodeSignRequest(signRequest, sponsorshipPolicy) {
135
+ const { payload, evmMessage, hash } = await this.requestRouter(signRequest, sponsorshipPolicy);
136
136
  return {
137
137
  nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
138
138
  path: this.nearAdapter.derivationPath,
@@ -283,7 +283,7 @@ class NearSafe {
283
283
  * - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
284
284
  * the payload (hashed data), and recovery data needed for reconstructing the signature request.
285
285
  */
286
- async requestRouter({ method, chainId, params }, usePaymaster) {
286
+ async requestRouter({ method, chainId, params }, sponsorshipPolicy) {
287
287
  const safeInfo = {
288
288
  address: { value: this.address },
289
289
  chainId: chainId.toString(),
@@ -318,7 +318,7 @@ class NearSafe {
318
318
  const userOp = await this.buildTransaction({
319
319
  chainId,
320
320
  transactions,
321
- usePaymaster,
321
+ ...(sponsorshipPolicy ? { sponsorshipPolicy } : {}),
322
322
  });
323
323
  const opHash = await this.opHash(chainId, userOp);
324
324
  return {
@@ -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
- usePaymaster: boolean;
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. */
@@ -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, usePaymaster: boolean, safeNotDeployed: boolean): Promise<PaymasterData>;
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>;
@@ -17,15 +17,16 @@ export class Erc4337Bundler {
17
17
  rpcSchema: rpcSchema(),
18
18
  });
19
19
  }
20
- async getPaymasterData(rawUserOp, usePaymaster, safeNotDeployed) {
20
+ async getPaymasterData(rawUserOp, safeNotDeployed, sponsorshipPolicy) {
21
21
  // TODO: Keep this option out of the bundler
22
- if (usePaymaster) {
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
  }
@@ -70,7 +70,7 @@ export declare class NearSafe {
70
70
  buildTransaction(args: {
71
71
  chainId: number;
72
72
  transactions: MetaTransaction[];
73
- usePaymaster: boolean;
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, usePaymaster: boolean): Promise<EncodedTxData>;
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, usePaymaster: boolean): Promise<{
176
+ requestRouter({ method, chainId, params }: SignRequestData, sponsorshipPolicy?: string): Promise<{
177
177
  evmMessage: string;
178
178
  payload: number[];
179
179
  hash: Hash;
@@ -90,7 +90,7 @@ export class NearSafe {
90
90
  * @throws {Error} - Throws an error if the transaction set is empty or if any operation fails during the building process.
91
91
  */
92
92
  async buildTransaction(args) {
93
- const { transactions, usePaymaster, chainId } = args;
93
+ const { transactions, sponsorshipPolicy, chainId } = args;
94
94
  if (transactions.length === 0) {
95
95
  throw new Error("Empty transaction set!");
96
96
  }
@@ -104,7 +104,7 @@ export class NearSafe {
104
104
  // Build Singular MetaTransaction for Multisend from transaction list.
105
105
  const tx = transactions.length > 1 ? encodeMulti(transactions) : transactions[0];
106
106
  const rawUserOp = await this.safePack.buildUserOp(nonce, tx, this.address, gasFees.fast, this.setup, !safeDeployed, this.safeSaltNonce);
107
- const paymasterData = await bundler.getPaymasterData(rawUserOp, usePaymaster, !safeDeployed);
107
+ const paymasterData = await bundler.getPaymasterData(rawUserOp, !safeDeployed, sponsorshipPolicy);
108
108
  const unsignedUserOp = { ...rawUserOp, ...paymasterData };
109
109
  return unsignedUserOp;
110
110
  }
@@ -134,8 +134,8 @@ export class NearSafe {
134
134
  * @param {boolean} usePaymaster - Flag indicating whether to use a paymaster for gas fees. If true, the transaction will be sponsored by the paymaster.
135
135
  * @returns {Promise<EncodedTxData>} - A promise that resolves to the encoded transaction data for the NEAR and EVM networks.
136
136
  */
137
- async encodeSignRequest(signRequest, usePaymaster) {
138
- const { payload, evmMessage, hash } = await this.requestRouter(signRequest, usePaymaster);
137
+ async encodeSignRequest(signRequest, sponsorshipPolicy) {
138
+ const { payload, evmMessage, hash } = await this.requestRouter(signRequest, sponsorshipPolicy);
139
139
  return {
140
140
  nearPayload: await this.nearAdapter.mpcContract.encodeSignatureRequestTx({
141
141
  path: this.nearAdapter.derivationPath,
@@ -286,7 +286,7 @@ export class NearSafe {
286
286
  * - Returns a promise that resolves to an object containing the Ethereum Virtual Machine (EVM) message,
287
287
  * the payload (hashed data), and recovery data needed for reconstructing the signature request.
288
288
  */
289
- async requestRouter({ method, chainId, params }, usePaymaster) {
289
+ async requestRouter({ method, chainId, params }, sponsorshipPolicy) {
290
290
  const safeInfo = {
291
291
  address: { value: this.address },
292
292
  chainId: chainId.toString(),
@@ -321,7 +321,7 @@ export class NearSafe {
321
321
  const userOp = await this.buildTransaction({
322
322
  chainId,
323
323
  transactions,
324
- usePaymaster,
324
+ ...(sponsorshipPolicy ? { sponsorshipPolicy } : {}),
325
325
  });
326
326
  const opHash = await this.opHash(chainId, userOp);
327
327
  return {
@@ -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
- usePaymaster: boolean;
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. */
@@ -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.6.2",
3
+ "version": "0.7.0",
4
4
  "license": "MIT",
5
5
  "description": "An SDK for controlling Ethereum Smart Accounts via ERC4337 from a Near Account.",
6
6
  "author": "bh2smith",
@@ -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.9",
47
+ "near-ca": "^0.5.10",
48
48
  "semver": "^7.6.3",
49
49
  "viem": "^2.16.5"
50
50
  },