Skip to content

Commit 01621d2

Browse files
committed
feat: add dual-token vault system with principal and yield tokens, including interfaces and libraries for token management
1 parent ff61966 commit 01621d2

25 files changed

Lines changed: 3467 additions & 41 deletions

‎.gitmodules‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "lib/forge-std"]
22
path = lib/forge-std
33
url = https://github.com/foundry-rs/forge-std
4+
[submodule "lib/solady"]
5+
path = lib/solady
6+
url = https://github.com/vectorized/solady

‎README.md‎

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
```txt █████
2-
░░███
3-
███████ ████████ █████ ████ ████████ ███████
4-
███░░███ ░░███░░███ ░░███ ░███ ░░███░░███ ░░░███░
2+
░░███
3+
███████ ████████ █████ ████ ████████ ███████
4+
███░░███ ░░███░░███ ░░███ ░███ ░░███░░███ ░░░███░
55
░███ ░███ ░███ ░░░ ░███ ░███ ░███ ░███ ░███
66
░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███
77
░░███████ █████ ░░████████ ████ █████ ░░█████
@@ -13,4 +13,50 @@
1313

1414
<p align="center" style="font-size: smaller;">
1515
<i>(wip)</i>
16-
</p>
16+
</p>
17+
18+
## Principal and Yield Token Vaults
19+
20+
The vault system implements a dual-token structure separating principal and yield:
21+
22+
### Deposit Phase
23+
24+
When depositing into the vault, users receive two types of shares:
25+
- **Principal Shares (PT)**: Equal to the amount of assets deposited
26+
- **Yield Shares (YT)**: Equal to the expected yield amount
27+
28+
**Example**: Depositing 1,000,000 USDC with an expected 10% return:
29+
- Receive: 1,000,000 PT + 100,000 YT
30+
31+
During the deposit phase, withdrawals are locked (`canWithdraw = false`).
32+
33+
### Redemption Phase
34+
35+
Once the vault receives assets and unlocks redemption (`canWithdraw = true`), the value of each share type is determined by the total assets in the vault.
36+
37+
#### Redemption Formula
38+
39+
```
40+
principalAssets = min(totalAssets, principalTokenSupply)
41+
yieldAssets = totalAssets - principalAssets
42+
43+
pricePerPrincipalShare = principalAssets / principalTokenSupply
44+
pricePerYieldShare = yieldAssets / yieldTokenSupply
45+
```
46+
47+
#### Redemption Examples
48+
49+
Given: 1,000,000 PT and 100,000 YT outstanding
50+
51+
| Total Assets | Principal Assets | Yield Assets | PT Price | YT Price |
52+
|--------------|------------------|--------------|----------|----------|
53+
| 900,000 | 900,000 | 0 | 0.9 | 0 |
54+
| 1,000,000 | 1,000,000 | 0 | 1.0 | 0 |
55+
| 1,050,000 | 1,000,000 | 50,000 | 1.0 | 0.5 |
56+
| 1,200,000 | 1,000,000 | 200,000 | 1.0 | 2.0 |
57+
58+
**Key Properties:**
59+
- Principal holders are prioritized: they receive up to 1:1 redemption of their shares
60+
- Yield holders receive any assets beyond the principal supply
61+
- If total assets < principal supply, principal holders share the loss proportionally
62+
- If total assets > principal supply, yield holders capture all upside

‎foundry.lock‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@
44
"name": "v1.11.0",
55
"rev": "8e40513d678f392f398620b3ef2b418648b33e89"
66
}
7+
},
8+
"lib/solady": {
9+
"tag": {
10+
"name": "v0.1.26",
11+
"rev": "acd959aa4bd04720d640bf4e6a5c71037510cc4b"
12+
}
713
}
814
}

‎foundry.toml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,10 @@ src = "src"
33
out = "out"
44
libs = ["lib"]
55

6+
[fmt]
7+
tab_width = 2
8+
9+
[lint]
10+
ignore = ["test/**/*"]
11+
612
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

‎lib/solady‎

Submodule solady added at acd959a

‎script/Request.s.sol‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22
pragma solidity ^0.8.13;
33

44
import {Script} from "forge-std/Script.sol";
5-
import {Request} from "../src/request/Request.sol";
5+
// import {Request} from "../src/request/Request.sol";
66

77
contract RequestScript is Script {
8-
Request public request;
8+
// Request public request;
99

10-
function setUp() public {}
10+
function setUp() public {}
1111

12-
function run() public {
13-
vm.startBroadcast();
12+
function run() public {
13+
vm.startBroadcast();
1414

15-
request = new Request();
15+
// request = new Request();
1616

17-
vm.stopBroadcast();
18-
}
17+
vm.stopBroadcast();
18+
}
1919
}

‎src/core/interfaces/IRequest.sol‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.20;
3+
4+
/// @title IRequest
5+
/// @notice Interface for handling token consumption, repayment, and validation logic in a request system.
6+
/// @dev This is the interface expected to intereact with the position manager to:
7+
/// - Account for remaining debt
8+
/// - Consume and repay the request token
9+
/// - Validate the request token and the consumer address
10+
interface IRequest {
11+
/**
12+
* @notice Consumes a specified amount of the request token and sends it to the given receiver address.
13+
* @dev After a repayment has been initiated, no further consumption can take place.
14+
* Requirements and expected state transitions are implementation-specific.
15+
* @dev The amount of consumed tokens should not be greater than the principal assets amount.
16+
* @param amount The amount of the request token to consume.
17+
* @param receiver The address that will receive the consumed tokens.
18+
*/
19+
function consume(uint256 amount, address receiver) external;
20+
21+
/**
22+
* @notice Repays a specified amount of the request token.
23+
* @dev This operation records repayment against the outstanding balance.
24+
* After any call to {repay}, no further consumption is allowed.
25+
* Only callable if full repayment has not yet been validated.
26+
* It should pull from the caller's balance.
27+
* @param amount The amount of the request token to repay.
28+
*/
29+
function repay(uint256 amount) external;
30+
31+
/**
32+
* @notice Validates and finalizes the state that the full repayment of the request token has been made.
33+
* @dev This function locks the contract against further repayments or consumptions,
34+
* and may enable a new phase in the workflow. Only callable if the amount repaid
35+
* matches the total amount required.
36+
*/
37+
function validateFullRepayment() external;
38+
39+
/**
40+
* @notice Returns the total amount of principal tokens that have been consumed.
41+
* @return The total amount of principal tokens that have been consumed.
42+
*/
43+
function principalSupply() external view returns (uint256);
44+
45+
/**
46+
* @notice Returns the total amount of principal assets that have been repaid.
47+
* @return The total amount of principal assets that have been repaid.
48+
*/
49+
function principalAssets() external view returns (uint256);
50+
51+
/**
52+
* @notice Returns the total amount of yield tokens that have been consumed.
53+
* @return The total amount of yield tokens that have been consumed.
54+
*/
55+
function yieldSupply() external view returns (uint256);
56+
57+
/**
58+
* @notice Returns the total amount of yield assets that have been repaid.
59+
* @return The total amount of yield assets that have been repaid.
60+
*/
61+
function yieldAssets() external view returns (uint256);
62+
63+
/**
64+
* @notice Returns the address of the ERC-20 token underlying this request.
65+
* @dev The returned address should reference the token contract used for consumption and repayment.
66+
* @return token The address of the request token used by this contract.
67+
*/
68+
function token() external view returns (address token);
69+
70+
/**
71+
* @notice Returns the address of the consumer of this request.
72+
* @return consumer The address of the consumer of this request.
73+
*/
74+
function consumer() external view returns (address consumer);
75+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.2;
3+
4+
/// @dev Interface of the ERC20 standard as defined in the EIP.
5+
/// @dev This includes the optional name, symbol, and decimals metadata.
6+
interface IERC20 {
7+
/// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
8+
event Transfer(address indexed from, address indexed to, uint256 value);
9+
10+
/// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
11+
/// is the new allowance.
12+
event Approval(address indexed owner, address indexed spender, uint256 value);
13+
14+
/// @notice Returns the amount of tokens in existence.
15+
function totalSupply() external view returns (uint256);
16+
17+
/// @notice Returns the amount of tokens owned by `account`.
18+
function balanceOf(address account) external view returns (uint256);
19+
20+
/// @notice Moves `amount` tokens from the caller's account to `to`.
21+
function transfer(address to, uint256 amount) external returns (bool);
22+
23+
/// @notice Returns the remaining number of tokens that `spender` is allowed
24+
/// to spend on behalf of `owner`
25+
function allowance(address owner, address spender) external view returns (uint256);
26+
27+
/// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
28+
/// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
29+
function approve(address spender, uint256 amount) external returns (bool);
30+
31+
/// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
32+
/// `amount` is then deducted from the caller's allowance.
33+
function transferFrom(address from, address to, uint256 amount) external returns (bool);
34+
35+
/// @notice Returns the name of the token.
36+
function name() external view returns (string memory);
37+
38+
/// @notice Returns the symbol of the token.
39+
function symbol() external view returns (string memory);
40+
41+
/// @notice Returns the decimals places of the token.
42+
function decimals() external view returns (uint8);
43+
}

0 commit comments

Comments
 (0)