-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add legacy SC: farm staking proxy v1.3
- Loading branch information
1 parent
c4d11a9
commit 163dfe6
Showing
13 changed files
with
1,869 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
[package] | ||
name = "farm-staking" | ||
version = "0.0.0" | ||
authors = [ "you",] | ||
edition = "2018" | ||
publish = false | ||
|
||
[lib] | ||
path = "src/lib.rs" | ||
|
||
[dependencies.config] | ||
path = "../../common/modules/farm/config" | ||
|
||
[dependencies.farm_token] | ||
path = "../../common/modules/farm/farm_token" | ||
|
||
[dependencies.rewards] | ||
path = "../../common/modules/farm/rewards" | ||
|
||
[dependencies.token_send] | ||
path = "../../common/modules/token_send" | ||
|
||
[dependencies.token_merge_old] | ||
path = "../../common/modules/token_merge_old" | ||
|
||
[dependencies.common_structs_old] | ||
path = "../../common/common_structs_old" | ||
|
||
[dependencies.common_errors_old] | ||
path = "../../common/common_errors_old" | ||
|
||
[dependencies.elrond-wasm] | ||
version = "0.28.0" | ||
features = ["cb_closure_managed_deser"] | ||
|
||
[dev-dependencies.elrond-wasm-debug] | ||
version = "0.28.0" | ||
|
||
[dev-dependencies] | ||
num-bigint = "0.4.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# Farm Staking setup steps | ||
|
||
## Deployment | ||
|
||
The init function takes the following arguments: | ||
- farming_token_id - the farming token ID, which will also be the reward token | ||
- division_safety_constant - used in some calculations for precision. I suggest something like 10^9. | ||
- max_apr - a percentage of max APR, which will limit the user's rewards, with two decimals precision (i.e. 10_000 = 100%). Can be more than 100%. | ||
- min_unbond_epochs - Number of epochs the user has to wait between unstake and unbond. | ||
``` | ||
#[init] | ||
fn init( | ||
&self, | ||
farming_token_id: TokenIdentifier, | ||
division_safety_constant: BigUint, | ||
max_apr: BigUint, | ||
min_unbond_epochs: u64, | ||
) | ||
``` | ||
|
||
## Additional config | ||
|
||
### Setup farm token | ||
|
||
You have to register/issue the farm token and set its local roles, which is the token used for positions in the staking contract. This is done through the following endpoints: | ||
|
||
``` | ||
#[payable("EGLD")] | ||
#[endpoint(registerFarmToken)] | ||
fn register_farm_token( | ||
&self, | ||
#[payment_amount] register_cost: BigUint, | ||
token_display_name: ManagedBuffer, | ||
token_ticker: ManagedBuffer, | ||
num_decimals: usize, | ||
) | ||
``` | ||
|
||
For issue parameters format restrictions, take a look here: https://docs.elrond.com/developers/esdt-tokens/#parameters-format | ||
|
||
payment_amount should be `0.05 EGLD`. | ||
|
||
``` | ||
#[endpoint(setLocalRolesFarmToken)] | ||
fn set_local_roles_farm_token(&self) | ||
``` | ||
|
||
### Set per block rewards | ||
|
||
``` | ||
#[endpoint(setPerBlockRewardAmount)] | ||
fn set_per_block_rewards(&self, per_block_amount: BigUint) | ||
``` | ||
|
||
Keep in mind amount has to take into consideration the token's decimals. So if you have a token with 18 decimals, you have to pass 10^18 for "1". | ||
|
||
### Add the reward tokens | ||
|
||
``` | ||
#[payable("*")] | ||
#[endpoint(topUpRewards)] | ||
fn top_up_rewards( | ||
&self, | ||
#[payment_token] payment_token: TokenIdentifier, | ||
#[payment_amount] payment_amount: BigUint, | ||
) | ||
``` | ||
|
||
No args needed, you only need to pay the reward tokens. In the staking farm, rewards are not minted, but added by the owner. | ||
|
||
### Final steps | ||
|
||
First, you have to enable rewards generation: | ||
|
||
``` | ||
#[endpoint(startProduceRewards)] | ||
fn start_produce_rewards(&self) | ||
``` | ||
|
||
Then, you have to the set the state to active: | ||
|
||
``` | ||
#[endpoint] | ||
fn resume(&self) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"language": "rust" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "farm-staking-abi" | ||
|
||
version = "0.0.0" | ||
authors = [ "you",] | ||
edition = "2018" | ||
publish = false | ||
|
||
[dependencies.farm-staking] | ||
path = ".." | ||
|
||
[dependencies.elrond-wasm] | ||
version = "0.28.0" | ||
|
||
[dependencies.elrond-wasm-debug] | ||
version = "0.28.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fn main() { | ||
elrond_wasm_debug::meta::perform::<farm_staking::AbiProvider>(); | ||
} |
186 changes: 186 additions & 0 deletions
186
legacy-contracts/farm-staking-proxy-v1.3/src/custom_rewards.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
elrond_wasm::imports!(); | ||
elrond_wasm::derive_imports!(); | ||
|
||
use common_structs_old::Nonce; | ||
use config::MAX_PERCENT; | ||
|
||
pub const BLOCKS_IN_YEAR: u64 = 31_536_000 / 6; // seconds_in_year / 6_seconds_per_block | ||
|
||
#[elrond_wasm::module] | ||
pub trait CustomRewardsModule: | ||
config::ConfigModule + token_send::TokenSendModule + farm_token::FarmTokenModule | ||
{ | ||
fn calculate_extra_rewards_since_last_allocation(&self) -> BigUint { | ||
let current_block_nonce = self.blockchain().get_block_nonce(); | ||
let last_reward_nonce = self.last_reward_block_nonce().get(); | ||
|
||
if current_block_nonce > last_reward_nonce { | ||
let extra_rewards_unbounded = | ||
self.calculate_per_block_rewards(current_block_nonce, last_reward_nonce); | ||
|
||
let farm_token_supply = self.farm_token_supply().get(); | ||
let extra_rewards_apr_bounded_per_block = | ||
self.get_amount_apr_bounded(&farm_token_supply); | ||
|
||
let block_nonce_diff = current_block_nonce - last_reward_nonce; | ||
let extra_rewards_apr_bounded = extra_rewards_apr_bounded_per_block * block_nonce_diff; | ||
|
||
self.last_reward_block_nonce().set(¤t_block_nonce); | ||
|
||
core::cmp::min(extra_rewards_unbounded, extra_rewards_apr_bounded) | ||
} else { | ||
BigUint::zero() | ||
} | ||
} | ||
|
||
fn generate_aggregated_rewards(&self) { | ||
let mut extra_rewards = self.calculate_extra_rewards_since_last_allocation(); | ||
if extra_rewards > 0 { | ||
let mut accumulated_rewards = self.accumulated_rewards().get(); | ||
let total_rewards = &accumulated_rewards + &extra_rewards; | ||
let reward_capacity = self.reward_capacity().get(); | ||
if total_rewards > reward_capacity { | ||
let amount_over_capacity = total_rewards - reward_capacity; | ||
extra_rewards -= amount_over_capacity; | ||
} | ||
|
||
accumulated_rewards += &extra_rewards; | ||
self.accumulated_rewards().set(&accumulated_rewards); | ||
|
||
self.update_reward_per_share(&extra_rewards); | ||
} | ||
} | ||
|
||
#[only_owner] | ||
#[payable("*")] | ||
#[endpoint(topUpRewards)] | ||
fn top_up_rewards(&self) { | ||
let (payment_amount, payment_token) = self.call_value().payment_token_pair(); | ||
let reward_token_id = self.reward_token_id().get(); | ||
require!(payment_token == reward_token_id, "Invalid token"); | ||
|
||
self.reward_capacity().update(|r| *r += payment_amount); | ||
} | ||
|
||
#[only_owner] | ||
#[endpoint] | ||
fn end_produce_rewards(&self) { | ||
self.generate_aggregated_rewards(); | ||
self.produce_rewards_enabled().set(&false); | ||
} | ||
|
||
#[only_owner] | ||
#[endpoint(setPerBlockRewardAmount)] | ||
fn set_per_block_rewards(&self, per_block_amount: BigUint) { | ||
require!(per_block_amount != 0, "Amount cannot be zero"); | ||
|
||
self.generate_aggregated_rewards(); | ||
self.per_block_reward_amount().set(&per_block_amount); | ||
} | ||
|
||
#[only_owner] | ||
#[endpoint(setMaxApr)] | ||
fn set_max_apr(&self, max_apr: BigUint) { | ||
require!(max_apr != 0, "Max APR cannot be zero"); | ||
|
||
self.max_annual_percentage_rewards().set(&max_apr); | ||
} | ||
|
||
#[only_owner] | ||
#[endpoint(setMinUnbondEpochs)] | ||
fn set_min_unbond_epochs(&self, min_unbond_epochs: u64) { | ||
self.min_unbond_epochs().set(&min_unbond_epochs); | ||
} | ||
|
||
fn calculate_per_block_rewards( | ||
&self, | ||
current_block_nonce: Nonce, | ||
last_reward_block_nonce: Nonce, | ||
) -> BigUint { | ||
if current_block_nonce <= last_reward_block_nonce || !self.produces_per_block_rewards() { | ||
return BigUint::zero(); | ||
} | ||
|
||
let per_block_reward = self.per_block_reward_amount().get(); | ||
let block_nonce_diff = current_block_nonce - last_reward_block_nonce; | ||
|
||
per_block_reward * block_nonce_diff | ||
} | ||
|
||
fn update_reward_per_share(&self, reward_increase: &BigUint) { | ||
let farm_token_supply = self.farm_token_supply().get(); | ||
if farm_token_supply > 0 { | ||
let increase = | ||
self.calculate_reward_per_share_increase(reward_increase, &farm_token_supply); | ||
self.reward_per_share().update(|r| *r += increase); | ||
} | ||
} | ||
|
||
fn calculate_reward_per_share_increase( | ||
&self, | ||
reward_increase: &BigUint, | ||
farm_token_supply: &BigUint, | ||
) -> BigUint { | ||
&(reward_increase * &self.division_safety_constant().get()) / farm_token_supply | ||
} | ||
|
||
fn calculate_reward( | ||
&self, | ||
amount: &BigUint, | ||
current_reward_per_share: &BigUint, | ||
initial_reward_per_share: &BigUint, | ||
) -> BigUint { | ||
if current_reward_per_share > initial_reward_per_share { | ||
let reward_per_share_diff = current_reward_per_share - initial_reward_per_share; | ||
amount * &reward_per_share_diff / self.division_safety_constant().get() | ||
} else { | ||
BigUint::zero() | ||
} | ||
} | ||
|
||
fn get_amount_apr_bounded(&self, amount: &BigUint) -> BigUint { | ||
let max_apr = self.max_annual_percentage_rewards().get(); | ||
amount * &max_apr / MAX_PERCENT / BLOCKS_IN_YEAR | ||
} | ||
|
||
#[only_owner] | ||
#[endpoint(startProduceRewards)] | ||
fn start_produce_rewards(&self) { | ||
require!( | ||
self.per_block_reward_amount().get() != 0, | ||
"Cannot produce zero reward amount" | ||
); | ||
require!( | ||
!self.produce_rewards_enabled().get(), | ||
"Producing rewards is already enabled" | ||
); | ||
let current_nonce = self.blockchain().get_block_nonce(); | ||
self.produce_rewards_enabled().set(&true); | ||
self.last_reward_block_nonce().set(¤t_nonce); | ||
} | ||
|
||
#[inline(always)] | ||
fn produces_per_block_rewards(&self) -> bool { | ||
self.produce_rewards_enabled().get() | ||
} | ||
|
||
#[view(getRewardPerShare)] | ||
#[storage_mapper("reward_per_share")] | ||
fn reward_per_share(&self) -> SingleValueMapper<BigUint>; | ||
|
||
#[view(getAccumulatedRewards)] | ||
#[storage_mapper("accumulatedRewards")] | ||
fn accumulated_rewards(&self) -> SingleValueMapper<BigUint>; | ||
|
||
#[view(getRewardCapacity)] | ||
#[storage_mapper("reward_capacity")] | ||
fn reward_capacity(&self) -> SingleValueMapper<BigUint>; | ||
|
||
#[view(getAnnualPercentageRewards)] | ||
#[storage_mapper("annualPercentageRewards")] | ||
fn max_annual_percentage_rewards(&self) -> SingleValueMapper<BigUint>; | ||
|
||
#[view(getMinUnbondEpochs)] | ||
#[storage_mapper("minUnbondEpochs")] | ||
fn min_unbond_epochs(&self) -> SingleValueMapper<u64>; | ||
} |
Oops, something went wrong.