@dynamic-labs/ethereum-gasless-core 5.3.1 → 5.4.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.
- package/CHANGELOG.md +39 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +3 -3
- package/src/lib/getEVMSponsoredTransactionStatus/getEVMSponsoredTransactionStatus.cjs +2 -1
- package/src/lib/getEVMSponsoredTransactionStatus/getEVMSponsoredTransactionStatus.d.ts +7 -1
- package/src/lib/getEVMSponsoredTransactionStatus/getEVMSponsoredTransactionStatus.js +2 -1
- package/src/lib/signSponsoredTransaction/signSponsoredTransaction.cjs +30 -24
- package/src/lib/signSponsoredTransaction/signSponsoredTransaction.js +30 -24
- package/src/lib/waitForSponsoredTransaction/waitForSponsoredTransaction.cjs +21 -9
- package/src/lib/waitForSponsoredTransaction/waitForSponsoredTransaction.d.ts +4 -3
- package/src/lib/waitForSponsoredTransaction/waitForSponsoredTransaction.js +21 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,43 @@
|
|
|
1
1
|
|
|
2
|
+
## [5.4.0](https://github.com/dynamic-labs/dynamic-auth/compare/v5.3.2...v5.4.0) (2026-08-25)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **aleo:** wire up signMessage on the WaaS Aleo connector ([#12235](https://github.com/dynamic-labs/dynamic-auth/issues/12235)) ([e56cbf5](https://github.com/dynamic-labs/dynamic-auth/commit/e56cbf5546bdcc21ce916dece43ae36dcea9b808))
|
|
8
|
+
* **waas:** thread isRawScalarImport through importPrivateKey for ed25519 chains ([#12220](https://github.com/dynamic-labs/dynamic-auth/issues/12220)) ([ced9efa](https://github.com/dynamic-labs/dynamic-auth/commit/ced9efac5fa5a4a1280383419f56fe22733722cd))
|
|
9
|
+
* **webview:** add policies message contract and controller ([#12248](https://github.com/dynamic-labs/dynamic-auth/issues/12248)) ([8557c1e](https://github.com/dynamic-labs/dynamic-auth/commit/8557c1ed3861e12f1f06b2bb610184813b577daa))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **bitcoin:** pass the wallet's address type when signing ([#12265](https://github.com/dynamic-labs/dynamic-auth/issues/12265)) ([b8a8eab](https://github.com/dynamic-labs/dynamic-auth/commit/b8a8eab7e4cc294cc307fb10ea6814e76111a274))
|
|
15
|
+
* remediate high-severity tar vulnerability and prune a dead override ([#12268](https://github.com/dynamic-labs/dynamic-auth/issues/12268)) ([6b1b343](https://github.com/dynamic-labs/dynamic-auth/commit/6b1b343e7947e8da30012446677e342ae9bfe14a))
|
|
16
|
+
* **waas-svm:** fix retry-classification and environmentId gaps in [#12240](https://github.com/dynamic-labs/dynamic-auth/issues/12240) ([#12247](https://github.com/dynamic-labs/dynamic-auth/issues/12247)) ([e79cecc](https://github.com/dynamic-labs/dynamic-auth/commit/e79ceccd424c48c3a7f45fdd6ea288c269ac0171))
|
|
17
|
+
* **wallet-connect:** reopen WalletConnect IndexedDB when the browser closes the connection ([#12262](https://github.com/dynamic-labs/dynamic-auth/issues/12262)) ([1a20dde](https://github.com/dynamic-labs/dynamic-auth/commit/1a20ddeaeec590ae3390c9e47ef3c14738379b83))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Performance Improvements
|
|
21
|
+
|
|
22
|
+
* **ethereum-gasless-core:** parallel 7702 auth signing and long-poll sponsored-tx status ([#12225](https://github.com/dynamic-labs/dynamic-auth/issues/12225)) ([53ca916](https://github.com/dynamic-labs/dynamic-auth/commit/53ca91668f8dd839c52d55a0e5d7bb5257f7f415))
|
|
23
|
+
|
|
24
|
+
### [5.3.2](https://github.com/dynamic-labs/dynamic-auth/compare/v5.3.1...v5.3.2) (2026-08-19)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* **bitcoin:** add allowedSighash to embedded wallet signPsbt ([#12233](https://github.com/dynamic-labs/dynamic-auth/issues/12233)) ([d0ff829](https://github.com/dynamic-labs/dynamic-auth/commit/d0ff8291a3d1771d5ca356172acc10af94349b15))
|
|
30
|
+
* **bitcoin:** forward signingIndexes to embedded PSBT signing ([#12211](https://github.com/dynamic-labs/dynamic-auth/issues/12211)) ([733da3d](https://github.com/dynamic-labs/dynamic-auth/commit/733da3d32f39a9ae6d813df84fcff6350ba12eb5))
|
|
31
|
+
* expose createOfflineRecoveryShares SDK function in webview and legacy client ([#12239](https://github.com/dynamic-labs/dynamic-auth/issues/12239)) ([c8056e8](https://github.com/dynamic-labs/dynamic-auth/commit/c8056e8d5a1c66ac0146b046bc7cc198de118f0b))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* **bitcoin:** parse PSBTs with the active network's address encoding ([#12219](https://github.com/dynamic-labs/dynamic-auth/issues/12219)) ([8b151e2](https://github.com/dynamic-labs/dynamic-auth/commit/8b151e23440094cfa0d48728e1d0f19de567ca05))
|
|
37
|
+
* **bitcoin:** stop ensureActiveAccount from resetting a switched network ([#12215](https://github.com/dynamic-labs/dynamic-auth/issues/12215)) ([86668b7](https://github.com/dynamic-labs/dynamic-auth/commit/86668b79d23cd89b7e96518551cdaecab7dd5ce3))
|
|
38
|
+
* remediate high-severity dependency vulnerabilities ([#12238](https://github.com/dynamic-labs/dynamic-auth/issues/12238)) ([a4fc78c](https://github.com/dynamic-labs/dynamic-auth/commit/a4fc78ca27ee44ce6a1038c4e0d07f55528b4994))
|
|
39
|
+
* **waas-svm:** retry SVM gas sponsorship before falling back, then throw ([#12240](https://github.com/dynamic-labs/dynamic-auth/issues/12240)) ([f599a7a](https://github.com/dynamic-labs/dynamic-auth/commit/f599a7af55727e06373772acf574ee7c43e56bbb))
|
|
40
|
+
|
|
2
41
|
### [5.3.1](https://github.com/dynamic-labs/dynamic-auth/compare/v5.3.0...v5.3.1) (2026-08-14)
|
|
3
42
|
|
|
4
43
|
## [5.3.0](https://github.com/dynamic-labs/dynamic-auth/compare/v5.2.0...v5.3.0) (2026-08-12)
|
package/package.cjs
CHANGED
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/ethereum-gasless-core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.0",
|
|
4
4
|
"main": "./src/index.cjs",
|
|
5
5
|
"module": "./src/index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"./package.json": "./package.json"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@dynamic-labs/sdk-api-core": "0.0
|
|
18
|
-
"@dynamic-labs/assert-package-version": "5.
|
|
17
|
+
"@dynamic-labs/sdk-api-core": "0.12.0",
|
|
18
|
+
"@dynamic-labs/assert-package-version": "5.4.0"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"viem": "^2.45.3"
|
|
@@ -13,12 +13,13 @@ var SponsorTransactionError = require('../errors/SponsorTransactionError/Sponsor
|
|
|
13
13
|
* as the building block for custom polling flows; for the common "wait until
|
|
14
14
|
* done" case prefer `waitForSponsoredTransaction`.
|
|
15
15
|
*/
|
|
16
|
-
const getEVMSponsoredTransactionStatus = async ({ requestId }, { sdkApi, environmentId }) => {
|
|
16
|
+
const getEVMSponsoredTransactionStatus = async ({ requestId, waitSeconds }, { sdkApi, environmentId }) => {
|
|
17
17
|
let data;
|
|
18
18
|
try {
|
|
19
19
|
data = await sdkApi.getEVMSponsoredTransactionStatus({
|
|
20
20
|
environmentId,
|
|
21
21
|
requestId,
|
|
22
|
+
...(waitSeconds ? { wait: waitSeconds } : {}),
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
25
|
catch (error) {
|
|
@@ -2,6 +2,12 @@ import type { EvmGaslessApiDeps, SponsoredTransactionStatusResult } from '../typ
|
|
|
2
2
|
type GetEVMSponsoredTransactionStatusParams = {
|
|
3
3
|
/** The request ID returned by `relaySponsoredTransaction` / `sendSponsoredTransaction`. */
|
|
4
4
|
requestId: string;
|
|
5
|
+
/**
|
|
6
|
+
* Seconds to let the API hold the response open while the transaction is
|
|
7
|
+
* still unresolved, so the hash comes back as soon as it exists instead of on
|
|
8
|
+
* a later call. Omitted or `0` responds immediately.
|
|
9
|
+
*/
|
|
10
|
+
waitSeconds?: number;
|
|
5
11
|
};
|
|
6
12
|
/**
|
|
7
13
|
* Fetches the current relay state for a sponsored EVM transaction.
|
|
@@ -11,5 +17,5 @@ type GetEVMSponsoredTransactionStatusParams = {
|
|
|
11
17
|
* as the building block for custom polling flows; for the common "wait until
|
|
12
18
|
* done" case prefer `waitForSponsoredTransaction`.
|
|
13
19
|
*/
|
|
14
|
-
export declare const getEVMSponsoredTransactionStatus: ({ requestId }: GetEVMSponsoredTransactionStatusParams, { sdkApi, environmentId }: EvmGaslessApiDeps) => Promise<SponsoredTransactionStatusResult>;
|
|
20
|
+
export declare const getEVMSponsoredTransactionStatus: ({ requestId, waitSeconds }: GetEVMSponsoredTransactionStatusParams, { sdkApi, environmentId }: EvmGaslessApiDeps) => Promise<SponsoredTransactionStatusResult>;
|
|
15
21
|
export {};
|
|
@@ -9,12 +9,13 @@ import { SponsorTransactionError } from '../errors/SponsorTransactionError/Spons
|
|
|
9
9
|
* as the building block for custom polling flows; for the common "wait until
|
|
10
10
|
* done" case prefer `waitForSponsoredTransaction`.
|
|
11
11
|
*/
|
|
12
|
-
const getEVMSponsoredTransactionStatus = async ({ requestId }, { sdkApi, environmentId }) => {
|
|
12
|
+
const getEVMSponsoredTransactionStatus = async ({ requestId, waitSeconds }, { sdkApi, environmentId }) => {
|
|
13
13
|
let data;
|
|
14
14
|
try {
|
|
15
15
|
data = await sdkApi.getEVMSponsoredTransactionStatus({
|
|
16
16
|
environmentId,
|
|
17
17
|
requestId,
|
|
18
|
+
...(waitSeconds ? { wait: waitSeconds } : {}),
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
catch (error) {
|
|
@@ -20,30 +20,36 @@ var toSerializedAuthorization = require('../toSerializedAuthorization/toSerializ
|
|
|
20
20
|
const signSponsoredTransaction = async ({ authorization, autoDelegate = true, calls, nonce: providedNonce, validForSeconds = constants.DEFAULT_VALID_FOR_SECONDS, }, deps) => {
|
|
21
21
|
const { chainId, walletClient } = deps;
|
|
22
22
|
const walletAddress = walletClient.account.address;
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
23
|
+
const authPromise = resolveAuthorization.resolveAuthorization({ authorization, autoDelegate, walletAddress }, deps);
|
|
24
|
+
const signPromise = (async () => {
|
|
25
|
+
const [nonce, { relayerAddress }] = await Promise.all([
|
|
26
|
+
providedNonce ?? generateIntentNonce.generateIntentNonce({ walletAddress }, deps),
|
|
27
|
+
getAvailableEvmGaslessRelayer.getAvailableEvmGaslessRelayer({ chainId }, deps),
|
|
28
|
+
]);
|
|
29
|
+
const deadline = BigInt(Math.floor(Date.now() / 1000) + validForSeconds);
|
|
30
|
+
const signature = await walletClient.signTypedData({
|
|
31
|
+
account: walletClient.account,
|
|
32
|
+
domain: {
|
|
33
|
+
chainId,
|
|
34
|
+
verifyingContract: constants.DELEGATION_CONTRACT_ADDRESS,
|
|
35
|
+
},
|
|
36
|
+
message: {
|
|
37
|
+
calls: calls.map((c) => ({
|
|
38
|
+
data: c.data,
|
|
39
|
+
target: c.target,
|
|
40
|
+
value: c.value,
|
|
41
|
+
})),
|
|
42
|
+
deadline,
|
|
43
|
+
mode: constants.BATCH_CALL_OPDATA_AUTH_MODE,
|
|
44
|
+
nonce,
|
|
45
|
+
relayer: relayerAddress,
|
|
46
|
+
},
|
|
47
|
+
primaryType: 'AuthorizedExecutions',
|
|
48
|
+
types: constants.AUTHORIZED_EXECUTIONS_TYPES,
|
|
49
|
+
});
|
|
50
|
+
return { deadline, nonce, relayerAddress, signature };
|
|
51
|
+
})();
|
|
52
|
+
const [resolvedAuthorization, { deadline, nonce, relayerAddress, signature },] = await Promise.all([authPromise, signPromise]);
|
|
47
53
|
return {
|
|
48
54
|
authorization: resolvedAuthorization
|
|
49
55
|
? toSerializedAuthorization.toSerializedAuthorization(resolvedAuthorization)
|
|
@@ -16,30 +16,36 @@ import { toSerializedAuthorization } from '../toSerializedAuthorization/toSerial
|
|
|
16
16
|
const signSponsoredTransaction = async ({ authorization, autoDelegate = true, calls, nonce: providedNonce, validForSeconds = DEFAULT_VALID_FOR_SECONDS, }, deps) => {
|
|
17
17
|
const { chainId, walletClient } = deps;
|
|
18
18
|
const walletAddress = walletClient.account.address;
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
19
|
+
const authPromise = resolveAuthorization({ authorization, autoDelegate, walletAddress }, deps);
|
|
20
|
+
const signPromise = (async () => {
|
|
21
|
+
const [nonce, { relayerAddress }] = await Promise.all([
|
|
22
|
+
providedNonce ?? generateIntentNonce({ walletAddress }, deps),
|
|
23
|
+
getAvailableEvmGaslessRelayer({ chainId }, deps),
|
|
24
|
+
]);
|
|
25
|
+
const deadline = BigInt(Math.floor(Date.now() / 1000) + validForSeconds);
|
|
26
|
+
const signature = await walletClient.signTypedData({
|
|
27
|
+
account: walletClient.account,
|
|
28
|
+
domain: {
|
|
29
|
+
chainId,
|
|
30
|
+
verifyingContract: DELEGATION_CONTRACT_ADDRESS,
|
|
31
|
+
},
|
|
32
|
+
message: {
|
|
33
|
+
calls: calls.map((c) => ({
|
|
34
|
+
data: c.data,
|
|
35
|
+
target: c.target,
|
|
36
|
+
value: c.value,
|
|
37
|
+
})),
|
|
38
|
+
deadline,
|
|
39
|
+
mode: BATCH_CALL_OPDATA_AUTH_MODE,
|
|
40
|
+
nonce,
|
|
41
|
+
relayer: relayerAddress,
|
|
42
|
+
},
|
|
43
|
+
primaryType: 'AuthorizedExecutions',
|
|
44
|
+
types: AUTHORIZED_EXECUTIONS_TYPES,
|
|
45
|
+
});
|
|
46
|
+
return { deadline, nonce, relayerAddress, signature };
|
|
47
|
+
})();
|
|
48
|
+
const [resolvedAuthorization, { deadline, nonce, relayerAddress, signature },] = await Promise.all([authPromise, signPromise]);
|
|
43
49
|
return {
|
|
44
50
|
authorization: resolvedAuthorization
|
|
45
51
|
? toSerializedAuthorization(resolvedAuthorization)
|
|
@@ -6,30 +6,42 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
6
6
|
var SponsorTransactionError = require('../errors/SponsorTransactionError/SponsorTransactionError.cjs');
|
|
7
7
|
var getEVMSponsoredTransactionStatus = require('../getEVMSponsoredTransactionStatus/getEVMSponsoredTransactionStatus.cjs');
|
|
8
8
|
|
|
9
|
-
const POLL_INTERVAL_MS = 2000;
|
|
10
9
|
const TIMEOUT_MS = 60000;
|
|
11
|
-
const
|
|
10
|
+
const MAX_WAIT_SECONDS = 10;
|
|
11
|
+
// Floor between requests. The API answers immediately instead of holding when
|
|
12
|
+
// a transaction already has too many waiters, and the loop would spin against
|
|
13
|
+
// that without this.
|
|
14
|
+
const MIN_REQUEST_INTERVAL_MS = 1000;
|
|
12
15
|
const TERMINAL_FAILURE_STATUSES = new Set([
|
|
13
16
|
'failure',
|
|
14
17
|
]);
|
|
15
18
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
19
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
20
|
+
* Waits until the relay reports the transaction has been included on-chain,
|
|
21
|
+
* then returns the transaction hash.
|
|
19
22
|
*
|
|
20
|
-
*
|
|
23
|
+
* Each request asks the API to hold the response open until the transaction
|
|
24
|
+
* resolves, so the hash comes back as soon as it exists.
|
|
21
25
|
*/
|
|
22
26
|
const waitForSponsoredTransaction = async ({ requestId }, deps) => {
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
28
|
+
while (Date.now() < deadline) {
|
|
29
|
+
const startedAt = Date.now();
|
|
30
|
+
// Never ask for a hold that would outlive the deadline; under a second
|
|
31
|
+
// left means asking for none at all. The clamp matters because the
|
|
32
|
+
// deadline can pass between the loop check and here.
|
|
33
|
+
const waitSeconds = Math.max(0, Math.min(MAX_WAIT_SECONDS, Math.floor((deadline - startedAt) / 1000)));
|
|
34
|
+
const result = await getEVMSponsoredTransactionStatus.getEVMSponsoredTransactionStatus({ requestId, waitSeconds }, deps);
|
|
25
35
|
if (TERMINAL_FAILURE_STATUSES.has(result.status)) {
|
|
26
36
|
throw new SponsorTransactionError.SponsorTransactionError(null, result.errorMessage);
|
|
27
37
|
}
|
|
28
38
|
if (result.transactionHash) {
|
|
29
39
|
return { transactionHash: result.transactionHash };
|
|
30
40
|
}
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
const elapsed = Date.now() - startedAt;
|
|
42
|
+
const remaining = deadline - Date.now();
|
|
43
|
+
if (remaining > 0 && elapsed < MIN_REQUEST_INTERVAL_MS) {
|
|
44
|
+
await sleep(Math.min(MIN_REQUEST_INTERVAL_MS - elapsed, remaining));
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
throw new SponsorTransactionError.SponsorTransactionError(null, `Timed out waiting for sponsored transaction ${requestId}`);
|
|
@@ -5,10 +5,11 @@ type WaitForSponsoredTransactionParams = {
|
|
|
5
5
|
requestId: string;
|
|
6
6
|
};
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Waits until the relay reports the transaction has been included on-chain,
|
|
9
|
+
* then returns the transaction hash.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Each request asks the API to hold the response open until the transaction
|
|
12
|
+
* resolves, so the hash comes back as soon as it exists.
|
|
12
13
|
*/
|
|
13
14
|
export declare const waitForSponsoredTransaction: ({ requestId }: WaitForSponsoredTransactionParams, deps: EvmGaslessApiDeps) => Promise<{
|
|
14
15
|
transactionHash: Hex;
|
|
@@ -2,30 +2,42 @@
|
|
|
2
2
|
import { SponsorTransactionError } from '../errors/SponsorTransactionError/SponsorTransactionError.js';
|
|
3
3
|
import { getEVMSponsoredTransactionStatus } from '../getEVMSponsoredTransactionStatus/getEVMSponsoredTransactionStatus.js';
|
|
4
4
|
|
|
5
|
-
const POLL_INTERVAL_MS = 2000;
|
|
6
5
|
const TIMEOUT_MS = 60000;
|
|
7
|
-
const
|
|
6
|
+
const MAX_WAIT_SECONDS = 10;
|
|
7
|
+
// Floor between requests. The API answers immediately instead of holding when
|
|
8
|
+
// a transaction already has too many waiters, and the loop would spin against
|
|
9
|
+
// that without this.
|
|
10
|
+
const MIN_REQUEST_INTERVAL_MS = 1000;
|
|
8
11
|
const TERMINAL_FAILURE_STATUSES = new Set([
|
|
9
12
|
'failure',
|
|
10
13
|
]);
|
|
11
14
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
16
|
+
* Waits until the relay reports the transaction has been included on-chain,
|
|
17
|
+
* then returns the transaction hash.
|
|
15
18
|
*
|
|
16
|
-
*
|
|
19
|
+
* Each request asks the API to hold the response open until the transaction
|
|
20
|
+
* resolves, so the hash comes back as soon as it exists.
|
|
17
21
|
*/
|
|
18
22
|
const waitForSponsoredTransaction = async ({ requestId }, deps) => {
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
24
|
+
while (Date.now() < deadline) {
|
|
25
|
+
const startedAt = Date.now();
|
|
26
|
+
// Never ask for a hold that would outlive the deadline; under a second
|
|
27
|
+
// left means asking for none at all. The clamp matters because the
|
|
28
|
+
// deadline can pass between the loop check and here.
|
|
29
|
+
const waitSeconds = Math.max(0, Math.min(MAX_WAIT_SECONDS, Math.floor((deadline - startedAt) / 1000)));
|
|
30
|
+
const result = await getEVMSponsoredTransactionStatus({ requestId, waitSeconds }, deps);
|
|
21
31
|
if (TERMINAL_FAILURE_STATUSES.has(result.status)) {
|
|
22
32
|
throw new SponsorTransactionError(null, result.errorMessage);
|
|
23
33
|
}
|
|
24
34
|
if (result.transactionHash) {
|
|
25
35
|
return { transactionHash: result.transactionHash };
|
|
26
36
|
}
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
const elapsed = Date.now() - startedAt;
|
|
38
|
+
const remaining = deadline - Date.now();
|
|
39
|
+
if (remaining > 0 && elapsed < MIN_REQUEST_INTERVAL_MS) {
|
|
40
|
+
await sleep(Math.min(MIN_REQUEST_INTERVAL_MS - elapsed, remaining));
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
throw new SponsorTransactionError(null, `Timed out waiting for sponsored transaction ${requestId}`);
|