@fairblock/stabletrust 2.0.13 → 2.0.15

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/README.md CHANGED
@@ -63,11 +63,11 @@ The following contract addresses are available for confidential transfers on tes
63
63
 
64
64
  | Network(Testnet) | Chain ID | Contract Address |
65
65
  | :--------------- | :------- | :------------------------------------------- |
66
- | Stable | 2201 | `0xe1c1456CAb802312759a8cFc3976f88bf87082cf` |
67
- | Arc | 5042002 | `0x1B4f05f67CC33788Da4C89a7cd0b2f8E0055E605` |
68
- | Base | 84532 | `0x4a251C9D79faCa20b193630A4ee313af7cBCDD93` |
69
- | Ethereum | 11155111 | `0x7507a13352AFAa79D33E994f86f2f62463ba8DE4` |
70
- | Arbitrum | 421614 | `0x5acECCdeb5CbD3C727eCB49F8706Eb80EF2f977F` |
66
+ | Stable | 2201 | `0x0b6791C168ffBF52e82F5E862929Dbf505c3A46E` |
67
+ | Arc | 5042002 | `0xA90621B79d49c8E3A5eeEBcaaa839E2f886240C5` |
68
+ | Base | 84532 | `0xb6cdAE7ccfEE03e351694c63436D5c5c073aEF84` |
69
+ | Ethereum | 11155111 | `0x5A061604A1d94f4fa9939544707f6B200d6bB5cf` |
70
+ | Arbitrum | 421614 | `0x147C6D8cA1a4784Ed76d98b0E3CcA41C38a49A5f` |
71
71
  | Tempo | 42431 | `0xE559fB936C69c46E216bf61B07C16bF1a6d444aa` |
72
72
 
73
73
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairblock/stabletrust",
3
- "version": "2.0.13",
3
+ "version": "2.0.15",
4
4
  "description": "SDK for confidential transfers using homomorphic encryption and zero-knowledge proofs",
5
5
  "main": "src/index.js",
6
6
  "types": "src/withdrawal-fees.d.ts",
@@ -49,7 +49,7 @@
49
49
  "@stablelib/chacha20poly1305": "^1.0.1",
50
50
  "axios": "^1.16.0",
51
51
  "dotenv": "^17.2.3",
52
- "ethers": "^6.16.0"
52
+ "ethers": "^6.17.0"
53
53
  },
