UNPKG

@ickb/order

Version:

UDT Limit Order utilities built on top of CCC

235 lines 7.26 kB
import { ccc } from "@ckb-ccc/core"; import { OrderData } from "./entities.js"; export class OrderCell { constructor(cell, data, ckbUnoccupied, absTotal, absProgress, maturity) { Object.defineProperty(this, "cell", { enumerable: true, configurable: true, writable: true, value: cell }); Object.defineProperty(this, "data", { enumerable: true, configurable: true, writable: true, value: data }); Object.defineProperty(this, "ckbUnoccupied", { enumerable: true, configurable: true, writable: true, value: ckbUnoccupied }); Object.defineProperty(this, "absTotal", { enumerable: true, configurable: true, writable: true, value: absTotal }); Object.defineProperty(this, "absProgress", { enumerable: true, configurable: true, writable: true, value: absProgress }); Object.defineProperty(this, "maturity", { enumerable: true, configurable: true, writable: true, value: maturity }); } get ckbValue() { return this.cell.cellOutput.capacity; } get udtValue() { return this.data.udtValue; } static tryFrom(cell) { try { return OrderCell.mustFrom(cell); } catch { return undefined; } } static mustFrom(cell) { const data = OrderData.decode(cell.outputData); data.validate(); const udtValue = data.udtValue; const ckbUnoccupied = cell.capacityFree; const { ckbToUdt, udtToCkb } = data.info; const isCkb2Udt = data.info.isCkb2Udt(); const isUdt2Ckb = data.info.isUdt2Ckb(); const isDualRatio = isCkb2Udt && isUdt2Ckb; const ckb2UdtValue = isCkb2Udt ? ckbUnoccupied * ckbToUdt.ckbScale + udtValue * ckbToUdt.udtScale : 0n; const udt2CkbValue = isUdt2Ckb ? ckbUnoccupied * udtToCkb.ckbScale + udtValue * udtToCkb.udtScale : 0n; const absTotal = ckb2UdtValue === 0n ? udt2CkbValue : udt2CkbValue === 0n ? ckb2UdtValue : (ckb2UdtValue * udtToCkb.ckbScale * udtToCkb.udtScale + udt2CkbValue * ckbToUdt.ckbScale * ckbToUdt.udtScale) >> 1n; const absProgress = isDualRatio ? absTotal : isCkb2Udt ? udtValue * ckbToUdt.udtScale : ckbUnoccupied * udtToCkb.ckbScale; const maturity = isDualRatio || absTotal !== absProgress ? undefined : 0n; return new OrderCell(cell, data, ckbUnoccupied, absTotal, absProgress, maturity); } isDualRatio() { return this.data.info.isDualRatio(); } isCkb2UdtMatchable() { return this.data.info.isCkb2Udt() && this.ckbUnoccupied > 0n; } isUdt2CkbMatchable() { return this.data.info.isUdt2Ckb() && this.data.udtValue > 0n; } isMatchable() { return this.isCkb2UdtMatchable() || this.isUdt2CkbMatchable(); } isFulfilled() { return !this.isMatchable(); } getMaster() { return this.data.getMaster(this.cell.outPoint); } validate(descendant) { if (this.cell.outPoint.eq(descendant.cell.outPoint)) { return; } if (!this.cell.cellOutput.lock.eq(descendant.cell.cellOutput.lock)) { throw Error("Order script different"); } const udt = this.cell.cellOutput.type; if (!udt || !descendant.cell.cellOutput.type?.eq(udt)) { throw Error("UDT type is different"); } if (!descendant.getMaster().eq(this.getMaster())) { throw Error("Master is different"); } if (!this.data.info.eq(this.data.info)) { throw Error("Info is different"); } if (this.absTotal > descendant.absTotal) { throw Error("Total value is lower than the original one"); } if (this.absProgress > descendant.absProgress) { throw Error("Progress is lower than the original one"); } } isValid(descendant) { try { this.validate(descendant); return true; } catch { return false; } } resolve(descendants) { let best = undefined; for (const descendant of descendants) { if (!this.isValid(descendant)) { continue; } if (!best || best.absProgress < descendant.absProgress || (best.absProgress === descendant.absProgress && !best.data.isMint())) { best = descendant; } } return best; } } export class MasterCell { constructor(cell) { Object.defineProperty(this, "cell", { enumerable: true, configurable: true, writable: true, value: cell }); Object.defineProperty(this, "udtValue", { enumerable: true, configurable: true, writable: true, value: 0n }); } static from(cellLike) { return new MasterCell(ccc.Cell.from(cellLike)); } validate(order) { if (!this.cell.cellOutput.type?.eq(order.cell.cellOutput.lock)) { throw Error("Order script different"); } if (!order.getMaster().eq(this.cell.outPoint)) { throw Error("Master is different"); } } get ckbValue() { return this.cell.cellOutput.capacity; } } export class OrderGroup { constructor(master, order, origin) { Object.defineProperty(this, "master", { enumerable: true, configurable: true, writable: true, value: master }); Object.defineProperty(this, "order", { enumerable: true, configurable: true, writable: true, value: order }); Object.defineProperty(this, "origin", { enumerable: true, configurable: true, writable: true, value: origin }); } static tryFrom(master, order, origin) { const og = new OrderGroup(master, order, origin); if (og.isValid()) { return og; } return undefined; } validate() { this.master.validate(this.order); this.origin.validate(this.order); } isValid() { try { this.validate(); return true; } catch { return false; } } isOwner(...locks) { const lock = this.master.cell.cellOutput.lock; return locks.some((l) => lock.eq(l)); } get ckbValue() { return (this.order.cell.cellOutput.capacity + this.master.cell.cellOutput.capacity); } get udtValue() { return this.order.data.udtValue; } } //# sourceMappingURL=cells.js.map