Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feat-token-extensions' into feat…
Browse files Browse the repository at this point in the history
…-token-extensions
  • Loading branch information
ametel01 committed Sep 13, 2024
2 parents 712459b + 1ebed30 commit 02be20d
Show file tree
Hide file tree
Showing 10 changed files with 1,056 additions and 14 deletions.
484 changes: 484 additions & 0 deletions cairo/src/contracts/mocks/erc4626_component.cairo

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions cairo/src/contracts/mocks/erc4626_mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#[starknet::contract]
mod ERC4626Mock {
use hyperlane_starknet::contracts::mocks::erc4626_component::{
ERC4626Component, ERC4626HooksEmptyImpl
};
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc20::ERC20Component;
use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait};
use starknet::{get_contract_address, ContractAddress};

component!(path: ERC4626Component, storage: erc4626, event: ERC4626Event);
component!(path: ERC20Component, storage: erc20, event: ERC20Event);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

#[abi(embed_v0)]
impl ERC4626Impl = ERC4626Component::ERC4626Impl<ContractState>;
impl ERC4626InternalImpl = ERC4626Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
erc4626: ERC4626Component::Storage,
#[substorage(v0)]
erc20: ERC20Component::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC4626Event: ERC4626Component::Event,
#[flat]
ERC20Event: ERC20Component::Event,
#[flat]
SRC5Event: SRC5Component::Event,
}

#[constructor]
fn constructor(
ref self: ContractState, asset: ContractAddress, name: ByteArray, symbol: ByteArray,
) {
self.erc4626.initializer(asset, name, symbol, 0);
}
}
228 changes: 228 additions & 0 deletions cairo/src/contracts/mocks/erc4626_yield_sharing_mock.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#[starknet::interface]
trait IERC4626YieldSharing<TContractState> {
fn set_fee(ref self: TContractState, new_fee: u256);
fn get_claimable_fees(self: @TContractState) -> u256;
}

#[starknet::contract]
mod ERC4626YieldSharingMock {
use core::integer::BoundedInt;
use hyperlane_starknet::contracts::libs::math;
use hyperlane_starknet::contracts::mocks::erc4626_component::{
ERC4626Component, ERC4626HooksEmptyImpl
};
use hyperlane_starknet::contracts::token::interfaces::ierc4626::IERC4626;
use openzeppelin::access::ownable::{OwnableComponent};
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc20::ERC20Component;
use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait};
use starknet::{get_contract_address, get_caller_address, ContractAddress};

component!(path: ERC4626Component, storage: erc4626, event: ERC4626Event);
component!(path: ERC20Component, storage: erc20, event: ERC20Event);
component!(path: SRC5Component, storage: src5, event: SRC5Event);
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);

impl ERC4626Impl = ERC4626Component::ERC4626Impl<ContractState>;
impl ERC4626InternalImpl = ERC4626Component::InternalImpl<ContractState>;

#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;
// E18
const SCALE: u256 = 1_000_000_000_000_000_000;

#[storage]
struct Storage {
fee: u256,
accumulated_fees: u256,
last_vault_balance: u256,
#[substorage(v0)]
erc4626: ERC4626Component::Storage,
#[substorage(v0)]
erc20: ERC20Component::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
#[substorage(v0)]
ownable: OwnableComponent::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC4626Event: ERC4626Component::Event,
#[flat]
ERC20Event: ERC20Component::Event,
#[flat]
SRC5Event: SRC5Component::Event,
#[flat]
OwnableEvent: OwnableComponent::Event
}

#[constructor]
fn constructor(
ref self: ContractState,
asset: ContractAddress,
name: ByteArray,
symbol: ByteArray,
initial_fee: u256
) {
self.erc4626.initializer(asset, name, symbol, 0);
self.fee.write(initial_fee);
self.ownable.initializer(get_caller_address());
}

