@dynamic-labs/aleo 4.80.0 → 4.82.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,42 @@
1
1
 
2
+ ## [4.82.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.81.0...v4.82.0) (2026-05-11)
3
+
4
+
5
+ ### Features
6
+
7
+ * adds react-native embedded webview ([#11168](https://github.com/dynamic-labs/dynamic-auth/issues/11168)) ([31c17e3](https://github.com/dynamic-labs/dynamic-auth/commit/31c17e34e198efc806299011ab286bf48a244908))
8
+ * **aleo:** expose WaaS-specific methods directly on AleoWallet ([#11174](https://github.com/dynamic-labs/dynamic-auth/issues/11174)) ([ccfb8f4](https://github.com/dynamic-labs/dynamic-auth/commit/ccfb8f4de690ee4a30b6c20e037fef51a69df8e1))
9
+ * **aleo:** two-step Exchange Send (unshield → public→public) ([#11172](https://github.com/dynamic-labs/dynamic-auth/issues/11172)) ([02a992c](https://github.com/dynamic-labs/dynamic-auth/commit/02a992c3239519ed283a9804f092bffca9ba4d18))
10
+ * **demo:** registry-driven Aleo transactions form ([#11105](https://github.com/dynamic-labs/dynamic-auth/issues/11105)) ([766949e](https://github.com/dynamic-labs/dynamic-auth/commit/766949e7bb9f613e157418ed4221cac43fbf0394))
11
+ * **sdk-react-core:** add ActiveMidnightWalletAddresses widget ([#11083](https://github.com/dynamic-labs/dynamic-auth/issues/11083)) ([1858292](https://github.com/dynamic-labs/dynamic-auth/commit/18582926348420332f89a3d1b2c2ca2cd453f69e))
12
+ * **widget:** auto-merge + auto-shield sponsored Aleo tokens on wallet load ([#11157](https://github.com/dynamic-labs/dynamic-auth/issues/11157)) ([cda1de1](https://github.com/dynamic-labs/dynamic-auth/commit/cda1de1f04789eb24bba2fbe0a49b5f42f6a609d))
13
+ * **widget:** auto-shield indicator + post-shield balance refresh polling ([#11173](https://github.com/dynamic-labs/dynamic-auth/issues/11173)) ([f49d480](https://github.com/dynamic-labs/dynamic-auth/commit/f49d480dc5ef85efd7d201687d9b0309090a69c8))
14
+ * **widget:** wire Aleo curated prices into the Shielded balance tab ([#11178](https://github.com/dynamic-labs/dynamic-auth/issues/11178)) ([eab1606](https://github.com/dynamic-labs/dynamic-auth/commit/eab16062140a0474d14a2cc43fd0a1624e4da92e))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **ci:** wire dd-trace ci visibility env vars for [@demov1](https://github.com/demov1) playwright task ([#11199](https://github.com/dynamic-labs/dynamic-auth/issues/11199)) ([9af4827](https://github.com/dynamic-labs/dynamic-auth/commit/9af48271e4721dac54d495097d43522f76ec724d))
20
+ * **playwright:** expose raw auth token for stable JWT reads ([#11204](https://github.com/dynamic-labs/dynamic-auth/issues/11204)) ([b002dfb](https://github.com/dynamic-labs/dynamic-auth/commit/b002dfb642755830c6c9c813ebb4d749882518d6))
21
+ * **playwright:** replace waitForTimeout in networkControl + reinitialize spec ([#11161](https://github.com/dynamic-labs/dynamic-auth/issues/11161)) ([5e72371](https://github.com/dynamic-labs/dynamic-auth/commit/5e723713ceb8c804d253c261be86abb997daeabd))
22
+ * remediate high-severity dependency vulnerabilities ([#11171](https://github.com/dynamic-labs/dynamic-auth/issues/11171)) ([285e7cb](https://github.com/dynamic-labs/dynamic-auth/commit/285e7cb0d07f957a97e8d9ef4fab0b8d2d24e031))
23
+
24
+ ## [4.81.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.80.0...v4.81.0) (2026-05-07)
25
+
26
+
27
+ ### Features
28
+
29
+ * **react-native:** add waitForAuthSuccess() to auth module ([#11137](https://github.com/dynamic-labs/dynamic-auth/issues/11137)) ([089a566](https://github.com/dynamic-labs/dynamic-auth/commit/089a5663283639e7e425eead291b135010c8b398))
30
+ * **waas:** hook Aleo into DynamicWaasMixin ([#11102](https://github.com/dynamic-labs/dynamic-auth/issues/11102)) ([ff42df9](https://github.com/dynamic-labs/dynamic-auth/commit/ff42df99d8993e22894caee3c0570cd9c332a3d1))
31
+ * **widget:** shielded/unshielded tabs + Shield Manually CTA on ActiveWalletBalance for Aleo ([#11103](https://github.com/dynamic-labs/dynamic-auth/issues/11103)) ([e278836](https://github.com/dynamic-labs/dynamic-auth/commit/e278836bfcb19c13828c10483ebb87d165f5d00d))
32
+
33
+
34
+ ### Bug Fixes
35
+
36
+ * **ethereum-aa:** return hex chain ids from EIP-5792 getCapabilities ([#11146](https://github.com/dynamic-labs/dynamic-auth/issues/11146)) ([b32dc8f](https://github.com/dynamic-labs/dynamic-auth/commit/b32dc8f6fdee3722073921787c4f77908ab4f740))
37
+ * remediate high-severity dependency vulnerabilities ([#11151](https://github.com/dynamic-labs/dynamic-auth/issues/11151)) ([1d84ef1](https://github.com/dynamic-labs/dynamic-auth/commit/1d84ef12e10544be0b2a80dbbbd63f615b03adc3))
38
+ * **wagmi-connector:** emit change unconditionally on MM accountsChanged DYNT-549 ([#11131](https://github.com/dynamic-labs/dynamic-auth/issues/11131)) ([348ee6f](https://github.com/dynamic-labs/dynamic-auth/commit/348ee6ff9a22cb55ef0cb84c4b8f8d2e843bd10e)), closes [#11043](https://github.com/dynamic-labs/dynamic-auth/issues/11043)
39
+
2
40
  ## [4.80.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.79.2...v4.80.0) (2026-05-05)
3
41
 
4
42
 
package/package.cjs CHANGED
@@ -3,6 +3,6 @@
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
- var version = "4.80.0";
6
+ var version = "4.82.0";
7
7
 
8
8
  exports.version = version;
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use client'
2
- var version = "4.80.0";
2
+ var version = "4.82.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/aleo",
3
- "version": "4.80.0",
3
+ "version": "4.82.0",
4
4
  "description": "A React SDK for implementing wallet web3 authentication and authorization to your website.",
5
5
  "author": "Dynamic Labs, Inc.",
6
6
  "license": "MIT",
@@ -19,18 +19,18 @@
19
19
  "homepage": "https://www.dynamic.xyz/",
20
20
  "dependencies": {
21
21
  "@dynamic-labs-sdk/client": "0.26.9",
22
- "@dynamic-labs/assert-package-version": "4.80.0",
22
+ "@dynamic-labs/assert-package-version": "4.82.0",
23
23
  "@dynamic-labs/sdk-api-core": "0.0.964",
24
24
  "@provablehq/aleo-wallet-adaptor-core": "0.3.0-alpha.3",
25
25
  "@provablehq/aleo-wallet-adaptor-shield": "0.3.0-alpha.3",
26
26
  "@provablehq/aleo-wallet-standard": "0.3.0-alpha.3",
27
27
  "@provablehq/aleo-types": "0.3.0-alpha.3",
28
- "@dynamic-labs/logger": "4.80.0",
29
- "@dynamic-labs/types": "4.80.0",
30
- "@dynamic-labs/utils": "4.80.0",
31
- "@dynamic-labs/waas": "4.80.0",
32
- "@dynamic-labs/wallet-book": "4.80.0",
33
- "@dynamic-labs/wallet-connector-core": "4.80.0"
28
+ "@dynamic-labs/logger": "4.82.0",
29
+ "@dynamic-labs/types": "4.82.0",
30
+ "@dynamic-labs/utils": "4.82.0",
31
+ "@dynamic-labs/waas": "4.82.0",
32
+ "@dynamic-labs/wallet-book": "4.82.0",
33
+ "@dynamic-labs/wallet-connector-core": "4.82.0"
34
34
  },
35
35
  "peerDependencies": {}
36
36
  }
@@ -99,6 +99,46 @@ const buildTransferInputs = (args) => {
99
99
  inputs: [to, `${value.toString()}u128`, recordPlaintext],
100
100
  };
101
101
  };
102
+ /**
103
+ * Builds the `(functionName, inputs, inputTypes)` triple for a
104
+ * `<program>/transfer_public` public-to-public transfer. Used as step 2
105
+ * of the Exchange flow (after step 1 unshield-to-self). `transfer_public`
106
+ * is NOT in the iframe's `SEALANCE_FUNCTIONS_REQUIRING_PROOF` set, so the
107
+ * caller controls inputs end-to-end (no Sealance proof injection).
108
+ *
109
+ * - `credits` u64. inputs `[recipient, amount]`.
110
+ * - `stablecoin` u128. inputs `[recipient, amount]`.
111
+ * - `arc21` u128. inputs `[token_id, recipient, amount]`. The
112
+ * `token_registry.aleo/transfer_public` transition keys
113
+ * off `token_id` since there's no record to encode it in.
114
+ */
115
+ const buildPublicTransferInputs = (args) => {
116
+ const { to, value, token } = args;
117
+ const functionName = 'transfer_public';
118
+ if (token.programKind === 'credits') {
119
+ return {
120
+ functionName,
121
+ inputTypes: ['address.public', 'u64.public'],
122
+ inputs: [to, `${value.toString()}u64`],
123
+ };
124
+ }
125
+ if (token.programKind === 'stablecoin') {
126
+ return {
127
+ functionName,
128
+ inputTypes: ['address.public', 'u128.public'],
129
+ inputs: [to, `${value.toString()}u128`],
130
+ };
131
+ }
132
+ // arc21 — token_registry.aleo/transfer_public(token_id, recipient, amount).
133
+ if (!token.tokenId) {
134
+ throw new utils.DynamicError(`transfer_public: ARC-21 token ${token.contractAddress} is missing tokenId in registry.`);
135
+ }
136
+ return {
137
+ functionName,
138
+ inputTypes: ['field.public', 'address.public', 'u128.public'],
139
+ inputs: [token.tokenId, to, `${value.toString()}u128`],
140
+ };
141
+ };
102
142
  /**
103
143
  * Builds the `(functionName, inputs, inputTypes)` triple for a
104
144
  * `<program>/transfer_public_to_private` shield call to self. Mirrors
@@ -156,6 +196,16 @@ const joinShapeForProgram = (programId) => {
156
196
  }
157
197
  return shape;
158
198
  };
199
+ // Two-step Exchange flow timing. Step 1 (`transfer_private_to_public` to
200
+ // self) needs to surface in the user's public balance before step 2
201
+ // (`transfer_public` to the exchange) can spend it. Aleo block time is
202
+ // ~10s plus a finalize phase; we wait `STEP1_INITIAL_DELAY_MS` and then
203
+ // retry step 2 up to `STEP2_MAX_RETRIES` times with a fixed backoff.
204
+ // The most likely retryable failure is "insufficient public balance"
205
+ // if step 1 hasn't finalized yet.
206
+ const EXCHANGE_STEP1_INITIAL_DELAY_MS = 15000;
207
+ const EXCHANGE_STEP2_MAX_RETRIES = 3;
208
+ const EXCHANGE_STEP2_RETRY_DELAY_MS = 10000;
159
209
  const hasUsableRecordShape = (r) => {
160
210
  const rec = r;
161
211
  return (typeof (rec === null || rec === void 0 ? void 0 : rec.record_plaintext) === 'string' &&
@@ -177,6 +227,12 @@ class DynamicWaasAleoConnector extends waas.withDynamicWaas(WaasAleoWalletConnec
177
227
  this.overrideKey = 'dynamicwaas';
178
228
  this.isEmbeddedWallet = true;
179
229
  this.logger = new logger.Logger('DynamicWaasAleoConnector');
230
+ // Programs whose Exchange flow is mid-orchestration (between step 1
231
+ // unshield-to-self and step 2 public-to-public). The auto-shield hook
232
+ // consults `canShieldToken` per balance refresh — gating shield here
233
+ // prevents the hook from re-shielding the just-unshielded amount before
234
+ // step 2 spends it. Add on Exchange entry, remove in `finally`.
235
+ this.tokensPendingExchange = new Set();
180
236
  this.walletUiUtils = props.walletUiUtils;
181
237
  }
182
238
  setVerifiedCredentials(verifiedCredentials) {
@@ -633,6 +689,15 @@ class DynamicWaasAleoConnector extends waas.withDynamicWaas(WaasAleoWalletConnec
633
689
  // which validateActiveWallet has already aligned with `from`.
634
690
  // Setting it explicitly keeps the contract obvious to readers.
635
691
  this.activeAccountAddress = from;
692
+ if (mode === 'exchange') {
693
+ return this.submitExchangeTransfer({
694
+ from,
695
+ recordPlaintext,
696
+ to,
697
+ token,
698
+ value,
699
+ });
700
+ }
636
701
  const { functionName, inputs, inputTypes } = buildTransferInputs({
637
702
  mode,
638
703
  recordPlaintext,
@@ -668,7 +733,15 @@ class DynamicWaasAleoConnector extends waas.withDynamicWaas(WaasAleoWalletConnec
668
733
  * `credits.aleo` entry when the literal address misses.
669
734
  */
670
735
  canShieldToken(token) {
671
- return Boolean(this.resolveShieldableToken(token));
736
+ const resolved = this.resolveShieldableToken(token);
737
+ if (!resolved)
738
+ return false;
739
+ // While an Exchange flow for this program is mid-orchestration the
740
+ // user's public balance is transient — auto-shield must not consume
741
+ // it before step 2's public→public transfer.
742
+ if (this.tokensPendingExchange.has(resolved.programId))
743
+ return false;
744
+ return true;
672
745
  }
673
746
  /**
674
747
  * True when the Feemaster currently sponsors a shield (
@@ -781,6 +854,91 @@ class DynamicWaasAleoConnector extends waas.withDynamicWaas(WaasAleoWalletConnec
781
854
  return result.txId;
782
855
  });
783
856
  }
857
+ /**
858
+ * Two-step Exchange transfer.
859
+ *
860
+ * Step 1: `<program>/transfer_private_to_public(record, SELF, amount)`
861
+ * — unshield the record into the user's *own* public balance.
862
+ * Step 2: `<program>/transfer_public(EXCHANGE, amount)`
863
+ * — public→public from the user to the exchange recipient.
864
+ *
865
+ * `tokensPendingExchange` gates `canShieldToken` for the duration of
866
+ * the orchestration so the auto-shield hook does not consume the
867
+ * just-unshielded amount before step 2 spends it. The set is cleared
868
+ * in `finally` regardless of outcome.
869
+ *
870
+ * Step 2 is retried on failure because Aleo's finalize phase can lag
871
+ * the broadcast — until step 1 has finalized, step 2 reads stale public
872
+ * balance and the validator rejects it. We wait
873
+ * `EXCHANGE_STEP1_INITIAL_DELAY_MS` first and then retry up to
874
+ * `EXCHANGE_STEP2_MAX_RETRIES` times. If all retries exhaust, we throw a
875
+ * `DynamicError` that explicitly tells the user step 1 succeeded so they
876
+ * can recover the funds via a manual public→public transfer.
877
+ *
878
+ * Step 2's txId is returned as the user-facing transaction id (it's the
879
+ * one that lands the funds at the exchange).
880
+ */
881
+ submitExchangeTransfer(args) {
882
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
883
+ var _a, _b, _c, _d, _e, _f;
884
+ const { from, to, value, recordPlaintext, token } = args;
885
+ this.tokensPendingExchange.add(token.programId);
886
+ try {
887
+ // --- Step 1: unshield record → self's public balance ---
888
+ const step1Build = buildTransferInputs({
889
+ mode: 'exchange',
890
+ recordPlaintext,
891
+ to: from,
892
+ token,
893
+ value,
894
+ });
895
+ const step1Result = yield this.proveTransaction({
896
+ broadcast: true,
897
+ functionName: step1Build.functionName,
898
+ inputTypes: step1Build.inputTypes,
899
+ inputs: step1Build.inputs,
900
+ programId: token.programId,
901
+ });
902
+ if (!step1Result.txId) {
903
+ throw new utils.DynamicError('Aleo Exchange step 1 (unshield) did not return a transaction id.');
904
+ }
905
+ (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `[exchangeTransfer:${token.programId}] step 1 (unshield) tx: ${step1Result.txId}`);
906
+ // Wait for step 1 to finalize and surface in public balance before
907
+ // attempting step 2. See `EXCHANGE_STEP1_INITIAL_DELAY_MS` notes.
908
+ yield new Promise((resolve) => setTimeout(resolve, EXCHANGE_STEP1_INITIAL_DELAY_MS));
909
+ // --- Step 2: public→public self → exchange ---
910
+ const step2Build = buildPublicTransferInputs({ to, token, value });
911
+ let lastErr;
912
+ for (let attempt = 0; attempt <= EXCHANGE_STEP2_MAX_RETRIES; attempt += 1) {
913
+ try {
914
+ const step2Result = yield this.proveTransaction({
915
+ broadcast: true,
916
+ functionName: step2Build.functionName,
917
+ inputTypes: step2Build.inputTypes,
918
+ inputs: step2Build.inputs,
919
+ programId: token.programId,
920
+ });
921
+ if (!step2Result.txId) {
922
+ throw new utils.DynamicError('Aleo Exchange step 2 (public transfer) did not return a transaction id.');
923
+ }
924
+ (_d = (_c = this.logger).debug) === null || _d === void 0 ? void 0 : _d.call(_c, `[exchangeTransfer:${token.programId}] step 2 (public transfer) tx: ${step2Result.txId}`);
925
+ return step2Result.txId;
926
+ }
927
+ catch (err) {
928
+ lastErr = err;
929
+ (_f = (_e = this.logger).debug) === null || _f === void 0 ? void 0 : _f.call(_e, `[exchangeTransfer:${token.programId}] step 2 attempt ${attempt + 1} failed: ${err instanceof Error ? err.message : String(err)}`);
930
+ if (attempt < EXCHANGE_STEP2_MAX_RETRIES) {
931
+ yield new Promise((resolve) => setTimeout(resolve, EXCHANGE_STEP2_RETRY_DELAY_MS));
932
+ }
933
+ }
934
+ }
935
+ throw new utils.DynamicError(`Aleo Exchange step 2 (public transfer) failed after ${EXCHANGE_STEP2_MAX_RETRIES + 1} attempts. Step 1 unshield (tx ${step1Result.txId}) succeeded — your funds are now in your public balance and can be sent to the exchange via a manual public transfer. Last error: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}`);
936
+ }
937
+ finally {
938
+ this.tokensPendingExchange.delete(token.programId);
939
+ }
940
+ });
941
+ }
784
942
  endSession(reason) {
785
943
  const _super = Object.create(null, {
786
944
  endSession: { get: () => super.endSession }
@@ -175,6 +175,7 @@ export declare class DynamicWaasAleoConnector extends DynamicWaasAleoConnector_b
175
175
  activeAccountAddress: string | undefined;
176
176
  verifiedCredentials: JwtVerifiedCredential[] | undefined;
177
177
  protected walletUiUtils: WalletUiUtils<InternalWalletConnector>;
178
+ private readonly tokensPendingExchange;
178
179
  constructor(props: DynamicWaasAleoConnectorProps);
179
180
  setVerifiedCredentials(verifiedCredentials: JwtVerifiedCredential[]): void;
180
181
  getWalletClientByAddress({ accountAddress, }: {
@@ -403,6 +404,31 @@ export declare class DynamicWaasAleoConnector extends DynamicWaasAleoConnector_b
403
404
  isNative?: boolean;
404
405
  amount: bigint;
405
406
  }): Promise<string>;
407
+ /**
408
+ * Two-step Exchange transfer.
409
+ *
410
+ * Step 1: `<program>/transfer_private_to_public(record, SELF, amount)`
411
+ * — unshield the record into the user's *own* public balance.
412
+ * Step 2: `<program>/transfer_public(EXCHANGE, amount)`
413
+ * — public→public from the user to the exchange recipient.
414
+ *
415
+ * `tokensPendingExchange` gates `canShieldToken` for the duration of
416
+ * the orchestration so the auto-shield hook does not consume the
417
+ * just-unshielded amount before step 2 spends it. The set is cleared
418
+ * in `finally` regardless of outcome.
419
+ *
420
+ * Step 2 is retried on failure because Aleo's finalize phase can lag
421
+ * the broadcast — until step 1 has finalized, step 2 reads stale public
422
+ * balance and the validator rejects it. We wait
423
+ * `EXCHANGE_STEP1_INITIAL_DELAY_MS` first and then retry up to
424
+ * `EXCHANGE_STEP2_MAX_RETRIES` times. If all retries exhaust, we throw a
425
+ * `DynamicError` that explicitly tells the user step 1 succeeded so they
426
+ * can recover the funds via a manual public→public transfer.
427
+ *
428
+ * Step 2's txId is returned as the user-facing transaction id (it's the
429
+ * one that lands the funds at the exchange).
430
+ */
431
+ private submitExchangeTransfer;
406
432
  endSession(reason?: LogoutReason): Promise<void>;
407
433
  getProvider(): undefined;
408
434
  }
@@ -95,6 +95,46 @@ const buildTransferInputs = (args) => {
95
95
  inputs: [to, `${value.toString()}u128`, recordPlaintext],
96
96
  };
97
97
  };
98
+ /**
99
+ * Builds the `(functionName, inputs, inputTypes)` triple for a
100
+ * `<program>/transfer_public` public-to-public transfer. Used as step 2
101
+ * of the Exchange flow (after step 1 unshield-to-self). `transfer_public`
102
+ * is NOT in the iframe's `SEALANCE_FUNCTIONS_REQUIRING_PROOF` set, so the
103
+ * caller controls inputs end-to-end (no Sealance proof injection).
104
+ *
105
+ * - `credits` u64. inputs `[recipient, amount]`.
106
+ * - `stablecoin` u128. inputs `[recipient, amount]`.
107
+ * - `arc21` u128. inputs `[token_id, recipient, amount]`. The
108
+ * `token_registry.aleo/transfer_public` transition keys
109
+ * off `token_id` since there's no record to encode it in.
110
+ */
111
+ const buildPublicTransferInputs = (args) => {
112
+ const { to, value, token } = args;
113
+ const functionName = 'transfer_public';
114
+ if (token.programKind === 'credits') {
115
+ return {
116
+ functionName,
117
+ inputTypes: ['address.public', 'u64.public'],
118
+ inputs: [to, `${value.toString()}u64`],
119
+ };
120
+ }
121
+ if (token.programKind === 'stablecoin') {
122
+ return {
123
+ functionName,
124
+ inputTypes: ['address.public', 'u128.public'],
125
+ inputs: [to, `${value.toString()}u128`],
126
+ };
127
+ }
128
+ // arc21 — token_registry.aleo/transfer_public(token_id, recipient, amount).
129
+ if (!token.tokenId) {
130
+ throw new DynamicError(`transfer_public: ARC-21 token ${token.contractAddress} is missing tokenId in registry.`);
131
+ }
132
+ return {
133
+ functionName,
134
+ inputTypes: ['field.public', 'address.public', 'u128.public'],
135
+ inputs: [token.tokenId, to, `${value.toString()}u128`],
136
+ };
137
+ };
98
138
  /**
99
139
  * Builds the `(functionName, inputs, inputTypes)` triple for a
100
140
  * `<program>/transfer_public_to_private` shield call to self. Mirrors
@@ -152,6 +192,16 @@ const joinShapeForProgram = (programId) => {
152
192
  }
153
193
  return shape;
154
194
  };
195
+ // Two-step Exchange flow timing. Step 1 (`transfer_private_to_public` to
196
+ // self) needs to surface in the user's public balance before step 2
197
+ // (`transfer_public` to the exchange) can spend it. Aleo block time is
198
+ // ~10s plus a finalize phase; we wait `STEP1_INITIAL_DELAY_MS` and then
199
+ // retry step 2 up to `STEP2_MAX_RETRIES` times with a fixed backoff.
200
+ // The most likely retryable failure is "insufficient public balance"
201
+ // if step 1 hasn't finalized yet.
202
+ const EXCHANGE_STEP1_INITIAL_DELAY_MS = 15000;
203
+ const EXCHANGE_STEP2_MAX_RETRIES = 3;
204
+ const EXCHANGE_STEP2_RETRY_DELAY_MS = 10000;
155
205
  const hasUsableRecordShape = (r) => {
156
206
  const rec = r;
157
207
  return (typeof (rec === null || rec === void 0 ? void 0 : rec.record_plaintext) === 'string' &&
@@ -173,6 +223,12 @@ class DynamicWaasAleoConnector extends withDynamicWaas(WaasAleoWalletConnector)
173
223
  this.overrideKey = 'dynamicwaas';
174
224
  this.isEmbeddedWallet = true;
175
225
  this.logger = new Logger('DynamicWaasAleoConnector');
226
+ // Programs whose Exchange flow is mid-orchestration (between step 1
227
+ // unshield-to-self and step 2 public-to-public). The auto-shield hook
228
+ // consults `canShieldToken` per balance refresh — gating shield here
229
+ // prevents the hook from re-shielding the just-unshielded amount before
230
+ // step 2 spends it. Add on Exchange entry, remove in `finally`.
231
+ this.tokensPendingExchange = new Set();
176
232
  this.walletUiUtils = props.walletUiUtils;
177
233
  }
178
234
  setVerifiedCredentials(verifiedCredentials) {
@@ -629,6 +685,15 @@ class DynamicWaasAleoConnector extends withDynamicWaas(WaasAleoWalletConnector)
629
685
  // which validateActiveWallet has already aligned with `from`.
630
686
  // Setting it explicitly keeps the contract obvious to readers.
631
687
  this.activeAccountAddress = from;
688
+ if (mode === 'exchange') {
689
+ return this.submitExchangeTransfer({
690
+ from,
691
+ recordPlaintext,
692
+ to,
693
+ token,
694
+ value,
695
+ });
696
+ }
632
697
  const { functionName, inputs, inputTypes } = buildTransferInputs({
633
698
  mode,
634
699
  recordPlaintext,
@@ -664,7 +729,15 @@ class DynamicWaasAleoConnector extends withDynamicWaas(WaasAleoWalletConnector)
664
729
  * `credits.aleo` entry when the literal address misses.
665
730
  */
666
731
  canShieldToken(token) {
667
- return Boolean(this.resolveShieldableToken(token));
732
+ const resolved = this.resolveShieldableToken(token);
733
+ if (!resolved)
734
+ return false;
735
+ // While an Exchange flow for this program is mid-orchestration the
736
+ // user's public balance is transient — auto-shield must not consume
737
+ // it before step 2's public→public transfer.
738
+ if (this.tokensPendingExchange.has(resolved.programId))
739
+ return false;
740
+ return true;
668
741
  }
669
742
  /**
670
743
  * True when the Feemaster currently sponsors a shield (
@@ -777,6 +850,91 @@ class DynamicWaasAleoConnector extends withDynamicWaas(WaasAleoWalletConnector)
777
850
  return result.txId;
778
851
  });
779
852
  }
853
+ /**
854
+ * Two-step Exchange transfer.
855
+ *
856
+ * Step 1: `<program>/transfer_private_to_public(record, SELF, amount)`
857
+ * — unshield the record into the user's *own* public balance.
858
+ * Step 2: `<program>/transfer_public(EXCHANGE, amount)`
859
+ * — public→public from the user to the exchange recipient.
860
+ *
861
+ * `tokensPendingExchange` gates `canShieldToken` for the duration of
862
+ * the orchestration so the auto-shield hook does not consume the
863
+ * just-unshielded amount before step 2 spends it. The set is cleared
864
+ * in `finally` regardless of outcome.
865
+ *
866
+ * Step 2 is retried on failure because Aleo's finalize phase can lag
867
+ * the broadcast — until step 1 has finalized, step 2 reads stale public
868
+ * balance and the validator rejects it. We wait
869
+ * `EXCHANGE_STEP1_INITIAL_DELAY_MS` first and then retry up to
870
+ * `EXCHANGE_STEP2_MAX_RETRIES` times. If all retries exhaust, we throw a
871
+ * `DynamicError` that explicitly tells the user step 1 succeeded so they
872
+ * can recover the funds via a manual public→public transfer.
873
+ *
874
+ * Step 2's txId is returned as the user-facing transaction id (it's the
875
+ * one that lands the funds at the exchange).
876
+ */
877
+ submitExchangeTransfer(args) {
878
+ return __awaiter(this, void 0, void 0, function* () {
879
+ var _a, _b, _c, _d, _e, _f;
880
+ const { from, to, value, recordPlaintext, token } = args;
881
+ this.tokensPendingExchange.add(token.programId);
882
+ try {
883
+ // --- Step 1: unshield record → self's public balance ---
884
+ const step1Build = buildTransferInputs({
885
+ mode: 'exchange',
886
+ recordPlaintext,
887
+ to: from,
888
+ token,
889
+ value,
890
+ });
891
+ const step1Result = yield this.proveTransaction({
892
+ broadcast: true,
893
+ functionName: step1Build.functionName,
894
+ inputTypes: step1Build.inputTypes,
895
+ inputs: step1Build.inputs,
896
+ programId: token.programId,
897
+ });
898
+ if (!step1Result.txId) {
899
+ throw new DynamicError('Aleo Exchange step 1 (unshield) did not return a transaction id.');
900
+ }
901
+ (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `[exchangeTransfer:${token.programId}] step 1 (unshield) tx: ${step1Result.txId}`);
902
+ // Wait for step 1 to finalize and surface in public balance before
903
+ // attempting step 2. See `EXCHANGE_STEP1_INITIAL_DELAY_MS` notes.
904
+ yield new Promise((resolve) => setTimeout(resolve, EXCHANGE_STEP1_INITIAL_DELAY_MS));
905
+ // --- Step 2: public→public self → exchange ---
906
+ const step2Build = buildPublicTransferInputs({ to, token, value });
907
+ let lastErr;
908
+ for (let attempt = 0; attempt <= EXCHANGE_STEP2_MAX_RETRIES; attempt += 1) {
909
+ try {
910
+ const step2Result = yield this.proveTransaction({
911
+ broadcast: true,
912
+ functionName: step2Build.functionName,
913
+ inputTypes: step2Build.inputTypes,
914
+ inputs: step2Build.inputs,
915
+ programId: token.programId,
916
+ });
917
+ if (!step2Result.txId) {
918
+ throw new DynamicError('Aleo Exchange step 2 (public transfer) did not return a transaction id.');
919
+ }
920
+ (_d = (_c = this.logger).debug) === null || _d === void 0 ? void 0 : _d.call(_c, `[exchangeTransfer:${token.programId}] step 2 (public transfer) tx: ${step2Result.txId}`);
921
+ return step2Result.txId;
922
+ }
923
+ catch (err) {
924
+ lastErr = err;
925
+ (_f = (_e = this.logger).debug) === null || _f === void 0 ? void 0 : _f.call(_e, `[exchangeTransfer:${token.programId}] step 2 attempt ${attempt + 1} failed: ${err instanceof Error ? err.message : String(err)}`);
926
+ if (attempt < EXCHANGE_STEP2_MAX_RETRIES) {
927
+ yield new Promise((resolve) => setTimeout(resolve, EXCHANGE_STEP2_RETRY_DELAY_MS));
928
+ }
929
+ }
930
+ }
931
+ throw new DynamicError(`Aleo Exchange step 2 (public transfer) failed after ${EXCHANGE_STEP2_MAX_RETRIES + 1} attempts. Step 1 unshield (tx ${step1Result.txId}) succeeded — your funds are now in your public balance and can be sent to the exchange via a manual public transfer. Last error: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}`);
932
+ }
933
+ finally {
934
+ this.tokensPendingExchange.delete(token.programId);
935
+ }
936
+ });
937
+ }
780
938
  endSession(reason) {
781
939
  const _super = Object.create(null, {
782
940
  endSession: { get: () => super.endSession }
@@ -151,6 +151,22 @@ class WaasAleoWalletConnector extends walletConnectorCore.WalletConnectorBase {
151
151
  supportsNetworkSwitching() {
152
152
  return true;
153
153
  }
154
+ /**
155
+ * Provable's hosted Aleo block explorer for the currently-selected
156
+ * network. Mainnet (chainId `0`) → `explorer.provable.com`, testnet
157
+ * (chainId `1`) → `testnet.explorer.provable.com`. The Send-flow's
158
+ * `TransactionStatusLayout` calls this through `getTransactionLink`
159
+ * to render a "View on explorer" link next to the broadcast txId.
160
+ */
161
+ getBlockExplorerUrlsForCurrentNetwork() {
162
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
163
+ var _a;
164
+ const chainId = (_a = this.getSelectedNetwork()) === null || _a === void 0 ? void 0 : _a.chainId;
165
+ const isMainnet = String(chainId !== null && chainId !== void 0 ? chainId : '') === '0';
166
+ const subdomain = isMainnet ? '' : 'testnet.';
167
+ return [`https://${subdomain}explorer.provable.com/`];
168
+ });
169
+ }
154
170
  /**
155
171
  * Returns the env-configured Aleo networks. The widget's NetworkPicker
156
172
  * uses this to render the chain-switcher dropdown and to decide whether
@@ -86,6 +86,14 @@ export declare abstract class WaasAleoWalletConnector extends WalletConnectorBas
86
86
  * widget UI exposes the chain-switcher dropdown for Aleo wallets.
87
87
  */
88
88
  supportsNetworkSwitching(): boolean;
89
+ /**
90
+ * Provable's hosted Aleo block explorer for the currently-selected
91
+ * network. Mainnet (chainId `0`) → `explorer.provable.com`, testnet
92
+ * (chainId `1`) → `testnet.explorer.provable.com`. The Send-flow's
93
+ * `TransactionStatusLayout` calls this through `getTransactionLink`
94
+ * to render a "View on explorer" link next to the broadcast txId.
95
+ */
96
+ getBlockExplorerUrlsForCurrentNetwork(): Promise<string[]>;
89
97
  /**
90
98
  * Returns the env-configured Aleo networks. The widget's NetworkPicker
91
99
  * uses this to render the chain-switcher dropdown and to decide whether
@@ -147,6 +147,22 @@ class WaasAleoWalletConnector extends WalletConnectorBase {
147
147
  supportsNetworkSwitching() {
148
148
  return true;
149
149
  }
150
+ /**
151
+ * Provable's hosted Aleo block explorer for the currently-selected
152
+ * network. Mainnet (chainId `0`) → `explorer.provable.com`, testnet
153
+ * (chainId `1`) → `testnet.explorer.provable.com`. The Send-flow's
154
+ * `TransactionStatusLayout` calls this through `getTransactionLink`
155
+ * to render a "View on explorer" link next to the broadcast txId.
156
+ */
157
+ getBlockExplorerUrlsForCurrentNetwork() {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ var _a;
160
+ const chainId = (_a = this.getSelectedNetwork()) === null || _a === void 0 ? void 0 : _a.chainId;
161
+ const isMainnet = String(chainId !== null && chainId !== void 0 ? chainId : '') === '0';
162
+ const subdomain = isMainnet ? '' : 'testnet.';
163
+ return [`https://${subdomain}explorer.provable.com/`];
164
+ });
165
+ }
150
166
  /**
151
167
  * Returns the env-configured Aleo networks. The widget's NetworkPicker
152
168
  * uses this to render the chain-switcher dropdown and to decide whether
package/src/index.cjs CHANGED
@@ -14,6 +14,7 @@ var isAleoWallet = require('./wallet/isAleoWallet/isAleoWallet.cjs');
14
14
  var AleoUiTransaction = require('./utils/AleoUiTransaction/AleoUiTransaction.cjs');
15
15
  var aleoWalletStandard = require('@provablehq/aleo-wallet-standard');
16
16
  var aleoSendableTokens = require('./utils/aleoSendableTokens/aleoSendableTokens.cjs');
17
+ var getAleoExplorerTxUrl = require('./utils/getAleoExplorerTxUrl/getAleoExplorerTxUrl.cjs');
17
18
  var DynamicWaasAleoConnector = require('./connectors/DynamicWaasAleoConnector/DynamicWaasAleoConnector.cjs');
18
19
 
19
20
  assertPackageVersion.assertPackageVersion('@dynamic-labs/aleo', _package.version);
@@ -50,5 +51,6 @@ exports.ALEO_TOKEN_REGISTRY_PROGRAM = aleoSendableTokens.ALEO_TOKEN_REGISTRY_PRO
50
51
  exports.extractRecordAtomicAmount = aleoSendableTokens.extractRecordAtomicAmount;
51
52
  exports.getAleoSendableTokensForNetwork = aleoSendableTokens.getAleoSendableTokensForNetwork;
52
53
  exports.recordMatchesSendableToken = aleoSendableTokens.recordMatchesSendableToken;
54
+ exports.getAleoExplorerTxUrl = getAleoExplorerTxUrl.getAleoExplorerTxUrl;
53
55
  exports.DynamicWaasAleoConnector = DynamicWaasAleoConnector.DynamicWaasAleoConnector;
54
56
  exports.AleoWalletConnectors = AleoWalletConnectors;
package/src/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export { ALEO_CHAINS, WalletDecryptPermission, WalletFeatureName, WalletReadySta
13
13
  export type { AleoChain, StandardWallet } from './types';
14
14
  export { ALEO_CREDITS_PROGRAM, ALEO_TOKEN_REGISTRY_PROGRAM, extractRecordAtomicAmount, getAleoSendableTokensForNetwork, recordMatchesSendableToken, } from './utils/aleoSendableTokens/aleoSendableTokens';
15
15
  export type { AleoOwnedRecord, AleoProgramKind, AleoSendableToken, } from './utils/aleoSendableTokens/aleoSendableTokens';
16
+ export { getAleoExplorerTxUrl } from './utils/getAleoExplorerTxUrl';
16
17
  export declare const AleoWalletConnectors: (props: {
17
18
  walletBook: WalletBookSchema;
18
19
  aleoNetworks: GenericNetwork[];
package/src/index.js CHANGED
@@ -11,6 +11,7 @@ export { isAleoWallet } from './wallet/isAleoWallet/isAleoWallet.js';
11
11
  export { AleoUiTransaction } from './utils/AleoUiTransaction/AleoUiTransaction.js';
12
12
  export { ALEO_CHAINS, WalletDecryptPermission, WalletFeatureName, WalletReadyState } from '@provablehq/aleo-wallet-standard';
13
13
  export { ALEO_CREDITS_PROGRAM, ALEO_TOKEN_REGISTRY_PROGRAM, extractRecordAtomicAmount, getAleoSendableTokensForNetwork, recordMatchesSendableToken } from './utils/aleoSendableTokens/aleoSendableTokens.js';
14
+ export { getAleoExplorerTxUrl } from './utils/getAleoExplorerTxUrl/getAleoExplorerTxUrl.js';
14
15
  export { DynamicWaasAleoConnector } from './connectors/DynamicWaasAleoConnector/DynamicWaasAleoConnector.js';
15
16
 
16
17
  assertPackageVersion('@dynamic-labs/aleo', version);
@@ -24,13 +24,19 @@ const ALEO_ADDRESS_REGEX = /^aleo1[a-z0-9]{58}$/;
24
24
  *
25
25
  * Both supported modes spend FROM a private record (so the wallet's
26
26
  * available balance is always the shielded record sum); they differ
27
- * only in what the recipient receives:
27
+ * in the on-chain orchestration they trigger inside the connector:
28
28
  *
29
- * - **Individual** = `<program>/transfer_private` — recipient also gets
30
- * a private record. The most common Aleo flow.
31
- * - **Exchange** = `<program>/transfer_private_to_public`
32
- * recipient's *public* balance is incremented. Useful for off-ramps,
33
- * exchange deposits, etc.
29
+ * - **Individual** = single `<program>/transfer_private` — recipient
30
+ * also gets a private record. The most common Aleo flow.
31
+ * - **Exchange** = two-step (driven by the connector's
32
+ * `submitExchangeTransfer`):
33
+ * 1. `<program>/transfer_private_to_public(record, SELF, amount)`
34
+ * — unshield to the sender's own public balance.
35
+ * 2. `<program>/transfer_public(EXCHANGE, amount)` — public→public
36
+ * from sender to the exchange recipient.
37
+ * Useful for off-ramps and exchange deposits. Splitting the unshield
38
+ * from the public transfer lets the connector pause auto-shield mid-
39
+ * flow and retry step 2 independently if Aleo's finalize phase lags.
34
40
  *
35
41
  * The submit path branches on the **selected token's program kind**:
36
42
  *
@@ -22,13 +22,19 @@ type AleoUiTransactionProps = {
22
22
  *
23
23
  * Both supported modes spend FROM a private record (so the wallet's
24
24
  * available balance is always the shielded record sum); they differ
25
- * only in what the recipient receives:
25
+ * in the on-chain orchestration they trigger inside the connector:
26
26
  *
27
- * - **Individual** = `<program>/transfer_private` — recipient also gets
28
- * a private record. The most common Aleo flow.
29
- * - **Exchange** = `<program>/transfer_private_to_public`
30
- * recipient's *public* balance is incremented. Useful for off-ramps,
31
- * exchange deposits, etc.
27
+ * - **Individual** = single `<program>/transfer_private` — recipient
28
+ * also gets a private record. The most common Aleo flow.
29
+ * - **Exchange** = two-step (driven by the connector's
30
+ * `submitExchangeTransfer`):
31
+ * 1. `<program>/transfer_private_to_public(record, SELF, amount)`
32
+ * — unshield to the sender's own public balance.
33
+ * 2. `<program>/transfer_public(EXCHANGE, amount)` — public→public
34
+ * from sender to the exchange recipient.
35
+ * Useful for off-ramps and exchange deposits. Splitting the unshield
36
+ * from the public transfer lets the connector pause auto-shield mid-
37
+ * flow and retry step 2 independently if Aleo's finalize phase lags.
32
38
  *
33
39
  * The submit path branches on the **selected token's program kind**:
34
40
  *
@@ -20,13 +20,19 @@ const ALEO_ADDRESS_REGEX = /^aleo1[a-z0-9]{58}$/;
20
20
  *
21
21
  * Both supported modes spend FROM a private record (so the wallet's
22
22
  * available balance is always the shielded record sum); they differ
23
- * only in what the recipient receives:
23
+ * in the on-chain orchestration they trigger inside the connector:
24
24
  *
25
- * - **Individual** = `<program>/transfer_private` — recipient also gets
26
- * a private record. The most common Aleo flow.
27
- * - **Exchange** = `<program>/transfer_private_to_public`
28
- * recipient's *public* balance is incremented. Useful for off-ramps,
29
- * exchange deposits, etc.
25
+ * - **Individual** = single `<program>/transfer_private` — recipient
26
+ * also gets a private record. The most common Aleo flow.
27
+ * - **Exchange** = two-step (driven by the connector's
28
+ * `submitExchangeTransfer`):
29
+ * 1. `<program>/transfer_private_to_public(record, SELF, amount)`
30
+ * — unshield to the sender's own public balance.
31
+ * 2. `<program>/transfer_public(EXCHANGE, amount)` — public→public
32
+ * from sender to the exchange recipient.
33
+ * Useful for off-ramps and exchange deposits. Splitting the unshield
34
+ * from the public transfer lets the connector pause auto-shield mid-
35
+ * flow and retry step 2 independently if Aleo's finalize phase lags.
30
36
  *
31
37
  * The submit path branches on the **selected token's program kind**:
32
38
  *
@@ -0,0 +1,41 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ /**
7
+ * Provable's hosted Aleo block explorer hosts mainnet and testnet on
8
+ * different subdomains. We pick by Aleo network id (`0` = mainnet,
9
+ * `1` = testnet — matching the registries in `aleoSendableTokens` /
10
+ * `aleoShieldableTokens`). Numeric and string ids are both accepted so
11
+ * callers can pass `connector.getSelectedNetwork()?.chainId` directly
12
+ * (which is typed `string | number` depending on env config). When
13
+ * `networkId` is missing we default to testnet — mainnet should always
14
+ * resolve a concrete id at runtime, but defaulting to testnet here
15
+ * means a stale UI never accidentally surfaces a mainnet explorer link
16
+ * for a testnet transaction.
17
+ */
18
+ const ALEO_MAINNET_NETWORK_ID = 0;
19
+ const isMainnet = (networkId) => {
20
+ if (networkId === undefined || networkId === null)
21
+ return false;
22
+ if (typeof networkId === 'number')
23
+ return networkId === ALEO_MAINNET_NETWORK_ID;
24
+ return Number(networkId) === ALEO_MAINNET_NETWORK_ID;
25
+ };
26
+ /**
27
+ * Build the Provable explorer URL for an Aleo transaction id.
28
+ *
29
+ * Examples:
30
+ * getAleoExplorerTxUrl('at1abc...', 0) → 'https://explorer.provable.com/transaction/at1abc...'
31
+ * getAleoExplorerTxUrl('at1abc...', 1) → 'https://testnet.explorer.provable.com/transaction/at1abc...'
32
+ *
33
+ * @param txId Aleo transaction id (`at1...`).
34
+ * @param networkId `0` = mainnet, `1` = testnet. Defaults to testnet.
35
+ */
36
+ const getAleoExplorerTxUrl = (txId, networkId) => {
37
+ const subdomain = isMainnet(networkId) ? '' : 'testnet.';
38
+ return `https://${subdomain}explorer.provable.com/transaction/${txId}`;
39
+ };
40
+
41
+ exports.getAleoExplorerTxUrl = getAleoExplorerTxUrl;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build the Provable explorer URL for an Aleo transaction id.
3
+ *
4
+ * Examples:
5
+ * getAleoExplorerTxUrl('at1abc...', 0) → 'https://explorer.provable.com/transaction/at1abc...'
6
+ * getAleoExplorerTxUrl('at1abc...', 1) → 'https://testnet.explorer.provable.com/transaction/at1abc...'
7
+ *
8
+ * @param txId Aleo transaction id (`at1...`).
9
+ * @param networkId `0` = mainnet, `1` = testnet. Defaults to testnet.
10
+ */
11
+ export declare const getAleoExplorerTxUrl: (txId: string, networkId?: number | string) => string;
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+ /**
3
+ * Provable's hosted Aleo block explorer hosts mainnet and testnet on
4
+ * different subdomains. We pick by Aleo network id (`0` = mainnet,
5
+ * `1` = testnet — matching the registries in `aleoSendableTokens` /
6
+ * `aleoShieldableTokens`). Numeric and string ids are both accepted so
7
+ * callers can pass `connector.getSelectedNetwork()?.chainId` directly
8
+ * (which is typed `string | number` depending on env config). When
9
+ * `networkId` is missing we default to testnet — mainnet should always
10
+ * resolve a concrete id at runtime, but defaulting to testnet here
11
+ * means a stale UI never accidentally surfaces a mainnet explorer link
12
+ * for a testnet transaction.
13
+ */
14
+ const ALEO_MAINNET_NETWORK_ID = 0;
15
+ const isMainnet = (networkId) => {
16
+ if (networkId === undefined || networkId === null)
17
+ return false;
18
+ if (typeof networkId === 'number')
19
+ return networkId === ALEO_MAINNET_NETWORK_ID;
20
+ return Number(networkId) === ALEO_MAINNET_NETWORK_ID;
21
+ };
22
+ /**
23
+ * Build the Provable explorer URL for an Aleo transaction id.
24
+ *
25
+ * Examples:
26
+ * getAleoExplorerTxUrl('at1abc...', 0) → 'https://explorer.provable.com/transaction/at1abc...'
27
+ * getAleoExplorerTxUrl('at1abc...', 1) → 'https://testnet.explorer.provable.com/transaction/at1abc...'
28
+ *
29
+ * @param txId Aleo transaction id (`at1...`).
30
+ * @param networkId `0` = mainnet, `1` = testnet. Defaults to testnet.
31
+ */
32
+ const getAleoExplorerTxUrl = (txId, networkId) => {
33
+ const subdomain = isMainnet(networkId) ? '' : 'testnet.';
34
+ return `https://${subdomain}explorer.provable.com/transaction/${txId}`;
35
+ };
36
+
37
+ export { getAleoExplorerTxUrl };
@@ -0,0 +1 @@
1
+ export * from './getAleoExplorerTxUrl';
@@ -6,6 +6,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
6
6
  var _tslib = require('../../../_virtual/_tslib.cjs');
7
7
  var utils = require('@dynamic-labs/utils');
8
8
  var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
9
+ var getAleoExplorerTxUrl = require('../../utils/getAleoExplorerTxUrl/getAleoExplorerTxUrl.cjs');
9
10
 
10
11
  class AleoWallet extends walletConnectorCore.Wallet {
11
12
  sendBalance(_a) {
@@ -47,6 +48,23 @@ class AleoWallet extends walletConnectorCore.Wallet {
47
48
  getPublicKey() {
48
49
  return this.address;
49
50
  }
51
+ /**
52
+ * Build the Provable explorer URL for an Aleo transaction id, scoped to
53
+ * this wallet's currently-selected network. `0` = mainnet, `1` = testnet.
54
+ * Available on every Aleo wallet flavour (embedded + Wallet Adapter).
55
+ *
56
+ * Use this to link freshly-broadcast txIds back to the explorer in your
57
+ * UI — e.g. after `wallet.proveTransaction()`, `wallet.shieldToken()`, or
58
+ * the Send flow returns. For Exchange-mode Send the returned txId is the
59
+ * second (`transfer_public`) transaction, which is the one the user
60
+ * cares about.
61
+ */
62
+ getExplorerTransactionUrl(txId) {
63
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
64
+ const networkId = yield this._connector.getNetwork();
65
+ return getAleoExplorerTxUrl.getAleoExplorerTxUrl(txId, networkId);
66
+ });
67
+ }
50
68
  /**
51
69
  * Returns the current network this wallet is connected to (e.g. 'aleo:mainnet').
52
70
  */
@@ -103,6 +121,119 @@ class AleoWallet extends walletConnectorCore.Wallet {
103
121
  return this._connector.requestRecords(program, options);
104
122
  });
105
123
  }
124
+ // -------------------------------------------------------------------------
125
+ // Embedded-WaaS proxy methods.
126
+ //
127
+ // The methods below delegate to `DynamicWaasAleoConnector` and only work
128
+ // when the wallet is an embedded Dynamic WaaS Aleo wallet. They throw on
129
+ // Wallet Adapter wallets (which expose `requestTransaction` /
130
+ // `requestRecords` for the same flows but route through the adapter app
131
+ // rather than Sodot MPC + Feemaster + DPS).
132
+ //
133
+ // Convenience over `(wallet.connector as DynamicWaasAleoConnector).foo`:
134
+ // - typed entry point on `wallet.*` matching the docs convention used
135
+ // for Stellar (`wallet.signTransaction`), EVM (`wallet.getPublicClient`),
136
+ // etc.
137
+ // - auto-sets `connector.activeAccountAddress` from `this.address` so
138
+ // callers don't have to mutate connector internals before each call.
139
+ // -------------------------------------------------------------------------
140
+ /**
141
+ * Resolves `this._connector` to a `DynamicWaasAleoConnector` and aligns
142
+ * its `activeAccountAddress` with this wallet's address. Duck-types on
143
+ * `proveTransaction` (the WaaS-specific entry point) so we don't need a
144
+ * runtime import of the WaaS connector. Throws a clear `DynamicError`
145
+ * if the underlying connector is the Wallet Adapter one.
146
+ */
147
+ getWaasConnectorOrThrow(methodName) {
148
+ const c = this._connector;
149
+ if (typeof c.proveTransaction !== 'function') {
150
+ throw new utils.DynamicError(`AleoWallet.${methodName}() is only available on embedded Aleo wallets (Dynamic WaaS). For Wallet Adapter Aleo wallets use \`requestTransaction\` / \`requestRecords\` instead.`);
151
+ }
152
+ c.activeAccountAddress = this.address;
153
+ return c;
154
+ }
155
+ /**
156
+ * Sign + prove + broadcast an arbitrary Aleo program transition through
157
+ * the Dynamic WaaS pipeline (Sodot MPC → optional Sealance proof
158
+ * injection → Feemaster sponsorship when covered → Provable DPS).
159
+ *
160
+ * Use this for the generic "execute any transition" path. The Send flow
161
+ * driven by the widget builds on top of this via `createUiTransaction`.
162
+ */
163
+ proveTransaction(args) {
164
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
165
+ return this.getWaasConnectorOrThrow('proveTransaction').proveTransaction(args);
166
+ });
167
+ }
168
+ /**
169
+ * List this wallet's owned Aleo records across every program (credits +
170
+ * stablecoins + ARC-21). Backed by Provable's RecordScanner via the
171
+ * iframe — view key never leaves the iframe.
172
+ *
173
+ * Each entry carries `program_name` + `record_name` so callers can
174
+ * group/display by token.
175
+ */
176
+ listOwnedRecords() {
177
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
178
+ return this.getWaasConnectorOrThrow('listOwnedRecords').listOwnedRecords();
179
+ });
180
+ }
181
+ /**
182
+ * Pairwise-merge owned records down to one per program by running
183
+ * `<program>/join` until a single record remains. Sponsored by Feemaster
184
+ * for `credits.aleo` today; stablecoin / ARC-21 fall through to user-paid
185
+ * if Feemaster doesn't yet cover the pair.
186
+ *
187
+ * Programs the connector doesn't recognise (no registered join shape)
188
+ * are reported as `skipped: 'unsupported'` rather than thrown — iterate
189
+ * `results` to surface what merged vs. what didn't.
190
+ */
191
+ joinRecords(opts) {
192
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
193
+ return this.getWaasConnectorOrThrow('joinRecords').joinRecords(opts);
194
+ });
195
+ }
196
+ /**
197
+ * Shield (`<program>/transfer_public_to_private`) `amount` of a token
198
+ * from this wallet's *public* balance into a fresh private record owned
199
+ * by self. `tokenAddress` matches `TokenBalance.address` from the
200
+ * unshielded-balance feed; pass `isNative: true` for native ALEO.
201
+ */
202
+ shieldToken(args) {
203
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
204
+ return this.getWaasConnectorOrThrow('shieldToken').shieldToken(args);
205
+ });
206
+ }
207
+ /**
208
+ * Returns `true` when the given token is registered as shieldable on the
209
+ * currently-selected Aleo network. Used to gate "Shield Manually" UI on
210
+ * unshielded balances surfaced by the public-balance feed (which can
211
+ * include third-party Aleo programs we don't know how to shield).
212
+ */
213
+ canShieldToken(token) {
214
+ return this.getWaasConnectorOrThrow('canShieldToken').canShieldToken(token);
215
+ }
216
+ /**
217
+ * Returns `true` when the Feemaster currently sponsors a shield
218
+ * (`transfer_public_to_private`) of the given token. Used to decide
219
+ * whether the shield CTA can dispatch silently or needs a user-paid fee
220
+ * confirmation modal.
221
+ */
222
+ isShieldSponsored(token) {
223
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
224
+ return this.getWaasConnectorOrThrow('isShieldSponsored').isShieldSponsored(token);
225
+ });
226
+ }
227
+ /**
228
+ * Generic Feemaster sponsorship check for any `(programId, functionName)`
229
+ * pair on the currently-selected network. Never throws — returns `false`
230
+ * on any failure so callers default to "show modal".
231
+ */
232
+ isFeemasterSponsored(args) {
233
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
234
+ return this.getWaasConnectorOrThrow('isFeemasterSponsored').isFeemasterSponsored(args);
235
+ });
236
+ }
106
237
  }
107
238
 
108
239
  exports.AleoWallet = AleoWallet;
@@ -1,5 +1,6 @@
1
1
  import { Wallet } from '@dynamic-labs/wallet-connector-core';
2
2
  import type { AleoWalletConnector } from '../../connectors/AleoWalletConnector';
3
+ import type { DynamicWaasAleoConnector } from '../../connectors/DynamicWaasAleoConnector';
3
4
  import type { AleoTransaction } from '../../types';
4
5
  export declare class AleoWallet extends Wallet<AleoWalletConnector> {
5
6
  sendBalance({ amount, toAddress, token, }: {
@@ -16,6 +17,18 @@ export declare class AleoWallet extends Wallet<AleoWalletConnector> {
16
17
  * In Aleo, the public key and address are the same value.
17
18
  */
18
19
  getPublicKey(): string;
20
+ /**
21
+ * Build the Provable explorer URL for an Aleo transaction id, scoped to
22
+ * this wallet's currently-selected network. `0` = mainnet, `1` = testnet.
23
+ * Available on every Aleo wallet flavour (embedded + Wallet Adapter).
24
+ *
25
+ * Use this to link freshly-broadcast txIds back to the explorer in your
26
+ * UI — e.g. after `wallet.proveTransaction()`, `wallet.shieldToken()`, or
27
+ * the Send flow returns. For Exchange-mode Send the returned txId is the
28
+ * second (`transfer_public`) transaction, which is the one the user
29
+ * cares about.
30
+ */
31
+ getExplorerTransactionUrl(txId: string): Promise<string>;
19
32
  /**
20
33
  * Returns the current network this wallet is connected to (e.g. 'aleo:mainnet').
21
34
  */
@@ -54,4 +67,92 @@ export declare class AleoWallet extends Wallet<AleoWalletConnector> {
54
67
  requestRecords(program: string, options?: {
55
68
  plaintext?: boolean;
56
69
  }): Promise<unknown[]>;
70
+ /**
71
+ * Resolves `this._connector` to a `DynamicWaasAleoConnector` and aligns
72
+ * its `activeAccountAddress` with this wallet's address. Duck-types on
73
+ * `proveTransaction` (the WaaS-specific entry point) so we don't need a
74
+ * runtime import of the WaaS connector. Throws a clear `DynamicError`
75
+ * if the underlying connector is the Wallet Adapter one.
76
+ */
77
+ private getWaasConnectorOrThrow;
78
+ /**
79
+ * Sign + prove + broadcast an arbitrary Aleo program transition through
80
+ * the Dynamic WaaS pipeline (Sodot MPC → optional Sealance proof
81
+ * injection → Feemaster sponsorship when covered → Provable DPS).
82
+ *
83
+ * Use this for the generic "execute any transition" path. The Send flow
84
+ * driven by the widget builds on top of this via `createUiTransaction`.
85
+ */
86
+ proveTransaction(args: {
87
+ programId: string;
88
+ functionName: string;
89
+ inputs: string[];
90
+ inputTypes: string[];
91
+ broadcast?: boolean;
92
+ }): Promise<{
93
+ txId?: string;
94
+ provingResponse?: unknown;
95
+ }>;
96
+ /**
97
+ * List this wallet's owned Aleo records across every program (credits +
98
+ * stablecoins + ARC-21). Backed by Provable's RecordScanner via the
99
+ * iframe — view key never leaves the iframe.
100
+ *
101
+ * Each entry carries `program_name` + `record_name` so callers can
102
+ * group/display by token.
103
+ */
104
+ listOwnedRecords(): Promise<{
105
+ records: unknown[];
106
+ }>;
107
+ /**
108
+ * Pairwise-merge owned records down to one per program by running
109
+ * `<program>/join` until a single record remains. Sponsored by Feemaster
110
+ * for `credits.aleo` today; stablecoin / ARC-21 fall through to user-paid
111
+ * if Feemaster doesn't yet cover the pair.
112
+ *
113
+ * Programs the connector doesn't recognise (no registered join shape)
114
+ * are reported as `skipped: 'unsupported'` rather than thrown — iterate
115
+ * `results` to surface what merged vs. what didn't.
116
+ */
117
+ joinRecords(opts?: Parameters<DynamicWaasAleoConnector['joinRecords']>[0]): Promise<Awaited<ReturnType<DynamicWaasAleoConnector['joinRecords']>>>;
118
+ /**
119
+ * Shield (`<program>/transfer_public_to_private`) `amount` of a token
120
+ * from this wallet's *public* balance into a fresh private record owned
121
+ * by self. `tokenAddress` matches `TokenBalance.address` from the
122
+ * unshielded-balance feed; pass `isNative: true` for native ALEO.
123
+ */
124
+ shieldToken(args: {
125
+ tokenAddress: string;
126
+ isNative?: boolean;
127
+ amount: bigint;
128
+ }): Promise<string>;
129
+ /**
130
+ * Returns `true` when the given token is registered as shieldable on the
131
+ * currently-selected Aleo network. Used to gate "Shield Manually" UI on
132
+ * unshielded balances surfaced by the public-balance feed (which can
133
+ * include third-party Aleo programs we don't know how to shield).
134
+ */
135
+ canShieldToken(token: {
136
+ address?: string;
137
+ isNative?: boolean;
138
+ }): boolean;
139
+ /**
140
+ * Returns `true` when the Feemaster currently sponsors a shield
141
+ * (`transfer_public_to_private`) of the given token. Used to decide
142
+ * whether the shield CTA can dispatch silently or needs a user-paid fee
143
+ * confirmation modal.
144
+ */
145
+ isShieldSponsored(token: {
146
+ address?: string;
147
+ isNative?: boolean;
148
+ }): Promise<boolean>;
149
+ /**
150
+ * Generic Feemaster sponsorship check for any `(programId, functionName)`
151
+ * pair on the currently-selected network. Never throws — returns `false`
152
+ * on any failure so callers default to "show modal".
153
+ */
154
+ isFeemasterSponsored(args: {
155
+ programId: string;
156
+ functionName: string;
157
+ }): Promise<boolean>;
57
158
  }
@@ -2,6 +2,7 @@
2
2
  import { __awaiter } from '../../../_virtual/_tslib.js';
3
3
  import { DynamicError } from '@dynamic-labs/utils';
4
4
  import { Wallet } from '@dynamic-labs/wallet-connector-core';
5
+ import { getAleoExplorerTxUrl } from '../../utils/getAleoExplorerTxUrl/getAleoExplorerTxUrl.js';
5
6
 
6
7
  class AleoWallet extends Wallet {
7
8
  sendBalance(_a) {
@@ -43,6 +44,23 @@ class AleoWallet extends Wallet {
43
44
  getPublicKey() {
44
45
  return this.address;
45
46
  }
47
+ /**
48
+ * Build the Provable explorer URL for an Aleo transaction id, scoped to
49
+ * this wallet's currently-selected network. `0` = mainnet, `1` = testnet.
50
+ * Available on every Aleo wallet flavour (embedded + Wallet Adapter).
51
+ *
52
+ * Use this to link freshly-broadcast txIds back to the explorer in your
53
+ * UI — e.g. after `wallet.proveTransaction()`, `wallet.shieldToken()`, or
54
+ * the Send flow returns. For Exchange-mode Send the returned txId is the
55
+ * second (`transfer_public`) transaction, which is the one the user
56
+ * cares about.
57
+ */
58
+ getExplorerTransactionUrl(txId) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ const networkId = yield this._connector.getNetwork();
61
+ return getAleoExplorerTxUrl(txId, networkId);
62
+ });
63
+ }
46
64
  /**
47
65
  * Returns the current network this wallet is connected to (e.g. 'aleo:mainnet').
48
66
  */
@@ -99,6 +117,119 @@ class AleoWallet extends Wallet {
99
117
  return this._connector.requestRecords(program, options);
100
118
  });
101
119
  }
120
+ // -------------------------------------------------------------------------
121
+ // Embedded-WaaS proxy methods.
122
+ //
123
+ // The methods below delegate to `DynamicWaasAleoConnector` and only work
124
+ // when the wallet is an embedded Dynamic WaaS Aleo wallet. They throw on
125
+ // Wallet Adapter wallets (which expose `requestTransaction` /
126
+ // `requestRecords` for the same flows but route through the adapter app
127
+ // rather than Sodot MPC + Feemaster + DPS).
128
+ //
129
+ // Convenience over `(wallet.connector as DynamicWaasAleoConnector).foo`:
130
+ // - typed entry point on `wallet.*` matching the docs convention used
131
+ // for Stellar (`wallet.signTransaction`), EVM (`wallet.getPublicClient`),
132
+ // etc.
133
+ // - auto-sets `connector.activeAccountAddress` from `this.address` so
134
+ // callers don't have to mutate connector internals before each call.
135
+ // -------------------------------------------------------------------------
136
+ /**
137
+ * Resolves `this._connector` to a `DynamicWaasAleoConnector` and aligns
138
+ * its `activeAccountAddress` with this wallet's address. Duck-types on
139
+ * `proveTransaction` (the WaaS-specific entry point) so we don't need a
140
+ * runtime import of the WaaS connector. Throws a clear `DynamicError`
141
+ * if the underlying connector is the Wallet Adapter one.
142
+ */
143
+ getWaasConnectorOrThrow(methodName) {
144
+ const c = this._connector;
145
+ if (typeof c.proveTransaction !== 'function') {
146
+ throw new DynamicError(`AleoWallet.${methodName}() is only available on embedded Aleo wallets (Dynamic WaaS). For Wallet Adapter Aleo wallets use \`requestTransaction\` / \`requestRecords\` instead.`);
147
+ }
148
+ c.activeAccountAddress = this.address;
149
+ return c;
150
+ }
151
+ /**
152
+ * Sign + prove + broadcast an arbitrary Aleo program transition through
153
+ * the Dynamic WaaS pipeline (Sodot MPC → optional Sealance proof
154
+ * injection → Feemaster sponsorship when covered → Provable DPS).
155
+ *
156
+ * Use this for the generic "execute any transition" path. The Send flow
157
+ * driven by the widget builds on top of this via `createUiTransaction`.
158
+ */
159
+ proveTransaction(args) {
160
+ return __awaiter(this, void 0, void 0, function* () {
161
+ return this.getWaasConnectorOrThrow('proveTransaction').proveTransaction(args);
162
+ });
163
+ }
164
+ /**
165
+ * List this wallet's owned Aleo records across every program (credits +
166
+ * stablecoins + ARC-21). Backed by Provable's RecordScanner via the
167
+ * iframe — view key never leaves the iframe.
168
+ *
169
+ * Each entry carries `program_name` + `record_name` so callers can
170
+ * group/display by token.
171
+ */
172
+ listOwnedRecords() {
173
+ return __awaiter(this, void 0, void 0, function* () {
174
+ return this.getWaasConnectorOrThrow('listOwnedRecords').listOwnedRecords();
175
+ });
176
+ }
177
+ /**
178
+ * Pairwise-merge owned records down to one per program by running
179
+ * `<program>/join` until a single record remains. Sponsored by Feemaster
180
+ * for `credits.aleo` today; stablecoin / ARC-21 fall through to user-paid
181
+ * if Feemaster doesn't yet cover the pair.
182
+ *
183
+ * Programs the connector doesn't recognise (no registered join shape)
184
+ * are reported as `skipped: 'unsupported'` rather than thrown — iterate
185
+ * `results` to surface what merged vs. what didn't.
186
+ */
187
+ joinRecords(opts) {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ return this.getWaasConnectorOrThrow('joinRecords').joinRecords(opts);
190
+ });
191
+ }
192
+ /**
193
+ * Shield (`<program>/transfer_public_to_private`) `amount` of a token
194
+ * from this wallet's *public* balance into a fresh private record owned
195
+ * by self. `tokenAddress` matches `TokenBalance.address` from the
196
+ * unshielded-balance feed; pass `isNative: true` for native ALEO.
197
+ */
198
+ shieldToken(args) {
199
+ return __awaiter(this, void 0, void 0, function* () {
200
+ return this.getWaasConnectorOrThrow('shieldToken').shieldToken(args);
201
+ });
202
+ }
203
+ /**
204
+ * Returns `true` when the given token is registered as shieldable on the
205
+ * currently-selected Aleo network. Used to gate "Shield Manually" UI on
206
+ * unshielded balances surfaced by the public-balance feed (which can
207
+ * include third-party Aleo programs we don't know how to shield).
208
+ */
209
+ canShieldToken(token) {
210
+ return this.getWaasConnectorOrThrow('canShieldToken').canShieldToken(token);
211
+ }
212
+ /**
213
+ * Returns `true` when the Feemaster currently sponsors a shield
214
+ * (`transfer_public_to_private`) of the given token. Used to decide
215
+ * whether the shield CTA can dispatch silently or needs a user-paid fee
216
+ * confirmation modal.
217
+ */
218
+ isShieldSponsored(token) {
219
+ return __awaiter(this, void 0, void 0, function* () {
220
+ return this.getWaasConnectorOrThrow('isShieldSponsored').isShieldSponsored(token);
221
+ });
222
+ }
223
+ /**
224
+ * Generic Feemaster sponsorship check for any `(programId, functionName)`
225
+ * pair on the currently-selected network. Never throws — returns `false`
226
+ * on any failure so callers default to "show modal".
227
+ */
228
+ isFeemasterSponsored(args) {
229
+ return __awaiter(this, void 0, void 0, function* () {
230
+ return this.getWaasConnectorOrThrow('isFeemasterSponsored').isFeemasterSponsored(args);
231
+ });
232
+ }
102
233
  }
103
234
 
104
235
  export { AleoWallet };