@veil-cash/sdk 0.4.0 → 0.6.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 +130 -464
- package/SDK.md +365 -0
- package/dist/cli/index.cjs +2260 -955
- package/dist/index.cjs +561 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +486 -1
- package/dist/index.d.ts +486 -1
- package/dist/index.js +544 -27
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/skills/veil/SKILL.md +526 -0
- package/skills/veil/reference.md +231 -0
- package/src/abi.ts +172 -0
- package/src/addresses.ts +20 -2
- package/src/balance.ts +4 -4
- package/src/cli/commands/balance.ts +126 -38
- package/src/cli/commands/deposit.ts +136 -63
- package/src/cli/commands/init.ts +95 -72
- package/src/cli/commands/keypair.ts +34 -16
- package/src/cli/commands/private-balance.ts +37 -35
- package/src/cli/commands/queue-balance.ts +48 -36
- package/src/cli/commands/register.ts +67 -53
- package/src/cli/commands/status.ts +196 -70
- package/src/cli/commands/subaccount.ts +354 -0
- package/src/cli/commands/transfer.ts +62 -53
- package/src/cli/commands/withdraw.ts +32 -30
- package/src/cli/config.ts +85 -5
- package/src/cli/errors.ts +8 -0
- package/src/cli/index.ts +27 -5
- package/src/cli/output.ts +87 -0
- package/src/cli/wallet.ts +75 -16
- package/src/index.ts +41 -1
- package/src/prover.ts +3 -0
- package/src/relay.ts +36 -24
- package/src/subaccount.ts +476 -0
- package/src/types.ts +134 -0
package/dist/index.cjs
CHANGED
|
@@ -375,9 +375,11 @@ var ADDRESSES = {
|
|
|
375
375
|
usdcPool: "0x5c50d58E49C59d112680c187De2Bf989d2a91242",
|
|
376
376
|
usdcQueue: "0x5530241b24504bF05C9a22e95A1F5458888e6a9B",
|
|
377
377
|
usdcToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
378
|
+
forwarderFactory: "0x2848Fd62293A1ff3b4a897E9FcD0e5962dcc8101",
|
|
378
379
|
chainId: 8453,
|
|
379
380
|
relayUrl: "https://veil-relay.up.railway.app"
|
|
380
381
|
};
|
|
382
|
+
var FORWARDER_CONTRACT_VERSION = "1";
|
|
381
383
|
var POOL_CONFIG = {
|
|
382
384
|
eth: {
|
|
383
385
|
decimals: 18,
|
|
@@ -417,6 +419,9 @@ function getQueueAddress(pool) {
|
|
|
417
419
|
throw new Error(`Unknown pool: ${pool}`);
|
|
418
420
|
}
|
|
419
421
|
}
|
|
422
|
+
function getForwarderFactoryAddress() {
|
|
423
|
+
return getAddresses().forwarderFactory;
|
|
424
|
+
}
|
|
420
425
|
function getRelayUrl() {
|
|
421
426
|
return ADDRESSES.relayUrl;
|
|
422
427
|
}
|
|
@@ -1037,6 +1042,170 @@ var ERC20_ABI = [
|
|
|
1037
1042
|
type: "function"
|
|
1038
1043
|
}
|
|
1039
1044
|
];
|
|
1045
|
+
var FORWARDER_FACTORY_ABI = [
|
|
1046
|
+
{
|
|
1047
|
+
inputs: [],
|
|
1048
|
+
name: "CONTRACT_VERSION",
|
|
1049
|
+
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
1050
|
+
stateMutability: "view",
|
|
1051
|
+
type: "function"
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
inputs: [
|
|
1055
|
+
{ internalType: "bytes32", name: "_salt", type: "bytes32" },
|
|
1056
|
+
{ internalType: "bytes", name: "_childDepositKey", type: "bytes" },
|
|
1057
|
+
{ internalType: "address", name: "_owner", type: "address" }
|
|
1058
|
+
],
|
|
1059
|
+
name: "computeAddress",
|
|
1060
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1061
|
+
stateMutability: "view",
|
|
1062
|
+
type: "function"
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
inputs: [
|
|
1066
|
+
{ internalType: "bytes32", name: "_salt", type: "bytes32" },
|
|
1067
|
+
{ internalType: "bytes", name: "_childDepositKey", type: "bytes" },
|
|
1068
|
+
{ internalType: "address", name: "_owner", type: "address" }
|
|
1069
|
+
],
|
|
1070
|
+
name: "deploy",
|
|
1071
|
+
outputs: [{ internalType: "address", name: "forwarder", type: "address" }],
|
|
1072
|
+
stateMutability: "nonpayable",
|
|
1073
|
+
type: "function"
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
inputs: [],
|
|
1077
|
+
name: "relayer",
|
|
1078
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1079
|
+
stateMutability: "view",
|
|
1080
|
+
type: "function"
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
inputs: [],
|
|
1084
|
+
name: "veilEntry",
|
|
1085
|
+
outputs: [{ internalType: "address payable", name: "", type: "address" }],
|
|
1086
|
+
stateMutability: "view",
|
|
1087
|
+
type: "function"
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
inputs: [],
|
|
1091
|
+
name: "usdc",
|
|
1092
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1093
|
+
stateMutability: "view",
|
|
1094
|
+
type: "function"
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
inputs: [],
|
|
1098
|
+
name: "owner",
|
|
1099
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1100
|
+
stateMutability: "view",
|
|
1101
|
+
type: "function"
|
|
1102
|
+
}
|
|
1103
|
+
];
|
|
1104
|
+
var FORWARDER_ABI = [
|
|
1105
|
+
{
|
|
1106
|
+
inputs: [],
|
|
1107
|
+
name: "CONTRACT_VERSION",
|
|
1108
|
+
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
1109
|
+
stateMutability: "view",
|
|
1110
|
+
type: "function"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
inputs: [
|
|
1114
|
+
{ internalType: "address", name: "_token", type: "address" },
|
|
1115
|
+
{ internalType: "address", name: "_to", type: "address" },
|
|
1116
|
+
{ internalType: "uint256", name: "_amount", type: "uint256" },
|
|
1117
|
+
{ internalType: "uint256", name: "_nonce", type: "uint256" },
|
|
1118
|
+
{ internalType: "uint256", name: "_deadline", type: "uint256" },
|
|
1119
|
+
{ internalType: "bytes", name: "_signature", type: "bytes" }
|
|
1120
|
+
],
|
|
1121
|
+
name: "withdraw",
|
|
1122
|
+
outputs: [],
|
|
1123
|
+
stateMutability: "nonpayable",
|
|
1124
|
+
type: "function"
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
1128
|
+
name: "usedNonces",
|
|
1129
|
+
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
1130
|
+
stateMutability: "view",
|
|
1131
|
+
type: "function"
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
inputs: [],
|
|
1135
|
+
name: "owner",
|
|
1136
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1137
|
+
stateMutability: "view",
|
|
1138
|
+
type: "function"
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
inputs: [],
|
|
1142
|
+
name: "factory",
|
|
1143
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1144
|
+
stateMutability: "view",
|
|
1145
|
+
type: "function"
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
inputs: [],
|
|
1149
|
+
name: "childDepositKey",
|
|
1150
|
+
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1151
|
+
stateMutability: "view",
|
|
1152
|
+
type: "function"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
inputs: [],
|
|
1156
|
+
name: "entry",
|
|
1157
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1158
|
+
stateMutability: "view",
|
|
1159
|
+
type: "function"
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
inputs: [],
|
|
1163
|
+
name: "usdc",
|
|
1164
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1165
|
+
stateMutability: "view",
|
|
1166
|
+
type: "function"
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
inputs: [],
|
|
1170
|
+
name: "sweepETH",
|
|
1171
|
+
outputs: [],
|
|
1172
|
+
stateMutability: "nonpayable",
|
|
1173
|
+
type: "function"
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
inputs: [],
|
|
1177
|
+
name: "sweepUSDC",
|
|
1178
|
+
outputs: [],
|
|
1179
|
+
stateMutability: "nonpayable",
|
|
1180
|
+
type: "function"
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
inputs: [],
|
|
1184
|
+
name: "eip712Domain",
|
|
1185
|
+
outputs: [
|
|
1186
|
+
{ internalType: "bytes1", name: "fields", type: "bytes1" },
|
|
1187
|
+
{ internalType: "string", name: "name", type: "string" },
|
|
1188
|
+
{ internalType: "string", name: "version", type: "string" },
|
|
1189
|
+
{ internalType: "uint256", name: "chainId", type: "uint256" },
|
|
1190
|
+
{ internalType: "address", name: "verifyingContract", type: "address" },
|
|
1191
|
+
{ internalType: "bytes32", name: "salt", type: "bytes32" },
|
|
1192
|
+
{ internalType: "uint256[]", name: "extensions", type: "uint256[]" }
|
|
1193
|
+
],
|
|
1194
|
+
stateMutability: "view",
|
|
1195
|
+
type: "function"
|
|
1196
|
+
},
|
|
1197
|
+
{ type: "error", name: "ZeroAddress", inputs: [] },
|
|
1198
|
+
{ type: "error", name: "ZeroAmount", inputs: [] },
|
|
1199
|
+
{ type: "error", name: "InvalidDepositKey", inputs: [] },
|
|
1200
|
+
{ type: "error", name: "NotRelayer", inputs: [] },
|
|
1201
|
+
{ type: "error", name: "NoETHBalance", inputs: [] },
|
|
1202
|
+
{ type: "error", name: "NoTokenBalance", inputs: [] },
|
|
1203
|
+
{ type: "error", name: "TokenApproveFailed", inputs: [] },
|
|
1204
|
+
{ type: "error", name: "ETHTransferFailed", inputs: [] },
|
|
1205
|
+
{ type: "error", name: "NonceUsed", inputs: [] },
|
|
1206
|
+
{ type: "error", name: "Unauthorized", inputs: [] },
|
|
1207
|
+
{ type: "error", name: "DeadlineExpired", inputs: [] }
|
|
1208
|
+
];
|
|
1040
1209
|
|
|
1041
1210
|
// src/deposit.ts
|
|
1042
1211
|
function buildRegisterTx(depositKey, ownerAddress) {
|
|
@@ -1152,12 +1321,12 @@ async function getQueueBalance(options) {
|
|
|
1152
1321
|
args: [nonce]
|
|
1153
1322
|
});
|
|
1154
1323
|
if (deposit.fallbackReceiver.toLowerCase() === address.toLowerCase()) {
|
|
1155
|
-
totalQueueBalance += deposit.
|
|
1324
|
+
totalQueueBalance += deposit.shieldAmount;
|
|
1156
1325
|
pendingDeposits.push({
|
|
1157
1326
|
nonce: nonce.toString(),
|
|
1158
1327
|
status: DEPOSIT_STATUS_MAP[deposit.status] || "pending",
|
|
1159
|
-
amount: viem.formatUnits(deposit.
|
|
1160
|
-
amountWei: deposit.
|
|
1328
|
+
amount: viem.formatUnits(deposit.shieldAmount, poolConfig.decimals),
|
|
1329
|
+
amountWei: deposit.shieldAmount.toString(),
|
|
1161
1330
|
timestamp: new Date(Number(deposit.timestamp) * 1e3).toISOString()
|
|
1162
1331
|
});
|
|
1163
1332
|
}
|
|
@@ -1335,7 +1504,10 @@ async function prove(input, circuitName) {
|
|
|
1335
1504
|
const result = await snarkjs.groth16.fullProve(
|
|
1336
1505
|
utils.stringifyBigInts(input),
|
|
1337
1506
|
wasmPath,
|
|
1338
|
-
zkeyPath
|
|
1507
|
+
zkeyPath,
|
|
1508
|
+
void 0,
|
|
1509
|
+
void 0,
|
|
1510
|
+
{ singleThread: true }
|
|
1339
1511
|
);
|
|
1340
1512
|
const proof = result.proof;
|
|
1341
1513
|
return "0x" + toFixedHex(proof.pi_a[0]).slice(2) + toFixedHex(proof.pi_a[1]).slice(2) + toFixedHex(proof.pi_b[0][1]).slice(2) + toFixedHex(proof.pi_b[0][0]).slice(2) + toFixedHex(proof.pi_b[1][1]).slice(2) + toFixedHex(proof.pi_b[1][0]).slice(2) + toFixedHex(proof.pi_c[0]).slice(2) + toFixedHex(proof.pi_c[1]).slice(2);
|
|
@@ -1488,6 +1660,27 @@ var RelayError = class extends Error {
|
|
|
1488
1660
|
this.network = network;
|
|
1489
1661
|
}
|
|
1490
1662
|
};
|
|
1663
|
+
async function postRelayJson(endpoint, body, relayUrl) {
|
|
1664
|
+
const url = relayUrl || getRelayUrl();
|
|
1665
|
+
const response = await fetch(`${url}${endpoint}`, {
|
|
1666
|
+
method: "POST",
|
|
1667
|
+
headers: {
|
|
1668
|
+
"Content-Type": "application/json"
|
|
1669
|
+
},
|
|
1670
|
+
body: JSON.stringify(body)
|
|
1671
|
+
});
|
|
1672
|
+
const data = await response.json();
|
|
1673
|
+
if (!response.ok) {
|
|
1674
|
+
const errorData = data;
|
|
1675
|
+
throw new RelayError(
|
|
1676
|
+
errorData.error || errorData.message || "Relay request failed",
|
|
1677
|
+
response.status,
|
|
1678
|
+
errorData.retryAfter,
|
|
1679
|
+
errorData.network
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1682
|
+
return data;
|
|
1683
|
+
}
|
|
1491
1684
|
async function submitRelay(options) {
|
|
1492
1685
|
const {
|
|
1493
1686
|
type,
|
|
@@ -1507,30 +1700,17 @@ async function submitRelay(options) {
|
|
|
1507
1700
|
throw new RelayError("Missing proofArgs or extData", 400);
|
|
1508
1701
|
}
|
|
1509
1702
|
const relayUrl = customRelayUrl || getRelayUrl();
|
|
1510
|
-
const endpoint =
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
"Content-Type": "application/json"
|
|
1515
|
-
},
|
|
1516
|
-
body: JSON.stringify({
|
|
1703
|
+
const endpoint = `/relay/${pool}`;
|
|
1704
|
+
return postRelayJson(
|
|
1705
|
+
endpoint,
|
|
1706
|
+
{
|
|
1517
1707
|
type,
|
|
1518
1708
|
proofArgs,
|
|
1519
1709
|
extData,
|
|
1520
1710
|
metadata
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
if (!response.ok) {
|
|
1525
|
-
const errorData = data;
|
|
1526
|
-
throw new RelayError(
|
|
1527
|
-
errorData.error || errorData.message || "Relay request failed",
|
|
1528
|
-
response.status,
|
|
1529
|
-
errorData.retryAfter,
|
|
1530
|
-
errorData.network
|
|
1531
|
-
);
|
|
1532
|
-
}
|
|
1533
|
-
return data;
|
|
1711
|
+
},
|
|
1712
|
+
relayUrl
|
|
1713
|
+
);
|
|
1534
1714
|
}
|
|
1535
1715
|
async function checkRelayHealth(relayUrl) {
|
|
1536
1716
|
const url = relayUrl || getRelayUrl();
|
|
@@ -1985,13 +2165,354 @@ async function mergeUtxos(options) {
|
|
|
1985
2165
|
recipient: "self"
|
|
1986
2166
|
};
|
|
1987
2167
|
}
|
|
2168
|
+
var SUBACCOUNT_CHILD_DOMAIN = "veil-sua-child";
|
|
2169
|
+
var SUBACCOUNT_SALT_DOMAIN = "veil-sua-salt";
|
|
2170
|
+
var ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
2171
|
+
var DEFAULT_WITHDRAW_DEADLINE_SECONDS = 3600n;
|
|
2172
|
+
var DEFAULT_MAX_NONCE_SCAN = 100n;
|
|
2173
|
+
var MAX_SUBACCOUNT_SLOTS = 3;
|
|
2174
|
+
function createBaseClient(rpcUrl) {
|
|
2175
|
+
return viem.createPublicClient({
|
|
2176
|
+
chain: chains.base,
|
|
2177
|
+
transport: viem.http(rpcUrl)
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
function assertPrivateKey(value, label) {
|
|
2181
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(value)) {
|
|
2182
|
+
throw new Error(`${label} must be a 0x-prefixed 32-byte hex string`);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function normalizeSlot(slot) {
|
|
2186
|
+
if (!Number.isInteger(slot) || slot < 0) {
|
|
2187
|
+
throw new Error("slot must be a non-negative integer");
|
|
2188
|
+
}
|
|
2189
|
+
if (slot >= MAX_SUBACCOUNT_SLOTS) {
|
|
2190
|
+
throw new Error(`slot must be less than ${MAX_SUBACCOUNT_SLOTS} (supported slots: 0-${MAX_SUBACCOUNT_SLOTS - 1})`);
|
|
2191
|
+
}
|
|
2192
|
+
return slot;
|
|
2193
|
+
}
|
|
2194
|
+
function normalizeAsset(asset) {
|
|
2195
|
+
if (asset !== "eth" && asset !== "usdc") {
|
|
2196
|
+
throw new Error('asset must be "eth" or "usdc"');
|
|
2197
|
+
}
|
|
2198
|
+
return asset;
|
|
2199
|
+
}
|
|
2200
|
+
function normalizeNonce(value) {
|
|
2201
|
+
const nonce = typeof value === "bigint" ? value : BigInt(value);
|
|
2202
|
+
if (nonce < 0n) {
|
|
2203
|
+
throw new Error("nonce must be non-negative");
|
|
2204
|
+
}
|
|
2205
|
+
return nonce;
|
|
2206
|
+
}
|
|
2207
|
+
function normalizeDeadline(deadline) {
|
|
2208
|
+
const nextDeadline = deadline === void 0 ? BigInt(Math.floor(Date.now() / 1e3)) + DEFAULT_WITHDRAW_DEADLINE_SECONDS : typeof deadline === "bigint" ? deadline : BigInt(deadline);
|
|
2209
|
+
if (nextDeadline <= 0n) {
|
|
2210
|
+
throw new Error("deadline must be greater than 0");
|
|
2211
|
+
}
|
|
2212
|
+
return nextDeadline;
|
|
2213
|
+
}
|
|
2214
|
+
function deriveSubaccountChildPrivateKey(rootPrivateKey, slot) {
|
|
2215
|
+
assertPrivateKey(rootPrivateKey, "rootPrivateKey");
|
|
2216
|
+
const normalizedSlot = normalizeSlot(slot);
|
|
2217
|
+
return viem.keccak256(
|
|
2218
|
+
viem.encodePacked(
|
|
2219
|
+
["bytes32", "string", "uint256"],
|
|
2220
|
+
[rootPrivateKey, SUBACCOUNT_CHILD_DOMAIN, BigInt(normalizedSlot)]
|
|
2221
|
+
)
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
function deriveSubaccountSalt(rootPrivateKey, slot) {
|
|
2225
|
+
assertPrivateKey(rootPrivateKey, "rootPrivateKey");
|
|
2226
|
+
const normalizedSlot = normalizeSlot(slot);
|
|
2227
|
+
return viem.keccak256(
|
|
2228
|
+
viem.encodePacked(
|
|
2229
|
+
["bytes32", "string", "uint256"],
|
|
2230
|
+
[rootPrivateKey, SUBACCOUNT_SALT_DOMAIN, BigInt(normalizedSlot)]
|
|
2231
|
+
)
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
function deriveSubaccountChildOwner(childPrivateKey) {
|
|
2235
|
+
assertPrivateKey(childPrivateKey, "childPrivateKey");
|
|
2236
|
+
return accounts.privateKeyToAddress(childPrivateKey);
|
|
2237
|
+
}
|
|
2238
|
+
function deriveSubaccountChildDepositKey(childPrivateKey) {
|
|
2239
|
+
assertPrivateKey(childPrivateKey, "childPrivateKey");
|
|
2240
|
+
return new Keypair(childPrivateKey).depositKey();
|
|
2241
|
+
}
|
|
2242
|
+
async function predictSubaccountForwarder(options) {
|
|
2243
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2244
|
+
return publicClient.readContract({
|
|
2245
|
+
abi: FORWARDER_FACTORY_ABI,
|
|
2246
|
+
address: getForwarderFactoryAddress(),
|
|
2247
|
+
functionName: "computeAddress",
|
|
2248
|
+
args: [options.salt, options.childDepositKey, options.childOwner]
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
async function deriveSubaccountSlot(options) {
|
|
2252
|
+
const normalizedSlot = normalizeSlot(options.slot);
|
|
2253
|
+
const childPrivateKey = deriveSubaccountChildPrivateKey(options.rootPrivateKey, normalizedSlot);
|
|
2254
|
+
const salt = deriveSubaccountSalt(options.rootPrivateKey, normalizedSlot);
|
|
2255
|
+
const childOwner = deriveSubaccountChildOwner(childPrivateKey);
|
|
2256
|
+
const childDepositKey = deriveSubaccountChildDepositKey(childPrivateKey);
|
|
2257
|
+
const forwarderAddress = await predictSubaccountForwarder({
|
|
2258
|
+
salt,
|
|
2259
|
+
childDepositKey,
|
|
2260
|
+
childOwner,
|
|
2261
|
+
rpcUrl: options.rpcUrl
|
|
2262
|
+
});
|
|
2263
|
+
return {
|
|
2264
|
+
slot: normalizedSlot,
|
|
2265
|
+
childOwner,
|
|
2266
|
+
childDepositKey,
|
|
2267
|
+
salt,
|
|
2268
|
+
forwarderAddress
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
async function isSubaccountForwarderDeployed(options) {
|
|
2272
|
+
if (!viem.isAddress(options.forwarderAddress)) {
|
|
2273
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2274
|
+
}
|
|
2275
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2276
|
+
const code = await publicClient.getCode({ address: options.forwarderAddress });
|
|
2277
|
+
return !!code && code !== "0x";
|
|
2278
|
+
}
|
|
2279
|
+
async function deploySubaccountForwarder(options) {
|
|
2280
|
+
const slot = await deriveSubaccountSlot({
|
|
2281
|
+
rootPrivateKey: options.rootPrivateKey,
|
|
2282
|
+
slot: options.slot,
|
|
2283
|
+
rpcUrl: options.rpcUrl
|
|
2284
|
+
});
|
|
2285
|
+
return postRelayJson(
|
|
2286
|
+
"/stealth/deploy",
|
|
2287
|
+
{
|
|
2288
|
+
salt: slot.salt,
|
|
2289
|
+
childDepositKey: slot.childDepositKey,
|
|
2290
|
+
childOwner: slot.childOwner,
|
|
2291
|
+
expectedForwarder: slot.forwarderAddress
|
|
2292
|
+
},
|
|
2293
|
+
options.relayUrl
|
|
2294
|
+
);
|
|
2295
|
+
}
|
|
2296
|
+
async function sweepSubaccountForwarder(options) {
|
|
2297
|
+
const asset = normalizeAsset(options.asset);
|
|
2298
|
+
if (!viem.isAddress(options.forwarderAddress)) {
|
|
2299
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2300
|
+
}
|
|
2301
|
+
return postRelayJson(
|
|
2302
|
+
"/stealth/sweep",
|
|
2303
|
+
{
|
|
2304
|
+
forwarder: options.forwarderAddress,
|
|
2305
|
+
asset
|
|
2306
|
+
},
|
|
2307
|
+
options.relayUrl
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
function toQueueStatus(asset, result) {
|
|
2311
|
+
return {
|
|
2312
|
+
asset,
|
|
2313
|
+
queueBalance: result.queueBalance,
|
|
2314
|
+
queueBalanceWei: result.queueBalanceWei,
|
|
2315
|
+
pendingCount: result.pendingCount,
|
|
2316
|
+
pendingDeposits: result.pendingDeposits
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
async function getSubaccountStatus(options) {
|
|
2320
|
+
const slot = await deriveSubaccountSlot(options);
|
|
2321
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2322
|
+
const addresses = getAddresses();
|
|
2323
|
+
const [deployed, ethWei, usdcWei, ethQueue, usdcQueue] = await Promise.all([
|
|
2324
|
+
isSubaccountForwarderDeployed({
|
|
2325
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2326
|
+
rpcUrl: options.rpcUrl
|
|
2327
|
+
}),
|
|
2328
|
+
publicClient.getBalance({ address: slot.forwarderAddress }),
|
|
2329
|
+
publicClient.readContract({
|
|
2330
|
+
address: addresses.usdcToken,
|
|
2331
|
+
abi: ERC20_ABI,
|
|
2332
|
+
functionName: "balanceOf",
|
|
2333
|
+
args: [slot.forwarderAddress]
|
|
2334
|
+
}),
|
|
2335
|
+
getQueueBalance({
|
|
2336
|
+
address: slot.forwarderAddress,
|
|
2337
|
+
pool: "eth",
|
|
2338
|
+
rpcUrl: options.rpcUrl
|
|
2339
|
+
}),
|
|
2340
|
+
getQueueBalance({
|
|
2341
|
+
address: slot.forwarderAddress,
|
|
2342
|
+
pool: "usdc",
|
|
2343
|
+
rpcUrl: options.rpcUrl
|
|
2344
|
+
})
|
|
2345
|
+
]);
|
|
2346
|
+
return {
|
|
2347
|
+
slot,
|
|
2348
|
+
deployed,
|
|
2349
|
+
balances: {
|
|
2350
|
+
eth: {
|
|
2351
|
+
balance: viem.formatEther(ethWei),
|
|
2352
|
+
balanceWei: ethWei.toString()
|
|
2353
|
+
},
|
|
2354
|
+
usdc: {
|
|
2355
|
+
balance: viem.formatUnits(usdcWei, 6),
|
|
2356
|
+
balanceWei: usdcWei.toString()
|
|
2357
|
+
}
|
|
2358
|
+
},
|
|
2359
|
+
queues: {
|
|
2360
|
+
eth: toQueueStatus("eth", ethQueue),
|
|
2361
|
+
usdc: toQueueStatus("usdc", usdcQueue)
|
|
2362
|
+
}
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
var WITHDRAW_TYPES = {
|
|
2366
|
+
Withdraw: [
|
|
2367
|
+
{ name: "token", type: "address" },
|
|
2368
|
+
{ name: "to", type: "address" },
|
|
2369
|
+
{ name: "amount", type: "uint256" },
|
|
2370
|
+
{ name: "nonce", type: "uint256" },
|
|
2371
|
+
{ name: "deadline", type: "uint256" }
|
|
2372
|
+
]
|
|
2373
|
+
};
|
|
2374
|
+
function buildSubaccountWithdrawTypedData(options) {
|
|
2375
|
+
if (!viem.isAddress(options.forwarderAddress)) {
|
|
2376
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2377
|
+
}
|
|
2378
|
+
if (!viem.isAddress(options.token)) {
|
|
2379
|
+
throw new Error("token must be a valid Ethereum address");
|
|
2380
|
+
}
|
|
2381
|
+
if (!viem.isAddress(options.to)) {
|
|
2382
|
+
throw new Error("to must be a valid Ethereum address");
|
|
2383
|
+
}
|
|
2384
|
+
return {
|
|
2385
|
+
domain: {
|
|
2386
|
+
name: "VeilForwarder",
|
|
2387
|
+
version: FORWARDER_CONTRACT_VERSION,
|
|
2388
|
+
chainId: getAddresses().chainId,
|
|
2389
|
+
verifyingContract: options.forwarderAddress
|
|
2390
|
+
},
|
|
2391
|
+
types: WITHDRAW_TYPES,
|
|
2392
|
+
primaryType: "Withdraw",
|
|
2393
|
+
message: {
|
|
2394
|
+
token: options.token,
|
|
2395
|
+
to: options.to,
|
|
2396
|
+
amount: options.amount,
|
|
2397
|
+
nonce: options.nonce,
|
|
2398
|
+
deadline: options.deadline
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
}
|
|
2402
|
+
async function signSubaccountWithdraw(options) {
|
|
2403
|
+
assertPrivateKey(options.childPrivateKey, "childPrivateKey");
|
|
2404
|
+
const account = accounts.privateKeyToAccount(options.childPrivateKey);
|
|
2405
|
+
return account.signTypedData({
|
|
2406
|
+
domain: options.typedData.domain,
|
|
2407
|
+
types: options.typedData.types,
|
|
2408
|
+
primaryType: options.typedData.primaryType,
|
|
2409
|
+
message: options.typedData.message
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
2412
|
+
async function isSubaccountWithdrawNonceUsed(options) {
|
|
2413
|
+
if (!viem.isAddress(options.forwarderAddress)) {
|
|
2414
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2415
|
+
}
|
|
2416
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2417
|
+
try {
|
|
2418
|
+
return await publicClient.readContract({
|
|
2419
|
+
abi: FORWARDER_ABI,
|
|
2420
|
+
address: options.forwarderAddress,
|
|
2421
|
+
functionName: "usedNonces",
|
|
2422
|
+
args: [normalizeNonce(options.nonce)]
|
|
2423
|
+
});
|
|
2424
|
+
} catch (error) {
|
|
2425
|
+
if (String(error).includes("returned no data")) {
|
|
2426
|
+
throw new Error("Subaccount forwarder is not deployed");
|
|
2427
|
+
}
|
|
2428
|
+
throw error;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
async function findNextSubaccountWithdrawNonce(options) {
|
|
2432
|
+
const startNonce = normalizeNonce(options.startNonce ?? 0n);
|
|
2433
|
+
const maxScan = normalizeNonce(options.maxScan ?? DEFAULT_MAX_NONCE_SCAN);
|
|
2434
|
+
const limit = startNonce + maxScan;
|
|
2435
|
+
let nonce = startNonce;
|
|
2436
|
+
while (await isSubaccountWithdrawNonceUsed({
|
|
2437
|
+
forwarderAddress: options.forwarderAddress,
|
|
2438
|
+
nonce,
|
|
2439
|
+
rpcUrl: options.rpcUrl
|
|
2440
|
+
})) {
|
|
2441
|
+
nonce += 1n;
|
|
2442
|
+
if (nonce > limit) {
|
|
2443
|
+
throw new Error("Unable to find an unused withdraw nonce within the scan limit");
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
return nonce;
|
|
2447
|
+
}
|
|
2448
|
+
async function buildSubaccountRecoveryTx(options) {
|
|
2449
|
+
if (!viem.isAddress(options.to)) {
|
|
2450
|
+
throw new Error("to must be a valid Ethereum address");
|
|
2451
|
+
}
|
|
2452
|
+
const asset = normalizeAsset(options.asset);
|
|
2453
|
+
const slot = await deriveSubaccountSlot({
|
|
2454
|
+
rootPrivateKey: options.rootPrivateKey,
|
|
2455
|
+
slot: options.slot,
|
|
2456
|
+
rpcUrl: options.rpcUrl
|
|
2457
|
+
});
|
|
2458
|
+
const deployed = await isSubaccountForwarderDeployed({
|
|
2459
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2460
|
+
rpcUrl: options.rpcUrl
|
|
2461
|
+
});
|
|
2462
|
+
if (!deployed) {
|
|
2463
|
+
throw new Error("Subaccount forwarder is not deployed");
|
|
2464
|
+
}
|
|
2465
|
+
const childPrivateKey = deriveSubaccountChildPrivateKey(options.rootPrivateKey, options.slot);
|
|
2466
|
+
const tokenAddress = asset === "eth" ? ETH_ADDRESS : getAddresses().usdcToken;
|
|
2467
|
+
const amountWei = asset === "eth" ? viem.parseEther(options.amount) : viem.parseUnits(options.amount, 6);
|
|
2468
|
+
const nonce = options.nonce === void 0 ? await findNextSubaccountWithdrawNonce({
|
|
2469
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2470
|
+
rpcUrl: options.rpcUrl
|
|
2471
|
+
}) : normalizeNonce(options.nonce);
|
|
2472
|
+
const deadline = normalizeDeadline(options.deadline);
|
|
2473
|
+
const typedData = buildSubaccountWithdrawTypedData({
|
|
2474
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2475
|
+
token: tokenAddress,
|
|
2476
|
+
to: options.to,
|
|
2477
|
+
amount: amountWei,
|
|
2478
|
+
nonce,
|
|
2479
|
+
deadline
|
|
2480
|
+
});
|
|
2481
|
+
const signature = await signSubaccountWithdraw({
|
|
2482
|
+
childPrivateKey,
|
|
2483
|
+
typedData
|
|
2484
|
+
});
|
|
2485
|
+
return {
|
|
2486
|
+
transaction: {
|
|
2487
|
+
to: slot.forwarderAddress,
|
|
2488
|
+
data: viem.encodeFunctionData({
|
|
2489
|
+
abi: FORWARDER_ABI,
|
|
2490
|
+
functionName: "withdraw",
|
|
2491
|
+
args: [tokenAddress, options.to, amountWei, nonce, deadline, signature]
|
|
2492
|
+
})
|
|
2493
|
+
},
|
|
2494
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2495
|
+
asset,
|
|
2496
|
+
amount: options.amount,
|
|
2497
|
+
amountWei: amountWei.toString(),
|
|
2498
|
+
nonce: nonce.toString(),
|
|
2499
|
+
deadline: deadline.toString(),
|
|
2500
|
+
recipient: options.to,
|
|
2501
|
+
tokenAddress,
|
|
2502
|
+
signature
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
1988
2505
|
|
|
1989
2506
|
exports.ADDRESSES = ADDRESSES;
|
|
1990
2507
|
exports.CIRCUIT_CONFIG = CIRCUIT_CONFIG;
|
|
1991
2508
|
exports.ENTRY_ABI = ENTRY_ABI;
|
|
1992
2509
|
exports.ERC20_ABI = ERC20_ABI;
|
|
1993
2510
|
exports.FIELD_SIZE = FIELD_SIZE;
|
|
2511
|
+
exports.FORWARDER_ABI = FORWARDER_ABI;
|
|
2512
|
+
exports.FORWARDER_CONTRACT_VERSION = FORWARDER_CONTRACT_VERSION;
|
|
2513
|
+
exports.FORWARDER_FACTORY_ABI = FORWARDER_FACTORY_ABI;
|
|
1994
2514
|
exports.Keypair = Keypair;
|
|
2515
|
+
exports.MAX_SUBACCOUNT_SLOTS = MAX_SUBACCOUNT_SLOTS;
|
|
1995
2516
|
exports.MERKLE_TREE_HEIGHT = MERKLE_TREE_HEIGHT;
|
|
1996
2517
|
exports.POOL_ABI = POOL_ABI;
|
|
1997
2518
|
exports.POOL_CONFIG = POOL_CONFIG;
|
|
@@ -2006,12 +2527,22 @@ exports.buildDepositTx = buildDepositTx;
|
|
|
2006
2527
|
exports.buildDepositUSDCTx = buildDepositUSDCTx;
|
|
2007
2528
|
exports.buildMerkleTree = buildMerkleTree;
|
|
2008
2529
|
exports.buildRegisterTx = buildRegisterTx;
|
|
2530
|
+
exports.buildSubaccountRecoveryTx = buildSubaccountRecoveryTx;
|
|
2531
|
+
exports.buildSubaccountWithdrawTypedData = buildSubaccountWithdrawTypedData;
|
|
2009
2532
|
exports.buildTransferProof = buildTransferProof;
|
|
2010
2533
|
exports.buildWithdrawProof = buildWithdrawProof;
|
|
2011
2534
|
exports.checkRecipientRegistration = checkRecipientRegistration;
|
|
2012
2535
|
exports.checkRelayHealth = checkRelayHealth;
|
|
2536
|
+
exports.deploySubaccountForwarder = deploySubaccountForwarder;
|
|
2537
|
+
exports.deriveSubaccountChildDepositKey = deriveSubaccountChildDepositKey;
|
|
2538
|
+
exports.deriveSubaccountChildOwner = deriveSubaccountChildOwner;
|
|
2539
|
+
exports.deriveSubaccountChildPrivateKey = deriveSubaccountChildPrivateKey;
|
|
2540
|
+
exports.deriveSubaccountSalt = deriveSubaccountSalt;
|
|
2541
|
+
exports.deriveSubaccountSlot = deriveSubaccountSlot;
|
|
2542
|
+
exports.findNextSubaccountWithdrawNonce = findNextSubaccountWithdrawNonce;
|
|
2013
2543
|
exports.getAddresses = getAddresses;
|
|
2014
2544
|
exports.getExtDataHash = getExtDataHash;
|
|
2545
|
+
exports.getForwarderFactoryAddress = getForwarderFactoryAddress;
|
|
2015
2546
|
exports.getMerklePath = getMerklePath;
|
|
2016
2547
|
exports.getPoolAddress = getPoolAddress;
|
|
2017
2548
|
exports.getPrivateBalance = getPrivateBalance;
|
|
@@ -2019,17 +2550,23 @@ exports.getQueueAddress = getQueueAddress;
|
|
|
2019
2550
|
exports.getQueueBalance = getQueueBalance;
|
|
2020
2551
|
exports.getRelayInfo = getRelayInfo;
|
|
2021
2552
|
exports.getRelayUrl = getRelayUrl;
|
|
2553
|
+
exports.getSubaccountStatus = getSubaccountStatus;
|
|
2554
|
+
exports.isSubaccountForwarderDeployed = isSubaccountForwarderDeployed;
|
|
2555
|
+
exports.isSubaccountWithdrawNonceUsed = isSubaccountWithdrawNonceUsed;
|
|
2022
2556
|
exports.mergeUtxos = mergeUtxos;
|
|
2023
2557
|
exports.packEncryptedMessage = packEncryptedMessage;
|
|
2024
2558
|
exports.poseidonHash = poseidonHash;
|
|
2025
2559
|
exports.poseidonHash2 = poseidonHash2;
|
|
2560
|
+
exports.predictSubaccountForwarder = predictSubaccountForwarder;
|
|
2026
2561
|
exports.prepareTransaction = prepareTransaction;
|
|
2027
2562
|
exports.prove = prove;
|
|
2028
2563
|
exports.randomBN = randomBN;
|
|
2029
2564
|
exports.selectCircuit = selectCircuit;
|
|
2030
2565
|
exports.selectUtxosForWithdraw = selectUtxosForWithdraw;
|
|
2031
2566
|
exports.shuffle = shuffle;
|
|
2567
|
+
exports.signSubaccountWithdraw = signSubaccountWithdraw;
|
|
2032
2568
|
exports.submitRelay = submitRelay;
|
|
2569
|
+
exports.sweepSubaccountForwarder = sweepSubaccountForwarder;
|
|
2033
2570
|
exports.toBuffer = toBuffer;
|
|
2034
2571
|
exports.toFixedHex = toFixedHex;
|
|
2035
2572
|
exports.transfer = transfer;
|