@veil-cash/sdk 0.5.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 +31 -2
- 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/src/abi.ts +172 -0
- package/src/addresses.ts +14 -0
- package/src/cli/commands/subaccount.ts +354 -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 +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) {
|
|
@@ -1491,6 +1660,27 @@ var RelayError = class extends Error {
|
|
|
1491
1660
|
this.network = network;
|
|
1492
1661
|
}
|
|
1493
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
|
+
}
|
|
1494
1684
|
async function submitRelay(options) {
|
|
1495
1685
|
const {
|
|
1496
1686
|
type,
|
|
@@ -1510,30 +1700,17 @@ async function submitRelay(options) {
|
|
|
1510
1700
|
throw new RelayError("Missing proofArgs or extData", 400);
|
|
1511
1701
|
}
|
|
1512
1702
|
const relayUrl = customRelayUrl || getRelayUrl();
|
|
1513
|
-
const endpoint =
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
"Content-Type": "application/json"
|
|
1518
|
-
},
|
|
1519
|
-
body: JSON.stringify({
|
|
1703
|
+
const endpoint = `/relay/${pool}`;
|
|
1704
|
+
return postRelayJson(
|
|
1705
|
+
endpoint,
|
|
1706
|
+
{
|
|
1520
1707
|
type,
|
|
1521
1708
|
proofArgs,
|
|
1522
1709
|
extData,
|
|
1523
1710
|
metadata
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
if (!response.ok) {
|
|
1528
|
-
const errorData = data;
|
|
1529
|
-
throw new RelayError(
|
|
1530
|
-
errorData.error || errorData.message || "Relay request failed",
|
|
1531
|
-
response.status,
|
|
1532
|
-
errorData.retryAfter,
|
|
1533
|
-
errorData.network
|
|
1534
|
-
);
|
|
1535
|
-
}
|
|
1536
|
-
return data;
|
|
1711
|
+
},
|
|
1712
|
+
relayUrl
|
|
1713
|
+
);
|
|
1537
1714
|
}
|
|
1538
1715
|
async function checkRelayHealth(relayUrl) {
|
|
1539
1716
|
const url = relayUrl || getRelayUrl();
|
|
@@ -1988,13 +2165,354 @@ async function mergeUtxos(options) {
|
|
|
1988
2165
|
recipient: "self"
|
|
1989
2166
|
};
|
|
1990
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
|
+
}
|
|
1991
2505
|
|
|
1992
2506
|
exports.ADDRESSES = ADDRESSES;
|
|
1993
2507
|
exports.CIRCUIT_CONFIG = CIRCUIT_CONFIG;
|
|
1994
2508
|
exports.ENTRY_ABI = ENTRY_ABI;
|
|
1995
2509
|
exports.ERC20_ABI = ERC20_ABI;
|
|
1996
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;
|
|
1997
2514
|
exports.Keypair = Keypair;
|
|
2515
|
+
exports.MAX_SUBACCOUNT_SLOTS = MAX_SUBACCOUNT_SLOTS;
|
|
1998
2516
|
exports.MERKLE_TREE_HEIGHT = MERKLE_TREE_HEIGHT;
|
|
1999
2517
|
exports.POOL_ABI = POOL_ABI;
|
|
2000
2518
|
exports.POOL_CONFIG = POOL_CONFIG;
|
|
@@ -2009,12 +2527,22 @@ exports.buildDepositTx = buildDepositTx;
|
|
|
2009
2527
|
exports.buildDepositUSDCTx = buildDepositUSDCTx;
|
|
2010
2528
|
exports.buildMerkleTree = buildMerkleTree;
|
|
2011
2529
|
exports.buildRegisterTx = buildRegisterTx;
|
|
2530
|
+
exports.buildSubaccountRecoveryTx = buildSubaccountRecoveryTx;
|
|
2531
|
+
exports.buildSubaccountWithdrawTypedData = buildSubaccountWithdrawTypedData;
|
|
2012
2532
|
exports.buildTransferProof = buildTransferProof;
|
|
2013
2533
|
exports.buildWithdrawProof = buildWithdrawProof;
|
|
2014
2534
|
exports.checkRecipientRegistration = checkRecipientRegistration;
|
|
2015
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;
|
|
2016
2543
|
exports.getAddresses = getAddresses;
|
|
2017
2544
|
exports.getExtDataHash = getExtDataHash;
|
|
2545
|
+
exports.getForwarderFactoryAddress = getForwarderFactoryAddress;
|
|
2018
2546
|
exports.getMerklePath = getMerklePath;
|
|
2019
2547
|
exports.getPoolAddress = getPoolAddress;
|
|
2020
2548
|
exports.getPrivateBalance = getPrivateBalance;
|
|
@@ -2022,17 +2550,23 @@ exports.getQueueAddress = getQueueAddress;
|
|
|
2022
2550
|
exports.getQueueBalance = getQueueBalance;
|
|
2023
2551
|
exports.getRelayInfo = getRelayInfo;
|
|
2024
2552
|
exports.getRelayUrl = getRelayUrl;
|
|
2553
|
+
exports.getSubaccountStatus = getSubaccountStatus;
|
|
2554
|
+
exports.isSubaccountForwarderDeployed = isSubaccountForwarderDeployed;
|
|
2555
|
+
exports.isSubaccountWithdrawNonceUsed = isSubaccountWithdrawNonceUsed;
|
|
2025
2556
|
exports.mergeUtxos = mergeUtxos;
|
|
2026
2557
|
exports.packEncryptedMessage = packEncryptedMessage;
|
|
2027
2558
|
exports.poseidonHash = poseidonHash;
|
|
2028
2559
|
exports.poseidonHash2 = poseidonHash2;
|
|
2560
|
+
exports.predictSubaccountForwarder = predictSubaccountForwarder;
|
|
2029
2561
|
exports.prepareTransaction = prepareTransaction;
|
|
2030
2562
|
exports.prove = prove;
|
|
2031
2563
|
exports.randomBN = randomBN;
|
|
2032
2564
|
exports.selectCircuit = selectCircuit;
|
|
2033
2565
|
exports.selectUtxosForWithdraw = selectUtxosForWithdraw;
|
|
2034
2566
|
exports.shuffle = shuffle;
|
|
2567
|
+
exports.signSubaccountWithdraw = signSubaccountWithdraw;
|
|
2035
2568
|
exports.submitRelay = submitRelay;
|
|
2569
|
+
exports.sweepSubaccountForwarder = sweepSubaccountForwarder;
|
|
2036
2570
|
exports.toBuffer = toBuffer;
|
|
2037
2571
|
exports.toFixedHex = toFixedHex;
|
|
2038
2572
|
exports.transfer = transfer;
|