@ickb/sdk
Version:
iCKB SDK built on top of CCC
242 lines • 9.09 kB
JavaScript
import { ccc } from "@ckb-ccc/core";
import { collect, CapacityManager, SmartTransaction, binarySearch, hexFrom, getHeader, Epoch, } from "@ickb/utils";
import { convert, ICKB_DEPOSIT_CAP, ickbExchangeRatio, } from "@ickb/core";
import { Info, OrderManager, Ratio, } from "@ickb/order";
import { getConfig } from "./constants.js";
import { PoolSnapshot } from "./codec.js";
export class IckbSdk {
constructor(ownedOwner, ickbLogic, order, capacity, bots) {
Object.defineProperty(this, "ownedOwner", {
enumerable: true,
configurable: true,
writable: true,
value: ownedOwner
});
Object.defineProperty(this, "ickbLogic", {
enumerable: true,
configurable: true,
writable: true,
value: ickbLogic
});
Object.defineProperty(this, "order", {
enumerable: true,
configurable: true,
writable: true,
value: order
});
Object.defineProperty(this, "capacity", {
enumerable: true,
configurable: true,
writable: true,
value: capacity
});
Object.defineProperty(this, "bots", {
enumerable: true,
configurable: true,
writable: true,
value: bots
});
}
static from(...args) {
const { managers: { ownedOwner, logic, order, capacity }, bots, } = getConfig(...args);
return new IckbSdk(ownedOwner, logic, order, capacity, bots);
}
static estimate(isCkb2Udt, amounts, system, options) {
options = {
fee: 1n,
feeBase: 100000n,
...options,
};
const { convertedAmount, ckbFee, info } = OrderManager.convert(isCkb2Udt, system.exchangeRatio, amounts, options);
const maturity = ckbFee >= 10n * system.feeRate
? IckbSdk.maturity({ info, amounts }, system)
: undefined;
return { convertedAmount, ckbFee, info, maturity };
}
static maturity(o, system) {
const info = "info" in o ? o.info : o.data.info;
const amounts = "amounts" in o
? o.amounts
: { ckbValue: o.ckbUnoccupied, udtValue: o.udtValue };
if (info.isDualRatio()) {
return;
}
const isCkb2Udt = info.isCkb2Udt();
const amount = isCkb2Udt ? amounts.ckbValue : amounts.udtValue;
const ratio = isCkb2Udt ? info.ckbToUdt : info.udtToCkb;
if (amount === 0n) {
return 0n;
}
const { tip, exchangeRatio, orderPool, ckbAvailable, ckbMaturing } = system;
const b = new Info(ratio, ratio, 1);
let ckb = isCkb2Udt ? amount : 0n;
let udt = isCkb2Udt ? 0n : amount;
for (const o of orderPool) {
const a = o.data.info;
if (a.isCkb2Udt()) {
if (!isCkb2Udt || a.ckb2UdtCompare(b) < 0) {
ckb += o.ckbUnoccupied;
}
}
else {
if (isCkb2Udt || a.udt2CkbCompare(b) < 0) {
udt += o.udtValue;
}
}
}
ckb -= convert(false, udt, exchangeRatio);
let maturity = 10n * 60n * 1000n;
if (isCkb2Udt) {
if (ckb > 0n) {
maturity *= 1n + ckb / ccc.fixedPointFrom("200000");
}
return maturity + ("info" in o ? BigInt(Date.now()) : tip.timestamp);
}
ckb += ckbAvailable;
if (ckb >= 0) {
return maturity + ("info" in o ? BigInt(Date.now()) : tip.timestamp);
}
const ckbNeeded = -ckb;
const i = binarySearch(ckbMaturing.length, (n) => ckbMaturing[n].ckbCumulative >= ckbNeeded);
return ckbMaturing[i]?.maturity;
}
async request(tx, user, info, amounts) {
user =
"codeHash" in user
? user
: (await user.getRecommendedAddressObj()).script;
this.order.mint(tx, user, info, amounts);
}
collect(tx, groups, options) {
this.order.melt(tx, groups, options);
}
async getL1State(client, locks) {
const tip = await client.getTipHeader();
const exchangeRatio = Ratio.from(ickbExchangeRatio(tip));
const [{ ckbAvailable, ckbMaturing }, orders, feeRate] = await Promise.all([
this.getCkb(client, tip),
collect(this.order.findOrders(client)),
client.getFeeRate(),
]);
const midInfo = new Info(exchangeRatio, exchangeRatio, 1);
const userOrders = [];
const systemOrders = [];
for (const group of orders) {
if (group.isOwner(...locks)) {
userOrders.push(group);
}
const { order } = group;
const info = order.data.info;
if ((order.isCkb2UdtMatchable() && info.ckb2UdtCompare(midInfo) < 0) ||
(order.isUdt2CkbMatchable() && info.udt2CkbCompare(midInfo) < 0)) {
systemOrders.push(order);
}
}
const system = {
feeRate,
tip,
exchangeRatio,
orderPool: systemOrders,
ckbAvailable,
ckbMaturing,
};
for (const { order } of userOrders) {
order.maturity = IckbSdk.maturity(order, system);
}
return {
system,
user: {
orders: userOrders,
},
};
}
async getCkb(client, tip) {
const opts = {
onChain: true,
tip,
};
const promiseBotWithdrawals = collect(this.ownedOwner.findWithdrawalGroups(client, this.bots, opts));
let poolSnapshotHex = "0x";
let poolSnapshotEpoch = Epoch.from([0n, 0n, 1n]);
const bot2Ckb = new Map();
const reserved = -ccc.fixedPointFrom("2000");
for await (const c of this.capacity.findCapacities(client, this.bots, opts)) {
const key = hexFrom(c.cell.cellOutput.lock);
const ckb = (bot2Ckb.get(key) ?? reserved) + c.ckbValue;
bot2Ckb.set(key, ckb);
const outputData = c.cell.outputData;
if (outputData.length % 256 === 2) {
const h = await getHeader(client, {
type: "txHash",
value: c.cell.outPoint.txHash,
});
const e = Epoch.from(h.epoch);
if (poolSnapshotEpoch.compare(e) < 0) {
poolSnapshotHex = outputData;
poolSnapshotEpoch = e;
}
}
}
const ckbMaturing = new Array();
for (const wr of await promiseBotWithdrawals) {
if (wr.owned.isReady) {
const key = hexFrom(wr.owner.cell.cellOutput.lock);
const ckb = (bot2Ckb.get(key) ?? reserved) + wr.ckbValue;
bot2Ckb.set(key, ckb);
continue;
}
ckbMaturing.push({
ckbValue: wr.ckbValue,
maturity: wr.owned.maturity.toUnix(tip),
});
}
let ckbAvailable = 0n;
for (const ckb of bot2Ckb.values()) {
if (ckb > 0n) {
ckbAvailable += ckb;
}
}
const tipEpoch = Epoch.from(tip.epoch);
const oneCycle = Epoch.from([180n, 0n, 1n]);
if (poolSnapshotHex !== "0x") {
const eNumber = tip.epoch[0];
let start = Epoch.from([eNumber - (eNumber % 180n), 0n, 1n]);
const step = Epoch.from([0n, 180n, 1024n]);
const depositSize = convert(false, ICKB_DEPOSIT_CAP, tip);
for (const binAmount of PoolSnapshot.decode(poolSnapshotHex)) {
const end = start.add(step);
if (binAmount > 0) {
ckbMaturing.push({
ckbValue: BigInt(binAmount) * depositSize,
maturity: tipEpoch.compare(tipEpoch) < 0
?
end.add(oneCycle).toUnix(tip)
:
end.toUnix(tip),
});
}
start = end;
}
}
else {
for await (const d of this.ickbLogic.findDeposits(client, opts)) {
ckbMaturing.push({
ckbValue: d.ckbValue,
maturity: d.maturity.toUnix(tip),
});
}
}
ckbMaturing.sort((a, b) => Number(a.maturity - b.maturity));
let cumulative = 0n;
const ckbCumulativeMaturing = [];
for (const { ckbValue, maturity } of ckbMaturing) {
cumulative += ckbValue;
ckbCumulativeMaturing.push({ ckbCumulative: cumulative, maturity });
}
return {
ckbAvailable,
ckbMaturing: ckbCumulativeMaturing,
};
}
}
//# sourceMappingURL=sdk.js.map