@ickb/dao
Version:
NervosDAO utilities built on top of CCC
177 lines • 6.78 kB
JavaScript
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