Skip to content
Back to skills

Reliquary Deployment

ASecurity

This skill should be used when the user asks to "deploy Reliquary", "add a Reliquary pool", "set up rolling rewarder", "create relic", "deposit into Reliquary", "withdraw from Reliquary", or needs practical guidance on deploying and interacting with Reliquary smart contracts.

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 10, 2026
blockchaintesting

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add cyotee/indexedex --skill reliquary-deployment --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Reliquary Deployment?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Reliquary Deployment
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/cyotee-reliquary-deployment/badge)](https://www.skillsdirectory.com/skills/cyotee-reliquary-deployment)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: reliquary-deployment
description: This skill should be used when the user asks to "deploy Reliquary", "add a Reliquary pool", "set up rolling rewarder", "create relic", "deposit into Reliquary", "withdraw from Reliquary", or needs practical guidance on deploying and interacting with Reliquary smart contracts.
license: MIT
---

# Reliquary Deployment & Usage

## Deployment Approaches

### 1. Direct Deployment (Tests / Development)

Reliquary is a standard non-Diamond contract. Deploy it directly with `new`:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Reliquary} from "@crane/contracts/protocols/staking/reliquary/v1/Reliquary.sol";
import {LinearCurve} from "@crane/contracts/protocols/staking/reliquary/v1/curves/LinearCurve.sol";
import {MockERC20} from "@crane/contracts/test/mocks/MockERC20.sol";

contract MyReliquaryDeploy {
    function deploy() external {
        // 1. Deploy reward token (any ERC20)
        MockERC20 rewardToken = new MockERC20("Reward", "RWD", 18);

        // 2. Deploy Reliquary — deployer gets DEFAULT_ADMIN_ROLE
        Reliquary reliquary = new Reliquary({
            _rewardToken: address(rewardToken),
            _emissionRate: 1e18,       // 1 reward token per second
            _name: "Reliquary Deposit",
            _symbol: "RELIC"
        });

        // 3. Fund Reliquary with reward tokens (must have balance to pay rewards)
        rewardToken.mint(address(this), 1_000_000e18);
        rewardToken.transfer(address(reliquary), 1_000_000e18);

        // 4. Deploy a curve for the pool
        LinearCurve curve = new LinearCurve({
            _slope: 100,                // multiplier increases by 100 per second
            _minMultiplier: 365 days * 100  // f(0) = 365e5 (no lock = 100% annual boost)
        });

        // 5. Approve Reliquary to spend pool token (for addPool bootstrap)
        MockERC20 poolToken = new MockERC20("Pool", "POOL", 18);
        poolToken.mint(address(this), 1_000_000e18);
        poolToken.approve(address(reliquary), type(uint256).max);

        // 6. Add pool — triggers 1 wei bootstrap deposit (caller must hold + approve 1 wei)
        reliquary.addPool({
            _allocPoint: 100,
            _poolToken: address(poolToken),
            _rewarder: address(0),           // or address of ParentRollingRewarder
            _curve: curve,
            _name: "My Pool",
            _nftDescriptor: address(0),
            _allowPartialWithdrawals: true,
            _to: address(this)               // receives the bootstrap 1 wei Relic
        });

        // 7. Create a Relic and deposit actual tokens
        poolToken.approve(address(reliquary), type(uint256).max);
        uint256 relicId = reliquary.createRelicAndDeposit({
            _to: address(this),
            _poolId: 0,
            _amount: 1000e18
        });
    }
}
```

### 2. Deterministic Deployment (Crane Create3Factory)

For production with deterministic addresses, use Crane's `Create3Factory`:

```solidity
// Deploy Reliquary deterministically via Create3Factory
ICreate3FactoryProxy create3Factory = ...; // from InitDevService.initEnv()

bytes memory initArgs = abi.encode(
    address(rewardToken),   // _rewardToken
    1e18,                   // _emissionRate
    "Reliquary Deposit",     // _name
    "RELIC"                 // _symbol
);

// Salt derived from type name (Crane convention)
bytes32 salt = keccak256(abi.encode("Reliquary"));

address reliquaryAddr = create3Factory.deploy(
    type(Reliquary).creationCode,
    initArgs,
    salt
);
vm.label(reliquaryAddr, "Reliquary");
```

---

## Adding a Pool

### Prerequisites

Before calling `addPool`, the caller must:

1. **Have `DEFAULT_ADMIN_ROLE`** on Reliquary
2. **Hold at least 1 unit** of the pool token
3. **Have approved** Reliquary to spend that 1 unit

```solidity
// Approve and add pool
IERC20(poolToken).approve(address(reliquary), type(uint256).max);
reliquary.addPool({
    _allocPoint: 100,
    _poolToken: address(poolToken),
    _rewarder: address(0),
    _curve: curve,
    _name: "ETH/USDC LP",
    _nftDescriptor: nftDescriptor,
    _allowPartialWithdrawals: true,
    _to: address(dao)      // DAO receives the bootstrap Relic
});
```

### Pool with Rolling Rewarder

```solidity
// 1. Deploy ParentRollingRewarder first (its owner will manage children)
ParentRollingRewarder rewarder = new ParentRollingRewarder();

// 2. Add pool with rewarder — Reliquary calls rewarder.initialize(poolId)
reliquary.addPool({
    _allocPoint: 100,
    _poolToken: address(poolToken),
    _rewarder: address(rewarder),
    _curve: curve,
    _name: "My Pool",
    _nftDescriptor: address(0),
    _allowPartialWithdrawals: true,
    _to: address(this)
});

// 3. As rewarder owner: create a child for each reward token
address child = rewarder.createChild(address(rewardToken));

