@ickb/order
Version:
UDT Limit Order utilities built on top of CCC
235 lines • 7.26 kB
JavaScript
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