@cloakely/sdk 0.2.1 → 0.2.4

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.
@@ -188,8 +188,19 @@ export class CloakPrivate {
188
188
  }
189
189
  /** The most a single relayed call of this kind can move from `pool`: the balance minus the relayer's fee. */
190
190
  async maxSpendable(pool, kind = "transact") {
191
- const [bal, q] = await Promise.all([this.balance(pool), this.relayFee(kind, pool)]);
192
- return bal > q.fee ? bal - q.fee : 0n;
191
+ const [notes, q, m] = await Promise.all([this.sync(pool), this.relayFee(kind, pool), this.relayFee("transact", pool)]);
192
+ // a transaction takes at most two notes, so using the k largest notes costs k-2 combining steps first (dust notes
193
+ // aren't worth one); the fee is re-quoted when sending, so keep a 25% cushion on every fee
194
+ const sorted = notes.map((n) => n.amount).sort((a, b) => (a > b ? -1 : a < b ? 1 : 0));
195
+ let best = 0n;
196
+ let sum = 0n;
197
+ for (let k = 1; k <= sorted.length; k++) {
198
+ sum += sorted[k - 1];
199
+ const spendable = sum - (q.fee * 5n) / 4n - (BigInt(Math.max(0, k - 2)) * m.fee * 5n) / 4n;
200
+ if (spendable > best)
201
+ best = spendable;
202
+ }
203
+ return best;
193
204
  }
194
205
  /** The Merkle tree of one epoch (default: the epoch the given notes live in, else the newest). */
195
206
  async tree(pool, epoch) {
@@ -305,8 +316,10 @@ export class CloakPrivate {
305
316
  const fee = (await this.relayFee("placeOrder", poolIn)).fee;
306
317
  if (fee > 0n) {
307
318
  const have = await this.balance(poolIn);
308
- if (sizes.reduce((a, x) => a + x, 0n) + fee * BigInt(sizes.length) > have) {
309
- const budget = have - fee * BigInt(maxOrders);
319
+ // headroom: the fee is re-quoted before every order (gas moves), and combining notes first costs one more fee
320
+ const reserve = (n) => (fee * BigInt(n) * 5n) / 4n + fee;
321
+ if (sizes.reduce((a, x) => a + x, 0n) + reserve(sizes.length) > have) {
322
+ const budget = have - reserve(maxOrders);
310
323
  if (budget <= 0n)
311
324
  throw new Error("insufficient private balance");
312
325
  ({ sizes } = splitStandard(budget < amount ? budget : amount, maxOrders));
@@ -320,7 +333,17 @@ export class CloakPrivate {
320
333
  const size = sizes[i];
321
334
  onStatus(`Order ${i + 1}/${sizes.length}: syncing…`);
322
335
  const q = await this.relayFee("placeOrder", poolIn);
323
- const inputs = await this.inputsFor(poolIn, size + q.fee, onStatus);
336
+ let inputs;
337
+ try {
338
+ inputs = await this.inputsFor(poolIn, size + q.fee, onStatus);
339
+ }
340
+ catch (e) {
341
+ // if fees rose mid-way, keep the orders already placed and leave the rest in the balance
342
+ if (!placed.length || !/insufficient private balance/.test(String(e.message)))
343
+ throw e;
344
+ rest += sizes.slice(i).reduce((a, x) => a + x, 0n);
345
+ break;
346
+ }
324
347
  const keyIndex = randomKeyIndex();
325
348
  const outKey = await this.keys.key(keyIndex);
326
349
  const outBlinding = randomField();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloakely/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.2.4",
4
4
  "description": "Cloak on Elysium: sealed-bid token launches and private trading (shielded balances, private swaps, batch auctions, private sends) with on-device zero-knowledge proofs.",
5
5
  "keywords": [
6
6
  "cloak",
@@ -20,7 +20,7 @@
20
20
  "homepage": "https://cloak.boblabs.tech/sdk",
21
21
  "repository": {
22
22
  "type": "git",
23
- "url": "git+https://github.com/NECOKIZZ/cloak.git",
23
+ "url": "git+https://github.com/NECOKIZZ/Cloak.git",
24
24
  "directory": "packages/sdk"
25
25
  },
26
26
  "bugs": {