pub impl ERC4626YieldSharingImpl of super::IERC4626YieldSharing<ContractState> {
fn set_fee(ref self: ContractState, new_fee: u256) {
self.ownable.assert_only_owner();
self.fee.write(new_fee);
}

fn get_claimable_fees(self: @ContractState) -> u256 {
let new_vault_balance = IERC20Dispatcher { contract_address: self.erc4626.asset() }
.balance_of(get_contract_address());
let last_vault_balance = self.last_vault_balance.read();
if new_vault_balance <= last_vault_balance {
return self.accumulated_fees.read();
}

let new_yield = new_vault_balance - last_vault_balance;
let new_fees = math::mul_div(new_yield, self.fee.read(), SCALE);

self.accumulated_fees.read() + new_fees
}
}

pub impl ERC4626 of IERC4626<ContractState> {
fn name(self: @ContractState) -> ByteArray {
self.erc4626.name()
}

fn symbol(self: @ContractState) -> ByteArray {
self.erc4626.symbol()
}

fn decimals(self: @ContractState) -> u8 {
self.erc4626.decimals()
}

fn total_supply(self: @ContractState) -> u256 {
self.erc4626.total_supply()
}

fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {
self.erc4626.balance_of(account)
}

fn allowance(
self: @ContractState, owner: ContractAddress, spender: ContractAddress
) -> u256 {
self.erc4626.allowance(owner, spender)
}

fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {
self.erc4626.transfer(recipient, amount)
}

fn transfer_from(
ref self: ContractState,
sender: ContractAddress,
recipient: ContractAddress,
amount: u256
) -> bool {
self.erc4626.transfer_from(sender, recipient, amount)
}

fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {
self.erc4626.approve(spender, amount)
}

fn asset(self: @ContractState) -> ContractAddress {
self.erc4626.asset()
}

fn convert_to_assets(self: @ContractState, shares: u256) -> u256 {
self.erc4626.convert_to_assets(shares)
}

fn convert_to_shares(self: @ContractState, assets: u256) -> u256 {
self.erc4626.convert_to_shares(assets)
}
// Overriden
fn deposit(ref self: ContractState, assets: u256, receiver: ContractAddress) -> u256 {
let last_vault_balance = self.last_vault_balance.read();
self.last_vault_balance.write(last_vault_balance + assets);
self.erc4626.deposit(assets, receiver)
}

fn mint(ref self: ContractState, shares: u256, receiver: ContractAddress) -> u256 {
self.erc4626.mint(shares, receiver)
}

fn preview_deposit(self: @ContractState, assets: u256) -> u256 {
self.erc4626.preview_deposit(assets)
}

fn preview_mint(self: @ContractState, shares: u256) -> u256 {
self.erc4626.preview_mint(shares)
}

fn preview_redeem(self: @ContractState, shares: u256) -> u256 {
self.erc4626.preview_redeem(shares)
}

fn preview_withdraw(self: @ContractState, assets: u256) -> u256 {
self.erc4626.preview_withdraw(assets)
}

fn max_deposit(self: @ContractState, receiver: ContractAddress) -> u256 {
BoundedInt::max()
}

fn max_mint(self: @ContractState, receiver: ContractAddress) -> u256 {
BoundedInt::max()
}

fn max_redeem(self: @ContractState, owner: ContractAddress) -> u256 {
self.erc4626.max_redeem(owner)
}

fn max_withdraw(self: @ContractState, owner: ContractAddress) -> u256 {
self.erc4626.max_withdraw(owner)
}
// Overriden
fn redeem(
ref self: ContractState, shares: u256, receiver: ContractAddress, owner: ContractAddress
) -> u256 {
self._accrue_yield();
self.erc4626.redeem(shares, receiver, owner)
}
// Overriden
fn total_assets(self: @ContractState) -> u256 {
self.erc4626.total_assets() - self.get_claimable_fees()
}

fn withdraw(
ref self: ContractState, assets: u256, receiver: ContractAddress, owner: ContractAddress
) -> u256 {
self.erc4626.withdraw(assets, receiver, owner)
}
}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn _accrue_yield(ref self: ContractState) {
let new_vault_balance = IERC20Dispatcher { contract_address: self.erc4626.asset() }
.balance_of(get_contract_address());
let last_vault_balance = self.last_vault_balance.read();
if new_vault_balance > last_vault_balance {
let new_yield = new_vault_balance - last_vault_balance;
let new_fees = math::mul_div(new_yield, self.fee.read(), SCALE);
let accumulated_fees = self.accumulated_fees.read();
self.accumulated_fees.write(accumulated_fees + new_fees);
self.last_vault_balance.write(new_vault_balance);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,20 @@ pub mod HypERC20CollateralVaultDeposit {
mailbox: ContractAddress,
vault: ContractAddress,
owner: ContractAddress,
hook: Option<ContractAddress>,
interchain_security_module: Option<ContractAddress>
hook: ContractAddress,
interchain_security_module: ContractAddress
) {
self.ownable.initializer(owner);
self.mailbox.initialize(mailbox, hook, interchain_security_module);
self
.mailbox
.initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module));
let vault_dispatcher = ERC4626ABIDispatcher { contract_address: vault };
let erc20 = vault_dispatcher.asset();
self.collateral.initialize(erc20);
self.vault.write(vault_dispatcher);
self.collateral.wrapped_token.read().approve(vault, BoundedInt::max());
}

