UNPKG

@ickb/sdk

Version:

iCKB SDK built on top of CCC

242 lines 9.09 kB
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