@fairblock/stabletrust 1.0.8 → 1.0.10
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 +28 -25
- package/examples/complete-flow.js +2 -4
- package/examples/simple-snippets.js +5 -10
- package/package.json +1 -1
- package/src/client.js +34 -9
- package/src/constants.js +13 -8
- package/src/index.d.ts +29 -5
- package/src/index.js +6 -1
package/README.md
CHANGED
|
@@ -36,14 +36,14 @@ yarn add @fairblock/stabletrust
|
|
|
36
36
|
|
|
37
37
|
The following contract addresses are available for confidential transfers on testnet networks. These are test deployments and should not be used with mainnet assets:
|
|
38
38
|
|
|
39
|
-
| Network
|
|
40
|
-
|
|
|
41
|
-
| Stable
|
|
42
|
-
| Arc
|
|
43
|
-
| Base
|
|
44
|
-
| Ethereum
|
|
45
|
-
| Arbitrum
|
|
46
|
-
| Tempo
|
|
39
|
+
| Network(Testnet) | Chain ID | Contract Address |
|
|
40
|
+
| :--------------- | :------- | :------------------------------------------- |
|
|
41
|
+
| Stable | 2201 | `0x29E4fd434758b1677c10854Fa81C2fc496D76E62` |
|
|
42
|
+
| Arc | 1244 | `0xf085e801a6FD9d03b09566a738734B7e2Bb065De` |
|
|
43
|
+
| Base | 84532 | `0x6FE45A71F5232a4E5e583Ae31A538360fB1e6aDb` |
|
|
44
|
+
| Ethereum | 11155111 | `0x81a2c161c0327464430658516eE74A669feFC7bC` |
|
|
45
|
+
| Arbitrum | 421614 | `0xa59462200F6E438c538b914eB5F980B3Fa723aA0` |
|
|
46
|
+
| Tempo | 42431 | `0xB7bdce025c8a25e341Cb55795f8ba865AB3e392C` |
|
|
47
47
|
|
|
48
48
|
## Usage
|
|
49
49
|
|
|
@@ -57,37 +57,40 @@ Import and initialize the client with your network configuration.
|
|
|
57
57
|
import { ConfidentialTransferClient } from "@fairblock/stabletrust";
|
|
58
58
|
import { ethers } from "ethers";
|
|
59
59
|
|
|
60
|
-
// Configuration for
|
|
60
|
+
// Configuration for Base Sepolia (uses SDK default StableTrust contract for chainId 84532)
|
|
61
61
|
const client = new ConfidentialTransferClient(
|
|
62
|
-
"https://
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
"https://sepolia.base.org",
|
|
63
|
+
84532,
|
|
64
|
+
);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If you are using a custom deployment, you can still pass an explicit contract address:
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
const customClient = new ConfidentialTransferClient(
|
|
71
|
+
"https://sepolia.base.org",
|
|
72
|
+
"0xYourCustomStableTrustContract",
|
|
73
|
+
84532,
|
|
65
74
|
);
|
|
66
75
|
```
|
|
67
76
|
|
|
68
77
|
#### Network Configuration Examples
|
|
69
78
|
|
|
70
|
-
For
|
|
79
|
+
For testnet networks listed above, use the following configurations:
|
|
71
80
|
|
|
72
81
|
```javascript
|
|
73
|
-
//
|
|
74
|
-
const
|
|
75
|
-
"https://
|
|
76
|
-
|
|
77
|
-
8453,
|
|
82
|
+
// Stable testnet
|
|
83
|
+
const stableClient = new ConfidentialTransferClient(
|
|
84
|
+
"https://rpc.testnet.stable.xyz",
|
|
85
|
+
2201,
|
|
78
86
|
);
|
|
79
87
|
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
"https://eth.public.netzach.io",
|
|
83
|
-
"0x7B5A0060dE15a1AA1b9712A0146145E9D01A1acA",
|
|
84
|
-
1,
|
|
85
|
-
);
|
|
88
|
+
// Arc testnet
|
|
89
|
+
const arcClient = new ConfidentialTransferClient("https://rpc.arc.xyz", 1244);
|
|
86
90
|
|
|
87
91
|
// Tempo (Stablecoin chain with special fee handling)
|
|
88
92
|
const tempoClient = new ConfidentialTransferClient(
|
|
89
93
|
"https://tempo-rpc.example.com",
|
|
90
|
-
"0x17176c409B66bb03d102215eeEdb34259Db0F5AD",
|
|
91
94
|
42431,
|
|
92
95
|
);
|
|
93
96
|
```
|
|
@@ -11,8 +11,7 @@ dotenv.config();
|
|
|
11
11
|
* https://docs.fairblock.network/docs/confidential_transfers/technical_overview
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
// You can deploy your own confidential contract on any EVM network and use that.
|
|
14
|
+
// Optional override for custom confidential contract deployments.
|
|
16
15
|
const CONTRACT_ADDRESS =
|
|
17
16
|
process.env.CONTRACT_ADDRESS || "0x29E4fd434758b1677c10854Fa81C2fc496D76E62";
|
|
18
17
|
// Standard ERC20 token contract. Any ERC20 on this chain ID can be used.
|
|
@@ -23,7 +22,6 @@ const RPC_URL =
|
|
|
23
22
|
const EXPLORER_URL =
|
|
24
23
|
process.env.EXPLORER_URL || "https://testnet.stablescan.xyz/tx/";
|
|
25
24
|
const CHAIN_ID = process.env.CHAIN_ID || 2201;
|
|
26
|
-
1;
|
|
27
25
|
|
|
28
26
|
/**
|
|
29
27
|
* Performance Utility: Tracks execution time and provides timestamps
|
|
@@ -47,7 +45,7 @@ async function main() {
|
|
|
47
45
|
const client = new ConfidentialTransferClient(
|
|
48
46
|
RPC_URL,
|
|
49
47
|
CONTRACT_ADDRESS,
|
|
50
|
-
CHAIN_ID,
|
|
48
|
+
Number(CHAIN_ID),
|
|
51
49
|
);
|
|
52
50
|
|
|
53
51
|
const provider = new ethers.JsonRpcProvider(RPC_URL);
|
|
@@ -2,17 +2,12 @@ import { ethers } from "ethers";
|
|
|
2
2
|
import dotenv from "dotenv";
|
|
3
3
|
import { ConfidentialTransferClient } from "@fairblock/stabletrust";
|
|
4
4
|
dotenv.config();
|
|
5
|
-
const RPC_URL =
|
|
6
|
-
process.env.ETHEREUM_RPC_URL || "https://rpc.testnet.stable.xyz";
|
|
5
|
+
const RPC_URL = process.env.RPC_URL || "https://rpc.testnet.stable.xyz";
|
|
7
6
|
const EXPLORER_URL =
|
|
8
7
|
process.env.EXPLORER_URL || "https://testnet.stablescan.xyz/tx/";
|
|
9
8
|
async function minimalFlow() {
|
|
10
9
|
// 1. Setup Client & Wallets
|
|
11
|
-
const client = new ConfidentialTransferClient(
|
|
12
|
-
RPC_URL,
|
|
13
|
-
"0x29E4fd434758b1677c10854Fa81C2fc496D76E62",
|
|
14
|
-
2201,
|
|
15
|
-
);
|
|
10
|
+
const client = new ConfidentialTransferClient(RPC_URL, 2201);
|
|
16
11
|
|
|
17
12
|
const provider = new ethers.JsonRpcProvider(RPC_URL);
|
|
18
13
|
const sender = new ethers.Wallet(process.env.SENDER_PRIVATE_KEY, provider);
|
|
@@ -26,7 +21,7 @@ async function minimalFlow() {
|
|
|
26
21
|
await client.ensureAccount(recipient);
|
|
27
22
|
|
|
28
23
|
const TOKEN = "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9";
|
|
29
|
-
const amount = ethers.parseUnits("0.
|
|
24
|
+
const amount = ethers.parseUnits("0.1", 2);
|
|
30
25
|
let res;
|
|
31
26
|
// 3. DEPOSIT: Move public ERC20 into the confidential contract
|
|
32
27
|
res = await client.confidentialDeposit(sender, TOKEN, amount);
|
|
@@ -37,11 +32,11 @@ async function minimalFlow() {
|
|
|
37
32
|
sender,
|
|
38
33
|
recipient.address,
|
|
39
34
|
TOKEN,
|
|
40
|
-
|
|
35
|
+
ethers.parseUnits("0.05", 2),
|
|
41
36
|
);
|
|
42
37
|
console.log("Transfer TX Hash:", EXPLORER_URL + res.hash);
|
|
43
38
|
// 5. WITHDRAW: Convert confidential balance back to public ERC20
|
|
44
|
-
res = await client.withdraw(recipient, TOKEN,
|
|
39
|
+
res = await client.withdraw(recipient, TOKEN, ethers.parseUnits("0.05", 2));
|
|
45
40
|
console.log("Withdraw TX Hash:", EXPLORER_URL + res.hash);
|
|
46
41
|
console.log("Confidential flow complete.");
|
|
47
42
|
}
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -2,7 +2,7 @@ import { ethers } from "ethers";
|
|
|
2
2
|
import {
|
|
3
3
|
CONTRACT_ABI,
|
|
4
4
|
ERC20_ABI,
|
|
5
|
-
|
|
5
|
+
getStableTrustContractAddress,
|
|
6
6
|
TEMPO_FEE_TOKEN_ADDRESS,
|
|
7
7
|
} from "./constants.js";
|
|
8
8
|
import { deriveKeys, decryptCiphertext, combineCiphertext } from "./crypto.js";
|
|
@@ -27,23 +27,48 @@ export class ConfidentialTransferClient {
|
|
|
27
27
|
* Create a new ConfidentialTransferClient instance
|
|
28
28
|
*
|
|
29
29
|
* @param {string} rpcUrl - RPC endpoint URL
|
|
30
|
-
* @param {string}
|
|
31
|
-
* @param {number} chainId - Chain ID
|
|
30
|
+
* @param {string|number} contractAddressOrChainId - Contract address or chain ID
|
|
31
|
+
* @param {number} [chainId] - Chain ID when contract address is provided
|
|
32
32
|
*/
|
|
33
|
-
constructor(rpcUrl,
|
|
33
|
+
constructor(rpcUrl, contractAddressOrChainId, chainId) {
|
|
34
34
|
// Validate required config
|
|
35
35
|
if (!rpcUrl) {
|
|
36
36
|
throw new Error("rpcUrl is required");
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
|
|
39
|
+
let resolvedChainId;
|
|
40
|
+
let resolvedContractAddress;
|
|
41
|
+
|
|
42
|
+
if (typeof contractAddressOrChainId === "number" && chainId === undefined) {
|
|
43
|
+
resolvedChainId = contractAddressOrChainId;
|
|
44
|
+
resolvedContractAddress = getStableTrustContractAddress(resolvedChainId);
|
|
45
|
+
} else {
|
|
46
|
+
resolvedChainId = chainId;
|
|
47
|
+
resolvedContractAddress =
|
|
48
|
+
contractAddressOrChainId ||
|
|
49
|
+
getStableTrustContractAddress(resolvedChainId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!resolvedChainId) {
|
|
53
|
+
throw new Error("chainId is required");
|
|
54
|
+
}
|
|
55
|
+
if (!resolvedContractAddress) {
|
|
56
|
+
const supportedChainIds = [2201, 1244, 84532, 11155111, 421614, 42431]
|
|
57
|
+
.map(String)
|
|
58
|
+
.join(", ");
|
|
59
|
+
throw new Error(
|
|
60
|
+
`contractAddress is required for chainId ${resolvedChainId}. No default StableTrust contract is configured for this chain. Supported chainIds: ${supportedChainIds}`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (!ethers.isAddress(resolvedContractAddress)) {
|
|
64
|
+
throw new Error(`Invalid contractAddress: ${resolvedContractAddress}`);
|
|
40
65
|
}
|
|
41
66
|
|
|
42
67
|
// Build config
|
|
43
68
|
this.config = {
|
|
44
69
|
rpcUrl,
|
|
45
|
-
contractAddress,
|
|
46
|
-
chainId,
|
|
70
|
+
contractAddress: ethers.getAddress(resolvedContractAddress),
|
|
71
|
+
chainId: Number(resolvedChainId),
|
|
47
72
|
};
|
|
48
73
|
|
|
49
74
|
// WASM will be auto-initialized on first use
|
|
@@ -948,7 +973,7 @@ export class ConfidentialTransferClient {
|
|
|
948
973
|
* @param {string} tokenAddress - Token address
|
|
949
974
|
* @returns {Promise<bigint>} Token balance
|
|
950
975
|
*/
|
|
951
|
-
async
|
|
976
|
+
async getPublicBalance(address, tokenAddress) {
|
|
952
977
|
try {
|
|
953
978
|
if (!address || !ethers.isAddress(address)) {
|
|
954
979
|
throw new Error(`Invalid address: ${address}`);
|
package/src/constants.js
CHANGED
|
@@ -20,13 +20,18 @@ export const ERC20_ABI = [
|
|
|
20
20
|
"function balanceOf(address account) external view returns (uint256)",
|
|
21
21
|
];
|
|
22
22
|
|
|
23
|
-
export const DEFAULT_CONFIG = {
|
|
24
|
-
CHAIN_ID: 421614,
|
|
25
|
-
EXPLORER_URL: "https://sepolia.arbiscan.io/tx/",
|
|
26
|
-
RPC_URL: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
27
|
-
CONTRACT_ADDRESS: "0x30bAc8a17DCACbA7f70F305f4ad908C9fd6d3E2E",
|
|
28
|
-
TOKEN_ADDRESS: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
29
|
-
};
|
|
30
|
-
|
|
31
23
|
export const TEMPO_FEE_TOKEN_ADDRESS =
|
|
32
24
|
"0x20c0000000000000000000000000000000000000";
|
|
25
|
+
|
|
26
|
+
export const STABLETRUST_CONTRACTS_BY_CHAIN_ID = Object.freeze({
|
|
27
|
+
2201: "0x29E4fd434758b1677c10854Fa81C2fc496D76E62",
|
|
28
|
+
1244: "0xf085e801a6FD9d03b09566a738734B7e2Bb065De",
|
|
29
|
+
84532: "0x6FE45A71F5232a4E5e583Ae31A538360fB1e6aDb",
|
|
30
|
+
11155111: "0x81a2c161c0327464430658516eE74A669feFC7bC",
|
|
31
|
+
421614: "0xa59462200F6E438c538b914eB5F980B3Fa723aA0",
|
|
32
|
+
42431: "0xB7bdce025c8a25e341Cb55795f8ba865AB3e392C",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export function getStableTrustContractAddress(chainId) {
|
|
36
|
+
return STABLETRUST_CONTRACTS_BY_CHAIN_ID[Number(chainId)] || null;
|
|
37
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ declare module "@fairblock/stabletrust" {
|
|
|
10
10
|
chainId: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export type StableTrustChainId =
|
|
14
|
+
| 1244
|
|
15
|
+
| 2201
|
|
16
|
+
| 42431
|
|
17
|
+
| 84532
|
|
18
|
+
| 421614
|
|
19
|
+
| 11155111;
|
|
20
|
+
|
|
13
21
|
/**
|
|
14
22
|
* Encryption keys
|
|
15
23
|
*/
|
|
@@ -71,6 +79,13 @@ declare module "@fairblock/stabletrust" {
|
|
|
71
79
|
* WASM auto-initializes on first use - no manual initialization required!
|
|
72
80
|
*/
|
|
73
81
|
export class ConfidentialTransferClient {
|
|
82
|
+
/**
|
|
83
|
+
* Create a new ConfidentialTransferClient
|
|
84
|
+
* @param rpcUrl RPC endpoint URL
|
|
85
|
+
* @param chainId Chain ID (uses default StableTrust contract for known networks)
|
|
86
|
+
*/
|
|
87
|
+
constructor(rpcUrl: string, chainId: number);
|
|
88
|
+
|
|
74
89
|
/**
|
|
75
90
|
* Create a new ConfidentialTransferClient
|
|
76
91
|
* @param rpcUrl RPC endpoint URL
|
|
@@ -153,6 +168,13 @@ declare module "@fairblock/stabletrust" {
|
|
|
153
168
|
* Get the current fee amount for confidential transfers
|
|
154
169
|
*/
|
|
155
170
|
getFeeAmount(): Promise<bigint>;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get the public ERC20 balance for an address (for comparison with confidential balance)
|
|
174
|
+
* @param address Account address
|
|
175
|
+
* @param tokenAddress Token contract address
|
|
176
|
+
*/
|
|
177
|
+
getPublicBalance(address: string, tokenAddress: string): Promise<bigint>;
|
|
156
178
|
}
|
|
157
179
|
|
|
158
180
|
/**
|
|
@@ -211,10 +233,12 @@ declare module "@fairblock/stabletrust" {
|
|
|
211
233
|
export const ERC20_ABI: any[];
|
|
212
234
|
|
|
213
235
|
/**
|
|
214
|
-
*
|
|
236
|
+
* StableTrust contract addresses by chain id
|
|
237
|
+
*/
|
|
238
|
+
export const STABLETRUST_CONTRACTS_BY_CHAIN_ID: Record<number, string>;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Resolve StableTrust contract address by chain id
|
|
215
242
|
*/
|
|
216
|
-
export
|
|
217
|
-
CHAIN_ID: number;
|
|
218
|
-
EXPLORER_URL: string;
|
|
219
|
-
};
|
|
243
|
+
export function getStableTrustContractAddress(chainId: number): string | null;
|
|
220
244
|
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export { ConfidentialTransferClient } from "./client.js";
|
|
2
2
|
export { deriveKeys, decryptCiphertext, combineCiphertext } from "./crypto.js";
|
|
3
3
|
export { encodeTransferProof, encodeWithdrawProof } from "./utils.js";
|
|
4
|
-
export {
|
|
4
|
+
export {
|
|
5
|
+
CONTRACT_ABI,
|
|
6
|
+
ERC20_ABI,
|
|
7
|
+
STABLETRUST_CONTRACTS_BY_CHAIN_ID,
|
|
8
|
+
getStableTrustContractAddress,
|
|
9
|
+
} from "./constants.js";
|
|
5
10
|
// Note: initializeWasm is now internal - WASM auto-initializes on first client use
|