@veil-cash/sdk 0.5.0 → 0.6.1
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 +75 -37
- package/SDK.md +55 -1
- package/dist/cli/index.cjs +806 -22
- package/dist/index.cjs +554 -20
- 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 +537 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/veil/SKILL.md +74 -2
- package/skills/veil/reference.md +71 -2
- package/src/abi.ts +172 -0
- package/src/addresses.ts +14 -0
- package/src/cli/commands/subaccount.ts +355 -0
- package/src/cli/errors.ts +4 -0
- package/src/cli/index.ts +5 -1
- package/src/cli/wallet.ts +2 -2
- package/src/index.ts +35 -0
- package/src/relay.ts +45 -24
- package/src/subaccount.ts +481 -0
- package/src/types.ts +134 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ethers } from 'ethers';
|
|
2
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
2
|
+
import { privateKeyToAccount, privateKeyToAddress } from 'viem/accounts';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
|
-
import { encodeFunctionData, parseEther, parseUnits, createPublicClient, http, formatUnits } from 'viem';
|
|
4
|
+
import { encodeFunctionData, parseEther, parseUnits, createPublicClient, http, formatUnits, keccak256, encodePacked, isAddress, formatEther } from 'viem';
|
|
5
5
|
import { base } from 'viem/chains';
|
|
6
6
|
import MerkleTree from 'fixed-merkle-tree-legacy';
|
|
7
7
|
import { groth16 } from 'snarkjs';
|
|
@@ -347,9 +347,11 @@ var ADDRESSES = {
|
|
|
347
347
|
usdcPool: "0x5c50d58E49C59d112680c187De2Bf989d2a91242",
|
|
348
348
|
usdcQueue: "0x5530241b24504bF05C9a22e95A1F5458888e6a9B",
|
|
349
349
|
usdcToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
350
|
+
forwarderFactory: "0x2848Fd62293A1ff3b4a897E9FcD0e5962dcc8101",
|
|
350
351
|
chainId: 8453,
|
|
351
352
|
relayUrl: "https://veil-relay.up.railway.app"
|
|
352
353
|
};
|
|
354
|
+
var FORWARDER_CONTRACT_VERSION = "1";
|
|
353
355
|
var POOL_CONFIG = {
|
|
354
356
|
eth: {
|
|
355
357
|
decimals: 18,
|
|
@@ -389,6 +391,9 @@ function getQueueAddress(pool) {
|
|
|
389
391
|
throw new Error(`Unknown pool: ${pool}`);
|
|
390
392
|
}
|
|
391
393
|
}
|
|
394
|
+
function getForwarderFactoryAddress() {
|
|
395
|
+
return getAddresses().forwarderFactory;
|
|
396
|
+
}
|
|
392
397
|
function getRelayUrl() {
|
|
393
398
|
return ADDRESSES.relayUrl;
|
|
394
399
|
}
|
|
@@ -1009,6 +1014,170 @@ var ERC20_ABI = [
|
|
|
1009
1014
|
type: "function"
|
|
1010
1015
|
}
|
|
1011
1016
|
];
|
|
1017
|
+
var FORWARDER_FACTORY_ABI = [
|
|
1018
|
+
{
|
|
1019
|
+
inputs: [],
|
|
1020
|
+
name: "CONTRACT_VERSION",
|
|
1021
|
+
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
1022
|
+
stateMutability: "view",
|
|
1023
|
+
type: "function"
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
inputs: [
|
|
1027
|
+
{ internalType: "bytes32", name: "_salt", type: "bytes32" },
|
|
1028
|
+
{ internalType: "bytes", name: "_childDepositKey", type: "bytes" },
|
|
1029
|
+
{ internalType: "address", name: "_owner", type: "address" }
|
|
1030
|
+
],
|
|
1031
|
+
name: "computeAddress",
|
|
1032
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1033
|
+
stateMutability: "view",
|
|
1034
|
+
type: "function"
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
inputs: [
|
|
1038
|
+
{ internalType: "bytes32", name: "_salt", type: "bytes32" },
|
|
1039
|
+
{ internalType: "bytes", name: "_childDepositKey", type: "bytes" },
|
|
1040
|
+
{ internalType: "address", name: "_owner", type: "address" }
|
|
1041
|
+
],
|
|
1042
|
+
name: "deploy",
|
|
1043
|
+
outputs: [{ internalType: "address", name: "forwarder", type: "address" }],
|
|
1044
|
+
stateMutability: "nonpayable",
|
|
1045
|
+
type: "function"
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
inputs: [],
|
|
1049
|
+
name: "relayer",
|
|
1050
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1051
|
+
stateMutability: "view",
|
|
1052
|
+
type: "function"
|
|
1053
|
+
},
|
|
1054
|
+
{
|
|
1055
|
+
inputs: [],
|
|
1056
|
+
name: "veilEntry",
|
|
1057
|
+
outputs: [{ internalType: "address payable", name: "", type: "address" }],
|
|
1058
|
+
stateMutability: "view",
|
|
1059
|
+
type: "function"
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
inputs: [],
|
|
1063
|
+
name: "usdc",
|
|
1064
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1065
|
+
stateMutability: "view",
|
|
1066
|
+
type: "function"
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
inputs: [],
|
|
1070
|
+
name: "owner",
|
|
1071
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1072
|
+
stateMutability: "view",
|
|
1073
|
+
type: "function"
|
|
1074
|
+
}
|
|
1075
|
+
];
|
|
1076
|
+
var FORWARDER_ABI = [
|
|
1077
|
+
{
|
|
1078
|
+
inputs: [],
|
|
1079
|
+
name: "CONTRACT_VERSION",
|
|
1080
|
+
outputs: [{ internalType: "string", name: "", type: "string" }],
|
|
1081
|
+
stateMutability: "view",
|
|
1082
|
+
type: "function"
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
inputs: [
|
|
1086
|
+
{ internalType: "address", name: "_token", type: "address" },
|
|
1087
|
+
{ internalType: "address", name: "_to", type: "address" },
|
|
1088
|
+
{ internalType: "uint256", name: "_amount", type: "uint256" },
|
|
1089
|
+
{ internalType: "uint256", name: "_nonce", type: "uint256" },
|
|
1090
|
+
{ internalType: "uint256", name: "_deadline", type: "uint256" },
|
|
1091
|
+
{ internalType: "bytes", name: "_signature", type: "bytes" }
|
|
1092
|
+
],
|
|
1093
|
+
name: "withdraw",
|
|
1094
|
+
outputs: [],
|
|
1095
|
+
stateMutability: "nonpayable",
|
|
1096
|
+
type: "function"
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
|
|
1100
|
+
name: "usedNonces",
|
|
1101
|
+
outputs: [{ internalType: "bool", name: "", type: "bool" }],
|
|
1102
|
+
stateMutability: "view",
|
|
1103
|
+
type: "function"
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
inputs: [],
|
|
1107
|
+
name: "owner",
|
|
1108
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1109
|
+
stateMutability: "view",
|
|
1110
|
+
type: "function"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
inputs: [],
|
|
1114
|
+
name: "factory",
|
|
1115
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1116
|
+
stateMutability: "view",
|
|
1117
|
+
type: "function"
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
inputs: [],
|
|
1121
|
+
name: "childDepositKey",
|
|
1122
|
+
outputs: [{ internalType: "bytes", name: "", type: "bytes" }],
|
|
1123
|
+
stateMutability: "view",
|
|
1124
|
+
type: "function"
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
inputs: [],
|
|
1128
|
+
name: "entry",
|
|
1129
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1130
|
+
stateMutability: "view",
|
|
1131
|
+
type: "function"
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
inputs: [],
|
|
1135
|
+
name: "usdc",
|
|
1136
|
+
outputs: [{ internalType: "address", name: "", type: "address" }],
|
|
1137
|
+
stateMutability: "view",
|
|
1138
|
+
type: "function"
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
inputs: [],
|
|
1142
|
+
name: "sweepETH",
|
|
1143
|
+
outputs: [],
|
|
1144
|
+
stateMutability: "nonpayable",
|
|
1145
|
+
type: "function"
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
inputs: [],
|
|
1149
|
+
name: "sweepUSDC",
|
|
1150
|
+
outputs: [],
|
|
1151
|
+
stateMutability: "nonpayable",
|
|
1152
|
+
type: "function"
|
|
1153
|
+
},
|
|
1154
|
+
{
|
|
1155
|
+
inputs: [],
|
|
1156
|
+
name: "eip712Domain",
|
|
1157
|
+
outputs: [
|
|
1158
|
+
{ internalType: "bytes1", name: "fields", type: "bytes1" },
|
|
1159
|
+
{ internalType: "string", name: "name", type: "string" },
|
|
1160
|
+
{ internalType: "string", name: "version", type: "string" },
|
|
1161
|
+
{ internalType: "uint256", name: "chainId", type: "uint256" },
|
|
1162
|
+
{ internalType: "address", name: "verifyingContract", type: "address" },
|
|
1163
|
+
{ internalType: "bytes32", name: "salt", type: "bytes32" },
|
|
1164
|
+
{ internalType: "uint256[]", name: "extensions", type: "uint256[]" }
|
|
1165
|
+
],
|
|
1166
|
+
stateMutability: "view",
|
|
1167
|
+
type: "function"
|
|
1168
|
+
},
|
|
1169
|
+
{ type: "error", name: "ZeroAddress", inputs: [] },
|
|
1170
|
+
{ type: "error", name: "ZeroAmount", inputs: [] },
|
|
1171
|
+
{ type: "error", name: "InvalidDepositKey", inputs: [] },
|
|
1172
|
+
{ type: "error", name: "NotRelayer", inputs: [] },
|
|
1173
|
+
{ type: "error", name: "NoETHBalance", inputs: [] },
|
|
1174
|
+
{ type: "error", name: "NoTokenBalance", inputs: [] },
|
|
1175
|
+
{ type: "error", name: "TokenApproveFailed", inputs: [] },
|
|
1176
|
+
{ type: "error", name: "ETHTransferFailed", inputs: [] },
|
|
1177
|
+
{ type: "error", name: "NonceUsed", inputs: [] },
|
|
1178
|
+
{ type: "error", name: "Unauthorized", inputs: [] },
|
|
1179
|
+
{ type: "error", name: "DeadlineExpired", inputs: [] }
|
|
1180
|
+
];
|
|
1012
1181
|
|
|
1013
1182
|
// src/deposit.ts
|
|
1014
1183
|
function buildRegisterTx(depositKey, ownerAddress) {
|
|
@@ -1463,6 +1632,27 @@ var RelayError = class extends Error {
|
|
|
1463
1632
|
this.network = network;
|
|
1464
1633
|
}
|
|
1465
1634
|
};
|
|
1635
|
+
async function postRelayJson(endpoint, body, relayUrl) {
|
|
1636
|
+
const url = relayUrl || getRelayUrl();
|
|
1637
|
+
const response = await fetch(`${url}${endpoint}`, {
|
|
1638
|
+
method: "POST",
|
|
1639
|
+
headers: {
|
|
1640
|
+
"Content-Type": "application/json"
|
|
1641
|
+
},
|
|
1642
|
+
body: JSON.stringify(body)
|
|
1643
|
+
});
|
|
1644
|
+
const data = await response.json();
|
|
1645
|
+
if (!response.ok) {
|
|
1646
|
+
const errorData = data;
|
|
1647
|
+
throw new RelayError(
|
|
1648
|
+
errorData.error || errorData.message || "Relay request failed",
|
|
1649
|
+
response.status,
|
|
1650
|
+
errorData.retryAfter,
|
|
1651
|
+
errorData.network
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
return data;
|
|
1655
|
+
}
|
|
1466
1656
|
async function submitRelay(options) {
|
|
1467
1657
|
const {
|
|
1468
1658
|
type,
|
|
@@ -1482,30 +1672,17 @@ async function submitRelay(options) {
|
|
|
1482
1672
|
throw new RelayError("Missing proofArgs or extData", 400);
|
|
1483
1673
|
}
|
|
1484
1674
|
const relayUrl = customRelayUrl || getRelayUrl();
|
|
1485
|
-
const endpoint =
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
"Content-Type": "application/json"
|
|
1490
|
-
},
|
|
1491
|
-
body: JSON.stringify({
|
|
1675
|
+
const endpoint = `/relay/${pool}`;
|
|
1676
|
+
return postRelayJson(
|
|
1677
|
+
endpoint,
|
|
1678
|
+
{
|
|
1492
1679
|
type,
|
|
1493
1680
|
proofArgs,
|
|
1494
1681
|
extData,
|
|
1495
1682
|
metadata
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
if (!response.ok) {
|
|
1500
|
-
const errorData = data;
|
|
1501
|
-
throw new RelayError(
|
|
1502
|
-
errorData.error || errorData.message || "Relay request failed",
|
|
1503
|
-
response.status,
|
|
1504
|
-
errorData.retryAfter,
|
|
1505
|
-
errorData.network
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
return data;
|
|
1683
|
+
},
|
|
1684
|
+
relayUrl
|
|
1685
|
+
);
|
|
1509
1686
|
}
|
|
1510
1687
|
async function checkRelayHealth(relayUrl) {
|
|
1511
1688
|
const url = relayUrl || getRelayUrl();
|
|
@@ -1960,7 +2137,344 @@ async function mergeUtxos(options) {
|
|
|
1960
2137
|
recipient: "self"
|
|
1961
2138
|
};
|
|
1962
2139
|
}
|
|
2140
|
+
var SUBACCOUNT_CHILD_DOMAIN = "veil-sua-child";
|
|
2141
|
+
var SUBACCOUNT_SALT_DOMAIN = "veil-sua-salt";
|
|
2142
|
+
var ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
2143
|
+
var DEFAULT_WITHDRAW_DEADLINE_SECONDS = 3600n;
|
|
2144
|
+
var DEFAULT_MAX_NONCE_SCAN = 100n;
|
|
2145
|
+
var MAX_SUBACCOUNT_SLOTS = 3;
|
|
2146
|
+
function createBaseClient(rpcUrl) {
|
|
2147
|
+
return createPublicClient({
|
|
2148
|
+
chain: base,
|
|
2149
|
+
transport: http(rpcUrl)
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
function assertPrivateKey(value, label) {
|
|
2153
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(value)) {
|
|
2154
|
+
throw new Error(`${label} must be a 0x-prefixed 32-byte hex string`);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
function normalizeSlot(slot) {
|
|
2158
|
+
if (!Number.isInteger(slot) || slot < 0) {
|
|
2159
|
+
throw new Error("slot must be a non-negative integer");
|
|
2160
|
+
}
|
|
2161
|
+
if (slot >= MAX_SUBACCOUNT_SLOTS) {
|
|
2162
|
+
throw new Error(`slot must be less than ${MAX_SUBACCOUNT_SLOTS} (supported slots: 0-${MAX_SUBACCOUNT_SLOTS - 1})`);
|
|
2163
|
+
}
|
|
2164
|
+
return slot;
|
|
2165
|
+
}
|
|
2166
|
+
function normalizeAsset(asset) {
|
|
2167
|
+
if (asset !== "eth" && asset !== "usdc") {
|
|
2168
|
+
throw new Error('asset must be "eth" or "usdc"');
|
|
2169
|
+
}
|
|
2170
|
+
return asset;
|
|
2171
|
+
}
|
|
2172
|
+
function normalizeNonce(value) {
|
|
2173
|
+
const nonce = typeof value === "bigint" ? value : BigInt(value);
|
|
2174
|
+
if (nonce < 0n) {
|
|
2175
|
+
throw new Error("nonce must be non-negative");
|
|
2176
|
+
}
|
|
2177
|
+
return nonce;
|
|
2178
|
+
}
|
|
2179
|
+
function normalizeDeadline(deadline) {
|
|
2180
|
+
const nextDeadline = deadline === void 0 ? BigInt(Math.floor(Date.now() / 1e3)) + DEFAULT_WITHDRAW_DEADLINE_SECONDS : typeof deadline === "bigint" ? deadline : BigInt(deadline);
|
|
2181
|
+
if (nextDeadline <= 0n) {
|
|
2182
|
+
throw new Error("deadline must be greater than 0");
|
|
2183
|
+
}
|
|
2184
|
+
return nextDeadline;
|
|
2185
|
+
}
|
|
2186
|
+
function deriveSubaccountChildPrivateKey(rootPrivateKey, slot) {
|
|
2187
|
+
assertPrivateKey(rootPrivateKey, "rootPrivateKey");
|
|
2188
|
+
const normalizedSlot = normalizeSlot(slot);
|
|
2189
|
+
return keccak256(
|
|
2190
|
+
encodePacked(
|
|
2191
|
+
["bytes32", "string", "uint256"],
|
|
2192
|
+
[rootPrivateKey, SUBACCOUNT_CHILD_DOMAIN, BigInt(normalizedSlot)]
|
|
2193
|
+
)
|
|
2194
|
+
);
|
|
2195
|
+
}
|
|
2196
|
+
function deriveSubaccountSalt(rootPrivateKey, slot) {
|
|
2197
|
+
assertPrivateKey(rootPrivateKey, "rootPrivateKey");
|
|
2198
|
+
const normalizedSlot = normalizeSlot(slot);
|
|
2199
|
+
return keccak256(
|
|
2200
|
+
encodePacked(
|
|
2201
|
+
["bytes32", "string", "uint256"],
|
|
2202
|
+
[rootPrivateKey, SUBACCOUNT_SALT_DOMAIN, BigInt(normalizedSlot)]
|
|
2203
|
+
)
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
function deriveSubaccountChildOwner(childPrivateKey) {
|
|
2207
|
+
assertPrivateKey(childPrivateKey, "childPrivateKey");
|
|
2208
|
+
return privateKeyToAddress(childPrivateKey);
|
|
2209
|
+
}
|
|
2210
|
+
function deriveSubaccountChildDepositKey(childPrivateKey) {
|
|
2211
|
+
assertPrivateKey(childPrivateKey, "childPrivateKey");
|
|
2212
|
+
return new Keypair(childPrivateKey).depositKey();
|
|
2213
|
+
}
|
|
2214
|
+
async function predictSubaccountForwarder(options) {
|
|
2215
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2216
|
+
return publicClient.readContract({
|
|
2217
|
+
abi: FORWARDER_FACTORY_ABI,
|
|
2218
|
+
address: getForwarderFactoryAddress(),
|
|
2219
|
+
functionName: "computeAddress",
|
|
2220
|
+
args: [options.salt, options.childDepositKey, options.childOwner]
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2223
|
+
async function deriveSubaccountSlot(options) {
|
|
2224
|
+
const normalizedSlot = normalizeSlot(options.slot);
|
|
2225
|
+
const childPrivateKey = deriveSubaccountChildPrivateKey(options.rootPrivateKey, normalizedSlot);
|
|
2226
|
+
const salt = deriveSubaccountSalt(options.rootPrivateKey, normalizedSlot);
|
|
2227
|
+
const childOwner = deriveSubaccountChildOwner(childPrivateKey);
|
|
2228
|
+
const childDepositKey = deriveSubaccountChildDepositKey(childPrivateKey);
|
|
2229
|
+
const forwarderAddress = await predictSubaccountForwarder({
|
|
2230
|
+
salt,
|
|
2231
|
+
childDepositKey,
|
|
2232
|
+
childOwner,
|
|
2233
|
+
rpcUrl: options.rpcUrl
|
|
2234
|
+
});
|
|
2235
|
+
return {
|
|
2236
|
+
slot: normalizedSlot,
|
|
2237
|
+
childOwner,
|
|
2238
|
+
childDepositKey,
|
|
2239
|
+
salt,
|
|
2240
|
+
forwarderAddress
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
async function isSubaccountForwarderDeployed(options) {
|
|
2244
|
+
if (!isAddress(options.forwarderAddress)) {
|
|
2245
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2246
|
+
}
|
|
2247
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2248
|
+
const code = await publicClient.getCode({ address: options.forwarderAddress });
|
|
2249
|
+
return !!code && code !== "0x";
|
|
2250
|
+
}
|
|
2251
|
+
async function deploySubaccountForwarder(options) {
|
|
2252
|
+
const slot = await deriveSubaccountSlot({
|
|
2253
|
+
rootPrivateKey: options.rootPrivateKey,
|
|
2254
|
+
slot: options.slot,
|
|
2255
|
+
rpcUrl: options.rpcUrl
|
|
2256
|
+
});
|
|
2257
|
+
return postRelayJson(
|
|
2258
|
+
"/stealth/deploy",
|
|
2259
|
+
{
|
|
2260
|
+
salt: slot.salt,
|
|
2261
|
+
childDepositKey: slot.childDepositKey,
|
|
2262
|
+
childOwner: slot.childOwner,
|
|
2263
|
+
expectedForwarder: slot.forwarderAddress
|
|
2264
|
+
},
|
|
2265
|
+
options.relayUrl
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
async function sweepSubaccountForwarder(options) {
|
|
2269
|
+
const asset = normalizeAsset(options.asset);
|
|
2270
|
+
if (!isAddress(options.forwarderAddress)) {
|
|
2271
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2272
|
+
}
|
|
2273
|
+
return postRelayJson(
|
|
2274
|
+
"/stealth/sweep",
|
|
2275
|
+
{
|
|
2276
|
+
forwarder: options.forwarderAddress,
|
|
2277
|
+
asset
|
|
2278
|
+
},
|
|
2279
|
+
options.relayUrl
|
|
2280
|
+
);
|
|
2281
|
+
}
|
|
2282
|
+
function toQueueStatus(asset, result) {
|
|
2283
|
+
return {
|
|
2284
|
+
asset,
|
|
2285
|
+
queueBalance: result.queueBalance,
|
|
2286
|
+
queueBalanceWei: result.queueBalanceWei,
|
|
2287
|
+
pendingCount: result.pendingCount,
|
|
2288
|
+
pendingDeposits: result.pendingDeposits
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
async function getSubaccountStatus(options) {
|
|
2292
|
+
const slot = await deriveSubaccountSlot(options);
|
|
2293
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2294
|
+
const addresses = getAddresses();
|
|
2295
|
+
const [deployed, ethWei, usdcWei, ethQueue, usdcQueue] = await Promise.all([
|
|
2296
|
+
isSubaccountForwarderDeployed({
|
|
2297
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2298
|
+
rpcUrl: options.rpcUrl
|
|
2299
|
+
}),
|
|
2300
|
+
publicClient.getBalance({ address: slot.forwarderAddress }),
|
|
2301
|
+
publicClient.readContract({
|
|
2302
|
+
address: addresses.usdcToken,
|
|
2303
|
+
abi: ERC20_ABI,
|
|
2304
|
+
functionName: "balanceOf",
|
|
2305
|
+
args: [slot.forwarderAddress]
|
|
2306
|
+
}),
|
|
2307
|
+
getQueueBalance({
|
|
2308
|
+
address: slot.forwarderAddress,
|
|
2309
|
+
pool: "eth",
|
|
2310
|
+
rpcUrl: options.rpcUrl
|
|
2311
|
+
}),
|
|
2312
|
+
getQueueBalance({
|
|
2313
|
+
address: slot.forwarderAddress,
|
|
2314
|
+
pool: "usdc",
|
|
2315
|
+
rpcUrl: options.rpcUrl
|
|
2316
|
+
})
|
|
2317
|
+
]);
|
|
2318
|
+
return {
|
|
2319
|
+
slot,
|
|
2320
|
+
deployed,
|
|
2321
|
+
balances: {
|
|
2322
|
+
eth: {
|
|
2323
|
+
balance: formatEther(ethWei),
|
|
2324
|
+
balanceWei: ethWei.toString()
|
|
2325
|
+
},
|
|
2326
|
+
usdc: {
|
|
2327
|
+
balance: formatUnits(usdcWei, 6),
|
|
2328
|
+
balanceWei: usdcWei.toString()
|
|
2329
|
+
}
|
|
2330
|
+
},
|
|
2331
|
+
queues: {
|
|
2332
|
+
eth: toQueueStatus("eth", ethQueue),
|
|
2333
|
+
usdc: toQueueStatus("usdc", usdcQueue)
|
|
2334
|
+
}
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
var WITHDRAW_TYPES = {
|
|
2338
|
+
Withdraw: [
|
|
2339
|
+
{ name: "token", type: "address" },
|
|
2340
|
+
{ name: "to", type: "address" },
|
|
2341
|
+
{ name: "amount", type: "uint256" },
|
|
2342
|
+
{ name: "nonce", type: "uint256" },
|
|
2343
|
+
{ name: "deadline", type: "uint256" }
|
|
2344
|
+
]
|
|
2345
|
+
};
|
|
2346
|
+
function buildSubaccountWithdrawTypedData(options) {
|
|
2347
|
+
if (!isAddress(options.forwarderAddress)) {
|
|
2348
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2349
|
+
}
|
|
2350
|
+
if (!isAddress(options.token)) {
|
|
2351
|
+
throw new Error("token must be a valid Ethereum address");
|
|
2352
|
+
}
|
|
2353
|
+
if (!isAddress(options.to)) {
|
|
2354
|
+
throw new Error("to must be a valid Ethereum address");
|
|
2355
|
+
}
|
|
2356
|
+
return {
|
|
2357
|
+
domain: {
|
|
2358
|
+
name: "VeilForwarder",
|
|
2359
|
+
version: FORWARDER_CONTRACT_VERSION,
|
|
2360
|
+
chainId: getAddresses().chainId,
|
|
2361
|
+
verifyingContract: options.forwarderAddress
|
|
2362
|
+
},
|
|
2363
|
+
types: WITHDRAW_TYPES,
|
|
2364
|
+
primaryType: "Withdraw",
|
|
2365
|
+
message: {
|
|
2366
|
+
token: options.token,
|
|
2367
|
+
to: options.to,
|
|
2368
|
+
amount: options.amount,
|
|
2369
|
+
nonce: options.nonce,
|
|
2370
|
+
deadline: options.deadline
|
|
2371
|
+
}
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
async function signSubaccountWithdraw(options) {
|
|
2375
|
+
assertPrivateKey(options.childPrivateKey, "childPrivateKey");
|
|
2376
|
+
const account = privateKeyToAccount(options.childPrivateKey);
|
|
2377
|
+
return account.signTypedData({
|
|
2378
|
+
domain: options.typedData.domain,
|
|
2379
|
+
types: options.typedData.types,
|
|
2380
|
+
primaryType: options.typedData.primaryType,
|
|
2381
|
+
message: options.typedData.message
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
async function isSubaccountWithdrawNonceUsed(options) {
|
|
2385
|
+
if (!isAddress(options.forwarderAddress)) {
|
|
2386
|
+
throw new Error("forwarderAddress must be a valid Ethereum address");
|
|
2387
|
+
}
|
|
2388
|
+
const publicClient = createBaseClient(options.rpcUrl);
|
|
2389
|
+
try {
|
|
2390
|
+
return await publicClient.readContract({
|
|
2391
|
+
abi: FORWARDER_ABI,
|
|
2392
|
+
address: options.forwarderAddress,
|
|
2393
|
+
functionName: "usedNonces",
|
|
2394
|
+
args: [normalizeNonce(options.nonce)]
|
|
2395
|
+
});
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
if (String(error).includes("returned no data")) {
|
|
2398
|
+
throw new Error("Subaccount forwarder is not deployed");
|
|
2399
|
+
}
|
|
2400
|
+
throw error;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
async function findNextSubaccountWithdrawNonce(options) {
|
|
2404
|
+
const startNonce = normalizeNonce(options.startNonce ?? 0n);
|
|
2405
|
+
const maxScan = normalizeNonce(options.maxScan ?? DEFAULT_MAX_NONCE_SCAN);
|
|
2406
|
+
const limit = startNonce + maxScan;
|
|
2407
|
+
let nonce = startNonce;
|
|
2408
|
+
while (await isSubaccountWithdrawNonceUsed({
|
|
2409
|
+
forwarderAddress: options.forwarderAddress,
|
|
2410
|
+
nonce,
|
|
2411
|
+
rpcUrl: options.rpcUrl
|
|
2412
|
+
})) {
|
|
2413
|
+
nonce += 1n;
|
|
2414
|
+
if (nonce > limit) {
|
|
2415
|
+
throw new Error("Unable to find an unused withdraw nonce within the scan limit");
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
return nonce;
|
|
2419
|
+
}
|
|
2420
|
+
async function buildSubaccountRecoveryTx(options) {
|
|
2421
|
+
if (!isAddress(options.to)) {
|
|
2422
|
+
throw new Error("to must be a valid Ethereum address");
|
|
2423
|
+
}
|
|
2424
|
+
const asset = normalizeAsset(options.asset);
|
|
2425
|
+
const slot = await deriveSubaccountSlot({
|
|
2426
|
+
rootPrivateKey: options.rootPrivateKey,
|
|
2427
|
+
slot: options.slot,
|
|
2428
|
+
rpcUrl: options.rpcUrl
|
|
2429
|
+
});
|
|
2430
|
+
const deployed = await isSubaccountForwarderDeployed({
|
|
2431
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2432
|
+
rpcUrl: options.rpcUrl
|
|
2433
|
+
});
|
|
2434
|
+
if (!deployed) {
|
|
2435
|
+
throw new Error("Subaccount forwarder is not deployed");
|
|
2436
|
+
}
|
|
2437
|
+
const childPrivateKey = deriveSubaccountChildPrivateKey(options.rootPrivateKey, options.slot);
|
|
2438
|
+
const tokenAddress = asset === "eth" ? ETH_ADDRESS : getAddresses().usdcToken;
|
|
2439
|
+
const amountWei = asset === "eth" ? parseEther(options.amount) : parseUnits(options.amount, 6);
|
|
2440
|
+
const nonce = options.nonce === void 0 ? await findNextSubaccountWithdrawNonce({
|
|
2441
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2442
|
+
rpcUrl: options.rpcUrl
|
|
2443
|
+
}) : normalizeNonce(options.nonce);
|
|
2444
|
+
const deadline = normalizeDeadline(options.deadline);
|
|
2445
|
+
const typedData = buildSubaccountWithdrawTypedData({
|
|
2446
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2447
|
+
token: tokenAddress,
|
|
2448
|
+
to: options.to,
|
|
2449
|
+
amount: amountWei,
|
|
2450
|
+
nonce,
|
|
2451
|
+
deadline
|
|
2452
|
+
});
|
|
2453
|
+
const signature = await signSubaccountWithdraw({
|
|
2454
|
+
childPrivateKey,
|
|
2455
|
+
typedData
|
|
2456
|
+
});
|
|
2457
|
+
return {
|
|
2458
|
+
transaction: {
|
|
2459
|
+
to: slot.forwarderAddress,
|
|
2460
|
+
data: encodeFunctionData({
|
|
2461
|
+
abi: FORWARDER_ABI,
|
|
2462
|
+
functionName: "withdraw",
|
|
2463
|
+
args: [tokenAddress, options.to, amountWei, nonce, deadline, signature]
|
|
2464
|
+
})
|
|
2465
|
+
},
|
|
2466
|
+
forwarderAddress: slot.forwarderAddress,
|
|
2467
|
+
asset,
|
|
2468
|
+
amount: options.amount,
|
|
2469
|
+
amountWei: amountWei.toString(),
|
|
2470
|
+
nonce: nonce.toString(),
|
|
2471
|
+
deadline: deadline.toString(),
|
|
2472
|
+
recipient: options.to,
|
|
2473
|
+
tokenAddress,
|
|
2474
|
+
signature
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
1963
2477
|
|
|
1964
|
-
export { ADDRESSES, CIRCUIT_CONFIG, ENTRY_ABI, ERC20_ABI, FIELD_SIZE, Keypair, MERKLE_TREE_HEIGHT, POOL_ABI, POOL_CONFIG, QUEUE_ABI, RelayError, Utxo, VEIL_SIGNED_MESSAGE, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, getAddresses, getExtDataHash, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, submitRelay, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
|
|
2478
|
+
export { ADDRESSES, CIRCUIT_CONFIG, ENTRY_ABI, ERC20_ABI, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, POOL_ABI, POOL_CONFIG, QUEUE_ABI, RelayError, Utxo, VEIL_SIGNED_MESSAGE, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
|
|
1965
2479
|
//# sourceMappingURL=index.js.map
|
|
1966
2480
|
//# sourceMappingURL=index.js.map
|