@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.js
CHANGED
|
@@ -3352,7 +3352,7 @@ var MemoryStore = class {
|
|
|
3352
3352
|
async getMerkleLeaf(chainId, cid) {
|
|
3353
3353
|
const rows = this.merkleLeavesByChain.get(chainId);
|
|
3354
3354
|
const row = rows?.[cid];
|
|
3355
|
-
if (!row) return void 0;
|
|
3355
|
+
if (!row || row.cid !== cid) return void 0;
|
|
3356
3356
|
return { chainId, cid: row.cid, commitment: row.commitment };
|
|
3357
3357
|
}
|
|
3358
3358
|
/**
|
|
@@ -4369,7 +4369,7 @@ var KeyValueStore = class {
|
|
|
4369
4369
|
if (!this.merkleLeafCids[String(chainId)]?.has(cid)) return void 0;
|
|
4370
4370
|
const raw = await this.options.client.get(this.sharedRecordKey("merkleLeaves", chainId, cid));
|
|
4371
4371
|
const row = this.parseJson(raw, null);
|
|
4372
|
-
if (!row) return void 0;
|
|
4372
|
+
if (!row || row.cid !== cid) return void 0;
|
|
4373
4373
|
return { chainId, cid: row.cid, commitment: row.commitment };
|
|
4374
4374
|
}
|
|
4375
4375
|
async appendMerkleLeaves(chainId, leaves) {
|
|
@@ -5131,10 +5131,10 @@ var ProofEngine = class {
|
|
|
5131
5131
|
merkle_root_index: parsed.merkle_root_index ?? context.merkle_root_index,
|
|
5132
5132
|
relayer: parsed.relayer ?? context.relayer,
|
|
5133
5133
|
recipient: parsed.recipient ?? context.recipient,
|
|
5134
|
-
withdraw_amount: parsed.withdraw_amount ? BigInt(parsed.withdraw_amount) : context.withdraw_amount,
|
|
5134
|
+
withdraw_amount: parsed.withdraw_amount != null ? BigInt(parsed.withdraw_amount) : context.withdraw_amount,
|
|
5135
5135
|
extra_data: parsed.extra_data ?? context.extra_data,
|
|
5136
|
-
relayer_fee: parsed.relayer_fee ? BigInt(parsed.relayer_fee) : context.relayer_fee,
|
|
5137
|
-
gas_drop_value: parsed.gas_drop_value ? BigInt(parsed.gas_drop_value) : context.gas_drop_value,
|
|
5136
|
+
relayer_fee: parsed.relayer_fee != null ? BigInt(parsed.relayer_fee) : context.relayer_fee,
|
|
5137
|
+
gas_drop_value: parsed.gas_drop_value != null ? BigInt(parsed.gas_drop_value) : context.gas_drop_value,
|
|
5138
5138
|
array_hash_digest: parsed.array_hash_digest ?? context.array_hash_digest,
|
|
5139
5139
|
gnark_output: parsed.gnark_output,
|
|
5140
5140
|
witness_json: parsed.witness_json,
|
|
@@ -6258,10 +6258,31 @@ var SyncEngine = class {
|
|
|
6258
6258
|
|
|
6259
6259
|
// src/planner/planner.ts
|
|
6260
6260
|
import { maxUint256, toHex as toHex4 } from "viem";
|
|
6261
|
+
|
|
6262
|
+
// src/utils/validators.ts
|
|
6263
|
+
import { getAddress as getAddress2 } from "viem";
|
|
6261
6264
|
var requireHex = (value, name) => {
|
|
6262
6265
|
if (isHexStrict(value, { minBytes: 1 })) return value;
|
|
6263
6266
|
throw new SdkError("CONFIG", `${name} must be a hex string starting with 0x`);
|
|
6264
6267
|
};
|
|
6268
|
+
var requireNumber = (value, name) => {
|
|
6269
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
6270
|
+
throw new SdkError("CONFIG", `${name} must be a finite number`);
|
|
6271
|
+
};
|
|
6272
|
+
var requireAddress = (value, name) => {
|
|
6273
|
+
if (typeof value !== "string") {
|
|
6274
|
+
throw new SdkError("CONFIG", `${name} must be a string address`);
|
|
6275
|
+
}
|
|
6276
|
+
return getAddress2(value);
|
|
6277
|
+
};
|
|
6278
|
+
var requireBigint = (value, name) => {
|
|
6279
|
+
if (typeof value === "bigint") return value;
|
|
6280
|
+
if (typeof value === "string" && value.length) return BigInt(value);
|
|
6281
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
|
|
6282
|
+
throw new SdkError("CONFIG", `${name} must be a bigint-compatible value`);
|
|
6283
|
+
};
|
|
6284
|
+
|
|
6285
|
+
// src/planner/planner.ts
|
|
6265
6286
|
var parsePlanInput = (input) => {
|
|
6266
6287
|
const action = input.action;
|
|
6267
6288
|
if (action !== "transfer" && action !== "withdraw") {
|
|
@@ -6339,9 +6360,12 @@ var recordsFee = (input, _records, expectedOutput, action, relayerFee, expectedI
|
|
|
6339
6360
|
}
|
|
6340
6361
|
} else {
|
|
6341
6362
|
cost = expectedIsWithFee ? expectedOutput : expectedOutput + fee;
|
|
6363
|
+
outputAmount = expectedIsWithFee ? expectedOutput - fee : expectedOutput;
|
|
6364
|
+
if (outputAmount < 0n) outputAmount = 0n;
|
|
6342
6365
|
}
|
|
6343
6366
|
if (total < cost) {
|
|
6344
6367
|
cost = 0n;
|
|
6368
|
+
outputAmount = 0n;
|
|
6345
6369
|
}
|
|
6346
6370
|
break;
|
|
6347
6371
|
}
|
|
@@ -6827,27 +6851,6 @@ var Planner = class {
|
|
|
6827
6851
|
};
|
|
6828
6852
|
|
|
6829
6853
|
// src/tx/txBuilder.ts
|
|
6830
|
-
import { getAddress as getAddress2 } from "viem";
|
|
6831
|
-
var requireNumber = (value, name) => {
|
|
6832
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
6833
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6834
|
-
};
|
|
6835
|
-
var requireHex2 = (value, name) => {
|
|
6836
|
-
if (isHexStrict(value, { minBytes: 1 })) return value;
|
|
6837
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6838
|
-
};
|
|
6839
|
-
var requireAddress = (value, name) => {
|
|
6840
|
-
if (typeof value !== "string") {
|
|
6841
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6842
|
-
}
|
|
6843
|
-
return getAddress2(value);
|
|
6844
|
-
};
|
|
6845
|
-
var requireBigint = (value, name) => {
|
|
6846
|
-
if (typeof value === "bigint") return value;
|
|
6847
|
-
if (typeof value === "string" && value.length) return BigInt(value);
|
|
6848
|
-
if (typeof value === "number" && Number.isSafeInteger(value)) return BigInt(value);
|
|
6849
|
-
throw new SdkError("CONFIG", `Missing ${name}`);
|
|
6850
|
-
};
|
|
6851
6854
|
var TxBuilder = class {
|
|
6852
6855
|
/**
|
|
6853
6856
|
* Build relayer request for transfer proofs.
|
|
@@ -6861,7 +6864,7 @@ var TxBuilder = class {
|
|
|
6861
6864
|
if (!Array.isArray(extraData) || extraData.length !== 3) {
|
|
6862
6865
|
throw new SdkError("CONFIG", "Transfer requires extra_data as bytes[3]");
|
|
6863
6866
|
}
|
|
6864
|
-
extraData.forEach((entry, idx) =>
|
|
6867
|
+
extraData.forEach((entry, idx) => requireHex(entry, `extra_data[${idx}]`));
|
|
6865
6868
|
const request = {
|
|
6866
6869
|
kind: "relayer",
|
|
6867
6870
|
method: "POST",
|
|
@@ -6894,7 +6897,7 @@ var TxBuilder = class {
|
|
|
6894
6897
|
if (Array.isArray(extraData)) {
|
|
6895
6898
|
throw new SdkError("CONFIG", "Withdraw requires extra_data as bytes");
|
|
6896
6899
|
}
|
|
6897
|
-
const extraDataHex =
|
|
6900
|
+
const extraDataHex = requireHex(extraData, "extra_data");
|
|
6898
6901
|
const request = {
|
|
6899
6902
|
kind: "relayer",
|
|
6900
6903
|
method: "POST",
|
|
@@ -7295,7 +7298,7 @@ var MerkleEngine = class _MerkleEngine {
|
|
|
7295
7298
|
const isZero = BigInt(onChainNorm) === 0n;
|
|
7296
7299
|
if (!isZero && onChainNorm !== result.rootHash) {
|
|
7297
7300
|
const target = state.mergedElements;
|
|
7298
|
-
await this.
|
|
7301
|
+
await this._rollback(chainId, target);
|
|
7299
7302
|
throw new SdkError("MERKLE", "Local merkle root mismatch with on-chain root \u2014 rolled back", {
|
|
7300
7303
|
chainId,
|
|
7301
7304
|
rootIndex,
|
|
@@ -7328,21 +7331,31 @@ var MerkleEngine = class _MerkleEngine {
|
|
|
7328
7331
|
}
|
|
7329
7332
|
// ── Rollback (tree O(1) + sync cursor reset) ──
|
|
7330
7333
|
/**
|
|
7331
|
-
*
|
|
7332
|
-
*
|
|
7334
|
+
* Public rollback: step back one batch (32 elements) from the current position.
|
|
7335
|
+
* Upper-layer code calls this on any error to reset and retry.
|
|
7333
7336
|
*
|
|
7334
7337
|
* What gets rolled back:
|
|
7335
7338
|
* - ChairmanMerkle tree version pointer (O(1) — old nodes still in storage)
|
|
7336
7339
|
* - Pending leaves buffer (cleared)
|
|
7337
7340
|
* - Sync cursor: memo + merkle fields (nullifier left unchanged — independent)
|
|
7338
7341
|
*
|
|
7342
|
+
* @returns true if rollback succeeded, false if already at 0 or target version doesn't exist.
|
|
7343
|
+
*/
|
|
7344
|
+
async rollback(chainId) {
|
|
7345
|
+
const state = this.ensureChainState(chainId);
|
|
7346
|
+
const target = Math.max(0, state.mergedElements - SUBTREE_SIZE);
|
|
7347
|
+
return this._rollback(chainId, target);
|
|
7348
|
+
}
|
|
7349
|
+
/**
|
|
7350
|
+
* Internal rollback to an exact batch boundary.
|
|
7351
|
+
*
|
|
7339
7352
|
* @param targetMergedElements Must be a non-negative multiple of 32.
|
|
7340
7353
|
* Pass 0 to reset to the empty tree.
|
|
7341
7354
|
* @returns true if rollback succeeded, false if the target version doesn't exist.
|
|
7342
7355
|
*/
|
|
7343
|
-
async
|
|
7356
|
+
async _rollback(chainId, targetMergedElements) {
|
|
7344
7357
|
if (targetMergedElements < 0 || targetMergedElements % SUBTREE_SIZE !== 0) {
|
|
7345
|
-
throw new SdkError("MERKLE", "
|
|
7358
|
+
throw new SdkError("MERKLE", "_rollback target must be a non-negative multiple of 32", { targetMergedElements });
|
|
7346
7359
|
}
|
|
7347
7360
|
const state = this.ensureChainState(chainId);
|
|
7348
7361
|
const pending = this.ensurePendingLeaves(chainId);
|
|
@@ -7393,44 +7406,64 @@ var MerkleEngine = class _MerkleEngine {
|
|
|
7393
7406
|
await this.hydrateFromStorage(input.chainId);
|
|
7394
7407
|
const canUseLocal = this.mode !== "remote";
|
|
7395
7408
|
if (canUseLocal) {
|
|
7396
|
-
const
|
|
7397
|
-
const
|
|
7398
|
-
if (
|
|
7409
|
+
const hasMerkleLeaf = typeof this.storage?.getMerkleLeaf === "function";
|
|
7410
|
+
const hasChairmanNode = typeof this.storage?.getChairmanMerkleNode === "function";
|
|
7411
|
+
if (hasMerkleLeaf && hasChairmanNode) {
|
|
7399
7412
|
const state = this.ensureChainState(input.chainId);
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
} else {
|
|
7410
|
-
try {
|
|
7411
|
-
const proof = [];
|
|
7412
|
-
for (const cid of cids) {
|
|
7413
|
-
if (cid >= contractTreeElements) {
|
|
7414
|
-
proof.push({ leaf_index: cid, path: new Array(this.treeDepth + 1).fill("0") });
|
|
7415
|
-
continue;
|
|
7416
|
-
}
|
|
7417
|
-
const path = await this.buildLocalProofPath(input.chainId, cid, version);
|
|
7418
|
-
proof.push({ leaf_index: cid, path });
|
|
7413
|
+
let version = contractTreeElements > 0 ? await this.storage?.getChairmanMerkleVersion?.(input.chainId, contractTreeElements) : void 0;
|
|
7414
|
+
let effectiveTreeElements = contractTreeElements;
|
|
7415
|
+
if (!version && contractTreeElements > 0 && state.mergedElements > 0) {
|
|
7416
|
+
const latest = await this.storage?.getLatestChairmanMerkleVersion?.(input.chainId);
|
|
7417
|
+
if (latest && latest.version > 0) {
|
|
7418
|
+
const allCovered = needsTreeProof.every((cid) => cid < latest.version);
|
|
7419
|
+
if (allCovered) {
|
|
7420
|
+
version = latest;
|
|
7421
|
+
effectiveTreeElements = latest.version;
|
|
7419
7422
|
}
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
};
|
|
7426
|
-
} catch (error) {
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
const hasDb = contractTreeElements === 0 || !!version;
|
|
7426
|
+
if (hasDb) {
|
|
7427
|
+
if (effectiveTreeElements > 0 && state.mergedElements < effectiveTreeElements) {
|
|
7427
7428
|
if (this.mode === "local") {
|
|
7428
|
-
throw new SdkError("MERKLE", "Local merkle
|
|
7429
|
+
throw new SdkError("MERKLE", "Local merkle db is behind contract", {
|
|
7430
|
+
chainId: input.chainId,
|
|
7431
|
+
cids,
|
|
7432
|
+
localMergedElements: state.mergedElements,
|
|
7433
|
+
contractTreeElements: effectiveTreeElements
|
|
7434
|
+
});
|
|
7435
|
+
}
|
|
7436
|
+
} else {
|
|
7437
|
+
try {
|
|
7438
|
+
const proof = [];
|
|
7439
|
+
for (const cid of cids) {
|
|
7440
|
+
if (cid >= effectiveTreeElements) {
|
|
7441
|
+
proof.push({ leaf_index: cid, path: new Array(this.treeDepth + 1).fill("0") });
|
|
7442
|
+
continue;
|
|
7443
|
+
}
|
|
7444
|
+
const path = await this.buildLocalProofPath(input.chainId, cid, version);
|
|
7445
|
+
proof.push({ leaf_index: cid, path });
|
|
7446
|
+
}
|
|
7447
|
+
const effectiveRoot = effectiveTreeElements > 0 ? _MerkleEngine.normalizeHex32(version.rootHash, "version.rootHash") : getZeroHash(this.treeDepth);
|
|
7448
|
+
const effectiveLatestCid = effectiveTreeElements > 0 ? effectiveTreeElements - 1 : -1;
|
|
7449
|
+
return {
|
|
7450
|
+
proof,
|
|
7451
|
+
merkle_root: effectiveRoot,
|
|
7452
|
+
latest_cid: effectiveLatestCid
|
|
7453
|
+
};
|
|
7454
|
+
} catch (error) {
|
|
7455
|
+
if (this.mode === "local") {
|
|
7456
|
+
throw new SdkError("MERKLE", "Local merkle proof build failed", { chainId: input.chainId, cids }, error);
|
|
7457
|
+
}
|
|
7429
7458
|
}
|
|
7430
7459
|
}
|
|
7460
|
+
} else {
|
|
7461
|
+
if (this.mode === "local" && needsTreeProof.length) {
|
|
7462
|
+
throw new SdkError("MERKLE", "Local merkle db unavailable", { chainId: input.chainId, cids, reason: "missing_adapter_or_version" });
|
|
7463
|
+
}
|
|
7431
7464
|
}
|
|
7432
7465
|
} else if (this.mode === "local" && needsTreeProof.length) {
|
|
7433
|
-
throw new SdkError("MERKLE", "Local merkle db unavailable", { chainId: input.chainId, cids, reason: "
|
|
7466
|
+
throw new SdkError("MERKLE", "Local merkle db unavailable", { chainId: input.chainId, cids, reason: "missing_adapter" });
|
|
7434
7467
|
}
|
|
7435
7468
|
}
|
|
7436
7469
|
if (needsTreeProof.length === 0) {
|