Fund Clawback
Recall funds from child node treasuries
Fund Clawback
Parent DAOs can recall (clawback) funds from child node treasuries. This mechanism ensures ultimate control over delegated resources while respecting the autonomy of sub-DAOs.
Overview
Clawback allows parent DAOs to:
- Recall unused or misallocated funds
- Recover assets during node dissolution
- Respond to emergencies
- Rebalance treasury allocations
When to Use Clawback
Appropriate Uses
- Node dissolution - Returning funds when closing a sub-DAO
- Budget reallocation - Moving unused funds to higher priorities
- Emergency recovery - Security incident response
- Charter violation - Funds used outside mandate
- Period end - Returning unspent quarterly allocation
Not Appropriate
- Punitive action without governance
- Overriding legitimate child decisions
- Routine budget adjustments (use transfers)
- Political disputes
Clawback is a significant action that can disrupt child operations. Always follow proper governance and provide adequate notice.
Clawback Process
Timelock Delay
Most configurations include a clawback delay:
Parent initiates Delay period Execution
clawback (3-7 days) (funds recalled)
│ │ │
▼ ▼ ▼
────●───────────────────●────────────────●────►
│ │ │
│ │ │
└───── Child can ───┘ │
respond or │
appeal │
│
Funds transfer to
parent treasuryDelay Rationale
| Delay | Use Case |
|---|---|
| 0 days | Emergency reserve (parent-controlled) |
| 1 day | Operational funds (tight oversight) |
| 3 days | Standard committees |
| 7 days | Autonomous sub-DAOs |
| 14 days | High-autonomy entities |
Clawback Methods
Method 1: Via UI
- Navigate to Organization > Hierarchy
- Select the child node
- Click "Clawback Funds"
- Configure the clawback
┌─────────────────────────────────────────────────────────────┐
│ Clawback Funds: Research Committee │
├─────────────────────────────────────────────────────────────┤
│ │
│ Child Treasury Balances: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ USDC 75,000.00 │ │
│ │ ETH 12.5 │ │
│ │ DAO 500,000 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Token to recall: [USDC ▼] │
│ │
│ Amount: [50,000 ] │
│ │
│ Reason (required): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Q4 budget underspent - reallocating to Grants │ │
│ │ Committee for year-end programs. │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ⏱️ Timelock: 3 days (per child configuration) │
│ │
│ [ Cancel ] [ Initiate Clawback ] │
│ │
└─────────────────────────────────────────────────────────────┘Method 2: Via Proposal
Recommended for significant clawbacks:
Proposal: "Recall Unspent Q4 Budget from Research Committee"
Description: |
The Research Committee has $50,000 USDC remaining from
their Q4 budget. This proposal recalls those funds for
reallocation to the Grants Committee.
## Rationale
- Research Committee spent $50K of $100K Q4 allocation
- Grants Committee has backlog of approved applications
- Fiscal year ends in 30 days
## Impact
- Research Committee retains $25K for ongoing projects
- $50K reallocated to high-priority grants
## Timeline
- 3-day clawback delay per committee charter
- Reallocation within 7 days of execution
Actions:
- type: clawback
childId: 42
token: USDC
amount: 50000000000 # 50K USDC (6 decimals)
reason: "Q4 budget reallocation"
- type: transfer
to: grants_committee
token: USDC
amount: 50000000000Method 3: Via SDK
import { HierarchyClient } from '@lux/dao-sdk';
import { ethers } from 'ethers';
const hierarchy = new HierarchyClient(signer);
// Initiate clawback
const tx = await hierarchy.clawback(
childId,
usdcAddress,
ethers.parseUnits('50000', 6), // 50K USDC
'Q4 budget reallocation'
);
const receipt = await tx.wait();
const clawbackId = receipt.events[0].args.clawbackId;
console.log(`Clawback initiated: ${clawbackId}`);
console.log(`Executes after: ${await hierarchy.clawbackExecutionTime(clawbackId)}`);Smart Contract Interface
interface IHierarchy {
/// @notice Emitted when clawback is initiated
event ClawbackInitiated(
uint256 indexed clawbackId,
uint256 indexed childId,
address token,
uint256 amount,
string reason,
uint256 executeAfter
);
/// @notice Emitted when clawback is executed
event ClawbackExecuted(
uint256 indexed clawbackId,
uint256 indexed childId,
address token,
uint256 amount
);
/// @notice Emitted when clawback is cancelled
event ClawbackCancelled(
uint256 indexed clawbackId,
string reason
);
/// @notice Initiates a clawback with timelock
/// @param childId Child node to recall from
/// @param token Token address (address(0) for native)
/// @param amount Amount to recall
/// @param reason Documented reason
/// @return clawbackId Unique identifier for this clawback
function clawback(
uint256 childId,
address token,
uint256 amount,
string calldata reason
) external returns (uint256 clawbackId);
/// @notice Executes a clawback after timelock
/// @param clawbackId The clawback to execute
function executeClawback(uint256 clawbackId) external;
/// @notice Cancels a pending clawback
/// @param clawbackId The clawback to cancel
/// @param reason Why it was cancelled
function cancelClawback(
uint256 clawbackId,
string calldata reason
) external;
/// @notice Returns clawback details
function getClawback(uint256 clawbackId) external view returns (
uint256 childId,
address token,
uint256 amount,
string memory reason,
uint256 executeAfter,
bool executed,
bool cancelled
);
}Clawback Lifecycle
States
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Pending │────▶│ Ready │────▶│ Executed │
└──────────┘ └──────────┘ └──────────┘
│ │
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│Cancelled │ │ Expired │
└──────────┘ └──────────┘| State | Description |
|---|---|
| Pending | Initiated, waiting for timelock |
| Ready | Timelock passed, can be executed |
| Executed | Funds transferred to parent |
| Cancelled | Clawback cancelled before execution |
| Expired | Not executed within grace period |
Execution Window
After timelock, there's a window to execute:
const clawback = await hierarchy.getClawback(clawbackId);
// Can execute between these times
const canExecuteAfter = clawback.executeAfter;
const mustExecuteBefore = clawback.executeAfter + EXECUTION_WINDOW;
if (block.timestamp > mustExecuteBefore) {
// Clawback expired, must reinitiate
}Child Response Options
When a clawback is initiated, the child can:
1. Accept and Prepare
// Child prepares for reduced budget
await childTreasury.adjustBudgets(reducedAmount);2. Request Cancellation
Appeal to parent governance:
Proposal: "Cancel Clawback - Active Projects Need Funds"
Description: |
The pending clawback of $50K would disrupt three active
research projects. We request cancellation.
## Affected Projects
1. Privacy Research - $20K committed
2. Scalability Study - $15K committed
3. Interop Analysis - $10K committed
## Alternative
We propose returning $25K and retaining $25K for
project completion.3. Emergency Use
If funds are legitimately needed:
// Child spends funds before execution
// (only if within mandate - improper use is misconduct)
await childTreasury.transfer(
projectAddress,
usdcAddress,
amount,
'Committed project funding'
);Cancelling a Clawback
Via UI
- Navigate to pending clawbacks
- Select the clawback
- Click "Cancel Clawback"
- Provide reason
Via Proposal
Proposal: "Cancel Research Committee Clawback"
Description: |
After reviewing the Research Committee's response, we
agree to cancel the clawback and allow completion of
current projects.
Actions:
- type: cancel_clawback
clawbackId: 123
reason: "Active projects need completion funding"Via SDK
await hierarchy.cancelClawback(
clawbackId,
'Projects need completion - agreed with child committee'
);Emergency Clawback
For urgent situations (security breach, misconduct):
// Emergency clawback bypasses normal timelock
// Requires GUARDIAN_ROLE
await hierarchy.emergencyClawback(
childId,
tokenAddress,
amount,
'Security incident - unauthorized access detected'
);Emergency clawback should only be used for genuine emergencies. Misuse undermines trust in the hierarchy.
Clawback All Funds
For node dissolution:
// Recall all tokens from child treasury
const balances = await hierarchy.getChildBalances(childId);
for (const balance of balances) {
if (balance.amount > 0) {
await hierarchy.clawback(
childId,
balance.token,
balance.amount,
'Node dissolution - returning all funds'
);
}
}Monitoring Clawbacks
Dashboard View
┌─────────────────────────────────────────────────────────────┐
│ Pending Clawbacks │
├─────────────────────────────────────────────────────────────┤
│ │
│ ID Child Amount Executes Status │
│ ───────────────────────────────────────────────────────── │
│ 123 Research Committee 50K USDC in 2 days Pending │
│ 124 Regional Chapter 10K USDC in 5 days Pending │
│ │
│ [View Details] [Cancel] [Execute When Ready] │
│ │
└─────────────────────────────────────────────────────────────┘Event Monitoring
hierarchy.on('ClawbackInitiated', (id, childId, token, amount, reason) => {
console.log(`Clawback ${id}: ${amount} from child ${childId}`);
notifyChildAdmin(childId, 'clawback_initiated', { amount, reason });
});
hierarchy.on('ClawbackExecuted', (id, childId, token, amount) => {
console.log(`Clawback ${id} executed: ${amount} returned to parent`);
});
hierarchy.on('ClawbackCancelled', (id, reason) => {
console.log(`Clawback ${id} cancelled: ${reason}`);
});Best Practices
Before Initiating
- Communicate - Inform child leadership in advance
- Document - Clear rationale in the reason field
- Consider alternatives - Is clawback the right approach?
- Verify amounts - Check child treasury balances
During Timelock
- Monitor - Watch for child response
- Be available - Respond to questions/appeals
- Review - Reconsider if new information emerges
After Execution
- Confirm receipt - Verify funds in parent treasury
- Reallocate - Use funds as intended
- Document - Record in governance records
- Follow up - Address any child concerns
Access Control
| Action | Required Permission |
|---|---|
| Initiate clawback | ADMIN_ROLE on parent |
| Execute clawback | Any (after timelock) |
| Cancel clawback | ADMIN_ROLE on parent |
| Emergency clawback | GUARDIAN_ROLE |
Troubleshooting
| Error | Cause | Solution |
|---|---|---|
Unauthorized | Missing admin role | Get proper permissions |
ClawbackDisabled | Config disables clawback | Cannot clawback this child |
InsufficientBalance | Child doesn't have funds | Reduce amount |
TimelockNotPassed | Too early to execute | Wait for timelock |
ClawbackExpired | Execution window passed | Reinitiate clawback |