Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ref-exchange/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ref-exchange"
version = "0.2.1"
version = "0.3.0"
authors = ["Illia Polosukhin <[email protected]>"]
edition = "2018"
publish = false
Expand Down
27 changes: 27 additions & 0 deletions ref-exchange/src/legacy.rs
Original file line number Diff line number Diff line change
@@ -1 +1,28 @@
//! This modules captures all the code needed to migrate from previous version.

use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{LookupMap, UnorderedSet, Vector};
use near_sdk::{near_bindgen, AccountId,};

use crate::account_deposit::Account;
use crate::pool::Pool;




#[near_bindgen]
#[derive(BorshSerialize, BorshDeserialize)]
pub struct OldContract {
/// Account of the owner.
pub owner_id: AccountId,
/// Exchange fee, that goes to exchange itself (managed by governance).
exchange_fee: u32,
/// Referral fee, that goes to referrer in the call.
referral_fee: u32,
/// List of all the pools.
pub pools: Vector<Pool>,
/// Accounts registered, keeping track all the amounts deposited, storage and more.
pub accounts: LookupMap<AccountId, Account>,
/// Set of whitelisted tokens by "owner".
pub whitelisted_tokens: UnorderedSet<AccountId>,
}
40 changes: 26 additions & 14 deletions ref-exchange/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use near_contract_standards::storage_management::{
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::{LookupMap, UnorderedSet, Vector};
use near_sdk::json_types::{ValidAccountId, U128};
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{
assert_one_yocto, env, log, near_bindgen, AccountId, Balance, PanicOnDefault, Promise,
PromiseResult, StorageUsage,
Expand Down Expand Up @@ -35,15 +36,22 @@ mod views;

near_sdk::setup_alloc!();

#[derive(Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize, Clone)]
#[serde(crate = "near_sdk::serde")]
pub struct InternalFeesRatio {
/// Portion (bps) of the fee going to exchange in total fee.
pub exchange_fee: u32,
/// Portion (bps) of the fee going to referral in total fee.
pub referral_fee: u32,
}

#[near_bindgen]
#[derive(BorshSerialize, BorshDeserialize, PanicOnDefault)]
pub struct Contract {
/// Account of the owner.
owner_id: AccountId,
/// Exchange fee, that goes to exchange itself (managed by governance).
exchange_fee: u32,
/// Referral fee, that goes to referrer in the call.
referral_fee: u32,
/// Exchange fee and referral_fee (managed by governance).
fee_policy: InternalFeesRatio,
/// List of all the pools.
pools: Vector<Pool>,
/// Accounts registered, keeping track all the amounts deposited, storage and more.
Expand All @@ -55,11 +63,10 @@ pub struct Contract {
#[near_bindgen]
impl Contract {
#[init]
pub fn new(owner_id: ValidAccountId, exchange_fee: u32, referral_fee: u32) -> Self {
pub fn new(owner_id: ValidAccountId, fee_policy: InternalFeesRatio) -> Self {
Self {
owner_id: owner_id.as_ref().clone(),
exchange_fee,
referral_fee,
fee_policy,
pools: Vector::new(b"p".to_vec()),
accounts: LookupMap::new(b"d".to_vec()),
whitelisted_tokens: UnorderedSet::new(b"w".to_vec()),
Expand All @@ -74,9 +81,7 @@ impl Contract {
self.internal_add_pool(Pool::SimplePool(SimplePool::new(
self.pools.len() as u32,
tokens,
fee + self.exchange_fee + self.referral_fee,
self.exchange_fee,
self.referral_fee,
fee,
)))
}

Expand Down Expand Up @@ -284,6 +289,7 @@ impl Contract {
min_amount_out,
&self.owner_id,
referral_id,
&self.fee_policy,
);
self.pools.replace(pool_id, &pool);
amount_out
Expand All @@ -305,7 +311,13 @@ mod tests {
fn setup_contract() -> (VMContextBuilder, Contract) {
let mut context = VMContextBuilder::new();
testing_env!(context.predecessor_account_id(accounts(0)).build());
let contract = Contract::new(accounts(0), 4, 1);
let contract = Contract::new(
accounts(0),
InternalFeesRatio {
exchange_fee: 2000,
referral_fee: 500,
}
);
(context, contract)
}

Expand Down Expand Up @@ -357,7 +369,7 @@ mod tests {
.predecessor_account_id(account_id.clone())
.attached_deposit(env::storage_byte_cost() * 300)
.build());
let pool_id = contract.add_simple_pool(tokens, 25);
let pool_id = contract.add_simple_pool(tokens, 30);
testing_env!(context
.predecessor_account_id(account_id.clone())
.attached_deposit(to_yocto("0.03"))
Expand Down Expand Up @@ -476,7 +488,7 @@ mod tests {
// Exchange fees left in the pool as liquidity + 1m from transfer.
assert_eq!(
contract.get_pool_total_shares(0).0,
33337501041992301475 + 1_000_000
50006251562988452212 + 1_000_000
);

contract.withdraw(
Expand Down Expand Up @@ -718,7 +730,7 @@ mod tests {
],
None,
);
// Roundtrip returns almost everything except 0.3% fee.
// Roundtrip returns almost everything except 0.25% fee.
assert_eq!(contract.get_deposit(acc, accounts(1)).0, 1_000_000 - 7);
}
}
28 changes: 25 additions & 3 deletions ref-exchange/src/owner.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Implement all the relevant logic for owner of this contract.

use crate::*;
use crate::utils::FEE_DIVISOR;
use crate::legacy::OldContract;


#[near_bindgen]
impl Contract {
Expand Down Expand Up @@ -32,12 +35,31 @@ impl Contract {
}
}

/// Migration function from v2 to v2.
/// Set fee policy.
pub fn set_fee_policy(&mut self, fee_policy: InternalFeesRatio) {
self.assert_owner();
assert!(
fee_policy.exchange_fee + fee_policy.referral_fee <= FEE_DIVISOR,
"ERR_FEE_TOO_LARGE"
);
self.fee_policy = fee_policy;
}

/// Migration function from v2 to v3.
/// For next version upgrades, change this function.
#[init(ignore_state)]
pub fn migrate() -> Self {
let contract: Contract = env::state_read().expect("ERR_NOT_INITIALIZED");
contract
let old_contract: OldContract = env::state_read().expect("ERR_NOT_INITIALIZED");
Contract {
owner_id: old_contract.owner_id,
fee_policy: InternalFeesRatio {
exchange_fee: 2000,
referral_fee: 500,
},
pools: old_contract.pools,
accounts: old_contract.accounts,
whitelisted_tokens: old_contract.whitelisted_tokens,
}
}

pub(crate) fn assert_owner(&self) {
Expand Down
3 changes: 3 additions & 0 deletions ref-exchange/src/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use near_sdk::{AccountId, Balance};

use crate::simple_pool::SimplePool;
use crate::utils::SwapVolume;
use crate::InternalFeesRatio;

/// Generic Pool, providing wrapper around different implementations of swap pools.
/// Allows to add new types of pools just by adding extra item in the enum without needing to migrate the storage.
Expand Down Expand Up @@ -81,6 +82,7 @@ impl Pool {
min_amount_out: Balance,
exchange_id: &AccountId,
referral_id: &Option<AccountId>,
fee_policy: &InternalFeesRatio,
) -> Balance {
match self {
Pool::SimplePool(pool) => pool.swap(
Expand All @@ -90,6 +92,7 @@ impl Pool {
min_amount_out,
exchange_id,
referral_id,
fee_policy,
),
}
}
Expand Down
34 changes: 21 additions & 13 deletions ref-exchange/src/simple_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::collections::LookupMap;
use near_sdk::json_types::ValidAccountId;
use near_sdk::{env, AccountId, Balance};
use crate::InternalFeesRatio;

use crate::errors::{
ERR13_LP_NOT_REGISTERED, ERR14_LP_ALREADY_REGISTERED, ERR31_ZERO_AMOUNT, ERR32_ZERO_SHARES,
Expand All @@ -27,9 +28,9 @@ pub struct SimplePool {
pub volumes: Vec<SwapVolume>,
/// Fee charged for swap (gets divided by FEE_DIVISOR).
pub total_fee: u32,
/// Portion of the fee going to exchange.
/// obsolete, not used in any logic, reserved for storage integrity
pub exchange_fee: u32,
/// Portion of the fee going to referral.
/// obsolete, not used in any logic, reserved for storage integrity
pub referral_fee: u32,
/// Shares of the pool by liquidity providers.
pub shares: LookupMap<AccountId, Balance>,
Expand All @@ -42,11 +43,9 @@ impl SimplePool {
id: u32,
token_account_ids: Vec<ValidAccountId>,
total_fee: u32,
exchange_fee: u32,
referral_fee: u32,
) -> Self {
assert!(
total_fee < FEE_DIVISOR && (exchange_fee + referral_fee) <= total_fee,
total_fee < FEE_DIVISOR,
"ERR_FEE_TOO_LARGE"
);
assert_ne!(token_account_ids.len(), 1, "ERR_NOT_ENOUGH_TOKENS");
Expand All @@ -59,8 +58,8 @@ impl SimplePool {
amounts: vec![0u128; token_account_ids.len()],
volumes: vec![SwapVolume::default(); token_account_ids.len()],
total_fee,
exchange_fee,
referral_fee,
exchange_fee: 0,
referral_fee: 0,
shares: LookupMap::new(format!("s{}", id).into_bytes()),
shares_total_supply: 0,
}
Expand Down Expand Up @@ -267,6 +266,7 @@ impl SimplePool {
min_amount_out: Balance,
exchange_id: &AccountId,
referral_id: &Option<AccountId>,
fee_policy: &InternalFeesRatio,
) -> Balance {
let in_idx = self.token_index(token_in);
let out_idx = self.token_index(token_out);
Expand Down Expand Up @@ -295,18 +295,18 @@ impl SimplePool {
let numerator = (new_invariant - prev_invariant) * U256::from(self.shares_total_supply);

// Allocate exchange fee as fraction of total fee by issuing LP shares proportionally.
if self.exchange_fee > 0 && numerator > U256::zero() {
let denominator = new_invariant * self.total_fee / self.exchange_fee;
if fee_policy.exchange_fee > 0 && numerator > U256::zero() {
let denominator = new_invariant * FEE_DIVISOR / fee_policy.exchange_fee;
self.mint_shares(&exchange_id, (numerator / denominator).as_u128());
}

// If there is referral provided and the account already registered LP, allocate it % of LP rewards.
if let Some(referral_id) = referral_id {
if self.referral_fee > 0
if fee_policy.referral_fee > 0
&& numerator > U256::zero()
&& self.shares.contains_key(referral_id)
{
let denominator = new_invariant * self.total_fee / self.referral_fee;
let denominator = new_invariant * FEE_DIVISOR / fee_policy.referral_fee;
self.mint_shares(&referral_id, (numerator / denominator).as_u128());
}
}
Expand Down Expand Up @@ -334,7 +334,7 @@ mod tests {
let mut context = VMContextBuilder::new();
context.predecessor_account_id(accounts(0));
testing_env!(context.build());
let mut pool = SimplePool::new(0, vec![accounts(1), accounts(2)], 30, 0, 0);
let mut pool = SimplePool::new(0, vec![accounts(1), accounts(2)], 20);
let mut amounts = vec![to_yocto("5"), to_yocto("10")];
let num_shares = pool.add_liquidity(accounts(0).as_ref(), &mut amounts);
assert_eq!(amounts, vec![to_yocto("5"), to_yocto("10")]);
Expand All @@ -349,6 +349,10 @@ mod tests {
1,
accounts(3).as_ref(),
&None,
&InternalFeesRatio {
exchange_fee: 0,
referral_fee: 0,
}
);
assert_eq!(
pool.share_balance_of(accounts(0).as_ref()),
Expand Down Expand Up @@ -380,7 +384,7 @@ mod tests {
let mut context = VMContextBuilder::new();
context.predecessor_account_id(accounts(0));
testing_env!(context.build());
let mut pool = SimplePool::new(0, vec![accounts(1), accounts(2)], 100, 100, 0);
let mut pool = SimplePool::new(0, vec![accounts(1), accounts(2)], 100);
let mut amounts = vec![to_yocto("5"), to_yocto("10")];
let num_shares = pool.add_liquidity(accounts(0).as_ref(), &mut amounts);
assert_eq!(amounts, vec![to_yocto("5"), to_yocto("10")]);
Expand All @@ -395,6 +399,10 @@ mod tests {
1,
accounts(3).as_ref(),
&None,
&InternalFeesRatio {
exchange_fee: 10000,
referral_fee: 0,
}
);
assert_eq!(
pool.share_balance_of(accounts(0).as_ref()),
Expand Down
26 changes: 25 additions & 1 deletion ref-exchange/src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::collections::HashMap;

use near_sdk::json_types::{ValidAccountId, U128};
use near_sdk::json_types::{ValidAccountId, U128, U64};
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{near_bindgen, AccountId};

Expand Down Expand Up @@ -39,8 +39,32 @@ impl From<Pool> for PoolInfo {
}
}


#[derive(Serialize, Deserialize)]
#[serde(crate = "near_sdk::serde")]
pub struct Metadata {
pub version: String,
pub owner_id: AccountId,
pub pool_count: U64,
pub whitelist_count: U64,
pub fee_policy: InternalFeesRatio,
}


#[near_bindgen]
impl Contract {

/// meta_data of this contract
pub fn get_metadata(&self) -> Metadata {
Metadata {
owner_id: self.owner_id.clone(),
version: env!("CARGO_PKG_VERSION").to_string(),
pool_count: self.pools.len().into(),
whitelist_count: self.whitelisted_tokens.len().into(),
fee_policy: self.fee_policy.clone(),
}
}

/// Returns semver of this contract.
pub fn version(&self) -> String {
env!("CARGO_PKG_VERSION").to_string()
Expand Down
7 changes: 6 additions & 1 deletion ref-exchange/tests/test_migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use near_sdk::json_types::ValidAccountId;
use near_sdk_sim::{deploy, init_simulator, to_yocto};

use ref_exchange::ContractContract as Exchange;
use ref_exchange::InternalFeesRatio;

near_sdk_sim::lazy_static_include::lazy_static_include_bytes! {
PREV_EXCHANGE_WASM_BYTES => "../res/ref_exchange_local.wasm",
Expand All @@ -19,7 +20,11 @@ fn test_upgrade() {
contract_id: "swap".to_string(),
bytes: &PREV_EXCHANGE_WASM_BYTES,
signer_account: root,
init_method: new(ValidAccountId::try_from(root.account_id.clone()).unwrap(), 4, 1)
init_method: new(ValidAccountId::try_from(root.account_id.clone()).unwrap(),
InternalFeesRatio {
exchange_fee: 2000,
referral_fee: 500,
})
);
// Failed upgrade with no permissions.
let result = test_user
Expand Down
Loading