#[abi(embed_v0)]
impl HypERC20CollateralVaultDepositImpl of super::IHypERC20CollateralVaultDeposit<
ContractState
> {
Expand Down Expand Up @@ -232,7 +234,7 @@ pub mod HypERC20CollateralVaultDeposit {
fn _withdraw_from_vault(ref self: ContractState, amount: u256, recipient: ContractAddress) {
let asset_deposited = self.asset_deposited.read();
self.asset_deposited.write(asset_deposited - amount);
self.vault.read().withdraw(amount, recipient, starknet::get_caller_address());
self.vault.read().withdraw(amount, recipient, starknet::get_contract_address());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ pub mod HypXERC20Lockbox {
// Upgradeable
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;


#[storage]
struct Storage {
#[substorage(v0)]
Expand Down
9 changes: 7 additions & 2 deletions cairo/src/contracts/token/interfaces/ierc4626.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use starknet::ContractAddress;
#[starknet::interface]
pub trait IERC4626<TState> {
// ************************************
// * IERC4626
// * IERC20
// ************************************
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
Expand All @@ -16,7 +16,12 @@ pub trait IERC4626<TState> {
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;

// ************************************
// * IERC20 metadata
// ************************************
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
fn decimals(self: @TState) -> u8;
// ************************************
// * IERC4626
// ************************************
Expand Down
7 changes: 7 additions & 0 deletions cairo/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ mod contracts {
}
pub mod mocks {
pub mod enumerable_map_holder;
pub mod erc4626_component;
pub mod erc4626_mock;
pub mod erc4626_yield_sharing_mock;
pub mod fee_hook;
pub mod fee_token;
pub mod hook;
Expand Down Expand Up @@ -143,6 +146,10 @@ mod tests {
pub mod hyp_erc721_test;
pub mod hyp_erc721_uri_storage_test;
}

pub mod vault_extensions {
pub mod hyp_erc20_collateral_vault_deposit_test;
}
}
pub mod libs {
pub mod test_enumerable_map;
Expand Down
8 changes: 5 additions & 3 deletions cairo/src/tests/token/hyp_erc20/common.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,11 @@ pub fn handle_local_transfer(setup: @Setup, transfer_amount: u256) {
stop_prank(CheatTarget::One((*setup).local_token.contract_address));
}

pub fn mint_and_approve(setup: @Setup, amount: u256, account: ContractAddress) {
(*setup).primary_token.mint(account, amount);
(*setup).primary_token.approve(account, amount);
pub fn mint_and_approve(
setup: @Setup, amount: u256, mint_to: ContractAddress, approve_to: ContractAddress
) {
(*setup).primary_token.mint(mint_to, amount);
(*setup).primary_token.approve(approve_to, amount);
}

pub fn set_custom_gas_config(setup: @Setup) {
Expand Down
Loading

0 comments on commit 02be20d

Please sign in to comment.