UNPKG

@ickb/dao

Version:

NervosDAO utilities built on top of CCC

177 lines 6.78 kB
import { ccc, mol } from "@ckb-ccc/core"; import { defaultFindCellsLimit, Epoch, unique, } from "@ickb/utils"; import { daoCellFrom } from "./cells.js"; export class DaoManager { constructor(script, cellDeps) { Object.defineProperty(this, "script", { enumerable: true, configurable: true, writable: true, value: script }); Object.defineProperty(this, "cellDeps", { enumerable: true, configurable: true, writable: true, value: cellDeps }); } isDeposit(cell) { const { cellOutput: { type }, outputData, } = cell; return (outputData === DaoManager.depositData() && type?.eq(this.script) === true); } isWithdrawalRequest(cell) { const { cellOutput: { type }, outputData, } = cell; return (outputData !== DaoManager.depositData() && type?.eq(this.script) === true); } static depositData() { return "0x0000000000000000"; } deposit(tx, capacities, lock) { if (capacities.length === 0) { return; } tx.addCellDeps(this.cellDeps); for (const capacity of capacities) { tx.addOutput({ capacity, lock, type: this.script, }, DaoManager.depositData()); } if (tx.outputs.length > 64) { throw Error("More than 64 output cells in a NervosDAO transaction"); } } requestWithdrawal(tx, deposits, lock, options) { const sameSizeOnly = options?.sameSizeOnly ?? true; const isReadyOnly = options?.isReadyOnly ?? false; if (isReadyOnly) { deposits = deposits.filter((d) => d.isReady); } if (deposits.length === 0) { return; } if (tx.inputs.length != tx.outputs.length || tx.outputs.length != tx.outputsData.length) { throw Error("Transaction have different inputs and outputs lengths"); } for (const deposit of deposits) { const { cell, isDeposit, headers } = deposit; if (!isDeposit) { throw Error("Not a deposit"); } if (sameSizeOnly && cell.cellOutput.lock.args.length != lock.args.length) { throw Error("Withdrawal request lock args has different size from deposit"); } tx.addCellDeps(this.cellDeps); const depositHeader = headers[0]; tx.addHeaders(depositHeader); tx.addInput(cell); tx.addOutput({ capacity: cell.cellOutput.capacity, lock, type: this.script, }, mol.Uint64LE.encode(depositHeader.header.number)); } if (tx.outputs.length > 64) { throw Error("More than 64 output cells in a NervosDAO transaction"); } } withdraw(tx, withdrawalRequests, options) { const isReadyOnly = options?.isReadyOnly ?? false; if (isReadyOnly) { withdrawalRequests = withdrawalRequests.filter((d) => d.isReady); } if (withdrawalRequests.length === 0) { return; } tx.addCellDeps(this.cellDeps); for (const withdrawalRequest of withdrawalRequests) { const { cell: { outPoint, cellOutput, outputData }, isDeposit, headers, maturity, } = withdrawalRequest; if (isDeposit) { throw Error("Not a withdrawal request"); } tx.addHeaders(headers); const depositHeader = headers[0]; const headerIndex = tx.headerDeps.findIndex((h) => h === depositHeader.header.hash); const inputIndex = tx.addInput({ outPoint, cellOutput, outputData, since: { relative: "absolute", metric: "epoch", value: maturity.toHex(), }, }) - 1; const witness = tx.getWitnessArgsAt(inputIndex) ?? ccc.WitnessArgs.from({}); if (witness.inputType) { throw Error("Witnesses of withdrawal request already in use"); } witness.inputType = ccc.hexFrom(ccc.numLeToBytes(headerIndex, 8)); tx.setWitnessArgsAt(inputIndex, witness); } if (tx.outputs.length > 64) { throw Error("More than 64 output cells in a NervosDAO transaction"); } } async *findDeposits(client, locks, options) { const tip = options?.tip ?? (await client.getTipHeader()); const limit = options?.limit ?? defaultFindCellsLimit; for (const lock of unique(locks)) { const findCellsArgs = [ { script: lock, scriptType: "lock", filter: { script: this.script, outputData: DaoManager.depositData(), outputDataSearchMode: "exact", }, scriptSearchMode: "exact", withData: true, }, "asc", limit, ]; for await (const cell of options?.onChain ? client.findCellsOnChain(...findCellsArgs) : client.findCells(...findCellsArgs)) { if (!this.isDeposit(cell) || !cell.cellOutput.lock.eq(lock)) { continue; } yield daoCellFrom({ cell, ...options, isDeposit: true, client, tip }); } } } async *findWithdrawalRequests(client, locks, options) { const tip = options?.tip ?? (await client.getTipHeader()); const limit = options?.limit ?? defaultFindCellsLimit; for (const lock of unique(locks)) { const findCellsArgs = [ { script: lock, scriptType: "lock", filter: { script: this.script, }, scriptSearchMode: "exact", withData: true, }, "asc", limit, ]; for await (const cell of options?.onChain ? client.findCellsOnChain(...findCellsArgs) : client.findCells(...findCellsArgs)) { if (!this.isWithdrawalRequest(cell) || !cell.cellOutput.lock.eq(lock)) { continue; } yield daoCellFrom({ cell, ...options, isDeposit: false, client, tip }); } } } } //# sourceMappingURL=dao.js.map