Overview
Reliquary is a programmable treasury continuity protocol built for onchain communities that cannot depend on a single founder forever. It transforms treasury succession from a trust-based promise into verifiable, executable onchain logic.
When a founder creates a Reliquary plan, they define the conditions under which their treasury should be transferred, distributed, or continued - and by whom. That plan is sealed onchain as immutable instructions. When the configured inactivity trigger fires, the protocol executes the plan automatically, with no human intermediary required.
What Reliquary is
- A programmable treasury succession agent - not a wallet, not a multisig, not a simple will app
- Built for crypto founders, DAOs, memecoin teams, AI agent projects, and onchain communities
- Completely onchain - rules are publicly verifiable and cannot be changed secretly
- Chain-agnostic at the interface level - Base, Ethereum, Polygon, Arbitrum, Optimism, BNB Chain
- Non-custodial - funds never pass through the Reliquary protocol contract
What Reliquary is not
- A private key recovery tool - Reliquary cannot recover lost keys
- A legal will or estate planning service
- A yield instrument or financial product
- A replacement for community governance in active projects
- A magic wand - it only executes the rules you sealed
Key properties
Immutable Once Sealed
After sealing, the plan's core parameters cannot be changed secretly. Any update requires a visible onchain action with a delay period.
Transparent Execution
Every trigger, condition check, and execution event is visible onchain. The community can monitor the plan's state at any time.
Conditional Execution
The plan only runs when the configured inactivity condition is provably met. No one can trigger it prematurely.
Grace Period Safety
A configurable grace period gives the founder a final window to confirm proof-of-life and halt succession before execution.
Quick Start
Get your first treasury succession plan sealed onchain in under 10 minutes. No code required - use the web interface. For programmatic usage, see the SDK section.
Step 1 - Connect your wallet
Navigate to Create Plan and connect a browser wallet (MetaMask, Rabby, Coinbase Wallet) or scan a WalletConnect QR code with your mobile wallet. The connected wallet becomes the treasury controller - the only address authorized to manage the plan.
Step 2 - Define plan identity
Enter your project name, the treasury wallet address, and select the target network. You can use your connected wallet as the treasury address with one click.
Step 3 - Set the inactivity trigger
Choose how many days the treasury controller can be inactive before the succession plan activates. The default is 30 days. You can also set a custom window (1–730 days) and a grace period (0–72 hours) that gives you a final chance to confirm proof-of-life.
Step 4 - Choose a succession mode
Select what happens when the trigger fires. Each mode defines a category of action:
- M1 - Founder Recovery: Control transfers to a backup wallet or multisig
- M2 - Community Vault: Funds move to a community-controlled vault
- M3 - Contributor Distribution: Assets distributed to predefined beneficiaries
- M4 - Agent Continuity: An AI agent or automated wallet continues operations
- M5 - Final Message: A sealed message is published onchain
Step 5 - Define beneficiaries
Add wallet addresses and assign percentage allocations. Total must equal 100%. Each address can be a wallet, multisig (e.g. Gnosis Safe), DAO vault, or agent wallet.
Step 6 - Write a final message (optional)
Compose a message for your community that will be published onchain only when the succession condition is triggered. Choose when it becomes visible - on trigger, or immediately.
Step 7 - Review and seal
Verify every address, allocation, and condition in the review card. Check the confirmation box, then click Seal Treasury Plan Onchain. Sign the transaction in your wallet. Once confirmed, the plan is live and monitoring begins.
After sealing, your plan is live. Monitor status at: https://reliquary.xyz/agent/YOUR_TREASURY_ADDRESS
Protocol
The Reliquary protocol is a state machine that moves a treasury plan through distinct phases - from Active monitoring to Sealed execution - based on provable onchain conditions.
Plan lifecycle
Proof-of-Life mechanism
The proof-of-life (PoL) mechanism is the heartbeat of the Reliquary protocol. The treasury controller must confirm activity within the configured inactivity window. Each confirmation resets the countdown.
PoL can be confirmed in three ways:
- Onchain transaction: Any transaction sent from the treasury wallet resets the countdown immediately
- Signed message: The controller signs a message via the Reliquary dashboard, which records the timestamp onchain
- Either method: Either approach is sufficient - most flexible option
The controller role
The controller is the wallet that creates and seals the plan. They are the only address authorized to:
- Confirm proof-of-life and reset the inactivity countdown
- Cancel the plan during the grace period
- Update non-core parameters (final message, description) before sealing
Trigger Types
A trigger defines the condition that must be met before the succession plan becomes executable. Reliquary supports three primary trigger types.
Inactivity Window (Primary)
The most common trigger. If the treasury controller does not confirm proof-of-life within the configured window (7 to 730 days), the succession plan activates. This is the default trigger for all five protocol modes.
// Pseudocode - simplified trigger logic
if (block.timestamp - lastProofOfLife > inactivityWindow) {
enterGracePeriod();
if (gracePeriodElapsed && !cancelled) {
executeWill();
}
}
Manual Trigger
The controller can set a future date as an additional or alternative trigger. On that date, the succession plan activates regardless of proof-of-life status. Useful for projects with a planned sunset or handover date.
Governance Trigger
In DAO-controlled configurations, a governance vote can trigger the succession plan. This requires the DAO multisig to calltriggerSuccession() with a valid vote snapshot. This is available for M2 (Community Vault) and M3 (Contributor Distribution) modes.
Configuring the inactivity window
| Window | Use case | Notes |
|---|---|---|
| 7 days | High-activity projects, small treasuries | Requires frequent check-ins |
| 30 days | Standard treasury management | Default - good balance |
| 60 days | DAO treasuries, larger teams | Less frequent check-ins |
| 90 days | Long-term foundations, public projects | Common for DAO treasuries |
| 180 days | Minimum-touch projects, LP positions | Requires planning reminders |
| Custom (1–730) | Any custom scenario | Fine-grained control |
Grace period
After the inactivity window closes, a grace period gives the controller a final window to confirm proof-of-life and cancel the succession. The grace period runs 0–72 hours after the trigger fires. During this window, the plan is "Triggered" but not yet "Executed."
Actions
Actions are the onchain operations that execute when a succession plan is triggered. Each protocol mode defines a different category of action.
M1 - Founder Recovery
Control of the treasury wallet transfers to a designated backup wallet or multisig. The backup wallet receives the same permissions as the original controller. The original controller address is recorded in the plan for auditability.
// Transfer controller role to successor wallet transferController(successorWallet, planId);
M2 - Community Vault
The treasury's ETH or tokens are transferred to a community-controlled Gnosis Safe or DAO multisig. The vault address is sealed at plan creation - it cannot be changed after sealing. This is the most common mode for community treasury projects.
M3 - Contributor Distribution
Assets are distributed according to the beneficiary allocation table defined in the plan. Each beneficiary receives their assigned percentage. Unallocated portions (if total< 100%) are locked in the plan contract and governed by the community vault.
M4 - Agent Continuity
An AI agent wallet or automated operations wallet receives a defined allowance and authorization to continue basic project operations - such as granting roles, executing parameter updates, or interacting with other protocols. The agent's authority scope is sealed at plan creation.
M5 - Final Message
No financial action is taken. Instead, a sealed message is published as an onchain event and optionally stored on IPFS or Arweave. This mode is often used alongside other modes as a communication layer.
Combining modes
A single plan can include multiple modes. They execute in the order they were defined. For example, a plan might simultaneously: transfer control to a backup wallet (M1), publish a final message (M5), and distribute remaining funds to contributors (M3).
Beneficiaries
Beneficiaries are the wallet addresses that receive treasury assets or control when a succession plan is executed. Each beneficiary is assigned a label, address, and percentage allocation.
Allocation rules
- Total allocation across all beneficiaries must equal 100% to distribute all funds
- Allocations under 100% leave unallocated funds locked in the plan contract - governed by the community vault
- Allocations over 100% are rejected at the review step before sealing
- Each beneficiary address is validated as a valid EVM address at input time
Beneficiary types
Individual Wallet
A standard EOA (externally owned account) or smart contract wallet. Receives direct token transfers on execution.
Gnosis Safe (Multisig)
A Gnosis Safe multisig as a beneficiary. Recommended for community vaults - requires multiple signers to move funds, providing a governance layer.
DAO Vault
A DAO governance contract address. On execution, tokens are transferred to the DAO's treasury module for community governance.
Agent Wallet
An AI agent or automated operations wallet. Receives a defined allowance for onchain operations, not full treasury control.
Example allocation table
| Label | Address | Allocation |
|---|---|---|
| Community Vault | 0x7f…a3c1 (Gnosis Safe) | 60% |
| Development Wallet | 0x3d…b92f | 20% |
| Core Contributors | 0x8a…c741 | 15% |
| Agent Operations | 0x1c…e204 | 5% |
Final Messages
A final message is an onchain record - text, data, or a link - that is published when the succession condition is triggered. It serves as the founder's last communication to the community.
Message content
Messages can contain up to 1,000 characters and are stored directly in the plan contract's event log. For longer content, a content identifier (CID) pointing to IPFS or Arweave storage can be included instead.
Visibility modes
- Reveal on trigger: The message is encrypted and sealed. It only becomes publicly readable when the succession condition is triggered. This is the default.
- Immediately public: The message is visible onchain but the succession has not yet executed. The community can read it before execution completes.
Auto-publish channels
- Onchain event log: Always published. The message is embedded in a contract event, making it permanently accessible via the chain explorer.
- IPFS / Arweave: Optionally stored on IPFS or Arweave for permanent, decentralized storage. The CID is stored in the plan contract.
Use cases
- A farewell message from the founder explaining the project status
- Instructions for the community on how to continue the project
- Links to community resources, documentation, or governance forums
- A final transaction log or audit record of the project's treasury
- Encrypted credentials or access keys for shared services
// Message is published as an event when trigger fires emit MessagePublished(planId, keccak256(abi.encode(message)), block.timestamp);
Contract Overview
The Reliquary protocol is implemented as a set of Solidity smart contracts deployed on EVM-compatible chains. The contracts are non-upgradable and self-contained.
Contract architecture
UI facade, event indexing
Plan state machine
Inactivity detection
Action dispatch
Plan data, mappings
Deployed addresses
| Network | Core Contract | Registry |
|---|---|---|
| Base | 0xSealed | 0xSealed |
| Ethereum | TBD | TBD |
| Polygon | TBD | TBD |
| Arbitrum | TBD | TBD |
| Optimism | TBD | TBD |
| BNB Chain | TBD | TBD |
Security properties
- Non-upgradable: Contract logic is immutable after deployment. No admin key can change plan parameters.
- Reentrancy guards: All state-changing functions use ReentrancyGuard.
- Checks-effects-interactions: Strict ordering to prevent reentrancy and flash-loan attacks.
- Integer overflow protection: All arithmetic uses Solidity 0.8+ built-in overflow checks.
- Access control: Role-based access for contract-level admin functions (pausing, registry updates).
deployWill()
Deploys a new treasury succession plan onchain. This is the primary entry point for creating a plan. Called by the treasury controller wallet.
function deployWill(
address treasury,
uint256 inactivityWindow,
uint256 gracePeriodHours,
uint8[] memory modes,
Beneficiary[] memory beneficiaries,
bytes32 messageHash
) external returns (uint256 planId) {
require(treasury != address(0), "Invalid treasury");
require(inactivityWindow >= 1 days && inactivityWindow <= 730 days, "Invalid window");
require(modes.length > 0, "At least one mode required");
planId = ++planCounter;
plans[planId] = Plan({
id: planId,
treasury: treasury,
controller: msg.sender,
inactivityWindow: inactivityWindow,
gracePeriodHours: gracePeriodHours,
modes: modes,
beneficiaries: beneficiaries,
messageHash: messageHash,
status: PlanStatus.Sealed,
lastProofOfLife: block.timestamp,
createdAt: block.timestamp,
sealedAt: block.timestamp
});
emit WillDeployed(planId, treasury, msg.sender);
}
Parameters
| Parameter | Type | Description |
|---|---|---|
treasury | address | The wallet whose inactivity triggers the plan |
inactivityWindow | uint256 | Seconds of inactivity before trigger fires (1–730 days) |
gracePeriodHours | uint256 | Hours between trigger and execution (0–72) |
modes | uint8[] | Array of mode IDs:1–5 |
beneficiaries | Beneficiary[] | Array of {address, uint256 pct} structs |
messageHash | bytes32 | Keccak256 hash of the final message (0 if none) |
Returns
planId - A unique unsigned integer identifying the plan. Used in all subsequent calls.
Emits
WillDeployed(planId, treasury, controller) - Indexed event for plan discovery by the Reliquary agent.
Requirements
- Caller must be the treasury controller (the wallet connecting to the UI)
treasurymust be a valid EVM addressinactivityWindowmust be between 1 day and 730 days- At least one mode must be specified
- Beneficiary percentages must not exceed 100% in total
executeWill()
Executes a sealed succession plan after the inactivity window and grace period have both elapsed without cancellation. This function is permissionless - anyone can call it once conditions are met.
function executeWill(uint256 planId) external {
Plan storage plan = plans[planId];
require(plan.id == planId, "Plan not found");
require(plan.status == PlanStatus.Triggered, "Not triggered");
require(
block.timestamp >= plan.sealedAt +
plan.inactivityWindow +
(plan.gracePeriodHours * 1 hours),
"Grace period not elapsed"
);
plan.status = PlanStatus.Executed;
plan.executedAt = block.timestamp;
// Dispatch actions based on modes
for (uint i = 0; i < plan.modes.length; i++) {
_dispatchAction(plan, plan.modes[i]);
}
emit WillExecuted(planId, block.timestamp);
}
Permissionless execution
Once the trigger conditions are provably met, executeWill() can be called by anyone - not just the controller. This is by design: the protocol should execute even if the original controller is completely unreachable. The Reliquary agent monitors plan state and will call this function automatically when conditions are met.
Action dispatch
Each mode triggers a different action dispatch:
- M1: Calls
transferController()on the target wallet or registry - M2: Calls
transferToVault()sending all plan-held tokens to the community vault - M3: Iterates beneficiaries and calls
transfer()for each allocation - M4: Authorizes the agent wallet with a sealed allowance and defined scope
- M5: Emits
MessagePublishedevent with the sealed message content
executeWill() completes, the succession actions cannot be undone. The plan status changes to Executed permanently.
revokeWill()
Revokes a sealed succession plan. Can only be called by the controller during the Active state. Once the plan is Triggered or Executed, revocation is not possible.
function revokeWill(uint256 planId) external {
Plan storage plan = plans[planId];
require(plan.id == planId, "Plan not found");
require(plan.controller == msg.sender, "Not controller");
require(plan.status == PlanStatus.Active, "Cannot revoke in current state");
plan.status = PlanStatus.Revoked;
plan.revokedAt = block.timestamp;
emit WillRevoked(planId, msg.sender, block.timestamp);
}
State restrictions
| Plan Status | revokeWill() allowed? |
|---|---|
| Sealed (monitoring) | Yes - controller can revoke at any time |
| Active (PoL confirmed) | Yes - controller can revoke at any time |
| Triggered (grace period) | No - grace period is final |
| Executed | No - plan is permanent |
| Revoked | No - already revoked |
Use cases
- Project is successful and no longer needs a succession plan
- Controller migrates to a new plan with updated parameters
- Team restructures and wants to start fresh with a new controller address
Revoking vs. cancelling
There is no "cancel" function during the grace period - this is intentional. Once a plan enters the Triggered state, the grace period is the controller's final window to call pingActivity() and reset the countdown. If they fail to do so, execution proceeds. This prevents a dead man's switch from being trivially disabled.
pingActivity()
Confirms proof-of-life from the treasury controller, resetting the inactivity countdown. This is the primary mechanism for keeping a plan in the Active state.
function pingActivity(uint256 planId) external {
Plan storage plan = plans[planId];
require(plan.id == planId, "Plan not found");
require(plan.controller == msg.sender, "Not controller");
require(
plan.status == PlanStatus.Active ||
plan.status == PlanStatus.Triggered,
"Invalid plan state"
);
plan.lastProofOfLife = block.timestamp;
// If triggered, reset back to Active (grace period cancelled)
if (plan.status == PlanStatus.Triggered) {
plan.status = PlanStatus.Active;
emit GracePeriodCancelled(planId, block.timestamp);
}
emit ProofOfLifeConfirmed(planId, block.timestamp);
}
Resetting the grace period
If the plan is in the Triggered state (grace period running), calling pingActivity() resets the plan back to Active and cancels the pending execution. This is the controller's safety mechanism during the grace window.
Proof-of-life methods
There are two ways to confirm proof-of-life:
- Via contract: Call
pingActivity(planId)directly from the treasury wallet. This is an onchain transaction. - Via signed message: Sign a message with the treasury wallet using
personal_sign. The Reliquary agent indexes the signature and callspingActivity()on the controller's behalf. This does not require gas.
Integration with the agent
The Reliquary agent monitors all active plans and can automatically callpingActivity() when it detects a signed message from the controller. This enables a "set and forget" workflow where the founder only needs to sign a message periodically - no gas required.
Wallet Connect
Reliquary uses WalletConnect v2 to enable mobile wallet connections. The protocol supports any WalletConnect-compatible wallet - MetaMask Mobile, Trust Wallet, Coinbase Wallet, Rainbow, and400+ others.
Connection flow
Supported methods
| Method | Purpose |
|---|---|
eth_sendTransaction | Deploy plan, transfer funds on execution |
personal_sign | Sign plan data, confirm proof-of-life |
eth_sign | Legacy signing (fallback) |
eth_signTypedData_v4 | EIP-712 typed data signing |
Browser wallet support (EIP-6963)
For desktop users, Reliquary automatically detects installed browser wallets via the EIP-6963 standard. Supported wallets include MetaMask, Rabby, Coinbase Wallet, Trust Wallet, Brave Wallet, and any other EIP-6963-compliant extension.
Project ID
WalletConnect requires a free Project ID from cloud.reown.com. The Reliquary UI uses a demo Project ID. For production, replaceWC_PROJECT_ID in scripts/create-plan.js with your own.
Automation
Reliquary supports automated proof-of-life confirmations and execution monitoring through several integration patterns.
Automatic PoL via signed messages
The Reliquary agent can be configured to watch for signed proof-of-life messages from the treasury controller. This allows the controller to confirm activity without spending gas - they simply sign a message using their wallet, and the agent callspingActivity() on their behalf.
// Agent watches for PoL signatures and auto-confirms
agent.on("signedMessage", async ({ planId, signature, controller }) => {
const isValid = await verifySignature(controller, message, signature);
if (isValid) {
await reliquary.pingActivity(planId);
console.log(`PoL confirmed for plan ${planId}`);
}
});
Automated execution monitoring
The Reliquary agent continuously monitors all active plans. When a plan enters the Triggered state, the agent:
- Sends an alert to the controller (email, Telegram, Discord webhook)
- Waits for the grace period to elapse
- Calls
executeWill()automatically if not cancelled - Publishes the execution event and final message
Webhook alerts
{
"event": "plan_triggered",
"planId": 42,
"project": "MemeCoin Protocol",
"treasury": "0x7f...a3c1",
"gracePeriodEnds": 1712304000,
"controller": "0x3d...b92f",
"actions": ["M2", "M5"]
}
External automation tools
- GitHub Actions cron: Run a daily check that sends a PoL signature if the last confirmation was more than (window - 5) days ago
- Zapier / Make: Trigger a PoL signature on a schedule, or send alerts to Discord when a plan is triggered
- The Graph: Index Reliquary events to build custom dashboards and monitoring tools
SDK
The Reliquary SDK provides a programmatic interface for creating, managing, and monitoring treasury succession plans. Available as an npm package.
npm install @reliquary/sdk
Initialization
import { Reliquary } from "@reliquary/sdk";
const reliquary = new Reliquary({
network: "base",
provider: window.ethereum, // or any EIP-1193 provider
});
Creating a plan
const plan = await reliquary.deployWill({
treasury: "0x7f...a3c1",
inactivityWindow: 30 * 24 * 60 * 60, // 30 days in seconds
gracePeriodHours: 48,
modes: ["M2", "M5"],
beneficiaries: [
{ address: "0x3d...b92f", pct: 60, label: "Community Vault" },
{ address: "0x8a...c741", pct: 20, label: "Development" },
{ address: "0x1c...e204", pct: 20, label: "Contributors" },
],
finalMessage: "It has been an honor. The treasury is yours now.",
});
console.log("Plan sealed:", plan.id);
Confirming proof-of-life
// Transaction-based PoL (pays gas) await reliquary.pingActivity(plan.id); // Signed message PoL (no gas, agent processes it) const signature = await reliquary.signProofOfLife(plan.id); await reliquary.submitSignedPoL(plan.id, signature);
Querying plan state
const plan = await reliquary.getPlan(plan.id); console.log(plan.status); // "Active" | "Triggered" | "Executed" console.log(plan.daysLeft); // Days until trigger console.log(plan.lastPoL); // Unix timestamp of last confirmation
Monitoring all plans
reliquary.on("WillDeployed", (plan) => {
console.log("New plan:", plan.id, plan.project);
});
reliquary.on("PlanTriggered", (plan) => {
console.warn(`Plan ${plan.id} triggered - grace period: ${plan.gracePeriodHours}h`);
});
reliquary.on("WillExecuted", (plan) => {
console.log(`Plan ${plan.id} executed on ${new Date(plan.executedAt * 1000)}`);
});
Revoking a plan
await reliquary.revokeWill(plan.id);
SDK reference
| Method | Description |
|---|---|
deployWill(params) | Create and seal a new succession plan |
pingActivity(planId) | Confirm proof-of-life (onchain tx) |
signProofOfLife(planId) | Generate a signed PoL message (no gas) |
submitSignedPoL(planId, sig) | Submit a signed PoL to the agent |
getPlan(planId) | Get plan details and current state |
getPlansByController(addr) | Get all plans for a controller address |
revokeWill(planId) | Revoke a plan (Active state only) |
on(event, handler) | Subscribe to contract events |