CurveTAREStrategy¶
ERC-4626-shaped MSV strategy. Single-sided USDC into TARE/USDC Curve StableSwap-NG LP — deepens PegKeeper liquidity; earns swap fees via virtual_price appreciation.
Source: khomdev-keep/src/strategies/CurveTAREStrategy.vy (Vyper 0.4.x)
Concept: Keep↔TARE symbiosis — MultiStrategyVault fallback strategy
Implementation overview¶
- Valuation —
totalAssets = lp_balance * get_virtual_price() / value_divisor(not spot withdraw quote) - Rewards — fees compound into virtual price;
harvest()/sync()no-ops - APR — cached
apr_bpsfor smart routing (admin-set, not on-chain realised) - Shares — internal accounting; only
vaultdeposits/withdraws - Slippage —
slippage_bpsfloors add/remove liquidity via virtual price
flowchart LR
MSV[MultiStrategyVault] --> CTS[CurveTAREStrategy]
CTS -->|single-sided USDC| Pool[TARE/USDC Curve NG]
Pool --> PK[PegKeeper liquidity]
Immutables¶
| Name | Role |
|---|---|
asset |
USDC |
vault |
Parent MSV |
pool |
Curve NG pool (also LP token) |
usdc_index / tare_index |
Coin indices |
value_divisor |
10**(36 - asset_decimals) for VP math |
Roles¶
| Role | Powers |
|---|---|
DEFAULT_ADMIN_ROLE |
APR, slippage, pause deposits, sweep stray tokens |
Events¶
| Event | When |
|---|---|
Deposit / Withdraw |
Vault capital flows |
AprUpdated / SlippageUpdated / DepositsPaused |
Admin config |
ERC-4626 views¶
totalAssets¶
CurveTAREStrategy.totalAssets() -> uint256
Strategy NAV from LP virtual price (manipulation-resistant).
Returns: uint256 — lp_balance * get_virtual_price() / value_divisor.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:147-148, _total_assets 363-368):
@external
@view
def totalAssets() -> uint256:
return self._total_assets()
# lp * get_virtual_price() // value_divisor
Example:
Cross-links: convertToShares · MultiStrategyVault report
balanceOf¶
CurveTAREStrategy.balanceOf(account) -> uint256
Internal strategy shares for account.
| Param | Type | Description |
|---|---|---|
account |
address |
Share holder (MSV in practice) |
Returns: uint256 — shares_held[account].
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:152-153):
Cross-links: totalSupply · convertToAssets
totalSupply¶
CurveTAREStrategy.totalSupply() -> uint256
Total strategy shares (vault position).
Returns: uint256 — total_shares.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:157-158):
Cross-links: totalAssets · balanceOf
convertToAssets¶
CurveTAREStrategy.convertToAssets(shares) -> uint256
Shares → USDC quote (floor) at current virtual-price NAV.
| Param | Type | Description |
|---|---|---|
shares |
uint256 |
Strategy shares |
Returns: uint256 — USDC value via total_shares / totalAssets ratio.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:162-163):
@external
@view
def convertToAssets(shares: uint256) -> uint256:
return self._convert_to_assets(shares, False)
Cross-links: convertToShares · previewRedeem
convertToShares¶
CurveTAREStrategy.convertToShares(assets) -> uint256
USDC → shares quote (floor).
| Param | Type | Description |
|---|---|---|
assets |
uint256 |
USDC amount |
Returns: uint256 — shares at current NAV.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:167-168):
@external
@view
def convertToShares(assets: uint256) -> uint256:
return self._convert_to_shares(assets, False)
Cross-links: convertToAssets · previewDeposit
maxDeposit¶
CurveTAREStrategy.maxDeposit(receiver) -> uint256
Max USDC depositable (ERC-4626 view).
| Param | Type | Description |
|---|---|---|
receiver |
address |
Must be vault |
Returns: uint256 — 0 if paused or receiver != vault; else max_uint256.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:172-175):
@external
@view
def maxDeposit(receiver: address) -> uint256:
if deposits_paused or receiver != vault:
return 0
return max_value(uint256)
Cross-links: deposit · maxMint
maxWithdraw¶
CurveTAREStrategy.maxWithdraw(owner) -> uint256
Max USDC withdrawable for owner.
| Param | Type | Description |
|---|---|---|
owner |
address |
Must be vault |
Returns: uint256 — convertToAssets(shares_held[vault]); 0 if not vault.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:179-182):
@external
@view
def maxWithdraw(owner: address) -> uint256:
if owner != vault:
return 0
return self._convert_to_assets(self.shares_held[owner], False)
Cross-links: withdraw · maxRedeem
previewDeposit¶
CurveTAREStrategy.previewDeposit(assets) -> uint256
Simulate deposit — shares minted (floor).
| Param | Type | Description |
|---|---|---|
assets |
uint256 |
USDC in |
Returns: uint256 — expected shares (= convertToShares).
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:186-187):
@external
@view
def previewDeposit(assets: uint256) -> uint256:
return self._convert_to_shares(assets, False)
Cross-links: deposit · convertToShares
previewWithdraw¶
CurveTAREStrategy.previewWithdraw(assets) -> uint256
Simulate withdraw — shares burned (ceil).
| Param | Type | Description |
|---|---|---|
assets |
uint256 |
USDC out |
Returns: uint256 — shares required.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:191-192):
@external
@view
def previewWithdraw(assets: uint256) -> uint256:
return self._convert_to_shares(assets, True)
Cross-links: withdraw · previewRedeem
previewRedeem¶
CurveTAREStrategy.previewRedeem(shares) -> uint256
Simulate redeem — USDC out (floor).
| Param | Type | Description |
|---|---|---|
shares |
uint256 |
Shares to burn |
Returns: uint256 — expected USDC (= convertToAssets).
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:196-197):
@external
@view
def previewRedeem(shares: uint256) -> uint256:
return self._convert_to_assets(shares, False)
Cross-links: redeem · convertToAssets
maxMint¶
CurveTAREStrategy.maxMint(receiver) -> uint256
Max shares mintable (vault-only).
| Param | Type | Description |
|---|---|---|
receiver |
address |
Must be vault |
Returns: uint256 — same gates as maxDeposit.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:201-204):
@external
@view
def maxMint(receiver: address) -> uint256:
if deposits_paused or receiver != vault:
return 0
return max_value(uint256)
Cross-links: mint · maxDeposit
maxRedeem¶
CurveTAREStrategy.maxRedeem(owner) -> uint256
Max shares redeemable by owner.
| Param | Type | Description |
|---|---|---|
owner |
address |
Must be vault |
Returns: uint256 — full shares_held[owner]; 0 if not vault.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:208-211):
@external
@view
def maxRedeem(owner: address) -> uint256:
if owner != vault:
return 0
return self.shares_held[owner]
Cross-links: redeem · maxWithdraw
IStrategy hooks¶
getApr¶
CurveTAREStrategy.getApr() -> uint256
Cached APR signal for MSV smart routing (admin-set; fees accrue via LP virtual price, not this field).
Returns: uint256 — apr_bps.
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:219-220):
Cross-links: set_apr_bps · Rebalancer compute_smart_targets
harvest¶
CurveTAREStrategy.harvest() -> uint256
No-op. Swap fees compound into LP get_virtual_price() — nothing to claim separately.
Returns: uint256 — always 0.
Access: Any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:223-225):
Cross-links: totalAssets · MultiStrategyVault report
sync¶
CurveTAREStrategy.sync()
No-op. NAV reads get_virtual_price() live on every totalAssets call.
Access: Any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:228-230):
Cross-links: totalAssets · harvest
deployAvailable¶
CurveTAREStrategy.deployAvailable(amount) -> uint256
Fallback deploy capacity — accepts full amount (vault max_debt is the real cap).
| Param | Type | Description |
|---|---|---|
amount |
uint256 |
Requested USDC deployment |
Returns: uint256 — always max_uint256 (unlike CoilMaker's idle-based cap).
Access: view, any caller.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:239-244):
Cross-links: MultiStrategyVault allocateCapital · CoilMakerStrategy deployAvailable
ERC-4626 writes¶
deposit¶
CurveTAREStrategy.deposit(assets, receiver) -> uint256
Vault deposits USDC; strategy single-sided adds to Curve LP; mints internal shares.
| Param | Type | Description |
|---|---|---|
assets |
uint256 |
USDC amount |
receiver |
address |
Must be vault |
Returns: uint256 — shares minted.
Returns / state: Pulls USDC from vault → _add_liquidity with slippage floor from virtual price.
Access: vault only. nonreentrant. Reverts if deposits_paused.
Events: Deposit.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:253-267):
@external
@nonreentrant
def deposit(assets, receiver) -> uint256:
self._only_vault()
shares = self._convert_to_shares(assets, False)
self._add_liquidity(assets)
total_shares += shares; shares_held[receiver] += shares
return shares
Cross-links: previewDeposit · withdraw
mint¶
CurveTAREStrategy.mint(shares, receiver) -> uint256
Mint exact shares; vault pays ceil-rounded USDC into Curve LP.
| Param | Type | Description |
|---|---|---|
shares |
uint256 |
Target shares |
receiver |
address |
Must be vault |
Returns: uint256 — USDC pulled from vault.
Access: vault only. Same gates as deposit.
Events: Deposit.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:271-284):
@external
@nonreentrant
def mint(shares, receiver) -> uint256:
assets = self._convert_to_assets(shares, True)
self._add_liquidity(assets)
total_shares += shares; shares_held[receiver] += shares
return assets
Cross-links: deposit · maxMint
withdraw¶
CurveTAREStrategy.withdraw(assets, receiver, owner) -> uint256
Burn shares; remove USDC liquidity from Curve pool to receiver.
| Param | Type | Description |
|---|---|---|
assets |
uint256 |
Target USDC |
receiver |
address |
Payout address |
owner |
address |
Must be vault |
Returns: uint256 — shares burned.
Access: vault only. nonreentrant. Full exit if requested shares ≥ held.
Events: Withdraw.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:288-306):
@external
@nonreentrant
def withdraw(assets, receiver, owner) -> uint256:
# burn shares; _remove_liquidity(assets_eff, receiver, full)
return shares
Cross-links: redeem · previewWithdraw
redeem¶
CurveTAREStrategy.redeem(shares, receiver, owner) -> uint256
Burn exact shares; remove USDC from Curve LP to receiver.
| Param | Type | Description |
|---|---|---|
shares |
uint256 |
Shares to burn |
receiver |
address |
USDC recipient |
owner |
address |
Must be vault |
Returns: uint256 — USDC paid (got from _remove_liquidity).
Access: vault only. nonreentrant.
Events: Withdraw (logs actual USDC got).
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:310-322):
@external
@nonreentrant
def redeem(shares, receiver, owner) -> uint256:
assets_eff = self._convert_to_assets(shares, False)
got = self._remove_liquidity(assets_eff, receiver, full)
return got
Cross-links: withdraw · previewRedeem
Admin¶
set_apr_bps¶
CurveTAREStrategy.set_apr_bps(new_bps)
Update cached APR for smart routing.
| Param | Type | Description |
|---|---|---|
new_bps |
uint256 |
APR bps (≤ MAX_APR_BPS = 5000) |
Access: DEFAULT_ADMIN_ROLE only.
Events: AprUpdated(new_bps).
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:329-333):
@external
def set_apr_bps(new_bps: uint256):
access_control._check_role(DEFAULT_ADMIN_ROLE, msg.sender)
self.apr_bps = new_bps
Cross-links: getApr
set_slippage_bps¶
CurveTAREStrategy.set_slippage_bps(new_bps)
Slippage tolerance for add/remove liquidity vs virtual price.
| Param | Type | Description |
|---|---|---|
new_bps |
uint256 |
Slippage bps (≤ MAX_SLIPPAGE_BPS = 1000) |
Access: DEFAULT_ADMIN_ROLE only.
Events: SlippageUpdated(new_bps).
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:336-340):
@external
def set_slippage_bps(new_bps: uint256):
access_control._check_role(DEFAULT_ADMIN_ROLE, msg.sender)
self.slippage_bps = new_bps
Cross-links: deposit · withdraw
set_deposits_paused¶
CurveTAREStrategy.set_deposits_paused(state)
Pause/resume vault deposits. Withdrawals always allowed.
| Param | Type | Description |
|---|---|---|
state |
bool |
True = pause deposits |
Access: DEFAULT_ADMIN_ROLE only.
Events: DepositsPaused(state).
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:343-346):
@external
def set_deposits_paused(state: bool):
access_control._check_role(DEFAULT_ADMIN_ROLE, msg.sender)
self.deposits_paused = state
Cross-links: deposit · maxDeposit
sweep¶
CurveTAREStrategy.sweep(token, to)
Recover stray ERC-20 (not USDC or pool LP token).
| Param | Type | Description |
|---|---|---|
token |
address |
Token to sweep |
to |
address |
Recipient |
Access: DEFAULT_ADMIN_ROLE only. Cannot sweep asset or pool.
Source (khomdev-keep/src/strategies/CurveTAREStrategy.vy:349-355):
@external
def sweep(token: address, to: address):
assert token != asset and token != pool
IERC20(token).transfer(to, IERC20(token).balanceOf(self))