@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 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
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "5.3.1";
6
+ var version = "5.4.0";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "5.3.1";
2
+ var version = "5.4.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/ethereum-gasless-core",
3
- "version": "5.3.1",
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.1118",
18
- "@dynamic-labs/assert-package-version": "5.3.1"
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 resolvedAuthorization = await resolveAuthorization.resolveAuthorization({ authorization, autoDelegate, walletAddress }, deps);
24
- const nonce = providedNonce ?? (await generateIntentNonce.generateIntentNonce({ walletAddress }, deps));
25
- const deadline = BigInt(Math.floor(Date.now() / 1000) + validForSeconds);
26
- const { relayerAddress } = await getAvailableEvmGaslessRelayer.getAvailableEvmGaslessRelayer({ chainId }, deps);
27
- const signature = await walletClient.signTypedData({
28
- account: walletClient.account,
29
- domain: {
30
- chainId,
31
- verifyingContract: constants.DELEGATION_CONTRACT_ADDRESS,
32
- },
33
- message: {
34
- calls: calls.map((c) => ({
35
- data: c.data,
36
- target: c.target,
37
- value: c.value,
38
- })),
39
- deadline,
40
- mode: constants.BATCH_CALL_OPDATA_AUTH_MODE,
41
- nonce,
42
- relayer: relayerAddress,
43
- },
44
- primaryType: 'AuthorizedExecutions',
45
- types: constants.AUTHORIZED_EXECUTIONS_TYPES,
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 resolvedAuthorization = await resolveAuthorization({ authorization, autoDelegate, walletAddress }, deps);
20
- const nonce = providedNonce ?? (await generateIntentNonce({ walletAddress }, deps));
21
- const deadline = BigInt(Math.floor(Date.now() / 1000) + validForSeconds);
22
- const { relayerAddress } = await getAvailableEvmGaslessRelayer({ chainId }, deps);
23
- const signature = await walletClient.signTypedData({
24
- account: walletClient.account,
25
- domain: {
26
- chainId,
27
- verifyingContract: DELEGATION_CONTRACT_ADDRESS,
28
- },
29
- message: {
30
- calls: calls.map((c) => ({
31
- data: c.data,
32
- target: c.target,
33
- value: c.value,
34
- })),
35
- deadline,
36
- mode: BATCH_CALL_OPDATA_AUTH_MODE,
37
- nonce,
38
- relayer: relayerAddress,
39
- },
40
- primaryType: 'AuthorizedExecutions',
41
- types: AUTHORIZED_EXECUTIONS_TYPES,
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 MAX_POLLING_ATTEMPTS = TIMEOUT_MS / POLL_INTERVAL_MS;
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
- * Polls `getEVMSponsoredTransactionStatus` until the relay reports the
18
- * transaction has been included on-chain, then returns the transaction hash.
20
+ * Waits until the relay reports the transaction has been included on-chain,
21
+ * then returns the transaction hash.
19
22
  *
20
- * Throws if the relay returns a terminal failure or if polling times out.
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
- for (let attempt = 0; attempt < MAX_POLLING_ATTEMPTS; attempt++) {
24
- const result = await getEVMSponsoredTransactionStatus.getEVMSponsoredTransactionStatus({ requestId }, deps);
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
- if (attempt < MAX_POLLING_ATTEMPTS - 1) {
32
- await sleep(POLL_INTERVAL_MS);
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
- * Polls `getEVMSponsoredTransactionStatus` until the relay reports the
9
- * transaction has been included on-chain, then returns the transaction hash.
8
+ * Waits until the relay reports the transaction has been included on-chain,
9
+ * then returns the transaction hash.
10
10
  *
11
- * Throws if the relay returns a terminal failure or if polling times out.
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 MAX_POLLING_ATTEMPTS = TIMEOUT_MS / POLL_INTERVAL_MS;
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
- * Polls `getEVMSponsoredTransactionStatus` until the relay reports the
14
- * transaction has been included on-chain, then returns the transaction hash.
16
+ * Waits until the relay reports the transaction has been included on-chain,
17
+ * then returns the transaction hash.
15
18
  *
16
- * Throws if the relay returns a terminal failure or if polling times out.
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
- for (let attempt = 0; attempt < MAX_POLLING_ATTEMPTS; attempt++) {
20
- const result = await getEVMSponsoredTransactionStatus({ requestId }, deps);
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
- if (attempt < MAX_POLLING_ATTEMPTS - 1) {
28
- await sleep(POLL_INTERVAL_MS);
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}`);