Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/cmds-bun/src/umbra/umbra_claim_utxo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export default class UmbraClaimUtxo extends BaseCommand {
console.log(` utxo data: ${safeJsonStringify(inputs.utxo_data).substring(0, 200)}...`);

try {
const result = await (claimUtxo as any)(inputs.utxo_data);
const utxoBatch = Array.isArray(inputs.utxo_data)
? inputs.utxo_data
: [inputs.utxo_data];
const result = await (claimUtxo as any)(utxoBatch);

console.log("UTXO claimed:", safeJsonStringify(result, 2));

Expand Down
20 changes: 17 additions & 3 deletions crates/cmds-bun/src/umbra/umbra_common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ import { BaseCommand, Value, type Context } from "@space-operator/flow-lib-bun";
import bs58 from "bs58";

export const INDEXER_ENDPOINT_MAINNET =
"https://acqzie0a1h.execute-api.eu-central-1.amazonaws.com";
"https://utxo-indexer.api.umbraprivacy.com";
export const INDEXER_ENDPOINT_DEVNET =
"https://utxo-indexer.api-devnet.umbraprivacy.com";
// Backward-compatible alias; prefer the network-specific constants above.
export const INDEXER_ENDPOINT = INDEXER_ENDPOINT_MAINNET;
export const RELAYER_ENDPOINT_MAINNET =
"https://6yn4ndrv2i.execute-api.eu-central-1.amazonaws.com";
"https://relayer.api.umbraprivacy.com";
// Devnet relayer inferred from the indexer URL pattern
// (utxo-indexer.api-devnet.umbraprivacy.com → relayer.api-devnet.umbraprivacy.com).
// If Umbra publishes a different devnet relayer domain, override via env or update here.
Expand Down Expand Up @@ -165,9 +165,20 @@ export function getPrimarySignature(result: unknown): string {

if (typeof result === "object" && result !== null) {
const record = result as Record<string, unknown>;
const batches = record.batches;
if (batches instanceof Map) {
const signatures: string[] = [];
for (const value of batches.values()) {
const signature = getPrimarySignature(value);
if (signature) signatures.push(signature);
}
if (signatures.length > 0) return signatures.join(",");
}

for (
const key of [
"signature",
"txSignature",
"createUtxoSignature",
"callbackSignature",
"createProofAccountSignature",
Expand Down Expand Up @@ -1405,7 +1416,10 @@ async function resolveCircuitAssets(
);
}

const zkeyRel = entry.zkey ?? entry.url ?? entry;
const selectedEntry = typeof entry === "object" && entry !== null && !entry.zkey && !entry.url
? entry.n1 ?? Object.values(entry)[0]
: entry;
const zkeyRel = selectedEntry.zkey ?? selectedEntry.url ?? selectedEntry;
const wasmRel = (typeof zkeyRel === "string" ? zkeyRel : "").replace(
".zkey",
".wasm",
Expand Down
19 changes: 4 additions & 15 deletions crates/cmds-bun/src/umbra/umbra_fetch_utxos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,10 @@ export default class UmbraFetchUtxos extends BaseCommand {
const myPublicSelfBurnable = publicSelfBurnable.filter(mineOnly);
const myPublicReceived = publicReceived.filter(mineOnly);

// `utxos` feeds umbra_claim_utxo, which is wired to the SDK's
// getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction. That claimer
// consumes the scanner's `received` category (encrypted receiver-claimable
// UTXOs). Public-tagged UTXOs have a different on-chain shape and make
// the claimer throw byte-size errors, so we expose them via separate
// outputs instead and keep `utxos` restricted to what Claim can process.
//
// SDK 4.0 note: as of @umbra-privacy/sdk@4.0.0, both
// getPublicBalanceToReceiverClaimableUtxoCreatorFunction AND
// getEncryptedBalanceToReceiverClaimableUtxoCreatorFunction round-trip
// through the indexer as `public-received` (not `received`). Until that
// upstream behavior changes, end-to-end Create→Fetch→Claim of
// receiver-claimable UTXOs on devnet will see the UTXO land in
// `publicReceived` and `utxos` stay empty. See docs/issues.md.
const claimableByReceiver = [...received];
// Receiver-claimable UTXOs created from public balances currently index as
// `publicReceived`; include them so treasury claim flows see real inbound
// deposits instead of an empty `received` bucket.
const claimableByReceiver = [...received, ...myPublicReceived];
const allUtxos = [
...selfBurnable,
...received,
Expand Down
51 changes: 42 additions & 9 deletions crates/cmds-bun/src/umbra/umbra_withdraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,57 @@ export default class UmbraWithdraw extends BaseCommand {
);

const withdraw = getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction({ client });
const amount = BigInt(inputs.amount) as any;
const amount = BigInt(inputs.amount);
const chunkSize = inputs.chunk_size !== undefined
? BigInt(inputs.chunk_size)
: 1_000_000n;
const destination = (inputs.destination || client.signer.address) as any;

console.log(`Withdrawing ${amount} tokens from encrypted balance...`);
console.log(` chunk_size: ${chunkSize}`);
console.log(` destination: ${destination}`);
console.log(` mint: ${inputs.mint}`);

const result = await withdraw(destination, inputs.mint as any, amount);
if (amount <= 0n) {
throw new Error("amount must be greater than zero");
}
if (chunkSize <= 0n) {
throw new Error("chunk_size must be greater than zero");
}

console.log("Withdrawal complete:", JSON.stringify(result, (_k: string, v: any) => typeof v === "bigint" ? v.toString() : v));
let remaining = amount;
const results: any[] = [];
while (remaining > 0n) {
const nextAmount = remaining > chunkSize ? chunkSize : remaining;
console.log(` withdrawing chunk: ${nextAmount}`);
const result = await withdraw(destination, inputs.mint as any, nextAmount as any);
results.push({ amount: nextAmount, result });
remaining -= nextAmount;
console.log("Withdrawal chunk complete:", JSON.stringify(result, (_k: string, v: any) => typeof v === "bigint" ? v.toString() : v));
}

const signature = Array.isArray(result)
? result.map(String).join(",")
: typeof result === "string"
? result
: JSON.stringify(result, (_k: string, v: any) => typeof v === "bigint" ? v.toString() : v);
console.log("Withdrawal complete:", JSON.stringify(results, (_k: string, v: any) => typeof v === "bigint" ? v.toString() : v));

return { signature };
const signature = results
.map((entry) => {
const result = entry.result;
if (typeof result === "string") return result;
if (Array.isArray(result)) return result.map(String).join(",");
if (result && typeof result === "object") {
return result.callbackSignature || result.queueSignature || result.signature || JSON.stringify(result, (_k: string, v: any) => typeof v === "bigint" ? v.toString() : v);
}
return String(result);
})
.filter(Boolean)
.join(",");

return {
signature,
chunks: results.map((entry) => ({
amount: entry.amount.toString(),
result: entry.result,
})),
};
}
}

Expand Down
Loading