diff --git a/.prettierignore b/.prettierignore index af1f7c6..2baa54b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,4 +8,5 @@ *.mdx *.md *.hbs -*.gif \ No newline at end of file +*.gif +*.svg diff --git a/docs/concepts/glossary.md b/docs/concepts/glossary.md index 20c3015..2d99854 100644 --- a/docs/concepts/glossary.md +++ b/docs/concepts/glossary.md @@ -6,7 +6,7 @@ sidebar_position: 6 ## Automated Market Maker -An automated market maker is a smart contract on Ethereum that holds liquidity reserves. Users can trade against these reserves at prices determined by a fixed formula. Anyone may contribute liquidity to these smart contracts, earning pro-rata trading fees in return. +An automated market maker is a smart contract on Ethereum that holds liquidity reserves. Users can trade against these reserves at prices determined by a fixed formula. Anyone may contribute liquidity to these smart contracts and receive pro-rata trading fees in return. ## Asset diff --git a/docs/concepts/protocol/swaps.md b/docs/concepts/protocol/swaps.md index 7e9a4f5..ec0ab1b 100644 --- a/docs/concepts/protocol/swaps.md +++ b/docs/concepts/protocol/swaps.md @@ -10,7 +10,7 @@ Swaps are the most common way of interacting with the Ring protocol. For end-use > note: Using web interfaces (websites) to swap via the Ring protocol can introduce additional permission structures, and may result in different execution behavior compared to using the Ring protocol directly. To learn more about the differences between the protocol and a web interface, see [Ring Overview](/concepts/overview). -Swaps using the Ring protocol are different from traditional order book trades in that they are not executed against discrete orders on a first-in-first-out basis — rather, swaps execute against a passive pool of liquidity, with liquidity providers earning fees proportional to their capital committed +Swaps using the Ring protocol are different from traditional order book trades in that they are not executed against discrete orders on a first-in-first-out basis — rather, swaps execute against a passive pool of liquidity, with fees allocated to liquidity providers in proportion to their capital committed ## Price Impact diff --git a/docs/concepts/ring-protocol.md b/docs/concepts/ring-protocol.md index fa0f773..7a38877 100644 --- a/docs/concepts/ring-protocol.md +++ b/docs/concepts/ring-protocol.md @@ -33,7 +33,7 @@ The core idea is simple: 1. wrap original assets into `FewToken` 2. use `FewToken` across trading and routing systems 3. improve usable liquidity and quote competitiveness -4. earn more order flow and fees from stronger execution +4. capture more order flow and fees from stronger execution ### What Ring is not diff --git a/docs/contracts/morpho-router/_category_.json b/docs/contracts/morpho-router/_category_.json deleted file mode 100644 index 5764005..0000000 --- a/docs/contracts/morpho-router/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "label": "Lending Router", - "position": 2.3, - "collapsed": true -} diff --git a/docs/contracts/morpho-router/deployments.md b/docs/contracts/morpho-router/deployments.md deleted file mode 100644 index 988ffcf..0000000 --- a/docs/contracts/morpho-router/deployments.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Deployment Addresses -sidebar_position: 3 -unlisted: true ---- - -# FewMorphoRouter Deployments - -The current tracked deployment in the router repository is on Ethereum Sepolia. - -## Sepolia - -| Contract | Address | -| --- | --- | -| FewMorphoRouter | `0xd5ee07e77209df777728B922FbDc52222F32d481` | -| Owner | `0xA1e54Ef984302C863f4e359fbF04C100F02a1c30` | -| Guardian | `0xA1e54Ef984302C863f4e359fbF04C100F02a1c30` | -| Morpho | `0xd011EE229E7459ba1ddd22631eF7bF528d424A14` | -| AdaptiveCurveIRM | `0x8C5dDCD3F601c91D1BF51c8ec26066010ACAbA7c` | -| Oracle Factory | `0xa6c843fc53aAf6EF1d173C4710B26419667bF6CD` | -| MetaMorphoFactory | `0x98CbFE4053ad6778E0E3435943aC821f565D0b03` | -| WETH | `0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14` | -| DAI | `0x5fbad067f69eBbc276410D78fF52823be133eD48` | -| USDC | `0xA7a151d60Bbd522d3611C2dB3F1F972Ee9904B3e` | -| fwWETH | `0x98b902eF4f9fEB2F6982ceEB4E98761294854D61` | -| fwDAI | `0x09D8486e42Aa76229a563bFa0f07CA301aCd29C9` | -| fwUSDC | `0x76AC72683C5b7F22C6B5Ed85B5B1511702464F7e` | -| fwDAI Vault | `0x9D9d28A3ae26a40Ce8B904c89Cc3C515EF1C0475` | - -## Market Configuration - -| Parameter | Value | -| --- | --- | -| Chain ID | `11155111` | -| Market ID | `0xCCB30F9DB4B3ADD563F8FE45154FA809AB79C61D85BB39766759CA28A4CCF136` | -| Loan Token | `0x09D8486e42Aa76229a563bFa0f07CA301aCd29C9` (`fwDAI`) | -| Collateral Token | `0x98b902eF4f9fEB2F6982ceEB4E98761294854D61` (`fwWETH`) | -| Oracle | `0x3C8bed4E15e76Cf1a5725B81BBeC0481053DD13d` | -| IRM | `0x8C5dDCD3F601c91D1BF51c8ec26066010ACAbA7c` | -| LLTV | `945000000000000000` | - -## Wrapper Notes - -Current wrapper assumptions documented in the router repository: - -- `fwWETH` and `fwDAI` are treated as 1:1 wrappers -- both wrappers use 18 decimals in the current Sepolia setup -- `token()` on the wrapper returns the underlying asset -- `fwUSDC` exists on Sepolia but is not part of the current router scope - -## Current Whitelist Scope - -The expected first-version whitelist is: - -- wrappers: `fwWETH`, `fwDAI` -- vaults: `fwDAI Vault` -- markets: the `fwWETH` / `fwDAI` market above - -## Deployment and Configuration Scripts - -The router repository includes these scripts: - -- `script/DeployRouter.s.sol` -- `script/ConfigureRouter.s.sol` -- `script/DeployAndConfigureRouter.s.sol` - -If environment overrides are omitted, the scripts default to the Sepolia values above. diff --git a/docs/contracts/morpho-router/integration.md b/docs/contracts/morpho-router/integration.md deleted file mode 100644 index 5cc2424..0000000 --- a/docs/contracts/morpho-router/integration.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Integration Guide -sidebar_position: 2 -unlisted: true ---- - -# Integrating FewMorphoRouter - -`FewMorphoRouter` is intended for applications that want to use underlying tokens in the UI while routing into Few wrapped token markets and vaults behind the scenes. - -## User Prerequisites - -Before calling router functions, users should complete the required approvals for the flow they want to use. - -For market flows: - -- approve the underlying ERC20 to `FewMorphoRouter` -- call `Morpho.setAuthorization(router, true)` before borrow, repay, or collateral withdrawal flows - -For vault withdrawal and redeem flows: - -- approve the vault share token to `FewMorphoRouter` - -## Market Flows - -### Supply Collateral - -Use `supplyCollateralWithWrap(marketId, wrapper, underlyingAmount)` when the user wants to post underlying collateral. - -The router: - -1. pulls the underlying token from the user -2. wraps it into the provided Few wrapper -3. validates that the wrapper is the market's collateral side -4. supplies collateral into Morpho on behalf of `msg.sender` - -### Borrow - -Use `borrowAndUnwrap(marketId, wrapper, borrowAssets)` when the market loan token is a Few wrapped token but the user wants to receive the underlying asset. - -The router: - -1. validates that the wrapper is the market's loan side -2. borrows wrapped assets from Morpho -3. unwraps them -4. transfers the underlying asset to `msg.sender` - -### Repay - -Use `repayWithWrap(marketId, wrapper, underlyingAmount)` when the user is repaying a wrapped-asset debt with the underlying token. - -The router: - -1. pulls the underlying token from the user -2. wraps it -3. repays the Morpho position using the wrapped asset - -### Withdraw Collateral - -Use `withdrawCollateralAndUnwrap(marketId, wrapper, collateralAssets)` when the user wants to withdraw collateral back as the underlying token. - -The router: - -1. withdraws wrapped collateral from Morpho -2. unwraps it -3. transfers the underlying asset to `msg.sender` - -## Vault Flows - -### Deposit - -Use `vaultDepositWithWrap(vault, wrapper, underlyingAmount)` when the vault asset is a Few wrapped token but the user is depositing the underlying asset. - -The router: - -1. pulls the underlying asset -2. wraps it -3. validates that `vault.asset()` matches the wrapper -4. deposits into the vault for `msg.sender` - -### Withdraw - -Use `vaultWithdrawAndUnwrap(vault, wrapper, assets)` when the user wants vault assets back as the underlying token. - -The router: - -1. withdraws wrapped assets from the vault -2. unwraps them -3. transfers the underlying token to `msg.sender` - -### Redeem - -Use `vaultRedeemAndUnwrap(vault, wrapper, shares)` when the user wants to redeem vault shares and receive the underlying token. - -The router: - -1. redeems shares from the vault -2. unwraps the returned wrapped assets -3. transfers the underlying token to `msg.sender` - -## Frontend Integration Notes - -Frontend integrations should use tracked artifacts from the router repository: - -- ABI: `abi/FewMorphoRouter.json` -- addresses: `deployments/sepolia.json` - -Do not parse `broadcast/` or `out/` as a deployment source of truth. - -## Current First-Version Scope - -The current implementation is intentionally narrow: - -- `msg.sender` is always the position owner and receiver -- no arbitrary call support -- no generic multicall -- no swap routing -- no admin-controlled user fund movement - -This keeps the first version easier to reason about and aligns with the repository's router spec. diff --git a/docs/contracts/morpho-router/overview.md b/docs/contracts/morpho-router/overview.md deleted file mode 100644 index db8f2f4..0000000 --- a/docs/contracts/morpho-router/overview.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Overview -sidebar_position: 1 -unlisted: true ---- - -# FewMorphoRouter - -`FewMorphoRouter` is a thin routing layer built on top of Morpho Blue, Few wrapped tokens, and ERC4626 vaults. - -It is designed to let users interact with underlying assets such as `WETH` and `DAI` while the router handles the extra wrap and unwrap steps required by Few wrapped tokens before calling Morpho or a vault. - -## Scope - -This package is intentionally minimal. It includes: - -- a router contract -- router-specific interfaces and libraries -- router unit tests -- Sepolia config and fork integration tests -- deployment and whitelist configuration scripts - -It does not modify Morpho core contracts. - -## Supported Flows - -The current router implementation supports these user flows: - -- `supplyCollateralWithWrap` -- `borrowAndUnwrap` -- `repayWithWrap` -- `withdrawCollateralAndUnwrap` -- `vaultDepositWithWrap` -- `vaultWithdrawAndUnwrap` -- `vaultRedeemAndUnwrap` - -## How It Works - -At a high level, the router orchestrates four steps: - -1. Pull the underlying asset from the user. -2. Wrap or unwrap through a Few wrapped token contract. -3. Call Morpho Blue or an ERC4626 vault. -4. Return the final asset back to the same user. - -The router is an orchestration layer only. It does not manage: - -- Morpho risk configuration -- market creation -- oracle logic -- vault strategy logic -- arbitrary call execution -- swap execution - -## Validation Model - -The router does not keep an admin-managed mapping from underlying token to wrapper token. Instead, it validates relationships onchain: - -- `IFewWrappedToken(wrapper).token() == underlying` -- `IERC4626(vault).asset() == wrapper` -- `market.loanToken == wrapper` for borrow and repay flows -- `market.collateralToken == wrapper` for collateral supply and withdrawal flows - -It also resolves market parameters from `Morpho.idToMarketParams(marketId)` instead of trusting frontend-supplied market params. - -## Admin Model - -The contract exposes a small admin surface: - -- `owner` -- `guardian` -- `paused` - -The owner can: - -- set the guardian -- pause and unpause -- whitelist wrappers -- whitelist vaults -- whitelist markets -- rescue tokens while paused -- transfer ownership - -The guardian can only pause. - -## Safety Properties - -The current implementation includes: - -- `nonReentrant` protection -- pause checks on all business functions -- wrapper, vault, and market whitelists -- onchain wrapper, vault, and market consistency validation -- safe ERC20 transfers -- lazy max approvals for wrapper, vault, and Morpho interactions - -The router is currently scoped to `msg.sender` only. The first version does not support custom receivers or generic multicall behavior. diff --git a/docs/contracts/morpho-router/technical-reference.md b/docs/contracts/morpho-router/technical-reference.md deleted file mode 100644 index 99ef30d..0000000 --- a/docs/contracts/morpho-router/technical-reference.md +++ /dev/null @@ -1,464 +0,0 @@ ---- -title: Technical Reference -sidebar_position: 4 -unlisted: true ---- - -# FewMorphoRouter Technical Reference - -This page documents the external API of `FewMorphoRouter` based on the current contract implementation. - -## State Variables and Public Getters - -The router exposes these public state variables: - -| Variable | Type | Description | -| --- | --- | --- | -| `morpho` | `IMorpho` | Immutable Morpho Blue core contract used by all market flows | -| `owner` | `address` | Router owner | -| `guardian` | `address` | Pause-only guardian | -| `paused` | `bool` | Global pause flag for user-facing functions | -| `isSupportedWrapper` | `mapping(address => bool)` | Wrapper whitelist | -| `isSupportedVault` | `mapping(address => bool)` | Vault whitelist | -| `isSupportedMarket` | `mapping(bytes32 => bool)` | Market whitelist | - -Because these variables are `public`, Solidity automatically generates getter functions: - -- `morpho() returns (address)` -- `owner() returns (address)` -- `guardian() returns (address)` -- `paused() returns (bool)` -- `isSupportedWrapper(address) returns (bool)` -- `isSupportedVault(address) returns (bool)` -- `isSupportedMarket(bytes32) returns (bool)` - -## Contract Construction - -### `constructor(address morpho_, address owner_)` - -Creates a new router instance and stores the immutable Morpho address. - -| Item | Description | -| --- | --- | -| `morpho_` | Address of the Morpho Blue contract | -| `owner_` | Initial router owner | -| Returns | None | - -Fails when: - -- `morpho_ == address(0)` -- `owner_ == address(0)` - -## External Dependencies - -The router relies on a small set of external interfaces. - -### `IFewWrappedToken` - -Few wrapper tokens are expected to support: - -| Function | Purpose | -| --- | --- | -| `token()` | Returns the underlying token address | -| `wrap(uint256 assets)` | Pulls underlying and mints wrapped tokens | -| `unwrap(uint256 assets)` | Burns wrapped tokens and returns underlying | - -### `IERC4626` - -Vault integrations depend on standard ERC4626 entrypoints: - -| Function | Purpose | -| --- | --- | -| `asset()` | Returns the vault asset address | -| `deposit(uint256 assets, address receiver)` | Deposits vault assets and mints shares | -| `withdraw(uint256 assets, address receiver, address owner)` | Burns shares and withdraws assets | -| `redeem(uint256 shares, address receiver, address owner)` | Redeems shares for assets | - -## Admin Functions - -### `setOwner(address newOwner)` - -Updates the router owner. - -| Item | Description | -| --- | --- | -| `newOwner` | New owner address | -| Returns | None | - -Fails when: - -- caller is not the current owner -- `newOwner == address(0)` -- `newOwner == owner` - -### `setGuardian(address newGuardian)` - -Updates the pause-only guardian role. - -| Item | Description | -| --- | --- | -| `newGuardian` | New guardian address | -| Returns | None | - -Fails when: - -- caller is not the owner -- `newGuardian == guardian` - -### `pause()` - -Pauses all user-facing business functions. - -| Item | Description | -| --- | --- | -| Parameters | None | -| Returns | None | - -Fails when: - -- caller is neither owner nor guardian -- router is already paused - -### `unpause()` - -Unpauses the router. - -| Item | Description | -| --- | --- | -| Parameters | None | -| Returns | None | - -Fails when: - -- caller is not the owner -- router is not paused - -### `setSupportedWrapper(address wrapper, bool newIsSupported)` - -Adds or removes a wrapper from the wrapper whitelist. - -| Item | Description | -| --- | --- | -| `wrapper` | Wrapper token address | -| `newIsSupported` | Desired whitelist status | -| Returns | None | - -Fails when: - -- caller is not the owner -- `newIsSupported` matches the current whitelist value - -### `setSupportedVault(address vault, bool newIsSupported)` - -Adds or removes a vault from the vault whitelist. - -| Item | Description | -| --- | --- | -| `vault` | ERC4626 vault address | -| `newIsSupported` | Desired whitelist status | -| Returns | None | - -Fails when: - -- caller is not the owner -- `newIsSupported` matches the current whitelist value - -### `setSupportedMarket(bytes32 marketId, bool newIsSupported)` - -Adds or removes a Morpho market from the market whitelist. - -| Item | Description | -| --- | --- | -| `marketId` | Morpho market id | -| `newIsSupported` | Desired whitelist status | -| Returns | None | - -Fails when: - -- caller is not the owner -- `newIsSupported` matches the current whitelist value - -### `rescueToken(address token, address to, uint256 amount)` - -Transfers tokens out of the router in paused state. - -| Item | Description | -| --- | --- | -| `token` | Token to rescue | -| `to` | Recipient address | -| `amount` | Amount to transfer | -| Returns | None | - -Fails when: - -- caller is not the owner -- router is not paused -- `to == address(0)` -- token transfer fails - -## Market Functions - -### `supplyCollateralWithWrap(bytes32 marketId, address wrapper, uint256 underlyingAmount) returns (uint256)` - -Supplies collateral into Morpho after wrapping the user's underlying token. - -| Item | Description | -| --- | --- | -| `marketId` | Target Morpho market | -| `wrapper` | Few wrapped token used as market collateral | -| `underlyingAmount` | Amount of underlying token to transfer and wrap | -| Returns | Wrapped collateral amount supplied | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `underlyingAmount == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `marketId` is not whitelisted -- Morpho market does not exist -- market collateral token does not equal `wrapper` -- ERC20 transfer, approval, wrap, or Morpho call fails - -### `borrowAndUnwrap(bytes32 marketId, address wrapper, uint256 borrowAssets) returns (uint256 assetsBorrowed, uint256 sharesBorrowed)` - -Borrows wrapped assets from Morpho, unwraps them, and sends the underlying token to the caller. - -| Item | Description | -| --- | --- | -| `marketId` | Target Morpho market | -| `wrapper` | Few wrapped token used as market loan token | -| `borrowAssets` | Wrapped asset amount to borrow | -| `assetsBorrowed` | Actual asset amount borrowed | -| `sharesBorrowed` | Borrow shares opened in Morpho | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `borrowAssets == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `marketId` is not whitelisted -- Morpho market does not exist -- market loan token does not equal `wrapper` -- borrow, unwrap, or final transfer fails - -### `repayWithWrap(bytes32 marketId, address wrapper, uint256 underlyingAmount) returns (uint256 assetsRepaid, uint256 sharesRepaid)` - -Wraps the user's underlying token and repays a Morpho borrow position. - -| Item | Description | -| --- | --- | -| `marketId` | Target Morpho market | -| `wrapper` | Few wrapped token used as market loan token | -| `underlyingAmount` | Underlying token amount to transfer and wrap | -| `assetsRepaid` | Actual asset amount repaid | -| `sharesRepaid` | Borrow shares repaid | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `underlyingAmount == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `marketId` is not whitelisted -- Morpho market does not exist -- market loan token does not equal `wrapper` -- ERC20 transfer, approval, wrap, or Morpho repay fails - -### `withdrawCollateralAndUnwrap(bytes32 marketId, address wrapper, uint256 collateralAssets) returns (uint256)` - -Withdraws wrapped collateral from Morpho, unwraps it, and returns the underlying token. - -| Item | Description | -| --- | --- | -| `marketId` | Target Morpho market | -| `wrapper` | Few wrapped token used as market collateral token | -| `collateralAssets` | Wrapped collateral amount to withdraw | -| Returns | Underlying asset amount sent back to the user | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `collateralAssets == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `marketId` is not whitelisted -- Morpho market does not exist -- market collateral token does not equal `wrapper` -- withdraw, unwrap, or final transfer fails - -## Vault Functions - -### `vaultDepositWithWrap(address vault, address wrapper, uint256 underlyingAmount) returns (uint256 shares)` - -Wraps underlying tokens and deposits the wrapped asset into a vault. - -| Item | Description | -| --- | --- | -| `vault` | Target ERC4626 vault | -| `wrapper` | Few wrapped token expected by the vault | -| `underlyingAmount` | Underlying token amount to transfer and wrap | -| `shares` | Vault shares minted to the user | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `underlyingAmount == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `vault` is not whitelisted -- `vault` has no code -- `vault.asset() != wrapper` -- ERC20 transfer, approval, wrap, or vault deposit fails - -### `vaultWithdrawAndUnwrap(address vault, address wrapper, uint256 assets) returns (uint256 shares)` - -Withdraws wrapped vault assets, unwraps them, and transfers underlying tokens to the user. - -| Item | Description | -| --- | --- | -| `vault` | Target ERC4626 vault | -| `wrapper` | Few wrapped token expected by the vault | -| `assets` | Wrapped vault asset amount to withdraw | -| `shares` | Vault shares burned during withdrawal | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `assets == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `vault` is not whitelisted -- `vault` has no code -- `vault.asset() != wrapper` -- vault withdrawal, unwrap, or final transfer fails - -### `vaultRedeemAndUnwrap(address vault, address wrapper, uint256 shares) returns (uint256 assets)` - -Redeems vault shares, unwraps the returned wrapped assets, and sends underlying tokens to the user. - -| Item | Description | -| --- | --- | -| `vault` | Target ERC4626 vault | -| `wrapper` | Few wrapped token expected by the vault | -| `shares` | Vault shares to redeem | -| `assets` | Wrapped assets redeemed before unwrapping | - -Fails when: - -- router is paused -- reentrancy guard is triggered -- `shares == 0` -- `wrapper` is not whitelisted -- `wrapper` has no code -- `wrapper.token()` is zero or has no code -- `vault` is not whitelisted -- `vault` has no code -- `vault.asset() != wrapper` -- vault redeem, unwrap, or final transfer fails - -## Events - -The contract emits these admin and recovery events: - -- `SetOwner(address newOwner)` -- `SetGuardian(address newGuardian)` -- `SetPaused(bool newPaused)` -- `SetSupportedWrapper(address wrapper, bool newIsSupported)` -- `SetSupportedVault(address vault, bool newIsSupported)` -- `SetSupportedMarket(bytes32 marketId, bool newIsSupported)` -- `RescueToken(address token, address to, uint256 amount)` - -## Router-Specific Error Strings - -`FewMorphoRouter` uses router-local error strings from `RouterErrorsLib` in addition to reverts propagated from Morpho, wrappers, vaults, and ERC20 transfers. - -| Error | Meaning | -| --- | --- | -| `paused` | Router is paused, or `rescueToken` was called while not paused | -| `reentrancy` | Reentrant call detected | -| `not owner or guardian` | Caller is neither owner nor guardian | -| `unsupported wrapper` | Wrapper is not whitelisted | -| `unsupported vault` | Vault is not whitelisted | -| `unsupported market` | Market is not whitelisted | -| `invalid wrapper underlying` | Wrapper returned an invalid underlying token | -| `invalid market side` | Wrapper does not match the expected loan or collateral side | -| `invalid vault asset` | Vault asset does not equal the provided wrapper | -| `approve reverted` | Low-level token approval reverted | -| `approve returned false` | Token approval returned `false` | - -## Notes on Upstream Reverts - -Several functions also depend on external contracts and can revert with upstream errors from: - -- Morpho Blue -- Few wrapped token contracts -- ERC4626 vaults -- ERC20 token transfers and approvals - -In practice, integrations should treat the router checks above as the first layer of validation, not the only source of failure. - -## Internal Validation and Approval Helpers - -These helpers are not part of the external ABI, but they explain several observable revert paths. - -### `_validateWrapper(address wrapper)` - -Checks that: - -- `wrapper` is whitelisted -- `wrapper` is a contract -- `IFewWrappedToken(wrapper).token()` returns a non-zero contract address - -Returns the wrapper's underlying token address. - -### `_validateVault(address vault, address wrapper)` - -Checks that: - -- `vault` is whitelisted -- `vault` is a contract -- `IERC4626(vault).asset() == wrapper` - -### `_validateMarket(bytes32 marketId, address wrapper, bool loanSide)` - -Checks that: - -- `marketId` is whitelisted -- `Morpho.idToMarketParams(marketId)` resolves to an existing market -- if `loanSide == true`, then `market.loanToken == wrapper` -- if `loanSide == false`, then `market.collateralToken == wrapper` - -Returns the resolved Morpho market parameters. - -### `_approveMaxIfNeeded(address token, address spender, uint256 amount)` - -Implements lazy approval behavior: - -- if current allowance is already at least `amount`, no approval is sent -- otherwise the router resets approval to `0` -- then sets approval to `type(uint256).max` - -This behavior exists to support tokens that require zeroing allowance before setting a new value. - -### `_approve(address token, address spender, uint256 amount)` - -Performs the low-level approval call used by `_approveMaxIfNeeded`. - -Fails when: - -- the token call reverts -- the token returns `false` diff --git a/docs/contracts/v4/concepts/fee-structure.mdx b/docs/contracts/v4/concepts/fee-structure.mdx index a001313..3dde648 100644 --- a/docs/contracts/v4/concepts/fee-structure.mdx +++ b/docs/contracts/v4/concepts/fee-structure.mdx @@ -10,7 +10,7 @@ This page describes fee behavior in the current `FewToken + Uniswap v4` integrat In this v4 integration stack, there are three main types of fees to understand: -- **LP Fee**: Fees earned by liquidity providers +- **LP Fee**: Fees allocated to liquidity providers - **Protocol Fee**: Fees collected by the protocol - **Swap Fee**: Total fee paid by swappers (calculated by applying protocol fee and LP fee sequentially) @@ -67,9 +67,9 @@ In this v4 integration stack, the swap fee is the effective total fee after appl 1. **Protocol fee** applied first to the input amount 2. **LP fee** applied second to the remaining input (after protocol fee deduction) -**Impact on LP Earnings:** +**Impact on LP Fee Share:** -Note that this sequential application means introducing or increasing protocol fees will reduce LP earnings even if swap volume remains constant, since LPs now earn fees on a smaller base amount. +Note that this sequential application means introducing or increasing protocol fees will reduce the LP fee share even if swap volume remains constant, since LP fees are calculated on a smaller base amount. ### Fee Cap diff --git a/docs/contracts/v4/concepts/hooks.mdx b/docs/contracts/v4/concepts/hooks.mdx index c03b037..458eec8 100644 --- a/docs/contracts/v4/concepts/hooks.mdx +++ b/docs/contracts/v4/concepts/hooks.mdx @@ -55,7 +55,7 @@ The liquidity modification hooks are extremely granular for security purposes. The introduction of hooks in this v4 integration model opens up a world of possibilities for developers to innovate and build new DeFi protocols. Some potential use cases include: - Customized AMMs with different pricing curves than xy = k. -- Yield farming and liquidity mining protocols that incentivize liquidity provision. +- Pool-specific accounting or settlement extensions. - Derivative and synthetic asset platforms built on top of v4 liquidity. - Lending hooks integrated with v4 pools. diff --git a/docs/contracts/v4/concepts/subscribers.mdx b/docs/contracts/v4/concepts/subscribers.mdx index d65bff1..d33fdb6 100644 --- a/docs/contracts/v4/concepts/subscribers.mdx +++ b/docs/contracts/v4/concepts/subscribers.mdx @@ -6,8 +6,8 @@ sidebar_position: 5 This page describes subscribers in the current `FewToken + Uniswap v4` integration environment. It should be read as integration guidance, not as documentation for a separate native v4 AMM. Subscribers in this v4 integration stack allow liquidity-position owners to opt in to a contract that receives _notifications_. -The new design is intended to support _liquidity mining_, additional rewards given to in-range liquidity providers. Through notification logic, position owners do not need -to risk their liquidity position and its underlying assets. Older incentive designs often required fully transferring the +The design can support external accounting for in-range liquidity positions. Through notification logic, position owners do not need +to risk their liquidity position and its underlying assets. Older opt-in designs often required fully transferring the liquidity position to an external contract; that pattern would give the external contract full ownership and control of the liquidity position. When a position owner _subscribes_ to a contract, the contract will receive notifcations when: diff --git a/docs/contracts/v4/guides/15-calculate-lp-fees.mdx b/docs/contracts/v4/guides/15-calculate-lp-fees.mdx index 43e4e5c..c48e03a 100644 --- a/docs/contracts/v4/guides/15-calculate-lp-fees.mdx +++ b/docs/contracts/v4/guides/15-calculate-lp-fees.mdx @@ -33,7 +33,7 @@ function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams } ``` -In this guide we will go through how you can calculate fees earned for a LP position in v4 specifically the **uncollected fees** and **total lifetime fees**. +In this guide we will go through how you can calculate LP position fee amounts in v4, specifically the **uncollected fees** and **total lifetime fees**. ## Contract Setup @@ -100,7 +100,7 @@ uint128 tokenAmount = (FullMath.mulDiv(feeGrowthInsideX128 - feeGrowthInsideLastX128, liquidity, FixedPoint128.Q128)).toUint128(); ``` -### Calculate lifetime fees earned for a LP range +### Calculate lifetime fee totals for a LP range Create a new function `getLifetimeFee(PositionConfig config, uint256 tokenId)`: diff --git a/docs/contracts/v4/guides/16-subscriber.mdx b/docs/contracts/v4/guides/16-subscriber.mdx index ff30c46..02f07ff 100644 --- a/docs/contracts/v4/guides/16-subscriber.mdx +++ b/docs/contracts/v4/guides/16-subscriber.mdx @@ -6,9 +6,9 @@ title: Subscriber This page describes subscribers in the current `FewToken + Uniswap v4` integration environment. It should be read as integration guidance, not as documentation for a separate native v4 AMM. -Subscribers are a v4 mechanism that allows external contracts (aka Subscribers) to be notified by the PositionManager whenever specific events occur (e.g. liquidity changes or burns). It’s especially useful for designing **liquidity incentive** systems or reward programs. +Subscribers are a v4 mechanism that allows external contracts (aka Subscribers) to be notified by the PositionManager whenever specific events occur (e.g. liquidity changes or burns). It’s especially useful for designing external position accounting systems. -Before diving into implementation details, let's understand what makes Subscribers valuable. When a position holder subscribes their position to a Subscriber contract, that contract receives notifications about various events: liquidity changes, position burns, and subscription status changes. This flow enables protocols to accurately track and reward liquidity provision. +Before diving into implementation details, let's understand what makes Subscribers valuable. When a position holder subscribes their position to a Subscriber contract, that contract receives notifications about various events: liquidity changes, position burns, and subscription status changes. This flow enables protocols to accurately track liquidity provision. Subscribers become of aware of the liquidity position, without needing to own the position and its underlying capital. @@ -31,9 +31,9 @@ This method is called whenever a position subscribes to your contract. The `toke For example, you might use this data parameter to specify: -- Lock-up duration for rewards +- Lock-up duration for subscriptions - Campaign-specific settings -- Custom reward parameters +- Custom accounting parameters > ***Note**: The data parameter is entirely optional and its interpretation is up to your implementation.* > @@ -126,7 +126,7 @@ contract MySubscriber is ISubscriber { } ``` -> ***Note**: This is a basic implementation. Your actual contract will need more sophisticated tracking depending on your reward mechanism.* +> ***Note**: This is a basic implementation. Your actual contract will need more sophisticated tracking depending on your accounting model.* > ### Step 2: Deploy Subscriber @@ -223,32 +223,32 @@ Since Subscriber contracts often handle value distribution, security is paramoun ### Value Distribution -- **Custody**: Implement secure mechanisms for handling reward tokens -- **Calculation**: Ensure reward calculations are accurate and cannot be manipulated +- **Custody**: Implement secure mechanisms for handling any value-bearing assets +- **Calculation**: Ensure distribution calculations are accurate and cannot be manipulated ### Protection Against Exploitation Be aware of potential attack vectors: - Users can artificially inflate `feesAccrued` in single-position pools -- Malicious actors might attempt to claim rewards they haven't earned +- Malicious actors might attempt to manipulate accounting state > ***Warning**: Always validate inputs and implement thorough checks before distributing any value.* > ## Implementation Example -Let’s build a **minimal** Subscriber implementation that demonstrates how to handle position events. In practice, you’d likely extend these concepts to include reward calculations or more complex accounting. +Let’s build a **minimal** Subscriber implementation that demonstrates how to handle position events. In practice, you’d likely extend these concepts to include more complex accounting. -> ***Note**: The code below focuses on the essential mechanics of receiving notifications. A production system will need additional logic for security, reward distribution, and user interaction.* +> ***Note**: The code below focuses on the essential mechanics of receiving notifications. A production system will need additional logic for security, value distribution, and user interaction.* > -### Basic Reward Tracking +### Basic Position Tracking First, we set up our contract with the necessary data structures and references: ```solidity -contract LiquidityRewardsSubscriber is ISubscriber { +contract LiquidityAccountingSubscriber is ISubscriber { // 1) Store subscription timestamps and liquidity for each position struct PositionData { uint256 subscriptionTime; @@ -336,7 +336,7 @@ function notifyModifyLiquidity( ### Why This Is Important - **`liquidityChange`** can be positive (liquidity added) or negative (liquidity removed). We update our internal tracking accordingly. -- **`feesAccrued`** is provided if the user also collects fees during this action. In a more advanced version, you could track these fees for reward calculations. +- **`feesAccrued`** is provided if the user also collects fees during this action. In a more advanced version, you could track these fees for accounting. ## Processing Position Burns @@ -368,7 +368,7 @@ To put this into practice: ```solidity // 1. Deploy the subscriber -LiquidityRewardsSubscriber subscriber = new LiquidityRewardsSubscriber(positionManager); +LiquidityAccountingSubscriber subscriber = new LiquidityAccountingSubscriber(positionManager); // 2. Users then subscribe their positions positionManager.subscribe(tokenId, subscriber, ""); @@ -382,7 +382,7 @@ positionManager.subscribe(tokenId, subscriber, ""); > This example is deliberately minimal. A real-world Subscriber would need: > -> - **Reward Distribution**: Logic that calculates and distributes incentives based on `subscriptionTime`, `currentLiquidity`, or `feesAccrued`. +> - **Value Distribution**: Logic that calculates and distributes values based on `subscriptionTime`, `currentLiquidity`, or `feesAccrued`. > - **Security Checks**: Safeguards against malicious actors who might inflate fees, manipulate liquidity changes, or spam subscribe/unsubscribe cycles. -> - **Access Control**: Roles or permissions if only certain addresses are allowed to manage rewards or update parameters. +> - **Access Control**: Roles or permissions if only certain addresses are allowed to manage configuration or update parameters. > - **Gas Optimization**: Especially if you expect many positions to subscribe/unsubscribe frequently. diff --git a/docs/contracts/v4/guides/custom-accounting.mdx b/docs/contracts/v4/guides/custom-accounting.mdx index 57237b8..d743e47 100644 --- a/docs/contracts/v4/guides/custom-accounting.mdx +++ b/docs/contracts/v4/guides/custom-accounting.mdx @@ -63,7 +63,7 @@ Custom curves in v4 represent a big change in AMM design flexibility. Unlike [Ri This is made possible through the hook system, particularly hooks that can interact with the swap process. Custom curves allow developers to eject the native concentrated liquidity pricing mechanism. These hooks can intercept swap requests, apply custom pricing logic, and return modified swap parameters. This enables the creation of pools with unique characteristics, such as: - Stable asset pairs with minimal price impact -- Curves for special token types like rebasing tokens, RWAs, vault tokens +- Curves for special token types like rebasing tokens, RWAs, or wrapped position tokens For example, creating a custom curve for a stable swap pool would involve designing a pricing function that maintains tighter price ranges when assets are near parity. This could be achieved by implementing a curve that's flatter in the middle (where assets are at their expected 1:1 ratio) and steeper at the edges (to discourage large imbalances). diff --git a/docs/contracts/v4/guides/hooks/few-hook-contracts.mdx b/docs/contracts/v4/guides/hooks/few-hook-contracts.mdx index a0f6c5d..e74421a 100644 --- a/docs/contracts/v4/guides/hooks/few-hook-contracts.mdx +++ b/docs/contracts/v4/guides/hooks/few-hook-contracts.mdx @@ -46,12 +46,6 @@ This hook is useful when you need: - Safer integration with non-standard token behavior - A dedicated hook path instead of reusing a standard ERC20 flow -### Liquid Staking Hooks - -Few also publishes dedicated hooks for wrapped staking assets such as `FewweETHHook` and `FewwstETHHook`. - -These hooks are intended for pools that need asset-specific handling around liquid staking wrappers while still integrating through the standard v4 hook lifecycle. - ## Mainnet Hook Deployments The following hook contracts are currently published for Ethereum mainnet: diff --git a/docs/contracts/v4/guides/hooks/your-first-hook.md b/docs/contracts/v4/guides/hooks/your-first-hook.md index 0aeadaa..fa7f09e 100644 --- a/docs/contracts/v4/guides/hooks/your-first-hook.md +++ b/docs/contracts/v4/guides/hooks/your-first-hook.md @@ -155,7 +155,7 @@ Most of the code at this point should be self-explanatory. It’s not doing anyt ### Points Logic -First, let’s setup the `POINTS` token that we’ll reward users with via creating another contract `PointsToken.sol` and import relevant dependencies like `ERC20` and `Owned`. +First, let’s set up the `POINTS` token that the hook can issue by creating another contract `PointsToken.sol` and importing relevant dependencies like `ERC20` and `Owned`. ```solidity // SPDX-License-Identifier: MIT diff --git a/docs/contracts/v4/guides/read-pool-state.mdx b/docs/contracts/v4/guides/read-pool-state.mdx index 19955a3..da4a359 100644 --- a/docs/contracts/v4/guides/read-pool-state.mdx +++ b/docs/contracts/v4/guides/read-pool-state.mdx @@ -228,14 +228,14 @@ function getPositionInfo( This function retrieves information about a specific liquidity position. It returns: - `liquidity`: The amount of liquidity in the position. -- `feeGrowthInside0LastX128` and `feeGrowthInside1LastX128`: The last recorded cumulative fees earned per unit of liquidity for each token. +- `feeGrowthInside0LastX128` and `feeGrowthInside1LastX128`: The last recorded cumulative fee growth per unit of liquidity for each token. **Use Case:** This function is crucial for applications that need to manage or analyze individual liquidity positions: -- A liquidity management dashboard could use this to display a user's current positions and earned fees. -- An automated liquidity provision system could use this to decide when to rebalance or compound rewards. +- A liquidity management dashboard could use this to display a user's current positions and accrued fees. +- An automated liquidity provision system could use this to decide when to rebalance positions. - An analytics tool could use this to calculate the performance of different liquidity provision strategies. ### `getFeeGrowthGlobal` @@ -257,7 +257,7 @@ This function retrieves the global fee growth for both tokens in the pool. These Global fee growth is essential for several advanced operations: -- Calculating the fees earned by a position that has been held for a long time. +- Calculating the fee amount for a position that has been held for a long time. - Analyzing the overall fee generation of a pool over its lifetime. - Comparing the performance of different pools or fee tiers. diff --git a/docs/contracts/v4/guides/state-view.mdx b/docs/contracts/v4/guides/state-view.mdx index e95195b..63d57ff 100644 --- a/docs/contracts/v4/guides/state-view.mdx +++ b/docs/contracts/v4/guides/state-view.mdx @@ -167,7 +167,7 @@ While **StateView** exposes many functions, here are several essential calls for - Used to assess trading depth and volatility. 3. **[`getPositionInfo(poolId, positionId)`](/contracts/v4/reference/periphery/lens/StateView#getpositioninfo)** - Returns `(uint128 liquidity, uint256 feeGrowthInside0Last, uint256 feeGrowthInside1Last)`. - - Critical for tracking user positions, especially to calculate earned fees over time. + - Critical for tracking user positions, especially to calculate accrued fees over time. 4. **[`getFeeGrowthGlobals(poolId)`](/contracts/v4/reference/periphery/lens/StateView#getfeegrowthglobals)** - Returns `(uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1)`. - Useful for analytics around total fee accumulation in the pool. diff --git a/docs/contracts/v4/guides/swap-routing.mdx b/docs/contracts/v4/guides/swap-routing.mdx index 8941dd9..48bcae9 100644 --- a/docs/contracts/v4/guides/swap-routing.mdx +++ b/docs/contracts/v4/guides/swap-routing.mdx @@ -45,7 +45,7 @@ Developers might need to configure the Universal Router for swapping on v4 pools 1. **Building a DEX aggregator**: If you’re creating a platform that finds the best rates across multiple DEXes, you’ll want to include v4 pools in your options. 2. **Developing a trading bot**: Automated trading strategies often require the ability to execute swaps programmatically across various pools and versions. -3. **Creating a Dapp**: Many DeFi applications (lending platforms, yield aggregators, etc.) need to perform token swaps as part of their core functionality. +3. **Creating a Dapp**: Many DeFi applications and portfolio tools need to perform token swaps as part of their core functionality. This guide focuses on how to interact with Universal Router from an on-chain contract. diff --git a/docs/contracts/v4/quickstart/subscriber.mdx b/docs/contracts/v4/quickstart/subscriber.mdx index 6e386ce..1283c94 100644 --- a/docs/contracts/v4/quickstart/subscriber.mdx +++ b/docs/contracts/v4/quickstart/subscriber.mdx @@ -7,7 +7,7 @@ sidebar_position: 5 This page describes subscriber contracts in the current `FewToken + Uniswap v4` integration environment. It should be read as integration guidance, not as documentation for a separate native v4 AMM. -For developers looking to support custom _liquidity mining_, Subscriber contracts can be used to receive notifications about position modifications or transfers. +For developers looking to support custom external position tracking, Subscriber contracts can be used to receive notifications about position modifications or transfers. ## Guide diff --git a/docusaurus.config.ts b/docusaurus.config.ts index f0e161e..c656ad3 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -23,7 +23,7 @@ const config: Config = { onBrokenMarkdownLinks: 'warn', }, }, - favicon: 'img/favicon.png', + favicon: 'img/ring-logo.svg', organizationName: 'RingProtocol', projectName: 'docs', headTags: [ diff --git a/src/components/Icons/index.tsx b/src/components/Icons/index.tsx index c3690dd..0279daa 100644 --- a/src/components/Icons/index.tsx +++ b/src/components/Icons/index.tsx @@ -99,56 +99,6 @@ export const ArrowRight: FC<{ ) } -export const MiniUnicon: FC<{ - className?: string - color?: 'accent-1' | 'neutral-1' -}> = ({ className, color = 'accent-1' }) => { - return ( - - ) -} - export const Emblem1: FC<{ className?: string color?: 'accent-1' diff --git a/src/theme/Footer.tsx b/src/theme/Footer.tsx index 43460af..4a6b1fc 100644 --- a/src/theme/Footer.tsx +++ b/src/theme/Footer.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react' -import { MiniUnicon, Github, X, Discord } from '../components/Icons' +import { Github, X, Discord } from '../components/Icons' import { LinkBase, TextButton } from '../components/base/Button' @@ -75,7 +75,12 @@ const Footer: FC = () => {
Ring Protocol Docs
diff --git a/src/theme/Navbar/Content/index.tsx b/src/theme/Navbar/Content/index.tsx
index c9374cd..743b6be 100644
--- a/src/theme/Navbar/Content/index.tsx
+++ b/src/theme/Navbar/Content/index.tsx
@@ -3,7 +3,7 @@ import { useNavbarMobileSidebar } from '@docusaurus/theme-common/internal'
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle'
// import SearchBar from '@theme/SearchBar'
import Link from '@docusaurus/Link'
-import { Menu, MiniUnicon } from '@site/src/components/Icons'
+import { Menu } from '@site/src/components/Icons'
import clsx from 'clsx'
// function useNavbarItems() {
@@ -59,7 +59,7 @@ export default function NavbarContent(): ReactNode {
target="_self"
aria-label="Ring Protocol documentation home"
>
-
Ring Protocol
Docs for builders
diff --git a/static/img/ring-logo.svg b/static/img/ring-logo.svg new file mode 100644 index 0000000..81d27f8 --- /dev/null +++ b/static/img/ring-logo.svg @@ -0,0 +1,31 @@ +