@shuffle-protocol/sdk 0.1.0 → 0.2.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 +22 -18
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +131 -46
- package/dist/cli/config.js +5 -2
- package/dist/cli/devnet.d.ts +1 -1
- package/dist/cli/devnet.js +5 -5
- package/dist/cli/index.js +1 -1
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +19 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.js +43 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +4 -3
- package/dist/errors.js +11 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/pda.d.ts +1 -0
- package/dist/pda.js +4 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,33 +22,21 @@ npm install -g @shuffle-protocol/sdk
|
|
|
22
22
|
|
|
23
23
|
# Configure for devnet
|
|
24
24
|
solana config set --url devnet
|
|
25
|
-
|
|
26
|
-
# Get devnet SOL
|
|
27
|
-
solana airdrop 2
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
###
|
|
27
|
+
### Get Started (Devnet)
|
|
31
28
|
|
|
32
29
|
```bash
|
|
33
|
-
# Create your privacy account
|
|
30
|
+
# 1. Create your privacy account
|
|
34
31
|
shuffle init
|
|
35
32
|
|
|
36
|
-
#
|
|
37
|
-
shuffle balance
|
|
38
|
-
|
|
39
|
-
# Get test tokens (devnet)
|
|
33
|
+
# 2. Get test tokens (also airdrops 1 SOL for fees)
|
|
40
34
|
shuffle faucet 10000
|
|
41
35
|
|
|
42
|
-
# Deposit into privacy account
|
|
43
|
-
shuffle
|
|
44
|
-
|
|
45
|
-
# Withdraw from privacy account
|
|
46
|
-
shuffle withdraw USDC 500
|
|
36
|
+
# 3. Deposit into privacy account
|
|
37
|
+
shuffle shield USDC 1000
|
|
47
38
|
|
|
48
|
-
#
|
|
49
|
-
shuffle transfer <solana-address> 100
|
|
50
|
-
|
|
51
|
-
# Place encrypted order
|
|
39
|
+
# 4. Place encrypted order
|
|
52
40
|
shuffle order TSLA_USDC buy 500
|
|
53
41
|
|
|
54
42
|
# Check status
|
|
@@ -58,6 +46,22 @@ shuffle status
|
|
|
58
46
|
shuffle settle
|
|
59
47
|
```
|
|
60
48
|
|
|
49
|
+
### Other Commands
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# View encrypted balances
|
|
53
|
+
shuffle balance
|
|
54
|
+
|
|
55
|
+
# Withdraw from privacy account
|
|
56
|
+
shuffle unshield USDC 500
|
|
57
|
+
|
|
58
|
+
# Private transfer to another user
|
|
59
|
+
shuffle transfer <solana-address> 100
|
|
60
|
+
|
|
61
|
+
# Get more SOL if needed
|
|
62
|
+
shuffle airdrop 2
|
|
63
|
+
```
|
|
64
|
+
|
|
61
65
|
## 📦 SDK Usage
|
|
62
66
|
|
|
63
67
|
```typescript
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export declare function executeCommand(): Promise<void>;
|
|
|
41
41
|
*/
|
|
42
42
|
export declare function statusCommand(): Promise<void>;
|
|
43
43
|
/**
|
|
44
|
-
* shuffle faucet <amount> -
|
|
44
|
+
* shuffle faucet <amount> - Claim USDC from program faucet (also airdrops 1 SOL for fees)
|
|
45
45
|
*/
|
|
46
46
|
export declare function faucetCommand(amountStr: string): Promise<void>;
|
|
47
47
|
/**
|
package/dist/cli/commands.js
CHANGED
|
@@ -55,10 +55,35 @@ exports.airdropCommand = airdropCommand;
|
|
|
55
55
|
exports.historyCommand = historyCommand;
|
|
56
56
|
const chalk_1 = __importDefault(require("chalk"));
|
|
57
57
|
const web3_js_1 = require("@solana/web3.js");
|
|
58
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
58
59
|
const config_1 = require("./config");
|
|
59
60
|
const output_1 = require("./output");
|
|
60
61
|
const devnet_1 = require("./devnet");
|
|
62
|
+
const pda_1 = require("../pda");
|
|
61
63
|
const constants_1 = require("../constants");
|
|
64
|
+
function getErrorLogs(error) {
|
|
65
|
+
if (!error)
|
|
66
|
+
return [];
|
|
67
|
+
if (Array.isArray(error.logs))
|
|
68
|
+
return error.logs;
|
|
69
|
+
if (Array.isArray(error.transactionLogs))
|
|
70
|
+
return error.transactionLogs;
|
|
71
|
+
if (Array.isArray(error.data?.logs))
|
|
72
|
+
return error.data.logs;
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
function formatUsdc(amount) {
|
|
76
|
+
const num = Number(amount) / 1000000;
|
|
77
|
+
if (num === 0)
|
|
78
|
+
return "0.00";
|
|
79
|
+
if (num < 0.01) {
|
|
80
|
+
return num.toLocaleString("en-US", { minimumFractionDigits: 6, maximumFractionDigits: 6 });
|
|
81
|
+
}
|
|
82
|
+
if (num < 1) {
|
|
83
|
+
return num.toLocaleString("en-US", { minimumFractionDigits: 4, maximumFractionDigits: 4 });
|
|
84
|
+
}
|
|
85
|
+
return num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
86
|
+
}
|
|
62
87
|
// ============================================================================
|
|
63
88
|
// ACCOUNT COMMANDS
|
|
64
89
|
// ============================================================================
|
|
@@ -136,25 +161,31 @@ async function balanceCommand() {
|
|
|
136
161
|
}
|
|
137
162
|
try {
|
|
138
163
|
// Fetch shielded, unshielded, and estimated payout (for lazy settlement)
|
|
139
|
-
const [shielded, unshielded, estimatedPayout] = await (0, output_1.withSpinner)("Fetching balances...", async () => {
|
|
140
|
-
const shieldedBalances = await
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
164
|
+
const [shielded, unshielded, estimatedPayout, solBalance] = await (0, output_1.withSpinner)("Fetching balances...", async () => {
|
|
165
|
+
const [shieldedBalances, unshieldedBalances, payout, sol] = await Promise.all([
|
|
166
|
+
config.shuffleClient.getBalance(),
|
|
167
|
+
config.shuffleClient.getUnshieldedBalances(),
|
|
168
|
+
config.shuffleClient.estimatePayout(),
|
|
169
|
+
config.connection.getBalance(config.wallet.publicKey),
|
|
170
|
+
]);
|
|
171
|
+
return [shieldedBalances, unshieldedBalances, payout, sol];
|
|
144
172
|
}, "Balances loaded!");
|
|
145
173
|
// Pass pending payout to display it directly in the balance (cyan color)
|
|
146
174
|
const pendingPayout = estimatedPayout
|
|
147
175
|
? { amount: estimatedPayout.estimatedPayout, assetId: estimatedPayout.outputAssetId }
|
|
148
176
|
: null;
|
|
149
|
-
(0, output_1.printBalanceTable)(shielded, unshielded, pendingPayout);
|
|
177
|
+
(0, output_1.printBalanceTable)(shielded, unshielded, pendingPayout, solBalance);
|
|
150
178
|
}
|
|
151
179
|
catch (e) {
|
|
152
180
|
// If account doesn't exist, show only unshielded
|
|
153
181
|
if (e.message?.includes("Account does not exist") || e.message?.includes("not found")) {
|
|
154
182
|
try {
|
|
155
|
-
const unshielded = await
|
|
183
|
+
const [unshielded, solBalance] = await Promise.all([
|
|
184
|
+
config.shuffleClient.getUnshieldedBalances(),
|
|
185
|
+
config.connection.getBalance(config.wallet.publicKey),
|
|
186
|
+
]);
|
|
156
187
|
console.log(chalk_1.default.yellow("\n ⚠ No shielded account. Run 'shuffle init' to create one.\n"));
|
|
157
|
-
(0, output_1.printBalanceTable)({ usdc: BigInt(0), tsla: BigInt(0), spy: BigInt(0), aapl: BigInt(0) }, unshielded);
|
|
188
|
+
(0, output_1.printBalanceTable)({ usdc: BigInt(0), tsla: BigInt(0), spy: BigInt(0), aapl: BigInt(0) }, unshielded, null, solBalance);
|
|
158
189
|
}
|
|
159
190
|
catch {
|
|
160
191
|
(0, output_1.printError)("Privacy account not found. Run 'shuffle init' first.");
|
|
@@ -1058,7 +1089,7 @@ async function statusCommand() {
|
|
|
1058
1089
|
// DEVNET COMMANDS
|
|
1059
1090
|
// ============================================================================
|
|
1060
1091
|
/**
|
|
1061
|
-
* shuffle faucet <amount> -
|
|
1092
|
+
* shuffle faucet <amount> - Claim USDC from program faucet (also airdrops 1 SOL for fees)
|
|
1062
1093
|
*/
|
|
1063
1094
|
async function faucetCommand(amountStr) {
|
|
1064
1095
|
const config = (0, config_1.getConfig)();
|
|
@@ -1072,13 +1103,16 @@ async function faucetCommand(amountStr) {
|
|
|
1072
1103
|
(0, output_1.printMockWarning)();
|
|
1073
1104
|
const state = (0, devnet_1.getMockState)();
|
|
1074
1105
|
const progress = (0, output_1.createProgressSpinner)([
|
|
1106
|
+
"Requesting 1 SOL for transaction fees...",
|
|
1075
1107
|
"Connecting to USDC faucet...",
|
|
1076
|
-
`
|
|
1108
|
+
`Claiming ${amount.toLocaleString()} USDC to your wallet...`,
|
|
1077
1109
|
"Tokens received!",
|
|
1078
1110
|
]);
|
|
1079
1111
|
progress.start();
|
|
1080
1112
|
await (0, devnet_1.mockDelay)("fast");
|
|
1081
1113
|
progress.nextStep();
|
|
1114
|
+
await (0, devnet_1.mockDelay)("fast");
|
|
1115
|
+
progress.nextStep();
|
|
1082
1116
|
await (0, devnet_1.mockDelay)("medium");
|
|
1083
1117
|
progress.nextStep();
|
|
1084
1118
|
await (0, devnet_1.mockDelay)("fast");
|
|
@@ -1087,61 +1121,112 @@ async function faucetCommand(amountStr) {
|
|
|
1087
1121
|
state.balances.usdc += amountRaw;
|
|
1088
1122
|
(0, devnet_1.updateMockState)({ balances: state.balances });
|
|
1089
1123
|
}
|
|
1090
|
-
progress.succeed(`Received ${amount.toLocaleString()} USDC!`);
|
|
1124
|
+
progress.succeed(`Received 1 SOL + ${amount.toLocaleString()} USDC!`);
|
|
1091
1125
|
console.log(chalk_1.default.gray(` Token: ${devnet_1.DEVNET_CONFIG.mints.USDC.toBase58().slice(0, 20)}...`));
|
|
1092
1126
|
(0, output_1.printTxSuccess)((0, devnet_1.mockSignature)(), config.network);
|
|
1093
1127
|
return;
|
|
1094
1128
|
}
|
|
1095
|
-
// Real mode -
|
|
1129
|
+
// Real mode - first airdrop SOL, then claim USDC via program faucet
|
|
1096
1130
|
if (!config.shuffleClient) {
|
|
1097
1131
|
(0, output_1.printError)("Not connected to Shuffle protocol");
|
|
1098
1132
|
return;
|
|
1099
1133
|
}
|
|
1100
1134
|
try {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1135
|
+
// Step 1: Airdrop 1 SOL for transaction fees (only on devnet/localnet)
|
|
1136
|
+
const pubkey = config.wallet.publicKey;
|
|
1137
|
+
let currentBalance = await config.connection.getBalance(pubkey);
|
|
1138
|
+
const minBalance = 0.1 * 1000000000; // 0.1 SOL minimum
|
|
1139
|
+
const minBalanceForTx = 0.005 * 1000000000; // 0.005 SOL absolute minimum for transaction
|
|
1140
|
+
if (currentBalance < minBalance) {
|
|
1141
|
+
console.log(chalk_1.default.gray(`\n 💰 Requesting 1 SOL for transaction fees...`));
|
|
1142
|
+
try {
|
|
1143
|
+
const airdropSig = await config.connection.requestAirdrop(pubkey, 1000000000 // 1 SOL
|
|
1144
|
+
);
|
|
1145
|
+
await config.connection.confirmTransaction(airdropSig, "confirmed");
|
|
1146
|
+
console.log(chalk_1.default.green(` ✓ Received 1 SOL`));
|
|
1147
|
+
// Refresh balance after successful airdrop
|
|
1148
|
+
currentBalance = await config.connection.getBalance(pubkey);
|
|
1105
1149
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1150
|
+
catch (airdropError) {
|
|
1151
|
+
// Non-fatal: warn but continue with USDC faucet
|
|
1152
|
+
const msg = airdropError.message || "";
|
|
1153
|
+
if (msg.includes("429") || msg.includes("airdrop limit") || msg.includes("Too Many Requests")) {
|
|
1154
|
+
// Rate limited - show helpful guide
|
|
1155
|
+
console.log(chalk_1.default.yellow(`\n ⚠ Airdrop rate limit reached. Get SOL manually:\n`));
|
|
1156
|
+
console.log(chalk_1.default.cyan(` 📍 Visit: ${chalk_1.default.white.bold("https://faucet.solana.com")}`));
|
|
1157
|
+
console.log(chalk_1.default.gray(` 1. Paste your wallet address: ${chalk_1.default.white(pubkey.toBase58())}`));
|
|
1158
|
+
console.log(chalk_1.default.gray(` 2. Request ${chalk_1.default.white("0.5 SOL")} (minimum recommended)`));
|
|
1159
|
+
console.log(chalk_1.default.gray(` 3. Complete the CAPTCHA\n`));
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
console.log(chalk_1.default.yellow(` ⚠ SOL airdrop failed: ${msg}`));
|
|
1163
|
+
}
|
|
1119
1164
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
);
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
console.log(chalk_1.default.gray(`\n ✓ Sufficient SOL balance (${(currentBalance / 1000000000).toFixed(4)} SOL)`));
|
|
1168
|
+
}
|
|
1169
|
+
// Check if we have enough SOL for the transaction after airdrop attempt
|
|
1170
|
+
if (currentBalance < minBalanceForTx) {
|
|
1171
|
+
console.log();
|
|
1172
|
+
(0, output_1.printError)(`Insufficient SOL for transaction fees (${(currentBalance / 1000000000).toFixed(4)} SOL).`);
|
|
1173
|
+
console.log(chalk_1.default.yellow(`\n You need at least 0.005 SOL to claim USDC from the faucet.`));
|
|
1174
|
+
console.log(chalk_1.default.cyan(` 📍 Get SOL from: ${chalk_1.default.white.bold("https://faucet.solana.com")}`));
|
|
1175
|
+
console.log(chalk_1.default.gray(` 1. Paste your wallet: ${chalk_1.default.white(pubkey.toBase58())}`));
|
|
1176
|
+
console.log(chalk_1.default.gray(` 2. Request ${chalk_1.default.white("0.5 SOL")}`));
|
|
1177
|
+
console.log(chalk_1.default.gray(` 3. Then run: ${chalk_1.default.white("shuffle faucet " + amountStr)}\n`));
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
// Step 2: Preflight check - ensure faucet vault has enough USDC
|
|
1181
|
+
try {
|
|
1182
|
+
const programId = config.network === "localnet"
|
|
1183
|
+
? devnet_1.LOCALNET_CONFIG.programId
|
|
1184
|
+
: devnet_1.DEVNET_CONFIG.programId;
|
|
1185
|
+
const [faucetVaultPDA] = (0, pda_1.getFaucetVaultPDA)(programId);
|
|
1186
|
+
const faucetVault = await (0, spl_token_1.getAccount)(config.connection, faucetVaultPDA);
|
|
1187
|
+
if (faucetVault.amount < amountRaw) {
|
|
1188
|
+
(0, output_1.printError)(`Faucet vault has insufficient USDC (available: ${formatUsdc(faucetVault.amount)}). ` +
|
|
1189
|
+
"Ask an admin to refill the faucet.");
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
catch (e) {
|
|
1194
|
+
// If the faucet vault isn't initialized or can't be fetched, surface a clear message
|
|
1195
|
+
const msg = e?.message?.toLowerCase?.() || "";
|
|
1196
|
+
if (msg.includes("failed to find account") || msg.includes("could not find account") || msg.includes("not found")) {
|
|
1197
|
+
(0, output_1.printError)("Faucet vault is not initialized. Ask an admin to initialize and fund the faucet.");
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
// Fall through to attempt faucet call; it may still succeed
|
|
1201
|
+
}
|
|
1202
|
+
// Step 3: Claim USDC from faucet
|
|
1203
|
+
const sig = await (0, output_1.withSpinner)(`Claiming ${amount.toLocaleString()} USDC to your wallet...`, async () => {
|
|
1204
|
+
if (typeof config.shuffleClient.faucet !== "function") {
|
|
1205
|
+
throw new Error("Faucet not supported by this SDK version.");
|
|
1206
|
+
}
|
|
1207
|
+
return await config.shuffleClient.faucet(Math.floor(amount * 1000000));
|
|
1132
1208
|
}, `Received ${amount.toLocaleString()} USDC!`);
|
|
1133
1209
|
(0, output_1.printTxSuccess)(sig, config.network);
|
|
1134
1210
|
}
|
|
1135
1211
|
catch (e) {
|
|
1136
1212
|
// Improve error messages
|
|
1137
|
-
|
|
1138
|
-
|
|
1213
|
+
const msg = e.message || "";
|
|
1214
|
+
const logs = getErrorLogs(e);
|
|
1215
|
+
const hasTokenInsufficient = logs.some((log) => log.toLowerCase().includes("error: insufficient funds"));
|
|
1216
|
+
if (msg.includes("Account does not exist") || msg.includes("not found")) {
|
|
1217
|
+
(0, output_1.printError)("Privacy account not found. Run 'shuffle init' first.");
|
|
1218
|
+
}
|
|
1219
|
+
else if (msg.includes("Faucet limit exceeded") || msg.includes("FaucetLimitExceeded")) {
|
|
1220
|
+
(0, output_1.printError)("Faucet limit exceeded. You can claim up to 1000 USDC total.");
|
|
1221
|
+
}
|
|
1222
|
+
else if (hasTokenInsufficient) {
|
|
1223
|
+
(0, output_1.printError)("Faucet vault has insufficient USDC. Try a smaller amount or ask an admin to refill the faucet.");
|
|
1139
1224
|
}
|
|
1140
|
-
else if (
|
|
1141
|
-
(0, output_1.printError)("Insufficient SOL for transaction fees.
|
|
1225
|
+
else if (msg.toLowerCase().includes("insufficient funds") && (msg.toLowerCase().includes("fee") || msg.toLowerCase().includes("transaction"))) {
|
|
1226
|
+
(0, output_1.printError)("Insufficient SOL for transaction fees. This shouldn't happen - please report this issue.");
|
|
1142
1227
|
}
|
|
1143
1228
|
else {
|
|
1144
|
-
(0, output_1.printError)(
|
|
1229
|
+
(0, output_1.printError)(msg || "Faucet failed");
|
|
1145
1230
|
}
|
|
1146
1231
|
}
|
|
1147
1232
|
}
|
package/dist/cli/config.js
CHANGED
|
@@ -192,17 +192,20 @@ async function loadConfig(opts) {
|
|
|
192
192
|
const connection = new web3_js_1.Connection(getRpcUrl(network), "confirmed");
|
|
193
193
|
const encryptionPrivateKey = loadOrCreateEncryptionKey(userProfile);
|
|
194
194
|
let shuffleClient = null;
|
|
195
|
-
// Select program ID based on network
|
|
195
|
+
// Select program ID and cluster offset based on network
|
|
196
196
|
const programId = network === "localnet"
|
|
197
197
|
? devnet_1.LOCALNET_CONFIG.programId
|
|
198
198
|
: devnet_1.DEVNET_CONFIG.programId;
|
|
199
|
+
const clusterOffset = network === "localnet"
|
|
200
|
+
? 0
|
|
201
|
+
: devnet_1.DEVNET_CONFIG.clusterOffset;
|
|
199
202
|
if (!mockMode) {
|
|
200
203
|
try {
|
|
201
204
|
shuffleClient = await client_1.ShuffleClient.create({
|
|
202
205
|
connection,
|
|
203
206
|
wallet,
|
|
204
207
|
programId,
|
|
205
|
-
clusterOffset
|
|
208
|
+
clusterOffset,
|
|
206
209
|
});
|
|
207
210
|
shuffleClient.initEncryption(encryptionPrivateKey);
|
|
208
211
|
}
|
package/dist/cli/devnet.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { PublicKey } from "@solana/web3.js";
|
|
|
11
11
|
export declare const MOCK_MODE = false;
|
|
12
12
|
/**
|
|
13
13
|
* Localnet program configuration (from arcium test)
|
|
14
|
-
*
|
|
14
|
+
* NOTE: This is automatically updated by setup-local.js
|
|
15
15
|
*/
|
|
16
16
|
export declare const LOCALNET_CONFIG: {
|
|
17
17
|
programId: PublicKey;
|
package/dist/cli/devnet.js
CHANGED
|
@@ -22,20 +22,20 @@ const web3_js_1 = require("@solana/web3.js");
|
|
|
22
22
|
exports.MOCK_MODE = false;
|
|
23
23
|
/**
|
|
24
24
|
* Localnet program configuration (from arcium test)
|
|
25
|
-
*
|
|
25
|
+
* NOTE: This is automatically updated by setup-local.js
|
|
26
26
|
*/
|
|
27
27
|
exports.LOCALNET_CONFIG = {
|
|
28
|
-
programId: new web3_js_1.PublicKey("
|
|
28
|
+
programId: new web3_js_1.PublicKey("DQ29rUToHVTyp2QxP3C7nt1MuYp6p6PKYNaDpGooPAFq"),
|
|
29
29
|
rpcUrl: "http://127.0.0.1:8899",
|
|
30
30
|
};
|
|
31
31
|
/**
|
|
32
32
|
* Devnet program and token configuration
|
|
33
33
|
*/
|
|
34
34
|
exports.DEVNET_CONFIG = {
|
|
35
|
-
// Program ID from successful devnet deployment (2026-02-
|
|
36
|
-
programId: new web3_js_1.PublicKey("
|
|
35
|
+
// Program ID from successful devnet deployment (2026-02-03 v0.7.0 - fresh deploy with synced IDs)
|
|
36
|
+
programId: new web3_js_1.PublicKey("D5hXtvqYeBHM4f8DqJuYyioPNDsQS6jhSRqj9DmFFvCH"),
|
|
37
37
|
rpcUrl: "https://devnet.helius-rpc.com/?api-key=a8e1a5ce-29c6-4356-b3f9-54c1c650ac08",
|
|
38
|
-
// Arcium cluster offset for v0.
|
|
38
|
+
// Arcium cluster offset for v0.7.0 (required for account derivations)
|
|
39
39
|
clusterOffset: 456,
|
|
40
40
|
// Token mints - deployed 2026-02-01
|
|
41
41
|
mints: {
|
package/dist/cli/index.js
CHANGED
|
@@ -134,7 +134,7 @@ program
|
|
|
134
134
|
// Devnet Utilities
|
|
135
135
|
program
|
|
136
136
|
.command("faucet <amount>")
|
|
137
|
-
.description("
|
|
137
|
+
.description("Claim devnet USDC (also airdrops 1 SOL for transaction fees)")
|
|
138
138
|
.action(commands_1.faucetCommand);
|
|
139
139
|
program
|
|
140
140
|
.command("airdrop [amount]")
|
package/dist/cli/output.d.ts
CHANGED
package/dist/cli/output.js
CHANGED
|
@@ -87,8 +87,26 @@ function printHeader(title) {
|
|
|
87
87
|
* Print balance table showing shielded and unshielded balances side-by-side
|
|
88
88
|
* If pendingPayout is provided, add it to the relevant asset in a different color
|
|
89
89
|
*/
|
|
90
|
-
function printBalanceTable(shielded, unshielded, pendingPayout) {
|
|
90
|
+
function printBalanceTable(shielded, unshielded, pendingPayout, solBalanceLamports) {
|
|
91
91
|
printHeader("Your Balances");
|
|
92
|
+
if (solBalanceLamports !== undefined) {
|
|
93
|
+
const sol = Number(solBalanceLamports) / 1000000000;
|
|
94
|
+
let solDisplay;
|
|
95
|
+
if (sol === 0) {
|
|
96
|
+
solDisplay = "0.0000";
|
|
97
|
+
}
|
|
98
|
+
else if (sol < 0.01) {
|
|
99
|
+
solDisplay = sol.toLocaleString("en-US", { minimumFractionDigits: 6, maximumFractionDigits: 6 });
|
|
100
|
+
}
|
|
101
|
+
else if (sol < 1) {
|
|
102
|
+
solDisplay = sol.toLocaleString("en-US", { minimumFractionDigits: 4, maximumFractionDigits: 4 });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
solDisplay = sol.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
106
|
+
}
|
|
107
|
+
console.log(chalk_1.default.gray(` SOL (wallet): ${chalk_1.default.white(solDisplay)} SOL`));
|
|
108
|
+
console.log();
|
|
109
|
+
}
|
|
92
110
|
const format = (val) => {
|
|
93
111
|
const num = Number(val) / 1000000; // 6 decimals
|
|
94
112
|
// Use more decimal places for small values to avoid showing 0.00 for non-zero amounts
|
package/dist/client.d.ts
CHANGED
|
@@ -41,6 +41,11 @@ export declare class ShuffleClient {
|
|
|
41
41
|
fetchUserAccount(owner?: PublicKey): Promise<any>;
|
|
42
42
|
/** Check if account exists */
|
|
43
43
|
accountExists(owner?: PublicKey): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Claim USDC from the program faucet.
|
|
46
|
+
* @param amount Amount in base units (6 decimals).
|
|
47
|
+
*/
|
|
48
|
+
faucet(amount: number): Promise<string>;
|
|
44
49
|
/** Deposit tokens into the protocol (add_balance). Uses internal encryption if params omitted. */
|
|
45
50
|
deposit(assetId: AssetId, amount: number, cipher?: RescueCipher, encryptionPublicKey?: Uint8Array): Promise<string>;
|
|
46
51
|
/** Withdraw tokens from the protocol (sub_balance). Uses internal encryption if params omitted. */
|
package/dist/client.js
CHANGED
|
@@ -153,6 +153,49 @@ class ShuffleClient {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
// =========================================================================
|
|
156
|
+
// DEVNET / FAUCET METHODS
|
|
157
|
+
// =========================================================================
|
|
158
|
+
/**
|
|
159
|
+
* Claim USDC from the program faucet.
|
|
160
|
+
* @param amount Amount in base units (6 decimals).
|
|
161
|
+
*/
|
|
162
|
+
async faucet(amount) {
|
|
163
|
+
const owner = this.wallet.publicKey;
|
|
164
|
+
const [userAccountPDA] = (0, pda_1.getUserAccountPDA)(this.programId, owner);
|
|
165
|
+
const [faucetVaultPDA] = (0, pda_1.getFaucetVaultPDA)(this.programId);
|
|
166
|
+
// Fetch pool to find the USDC mint
|
|
167
|
+
const pool = await this.program.account.pool.fetch(this.poolPDA);
|
|
168
|
+
const usdcMint = pool.usdcMint;
|
|
169
|
+
// Ensure the user's USDC ATA exists (create if missing)
|
|
170
|
+
const userUsdcAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(usdcMint, owner);
|
|
171
|
+
try {
|
|
172
|
+
await (0, spl_token_1.getAccount)(this.connection, userUsdcAccount);
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
if (e instanceof spl_token_1.TokenAccountNotFoundError) {
|
|
176
|
+
const ix = (0, spl_token_1.createAssociatedTokenAccountInstruction)(owner, // payer
|
|
177
|
+
userUsdcAccount, owner, usdcMint);
|
|
178
|
+
const tx = new web3_js_1.Transaction().add(ix);
|
|
179
|
+
await this.provider.sendAndConfirm(tx, []);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
throw e;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const sig = await this.program.methods
|
|
186
|
+
.faucet(new anchor.BN(amount))
|
|
187
|
+
.accounts({
|
|
188
|
+
user: owner,
|
|
189
|
+
userAccount: userAccountPDA,
|
|
190
|
+
userUsdcAccount,
|
|
191
|
+
pool: this.poolPDA,
|
|
192
|
+
faucetVault: faucetVaultPDA,
|
|
193
|
+
tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
|
|
194
|
+
})
|
|
195
|
+
.rpc({ commitment: "confirmed" });
|
|
196
|
+
return sig;
|
|
197
|
+
}
|
|
198
|
+
// =========================================================================
|
|
156
199
|
// BALANCE METHODS
|
|
157
200
|
// =========================================================================
|
|
158
201
|
/** Deposit tokens into the protocol (add_balance). Uses internal encryption if params omitted. */
|
package/dist/constants.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare const USER_SEED = "user";
|
|
|
25
25
|
export declare const BATCH_ACCUMULATOR_SEED = "batch_accumulator";
|
|
26
26
|
export declare const BATCH_LOG_SEED = "batch_log";
|
|
27
27
|
export declare const VAULT_SEED = "vault";
|
|
28
|
+
export declare const FAUCET_USDC_SEED = "faucet_usdc";
|
|
28
29
|
export declare const VAULT_ASSET_SEEDS: Record<AssetId, string>;
|
|
29
30
|
export declare const ASSET_LABELS: Record<AssetId, string>;
|
|
30
31
|
export declare const PAIR_TOKENS: Record<PairId, [AssetId, AssetId]>;
|
package/dist/constants.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PAIR_TOKENS = exports.ASSET_LABELS = exports.VAULT_ASSET_SEEDS = exports.VAULT_SEED = exports.BATCH_LOG_SEED = exports.BATCH_ACCUMULATOR_SEED = exports.USER_SEED = exports.POOL_SEED = exports.NUM_ASSETS = exports.NUM_PAIRS = exports.Direction = exports.PairId = exports.AssetId = exports.PROGRAM_ID = void 0;
|
|
3
|
+
exports.PAIR_TOKENS = exports.ASSET_LABELS = exports.VAULT_ASSET_SEEDS = exports.FAUCET_USDC_SEED = exports.VAULT_SEED = exports.BATCH_LOG_SEED = exports.BATCH_ACCUMULATOR_SEED = exports.USER_SEED = exports.POOL_SEED = exports.NUM_ASSETS = exports.NUM_PAIRS = exports.Direction = exports.PairId = exports.AssetId = exports.PROGRAM_ID = void 0;
|
|
4
4
|
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
-
// Program ID (localnet default
|
|
6
|
-
exports.PROGRAM_ID = new web3_js_1.PublicKey("
|
|
5
|
+
// Program ID (localnet default, use DEVNET_CONFIG for devnet)
|
|
6
|
+
exports.PROGRAM_ID = new web3_js_1.PublicKey("DQ29rUToHVTyp2QxP3C7nt1MuYp6p6PKYNaDpGooPAFq");
|
|
7
7
|
// Asset IDs matching contract/programs/shuffle_protocol/src/constants.rs
|
|
8
8
|
var AssetId;
|
|
9
9
|
(function (AssetId) {
|
|
@@ -36,6 +36,7 @@ exports.USER_SEED = "user";
|
|
|
36
36
|
exports.BATCH_ACCUMULATOR_SEED = "batch_accumulator";
|
|
37
37
|
exports.BATCH_LOG_SEED = "batch_log";
|
|
38
38
|
exports.VAULT_SEED = "vault";
|
|
39
|
+
exports.FAUCET_USDC_SEED = "faucet_usdc";
|
|
39
40
|
// Per-asset vault sub-seeds
|
|
40
41
|
exports.VAULT_ASSET_SEEDS = {
|
|
41
42
|
[AssetId.USDC]: "usdc",
|
package/dist/errors.js
CHANGED
|
@@ -8,7 +8,7 @@ exports.ERROR_MAP = {
|
|
|
8
8
|
6001: { name: "Unauthorized", message: "Unauthorized" },
|
|
9
9
|
6002: { name: "InvalidAmount", message: "Invalid amount" },
|
|
10
10
|
6003: { name: "InvalidAsset", message: "Invalid asset" },
|
|
11
|
-
6004: { name: "InvalidAssetId", message: "Invalid asset ID (must be 0-3)" },
|
|
11
|
+
6004: { name: "InvalidAssetId", message: "Invalid asset ID (must be 0-3 for USDC, TSLA, SPY, AAPL)" },
|
|
12
12
|
6005: { name: "InvalidPairId", message: "Invalid pair ID (must be 0-5)" },
|
|
13
13
|
6006: { name: "InvalidMint", message: "Invalid token mint" },
|
|
14
14
|
6007: { name: "InvalidOwner", message: "Invalid token account owner" },
|
|
@@ -17,13 +17,16 @@ exports.ERROR_MAP = {
|
|
|
17
17
|
6010: { name: "NoPendingOrder", message: "No pending order to settle" },
|
|
18
18
|
6011: { name: "BatchNotFinalized", message: "Batch not yet executed" },
|
|
19
19
|
6012: { name: "BatchIdMismatch", message: "Batch ID mismatch" },
|
|
20
|
-
6013: { name: "
|
|
21
|
-
6014: { name: "
|
|
22
|
-
6015: { name: "
|
|
23
|
-
6016: { name: "
|
|
24
|
-
6017: { name: "
|
|
25
|
-
6018: { name: "
|
|
26
|
-
6019: { name: "
|
|
20
|
+
6013: { name: "InvalidBatchId", message: "Invalid batch ID - doesn't match BatchLog" },
|
|
21
|
+
6014: { name: "SwapsAlreadyExecuted", message: "Swaps already executed for this batch" },
|
|
22
|
+
6015: { name: "InsufficientBalance", message: "Insufficient balance" },
|
|
23
|
+
6016: { name: "MinOutputNotMet", message: "Minimum output not met" },
|
|
24
|
+
6017: { name: "DivisionByZero", message: "Division by zero in settlement - no input for this pair" },
|
|
25
|
+
6018: { name: "AbortedComputation", message: "The computation was aborted" },
|
|
26
|
+
6019: { name: "ComputationFailed", message: "MPC computation failed" },
|
|
27
|
+
6020: { name: "ClusterNotSet", message: "Cluster not set" },
|
|
28
|
+
6021: { name: "RecipientAccountNotFound", message: "Recipient account not found - they must create a privacy account first" },
|
|
29
|
+
6022: { name: "FaucetLimitExceeded", message: "Faucet limit exceeded - you can only claim up to 1000 USDC total" },
|
|
27
30
|
};
|
|
28
31
|
class ShuffleError extends Error {
|
|
29
32
|
constructor(code) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { ShuffleClient } from "./client";
|
|
2
2
|
export type { UserBalance, OrderInfo, DecryptedOrderInfo, BatchInfo, BatchResult, PairResult, ShuffleConfig, EstimatedPayout, EffectiveBalance, } from "./types";
|
|
3
|
-
export { AssetId, PairId, Direction, PROGRAM_ID, NUM_PAIRS, NUM_ASSETS, POOL_SEED, USER_SEED, BATCH_ACCUMULATOR_SEED, BATCH_LOG_SEED, VAULT_SEED, VAULT_ASSET_SEEDS, ASSET_LABELS, PAIR_TOKENS, } from "./constants";
|
|
4
|
-
export { getPoolPDA, getUserAccountPDA, getBatchAccumulatorPDA, getBatchLogPDA, getVaultPDA, } from "./pda";
|
|
3
|
+
export { AssetId, PairId, Direction, PROGRAM_ID, NUM_PAIRS, NUM_ASSETS, POOL_SEED, USER_SEED, BATCH_ACCUMULATOR_SEED, BATCH_LOG_SEED, VAULT_SEED, FAUCET_USDC_SEED, VAULT_ASSET_SEEDS, ASSET_LABELS, PAIR_TOKENS, } from "./constants";
|
|
4
|
+
export { getPoolPDA, getUserAccountPDA, getBatchAccumulatorPDA, getBatchLogPDA, getVaultPDA, getFaucetVaultPDA, } from "./pda";
|
|
5
5
|
export { generateEncryptionKeypair, createCipher, encryptValue, decryptValue, fetchMXEPublicKey, nonceToBN, } from "./encryption";
|
|
6
6
|
export type { EncryptionKeypair, EncryptedValue } from "./encryption";
|
|
7
7
|
export { ShuffleError, parseError, ERROR_MAP } from "./errors";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ERROR_MAP = exports.parseError = exports.ShuffleError = exports.nonceToBN = exports.fetchMXEPublicKey = exports.decryptValue = exports.encryptValue = exports.createCipher = exports.generateEncryptionKeypair = exports.getVaultPDA = exports.getBatchLogPDA = exports.getBatchAccumulatorPDA = exports.getUserAccountPDA = exports.getPoolPDA = exports.PAIR_TOKENS = exports.ASSET_LABELS = exports.VAULT_ASSET_SEEDS = exports.VAULT_SEED = exports.BATCH_LOG_SEED = exports.BATCH_ACCUMULATOR_SEED = exports.USER_SEED = exports.POOL_SEED = exports.NUM_ASSETS = exports.NUM_PAIRS = exports.PROGRAM_ID = exports.Direction = exports.PairId = exports.AssetId = exports.ShuffleClient = void 0;
|
|
3
|
+
exports.ERROR_MAP = exports.parseError = exports.ShuffleError = exports.nonceToBN = exports.fetchMXEPublicKey = exports.decryptValue = exports.encryptValue = exports.createCipher = exports.generateEncryptionKeypair = exports.getFaucetVaultPDA = exports.getVaultPDA = exports.getBatchLogPDA = exports.getBatchAccumulatorPDA = exports.getUserAccountPDA = exports.getPoolPDA = exports.PAIR_TOKENS = exports.ASSET_LABELS = exports.VAULT_ASSET_SEEDS = exports.FAUCET_USDC_SEED = exports.VAULT_SEED = exports.BATCH_LOG_SEED = exports.BATCH_ACCUMULATOR_SEED = exports.USER_SEED = exports.POOL_SEED = exports.NUM_ASSETS = exports.NUM_PAIRS = exports.PROGRAM_ID = exports.Direction = exports.PairId = exports.AssetId = exports.ShuffleClient = void 0;
|
|
4
4
|
// Main SDK exports
|
|
5
5
|
var client_1 = require("./client");
|
|
6
6
|
Object.defineProperty(exports, "ShuffleClient", { enumerable: true, get: function () { return client_1.ShuffleClient; } });
|
|
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "USER_SEED", { enumerable: true, get: function ()
|
|
|
17
17
|
Object.defineProperty(exports, "BATCH_ACCUMULATOR_SEED", { enumerable: true, get: function () { return constants_1.BATCH_ACCUMULATOR_SEED; } });
|
|
18
18
|
Object.defineProperty(exports, "BATCH_LOG_SEED", { enumerable: true, get: function () { return constants_1.BATCH_LOG_SEED; } });
|
|
19
19
|
Object.defineProperty(exports, "VAULT_SEED", { enumerable: true, get: function () { return constants_1.VAULT_SEED; } });
|
|
20
|
+
Object.defineProperty(exports, "FAUCET_USDC_SEED", { enumerable: true, get: function () { return constants_1.FAUCET_USDC_SEED; } });
|
|
20
21
|
Object.defineProperty(exports, "VAULT_ASSET_SEEDS", { enumerable: true, get: function () { return constants_1.VAULT_ASSET_SEEDS; } });
|
|
21
22
|
Object.defineProperty(exports, "ASSET_LABELS", { enumerable: true, get: function () { return constants_1.ASSET_LABELS; } });
|
|
22
23
|
Object.defineProperty(exports, "PAIR_TOKENS", { enumerable: true, get: function () { return constants_1.PAIR_TOKENS; } });
|
|
@@ -27,6 +28,7 @@ Object.defineProperty(exports, "getUserAccountPDA", { enumerable: true, get: fun
|
|
|
27
28
|
Object.defineProperty(exports, "getBatchAccumulatorPDA", { enumerable: true, get: function () { return pda_1.getBatchAccumulatorPDA; } });
|
|
28
29
|
Object.defineProperty(exports, "getBatchLogPDA", { enumerable: true, get: function () { return pda_1.getBatchLogPDA; } });
|
|
29
30
|
Object.defineProperty(exports, "getVaultPDA", { enumerable: true, get: function () { return pda_1.getVaultPDA; } });
|
|
31
|
+
Object.defineProperty(exports, "getFaucetVaultPDA", { enumerable: true, get: function () { return pda_1.getFaucetVaultPDA; } });
|
|
30
32
|
// Encryption helpers
|
|
31
33
|
var encryption_1 = require("./encryption");
|
|
32
34
|
Object.defineProperty(exports, "generateEncryptionKeypair", { enumerable: true, get: function () { return encryption_1.generateEncryptionKeypair; } });
|
package/dist/pda.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export declare function getUserAccountPDA(programId: PublicKey, owner: PublicKey
|
|
|
5
5
|
export declare function getBatchAccumulatorPDA(programId: PublicKey): [PublicKey, number];
|
|
6
6
|
export declare function getBatchLogPDA(programId: PublicKey, batchId: number | anchor.BN): [PublicKey, number];
|
|
7
7
|
export declare function getVaultPDA(programId: PublicKey, assetSeed: string): [PublicKey, number];
|
|
8
|
+
export declare function getFaucetVaultPDA(programId: PublicKey): [PublicKey, number];
|
package/dist/pda.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.getUserAccountPDA = getUserAccountPDA;
|
|
|
38
38
|
exports.getBatchAccumulatorPDA = getBatchAccumulatorPDA;
|
|
39
39
|
exports.getBatchLogPDA = getBatchLogPDA;
|
|
40
40
|
exports.getVaultPDA = getVaultPDA;
|
|
41
|
+
exports.getFaucetVaultPDA = getFaucetVaultPDA;
|
|
41
42
|
const web3_js_1 = require("@solana/web3.js");
|
|
42
43
|
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
43
44
|
const constants_1 = require("./constants");
|
|
@@ -57,3 +58,6 @@ function getBatchLogPDA(programId, batchId) {
|
|
|
57
58
|
function getVaultPDA(programId, assetSeed) {
|
|
58
59
|
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.VAULT_SEED), Buffer.from(assetSeed)], programId);
|
|
59
60
|
}
|
|
61
|
+
function getFaucetVaultPDA(programId) {
|
|
62
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.FAUCET_USDC_SEED)], programId);
|
|
63
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shuffle-protocol/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI and SDK for the Shuffle privacy protocol on Solana",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"url": "https://github.com/tothster/shuffle/issues"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@arcium-hq/client": "0.
|
|
34
|
+
"@arcium-hq/client": "0.7.0",
|
|
35
35
|
"@coral-xyz/anchor": "^0.32.1",
|
|
36
36
|
"@solana/web3.js": "^1.98.4",
|
|
37
37
|
"@solana/spl-token": "^0.4.14",
|