+
DAO (Claimed)
+
diff --git a/src/lib/lumos-adapter/create-lumos-ckb-builder.js b/src/lib/lumos-adapter/create-lumos-ckb-builder.js
index 67289ef..6acea5b 100644
--- a/src/lib/lumos-adapter/create-lumos-ckb-builder.js
+++ b/src/lib/lumos-adapter/create-lumos-ckb-builder.js
@@ -49,13 +49,13 @@ export default function createLumosCkbBuilder(config) {
withdrawDao: async function ({ cell }) {
return await createBuildingPacketByCreator(dao, "withdraw", {
- previousOutput: cell.outPoint,
+ cellPointer: cell.outPoint,
});
},
claimDao: async function ({ cell }) {
return await createBuildingPacketByCreator(dao, "claim", {
- previousOutput: cell.outPoint,
+ cellPointer: cell.outPoint,
});
},
};
diff --git a/src/lib/papps/dao/__tests__/schema.test.js b/src/lib/papps/dao/__tests__/schema.test.js
index dfda0d2..b3e735d 100644
--- a/src/lib/papps/dao/__tests__/schema.test.js
+++ b/src/lib/papps/dao/__tests__/schema.test.js
@@ -3,31 +3,60 @@ import { DaoActionData } from "../schema";
test("pack/unpack", () => {
const input = {
- type: "MultipleOperations",
- value: [
+ deposits: [
{
- type: "Deposit",
- value: {
- lock: {
- codeHash: `0x01${"0".repeat(62)}`,
- hashType: "data",
- args: "0x02",
- },
- capacity: BI.from(300),
+ from: {
+ codeHash: `0x01${"0".repeat(62)}`,
+ hashType: "data",
+ args: "0x02",
+ },
+ to: {
+ codeHash: `0x03${"0".repeat(62)}`,
+ hashType: "data",
+ args: "0x04",
+ },
+ amount: {
+ shannons: BI.from(500),
},
},
+ ],
+ claims: [
{
- type: "ClaimTo",
- value: {
- previousOutput: {
- txHash: `0x04${"0".repeat(62)}`,
- index: 5,
+ cellPointer: {
+ txHash: `0x06${"0".repeat(62)}`,
+ index: 7,
+ },
+ from: {
+ codeHash: `0x08${"0".repeat(62)}`,
+ hashType: "data",
+ args: "0x09",
+ },
+ to: {
+ codeHash: `0x0a${"0".repeat(62)}`,
+ hashType: "data",
+ args: "0x0b",
+ },
+ depositInfo: {
+ depositBlockNumber: BI.from("0x0c"),
+ depositTimestamp: {
+ unixMilliseconds: BI.from("0x0d"),
+ },
+ amount: {
+ shannons: BI.from("0x0e"),
+ },
+ },
+ withdrawInfo: {
+ withdrawBlockNumber: BI.from("0x0f"),
+ withdrawTimestamp: {
+ unixMilliseconds: BI.from("0x10"),
+ },
+ componsationAmount: {
+ shannons: BI.from("0x11"),
},
- totalClaimedCapacity: BI.from(600),
- to: undefined,
},
},
],
+ withdraws: [],
};
const unpacked = DaoActionData.unpack(DaoActionData.pack(input));
diff --git a/src/lib/papps/dao/action-creators.js b/src/lib/papps/dao/action-creators.js
index 85efc5e..f8a8c81 100644
--- a/src/lib/papps/dao/action-creators.js
+++ b/src/lib/papps/dao/action-creators.js
@@ -21,74 +21,53 @@ function buildSingleOperation(operation) {
export function depositWithFormData(config, formData) {
const lumosOptions = { config: config.ckbChainConfig };
- const lock = addressToScript(formData.get("lock"), lumosOptions);
- const capacity = parseUnit(formData.get("capacity"), "ckb");
- const from = addressToScriptOpt(formData.get("from"), lumosOptions);
- return deposit({ lock, capacity, from }, config);
+ const from = addressToScript(formData.get("from"), lumosOptions);
+ const amount = { shannons: parseUnit(formData.get("amount"), "ckb") };
+ return deposit({ from, amount, to: from }, config);
}
-export function deposit({ lock, capacity, from = undefined }) {
- return buildSingleOperation(
- from === null || from === undefined
- ? {
- type: "Deposit",
- value: { lock, capacity },
- }
- : {
- type: "DepositFrom",
- value: { lock, capacity, from },
- },
- );
+export function deposit({ from, to, amount }) {
+ return {
+ deposits: [{ from, to, amount }],
+ withdraws: [],
+ claims: [],
+ };
}
export function withdrawWithFormData(config, formData) {
const lumosOptions = { config: config.ckbChainConfig };
- const previousOutput = {
- txHash: formData.get("previousOutput.txHash"),
- index: parseInt(formData.get("previousOutput.index"), 10),
+ const cellPointer = {
+ txHash: formData.get("cellPointer.txHash"),
+ index: parseInt(formData.get("cellPointer.index"), 10),
};
const to = addressToScriptOpt(formData.get("to"), lumosOptions);
- return withdraw({ previousOutput, to }, config);
+ return withdraw({ cellPointer, to }, config);
}
-export function withdraw({ previousOutput, to = undefined }) {
- return buildSingleOperation(
- to === null || to === undefined
- ? {
- type: "Withdraw",
- value: { previousOutput },
- }
- : {
- type: "WithdrawTo",
- value: { previousOutput, to },
- },
- );
+export function withdraw({ cellPointer, to = undefined }) {
+ return {
+ deposits: [],
+ withdraws: [{ cellPointer, to }],
+ claims: [],
+ };
}
export function claimWithFormData(config, formData) {
const lumosOptions = { config: config.ckbChainConfig };
- const previousOutput = {
- txHash: formData.get("previousOutput.txHash"),
- index: parseInt(formData.get("previousOutput.index"), 10),
+ const cellPointer = {
+ txHash: formData.get("cellPointer.txHash"),
+ index: parseInt(formData.get("cellPointer.index"), 10),
};
const to = addressToScriptOpt(formData.get("to"), lumosOptions);
- return claim({ previousOutput, to }, config);
+ return claim({ cellPointer, to }, config);
}
-export function claim({ previousOutput, to = undefined }) {
- // Set a dummy value, which will be set by the script later.
- const totalClaimedCapacity = 0;
- return buildSingleOperation(
- to === null || to === undefined
- ? {
- type: "Claim",
- value: { previousOutput, totalClaimedCapacity },
- }
- : {
- type: "ClaimTo",
- value: { previousOutput, totalClaimedCapacity, to },
- },
- );
+export function claim({ cellPointer, to = undefined }) {
+ return {
+ deposits: [],
+ withdraws: [],
+ claims: [{ cellPointer, to }],
+ };
}
diff --git a/src/lib/papps/dao/lumos-callbacks.js b/src/lib/papps/dao/lumos-callbacks.js
index 7ba9fe2..792440d 100644
--- a/src/lib/papps/dao/lumos-callbacks.js
+++ b/src/lib/papps/dao/lumos-callbacks.js
@@ -1,6 +1,8 @@
import { RPC } from "@ckb-lumos/rpc";
+import { BI } from "@ckb-lumos/bi";
import { common as commonScripts, dao } from "@ckb-lumos/common-scripts";
-import { bytes } from "@ckb-lumos/codec";
+import { blockchain } from "@ckb-lumos/base";
+import { bytes, number } from "@ckb-lumos/codec";
import * as lumosHelpers from "@ckb-lumos/helpers";
import { getCellWithoutCache } from "@/actions/get-cell";
@@ -50,22 +52,62 @@ function pushDistinctBy(list, items, eq) {
return newItems.length > 0 ? list.push(...newItems) : list;
}
+const DAO_CYCLE = BI.from(180);
+// 4 hours
+const ESITMATED_EPOCH_DURATION = BI.from(4 * 60 * 60 * 1000);
+
+function parseEpoch(epoch) {
+ return {
+ length: epoch.shr(40).and(0xfff),
+ index: epoch.shr(24).and(0xfff),
+ number: epoch.and(0xffffff),
+ };
+}
+
+function computeWaitingMilliseconds(from, to) {
+ let fromEpoch = parseEpoch(BI.from(from.epoch));
+ let toEpoch = parseEpoch(BI.from(to.epoch));
+
+ let fromEpochPassedDuration = fromEpoch.index
+ .mul(ESITMATED_EPOCH_DURATION)
+ .div(fromEpoch.length);
+ let toEpochPassedDuration = toEpoch.index
+ .mul(ESITMATED_EPOCH_DURATION)
+ .div(toEpoch.length);
+
+ // find next cycle
+ let remainingEpochsDraft = DAO_CYCLE.sub(
+ toEpoch.number.sub(fromEpoch.number).mod(DAO_CYCLE),
+ );
+ let remainingEpochs =
+ remainingEpochsDraft.eq(DAO_CYCLE) &&
+ toEpoch.number.gt(fromEpoch.number) &&
+ fromEpochPassedDuration.gte(toEpochPassedDuration)
+ ? BI.from(0)
+ : remainingEpochsDraft;
+
+ return remainingEpochs
+ .mul(ESITMATED_EPOCH_DURATION)
+ .add(fromEpochPassedDuration)
+ .sub(toEpochPassedDuration)
+ .toHexString();
+}
+
async function onDeposit({ ckbChainConfig }, txSkeleton, op) {
- const { lock, capacity } = op;
- const from = op.from ?? lock;
+ const { from, to, amount } = op;
const fromAddress = lumosHelpers.encodeToAddress(from, {
config: ckbChainConfig,
});
- const lockAddress = lumosHelpers.encodeToAddress(lock, {
+ const toAddress = lumosHelpers.encodeToAddress(to, {
config: ckbChainConfig,
});
txSkeleton = await dao.deposit(
txSkeleton,
fromAddress,
- lockAddress,
- capacity,
+ toAddress,
+ amount.shannons,
{
config: ckbChainConfig,
},
@@ -75,7 +117,7 @@ async function onDeposit({ ckbChainConfig }, txSkeleton, op) {
txSkeleton = await commonScripts.injectCapacity(
txSkeleton,
[fromAddress],
- capacity,
+ amount.shannons,
fromAddress,
undefined,
{ config: ckbChainConfig },
@@ -85,8 +127,19 @@ async function onDeposit({ ckbChainConfig }, txSkeleton, op) {
}
async function onWithdraw({ ckbChainConfig, ckbRpcUrl }, txSkeleton, op) {
- const { previousOutput } = op;
- const cell = await getCellWithoutCache(previousOutput, { ckbRpcUrl });
+ const { cellPointer } = op;
+ const rpc = new RPC(ckbRpcUrl);
+
+ const cell = await getCellWithoutCache(cellPointer, { ckbRpcUrl });
+ const depositHeader = await rpc.getHeader(cell.blockHash);
+ const safeWithdrawBlockNumber = BI.from(await rpc.getTipBlockNumber()).sub(
+ 24,
+ );
+ const estimatedWithdrawHeader = safeWithdrawBlockNumber.gt(
+ BI.from(depositHeader.number),
+ )
+ ? await rpc.getHeaderByNumber(safeWithdrawBlockNumber.toHexString())
+ : depositHeader;
const from = cell.cellOutput.lock;
const fromAddress = lumosHelpers.encodeToAddress(from, {
@@ -119,23 +172,84 @@ async function onWithdraw({ ckbChainConfig, ckbRpcUrl }, txSkeleton, op) {
config: ckbChainConfig,
});
- return [txSkeleton, op];
+ // Add header dep for withdraw estimation
+ while (txSkeleton.get("witnesses").size < txSkeleton.get("inputs").size - 1) {
+ txSkeleton = txSkeleton.update("witnesses", (witnesses) =>
+ witnesses.push("0x"),
+ );
+ }
+ // add header deps
+ txSkeleton = txSkeleton.update("headerDeps", (headerDeps) =>
+ addDistinctHeaderDep(headerDeps, estimatedWithdrawHeader.hash),
+ );
+ const estimatedWithdrawHeaderDepIndex = txSkeleton.get("headerDeps").size - 1;
+ txSkeleton = txSkeleton.updateIn(["witnesses", 0], (witness) => {
+ let unpackedWitnessArgs =
+ witness === "0x" ? {} : blockchain.WitnessArgs.unpack(witness);
+ let newWitnessArgs = {
+ ...unpackedWitnessArgs,
+ inputType: bytes.hexify(
+ number.Uint64LE.pack(estimatedWithdrawHeaderDepIndex),
+ ),
+ };
+ return bytes.hexify(blockchain.WitnessArgs.pack(newWitnessArgs));
+ });
+
+ return [
+ txSkeleton,
+ {
+ ...op,
+ from,
+ to: op.to ?? from,
+ depositInfo: {
+ amount: { shannons: cell.cellOutput.capacity },
+ depositBlockNumber: depositHeader.number,
+ depositTimestamp: {
+ unixMilliseconds: depositHeader.timestamp,
+ },
+ },
+ estimatedWithdrawInfo: {
+ waitingMilliseconds: computeWaitingMilliseconds(
+ depositHeader,
+ estimatedWithdrawHeader,
+ ),
+ withdrawInfo: {
+ withdrawBlockNumber: estimatedWithdrawHeader.number,
+ withdrawTimestamp: {
+ unixMilliseconds: estimatedWithdrawHeader.timestamp,
+ },
+ componsationAmount: {
+ shannons: BI.from(
+ dao.calculateMaximumWithdraw(
+ cell,
+ depositHeader.dao,
+ estimatedWithdrawHeader.dao,
+ ),
+ )
+ .sub(cell.cellOutput.capacity)
+ .toHexString(),
+ },
+ },
+ },
+ },
+ ];
}
async function onClaim({ ckbRpcUrl, ckbChainConfig }, txSkeleton, op) {
- const { previousOutput } = op;
+ const { cellPointer } = op;
const rpc = new RPC(ckbRpcUrl);
- const cell = await getCellWithoutCache(previousOutput, { ckbRpcUrl });
+ const cell = await getCellWithoutCache(cellPointer, { ckbRpcUrl });
const depositBlockNumber = getDepositBlockNumberFromWithdrawCell(cell);
const depositBlockHash = await rpc.getBlockHash(depositBlockNumber);
const depositHeader = await rpc.getHeader(depositBlockHash);
const withdrawHeader = await rpc.getHeader(cell.blockHash);
- const fromAddress = lumosHelpers.encodeToAddress(cell.cellOutput.lock, {
+ const from = cell.cellOutput.lock;
+ const fromAddress = lumosHelpers.encodeToAddress(from, {
config: ckbChainConfig,
});
- const to = op.to ?? cell.cellOutput.lock;
+ const to = op.to ?? from;
// add input
const since =
@@ -193,72 +307,88 @@ async function onClaim({ ckbRpcUrl, ckbChainConfig }, txSkeleton, op) {
() => packDaoWitnessArgs(depositBlockPos),
);
- return [txSkeletonMutable.asImmutable(), op];
+ return [
+ txSkeletonMutable.asImmutable(),
+ {
+ ...op,
+ from,
+ to,
+ depositInfo: {
+ amount: { shannons: cell.cellOutput.capacity },
+ depositBlockNumber: depositHeader.number,
+ depositTimestamp: {
+ unixMilliseconds: depositHeader.timestamp,
+ },
+ },
+ withdrawInfo: {
+ withdrawBlockNumber: withdrawHeader.number,
+ withdrawTimestamp: {
+ unixMilliseconds: withdrawHeader.timestamp,
+ },
+ componsationAmount: {
+ shannons: BI.from(
+ dao.calculateMaximumWithdraw(
+ cell,
+ depositHeader.dao,
+ withdrawHeader.dao,
+ ),
+ )
+ .sub(cell.cellOutput.capacity)
+ .toHexString(),
+ },
+ },
+ },
+ ];
}
-const handlers = {
- Deposit: onDeposit,
- DepositFrom: onDeposit,
- Withdraw: onWithdraw,
- WithdrawTo: onWithdraw,
- Claim: onClaim,
- ClaimTo: onClaim,
-};
-
-export async function addOperations(config, buildingPacket, operations) {
+export async function willAddAction(config, buildingPacket, actionData) {
let txSkeleton = createSkeletonFromBuildingPacket(buildingPacket, config);
- const newOperations = Array(operations.length);
- for (const [i, op] of operations.entries()) {
- const [newTxSkeleton, newOpValue] = await handlers[op.type](
+ const newActionData = { deposits: [], withdraws: [], claims: [] };
+ for (const [i, deposit] of actionData.deposits.entries()) {
+ const [newTxSkeleton, newDeposit] = await onDeposit(
config,
txSkeleton,
- op.value,
+ deposit,
);
-
txSkeleton = newTxSkeleton;
- newOperations[i] = { type: op.type, value: newOpValue };
+ newActionData.deposits[i] = newDeposit;
+ }
+ for (const [i, withdraw] of actionData.withdraws.entries()) {
+ const [newTxSkeleton, newWithdraw] = await onWithdraw(
+ config,
+ txSkeleton,
+ withdraw,
+ );
+ txSkeleton = newTxSkeleton;
+ newActionData.withdraws[i] = newWithdraw;
+ }
+ for (const [i, claim] of actionData.claims.entries()) {
+ const [newTxSkeleton, newClaim] = await onClaim(config, txSkeleton, claim);
+ txSkeleton = newTxSkeleton;
+ newActionData.claims[i] = newClaim;
}
- return [
- mergeBuildingPacketFromSkeleton(buildingPacket, txSkeleton),
- newOperations,
- ];
-}
-
-export async function willAddAction(config, buildingPacket, actionData) {
- const ops =
- actionData.type === "SingleOperation"
- ? [actionData.value]
- : actionData.value;
-
- const [newBuildingPacket, newOps] = await addOperations(
- config,
+ const newBuildingPacket = mergeBuildingPacketFromSkeleton(
buildingPacket,
- ops,
+ txSkeleton,
);
- buildingPacket = newBuildingPacket;
const action = {
scriptHash: getDaoScriptHash(config),
scriptInfoHash: getDaoScriptInfoHash(config),
- data: bytes.hexify(
- DaoActionData.pack({
- type: actionData.type,
- value: actionData.type === "SingleOperation" ? newOps[0] : newOps,
- }),
- ),
+ data: bytes.hexify(DaoActionData.pack(newActionData)),
};
const scriptInfo = getDaoScriptInfo(config);
return {
- type: buildingPacket.type,
+ type: newBuildingPacket.type,
value: {
- ...buildingPacket.value,
+ ...newBuildingPacket.value,
message: {
- actions: [...buildingPacket.value.message.actions, action],
+ actions: [...newBuildingPacket.value.message.actions, action],
},
- scriptInfos: [...buildingPacket.value.scriptInfos, scriptInfo],
+ scriptInfos: [...newBuildingPacket.value.scriptInfos, scriptInfo],
},
};
}
diff --git a/src/lib/papps/dao/schema.js b/src/lib/papps/dao/schema.js
index 8df3635..bc31deb 100644
--- a/src/lib/papps/dao/schema.js
+++ b/src/lib/papps/dao/schema.js
@@ -1,235 +1,173 @@
import { blockchain } from "@ckb-lumos/base";
import { molecule, number } from "@ckb-lumos/codec";
const { Uint64 } = number;
-const { table, vector, union } = molecule;
-const { Script, ScriptOpt, OutPoint } = blockchain;
+const { struct, table, vector, option } = molecule;
+const { Script, OutPoint } = blockchain;
const Address = Script;
-const AddressOpt = ScriptOpt;
export const MESSAGE_TYPE = "DaoActionData";
export const SCHEMA = `
array Uint32 [byte; 4];
array Uint64 [byte; 8];
+
array Byte32 [byte; 32];
vector Bytes ;
+struct Capacity {
+ shannons: Uint64,
+}
+struct Timestamp {
+ unix_milliseconds: Uint64,
+}
+
table Address {
- code_hash: Byte32,
- hash_type: byte,
- args: Bytes,
+ code_hash: Byte32,
+ hash_type: byte,
+ args: Bytes,
}
-option AddressOpt (Address);
+
struct OutPoint {
- tx_hash: Byte32,
- index: Uint32,
+ tx_hash: Byte32,
+ index: Uint32,
}
-table Deposit {
- lock: Address,
- capacity: Uint64,
-}
-table DepositFrom {
- lock: Address,
- capacity: Uint64,
- from: AddressOpt,
+struct DepositInfo {
+ amount: Capacity,
+ deposit_block_number: Uint64,
+ deposit_timestamp: Timestamp,
}
-table Withdraw {
- previous_output: OutPoint,
+
+struct WithdrawInfo {
+ withdraw_block_number: Uint64,
+ withdraw_timestamp: Timestamp,
+ componsation_amount: Capacity,
}
-table WithdrawTo {
- previous_output: OutPoint,
- to: AddressOpt,
+
+struct EstimatedWithdrawInfo {
+ waiting_milliseconds: Uint64,
+ withdraw_info: WithdrawInfo,
}
-table Claim {
- previous_output: OutPoint,
- total_claimed_capacity: Uint64,
+
+option EstimatedWithdrawInfoOpt (EstimatedWithdrawInfo);
+
+table Deposit {
+ from: Address,
+ to: Address,
+ amount: Capacity,
}
-table ClaimTo {
- previous_output: OutPoint,
- total_claimed_capacity: Uint64,
- to: AddressOpt,
+
+table Withdraw {
+ cell_pointer: OutPoint,
+ from: Address,
+ to: Address,
+ deposit_info: DepositInfo,
+ estimated_withdraw_info: EstimatedWithdrawInfoOpt,
}
-union SingleOperation {
- Deposit,
- DepositFrom,
- Withdraw,
- WithdrawTo,
- Claim,
- ClaimTo,
+table Claim {
+ cell_pointer: OutPoint,
+ from: Address,
+ to: Address,
+ deposit_info: DepositInfo,
+ withdraw_info: WithdrawInfo,
}
-vector MultipleOperations ;
-union DaoActionData {
- SingleOperation,
- MultipleOperations,
+vector DepositVec ;
+vector WithdrawVec ;
+vector ClaimVec ;
+
+table DaoActionData {
+ deposits: DepositVec,
+ withdraws: WithdrawVec,
+ claims: ClaimVec,
}
`;
-// Create a DAO deposit cell with the specific lock script and capacity. The ckb tokens come from the same lock script.
-//
-// This action is a shortcut of `DepositFrom` which `from` is the same as `lock`. See more details in the docs of `DepositFrom`.
-export const Deposit = table(
+export const Capacity = struct(
{
- lock: Address,
- capacity: Uint64,
+ shannons: Uint64,
},
- ["lock", "capacity"],
+ ["shannons"],
);
-// Create a DAO deposit cell with the specific lock script and capacity. The ckb tokens come from the lock script `from`.
-//
-// ## Builder
-//
-// If `from` is not `None`, the builder MUST add input cells that have the lock script `from`, empty type script and empty data. The total capacity of added input cells MUST be equal to or larger then `capacity`. If the total capacity is larger than `capacity`, the extra CKB tokens MUST be added to the change output. The builder MUST create the change output if there's no one set yet and the extra CKB tokens are enough to create the output.
-//
-// If `from` is `None`, the builder SHOULD NOT add inputs for this deposit.
-//
-// The builder MUST add the deposit cell as a new output cell with the cell data required by DAO rules.
-//
-// The builder MUST add cell deps for DAO but MAY not for the lock script.
-//
-// ## Verifier
-//
-// If `from` is `None`, the DAO tx verifier will not verify whether this deposit has been fully funded by a specific lock script. Otherwise see the section Capacity Verification in the docs of `DaoActionData`.
-//
-// The verifier MUST check the deposit cell exists in the outputs.
-export const DepositFrom = table(
+export const Timestamp = struct(
{
- lock: Address,
- capacity: Uint64,
- from: AddressOpt,
+ unixMilliseconds: Uint64,
},
- ["lock", "capacity", "from"],
+ ["unixMilliseconds"],
);
-// Create a DAO withdraw cell from the deposit cell referenced by `previousOutput`. The withdraw cell has the same lock script as the deposit cell.
-//
-// This action is a shortcut of `WithdrawTo` which `to` is the lock script of the deposit cell.
-//
-// Because of the rules of the DAO, the deposit cell must be at the same position in the outputs as the corresponding withdraw cell in the inputs.
-export const Withdraw = table(
+export const DepositInfo = table(
{
- previousOutput: OutPoint,
+ amount: Capacity,
+ depositBlockNumber: Uint64,
+ depositTimestamp: Timestamp,
},
- ["previousOutput"],
+ ["amount", "depositBlockNumber", "depositTimestamp"],
);
-// Create a DAO withdraw cell from the deposit cell referenced by `previousOutput`. The withdraw cell has the lock script `to`.
-//
-// Because of the rules of the DAO, the deposit cell must be at the same position in the outputs as the corresponding withdraw cell in the inputs and the lock script `to` must have the same size as the deposit cell lock script. In other words, their args fields must have the same length.
-//
-// ## Builder
-//
-// The builder MUST add the deposit cell as an input, and the withdraw cell as a output. The withdraw cell MUST have the lock script `to` and the same `capacity` as the deposit cell.
-//
-// The builder MUST abort if the existing transaction does not have the same number of inputs and outputs.
-//
-// The builder MUST set the withdraw cell data to the depositing block number, and add the depositing block hash to header deps.
-//
-// The builder MUST add cell deps for DAO but MAY not for the lock script.
-//
-// ## Verifier
-//
-// The verifier MUST check the deposit cell exists in the inputs, and the withdraw cell is at the same position in the outputs.
-export const WithdrawTo = table(
+export const WithdrawInfo = struct(
{
- previousOutput: OutPoint,
- to: Address,
+ withdrawBlockNumber: Uint64,
+ withdrawTimestamp: Timestamp,
+ componsationAmount: Capacity,
},
- ["previousOutput", "to"],
+ ["withdrawBlockNumber", "withdrawTimestamp", "componsationAmount"],
);
-// Claim locked CKB tokens and the DAO compensation from the DAO withdraw cell referenced by `previousOutput`. The recipient lock script is the same as the withdraw cell.
-//
-// This action is a shortcut of `ClaimTo` which `to` is the lock script of the withdraw cell. See more details in the docs of `ClaimTo`.
-export const Claim = table(
+export const EstimatedWithdrawInfo = struct(
+ {
+ waitingMilliseconds: Uint64,
+ withdrawInfo: WithdrawInfo,
+ },
+ ["waitingMilliseconds", "withdrawInfo"],
+);
+
+export const EstimatedWithdrawInfoOpt = option(EstimatedWithdrawInfo);
+
+export const Deposit = table(
{
- previousOutput: OutPoint,
- // The sum of the locked CKB tokens and the DAO compensation.
- totalClaimedCapacity: Uint64,
+ from: Address,
+ to: Address,
+ amount: Capacity,
},
- ["previousOutput", "totalClaimedCapacity"],
+ ["from", "to", "amount"],
);
-// Claim locked CKB tokens and the DAO compensation from the DAO withdraw cell referenced by `previousOutput`. The recipient lock script is `to`.
-//
-// ## Builder
-//
-// The builder MUST add the withdraw cell as an input and set since to the earliest allowed epoch with fraction.
-//
-// If `to` is not `None`, the builder MUST create an output to receive all the claimed CKB tokens. The output MUST have empty type script and empty cell data. The output MUST be set as the change output if there's one set yet.
-//
-// If `to` is `None`, the builder MUST NOT create outputs to receive the claimed CKB tokens.
-//
-// The builder MUST add the depositing block hash and the withdrawing block hash to header deps. It MUST set the witness at the same position as the input withdraw cell to `WitnessArgs` and set the `input_type` field to the depositing block hash position in header deps.
-//
-// The builder MUST add cell deps for DAO but MAY not for the lock script.
-//
-// ## Verifier
-//
-// If `to` is `None`, the DAO tx verifier will not verify whether a recipient has fully received the claimed CKB tokens. Otherwise see the section Capacity Verification in the docs of `DaoActionData`.
-//
-// The verifier MUST check the withdraw cell exists in the inputs.
-export const ClaimTo = table(
+export const Withdraw = table(
{
- previousOutput: OutPoint,
- // The sum of the locked CKB tokens and the DAO componsation.
- totalClaimedCapacity: Uint64,
- to: AddressOpt,
+ cellPointer: OutPoint,
+ from: Address,
+ to: Address,
+ depositInfo: DepositInfo,
+ estimatedWithdrawInfo: EstimatedWithdrawInfoOpt,
},
- ["previousOutput", "totalClaimedCapacity", "to"],
+ ["cellPointer", "from", "to", "depositInfo", "estimatedWithdrawInfo"],
);
-// Perform a single DAO operation.
-export const SingleOperation = union(
+export const Claim = table(
{
- Deposit,
- DepositFrom,
- Withdraw,
- WithdrawTo,
- Claim,
- ClaimTo,
+ cellPointer: OutPoint,
+ from: Address,
+ to: Address,
+ depositInfo: DepositInfo,
+ withdrawInfo: WithdrawInfo,
},
- ["Deposit", "DepositFrom", "Withdraw", "WithdrawTo", "Claim", "ClaimTo"],
+ ["cellPointer", "from", "to", "depositInfo", "withdrawInfo"],
);
-// Perform multiple DAO operations.
-//
-// Conflict operations are not allowed:
-// - The Withdraw/WithdrawTo operations should not have the same previous output.
-// - The Claim/ClaimTo operations should not have the same previous output.
-//
-// ## Builder
-//
-// Builder MUST perform the operations in the declared order.
-export const MultipleOperations = vector(SingleOperation);
-
-// The schema of the Action.data for DAO
-//
-// ## Verifier
-//
-// The verifier MUST check there are no two operations are using the same DAO withdraw cell or deposit cell. It must check there are no cells with the DAO type script that does not participate in the listed DAO operations.
-//
-// ### Capacity Verification
-//
-// For each lock script found in `Deposit.lock` and `DepositFrom.from` (not `None`), tally the expected fund and the actual fund.
-//
-// - For matched `Deposit.lock` and `DepositFrom.from`, add the `capacity` to the expected fund.
-// - For each input having the lock script, add the `totalClaimedCapacity` to the actual fund if it is a DAO withdraw cell, otherwise add the cell `capacity` to the actual fund.
-//
-// The actual fund MUST be equal to or larger than the expected fund. Use `DepositFrom` and set `from` to None to bypass the verification.
-//
-// For each lock script found in the withdraw cell referenced by `Claim.previousOutput` and `Claim.to` (not `None`), tally the expected incoming and the actual incoming.
-//
-// - For matched `Claim.previousOutput` and `Claim.to`, add the `totalClaimedCapacity` to the expected incoming.
-// - For each output having the lock script, add the cell `capacity` to the actual incoming.
-//
-// The sum of all positive (expected incoming - actual incoming) MUST be equal to or less than the transaction fee. Use `Claim` and set `to` to None to bypass this verification.
-export const DaoActionData = union({ SingleOperation, MultipleOperations }, [
- "SingleOperation",
- "MultipleOperations",
-]);
+export const DepositVec = vector(Deposit);
+export const WithdrawVec = vector(Withdraw);
+export const ClaimVec = vector(Claim);
+
+export const DaoActionData = table(
+ {
+ deposits: DepositVec,
+ withdraws: WithdrawVec,
+ claims: ClaimVec,
+ },
+ ["deposits", "withdraws", "claims"],
+);
export default DaoActionData;