54
54
  "engines": {
55
55
  "node": ">=18.0.0"
package/src/backend.js CHANGED
@@ -2,6 +2,12 @@ import axios from "axios";
2
2
 
3
3
  const DEFAULT_API_BASE_URL = "https://stabletrust-backend-api.fairblock.network";
4
4
 
5
+ // Bound each request-row POST. Callers await these,
6
+ // so without a timeout a slow/hung backend would stall the caller's transfer()
7
+ // even though the on-chain transfer already completed. On timeout axios rejects
8
+ // with ECONNABORTED, which _postRequest turns into a warning.
9
+ const REQUEST_TIMEOUT_MS = 5000;
10
+
5
11
  export const CHAIN_KEY_BY_ID = {
6
12
  2201: "stable",
7
13
  5042002: "arc",
@@ -16,5 +22,7 @@ export const CHAIN_KEY_BY_ID = {
16
22
  * POST /api/requests — create a request row
17
23
  */
18
24
  export async function createRequest(requestData, apiBaseUrl = DEFAULT_API_BASE_URL) {
19
- await axios.post(`${apiBaseUrl}/api/requests`, requestData);
25
+ await axios.post(`${apiBaseUrl}/api/requests`, requestData, {
26
+ timeout: REQUEST_TIMEOUT_MS,
27
+ });
20
28
  }
@@ -121,7 +121,10 @@ export class ConfidentialTransferClient {
121
121
  }
122
122
 
123
123
  /**
124
- * Fire-and-forget request row creation
124
+ * Post a request row. Returns the createRequest promise (which resolves even
125
+ * on failure - errors are logged and swallowed) so callers that need the row
126
+ * to exist before returning (e.g. confidentialTransfer) can `await` it.
127
+ * Callers that don't await keep the old fire-and-forget behavior.
125
128
  * @private
126
129
  */
127
130
  _postRequest(data) {
@@ -132,7 +135,7 @@ export class ConfidentialTransferClient {
132
135
  applicationName: this.config.applicationName,
133
136
  ...data,
134
137
  };
135
- createRequest(payload, apiBaseUrl).catch((err) => {
138
+ return createRequest(payload, apiBaseUrl).catch((err) => {
136
139
  const status = err?.response?.status;
137
140
  const body = err?.response?.data;
138
141
  console.warn("[StableTrust] Failed to post request:", err.message);
@@ -882,67 +885,72 @@ export class ConfidentialTransferClient {
882
885
  ]);
883
886
  const tokenSymbol = await tokenContract.symbol();
884
887
 
885
- Promise.all([
886
- encryptRandomness(senderAddress, proof.data.sender_randomness),
887
- encryptRandomness(recipientAddress, proof.data.receiver_randomness),
888
- ])
889
- .then(([senderEnc, receiverEnc]) => {
890
- const transferDetails = {
891
- sender: senderAddress.toLowerCase(),
892
- recipient: recipientAddress.toLowerCase(),
893
- token: tokenSymbol,
894
- tokenAddress,
895
- };
896
-
897
- const queuedFields =
898
- senderEnc.queuedEncrypted && receiverEnc.queuedEncrypted
899
- ? {
900
- queuedEncryptedSenderRandomness: senderEnc.queuedEncrypted,
901
- queuedEncryptedSenderRandomnessId:
902
- senderEnc.queuedEncryptedId,
903
- queuedEncryptedReceiverRandomness:
904
- receiverEnc.queuedEncrypted,
905
- queuedEncryptedReceiverRandomnessId:
906
- receiverEnc.queuedEncryptedId,
907
- }
908
- : {};
909
-
910
- this._postRequest({
911
- requestId,
912
- userAddress: senderAddress.toLowerCase(),
913
- operationKind: 2,
914
- type: "transfer",
915
- transactionHash: txHash,
916
- status: "pending",
917
- details: transferDetails,
918
- encryptedSenderRandomness: senderEnc.encrypted,
919
- encryptedSenderRandomnessId: senderEnc.encryptedId,
920
- encryptedReceiverRandomness: receiverEnc.encrypted,
921
- encryptedReceiverRandomnessId: receiverEnc.encryptedId,
922
- ...queuedFields,
923
- });
924
-
925
- this._postRequest({
926
- requestId: `received-${requestId}-${recipientAddress.toLowerCase()}`,
927
- userAddress: recipientAddress.toLowerCase(),
928
- operationKind: 2,
929
- type: "received",
930
- transactionHash: txHash,
931
- status: "pending",
932
- details: transferDetails,
933
- encryptedReceiverRandomness: receiverEnc.encrypted,
934
- encryptedReceiverRandomnessId: receiverEnc.encryptedId,
935
- ...(receiverEnc.queuedEncrypted
936
- ? {
937
- queuedEncryptedReceiverRandomness:
938
- receiverEnc.queuedEncrypted,
939
- queuedEncryptedReceiverRandomnessId:
940
- receiverEnc.queuedEncryptedId,
941
- }
942
- : {}),
943
- });
944
- })
945
- .catch(() => {});
888
+ let senderEnc = null;
889
+ let receiverEnc = null;
890
+ try {
891
+ [senderEnc, receiverEnc] = await Promise.all([
892
+ encryptRandomness(senderAddress, proof.data.sender_randomness),
893
+ encryptRandomness(recipientAddress, proof.data.receiver_randomness),
894
+ ]);
895
+ } catch (encErr) {
896
+ console.warn(
897
+ `[StableTrust] Transfer ${txHash} Failed to get IBE Randomness (${encErr?.message ?? encErr}). `,
898
+ );
899
+ }
900
+
901
+ if (senderEnc && receiverEnc) {
902
+ const transferDetails = {
903
+ sender: senderAddress.toLowerCase(),
904
+ recipient: recipientAddress.toLowerCase(),
905
+ token: tokenSymbol,
906
+ tokenAddress,
907
+ };
908
+
909
+ const queuedFields =
910
+ senderEnc.queuedEncrypted && receiverEnc.queuedEncrypted
911
+ ? {
912
+ queuedEncryptedSenderRandomness: senderEnc.queuedEncrypted,
913
+ queuedEncryptedSenderRandomnessId: senderEnc.queuedEncryptedId,
914
+ queuedEncryptedReceiverRandomness: receiverEnc.queuedEncrypted,
915
+ queuedEncryptedReceiverRandomnessId:
916
+ receiverEnc.queuedEncryptedId,
917
+ }
918
+ : {};
919
+
920
+ await this._postRequest({
921
+ requestId,
922
+ userAddress: senderAddress.toLowerCase(),
923
+ operationKind: 2,
924
+ type: "transfer",
925
+ transactionHash: txHash,
926
+ status: "pending",
927
+ details: transferDetails,
928
+ encryptedSenderRandomness: senderEnc.encrypted,
929
+ encryptedSenderRandomnessId: senderEnc.encryptedId,
930
+ encryptedReceiverRandomness: receiverEnc.encrypted,
931
+ encryptedReceiverRandomnessId: receiverEnc.encryptedId,
932
+ ...queuedFields,
933
+ });
934
+
935
+ await this._postRequest({
936
+ requestId: `received-${requestId}-${recipientAddress.toLowerCase()}`,
937
+ userAddress: recipientAddress.toLowerCase(),
938
+ operationKind: 2,
939
+ type: "received",
940
+ transactionHash: txHash,
941
+ status: "pending",
942
+ details: transferDetails,
943
+ encryptedReceiverRandomness: receiverEnc.encrypted,
944
+ encryptedReceiverRandomnessId: receiverEnc.encryptedId,
945
+ ...(receiverEnc.queuedEncrypted
946
+ ? {
947
+ queuedEncryptedReceiverRandomness: receiverEnc.queuedEncrypted,
948
+ queuedEncryptedReceiverRandomnessId:
949
+ receiverEnc.queuedEncryptedId,
950
+ }
951
+ : {}),
952
+ });
953
+ }
946
954
 
947
955
  if (waitForFinalization) {
948
956
  await this._waitForGlobalState(senderAddress, "transfer");
package/src/constants.js CHANGED
@@ -2,15 +2,25 @@
2
2
  * Contract ABIs and Constants
3
3
  */
4
4
 
5
- const CREATE_CONFIDENTIAL_ACCOUNT_SIGNATURE = "createConfidentialAccount(bytes)";
6
- const DEPOSIT_SIGNATURE = "deposit(address,uint256)";
5
+ export const PREDICATE_ATTESTATION_SIGNATURE =
6
+ "(string,uint256,address,bytes)";
7
+ const PREDICATE_ATTESTATION_ABI =
8
+ "(string uuid,uint256 expiration,address attester,bytes signature) attestation";
9
+
10
+ export const CREATE_CONFIDENTIAL_ACCOUNT_SIGNATURE =
11
+ `createConfidentialAccount(bytes,${PREDICATE_ATTESTATION_SIGNATURE})`;
12
+ export const DEPOSIT_SIGNATURE =
13
+ `deposit(address,uint256,${PREDICATE_ATTESTATION_SIGNATURE})`;
7
14
  const GET_ACCOUNT_CORE_SIGNATURE = "getAccountCore(address)";
8
15
  const GET_AVAILABLE_SIGNATURE = "getAvailable(address,address)";
9
16
  const GET_PENDING_SIGNATURE = "getPending(address,address)";
10
- const APPLY_PENDING_SIGNATURE = "applyPending()";
17
+ export const APPLY_PENDING_SIGNATURE =
18
+ `applyPending(${PREDICATE_ATTESTATION_SIGNATURE})`;
11
19
 
12
- export const TRANSFER_CONFIDENTIAL_SIGNATURE = "transferConfidential(address,address,bytes,bool)";
13
- export const WITHDRAW_CONFIDENTIAL_SIGNATURE = "withdraw(address,uint256,bytes,bool)";
20
+ export const TRANSFER_CONFIDENTIAL_SIGNATURE =
21
+ `transferConfidential(address,address,bytes,bool,${PREDICATE_ATTESTATION_SIGNATURE})`;
22
+ export const WITHDRAW_CONFIDENTIAL_SIGNATURE =
23
+ `withdraw(address,uint256,bytes,bool,${PREDICATE_ATTESTATION_SIGNATURE})`;
14
24
  export const FEE_TOKEN_SIGNATURE = "feeToken()";
15
25
  export const FEE_ACCOUNT_SIGNATURE = "feeAccount()";
16
26
  export const NON_ANONYMOUS_TRANSFER_FEE_SIGNATURE = "nonAnonymousTransferFee()";
@@ -21,14 +31,14 @@ export const ANONYMOUS_INLINE_TRANSFER_FEE_SIGNATURE = "anonymousInlineTransferF
21
31
  export const ANONYMOUS_WITHDRAW_FEE_PPM_SIGNATURE = "anonymousWithdrawFeePpm()";
22
32
 
23
33
  export const CONTRACT_ABI = [
24
- `function ${CREATE_CONFIDENTIAL_ACCOUNT_SIGNATURE} external`,
25
- `function ${DEPOSIT_SIGNATURE} external`,
34
+ `function createConfidentialAccount(bytes elgamalPubkey,${PREDICATE_ATTESTATION_ABI}) external`,
35
+ `function deposit(address token,uint256 plainAmount,${PREDICATE_ATTESTATION_ABI}) external`,
26
36
  `function ${GET_ACCOUNT_CORE_SIGNATURE} external view returns ((bool exists, bool finalized, bool pendingAction, uint256 txId, bytes elgamalPubkey, uint64 pendingCreditCounter))`,
27
37
  `function ${GET_AVAILABLE_SIGNATURE} external view returns (bytes c1, bytes c2)`,
28
38
  `function ${GET_PENDING_SIGNATURE} external view returns (bytes c1, bytes c2)`,
29
- `function ${TRANSFER_CONFIDENTIAL_SIGNATURE} external payable`,
30
- `function ${WITHDRAW_CONFIDENTIAL_SIGNATURE} external payable`,
31
- `function ${APPLY_PENDING_SIGNATURE} external`,
39
+ `function transferConfidential(address recipient,address token,bytes proof,bool offchainZKP,${PREDICATE_ATTESTATION_ABI}) external payable`,
40
+ `function withdraw(address token,uint256 plainAmount,bytes proof,bool offchainZKP,${PREDICATE_ATTESTATION_ABI}) external payable`,
41
+ `function applyPending(${PREDICATE_ATTESTATION_ABI}) external`,
32
42
  `function ${FEE_TOKEN_SIGNATURE} external view returns (address)`,
33
43
  `function ${FEE_ACCOUNT_SIGNATURE} external view returns (address)`,
34
44
  `function ${NON_ANONYMOUS_TRANSFER_FEE_SIGNATURE} external view returns (uint256)`,
@@ -63,11 +73,11 @@ export const ERC20_ABI = [
63
73
  export const TEMPO_FEE_TOKEN_ADDRESS =
64
74
  "0x20c0000000000000000000000000000000000000";
65
75
  export const STABLETRUST_CONTRACTS_BY_CHAIN_ID = Object.freeze({
66
- 2201: "0xe1c1456CAb802312759a8cFc3976f88bf87082cf", //Stable
67
- 5042002: "0x1B4f05f67CC33788Da4C89a7cd0b2f8E0055E605", //Arc
68
- 84532: "0x4a251C9D79faCa20b193630A4ee313af7cBCDD93", //Base
69
- 11155111: "0x7507a13352AFAa79D33E994f86f2f62463ba8DE4", //Ethereum
70
- 421614: "0x5acECCdeb5CbD3C727eCB49F8706Eb80EF2f977F", //Arbitrum
76
+ 2201: "0x0b6791C168ffBF52e82F5E862929Dbf505c3A46E", //Stable
77
+ 5042002: "0xA90621B79d49c8E3A5eeEBcaaa839E2f886240C5", //Arc
78
+ 84532: "0xb6cdAE7ccfEE03e351694c63436D5c5c073aEF84", //Base
79
+ 11155111: "0x5A061604A1d94f4fa9939544707f6B200d6bB5cf", //Ethereum
80
+ 421614: "0x147C6D8cA1a4784Ed76d98b0E3CcA41C38a49A5f", //Arbitrum Sepolia
71
81
  42431: "0xE559fB936C69c46E216bf61B07C16bF1a6d444aa", //Tempo
72
82
  42161: "0xCAA6384D5Ac8b9111D482dd676BB890f2b6e9513", //Arbitrum One (mainnet)
73
83
  });
package/src/ibe.js CHANGED
@@ -3,6 +3,14 @@ import { timelockEncrypt } from "./vendor/ts-ibe/index.js";
3
3
  const IBE_PUBKEY_URL =
4
4
  "https://anon-testnet-api.fairblock.network/fairyring/keyshare/pubkey";
5
5
 
6
+ // Bound the IBE public-key fetch. Without this, a slow or hung endpoint (a
7
+ // socket that accepts the connection but never responds) would block the fetch
8
+ // indefinitely, and because confidentialTransfer awaits encryptRandomness that
9
+ // stall propagates to the caller's transfer() call even though the on-chain
10
+ // transfer already happened. On timeout the fetch aborts and encryptRandomness
11
+ // rejects, which the caller handles as "dashboard record skipped".
12
+ const IBE_FETCH_TIMEOUT_MS = 5000;
13
+
6
14
  /** @type {{ active: string, queued: string | null } | null} */
7
15
  let cachedIbePublicKeys = null;
8
16
 
@@ -23,9 +31,22 @@ function buildIbeId(userAddress, publicKey) {
23
31
  async function getIbePublicKeys() {
24
32
  if (cachedIbePublicKeys) return cachedIbePublicKeys;
25
33
 
26
- const response = await fetch(IBE_PUBKEY_URL, {
27
- headers: { Accept: "application/json" },
28
- });
34
+ let response;
35
+ try {
36
+ response = await fetch(IBE_PUBKEY_URL, {
37
+ headers: { Accept: "application/json" },
38
+ signal: AbortSignal.timeout(IBE_FETCH_TIMEOUT_MS),
39
+ });
40
+ } catch (err) {
41
+ if (err?.name === "TimeoutError" || err?.name === "AbortError") {
42
+ throw new Error(
43
+ `IBE public key fetch timed out after ${IBE_FETCH_TIMEOUT_MS}ms`,
44
+ );
45
+ }
46
+ throw new Error(
47
+ `IBE public key fetch failed: ${err?.message ?? err}`,
48
+ );
49
+ }
29
50
 
30
51
  if (!response.ok) {
31
52
  throw new Error(
package/src/index.js CHANGED
@@ -3,7 +3,7 @@ export {
3
3
  ControlledConfidentialTransferClient,
4
4
  AnonymousTransferClient,
5
5
  WITHDRAW_FEE_PPM_DENOMINATOR,
6
- } from "./fee-aware-clients.js";
6
+ } from "./predicate-fee-aware-clients.js";
7
7
  export { deriveKeys, decryptCiphertext, combineCiphertext } from "./crypto.js";
8
8
  export {
9
9
  encodeTransferProof,
@@ -14,7 +14,11 @@ export {
14
14
  export {
15
15
  CONTRACT_ABI,
16
16
  ERC20_ABI,
17
+ PREDICATE_ATTESTATION_SIGNATURE,
18
+ CREATE_CONFIDENTIAL_ACCOUNT_SIGNATURE,
19
+ DEPOSIT_SIGNATURE,
17
20
  TRANSFER_CONFIDENTIAL_SIGNATURE,
21
+ APPLY_PENDING_SIGNATURE,
18
22
  WITHDRAW_CONFIDENTIAL_SIGNATURE,
19
23
  FEE_TOKEN_SIGNATURE,
20
24
  FEE_ACCOUNT_SIGNATURE,
@@ -27,4 +31,9 @@ export {
27
31
  STABLETRUST_CONTRACTS_BY_CHAIN_ID,
28
32
  getStabletrustContractAddress,
29
33
  } from "./constants.js";
34
+ export {
35
+ emptyPredicateAttestation,
36
+ injectPredicateAttestation,
37
+ isPredicateAttestation,
38
+ } from "./predicate-contract.js";
30
39
  // Note: initializeWasm is now internal - WASM auto-initializes on first client use
@@ -0,0 +1,126 @@
1
+ import { ethers } from "ethers";
2
+
3
+ const PREDICATE_BUSINESS_ARG_COUNTS = Object.freeze({
4
+ createConfidentialAccount: 1,
5
+ deposit: 2,
6
+ transferConfidential: 4,
7
+ applyPending: 0,
8
+ withdraw: 4,
9
+ });
10
+
11
+ const METHOD_HELPERS = [
12
+ "estimateGas",
13
+ "populateTransaction",
14
+ "send",
15
+ "staticCall",
16
+ "staticCallResult",
17
+ ];
18
+
19
+ const PREDICATE_WRAPPED = Symbol("stabletrust.predicateWrappedContract");
20
+
21
+ /**
22
+ * Empty Predicate attestation used while on-chain Predicate enforcement is off.
23
+ * A fresh array is returned for each call so callers/ethers never share mutable
24
+ * tuple state between transactions.
25
+ */
26
+ export function emptyPredicateAttestation() {
27
+ return ["", 0n, ethers.ZeroAddress, "0x"];
28
+ }
29
+
30
+ export function isPredicateAttestation(value) {
31
+ if (Array.isArray(value)) return value.length === 4;
32
+ if (!value || typeof value !== "object") return false;
33
+ return (
34
+ "uuid" in value &&
35
+ "expiration" in value &&
36
+ "attester" in value &&
37
+ "signature" in value
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Convert the pre-Predicate SDK call shape into the upgraded Diamond call shape.
43
+ * Existing SDK methods may optionally append an ethers transaction-overrides
44
+ * object, so the attestation must be inserted immediately before overrides.
45
+ *
46
+ * Calls that already contain an explicit attestation are left unchanged. This
47
+ * makes the wrapped contract usable directly by applications that obtain a real
48
+ * Predicate attestation later.
49
+ */
50
+ export function injectPredicateAttestation(methodName, args) {
51
+ const businessArgCount = PREDICATE_BUSINESS_ARG_COUNTS[methodName];
52
+ if (businessArgCount === undefined) return args;
53
+
54
+ if (args.length === businessArgCount) {
55
+ return [...args, emptyPredicateAttestation()];
56
+ }
57
+
58
+ if (args.length === businessArgCount + 1) {
59
+ if (isPredicateAttestation(args[businessArgCount])) {
60
+ return args;
61
+ }
62
+ return [
63
+ ...args.slice(0, businessArgCount),
64
+ emptyPredicateAttestation(),
65
+ args[businessArgCount],
66
+ ];
67
+ }
68
+
69
+ if (
70
+ args.length === businessArgCount + 2 &&
71
+ isPredicateAttestation(args[businessArgCount])
72
+ ) {
73
+ return args;
74
+ }
75
+
76
+ // Let ethers surface its normal argument-count/type error for malformed calls.
77
+ return args;
78
+ }
79
+
80
+ function wrapPredicateMethod(methodName, method) {
81
+ const wrapped = (...args) =>
82
+ method(...injectPredicateAttestation(methodName, args));
83
+
84
+ for (const helperName of METHOD_HELPERS) {
85
+ const helper = method?.[helperName];
86
+ if (typeof helper === "function") {
87
+ wrapped[helperName] = (...args) =>
88
+ helper(...injectPredicateAttestation(methodName, args));
89
+ }
90
+ }
91
+
92
+ return wrapped;
93
+ }
94
+
95
+ /**
96
+ * Wrap an ethers Contract so the SDK's existing high-level methods keep their
97
+ * public API while targeting the upgraded Attestation-bearing selectors.
98
+ *
99
+ * `connect()` is wrapped recursively because every state-changing SDK path uses
100
+ * `this.contract.connect(wallet)` before invoking the contract.
101
+ */
102
+ export function wrapPredicateContract(contract) {
103
+ if (!contract || contract[PREDICATE_WRAPPED]) return contract;
104
+
105
+ return new Proxy(contract, {
106
+ get(target, prop) {
107
+ if (prop === PREDICATE_WRAPPED) return true;
108
+
109
+ if (prop === "connect") {
110
+ return (runner) => wrapPredicateContract(target.connect(runner));
111
+ }
112
+
113
+ if (
114
+ typeof prop === "string" &&
115
+ Object.prototype.hasOwnProperty.call(PREDICATE_BUSINESS_ARG_COUNTS, prop)
116
+ ) {
117
+ const method = target[prop];
118
+ if (typeof method === "function") {
119
+ return wrapPredicateMethod(prop, method);
120
+ }
121
+ }
122
+
123
+ return Reflect.get(target, prop, target);
124
+ },
125
+ });
126
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ ConfidentialTransferClient as FeeAwareConfidentialTransferClient,
3
+ ControlledConfidentialTransferClient as FeeAwareControlledConfidentialTransferClient,
4
+ AnonymousTransferClient,
5
+ WITHDRAW_FEE_PPM_DENOMINATOR,
6
+ } from "./fee-aware-clients.js";
7
+ import { wrapPredicateContract } from "./predicate-contract.js";
8
+
9
+ /**
10
+ * Public non-anonymous clients target the upgraded ConfidentialMirror ABI.
11
+ * Existing SDK methods still call the pre-Predicate argument shape internally;
12
+ * the wrapped ethers Contract inserts an empty attestation while enforcement is
13
+ * disabled, without changing the high-level SDK API.
14
+ */
15
+ export class ConfidentialTransferClient extends FeeAwareConfidentialTransferClient {
16
+ constructor(...args) {
17
+ super(...args);
18
+ this.contract = wrapPredicateContract(this.contract);
19
+ }
20
+ }
21
+
22
+ export class ControlledConfidentialTransferClient extends FeeAwareControlledConfidentialTransferClient {
23
+ constructor(...args) {
24
+ super(...args);
25
+ this.contract = wrapPredicateContract(this.contract);
26
+ }
27
+ }
28
+
29
+ // Anonymous flows are intentionally not Predicate-gated and must remain
30
+ // byte-for-byte/API compatible with the existing anonymous client behavior.
31
+ export { AnonymousTransferClient, WITHDRAW_FEE_PPM_DENOMINATOR };