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