@fairblock/stabletrust 2.0.5 → 2.0.7

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
@@ -572,28 +572,15 @@ try {
572
572
 
573
573
  ### Common Issues and Solutions
574
574
 
575
- | Issue | Cause | Solution |
576
- | :----------------------------- | :------------------------------------------------------ | :------------------------------------------------------ |
577
- | "Account does not exist" | Recipient hasn't initialized their confidential account | Recipient must call `ensureAccount()` first |
578
- | "Insufficient balance" | Transfer amount exceeds available confidential balance | Deposit more tokens or reduce transfer amount |
579
- | "Account finalization timeout" | Account creation is still processing | Wait a few minutes and retry |
580
- | "Proof generation failed" | Invalid inputs or cryptographic operation error | Verify all parameters and ensure sufficient balance |
581
- | "Amount too small" | Amount rounds to 0 in contract scale | Use a larger amount (minimum depends on token decimals) |
575
+ | Issue | Cause | Solution |
576
+ | :--------------------------------- | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ |
577
+ | "Account does not exist" | Recipient hasn't initialized their confidential account | Recipient must call `ensureAccount()` first |
578
+ | "Insufficient balance" | Transfer amount exceeds available confidential balance | Deposit more tokens or reduce transfer amount |
579
+ | "Account finalization timeout" | Account creation is still processing | Wait a few minutes and retry |
580
+ | "Proof generation failed" | Invalid inputs or cryptographic operation error | Verify all parameters and ensure sufficient balance |
581
+ | "Amount too small" | Amount rounds to 0 in contract scale | Use a larger amount (minimum depends on token decimals) |
582
582
  | "Insufficient prepaid fee balance" | Anonymous account's prepaid fee reserve is below the per-transfer fee | Call `depositFees(wallet, accountId, amount)` to top up; use `getFeeToken()`/`getFeeAmount()` to check requirements |
583
- | Fairycloak HTTP error | Relay unreachable or request rejected | Check the relay URL, API key, and account authorization |
584
-
585
- ---
586
-
587
- ## Performance Metrics
588
-
589
- The following are estimated execution times for standard operations. Durations may vary based on network congestion and hardware.
590
-
591
- | Operation | Avg Duration |
592
- | :--------------- | :----------- |
593
- | Account Creation | 45s |
594
- | Deposit | 63s |
595
- | Transfer | 58s |
596
- | Withdraw | 58s |
583
+ | Fairycloak HTTP error | Relay unreachable or request rejected | Check the relay URL, API key, and account authorization |
597
584
 
598
585
  ---
599
586
 
@@ -24,7 +24,7 @@ dotenv.config();
24
24
 
25
25
  const EVM_RPC = process.env.EVM_RPC ?? "http://127.0.0.1:8545";
26
26
  const FAIRYCLOAK_URL = process.env.FAIRYCLOAK_URL ?? "http://127.0.0.1:8080";
27
- const CHAIN_ID = Number(process.env.CHAIN_ID ?? "31337");
27
+ const CHAIN_ID = Number(process.env.CHAIN_ID ?? "5042002");
28
28
  const TOKEN_ADDRESS =
29
29
  process.env.TOKEN_ADDRESS ?? "0xE915164570b027C2A0FfadcB1B672192E35BF008";
30
30
  const TOKEN_DECIMALS = Number(process.env.TOKEN_DECIMALS ?? "6");
@@ -236,34 +236,39 @@ async function main() {
236
236
  log(" fee token :", feeToken);
237
237
  log(" fee/xfer :", fmt(feeAmount, TOKEN_DECIMALS), "tokens");
238
238
 
239
- const feeBalance = await client.getPrepaidFeeBalance(anon1Id, feeToken);
240
- log(" fee balance:", fmt(feeBalance, TOKEN_DECIMALS), "tokens");
241
-
242
- if (feeBalance < MIN_FEE_BALANCE) {
243
- log(
244
- ` balance < ${fmt(MIN_FEE_BALANCE, TOKEN_DECIMALS)}, depositing ${fmt(FEE_DEPOSIT_AMOUNT, TOKEN_DECIMALS)} fee tokens (anon1 pays gas) …`,
245
- );
246
- const fd = await client.depositFees(
247
- anon1Wallet,
248
- anon1Id,
249
- FEE_DEPOSIT_AMOUNT,
250
- );
251
- log(" request_id:", fd.request_id, " status:", fd.status);
252
- if (fd.error) log(" [!] Error:", fd.error);
253
- if (fd.tx_hash) log(" tx_hash :", fd.tx_hash);
254
-
255
- const fdFinal = await client.waitForRequest(fd.request_id, {
256
- timeoutMs: 180_000,
257
- });
258
- log(" status :", fdFinal.status, " tx_hash:", fdFinal.tx_hash ?? "—");
259
- if (fdFinal.error) log(" [!] Error:", fdFinal.error);
260
-
261
- const newFeeBalance = await client.getPrepaidFeeBalance(anon1Id, feeToken);
262
- log(" new fee balance:", fmt(newFeeBalance, TOKEN_DECIMALS), "tokens");
239
+ if (feeAmount === 0n) {
240
+ log(" no fee configured — transactions are free, skipping top-up.");
263
241
  } else {
264
- log(
265
- ` fee balance sufficient (${fmt(feeBalance, TOKEN_DECIMALS)} ≥ ${fmt(MIN_FEE_BALANCE, TOKEN_DECIMALS)}), skipping deposit.`,
266
- );
242
+ const feeBalance = await client.getPrepaidFeeBalance(anon1Id, feeToken);
243
+ log(" fee balance:", fmt(feeBalance, TOKEN_DECIMALS), "tokens");
244
+
245
+ if (feeBalance < MIN_FEE_BALANCE) {
246
+ const currency = feeToken === ethers.ZeroAddress ? "native" : "ERC-20";
247
+ log(
248
+ ` balance < ${fmt(MIN_FEE_BALANCE, TOKEN_DECIMALS)}, depositing ${fmt(FEE_DEPOSIT_AMOUNT, TOKEN_DECIMALS)} ${currency} fee tokens (anon1 pays gas) …`,
249
+ );
250
+ const fd = await client.depositFees(
251
+ anon1Wallet,
252
+ anon1Id,
253
+ FEE_DEPOSIT_AMOUNT,
254
+ );
255
+ log(" request_id:", fd.request_id, " status:", fd.status);
256
+ if (fd.error) log(" [!] Error:", fd.error);
257
+ if (fd.tx_hash) log(" tx_hash :", fd.tx_hash);
258
+
259
+ const fdFinal = await client.waitForRequest(fd.request_id, {
260
+ timeoutMs: 180_000,
261
+ });
262
+ log(" status :", fdFinal.status, " tx_hash:", fdFinal.tx_hash ?? "—");
263
+ if (fdFinal.error) log(" [!] Error:", fdFinal.error);
264
+
265
+ const newFeeBalance = await client.getPrepaidFeeBalance(anon1Id, feeToken);
266
+ log(" new fee balance:", fmt(newFeeBalance, TOKEN_DECIMALS), "tokens");
267
+ } else {
268
+ log(
269
+ ` fee balance sufficient (${fmt(feeBalance, TOKEN_DECIMALS)} ≥ ${fmt(MIN_FEE_BALANCE, TOKEN_DECIMALS)}), skipping deposit.`,
270
+ );
271
+ }
267
272
  }
268
273
 
269
274
  // ── [6] Transfer anon1 → anon2 ───────────────────────────────────────────────
@@ -1,7 +1,6 @@
1
1
  import { ethers } from "ethers";
2
2
  import dotenv from "dotenv";
3
- import { ConfidentialTransferClient } from "@fairblock/stabletrust";
4
- import { ERC20_ABI } from "./constants.js";
3
+ import { ConfidentialTransferClient, ERC20_ABI } from "@fairblock/stabletrust";
5
4
  dotenv.config();
6
5
 
7
6
  /**
@@ -1,7 +1,6 @@
1
1
  import { ethers } from "ethers";
2
2
  import dotenv from "dotenv";
3
- import { ConfidentialTransferClient } from "@fairblock/stabletrust";
4
- import { ERC20_ABI } from "./constants.js";
3
+ import { ConfidentialTransferClient, ERC20_ABI } from "@fairblock/stabletrust";
5
4
  dotenv.config();
6
5
  const RPC_URL = process.env.RPC_URL || "https://rpc.testnet.stable.xyz";
7
6
  const EXPLORER_URL =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairblock/stabletrust",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "SDK for confidential transfers using homomorphic encryption and zero-knowledge proofs",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -21,8 +21,7 @@
21
21
  "src/",
22
22
  "pkg/",
23
23
  "examples/",
24
- "README.md",
25
- "CHANGELOG.md"
24
+ "README.md"
26
25
  ],
27
26
  "repository": {
28
27
  "type": "git",
@@ -1488,8 +1488,6 @@ export class AnonymousTransferClient {
1488
1488
  ]);
1489
1489
  if (feeAmount === 0n) return; // no fee configured
1490
1490
 
1491
- if (feeToken === ethers.ZeroAddress) return; // fee token not set
1492
-
1493
1491
  const balance = await this.getPrepaidFeeBalance(accountId, feeToken);
1494
1492
  if (balance < feeAmount) {
1495
1493
  throw new Error(
@@ -1534,45 +1532,51 @@ export class AnonymousTransferClient {
1534
1532
  if (amountBig <= 0n) throw new Error("Amount must be greater than 0");
1535
1533
 
1536
1534
  const feeToken = await this.getFeeToken();
1537
- if (feeToken === ethers.ZeroAddress)
1538
- throw new Error(
1539
- "No fee token is currently configured on the contract. Contact the protocol operator.",
1540
- );
1535
+ const isNative = feeToken === ethers.ZeroAddress;
1541
1536
 
1542
1537
  const depositorAddress = await authWallet.getAddress();
1543
1538
  const walletWithProvider = authWallet.connect
1544
1539
  ? authWallet.connect(this.provider)
1545
1540
  : authWallet;
1546
1541
 
1547
- // 1. Approve if needed
1548
- const tokenContract = new ethers.Contract(
1549
- feeToken,
1550
- [
1551
- "function balanceOf(address) view returns (uint256)",
1552
- "function allowance(address,address) view returns (uint256)",
1553
- "function approve(address,uint256) returns (bool)",
1554
- ],
1555
- walletWithProvider,
1556
- );
1542
+ if (isNative) {
1543
+ // Native currency: check wallet balance, no ERC-20 approve needed
1544
+ const balance = await this.provider.getBalance(depositorAddress);
1545
+ if (balance < amountBig)
1546
+ throw new Error(
1547
+ `Insufficient native balance. Required: ${amountBig}, available: ${balance}`,
1548
+ );
1549
+ } else {
1550
+ // ERC-20: approve if needed
1551
+ const tokenContract = new ethers.Contract(
1552
+ feeToken,
1553
+ [
1554
+ "function balanceOf(address) view returns (uint256)",
1555
+ "function allowance(address,address) view returns (uint256)",
1556
+ "function approve(address,uint256) returns (bool)",
1557
+ ],
1558
+ walletWithProvider,
1559
+ );
1557
1560
 
1558
- const [balance, allowance] = await Promise.all([
1559
- tokenContract.balanceOf(depositorAddress),
1560
- tokenContract.allowance(depositorAddress, this.diamondAddress),
1561
- ]);
1561
+ const [balance, allowance] = await Promise.all([
1562
+ tokenContract.balanceOf(depositorAddress),
1563
+ tokenContract.allowance(depositorAddress, this.diamondAddress),
1564
+ ]);
1562
1565
 
1563
- if (balance < amountBig)
1564
- throw new Error(
1565
- `Insufficient fee token balance. Required: ${amountBig}, available: ${balance}`,
1566
- );
1566
+ if (balance < amountBig)
1567
+ throw new Error(
1568
+ `Insufficient fee token balance. Required: ${amountBig}, available: ${balance}`,
1569
+ );
1567
1570
 
1568
- if (allowance < amountBig) {
1569
- const approveTx = await tokenContract.approve(
1570
- this.diamondAddress,
1571
- ethers.MaxUint256,
1572
- );
1573
- const receipt = await approveTx.wait();
1574
- if (!receipt || receipt.status === 0)
1575
- throw new Error("Fee token approval failed");
1571
+ if (allowance < amountBig) {
1572
+ const approveTx = await tokenContract.approve(
1573
+ this.diamondAddress,
1574
+ ethers.MaxUint256,
1575
+ );
1576
+ const receipt = await approveTx.wait();
1577
+ if (!receipt || receipt.status === 0)
1578
+ throw new Error("Fee token approval failed");
1579
+ }
1576
1580
  }
1577
1581
 
1578
1582
  // 2. EIP-712 AnonymousAddFees signature
@@ -1614,6 +1618,7 @@ export class AnonymousTransferClient {
1614
1618
  this.provider.getFeeData(),
1615
1619
  ]);
1616
1620
 
1621
+ const txValue = isNative ? amountBig : 0n;
1617
1622
  let txObj;
1618
1623
  if (feeData.maxFeePerGas) {
1619
1624
  txObj = {
@@ -1624,7 +1629,7 @@ export class AnonymousTransferClient {
1624
1629
  gasLimit,
1625
1630
  maxFeePerGas: (feeData.maxFeePerGas * 12n) / 10n,
1626
1631
  maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0n,
1627
- value: 0n,
1632
+ value: txValue,
1628
1633
  };
1629
1634
  } else {
1630
1635
  txObj = {
@@ -1634,7 +1639,7 @@ export class AnonymousTransferClient {
1634
1639
  chainId: BigInt(this.chainId),
1635
1640
  gasLimit,
1636
1641
  gasPrice: (feeData.gasPrice * 11n) / 10n,
1637
- value: 0n,
1642
+ value: txValue,
1638
1643
  };
1639
1644
  }
1640
1645
 
@@ -840,52 +840,6 @@ export class ConfidentialTransferClient {
840
840
  );
841
841
  }
842
842
 
843
- /**
844
- * Wait for pending balance to appear
845
- *
846
- * @param {string} address - Account address
847
- * @param {string} privateKey - Private key for decryption
848
- * @param {string} tokenAddress - Token address
849
- * @param {Object} [options] - Options
850
- * @param {number} [options.maxAttempts=225] - Maximum polling attempts
851
- * @param {number} [options.intervalMs=400] - Polling interval in milliseconds
852
- * @returns {Promise<{amount: number, ciphertext: string}>}
853
- */
854
- async _waitForPendingBalance(
855
- address,
856
- privateKey,
857
- tokenAddress,
858
- options = {},
859
- ) {
860
- const { maxAttempts = 225, intervalMs = 400 } = options;
861
- await sleep(1000);
862
-
863
- try {
864
- for (let i = 0; i < maxAttempts; i++) {
865
- const pending = await this._getPendingBalance(
866
- address,
867
- privateKey,
868
- tokenAddress,
869
- );
870
-
871
- if (pending.amount > 0n) {
872
- return pending;
873
- }
874
-
875
- await sleep(intervalMs);
876
- }
877
-
878
- throw new Error(
879
- `Timeout waiting for pending balance after ${maxAttempts} attempts. The transfer may still be processing.`,
880
- );
881
- } catch (error) {
882
- if (error.message.includes("Timeout waiting for pending balance")) {
883
- throw error;
884
- }
885
- throw new Error(`Failed to wait for pending balance: ${error.message}`);
886
- }
887
- }
888
-
889
843
  /**
890
844
  * Get the current fee amount for confidential transfers
891
845
  *
package/src/constants.js CHANGED
@@ -24,11 +24,11 @@ export const ERC20_ABI = [
24
24
  export const TEMPO_FEE_TOKEN_ADDRESS =
25
25
  "0x20c0000000000000000000000000000000000000";
26
26
  export const STABLETRUST_CONTRACTS_BY_CHAIN_ID = Object.freeze({
27
- 2201: "0xA98e5ba75Bb44459916D52Abf1604caB3d98CC4B", //Stable
28
- 5042002: "0xfc29476B8e53765Ca16ea32882370d850b245336", //Arc
29
- 84532: "0xF66A0a1670F14AE5D1852B4E7d1e4C693b2Accfd", //Base
30
- 11155111: "0xa066b5C30382110d19925108BBa1Eef613a3A041", //Ethereum
31
- 421614: "0xbda65d65A7833D28F9391FF01d0b212B75538Cf2", //Arbitrum
27
+ 2201: "0xe1c1456CAb802312759a8cFc3976f88bf87082cf", //Stable
28
+ 5042002: "0x1B4f05f67CC33788Da4C89a7cd0b2f8E0055E605", //Arc
29
+ 84532: "0x4a251C9D79faCa20b193630A4ee313af7cBCDD93", //Base
30
+ 11155111: "0x7507a13352AFAa79D33E994f86f2f62463ba8DE4", //Ethereum
31
+ 421614: "0x5acECCdeb5CbD3C727eCB49F8706Eb80EF2f977F", //Arbitrum
32
32
  42431: "0xE559fB936C69c46E216bf61B07C16bF1a6d444aa", //Tempo
33
33
  });
34
34
 
package/src/index.d.ts CHANGED
@@ -188,9 +188,7 @@ declare module "@fairblock/stabletrust" {
188
188
  /**
189
189
  * Get on-chain core state for an anonymous account.
190
190
  */
191
- getAnonymousAccountInfo(
192
- accountId: number | bigint,
193
- ): Promise<AnonymousAccountInfo>;
191
+ getAnonymousAccountInfo(accountId: string): Promise<AnonymousAccountInfo>;
194
192
 
195
193
  /**
196
194
  * Read the current authNonce for an anonymous account.
@@ -211,7 +209,7 @@ declare module "@fairblock/stabletrust" {
211
209
  * Use `.available` and `.pending` for individual balances, `.amount` for the total.
212
210
  */
213
211
  getBalance(
214
- accountId: number | bigint,
212
+ accountId: string,
215
213
  tokenAddress: string,
216
214
  elGamalPrivateKey: string,
217
215
  ): Promise<AnonymousAccountBalance>;
@@ -221,7 +219,7 @@ declare module "@fairblock/stabletrust" {
221
219
  * Use `getBalance` for a simpler numeric summary.
222
220
  */
223
221
  getAnonymousBalance(
224
- accountId: number | bigint,
222
+ accountId: string,
225
223
  tokenAddress: string,
226
224
  elGamalPrivateKey: string,
227
225
  ): Promise<AnonymousBalance>;
@@ -484,7 +482,7 @@ declare module "@fairblock/stabletrust" {
484
482
  /**
485
483
  * Deposit options
486
484
  */
487
- export interface DepositOptions {
485
+ export interface ConfidentialDepositOptions {
488
486
  waitForFinalization?: boolean;
489
487
  }
490
488
 
@@ -560,7 +558,7 @@ declare module "@fairblock/stabletrust" {
560
558
  wallet: ethers.Wallet | ethers.Signer,
561
559
  tokenAddress: string,
562
560
  amount: bigint | string | number,
563
- options?: DepositOptions,
561
+ options?: ConfidentialDepositOptions,
564
562
  ): Promise<ethers.ContractTransactionReceipt>;
565
563
 
566
564
  /**
@@ -1,6 +0,0 @@
1
- export const ERC20_ABI = [
2
- "function approve(address spender, uint256 amount) external returns (bool)",
3
- "function allowance(address owner, address spender) external view returns (uint256)",
4
- "function balanceOf(address account) external view returns (uint256)",
5
- "function decimals() external view returns (uint8)",
6
- ];