// 4. Fund the child — owner approves and calls fund()
IERC20(rewardToken).approve(child, type(uint256).max);
RollingRewarder(child).fund(100_000e18);

// 5. Optionally set distribution period before funding
RollingRewarder(child).updateDistributionPeriod(30 days);
```

---

## User Operations

### Deposit into Existing Relic

```solidity
// Approve Reliquary to spend your pool tokens
IERC20(poolToken).approve(address(reliquary), type(uint256).max);

// Deposit and harvest rewards in one transaction
reliquary.deposit({
    _amount: 500e18,
    _relicId: 1,
    _harvestTo: address(this)  // non-zero = harvest too
});
```

### Create New Relic + Deposit

```solidity
uint256 relicId = reliquary.createRelicAndDeposit({
    _to: address(this),
    _poolId: 0,
    _amount: 1000e18
});
```

### Withdraw + Harvest

```solidity
reliquary.withdraw({
    _amount: 200e18,
    _relicId: 1,
    _harvestTo: address(this)  // harvests rewards while withdrawing
});
```

### Harvest Only (No Deposit/Withdraw)

```solidity
reliquary.update({
    _relicId: 1,
    _harvestTo: address(this)   // non-zero address triggers harvest
});
```

### Split a Relic

```solidity
// Requires allowPartialWithdrawals == true for this pool
uint256 newId = reliquary.split({
    _relicId: 1,
    _amount: 100e18,
    _to: address(otherPerson)
});
```

### Merge Two Relics

```solidity
// Both relics must be in the same pool
reliquary.merge({
    _fromId: 2,
    _toId: 1   // burns fromId, moves all amount into toId
});
```

### Burn Empty Relic

```solidity
// Can only burn if amount == 0 AND no pending rewards
reliquary.burn(relicId);
```

---

## Using Deposit Helpers

For pools with ERC4626 vault or Reaper vault poolTokens, use the helpers instead of direct Reliquary calls.

### ERC4626 Vault Pool

```solidity
// Deploy helper
DepositHelperERC4626 helper = new DepositHelperERC4626({
    _reliquary: reliquary,
    _weth: address(weth)
});

// Approve helper to pull underlying (e.g., USDC)
IERC20(underlying).approve(address(helper), type(uint256).max);

// Create relic + deposit underlying (helper converts to vault shares, deposits to Reliquary)
uint256 relicId = helper.createRelicAndDeposit{value: 0}(0, amount);

// Withdraw — optionally as ETH
helper.withdraw(amount, relicId, harvest, withdrawAsETH);

// Deposit ETH directly
uint256 relicId = helper.createRelicAndDeposit{value: 1 ether}(0, 1 ether);
```

### Reaper Vault Pool

```solidity
DepositHelperReaperVault helper = new DepositHelperReaperVault({
    _reliquary: reliquary,
    _weth: address(weth)
});

// Approve underlying token
IERC20(underlying).approve(address(helper), type(uint256).max);

// Deposit
(uint256 relicId, uint256 shares) = helper.createRelicAndDeposit(0, amount);

// Withdraw underlying (optionally as ETH)
helper.withdraw(amount, relicId, harvest, asETH);
```

---

## Important Constraints

### Reward Funding
Reliquary pays rewards from its own balance. If `rewardToken.balanceOf(reliquary) < pendingReward`, users receive less than shown in `pendingReward()`. **Keep Reliquary funded** by topping up its balance periodically.

### Partial Withdrawals
If `allowPartialWithdrawals == false`:
- Users cannot withdraw less than their full position
- `split()` and `shift()` are disabled

### Pool Token Restrictions
- Pool token cannot be the reward token (reverts in `addPool`)
- Pool token `totalSupply()` must be ≤ `100e9 ether`

### Curve Constraints
- `curve.getFunction(0)` must be > 0
- 10-year projection must not overflow: `emissionRate * 10years * ACC_REWARD_PRECISION * curve.getFunction(10years)` checked at `addPool` time

### Max Pools
Pool IDs are `uint8` — maximum 255 pools per Reliquary.

---

## Testing Pattern (Crane)

Use `TestBase_Reliquary` for tests:

```solidity
contract MyReliquaryTest is TestBase_Reliquary {
    function testDeposit() public {
        // Inherits: reliquary, linearCurve, rewardToken, poolToken, etc.
        // setUp() already: deploys Reliquary, adds pool 0 with linearCurve
        
        uint256 relicId = _createAndDeposit(0, 1000e18);
        assertEq(reliquary.getPositionForId(relicId).amount, 1000e18);
    }
}
```

For ERC4626/Reaper vault pools, extend `TestBase_Reliquary`:

```solidity
contract MyERC4626PoolTest is TestBase_Reliquary {
    DepositHelperERC4626 helper;
    IERC4626 vault;
    MockERC20 oath;
    WETH9 weth;

    function setUp() public override {
        TestBase_Reliquary.setUp();
        
        oath = new MockERC20("Oath", "OATH", 18);
        Reliquary r = new Reliquary(address(oath), 1e17, "Rel", "REL");
        reliquary = IReliquary(address(r));
        
        weth = new WETH9();
        vault = new ERC4626Mock(address(weth), "Vault", "VLT", 18);
        linearCurve = new LinearCurve(100, 365 days * 100);
        
        // ... add pool with vault as poolToken
    }
}
```

---

## See Also

- `skill:reliquary-architecture` — conceptual model, curves, rewarders
- `contracts/protocols/staking/reliquary/v1/test/bases/TestBase_Reliquary.sol` — test deployment pattern
- `contracts/protocols/staking/reliquary/v1/helpers/` — helper contracts for ERC4626/Reaper/BPT pools

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…