Skip to content

Commit ae90b63

Browse files
authored
Merge pull request #113 from Sendi0011/docs/strategy-allocation-wiki-41
docs: document strategy allocation and RWA risk management models (#41)
2 parents fdfeb58 + d63ea6d commit ae90b63

1 file changed

Lines changed: 194 additions & 83 deletions

File tree

docs/strategy-allocation-wiki.md

Lines changed: 194 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,26 @@
22

33
## 1. Overview
44

5-
YieldVault is a Soroban smart contract vault on the Stellar network that accepts USDC deposits from retail users and generates yield by allocating funds into tokenized Real-World Assets (RWAs) such as sovereign debt instruments and US Treasuries. This document describes the mathematical strategies and risk parameters governing the vault.
5+
YieldVault is a Soroban smart contract vault on the Stellar network that accepts USDC deposits
6+
from retail users and generates yield by allocating funds into tokenized Real-World Assets (RWAs)
7+
such as sovereign debt instruments and US Treasuries. This document describes the mathematical
8+
strategies and risk parameters governing the vault.
69

710
---
811

912
## 2. Core Mathematical Strategies
1013

1114
### 2.1 Share Price Model (ERC-4626 Style)
1215

13-
YieldVault uses a proportional share model. When a user deposits USDC, they receive vault shares (yvUSDC) representing their fractional ownership of the total vault assets.
16+
YieldVault uses a proportional share model. When a user deposits USDC they receive vault shares
17+
(yvUSDC) representing their fractional ownership of total vault assets.
1418

1519
**Share Minting Formula (Deposit):**
1620
```
1721
shares_to_mint = deposit_amount × total_shares / total_assets
1822
```
19-
> If the vault is empty (`total_assets = 0` or `total_shares = 0`), shares are minted 1:1 with the deposit amount.
23+
> If the vault is empty (`total_assets = 0` or `total_shares = 0`), shares are minted 1:1 with
24+
> the deposit amount.
2025
2126
**Asset Redemption Formula (Withdrawal):**
2227
```
@@ -27,135 +32,241 @@ assets_to_return = shares_burned × total_assets / total_shares
2732
```
2833
share_price = total_assets / total_shares
2934
```
30-
As yield accrues, `total_assets` increases while `total_shares` stays constant — meaning each share becomes redeemable for more USDC over time.
35+
36+
As yield accrues, `total_assets` increases while `total_shares` stays constant — each share
37+
becomes redeemable for more USDC over time.
38+
39+
Source: [`contracts/vault/src/lib.rs`](../contracts/vault/src/lib.rs)`calculate_shares`,
40+
`calculate_assets`.
3141

3242
---
3343

3444
### 2.2 Yield Accrual Model
3545

36-
Yield is accrued by the admin (or strategy contract in future phases) calling `accrue_yield(amount)`. This transfers real USDC into the vault and bumps `total_assets`, immediately increasing the share price for all existing holders.
46+
There are three yield accrual paths, all of which increase `total_assets` without minting new
47+
shares, thereby raising the share price for all existing holders.
48+
49+
#### 2.2.1 Admin Direct Accrual (`accrue_yield`)
50+
51+
The admin transfers USDC directly into the vault:
52+
3753
```
3854
new_total_assets = total_assets + yield_amount
3955
new_share_price = new_total_assets / total_shares
4056
```
4157

42-
This means yield is **socialized proportionally** — all shareholders benefit instantly and equally based on their share holdings.
58+
#### 2.2.2 BENJI Strategy Push (`report_benji_yield`)
59+
60+
The configured BENJI strategy contract calls back into the vault to report harvested yield.
61+
The vault verifies the caller matches the on-chain `BenjiStrategy` address before accepting
62+
the transfer:
63+
64+
```
65+
require strategy == configured_benji_strategy
66+
new_total_assets = total_assets + amount
67+
```
68+
69+
#### 2.2.3 Korean Sovereign Debt Pull (`accrue_korean_debt_yield`)
70+
71+
The admin triggers a pull-based harvest from the configured Korean debt strategy contract.
72+
The strategy's `harvest_yield()` is called and the returned amount is added to `total_assets`:
73+
74+
```
75+
harvested = KoreanDebtStrategy.harvest_yield()
76+
require harvested > 0
77+
new_total_assets = total_assets + harvested
78+
```
79+
80+
Yield is **socialized proportionally** across all shareholders — no new shares are minted,
81+
so every existing holder's redemption value increases equally.
4382

4483
---
4584

46-
### 2.3 Worked Example (From Test Snapshot)
85+
### 2.3 Korean Sovereign Debt Yield Curve Model
86+
87+
The `MockKoreanSovereignStrategy` contract implements a linear step-up yield curve used for
88+
testing and simulation:
89+
90+
```
91+
yield(epoch) = base_yield + step_yield × epoch
92+
```
93+
94+
| Parameter | Description |
95+
|--------------|--------------------------------------------------|
96+
| `base_yield` | Fixed yield floor per harvest epoch (in stroops) |
97+
| `step_yield` | Incremental yield added each successive epoch |
98+
| `epoch` | Auto-incremented counter on each `harvest_yield` call |
99+
100+
This models a bond instrument where coupon payments increase over time. The admin can update
101+
`base_yield` and `step_yield` at any time via `set_yield_curve`.
102+
103+
Source: [`contracts/mock-strategy/src/lib.rs`](../contracts/mock-strategy/src/lib.rs).
47104

48-
| Step | Action | total_assets | total_shares | Share Price |
49-
|---|---|---|---|---|
50-
| 1 | User A deposits 100 USDC | 100 | 100 | 1.00 |
51-
| 2 | User B deposits 200 USDC | 300 | 300 | 1.00 |
52-
| 3 | Admin accrues 30 USDC yield | 330 | 300 | 1.10 |
53-
| 4 | User A withdraws 100 shares | 220 | 200 | 1.10 |
54-
| 5 | User B withdraws 100 shares | 110 | 100 | 1.10 |
105+
---
106+
107+
### 2.4 Worked Example
55108

56-
**User A final balance:** 110 USDC (deposited 100, earned 10 from yield)
57-
**User B final balance:** 910 USDC remaining (deposited 200, partial withdrawal of 110)
109+
| Step | Action | total_assets | total_shares | Share Price |
110+
|------|-------------------------------|-------------|-------------|-------------|
111+
| 1 | User A deposits 100 USDC | 100 | 100 | 1.00 |
112+
| 2 | User B deposits 200 USDC | 300 | 300 | 1.00 |
113+
| 3 | Admin accrues 30 USDC yield | 330 | 300 | 1.10 |
114+
| 4 | User A withdraws 100 shares | 220 | 200 | 1.10 |
115+
| 5 | User B withdraws 100 shares | 110 | 100 | 1.10 |
58116

59-
This matches the test snapshot final state:
60-
- `TotalAssets: 110`
61-
- `TotalShares: 100`
62-
- User A share balance: `0`
63-
- User B share balance: `100`
117+
- User A: deposited 100 USDC, withdrew 110 USDC (+10 yield)
118+
- User B: deposited 200 USDC, partial withdrawal of 110 USDC, 100 shares remain
64119

65120
---
66121

67-
## 3. RWA Risk Management Parameters
122+
## 3. DAO Governance & Strategy Allocation
123+
124+
Strategy addresses are not hardcoded — they are set through an on-chain governance proposal
125+
lifecycle.
68126

69-
### 3.1 Current Risk Parameters
127+
### 3.1 Proposal Lifecycle
128+
129+
```
130+
create_strategy_proposal(proposer, strategy) → proposal_id
131+
vote_on_proposal(voter, proposal_id, support, weight)
132+
execute_strategy_proposal(proposal_id) → sets BenjiStrategy
133+
```
70134

71-
| Parameter | Value | Description |
72-
|---|---|---|
73-
| Underlying Asset | USDC (Stellar) | Stablecoin deposit currency |
74-
| Token Decimals | 7 | Standard Stellar token precision |
75-
| Min Deposit | > 0 USDC | Any positive amount accepted |
76-
| Min Withdrawal | > 0 shares | Any positive share amount accepted |
77-
| Max Deposit | None (Phase 1) | No cap in current implementation |
78-
| Admin Control | Single admin key | Controls yield accrual and strategy allocation |
79-
| Protocol Version | Soroban v22 | Stellar Soroban smart contract runtime |
80-
| Entry TTL (Persistent) | 6,312,000 ledgers | ~1 year at 5s/ledger |
81-
| Entry TTL (Temporary) | 16 ledgers minimum | Short-lived auth entries |
135+
### 3.2 Execution Rules
136+
137+
| Rule | Detail |
138+
|------|--------|
139+
| Quorum | `yes_votes >= dao_threshold` |
140+
| Majority | `yes_votes > no_votes` |
141+
| One vote per address | Duplicate votes are rejected |
142+
| Immutable once executed | `executed = true` blocks re-execution |
143+
144+
`dao_threshold` is set by the admin via `set_dao_threshold` and must be `> 0`.
145+
146+
The Korean debt strategy bypasses governance — it is set directly by the admin via
147+
`configure_korean_strategy`. This is a Phase 1 simplification; governance coverage for all
148+
strategies is planned for Phase 3.
82149

83150
---
84151

85-
### 3.2 Risk Categories
152+
## 4. RWA Risk Parameters
153+
154+
### 4.1 Current Risk Parameters
155+
156+
| Parameter | Value | Description |
157+
|---------------------|------------------------|--------------------------------------------------|
158+
| Underlying Asset | USDC (Stellar) | Stablecoin deposit currency |
159+
| Token Decimals | 7 | Standard Stellar stroop precision |
160+
| Min Deposit | > 0 | Any positive stroop amount accepted |
161+
| Min Withdrawal | > 0 shares | Any positive share amount accepted |
162+
| Max Deposit | None (Phase 1) | No cap in current implementation |
163+
| Max Page Size | 50 | Shipment pagination hard cap |
164+
| Admin Control | Single admin key | Controls yield accrual and strategy allocation |
165+
| DAO Threshold | Configurable (default 1) | Minimum yes-votes to execute a proposal |
166+
| Protocol Version | Soroban v22 | Stellar Soroban smart contract runtime |
167+
| Entry TTL (Persistent) | 6,312,000 ledgers | ~1 year at 5 s/ledger |
168+
| Entry TTL (Temporary) | 16 ledgers minimum | Short-lived auth entries |
169+
170+
### 4.2 Risk Categories
86171

87172
**Smart Contract Risk**
88-
The vault is a single Soroban contract with no upgrade mechanism in Phase 1. All state is stored in instance storage. An audit is required before mainnet deployment (Phase 4).
173+
Single Soroban contract with no upgrade mechanism in Phase 1. All state is stored in instance
174+
storage. An audit is required before mainnet deployment (Phase 4).
89175

90176
**Counterparty Risk**
91-
In Phase 1, yield is manually accrued by a trusted admin. In future phases, yield will be pulled from RWA issuers (e.g. Franklin Templeton BENJI, tokenized Korean bonds, US Treasuries) via strategy bridge contracts. Each RWA issuer introduces its own counterparty risk.
177+
Phase 1 yield is manually accrued by a trusted admin. Phase 3+ yield is pulled from RWA issuers
178+
(Franklin Templeton BENJI, tokenized Korean sovereign bonds) via strategy bridge contracts. Each
179+
issuer introduces its own counterparty risk.
92180

93181
**Liquidity Risk**
94-
Withdrawals are processed immediately against vault USDC balance. If vault funds are deployed into illiquid RWA strategies in future phases, a withdrawal queue or lock-up period may be required.
182+
Withdrawals are processed immediately against the vault's USDC balance. If funds are deployed
183+
into illiquid RWA strategies in future phases, a withdrawal queue or lock-up period may be
184+
required.
95185

96186
**Admin Key Risk**
97-
The current implementation uses a single admin address for yield accrual and strategy control. Phase 2 will introduce multi-sig or DAO governance to mitigate this risk.
187+
A single admin address controls yield accrual, strategy configuration, and DAO threshold. Phase 2
188+
introduces multi-sig or DAO governance to mitigate this.
98189

99190
**Oracle / Price Risk**
100-
Phase 1 has no oracle dependency — USDC is treated as 1:1 USD. Future phases integrating non-stablecoin RWAs will require price feeds and introduce oracle risk.
101-
102-
---
191+
Phase 1 has no oracle dependency — USDC is treated as 1:1 USD. Future phases integrating
192+
non-stablecoin RWAs will require price feeds and introduce oracle risk.
103193

104-
## 4. Contract State Reference
105-
106-
| State Key | Type | Description |
107-
|---|---|---|
108-
| `Admin` | Address | Controls yield accrual and initialization |
109-
| `Token` | Address | Underlying USDC token contract address |
110-
| `TotalShares` | i128 | Total vault shares currently minted |
111-
| `TotalAssets` | i128 | Total USDC held/tracked by the vault |
112-
| `ShareBalance(Address)` | i128 | Individual user share balance |
194+
**Governance Attack Risk**
195+
The DAO threshold defaults to `1`, meaning a single vote can pass a proposal in Phase 1. This is
196+
intentional for testnet iteration. Threshold must be raised before mainnet deployment.
113197

114198
---
115199

116-
## 5. Contract Functions Reference
200+
## 5. Strategy Integrations
117201

118-
| Function | Access | Description |
119-
|---|---|---|
120-
| `initialize(admin, token)` | Admin | Sets up vault with USDC token and admin |
121-
| `deposit(user, amount)` | User | Deposits USDC, mints proportional shares |
122-
| `withdraw(user, shares)` | User | Burns shares, returns proportional USDC |
123-
| `accrue_yield(amount)` | Admin only | Transfers yield into vault, raises share price |
124-
| `calculate_shares(assets)` | Read-only | Returns shares for a given asset amount |
125-
| `calculate_assets(shares)` | Read-only | Returns assets redeemable for given shares |
126-
| `total_shares()` | Read-only | Returns total minted shares |
127-
| `total_assets()` | Read-only | Returns total vault assets |
128-
| `balance(user)` | Read-only | Returns a user's share balance |
202+
### 5.1 Active / Planned Strategies
129203

130-
---
204+
| Strategy | Asset Type | Yield Model | Risk Level | Status |
205+
|---------------------------------|-------------------------|----------------------|-------------|------------|
206+
| Franklin Templeton BENJI | US Treasury Money Market | Push (callback) | Low | Phase 2 |
207+
| Korean Sovereign Debt (Mock) | Sovereign Debt Bond | Pull (step-up curve) | Low-Medium | Phase 2 |
208+
| Stellar-native RWA Bridges | TBD | TBD | Medium | Phase 3+ |
131209

132-
## 6. Planned Strategy Integrations (Phase 3+)
210+
### 5.2 BENJI Connector
133211

134-
| Strategy | Asset Type | Target APY | Risk Level |
135-
|---|---|---|---|
136-
| Franklin Templeton BENJI | US Treasury Fund | ~5% | Low |
137-
| Tokenized Korean Sovereign Bonds | Sovereign Debt | ~4-6% | Low-Medium |
138-
| Stellar-native RWA Bridges | TBD | TBD | Medium |
212+
- Strategy type: **push-based** — the strategy contract calls `report_benji_yield(strategy, amount)`.
213+
- Authorization: vault verifies `strategy == BenjiStrategy` on-chain before accepting funds.
214+
- Strategy address is set via DAO governance (`execute_strategy_proposal`).
215+
- Frontend metadata: [`frontend/src/lib/strategy.ts`](../frontend/src/lib/strategy.ts).
139216

140-
> Note: APY figures are indicative and subject to market conditions. Risk parameters will be updated as integrations are confirmed.
217+
### 5.3 Korean Sovereign Debt Strategy
141218

142-
---
219+
- Strategy type: **pull-based** — vault admin calls `accrue_korean_debt_yield()`, which invokes
220+
`KoreanDebtStrategy.harvest_yield()` on the configured contract.
221+
- Yield model: linear step-up curve — `yield(epoch) = base_yield + step_yield × epoch`.
222+
- Strategy address is set directly by admin via `configure_korean_strategy`.
143223

144-
## 7. Changelog
224+
---
145225

146-
| Version | Date | Change |
147-
|---|---|---|
148-
| 0.1.0 | 2026-03-24 | Initial wiki — Phase 1 strategy and risk documentation |
149-
```
226+
## 6. Contract State Reference
227+
228+
| State Key | Type | Description |
229+
|------------------------------|-----------|--------------------------------------------------|
230+
| `Admin` | Address | Controls yield accrual and initialization |
231+
| `TokenAsset` | Address | Underlying USDC token contract address |
232+
| `TotalShares` | i128 | Total vault shares currently minted |
233+
| `TotalAssets` | i128 | Total USDC held/tracked by the vault |
234+
| `ShareBalance(Address)` | i128 | Individual user share balance |
235+
| `BenjiStrategy` | Address | Active BENJI strategy (set via governance) |
236+
| `KoreanDebtStrategy` | Address | Korean debt strategy (set by admin) |
237+
| `DaoThreshold` | i128 | Minimum yes-votes required to execute a proposal |
238+
| `ProposalNonce` | u32 | Auto-incrementing proposal ID counter |
239+
| `Proposal(u32)` | StrategyProposal | Proposal state by ID |
240+
| `Vote(u32, Address)` | bool | Deduplication guard per voter per proposal |
150241

151242
---
152243

153-
## Where to Place It
154-
155-
Save this file as `docs/strategy-allocation-wiki.md` in your project root.
244+
## 7. Contract Functions Reference
245+
246+
| Function | Access | Description |
247+
|-----------------------------------|------------|----------------------------------------------------------|
248+
| `initialize(admin, token)` | Admin | Sets up vault with USDC token and admin |
249+
| `deposit(user, amount)` | User | Deposits USDC, mints proportional shares |
250+
| `withdraw(user, shares)` | User | Burns shares, returns proportional USDC |
251+
| `accrue_yield(amount)` | Admin only | Transfers yield into vault, raises share price |
252+
| `report_benji_yield(strategy, amount)` | BENJI strategy | Push-based yield from BENJI connector |
253+
| `accrue_korean_debt_yield()` | Admin only | Pull-based yield harvest from Korean debt strategy |
254+
| `configure_korean_strategy(addr)` | Admin only | Sets the Korean debt strategy contract address |
255+
| `create_strategy_proposal(proposer, strategy)` | Any | Opens a governance proposal for a new strategy |
256+
| `vote_on_proposal(voter, id, support, weight)` | Any | Casts a weighted vote on a proposal |
257+
| `execute_strategy_proposal(id)` | Any | Executes a passed proposal, sets `BenjiStrategy` |
258+
| `set_dao_threshold(threshold)` | Admin only | Updates the minimum yes-vote quorum |
259+
| `calculate_shares(assets)` | Read-only | Returns shares for a given asset amount |
260+
| `calculate_assets(shares)` | Read-only | Returns assets redeemable for given shares |
261+
| `total_shares()` | Read-only | Returns total minted shares |
262+
| `total_assets()` | Read-only | Returns total vault assets |
263+
| `balance(user)` | Read-only | Returns a user's share balance |
156264

157265
---
158266

159-
## Commit Message
160-
```
161-
docs: add strategy allocation and RWA risk management wiki
267+
## 8. Changelog
268+
269+
| Version | Date | Change |
270+
|---------|------------|------------------------------------------------------------------------|
271+
| 0.2.0 | 2026-03-25 | Full rewrite: added DAO governance model, Korean debt yield curve, BENJI push model, complete state/function reference, updated risk parameters |
272+
| 0.1.0 | 2026-03-24 | Initial wiki — Phase 1 strategy and risk documentation |

0 commit comments

Comments
 (0)