Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
SwayStar123 committed Sep 17, 2023
1 parent 5eadf8d commit fb9e76f
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 0 deletions.
Binary file added standards/src_6/.docs/src-5-logo-dark-theme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added standards/src_6/.docs/src-5-logo-light-theme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions standards/src_6/Forc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "src_5.sw"
license = "Apache-2.0"
name = "src_5"
87 changes: 87 additions & 0 deletions standards/src_6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset=".docs/src-5-logo-dark-theme.png">
<img alt="SRC-5 logo" width="400px" src=".docs/src-5-logo-light-theme.png">
</picture>
</p>

# Abstract
The following standard allows for the implementation of a standard API for token vaults such as yield bearing token vaults. This standard is an optional add-on to the SRC-20 standard.

# Motivation
Token vaults allow users to own shares of variable amount of assets, such as lending protocols which may have growing assets due to profits from interest. This pattern is highly useful and would greatly benefit from standardisation

# Prior Art
Token vaults have been thoroughly explored on Ethereum and with [EIP 4626](https://eips.ethereum.org/EIPS/eip-4626) they have their own standard for it. However as Fuel's native assets are fundamentally different to Ethereum's ERC-20 tokens, the implementation will differ, but the interface may be used as reference.

# Specification
## Required public functions
The following functions MUST be implemented (on top of the SRC-20 functions) to follow the SRC-6 standard

### `fn deposit(receiver: Identity)`
Method that allows depositing of the underlying asset in exchange for shares of the vault.

MUST revert if any AssetId other than the underlying is forwarded.
MUST mint `preview_deposit(deposited_assets)` amount of shares to `receiver`
MUST increase `managed_assets` by `deposited_assets` (through any means including `std::context::this_balance(ASSET_ID)` if applicable)
MUST increase `total_supply` by newly minted shares

### `fn withdraw(asset: AssetId, receiver: Identity)`
Method that allows the redeeming of the vault shares in exchange for a pro-rata amount of the underlying asset

MUST revert if any AssetId other than the AssetId of the self contract is forwarded.
MUST send `preview_withdraw(redeemed_shares)` amount of assets to `receiver`
MUST burn the received shares
MUST reduce `total_assets` by `preview_withdraw(redeemed_shares)`
MUST reduce `total_supply` by amount of burnt shares

### `fn managed_assets(asset: AssetId) -> u64`
Method that returns the total assets under management by vault. Includes assets controlled by the vault but not directly possessed by vault

MUST return total amount of assets of underlying AssetId under management by vault

### `fn convert_to_shares(asset: AssetId, assets: u64) -> Option<u64>`
Helper method for converting



## Required logs
The following logs MUST be emitted at the specified occasions

```
pub struct Deposit {
caller: Identity,
receiver: Identity,
assets: u64,
shares: u64,
}
```
`caller` has called the `deposit` method sending `assets` assets of the underlying asset_id, in exchange for `shares` shares sent to the receiver `receiver`

The `Deposit` struct MUST be logged whenever new shares are minted via the `deposit` method

```
pub struct Withdraw {
caller: Identity,
receiver: Identity,
assets: u64,
shares: u64,
}
```
`caller` has called the `withdraw` method sending `shares` shares in exchange for `assets` assets to the receiver `receiver`

The `Withdraw` struct MUST be logged whenever shares are redeemed for assets via the `withdraw` method

# Rationale
The ABI discussed is simple and covers the known use cases of token vaults while allowing safe implementations

# Backwards compatibility
This standard is fully compatible with the SRC-20 standard

# Security Considerations
Incorrect implementation of token vaults could allow attackers to steal underlying assets. It is recommended to properly audit any code using this standard to ensure exploits are not possible.

# Reference implementation
Full reference implementation can be seen [here](https://github.com/SwayStar123/vault-standard-reference-implementation/blob/master/src/main.sw)

This is a draft standard
188 changes: 188 additions & 0 deletions standards/src_6/src/src_6.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
library;

// Required logs:

/// Event logged when a deposit is made.
pub struct Deposit {
/// The caller of the deposit function.
caller: Identity,
/// The receiver of the deposit.
receiver: Identity,
/// The asset being deposited.
asset: AssetId,
/// The amount of assets being deposited.
assets: u64,
/// The amount of shares being minted.
shares: u64,
}

/// Event logged when a withdrawal is made.
pub struct Withdraw {
/// The caller of the withdrawal function.
caller: Identity,
/// The receiver of the withdrawal.
receiver: Identity,
/// The asset being withdrawn.
asset: AssetId,
/// The amount of assets being withdrawn.
assets: u64,
/// The amount of shares being burned.
shares: u64,
}

abi SRC6 {
// SRC-6
// Deposit/Withdrawal

/// Deposits assets into the contract and mints shares to the receiver.
///
/// # Additional Information
///
/// * Assets must be forwarded to the contract in the contract call.
///
/// # Arguments
///
/// * `receiver`: [Identity] - The receiver of the shares.
///
/// # Returns
///
/// * [u64] - The amount of shares minted.
///
/// # Reverts
///
/// * If the asset is not supported by the contract.
/// * If the amount of assets is zero.
/// * The user crosses any global or user specific deposit limits.
#[storage(read, write)]
fn deposit(receiver: Identity) -> u64;
/// Burns shares from the sender and transfers assets to the receiver.
///
/// # Additional Information
///
/// * Shares must be forwarded to the contract in the contract call.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the shares should be burned.
/// * `receiver`: [Identity] - The receiver of the assets.
///
/// # Returns
///
/// * [u64] - The amount of assets transferred.
///
/// # Reverts
///
/// * If the asset is not supported by the contract.
/// * If the amount of shares is zero.
/// * If the transferred shares do not corresspond to the given asset.
/// * The user crosses any global or user specific withdrawal limits.
#[storage(read, write)]
fn withdraw(asset: AssetId, receiver: Identity) -> u64;

// Accounting

/// Returns the amount of managed assets of the given asset.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the amount of managed assets should be returned.
///
/// # Returns
///
/// * [u64] - The amount of managed assets of the given asset.
#[storage(read)]
fn managed_assets(asset: AssetId) -> u64;
/// Returns how many shares would be minted for the given amount of assets, in an ideal scenario (No accounting for slippage, or any limits).
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the amount of shares should be returned.
/// * `assets`: [u64] - The amount of assets for which the amount of shares should be returned.
///
/// # Returns
///
/// * [Some(u64)] - The amount of shares that would be minted for the given amount of assets.
/// * [None] - If the asset is not supported by the contract.
#[storage(read)]
fn convert_to_shares(asset: AssetId, assets: u64) -> Option<u64>;
/// Returns how many assets would be transferred for the given amount of shares, in an ideal scenario (No accounting for slippage, or any limits).
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the amount of assets should be returned.
/// * `shares`: [u64] - The amount of shares for which the amount of assets should be returned.
///
/// # Returns
///
/// * [Some(u64)] - The amount of assets that would be transferred for the given amount of shares.
/// * [None] - If the asset is not supported by the contract.
#[storage(read)]
fn convert_to_assets(asset: AssetId, shares: u64) -> Option<u64>;
/// Returns how many shares would have been minted for the given amount of assets, if this was a deposit call.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the amount of shares should be returned.
/// * `assets`: [u64] - The amount of assets for which the amount of shares should be returned.
///
/// # Returns
///
/// * [u64] - The amount of shares that would have been minted for the given amount of assets.
///
/// # Reverts
///
/// * For any reason a deposit would revert.
#[storage(read)]
fn preview_deposit(asset: AssetId, assets: u64) -> u64;
/// Returns how many assets would have been transferred for the given amount of shares, if this was a withdrawal call.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the amount of assets should be returned.
/// * `shares`: [u64] - The amount of shares for which the amount of assets should be returned.
///
/// # Returns
///
/// * [u64] - The amount of assets that would have been transferred for the given amount of shares.
///
/// # Reverts
///
/// * For any reason a withdrawal would revert.
#[storage(read)]
fn preview_withdraw(asset: AssetId, shares: u64) -> u64;

// Deposit/Withdrawal Limits

/// Returns the maximum amount of assets that can be deposited into the contract, for the given asset.
///
/// # Additional Information
///
/// Does not account for any user or global limits.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the maximum amount of depositable assets should be returned.
///
/// # Returns
///
/// * [Some(u64)] - The maximum amount of assets that can be deposited into the contract, for the given asset.
/// * [None] - If the asset is not supported by the contract.
#[storage(read)]
fn max_depositable(asset: AssetId) -> Option<u64>;
/// Returns the maximum amount of assets that can be withdrawn from the contract, for the given asset.
///
/// # Additional Information
///
/// Does not account for any user or global limits.
///
/// # Arguments
///
/// * `asset`: [AssetId] - The asset for which the maximum amount of withdrawable assets should be returned.
///
/// # Returns
///
/// * [Some(u64)] - The maximum amount of assets that can be withdrawn from the contract, for the given asset.
/// * [None] - If the asset is not supported by the contract.
#[storage(read)]
fn max_withdrawable(asset: AssetId) -> Option<u64>;
}

0 comments on commit fb9e76f

Please sign in to comment.