@fairblock/stabletrust 1.0.21 → 2.0.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/README.md +365 -108
- package/examples/anonymous-flow.js +334 -0
- package/examples/complete-flow.js +1 -1
- package/package.json +1 -1
- package/pkg/README.md +310 -0
- package/pkg/confidential_transfer_proof_generation.d.ts +13 -9
- package/pkg/confidential_transfer_proof_generation.js +571 -604
- package/pkg/confidential_transfer_proof_generation_bg.wasm +0 -0
- package/pkg/confidential_transfer_proof_generation_bg.wasm.d.ts +2 -0
- package/src/anonymous-client.js +1371 -0
- package/src/{client.js → confidential-client.js} +12 -9
- package/src/constants.js +8 -9
- package/src/index.d.ts +345 -2
- package/src/index.js +2 -1
- package/pkg/proof_generator_wasm_bg.wasm +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous Confidential Transfer — Basic Flow
|
|
3
|
+
*
|
|
4
|
+
* [1] Create two anonymous accounts (anon1, anon2)
|
|
5
|
+
* [2] Deposit tokens into anon1
|
|
6
|
+
* [3] Transfer anon1 → anon2
|
|
7
|
+
* [4] Apply pending balance for anon2
|
|
8
|
+
* [5] Withdraw anon2 → public address
|
|
9
|
+
*
|
|
10
|
+
* Run:
|
|
11
|
+
* node examples/anonymous-flow.js
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { ethers } from "ethers";
|
|
15
|
+
import dotenv from "dotenv";
|
|
16
|
+
import { AnonymousTransferClient } from "@fairblock/stabletrust";
|
|
17
|
+
|
|
18
|
+
dotenv.config();
|
|
19
|
+
|
|
20
|
+
// ── Config ────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
const EVM_RPC = process.env.EVM_RPC ?? "http://127.0.0.1:8545";
|
|
23
|
+
const FAIRYCLOAK_URL = process.env.FAIRYCLOAK_URL ?? "http://127.0.0.1:8080";
|
|
24
|
+
const CHAIN_ID = Number(process.env.CHAIN_ID ?? "31337");
|
|
25
|
+
const TOKEN_ADDRESS =
|
|
26
|
+
process.env.TOKEN_ADDRESS ?? "0xE915164570b027C2A0FfadcB1B672192E35BF008";
|
|
27
|
+
const TOKEN_DECIMALS = Number(process.env.TOKEN_DECIMALS ?? "6");
|
|
28
|
+
|
|
29
|
+
// Amounts in raw ERC-20 units (6 decimals — matches complete-flow.js)
|
|
30
|
+
const DEPOSIT_AMOUNT = 100_000n; // 0.1 USDC
|
|
31
|
+
const TRANSFER_AMOUNT = 50_000n; // 0.05 USDC
|
|
32
|
+
const WITHDRAW_AMOUNT = 25_000n; // 0.025 USDC
|
|
33
|
+
|
|
34
|
+
// Anvil test accounts — override via .env
|
|
35
|
+
const ANON1_PK =
|
|
36
|
+
process.env.ANON1_PRIVATE_KEY ??
|
|
37
|
+
"0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6";
|
|
38
|
+
const ANON2_PK =
|
|
39
|
+
process.env.ANON2_PRIVATE_KEY ??
|
|
40
|
+
"0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba";
|
|
41
|
+
const WITHDRAW_DEST_PK =
|
|
42
|
+
process.env.WITHDRAW_DEST_PK ??
|
|
43
|
+
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
|
|
44
|
+
|
|
45
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
const log = console.log;
|
|
48
|
+
const fmt = (n, decimals) => ethers.formatUnits(BigInt(n), decimals);
|
|
49
|
+
|
|
50
|
+
async function logBalance(client, accountId, keys, token, _decimals, label) {
|
|
51
|
+
const bal = await client.getBalance(accountId, token, keys.privateKey);
|
|
52
|
+
|
|
53
|
+
// getBalance() returns amounts in contract scale (rawAmount * 100 / 10^tokenDecimals).
|
|
54
|
+
// Formatting with 2 decimal places gives the correct human-readable value: e.g.
|
|
55
|
+
// contract scale 10 → formatUnits(10, 2) = "0.10" (= 0.1 USDC)
|
|
56
|
+
// contract scale 5 → formatUnits(5, 2) = "0.05" (= 0.05 USDC)
|
|
57
|
+
console.log(
|
|
58
|
+
`[BALANCE][${label}] acc=${accountId} | ` +
|
|
59
|
+
`avail=${fmt(bal.available, 2)} | ` +
|
|
60
|
+
`pending=${fmt(bal.pending, 2)} | ` +
|
|
61
|
+
`total=${fmt(bal.amount, 2)}`,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return bal;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function diff(prev, next) {
|
|
68
|
+
return {
|
|
69
|
+
avail: next.available - prev.available,
|
|
70
|
+
pending: next.pending - prev.pending,
|
|
71
|
+
total: next.amount - prev.amount,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Poll until hasPendingAction flips to false (CW→EVM state settled).
|
|
77
|
+
*/
|
|
78
|
+
async function waitForAccountReady(
|
|
79
|
+
client,
|
|
80
|
+
accountId,
|
|
81
|
+
{ timeoutMs = 180_000, label = "" } = {},
|
|
82
|
+
) {
|
|
83
|
+
const tag = label ? `[${label}] ` : "";
|
|
84
|
+
process.stdout.write(
|
|
85
|
+
` → ${tag}waiting for account ${accountId} to be ready`,
|
|
86
|
+
);
|
|
87
|
+
const deadline = Date.now() + timeoutMs;
|
|
88
|
+
while (Date.now() < deadline) {
|
|
89
|
+
const info = await client.getAnonymousAccountInfo(accountId);
|
|
90
|
+
if (!info.hasPendingAction) {
|
|
91
|
+
console.log(" ✓");
|
|
92
|
+
return info;
|
|
93
|
+
}
|
|
94
|
+
process.stdout.write(".");
|
|
95
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
96
|
+
}
|
|
97
|
+
console.log(" ✗");
|
|
98
|
+
throw new Error(`Timeout waiting for account ${accountId} to be ready`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
async function main() {
|
|
104
|
+
console.log("\n═══════════════════════════════════════════════════════");
|
|
105
|
+
console.log(" Anonymous Confidential Transfer — Basic Flow");
|
|
106
|
+
console.log("═══════════════════════════════════════════════════════");
|
|
107
|
+
log("Fairycloak :", FAIRYCLOAK_URL);
|
|
108
|
+
log("Token :", TOKEN_ADDRESS);
|
|
109
|
+
|
|
110
|
+
const provider = new ethers.JsonRpcProvider(EVM_RPC);
|
|
111
|
+
const anon1Wallet = new ethers.Wallet(ANON1_PK, provider);
|
|
112
|
+
const anon2Wallet = new ethers.Wallet(ANON2_PK, provider);
|
|
113
|
+
const withdrawDest = new ethers.Wallet(WITHDRAW_DEST_PK, provider).address;
|
|
114
|
+
|
|
115
|
+
log("Anon1 :", anon1Wallet.address);
|
|
116
|
+
log("Anon2 :", anon2Wallet.address);
|
|
117
|
+
log("Withdraw → :", withdrawDest);
|
|
118
|
+
|
|
119
|
+
const client = new AnonymousTransferClient({
|
|
120
|
+
fairycloakUrl: FAIRYCLOAK_URL,
|
|
121
|
+
chainId: CHAIN_ID,
|
|
122
|
+
rpcUrl: EVM_RPC,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ── [1] Create accounts ──────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
console.log("\n── [1] Create anonymous accounts ───────────────────────");
|
|
128
|
+
|
|
129
|
+
const nextId = await client.getNextAccountId();
|
|
130
|
+
const anon1Id = Number(nextId) + 1;
|
|
131
|
+
const anon2Id = anon1Id + 1;
|
|
132
|
+
log("anon1 id will be:", anon1Id);
|
|
133
|
+
log("anon2 id will be:", anon2Id);
|
|
134
|
+
|
|
135
|
+
log("Deriving ElGamal keypairs …");
|
|
136
|
+
const [anon1Keys, anon2Keys] = await Promise.all([
|
|
137
|
+
client.deriveAnonymousKeys(anon1Wallet, anon1Id),
|
|
138
|
+
client.deriveAnonymousKeys(anon2Wallet, anon2Id),
|
|
139
|
+
]);
|
|
140
|
+
log("anon1 pubkey:", anon1Keys.publicKey);
|
|
141
|
+
log("anon2 pubkey:", anon2Keys.publicKey);
|
|
142
|
+
|
|
143
|
+
log(` creating anon1 (id=${anon1Id}) …`);
|
|
144
|
+
const cr1 = await client.createAccount(anon1Wallet, anon1Keys.publicKey);
|
|
145
|
+
log(` request_id: ${cr1.request_id} status: ${cr1.status}`);
|
|
146
|
+
if (cr1.error) log(` [!] Error: ${cr1.error}`);
|
|
147
|
+
|
|
148
|
+
log(` creating anon2 (id=${anon2Id}) …`);
|
|
149
|
+
const cr2 = await client.createAccount(anon2Wallet, anon2Keys.publicKey);
|
|
150
|
+
log(` request_id: ${cr2.request_id} status: ${cr2.status}`);
|
|
151
|
+
if (cr2.error) log(` [!] Error: ${cr2.error}`);
|
|
152
|
+
|
|
153
|
+
log("Waiting for Fairycloak to confirm account creations …");
|
|
154
|
+
const [r1, r2] = await Promise.all([
|
|
155
|
+
client.waitForRequest(cr1.request_id, { timeoutMs: 180_000 }),
|
|
156
|
+
client.waitForRequest(cr2.request_id, { timeoutMs: 180_000 }),
|
|
157
|
+
]);
|
|
158
|
+
if (r1.error) log(" [!] create anon1 failed:", r1.error);
|
|
159
|
+
if (r2.error) log(" [!] create anon2 failed:", r2.error);
|
|
160
|
+
|
|
161
|
+
await waitForAccountReady(client, anon1Id, { label: "anon1" });
|
|
162
|
+
await waitForAccountReady(client, anon2Id, { label: "anon2" });
|
|
163
|
+
|
|
164
|
+
const [i1, i2] = await Promise.all([
|
|
165
|
+
client.getAnonymousAccountInfo(anon1Id),
|
|
166
|
+
client.getAnonymousAccountInfo(anon2Id),
|
|
167
|
+
]);
|
|
168
|
+
log(`anon1 exists=${i1.exists} finalized=${i1.finalized}`);
|
|
169
|
+
log(`anon2 exists=${i2.exists} finalized=${i2.finalized}`);
|
|
170
|
+
|
|
171
|
+
await logBalance(
|
|
172
|
+
client,
|
|
173
|
+
anon1Id,
|
|
174
|
+
anon1Keys,
|
|
175
|
+
TOKEN_ADDRESS,
|
|
176
|
+
TOKEN_DECIMALS,
|
|
177
|
+
"anon1:post-create",
|
|
178
|
+
);
|
|
179
|
+
await logBalance(
|
|
180
|
+
client,
|
|
181
|
+
anon2Id,
|
|
182
|
+
anon2Keys,
|
|
183
|
+
TOKEN_ADDRESS,
|
|
184
|
+
TOKEN_DECIMALS,
|
|
185
|
+
"anon2:post-create",
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// ── [2] Deposit ──────────────────────────────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
console.log("\n── [2] Deposit into anon1 ──────────────────────────────");
|
|
191
|
+
log(`Depositing ${fmt(DEPOSIT_AMOUNT, TOKEN_DECIMALS)} tokens into anon1 (anon1 pays gas) …`);
|
|
192
|
+
|
|
193
|
+
const dep = await client.deposit(
|
|
194
|
+
anon1Wallet,
|
|
195
|
+
anon1Id,
|
|
196
|
+
TOKEN_ADDRESS,
|
|
197
|
+
DEPOSIT_AMOUNT,
|
|
198
|
+
);
|
|
199
|
+
log(" request_id:", dep.request_id, " status:", dep.status);
|
|
200
|
+
if (dep.error) log(" [!] Error:", dep.error);
|
|
201
|
+
if (dep.tx_hash) log(" tx_hash :", dep.tx_hash);
|
|
202
|
+
|
|
203
|
+
const depFinal = await client.waitForRequest(dep.request_id, {
|
|
204
|
+
timeoutMs: 180_000,
|
|
205
|
+
});
|
|
206
|
+
log(" status :", depFinal.status, " tx_hash:", depFinal.tx_hash ?? "—");
|
|
207
|
+
|
|
208
|
+
await waitForAccountReady(client, anon1Id, { label: "deposit" });
|
|
209
|
+
|
|
210
|
+
await logBalance(
|
|
211
|
+
client,
|
|
212
|
+
anon1Id,
|
|
213
|
+
anon1Keys,
|
|
214
|
+
TOKEN_ADDRESS,
|
|
215
|
+
TOKEN_DECIMALS,
|
|
216
|
+
"anon1:post-deposit",
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// ── [3] Transfer anon1 → anon2 ───────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
console.log("\n── [3] Transfer anon1 → anon2 ──────────────────────────");
|
|
222
|
+
log(`Transferring ${fmt(TRANSFER_AMOUNT, TOKEN_DECIMALS)} tokens …`);
|
|
223
|
+
|
|
224
|
+
const txAnon = await client.transferToAnonymous(anon1Wallet, anon1Id, {
|
|
225
|
+
recipientId: anon2Id,
|
|
226
|
+
token: TOKEN_ADDRESS,
|
|
227
|
+
elGamalPrivateKey: anon1Keys.privateKey,
|
|
228
|
+
amount: TRANSFER_AMOUNT,
|
|
229
|
+
});
|
|
230
|
+
log(" request_id:", txAnon.request_id, " status:", txAnon.status);
|
|
231
|
+
if (txAnon.error) log(" [!] Error:", txAnon.error);
|
|
232
|
+
|
|
233
|
+
const txAnonFinal = await client.waitForRequest(txAnon.request_id, {
|
|
234
|
+
timeoutMs: 180_000,
|
|
235
|
+
});
|
|
236
|
+
log(
|
|
237
|
+
" status :",
|
|
238
|
+
txAnonFinal.status,
|
|
239
|
+
" tx_hash:",
|
|
240
|
+
txAnonFinal.tx_hash ?? "—",
|
|
241
|
+
);
|
|
242
|
+
if (txAnonFinal.error) log(" [!] Error:", txAnonFinal.error);
|
|
243
|
+
|
|
244
|
+
await waitForAccountReady(client, anon1Id, { label: "transfer sender" });
|
|
245
|
+
await waitForAccountReady(client, anon2Id, { label: "transfer recipient" });
|
|
246
|
+
|
|
247
|
+
await logBalance(
|
|
248
|
+
client,
|
|
249
|
+
anon1Id,
|
|
250
|
+
anon1Keys,
|
|
251
|
+
TOKEN_ADDRESS,
|
|
252
|
+
TOKEN_DECIMALS,
|
|
253
|
+
"anon1:post-transfer",
|
|
254
|
+
);
|
|
255
|
+
await logBalance(
|
|
256
|
+
client,
|
|
257
|
+
anon2Id,
|
|
258
|
+
anon2Keys,
|
|
259
|
+
TOKEN_ADDRESS,
|
|
260
|
+
TOKEN_DECIMALS,
|
|
261
|
+
"anon2:post-transfer",
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// ── [4] Apply pending for anon2 ──────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
console.log("\n── [4] Apply pending balance for anon2 ─────────────────");
|
|
267
|
+
log("Moving incoming credit from pending → available …");
|
|
268
|
+
|
|
269
|
+
const ap = await client.applyPending(anon2Wallet, anon2Id);
|
|
270
|
+
log(" request_id:", ap.request_id, " status:", ap.status);
|
|
271
|
+
if (ap.error) log(" [!] Error:", ap.error);
|
|
272
|
+
|
|
273
|
+
const apFinal = await client.waitForRequest(ap.request_id, {
|
|
274
|
+
timeoutMs: 180_000,
|
|
275
|
+
});
|
|
276
|
+
log(" status :", apFinal.status, " tx_hash:", apFinal.tx_hash ?? "—");
|
|
277
|
+
if (apFinal.error) log(" [!] Error:", apFinal.error);
|
|
278
|
+
|
|
279
|
+
await waitForAccountReady(client, anon2Id, { label: "applyPending" });
|
|
280
|
+
|
|
281
|
+
await logBalance(
|
|
282
|
+
client,
|
|
283
|
+
anon2Id,
|
|
284
|
+
anon2Keys,
|
|
285
|
+
TOKEN_ADDRESS,
|
|
286
|
+
TOKEN_DECIMALS,
|
|
287
|
+
"anon2:post-apply",
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
// ── [5] Withdraw anon2 → public address ──────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
console.log("\n── [5] Withdraw from anon2 ─────────────────────────────");
|
|
293
|
+
log(`Withdrawing ${fmt(WITHDRAW_AMOUNT, TOKEN_DECIMALS)} tokens → ${withdrawDest} …`);
|
|
294
|
+
|
|
295
|
+
const wd = await client.withdraw(anon2Wallet, anon2Id, {
|
|
296
|
+
destination: withdrawDest,
|
|
297
|
+
token: TOKEN_ADDRESS,
|
|
298
|
+
plainAmount: WITHDRAW_AMOUNT,
|
|
299
|
+
elGamalPrivateKey: anon2Keys.privateKey,
|
|
300
|
+
});
|
|
301
|
+
log(" request_id:", wd.request_id, " status:", wd.status);
|
|
302
|
+
if (wd.error) log(" [!] Error:", wd.error);
|
|
303
|
+
|
|
304
|
+
const wdFinal = await client.waitForRequest(wd.request_id, {
|
|
305
|
+
timeoutMs: 180_000,
|
|
306
|
+
});
|
|
307
|
+
log(" status :", wdFinal.status, " tx_hash:", wdFinal.tx_hash ?? "—");
|
|
308
|
+
if (wdFinal.error) log(" [!] Error:", wdFinal.error);
|
|
309
|
+
|
|
310
|
+
await waitForAccountReady(client, anon2Id, { label: "withdraw" });
|
|
311
|
+
|
|
312
|
+
await logBalance(
|
|
313
|
+
client,
|
|
314
|
+
anon2Id,
|
|
315
|
+
anon2Keys,
|
|
316
|
+
TOKEN_ADDRESS,
|
|
317
|
+
TOKEN_DECIMALS,
|
|
318
|
+
"anon2:post-withdraw",
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// ── Done ─────────────────────────────────────────────────────────────────────
|
|
322
|
+
|
|
323
|
+
console.log("\n═══════════════════════════════════════════════════════");
|
|
324
|
+
console.log(" ✅ Anonymous flow completed!");
|
|
325
|
+
console.log("═══════════════════════════════════════════════════════");
|
|
326
|
+
log("Deposited :", ethers.formatUnits(DEPOSIT_AMOUNT, TOKEN_DECIMALS), "tokens → anon1");
|
|
327
|
+
log("Transferred:", ethers.formatUnits(TRANSFER_AMOUNT, TOKEN_DECIMALS), "tokens → anon2");
|
|
328
|
+
log("Withdrawn :", ethers.formatUnits(WITHDRAW_AMOUNT, TOKEN_DECIMALS), "tokens →", withdrawDest);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
main().catch((err) => {
|
|
332
|
+
console.error("\n[!]", err.message ?? err);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
});
|
package/package.json
CHANGED
package/pkg/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Confidential Tokens - Privacy-First Digital Assets
|
|
2
|
+
|
|
3
|
+
A complete implementation of confidential token operations using ElGamal encryption and zero-knowledge proofs. This project includes both a Solidity smart contract (GatewayContract) and a professional web frontend for confidential token management.
|
|
4
|
+
|
|
5
|
+
## 🚀 Features
|
|
6
|
+
|
|
7
|
+
### Smart Contract (GatewayContract.sol)
|
|
8
|
+
- **Account Management**: Create and manage confidential accounts with ElGamal keypairs
|
|
9
|
+
- **Token Conversion**: Convert ERC20 tokens to confidential tokens
|
|
10
|
+
- **Private Transfers**: Transfer confidential tokens with zero-knowledge proofs
|
|
11
|
+
- **Secure Withdrawals**: Convert confidential tokens back to ERC20 tokens
|
|
12
|
+
- **Access Control**: Role-based permissions for admin and relayer operations
|
|
13
|
+
- **Event Logging**: Comprehensive event system for tracking operations
|
|
14
|
+
|
|
15
|
+
### Frontend Application
|
|
16
|
+
- **Professional UI**: Modern, responsive design optimized for real-world usage
|
|
17
|
+
- **Wallet Integration**: MetaMask connection for Ethereum blockchain access
|
|
18
|
+
- **Account Dashboard**: View encrypted balances and account status
|
|
19
|
+
- **Token Operations**: Convert, transfer, and withdraw confidential tokens
|
|
20
|
+
- **Client-Side Security**: All cryptographic operations performed in WebAssembly
|
|
21
|
+
- **Real-Time Updates**: Live status updates and transaction tracking
|
|
22
|
+
|
|
23
|
+
## 🏗️ Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
27
|
+
│ Frontend │ │ GatewayContract │ │ Relayer │
|
|
28
|
+
│ (Web App) │◄──►│ (Solidity) │◄──►│ (Off-chain) │
|
|
29
|
+
│ │ │ │ │ │
|
|
30
|
+
│ • MetaMask │ │ • Account Mgmt │ │ • Proof Verify │
|
|
31
|
+
│ • WebAssembly │ │ • Token Escrow │ │ • State Update │
|
|
32
|
+
│ • Proof Gen │ │ • Event Logging │ │ • Balance Mgmt │
|
|
33
|
+
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 📁 Project Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
elgamal-demo/
|
|
40
|
+
├── GatewayContract.sol # Main smart contract
|
|
41
|
+
├── src/ # Rust cryptographic library
|
|
42
|
+
│ ├── components/ # Core cryptographic components
|
|
43
|
+
│ │ ├── elgamal.rs # ElGamal encryption implementation
|
|
44
|
+
│ │ ├── proofs.rs # Zero-knowledge proofs
|
|
45
|
+
│ │ └── ...
|
|
46
|
+
│ ├── transfer.rs # Transfer proof generation
|
|
47
|
+
│ ├── withdraw.rs # Withdraw proof generation
|
|
48
|
+
│ └── lib.rs # Library exports
|
|
49
|
+
├── frontend/ # Web application
|
|
50
|
+
│ ├── index.html # Main application page
|
|
51
|
+
│ ├── styles.css # Professional styling
|
|
52
|
+
│ ├── app.js # Main application logic
|
|
53
|
+
│ ├── deployment-config.js # Configuration file
|
|
54
|
+
│ ├── src/lib.rs # WebAssembly bindings
|
|
55
|
+
│ └── pkg/ # Built WebAssembly files
|
|
56
|
+
└── scripts/ # Deployment scripts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 🛠️ Installation & Setup
|
|
60
|
+
|
|
61
|
+
### Prerequisites
|
|
62
|
+
- **Rust**: Latest stable version
|
|
63
|
+
- **Node.js**: 16+ with npm
|
|
64
|
+
- **Solidity**: For smart contract development
|
|
65
|
+
- **MetaMask**: Browser extension for wallet connection
|
|
66
|
+
|
|
67
|
+
### 1. Clone and Setup
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone <repository-url>
|
|
71
|
+
cd elgamal-demo
|
|
72
|
+
|
|
73
|
+
# Install Rust dependencies
|
|
74
|
+
cargo build
|
|
75
|
+
|
|
76
|
+
# Setup frontend
|
|
77
|
+
cd frontend
|
|
78
|
+
npm install
|
|
79
|
+
npm run build:wasm
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 2. Smart Contract Deployment
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Deploy GatewayContract (example using Hardhat)
|
|
86
|
+
npx hardhat compile
|
|
87
|
+
npx hardhat run scripts/deploy.js --network <network>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Configuration
|
|
91
|
+
|
|
92
|
+
Update `frontend/deployment-config.js` with your deployed contract address and network settings:
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
export const CONFIG = {
|
|
96
|
+
GATEWAY_CONTRACT_ADDRESS: '0xYourDeployedContractAddress',
|
|
97
|
+
NETWORK: {
|
|
98
|
+
chainId: 1, // Mainnet or testnet
|
|
99
|
+
name: 'Ethereum Mainnet',
|
|
100
|
+
// ... other settings
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 4. Start Development Server
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cd frontend
|
|
109
|
+
npm run dev
|
|
110
|
+
# Open http://localhost:8080
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 🔧 Usage
|
|
114
|
+
|
|
115
|
+
### 1. Connect Wallet
|
|
116
|
+
- Click "Connect Wallet" in the header
|
|
117
|
+
- Approve MetaMask connection
|
|
118
|
+
- Verify your address is displayed
|
|
119
|
+
|
|
120
|
+
### 2. Create Confidential Account
|
|
121
|
+
- Click "Create Account" card
|
|
122
|
+
- Generate ElGamal keypair (stored locally)
|
|
123
|
+
- Submit account creation to contract
|
|
124
|
+
|
|
125
|
+
### 3. Convert Tokens
|
|
126
|
+
- Select token (USDC, USDT, DAI)
|
|
127
|
+
- Enter amount to convert
|
|
128
|
+
- Approve token transfer and confirm conversion
|
|
129
|
+
|
|
130
|
+
### 4. Private Transfers
|
|
131
|
+
- Enter recipient address
|
|
132
|
+
- Select token and amount
|
|
133
|
+
- Generate zero-knowledge proof
|
|
134
|
+
- Submit confidential transfer
|
|
135
|
+
|
|
136
|
+
### 5. Withdraw Tokens
|
|
137
|
+
- Select token and amount
|
|
138
|
+
- Generate withdrawal proof
|
|
139
|
+
- Convert back to ERC20 tokens
|
|
140
|
+
|
|
141
|
+
## 🔐 Security Features
|
|
142
|
+
|
|
143
|
+
### Cryptographic Security
|
|
144
|
+
- **ElGamal Encryption**: All balances encrypted with user-specific keys
|
|
145
|
+
- **Zero-Knowledge Proofs**: Transfer amounts and balances remain private
|
|
146
|
+
- **Client-Side Generation**: All proofs generated locally in WebAssembly
|
|
147
|
+
- **No Server Dependency**: Cryptographic operations don't require server trust
|
|
148
|
+
|
|
149
|
+
### Smart Contract Security
|
|
150
|
+
- **Access Control**: Role-based permissions for sensitive operations
|
|
151
|
+
- **Reentrancy Protection**: Guards against reentrancy attacks
|
|
152
|
+
- **Pausable Operations**: Admin can pause contract in emergencies
|
|
153
|
+
- **Event Logging**: Comprehensive audit trail for all operations
|
|
154
|
+
|
|
155
|
+
### Frontend Security
|
|
156
|
+
- **Local Key Storage**: Private keys stored in browser (use secure storage in production)
|
|
157
|
+
- **Input Validation**: All user inputs validated before processing
|
|
158
|
+
- **Error Handling**: Comprehensive error handling and user feedback
|
|
159
|
+
- **HTTPS Required**: All operations require secure connections
|
|
160
|
+
|
|
161
|
+
## 🌐 Network Support
|
|
162
|
+
|
|
163
|
+
### Mainnet
|
|
164
|
+
- Ethereum Mainnet
|
|
165
|
+
- Token addresses for USDC, USDT, DAI
|
|
166
|
+
|
|
167
|
+
### Testnet
|
|
168
|
+
- Sepolia Testnet
|
|
169
|
+
- Test token addresses
|
|
170
|
+
- Faucet integration for testing
|
|
171
|
+
|
|
172
|
+
## 📊 API Reference
|
|
173
|
+
|
|
174
|
+
### WebAssembly Functions
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
// Generate ElGamal keypair
|
|
178
|
+
const keypair = await generate_keypair();
|
|
179
|
+
|
|
180
|
+
// Encrypt amount with public key
|
|
181
|
+
const encrypted = await encrypt_amount(amount, pubkey);
|
|
182
|
+
|
|
183
|
+
// Generate transfer proof
|
|
184
|
+
const proof = await generate_transfer_proof({
|
|
185
|
+
current_balance_ciphertext,
|
|
186
|
+
current_balance,
|
|
187
|
+
transfer_amount,
|
|
188
|
+
source_keypair,
|
|
189
|
+
destination_pubkey
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Generate withdraw proof
|
|
193
|
+
const proof = await generate_withdraw_proof({
|
|
194
|
+
current_balance_ciphertext,
|
|
195
|
+
current_balance,
|
|
196
|
+
withdraw_amount,
|
|
197
|
+
keypair
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Smart Contract Functions
|
|
202
|
+
|
|
203
|
+
```solidity
|
|
204
|
+
// Create confidential account
|
|
205
|
+
function createConfidentialAccount(bytes calldata elgamalPubkey)
|
|
206
|
+
external returns (bytes32);
|
|
207
|
+
|
|
208
|
+
// Convert tokens to confidential
|
|
209
|
+
function deposit(DepositItem[] calldata items)
|
|
210
|
+
external returns (bytes32);
|
|
211
|
+
|
|
212
|
+
// Private transfer
|
|
213
|
+
function transferConfidential(address recipient, string calldata denom, bytes calldata transferProofData)
|
|
214
|
+
external returns (bytes32);
|
|
215
|
+
|
|
216
|
+
// Withdraw tokens
|
|
217
|
+
function withdraw(string calldata denom, uint256 amount, bytes calldata withdrawProofData)
|
|
218
|
+
external returns (bytes32);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 🚀 Deployment
|
|
222
|
+
|
|
223
|
+
### Frontend Deployment (Vercel)
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Install Vercel CLI
|
|
227
|
+
npm i -g vercel
|
|
228
|
+
|
|
229
|
+
# Deploy
|
|
230
|
+
cd frontend
|
|
231
|
+
vercel --prod
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Smart Contract Deployment
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Using Hardhat
|
|
238
|
+
npx hardhat run scripts/deploy.js --network mainnet
|
|
239
|
+
|
|
240
|
+
# Using Foundry
|
|
241
|
+
forge create --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> GatewayContract
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## 🔍 Testing
|
|
245
|
+
|
|
246
|
+
### Frontend Testing
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
cd frontend
|
|
250
|
+
npm test
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Smart Contract Testing
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npx hardhat test
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### WebAssembly Testing
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
cd frontend
|
|
263
|
+
npm run test:wasm
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## 📝 Development
|
|
267
|
+
|
|
268
|
+
### Adding New Features
|
|
269
|
+
|
|
270
|
+
1. **Smart Contract**: Add functions to `GatewayContract.sol`
|
|
271
|
+
2. **Cryptography**: Implement in Rust library (`src/`)
|
|
272
|
+
3. **WebAssembly**: Expose functions in `frontend/src/lib.rs`
|
|
273
|
+
4. **Frontend**: Add UI components in `app.js`
|
|
274
|
+
|
|
275
|
+
### Code Style
|
|
276
|
+
|
|
277
|
+
- **Rust**: Follow standard Rust conventions
|
|
278
|
+
- **JavaScript**: Use ES6+ features, async/await
|
|
279
|
+
- **Solidity**: Follow OpenZeppelin patterns
|
|
280
|
+
- **CSS**: Use CSS custom properties for theming
|
|
281
|
+
|
|
282
|
+
## 🤝 Contributing
|
|
283
|
+
|
|
284
|
+
1. Fork the repository
|
|
285
|
+
2. Create feature branch (`git checkout -b feature/amazing-feature`)
|
|
286
|
+
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
|
287
|
+
4. Push to branch (`git push origin feature/amazing-feature`)
|
|
288
|
+
5. Open Pull Request
|
|
289
|
+
|
|
290
|
+
## 📄 License
|
|
291
|
+
|
|
292
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
293
|
+
|
|
294
|
+
## 🆘 Support
|
|
295
|
+
|
|
296
|
+
- **Issues**: Report bugs and feature requests on GitHub Issues
|
|
297
|
+
- **Discussions**: Join community discussions on GitHub Discussions
|
|
298
|
+
- **Documentation**: Check the [USAGE.md](frontend/USAGE.md) for detailed usage guide
|
|
299
|
+
|
|
300
|
+
## 🔮 Roadmap
|
|
301
|
+
|
|
302
|
+
- [ ] Multi-asset support
|
|
303
|
+
- [ ] Batch operations
|
|
304
|
+
- [ ] Mobile app
|
|
305
|
+
- [ ] Advanced privacy features
|
|
306
|
+
- [ ] Integration with other DeFi protocols
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
**Built with ❤️ for privacy and decentralization**
|