@ocash/sdk 0.1.4-rc.2 → 0.1.4-rc.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.
- package/dist/browser.cjs +103 -70
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +97 -64
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +103 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +97 -64
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +136 -92
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +130 -86
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3419,7 +3419,7 @@ var MemoryStore = class {
|
|
|
3419
3419
|
async getMerkleLeaf(chainId, cid) {
|
|
3420
3420
|
const rows = this.merkleLeavesByChain.get(chainId);
|
|
3421
3421
|
const row = rows?.[cid];
|
|
3422
|
-
if (!row) return void 0;
|
|
3422
|
+
if (!row || row.cid !== cid) return void 0;
|
|
3423
3423
|
return { chainId, cid: row.cid, commitment: row.commitment };
|
|
3424
3424
|
}
|
|
3425
3425
|
/**
|
|
@@ -4436,7 +4436,7 @@ var KeyValueStore = class {
|
|
|
4436
4436
|
if (!this.merkleLeafCids[String(chainId)]?.has(cid)) return void 0;
|
|
4437
4437
|
const raw = await this.options.client.get(this.sharedRecordKey("merkleLeaves", chainId, cid));
|
|
4438
4438
|
const row = this.parseJson(raw, null);
|
|
4439
|
-
if (!row) return void 0;
|
|
4439
|
+
if (!row || row.cid !== cid) return void 0;
|
|
4440
4440
|
return { chainId, cid: row.cid, commitment: row.commitment };
|
|
4441
4441
|
}
|
|
4442
4442
|
async appendMerkleLeaves(chainId, leaves) {
|
|
@@ -5198,10 +5198,10 @@ var ProofEngine = class {
|
|
|
5198
5198
|
merkle_root_index: parsed.merkle_root_index ?? context.merkle_root_index,
|
|
5199
5199
|
relayer: parsed.relayer ?? context.relayer,
|
|
5200
5200
|
recipient: parsed.recipient ?? context.recipient,
|
|
5201
|
-
withdraw_amount: parsed.withdraw_amount ? BigInt(parsed.withdraw_amount) : context.withdraw_amount,
|
|
5201
|
+
withdraw_amount: parsed.withdraw_amount != null ? BigInt(parsed.withdraw_amount) : context.withdraw_amount,
|
|
5202
5202
|
extra_data: parsed.extra_data ?? context.extra_data,
|
|
5203
|
-
relayer_fee: parsed.relayer_fee ? BigInt(parsed.relayer_fee) : context.relayer_fee,
|
|
5204
|
-
gas_drop_value: parsed.gas_drop_value ? BigInt(parsed.gas_drop_value) : context.gas_drop_value,
|
|
5203
|
+
relayer_fee: parsed.relayer_fee != null ? BigInt(parsed.relayer_fee) : context.relayer_fee,
|
|
5204
|
+
gas_drop_value: parsed.gas_drop_value != null ? BigInt(parsed.gas_drop_value) : context.gas_drop_value,
|
|
5205
5205
|
array_hash_digest: parsed.array_hash_digest ?? context.array_hash_digest,
|
|
5206
5206
|
gnark_output: parsed.gnark_output,
|
|
5207
5207
|
witness_json: parsed.witness_json,
|
|
@@ -6324,11 +6324,32 @@ var SyncEngine = class {
|
|
|
6324
6324
|
};
|
|
6325
6325
|
|
|
6326
6326
|
// src/planner/planner.ts
|
|
6327
|
+
var import_viem8 = require("viem");
|
|
6328
|
+
|
|
6329
|
+
// src/utils/validators.ts
|
|
6327
6330
|
var import_viem7 = require("viem");
|
|
6328
6331
|
var requireHex = (value, name) => {
|
|
6329
6332
|
if (isHexStrict(value, { minBytes: 1 })) return value;
|
|
6330
6333
|
throw new SdkError("CONFIG", `${name} must be a hex string starting with 0x`);
|
|
6331
6334
|
};
|
|
6335
|
+
var requireNumber = (value, name) => {
|
|
6336
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
6337
|
+
throw new SdkError("CONFIG", `${name} must be a finite number`);
|
|
6338
|
+
};
|
|
6339
|
+
var requireAddress = (value, name) => {
|
|
6340
|
+
if (typeof value !== "string") {
|
|
6341
|
+
throw new SdkError("CONFIG", `${name} must be a string address`);
|
|
6342
|
+
}
|
|
6343
|
+
return (0, import_viem7.getAddress)(value);
|
|
6344
|
+
};
|
|
6345
|
+
var requireBigint = (value, name) => {
|
|
6346
|
+
if (typeof value === "bigint") return value;
|
|
6347
|
+
if (typeof value === "string" && value.length) return BigInt(value);
|
|
6348
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
|
|
6349
|
+
throw new SdkError("CONFIG", `${name} must be a bigint-compatible value`);
|
|
6350
|
+
};
|
|
6351
|
+
|
|
6352
|
+
// src/planner/planner.ts
|
|
6332
6353
|
var parsePlanInput = (input) => {
|
|
6333
6354
|
const action = input.action;
|
|
6334
6355
|
if (action !== "transfer" && action !== "withdraw") {
|
|
@@ -6357,7 +6378,7 @@ var parsePlanInput = (input) => {
|
|
|
6357
6378
|
if (gasDropValue != null && typeof gasDropValue !== "bigint") throw new SdkError("CONFIG", "gasDropValue must be bigint");
|
|
6358
6379
|
return { action, chainId, assetId, amount, recipient, gasDropValue, payIncludesFee, relayerUrl: relayerUrl ?? void 0 };
|
|
6359
6380
|
};
|
|
6360
|
-
var tokenFeeKey = (token) => (0,
|
|
6381
|
+
var tokenFeeKey = (token) => (0, import_viem8.toHex)(BigInt(token.id), { size: 32 }).toLowerCase();
|
|
6361
6382
|
var selectTransferInputs = (utxos, required, maxInputs = 3) => {
|
|
6362
6383
|
const sorted = [...utxos].sort((a, b) => b.amount > a.amount ? 1 : b.amount < a.amount ? -1 : 0);
|
|
6363
6384
|
const selected = [];
|
|
@@ -6397,7 +6418,7 @@ var recordsFee = (input, _records, expectedOutput, action, relayerFee, expectedI
|
|
|
6397
6418
|
total = records.reduce((acc, cur) => acc + cur, 0n);
|
|
6398
6419
|
fee = BigInt(feeCount) * relayerFee.transfer;
|
|
6399
6420
|
relayFee = fee;
|
|
6400
|
-
if (
|
|
6421
|
+
if (import_viem8.maxUint256 === expectedOutput) {
|
|
6401
6422
|
cost = total;
|
|
6402
6423
|
outputAmount = total - fee;
|
|
6403
6424
|
if (outputAmount < 0n) {
|
|
@@ -6406,9 +6427,12 @@ var recordsFee = (input, _records, expectedOutput, action, relayerFee, expectedI
|
|
|
6406
6427
|
}
|
|
6407
6428
|
} else {
|
|
6408
6429
|
cost = expectedIsWithFee ? expectedOutput : expectedOutput + fee;
|
|
6430
|
+
outputAmount = expectedIsWithFee ? expectedOutput - fee : expectedOutput;
|
|
6431
|
+
if (outputAmount < 0n) outputAmount = 0n;
|
|
6409
6432
|
}
|
|
6410
6433
|
if (total < cost) {
|
|
6411
6434
|
cost = 0n;
|
|
6435
|
+
outputAmount = 0n;
|
|
6412
6436
|
}
|
|
6413
6437
|
break;
|
|
6414
6438
|
}
|
|
@@ -6419,7 +6443,7 @@ var recordsFee = (input, _records, expectedOutput, action, relayerFee, expectedI
|
|
|
6419
6443
|
const withdrawFeeDenominator = bpsBase + withdrawFeeBps;
|
|
6420
6444
|
total = records.reduce((acc, cur) => acc + cur, 0n);
|
|
6421
6445
|
fee = BigInt(feeCount - 1) * relayerFee.transfer;
|
|
6422
|
-
if (
|
|
6446
|
+
if (import_viem8.maxUint256 === expectedOutput) {
|
|
6423
6447
|
const withdrawBase = (total - fee) * bpsBase / withdrawFeeDenominator;
|
|
6424
6448
|
outputAmount = withdrawBase - relayFeePay;
|
|
6425
6449
|
relayFee = fee + relayFeePay;
|
|
@@ -6490,7 +6514,7 @@ var estimateRecords = (input) => {
|
|
|
6490
6514
|
const payRecords = [];
|
|
6491
6515
|
let payInfo = recordsFee({ withdrawFeeBps: input.withdrawFeeBps }, [...payRecords], input.expectedOutput, input.action, input.relayerFee, input.expectedIsWithFee);
|
|
6492
6516
|
const maxRecords = [];
|
|
6493
|
-
let maxInfo = recordsFee({ withdrawFeeBps: input.withdrawFeeBps }, [...maxRecords],
|
|
6517
|
+
let maxInfo = recordsFee({ withdrawFeeBps: input.withdrawFeeBps }, [...maxRecords], import_viem8.maxUint256, input.action, input.relayerFee, input.expectedIsWithFee);
|
|
6494
6518
|
for (const record of sorted) {
|
|
6495
6519
|
const isExceedPay = input.expectedIsWithFee ? payInfo.cost >= input.expectedOutput : payInfo.outputAmount >= input.expectedOutput;
|
|
6496
6520
|
if (payInfo.cost === 0n || !isExceedPay) {
|
|
@@ -6498,7 +6522,7 @@ var estimateRecords = (input) => {
|
|
|
6498
6522
|
payInfo = recordsFee({ withdrawFeeBps: input.withdrawFeeBps }, [...payRecords], input.expectedOutput, input.action, input.relayerFee, input.expectedIsWithFee);
|
|
6499
6523
|
}
|
|
6500
6524
|
maxRecords.push(record);
|
|
6501
|
-
const tempMax = recordsFee({ withdrawFeeBps: input.withdrawFeeBps }, [...maxRecords],
|
|
6525
|
+
const tempMax = recordsFee({ withdrawFeeBps: input.withdrawFeeBps }, [...maxRecords], import_viem8.maxUint256, input.action, input.relayerFee, input.expectedIsWithFee);
|
|
6502
6526
|
if (maxInfo.cost === 0n || tempMax.outputAmount > maxInfo.outputAmount) {
|
|
6503
6527
|
maxInfo = tempMax;
|
|
6504
6528
|
}
|
|
@@ -6663,7 +6687,7 @@ var Planner = class {
|
|
|
6663
6687
|
const records = utxos.map((u) => u.amount).filter((v) => v > 0n);
|
|
6664
6688
|
const estimates = estimateRecords({
|
|
6665
6689
|
records,
|
|
6666
|
-
expectedOutput:
|
|
6690
|
+
expectedOutput: import_viem8.maxUint256,
|
|
6667
6691
|
action: input.action,
|
|
6668
6692
|
relayerFee: { transfer: transferFee, withdraw: relayerFee },
|
|
6669
6693
|
withdrawFeeBps: token.withdrawFeeBps,
|
|
@@ -6894,27 +6918,6 @@ var Planner = class {
|
|
|
6894
6918
|
};
|
|
6895
6919
|
|
|
6896
6920
|
// src/tx/txBuilder.ts
|
|
6897
|
-
var import_viem8 = require("viem");
|
|
6898
|
-
var requireNumber = (value, name) => {
|
|
6899
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
6900
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6901
|
-
};
|
|
6902
|
-
var requireHex2 = (value, name) => {
|
|
6903
|
-
if (isHexStrict(value, { minBytes: 1 })) return value;
|
|
6904
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6905
|
-
};
|
|
6906
|
-
var requireAddress = (value, name) => {
|
|
6907
|
-
if (typeof value !== "string") {
|
|
6908
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6909
|
-
}
|
|
6910
|
-
return (0, import_viem8.getAddress)(value);
|
|
6911
|
-
};
|
|
6912
|
-
var requireBigint = (value, name) => {
|
|
6913
|
-
if (typeof value === "bigint") return value;
|
|
6914
|
-
if (typeof value === "string" && value.length) return BigInt(value);
|
|
6915
|
-
if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
|
|
6916
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6917
|
-
};
|
|
6918
6921
|
var TxBuilder = class {
|
|
6919
6922
|
/**
|
|
6920
6923
|
* Build relayer request for transfer proofs.
|
|
@@ -6928,7 +6931,7 @@ var TxBuilder = class {
|
|
|
6928
6931
|
if (!Array.isArray(extraData) || extraData.length !== 3) {
|
|
6929
6932
|
throw new SdkError("CONFIG", "Transfer requires extra_data as bytes[3]");
|
|
6930
6933
|
}
|
|
6931
|
-
extraData.forEach((entry, idx) =>
|
|
6934
|
+
extraData.forEach((entry, idx) => requireHex(entry, `extra_data[${idx}]`));
|
|
6932
6935
|
const request = {
|
|
6933
6936
|
kind: "relayer",
|
|
6934
6937
|
method: "POST",
|
|
@@ -6961,7 +6964,7 @@ var TxBuilder = class {
|
|
|
6961
6964
|
if (Array.isArray(extraData)) {
|
|
6962
6965
|
throw new SdkError("CONFIG", "Withdraw requires extra_data as bytes");
|
|
6963
6966
|
}
|
|
6964
|
-
const extraDataHex =
|
|
6967
|
+
const extraDataHex = requireHex(extraData, "extra_data");
|
|
6965
6968
|
const request = {
|
|
6966
6969
|
kind: "relayer",
|
|
6967
6970
|
method: "POST",
|
|
@@ -7362,7 +7365,7 @@ var MerkleEngine = class _MerkleEngine {
|
|
|
7362
7365
|
const isZero = BigInt(onChainNorm) === 0n;
|
|
7363
7366
|
if (!isZero && onChainNorm !== result.rootHash) {
|
|
7364
7367
|
const target = state.mergedElements;
|
|
7365
|
-
await this.
|
|
7368
|
+
await this._rollback(chainId, target);
|
|
7366
7369
|
throw new SdkError("MERKLE", "Local merkle root mismatch with on-chain root \u2014 rolled back", {
|
|
7367
7370
|
chainId,
|
|
7368
7371
|
rootIndex,
|
|
@@ -7395,21 +7398,31 @@ var MerkleEngine = class _MerkleEngine {
|
|
|
7395
7398
|
}
|
|
7396
7399
|
// ── Rollback (tree O(1) + sync cursor reset) ──
|
|
7397
7400
|
/**
|
|
7398
|
-
*
|
|
7399
|
-
*
|
|
7401
|
+
* Public rollback: step back one batch (32 elements) from the current position.
|
|
7402
|
+
* Upper-layer code calls this on any error to reset and retry.
|
|
7400
7403
|
*
|
|
7401
7404
|
* What gets rolled back:
|
|
7402
7405
|
* - ChairmanMerkle tree version pointer (O(1) — old nodes still in storage)
|
|
7403
7406
|
* - Pending leaves buffer (cleared)
|
|
7404
7407
|
* - Sync cursor: memo + merkle fields (nullifier left unchanged — independent)
|
|
7405
7408
|
*
|
|
7409
|
+
* @returns true if rollback succeeded, false if already at 0 or target version doesn't exist.
|
|
7410
|
+
*/
|
|
7411
|
+
async rollback(chainId) {
|
|
7412
|
+
const state = this.ensureChainState(chainId);
|
|
7413
|
+
const target = Math.max(0, state.mergedElements - SUBTREE_SIZE);
|
|
7414
|
+
return this._rollback(chainId, target);
|
|
7415
|
+
}
|
|
7416
|
+
/**
|
|
7417
|
+
* Internal rollback to an exact batch boundary.
|
|
7418
|
+
*
|
|
7406
7419
|
* @param targetMergedElements Must be a non-negative multiple of 32.
|
|
7407
7420
|
* Pass 0 to reset to the empty tree.
|
|
7408
7421
|
* @returns true if rollback succeeded, false if the target version doesn't exist.
|
|
7409
7422
|
*/
|
|
7410
|
-
async
|
|
7423
|
+
async _rollback(chainId, targetMergedElements) {
|
|
7411
7424
|
if (targetMergedElements < 0 || targetMergedElements % SUBTREE_SIZE !== 0) {
|
|
7412
|
-
throw new SdkError("MERKLE", "
|
|
7425
|
+
throw new SdkError("MERKLE", "_rollback target must be a non-negative multiple of 32", { targetMergedElements });
|
|
7413
7426
|
}
|
|
7414
7427
|
const state = this.ensureChainState(chainId);
|
|
7415
7428
|
const pending = this.ensurePendingLeaves(chainId);
|
|
@@ -7460,44 +7473,64 @@ var MerkleEngine = class _MerkleEngine {
|
|
|
7460
7473
|
await this.hydrateFromStorage(input.chainId);
|
|
7461
7474
|
const canUseLocal = this.mode !== "remote";
|
|
7462
7475
|
if (canUseLocal) {
|
|
7463
|
-
const
|
|
7464
|
-
const
|
|
7465
|
-
if (
|
|
7476
|
+
const hasMerkleLeaf = typeof this.storage?.getMerkleLeaf === "function";
|
|
7477
|
+
const hasChairmanNode = typeof this.storage?.getChairmanMerkleNode === "function";
|
|
7478
|
+
if (hasMerkleLeaf && hasChairmanNode) {
|
|
7466
7479
|
const state = this.ensureChainState(input.chainId);
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
} else {
|
|
7477
|
-
try {
|
|
7478
|
-
const proof = [];
|
|
7479
|
-
for (const cid of cids) {
|
|
7480
|
-
if (cid >= contractTreeElements) {
|
|
7481
|
-
proof.push({ leaf_index: cid, path: new Array(this.treeDepth + 1).fill("0") });
|
|
7482
|
-
continue;
|
|
7483
|
-
}
|
|
7484
|
-
const path = await this.buildLocalProofPath(input.chainId, cid, version);
|
|
7485
|
-
proof.push({ leaf_index: cid, path });
|
|
7480
|
+
let version = contractTreeElements > 0 ? await this.storage?.getChairmanMerkleVersion?.(input.chainId, contractTreeElements) : void 0;
|
|
7481
|
+
let effectiveTreeElements = contractTreeElements;
|
|
7482
|
+
if (!version && contractTreeElements > 0 && state.mergedElements > 0) {
|
|
7483
|
+
const latest = await this.storage?.getLatestChairmanMerkleVersion?.(input.chainId);
|
|
7484
|
+
if (latest && latest.version > 0) {
|
|
7485
|
+
const allCovered = needsTreeProof.every((cid) => cid < latest.version);
|
|
7486
|
+
if (allCovered) {
|
|
7487
|
+
version = latest;
|
|
7488
|
+
effectiveTreeElements = latest.version;
|
|
7486
7489
|
}
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
};
|
|
7493
|
-
} catch (error) {
|
|
7490
|
+
}
|
|
7491
|
+
}
|
|
7492
|
+
const hasDb = contractTreeElements === 0 || !!version;
|
|
7493
|
+
if (hasDb) {
|
|
7494
|
+
if (effectiveTreeElements > 0 && state.mergedElements < effectiveTreeElements) {
|
|
7494
7495
|
if (this.mode === "local") {
|
|
7495
|
-
throw new SdkError("MERKLE", "Local merkle
|
|
7496
|
+
throw new SdkError("MERKLE", "Local merkle db is behind contract", {
|
|
7497
|
+
chainId: input.chainId,
|
|
7498
|
+
cids,
|
|
7499
|
+
localMergedElements: state.mergedElements,
|
|
7500
|
+
contractTreeElements: effectiveTreeElements
|
|
7501
|
+
});
|
|
7502
|
+
}
|
|
7503
|
+
} else {
|
|
7504
|
+
try {
|
|
7505
|
+
const proof = [];
|
|
7506
|
+
for (const cid of cids) {
|
|
7507
|
+
if (cid >= effectiveTreeElements) {
|
|
7508
|
+
proof.push({ leaf_index: cid, path: new Array(this.treeDepth + 1).fill("0") });
|
|
7509
|
+
continue;
|
|
7510
|
+
}
|
|
7511
|
+
const path = await this.buildLocalProofPath(input.chainId, cid, version);
|
|
7512
|
+
proof.push({ leaf_index: cid, path });
|
|
7513
|
+
}
|
|
7514
|
+
const effectiveRoot = effectiveTreeElements > 0 ? _MerkleEngine.normalizeHex32(version.rootHash, "version.rootHash") : getZeroHash(this.treeDepth);
|
|
7515
|
+
const effectiveLatestCid = effectiveTreeElements > 0 ? effectiveTreeElements - 1 : -1;
|
|
7516
|
+
return {
|
|
7517
|
+
proof,
|
|
7518
|
+
merkle_root: effectiveRoot,
|
|
7519
|
+
latest_cid: effectiveLatestCid
|
|
7520
|
+
};
|
|
7521
|
+
} catch (error) {
|
|
7522
|
+
if (this.mode === "local") {
|
|
7523
|
+
throw new SdkError("MERKLE", "Local merkle proof build failed", { chainId: input.chainId, cids }, error);
|
|
7524
|
+
}
|
|
7496
7525
|
}
|
|
7497
7526
|
}
|
|
7527
|
+
} else {
|
|
7528
|
+
if (this.mode === "local" && needsTreeProof.length) {
|
|
7529
|
+
throw new SdkError("MERKLE", "Local merkle db unavailable", { chainId: input.chainId, cids, reason: "missing_adapter_or_version" });
|
|
7530
|
+
}
|
|
7498
7531
|
}
|
|
7499
7532
|
} else if (this.mode === "local" && needsTreeProof.length) {
|
|
7500
|
-
throw new SdkError("MERKLE", "Local merkle db unavailable", { chainId: input.chainId, cids, reason: "
|
|
7533
|
+
throw new SdkError("MERKLE", "Local merkle db unavailable", { chainId: input.chainId, cids, reason: "missing_adapter" });
|
|
7501
7534
|
}
|
|
7502
7535
|
}
|
|
7503
7536
|
if (needsTreeProof.length === 0) {
|