diff --git a/staking_contract/staking_contract/src/data.rs b/staking_contract/staking_contract/src/data.rs index 936dbe9..04d97bd 100644 --- a/staking_contract/staking_contract/src/data.rs +++ b/staking_contract/staking_contract/src/data.rs @@ -32,6 +32,21 @@ pub const EARLY_WITHDRAW_REWARD: &str = "early_withdraw_reward"; pub const STAKED_TOTAL: &str = "staked_total"; pub const REWARD_BALANCE: &str = "reward_balance"; pub const STAKED_BALANCE: &str = "staked_balance"; +const AMOUNT: &str = "amount"; +const REWARD: &str = "reward"; +const TOKEN_ADDRESS: &str = "token_address"; +const STAKER_ADDRESS: &str = "staker_address"; +const REWARD_AMOUNT: &str = "reward_amount"; +const STAKED_AMOUNT: &str = "staked_amount"; +const REQUESTED_AMOUNT: &str = "requested_amount"; +const WITHDRAWABLE_AMOUNT: &str = "withdrawable_amount"; + +const EVENT_TYPE: &str = "event_type"; + +const STAKE: &str = "stake"; +const PAID_OUT: &str = "paid_out"; +const ADD_REWARD: &str = "add_reward"; +const REFUNDED: &str = "refunded"; // Structure for managing staked tokens pub struct StakedTokens { @@ -72,7 +87,9 @@ impl StakedTokens { let staked_amount = self .get_amount_staked_by_address(owner) .ok_or(Error::NotAStaker)?; - let new_amount = staked_amount - *amount; + let new_amount = staked_amount + .checked_sub(*amount) + .ok_or(Error::CheckedSub)?; self.addresses_staked_dict .set(&key_to_str(owner), new_amount); Ok(()) @@ -136,7 +153,7 @@ pub fn withdraw_ends() -> u64 { /// Sets the withdrawal end time pub fn set_withdraw_ends(withdraw_ends: u64) { - set_key(WITHDRAW_STARTS, withdraw_ends); + set_key(WITHDRAW_ENDS, withdraw_ends); } /// Retrieves the total staking amount @@ -149,16 +166,6 @@ pub fn set_staking_total(staking_total: U256) { set_key(STAKING_TOTAL, staking_total); } -/// Retrieves the stake balance -pub fn stake_balance() -> u64 { - get_key(STAKED_BALANCE).unwrap_or_default() -} - -/// Sets the stake balance -pub fn set_stake_balance(stake_balance: u64) { - set_key(STAKED_BALANCE, stake_balance); -} - /// Retrieves the total reward amount pub fn total_reward() -> U256 { get_key(TOTAL_REWARD).unwrap_or_default() @@ -236,14 +243,14 @@ pub fn emit(event: &StakingContractEvent) { } => { let mut param = BTreeMap::new(); param.insert(CONTRACT_PACKAGE_HASH, package.to_string()); - param.insert("event_type", "stake".to_string()); - param.insert("token_address", token_address.to_string()); + param.insert(EVENT_TYPE, STAKE.to_string()); + param.insert(TOKEN_ADDRESS, token_address.to_string()); param.insert( - "staker_address", + STAKER_ADDRESS, TryInto::::try_into(*staker_address).unwrap(), ); - param.insert("requested_amount", requested_amount.to_string()); - param.insert("staked_amount", staked_amount.to_string()); + param.insert(REQUESTED_AMOUNT, requested_amount.to_string()); + param.insert(STAKED_AMOUNT, staked_amount.to_string()); events.push(param); } StakingContractEvent::PaidOut { @@ -254,13 +261,14 @@ pub fn emit(event: &StakingContractEvent) { } => { let mut param = BTreeMap::new(); param.insert(CONTRACT_PACKAGE_HASH, package.to_string()); - param.insert("token_address", token_address.to_string()); + param.insert(EVENT_TYPE, PAID_OUT.to_string()); + param.insert(TOKEN_ADDRESS, token_address.to_string()); param.insert( - "staker_address", + STAKER_ADDRESS, TryInto::::try_into(*staker_address).unwrap(), ); - param.insert("amount", amount.to_string()); - param.insert("reward", reward.to_string()); + param.insert(AMOUNT, amount.to_string()); + param.insert(REWARD, reward.to_string()); events.push(param); } StakingContractEvent::AddReward { @@ -269,9 +277,9 @@ pub fn emit(event: &StakingContractEvent) { } => { let mut param = BTreeMap::new(); param.insert(CONTRACT_PACKAGE_HASH, package.to_string()); - param.insert("event_type", "add_reward".to_string()); - param.insert("reward_amount", reward_amount.to_string()); - param.insert("withdrawable_amount", withdrawable_amount.to_string()); + param.insert(EVENT_TYPE, ADD_REWARD.to_string()); + param.insert(REWARD_AMOUNT, reward_amount.to_string()); + param.insert(WITHDRAWABLE_AMOUNT, withdrawable_amount.to_string()); events.push(param); } StakingContractEvent::Refunded { @@ -281,12 +289,13 @@ pub fn emit(event: &StakingContractEvent) { } => { let mut param = BTreeMap::new(); param.insert(CONTRACT_PACKAGE_HASH, package.to_string()); - param.insert("token_address", token_address.to_string()); + param.insert(EVENT_TYPE, REFUNDED.to_string()); + param.insert(TOKEN_ADDRESS, token_address.to_string()); param.insert( - "staker_address", + STAKER_ADDRESS, TryInto::::try_into(*staker_address).unwrap(), ); - param.insert("amount", amount.to_string()); + param.insert(AMOUNT, amount.to_string()); events.push(param); } }; diff --git a/staking_contract/staking_contract/src/error.rs b/staking_contract/staking_contract/src/error.rs index 45bc232..cd9f27f 100644 --- a/staking_contract/staking_contract/src/error.rs +++ b/staking_contract/staking_contract/src/error.rs @@ -11,34 +11,42 @@ pub enum Error { WrongArguments = 2, /// Not required stake. NotRequiredStake = 3, - /// Bad timing. - BadTiming = 4, + /// After bad timing. + AfterBadTiming = 4, + /// Before bad timing. + BeforeBadTiming = 5, /// Invalid context. - InvalidContext = 5, + InvalidContext = 6, /// Negative reward. - NegativeReward = 6, + NegativeReward = 7, /// Negative withdrawable reward. - NegativeWithdrawableReward = 7, + NegativeWithdrawableReward = 8, /// Negative amount. - NegativeAmount = 8, + NegativeAmount = 9, /// Missing contract package hash. - MissingContractPackageHash = 9, + MissingContractPackageHash = 10, /// Invalid contract package hash. - InvalidContractPackageHash = 10, + InvalidContractPackageHash = 11, /// Invalid contract hash. - InvalidContractHash = 11, + InvalidContractHash = 12, /// Withdraw check error early. - WithdrawCheckErrorEarly = 12, + WithdrawCheckErrorEarly = 13, /// Withdraw check error. - WithdrawCheckError = 13, + WithdrawCheckError = 14, /// Neither account hash nor neither contract package hash. - NeitherAccountHashNorNeitherContractPackageHash = 14, + NeitherAccountHashNorNeitherContractPackageHash = 15, /// Not a staker. - NotAStaker = 15, + NotAStaker = 16, /// Immediate caller address fail. - ImmediateCallerAddressFail = 16, + ImmediateCallerAddressFail = 17, /// Not staking contract package hash. - NotStakingContractPackageHash = 17, + NotStakingContractPackageHash = 18, + StakingEndsBeforeStakingStarts = 19, + WithdrawStartsStakingEnds = 20, + WithdrawEndsWithdrawStarts = 21, + StakingStartsNow = 22, + /// Subtraction underflow + CheckedSub = 23, } impl From for ApiError { diff --git a/staking_contract/staking_contract/src/main.rs b/staking_contract/staking_contract/src/main.rs index e7f0870..3afecb9 100644 --- a/staking_contract/staking_contract/src/main.rs +++ b/staking_contract/staking_contract/src/main.rs @@ -24,19 +24,52 @@ use casper_types::{ use contract_utils::{ContractContext, OnChainContractStorage}; use staking_contract::staking_contract::CEP20STK; +const ENTRY_POINT_NAME: &str = "name"; +const ENTRY_POINT_ADDRESS: &str = "address"; +const ENTRY_POINT_STAKING_STARTS: &str = "staking_starts"; +const ENTRY_POINT_WITHDRAW_STARTS: &str = "withdraw_starts"; +const ENTRY_POINT_WITHDRAW_ENDS: &str = "withdraw_ends"; +const ENTRY_POINT_STAKING_TOTAL: &str = "staking_total"; +const ENTRY_POINT_WITHDRAW: &str = "withdraw"; +const ENTRY_POINT_STAKE: &str = "stake"; +const ENTRY_POINT_ADD_REWARD: &str = "add_reward"; +const ENTRY_POINT_AMOUNT_STAKED: &str = "amount_staked"; +const ENTRY_POINT_GET_CURRENT_REWARD: &str = "get_current_reward"; +const ENTRY_POINT_CONSTRUCTOR: &str = "constructor"; +const ENTRY_POINT_STAKER_REWARD: &str = "staker_reward"; + +const AMOUNT: &str = "amount"; +const STAKER: &str = "staker"; +const ADDRESS: &str = "address"; +const NAME: &str = "name"; +const STAKING_STARTS: &str = "staking_starts"; +const STAKING_ENDS: &str = "staking_ends"; +const WITHDRAW_STARTS: &str = "withdraw_starts"; +const WITHDRAW_ENDS: &str = "withdraw_ends"; +const STAKING_TOTAL: &str = "staking_total"; +const STACKING_CONTRACT_PACKAGE_HASH: &str = "stacking_contract_package_hash"; +const ERC20_CONTRACT_PACKAGE_HASH: &str = "erc20_contract_package_hash"; +const STAKER_ADDRESS: &str = "staker_address"; +const WITHDRAWABLE_AMOUNT: &str = "withdrawable_amount"; +const REWARD_AMOUNT: &str = "reward_amount"; +const CONTRACT_PACKAGE_HASH: &str = "contract_package_hash"; +const STAKING_CONTRACT_HASH: &str = "staking_contract_hash"; + +const CONSTRUCTOR_GROUP: &str = "constructor"; + /// Struct representing the token contract. #[derive(Default)] -struct Token(OnChainContractStorage); +struct Staking(OnChainContractStorage); -impl ContractContext for Token { +impl ContractContext for Staking { fn storage(&self) -> &OnChainContractStorage { &self.0 } } -impl CEP20STK for Token {} +impl CEP20STK for Staking {} -impl Token { +impl Staking { /// Constructor for the token contract. #[allow(clippy::too_many_arguments)] fn constructor( @@ -58,7 +91,8 @@ impl Token { withdraw_starts, withdraw_ends, staking_total, - ); + ) + .unwrap_or_revert(); } } @@ -73,32 +107,32 @@ impl Token { #[no_mangle] pub extern "C" fn constructor() { // Read arguments for the constructor call. - let name = runtime::get_named_arg::("name"); - let address = runtime::get_named_arg::("address"); - let staking_starts: u64 = runtime::get_named_arg::("staking_starts"); - let staking_ends: u64 = runtime::get_named_arg::("staking_ends"); - let withdraw_starts: u64 = runtime::get_named_arg::("withdraw_starts"); - let withdraw_ends: u64 = runtime::get_named_arg::("withdraw_ends"); - let staking_total: U256 = runtime::get_named_arg::("staking_total"); + let name = runtime::get_named_arg::(NAME); + let address = runtime::get_named_arg::(ADDRESS); + let staking_starts: u64 = runtime::get_named_arg::(STAKING_STARTS); + let staking_ends: u64 = runtime::get_named_arg::(STAKING_ENDS); + let withdraw_starts: u64 = runtime::get_named_arg::(WITHDRAW_STARTS); + let withdraw_ends: u64 = runtime::get_named_arg::(WITHDRAW_ENDS); + let staking_total: U256 = runtime::get_named_arg::(STAKING_TOTAL); let stacking_contract_package_hash = - runtime::get_named_arg::("stacking_contract_package_hash"); - let erc20_contract_package_hash = runtime::get_named_arg::("erc20_contract_package_hash"); + runtime::get_named_arg::(STACKING_CONTRACT_PACKAGE_HASH); + let erc20_contract_package_hash = runtime::get_named_arg::(ERC20_CONTRACT_PACKAGE_HASH); // Store the stacking_contract_package_hash and erc20_contract_package_hash as keys #[allow(clippy::useless_conversion)] runtime::put_key( - "stacking_contract_package_hash", + STACKING_CONTRACT_PACKAGE_HASH, stacking_contract_package_hash.into(), ); #[allow(clippy::useless_conversion)] runtime::put_key( - "erc20_contract_package_hash", + ERC20_CONTRACT_PACKAGE_HASH, erc20_contract_package_hash.into(), ); // Initialize the token contract using the constructor arguments - Token::default().constructor( + Staking::default().constructor( name, address, staking_starts, @@ -112,49 +146,49 @@ pub extern "C" fn constructor() { // The `name` function returns the name of the staking contract. #[no_mangle] pub extern "C" fn name() { - let ret = Token::default().name(); + let ret = Staking::default().name(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `address` function returns the address of the staking contract. #[no_mangle] pub extern "C" fn address() { - let ret = Token::default().address(); + let ret = Staking::default().address(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `staking_starts` function returns the start date of the staking period. #[no_mangle] pub extern "C" fn staking_starts() { - let ret = Token::default().staking_starts(); + let ret = Staking::default().staking_starts(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `staking_ends` function returns the end date of the staking period. #[no_mangle] pub extern "C" fn staking_ends() { - let ret = Token::default().staking_starts(); + let ret = Staking::default().staking_starts(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `withdraw_starts` function returns the start date of the withdrawal period. #[no_mangle] pub extern "C" fn withdraw_starts() { - let ret = Token::default().withdraw_starts(); + let ret = Staking::default().withdraw_starts(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `withdraw_ends` function returns the end date of the withdrawal period. #[no_mangle] pub extern "C" fn withdraw_ends() { - let ret = Token::default().withdraw_ends(); + let ret = Staking::default().withdraw_ends(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `staking_total` function returns the total number of tokens that can be staked. #[no_mangle] pub extern "C" fn staking_total() { - let ret = Token::default().staking_total(); + let ret = Staking::default().staking_total(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } @@ -163,23 +197,18 @@ pub extern "C" fn staking_total() { // The function returns the number of tokens that the staker has staked. #[no_mangle] pub extern "C" fn amount_staked() { - let staker = runtime::get_named_arg::("staker"); - let ret = Token::default().amount_staked(staker).unwrap_or_revert(); + let staker = runtime::get_named_arg::(STAKER); + let ret = Staking::default().amount_staked(staker).unwrap_or_revert(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } // The `stake` function takes the following arguments: // - amount: The number of tokens to stake. -// - staking_contract_package_hash: The hash of the staking contract package. // The function stakes the specified number of tokens in the staking contract. #[no_mangle] pub extern "C" fn stake() { - let amount = runtime::get_named_arg::("amount"); - let staking_contract_package_hash = - runtime::get_named_arg::("staking_contract_package_hash"); - let ret = Token::default() - .stake(amount, staking_contract_package_hash) - .unwrap_or_revert(); + let amount = runtime::get_named_arg::(AMOUNT); + let ret = Staking::default().stake(amount).unwrap_or_revert(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } @@ -188,8 +217,8 @@ pub extern "C" fn stake() { // The function withdraws the specified number of tokens from the staking contract. #[no_mangle] pub extern "C" fn withdraw() { - let amount = runtime::get_named_arg::("amount"); - let ret = Token::default().withdraw(amount).unwrap_or_revert(); + let amount = runtime::get_named_arg::(AMOUNT); + let ret = Staking::default().withdraw(amount).unwrap_or_revert(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } @@ -199,9 +228,9 @@ pub extern "C" fn withdraw() { // The function adds the specified amount of reward to the staking contract and updates the withdrawable amount. #[no_mangle] pub extern "C" fn add_reward() { - let reward_amount = runtime::get_named_arg::("reward_amount"); - let withdrawable_amount = runtime::get_named_arg::("withdrawable_amount"); - let ret = Token::default() + let reward_amount = runtime::get_named_arg::(REWARD_AMOUNT); + let withdrawable_amount = runtime::get_named_arg::(WITHDRAWABLE_AMOUNT); + let ret = Staking::default() .add_reward(reward_amount, withdrawable_amount) .unwrap_or_revert(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); @@ -210,7 +239,7 @@ pub extern "C" fn add_reward() { // The `get_current_reward` function returns the current reward amount. #[no_mangle] pub extern "C" fn get_current_reward() { - let ret = Token::default().reward_balance(); + let ret = Staking::default().reward_balance(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); } @@ -219,8 +248,8 @@ pub extern "C" fn get_current_reward() { // The function returns the reward amount that the staker has earned. #[no_mangle] pub extern "C" fn staker_reward() { - let staker_address = runtime::get_named_arg::("staker_address"); - let ret = Token::default() + let staker_address = runtime::get_named_arg::(STAKER_ADDRESS); + let ret = Staking::default() .staker_reward(staker_address) .unwrap_or_revert(); runtime::ret(CLValue::from_t(ret).unwrap_or_revert()); @@ -229,109 +258,24 @@ pub extern "C" fn staker_reward() { #[no_mangle] pub extern "C" fn call() { // Read arguments for the constructor call. - let name: String = runtime::get_named_arg("name"); - let address = runtime::get_named_arg::("address"); - let staking_starts: u64 = runtime::get_named_arg::("staking_starts"); - let staking_ends: u64 = runtime::get_named_arg::("staking_ends"); - let withdraw_starts: u64 = runtime::get_named_arg::("withdraw_starts"); - let withdraw_ends: u64 = runtime::get_named_arg::("withdraw_ends"); - let staking_total: U256 = runtime::get_named_arg::("staking_total"); - let erc20_contract_package_hash = runtime::get_named_arg::("erc20_contract_package_hash"); - - /* - - casper-client put-deploy \ - --chain-name casper-test \ - --node-address http://44.208.234.65:7777 \ - --secret-key ./staking_contract/keys/secret_key.pem \ - --session-path ./staking_contract/target/wasm32-unknown-unknown/release/staking_contract.wasm \ - --payment-amount 200000000000 \ - --session-arg "name:string='FerrumX'" \ - --session-arg "address:string='782fe4b0bb944e6b1fd2c5a1456a78f0e2193d47dee9b1af5711d6b6e6aaca60'" \ - --session-arg "staking_starts:u64='0'" \ - --session-arg "staking_ends:u64='1755994649'" \ - --session-arg "withdraw_starts:u64='0'" \ - --session-arg "withdraw_ends:u64='1755994649'" \ - --session-arg "staking_total:U256='500000'" \ - --session-arg "erc20_contract_hash:string='contract-2934370f0a53457987d4dba9d68d71e7ee45b676677fbc66653bd15ea58db60f'" - - */ - // casper-client put-deploy \ - // --chain-name casper-test \ - // --node-address http://44.208.234.65:7777 \ - // --secret-key ./staking_contract/keys/secret_key.pem \ - // --session-path ./target/wasm32-unknown-unknown/release/staking_contract.wasm \ - // --payment-amount 200000000000 \ - // --session-arg "name:string='FerrumX'" \ - // --session-arg "address:string='782fe4b0bb944e6b1fd2c5a1456a78f0e2193d47dee9b1af5711d6b6e6aaca60'" \ - // --session-arg "staking_starts:u64='0'" \ - // --session-arg "staking_ends:u64='1755994649'" \ - // --session-arg "withdraw_starts:u64='0'" \ - // --session-arg "withdraw_ends:u64='1755994649'" \ - // --session-arg "staking_total:U256='500000'" \ - // --session-arg "erc20_contract_hash:string='contract-4277c47789fd154c42471978b4bffec31760f56b07507334a219682cf475181f'" - - /* - casper-client put-deploy \ - --chain-name casper-test \ - --node-address http://44.208.234.65:7777 \ - --secret-key staking_contract/keys/secret_key.pem \ - --payment-amount 8000000000 \ - --session-hash 5ec37b076f618f8e3205eda7ad1f8bade197b296716b7e0cbba11a7b7c613bc6 \ - --session-entry-point "stake" \ - --session-arg "amount:u256='5'" - */ - - // casper-client put-deploy \ - // --chain-name casper-test \ - // --node-address http://44.208.234.65:7777 \ - // --secret-key staking_contract/keys/secret_key.pem \ - // --session-hash hash-4277c47789fd154c42471978b4bffec31760f56b07507334a219682cf475181f \ - // --session-entry-point approve \ - // --payment-amount 20000000000 \ - // --session-arg "spender:key='hash-153fc70a20eeefb8b6a02b217b6a19cc8439092569750ced554bde9d3d8f9896'" \ - // --session-arg "amount:u256='1000000000'" - - /* - casper-client put-deploy \ - --chain-name casper-test \ - --node-address http://159.65.118.250:7777 \ - --secret-key cep47/keys/secret_key.pem \ - --payment-amount 4000000000 \ - --session-hash 6adb2902bf7c56116ead7ea7a2ffa269b8d4b117b632d2c44052f3c951dcaa0b \ - --session-entry-point "withdraw" \ - --session-arg "amount:u256='10'" - */ - - /* - casper-client put-deploy \ - --chain-name casper-test \ - --node-address http://159.65.118.250:7777 \ - --secret-key cep47/keys/secret_key.pem \ - --payment-amount 4000000000 \ - --session-hash 6adb2902bf7c56116ead7ea7a2ffa269b8d4b117b632d2c44052f3c951dcaa0b \ - --session-entry-point "add_reward" \ - --session-arg "reward_amount:u256='20'" \ - --session-arg "withdrawable_amount:u256='19'" - */ - - /* - casper-client get-deploy \ - --id 2 \ - --node-address http://159.65.118.250:7777 \ - ee07324ad466aad373e94f787b3dbf1ba1ff00175e97a0bce002bb45737ad5e6 - - */ + let name: String = runtime::get_named_arg(NAME); + let address = runtime::get_named_arg::(ADDRESS); + let staking_starts: u64 = runtime::get_named_arg::(STAKING_STARTS); + let staking_ends: u64 = runtime::get_named_arg::(STAKING_ENDS); + let withdraw_starts: u64 = runtime::get_named_arg::(WITHDRAW_STARTS); + let withdraw_ends: u64 = runtime::get_named_arg::(WITHDRAW_ENDS); + let staking_total: U256 = runtime::get_named_arg::(STAKING_TOTAL); + let erc20_contract_package_hash = runtime::get_named_arg::(ERC20_CONTRACT_PACKAGE_HASH); let (contract_hash, _) = storage::new_contract( get_entry_points(), None, - Some(String::from("contract_package_hash")), + Some(String::from(CONTRACT_PACKAGE_HASH)), None, ); let package_hash: ContractPackageHash = ContractPackageHash::new( - runtime::get_key("contract_package_hash") + runtime::get_key(CONTRACT_PACKAGE_HASH) .unwrap_or_revert() .into_hash() .unwrap_or_revert(), @@ -341,31 +285,31 @@ pub extern "C" fn call() { // Prepare constructor args let constructor_args = runtime_args! { - "name" => name, - "address" => address, - "staking_starts" => staking_starts, - "staking_ends" => staking_ends, - "withdraw_starts" => withdraw_starts, - "withdraw_ends" => withdraw_ends, - "staking_total" => staking_total, - "stacking_contract_package_hash" => package_hash_key, - "erc20_contract_package_hash" => erc20_contract_package_hash, + NAME => name, + ADDRESS => address, + STAKING_STARTS => staking_starts, + STAKING_ENDS => staking_ends, + WITHDRAW_STARTS => withdraw_starts, + WITHDRAW_ENDS => withdraw_ends, + STAKING_TOTAL => staking_total, + STACKING_CONTRACT_PACKAGE_HASH => package_hash_key, + ERC20_CONTRACT_PACKAGE_HASH => erc20_contract_package_hash, }; let constructor_access: URef = - storage::create_contract_user_group(package_hash, "constructor", 1, Default::default()) + storage::create_contract_user_group(package_hash, CONSTRUCTOR_GROUP, 1, Default::default()) .unwrap_or_revert() .pop() .unwrap_or_revert(); - let _: () = runtime::call_contract(contract_hash, "constructor", constructor_args); + let _: () = runtime::call_contract(contract_hash, ENTRY_POINT_CONSTRUCTOR, constructor_args); let mut urefs = BTreeSet::new(); urefs.insert(constructor_access); - storage::remove_contract_user_group_urefs(package_hash, "constructor", urefs) + storage::remove_contract_user_group_urefs(package_hash, CONSTRUCTOR_GROUP, urefs) .unwrap_or_revert(); - runtime::put_key("staking_contract_hash", contract_hash.into()); + runtime::put_key(STAKING_CONTRACT_HASH, contract_hash.into()); runtime::put_key( &format!("{contract_hash}_contract_hash_wrapped"), storage::new_uref(contract_hash).into(), @@ -375,107 +319,104 @@ pub extern "C" fn call() { fn get_entry_points() -> EntryPoints { let mut entry_points = EntryPoints::new(); entry_points.add_entry_point(EntryPoint::new( - "constructor", + ENTRY_POINT_CONSTRUCTOR, vec![ - Parameter::new("name", String::cl_type()), - Parameter::new("address", String::cl_type()), - Parameter::new("staking_starts", u64::cl_type()), - Parameter::new("staking_ends", u64::cl_type()), - Parameter::new("withdraw_starts", u64::cl_type()), - Parameter::new("withdraw_ends", u64::cl_type()), - Parameter::new("staking_total", U256::cl_type()), - Parameter::new("erc20_contract_package_hash", String::cl_type()), + Parameter::new(NAME, String::cl_type()), + Parameter::new(ADDRESS, String::cl_type()), + Parameter::new(STAKING_STARTS, u64::cl_type()), + Parameter::new(STAKING_ENDS, u64::cl_type()), + Parameter::new(WITHDRAW_STARTS, u64::cl_type()), + Parameter::new(WITHDRAW_ENDS, u64::cl_type()), + Parameter::new(STAKING_TOTAL, U256::cl_type()), + Parameter::new(ERC20_CONTRACT_PACKAGE_HASH, String::cl_type()), ], <()>::cl_type(), - EntryPointAccess::Groups(vec![Group::new("constructor")]), + EntryPointAccess::Groups(vec![Group::new(CONSTRUCTOR_GROUP)]), EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "name", + ENTRY_POINT_NAME, vec![], String::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "address", + ENTRY_POINT_ADDRESS, vec![], String::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "staking_starts", + ENTRY_POINT_STAKING_STARTS, vec![], u64::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "withdraw_starts", + ENTRY_POINT_WITHDRAW_STARTS, vec![], u64::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "withdraw_ends", + ENTRY_POINT_WITHDRAW_ENDS, vec![], u64::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "staking_total", + ENTRY_POINT_STAKING_TOTAL, vec![], U256::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "withdraw", - vec![Parameter::new("amount", Key::cl_type())], + ENTRY_POINT_WITHDRAW, + vec![Parameter::new(AMOUNT, Key::cl_type())], <()>::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "stake", - vec![ - Parameter::new("amount", U256::cl_type()), - Parameter::new("staking_contract_package_hash", String::cl_type()), - ], + ENTRY_POINT_STAKE, + vec![Parameter::new(AMOUNT, U256::cl_type())], <()>::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "amount_staked", - vec![Parameter::new("staker", Key::cl_type())], + ENTRY_POINT_AMOUNT_STAKED, + vec![Parameter::new(STAKER, Key::cl_type())], <()>::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "add_reward", + ENTRY_POINT_ADD_REWARD, vec![ - Parameter::new("reward_amount", Key::cl_type()), - Parameter::new("withdrawable_amount", Key::cl_type()), + Parameter::new(REWARD_AMOUNT, Key::cl_type()), + Parameter::new(WITHDRAWABLE_AMOUNT, Key::cl_type()), ], <()>::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "get_current_reward", + ENTRY_POINT_GET_CURRENT_REWARD, vec![], ::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, )); entry_points.add_entry_point(EntryPoint::new( - "staker_reward", - vec![Parameter::new("staker_address", Key::cl_type())], + ENTRY_POINT_STAKER_REWARD, + vec![Parameter::new(STAKER_ADDRESS, Key::cl_type())], ::cl_type(), EntryPointAccess::Public, EntryPointType::Contract, diff --git a/staking_contract/staking_contract/src/modifiers.rs b/staking_contract/staking_contract/src/modifiers.rs index c775b79..39cd54c 100644 --- a/staking_contract/staking_contract/src/modifiers.rs +++ b/staking_contract/staking_contract/src/modifiers.rs @@ -31,7 +31,7 @@ pub fn positive(amount: U256) -> Result<(), Error> { /// A `Result`. If the current block time is after the event time, the result will be `Ok(())`. If the current block time is before the event time, the result will be `Err(Error::BadTiming)`. pub fn after(event_time: u64) -> Result<(), Error> { if runtime::get_blocktime() < BlockTime::new(event_time) { - Err(Error::BadTiming) + Err(Error::AfterBadTiming) } else { Ok(()) } @@ -48,7 +48,7 @@ pub fn after(event_time: u64) -> Result<(), Error> { /// A `Result`. If the current block time is before the event time, the result will be `Ok(())`. If the current block time is after the event time, the result will be `Err(Error::BadTiming)`. pub fn before(event_time: u64) -> Result<(), Error> { if runtime::get_blocktime() >= BlockTime::new(event_time) { - Err(Error::BadTiming) + Err(Error::BeforeBadTiming) } else { Ok(()) } diff --git a/staking_contract/staking_contract/src/staking_contract.rs b/staking_contract/staking_contract/src/staking_contract.rs index 0c9ad6e..6a3fbeb 100644 --- a/staking_contract/staking_contract/src/staking_contract.rs +++ b/staking_contract/staking_contract/src/staking_contract.rs @@ -11,8 +11,12 @@ use casper_contract::{contract_api::runtime, unwrap_or_revert::UnwrapOrRevert}; use casper_types::{runtime_args, BlockTime, ContractPackageHash, Key, RuntimeArgs, U256}; use contract_utils::{ContractContext, ContractStorage}; -// This code defines a trait for the staking contract. +const STACKING_CONTRACT_PACKAGE_HASH: &str = "stacking_contract_package_hash"; +const ERC20_CONTRACT_PACKAGE_HASH: &str = "erc20_contract_package_hash"; + +const ENTRY_POINT_TRANSFER: &str = "transfer"; +// This code defines a trait for the staking contract. #[allow(clippy::too_many_arguments)] pub trait CEP20STK: ContractContext { // Initializes the staking contract. @@ -26,7 +30,19 @@ pub trait CEP20STK: ContractContext { withdraw_starts: u64, withdraw_ends: u64, staking_total: U256, - ) { + ) -> Result<(), Error> { + if staking_ends < staking_starts { + return Err(Error::StakingEndsBeforeStakingStarts); + } + if withdraw_starts < staking_ends { + return Err(Error::WithdrawStartsStakingEnds); + } + if withdraw_ends < withdraw_starts { + return Err(Error::WithdrawEndsWithdrawStarts); + } + if staking_starts < u64::from(runtime::get_blocktime()) { + return Err(Error::StakingStartsNow); + } data::set_name(name); data::set_address(address); data::set_staking_starts(staking_starts); @@ -36,6 +52,7 @@ pub trait CEP20STK: ContractContext { data::set_staking_total(staking_total); // Initialize the staked tokens map. StakedTokens::init(); + Ok(()) } // Returns the contract name. @@ -93,6 +110,7 @@ pub trait CEP20STK: ContractContext { data::staked_balance() } + // Sets the staked balance. fn set_staked_balance(&self, staked_balance: U256) { data::set_staked_balance(staked_balance) } @@ -133,11 +151,7 @@ pub trait CEP20STK: ContractContext { } /// Stakes the given amount of tokens. - fn stake( - &mut self, - amount: U256, - staking_contract_package_hash: String, - ) -> Result { + fn stake(&mut self, amount: U256) -> Result { modifiers::positive(amount)?; modifiers::after(self.staking_starts())?; modifiers::before(self.staking_ends())?; @@ -151,7 +165,7 @@ pub trait CEP20STK: ContractContext { let mut remaining_token = amount; - if let Some(diff) = self.staking_total().checked_sub(remaining_token) { + if let Some(diff) = self.staking_total().checked_sub(amount) { if remaining_token > diff { remaining_token = diff; } @@ -165,15 +179,7 @@ pub trait CEP20STK: ContractContext { return Err(Error::NotRequiredStake); } - let staking_contract_package_hash = - ContractPackageHash::from_formatted_str(staking_contract_package_hash.as_str()) - .map_err(|_| Error::NotStakingContractPackageHash)?; - - self.pay_to( - staker_address, - crate::address::Address::ContractPackage(staking_contract_package_hash), - remaining_token, - ); + self.pay_me(staker_address, remaining_token); self.emit(StakingContractEvent::Stake { token_address, @@ -184,8 +190,7 @@ pub trait CEP20STK: ContractContext { if remaining_token < amount { let refund = amount - remaining_token; - self.pay_direct(staker_address, refund) - .unwrap_or_revert_with(Error::NegativeAmount); + self.pay_to(staker_address, staker_address, refund); } self.set_staking_total(self.staking_total() + remaining_token); @@ -197,7 +202,7 @@ pub trait CEP20STK: ContractContext { /// Withdraws the given amount of tokens. fn withdraw(&mut self, amount: U256) -> Result { modifiers::positive(amount)?; - modifiers::after(self.staking_starts())?; + modifiers::after(self.withdraw_starts())?; let stakers_dict = StakedTokens::instance(); let caller_address = detail::get_immediate_caller_address()?; @@ -224,15 +229,32 @@ pub trait CEP20STK: ContractContext { .unwrap_or_revert_with(Error::ImmediateCallerAddressFail); let token_address = self.address(); - let denom = U256::from(self.withdraw_ends() - self.staking_ends()) * self.staking_total(); + let denom = U256::from( + self.withdraw_ends() + .checked_sub(self.staking_ends()) + .ok_or(Error::CheckedSub)?, + ) * self.staked_total(); - let reward: U256 = - U256::from(u64::from(runtime::get_blocktime()) - self.staking_ends()) * amount / denom; + let reward: U256 = U256::from( + u64::from(runtime::get_blocktime()) + .checked_sub(self.staking_ends()) + .ok_or(Error::CheckedSub)?, + ) * self.early_withdraw_reward() + * amount + / denom; let pay_out = amount + reward; - self.set_reward_balance(self.reward_balance() - reward); - self.set_staked_balance(self.staked_balance() - amount); + self.set_reward_balance( + self.reward_balance() + .checked_sub(reward) + .ok_or(Error::CheckedSub)?, + ); + self.set_staked_balance( + self.staked_balance() + .checked_sub(amount) + .ok_or(Error::CheckedSub)?, + ); let stakers_dict = StakedTokens::instance(); stakers_dict.withdraw_stake(&Key::from(caller_address), &amount)?; // pay the tokens @@ -309,10 +331,18 @@ pub trait CEP20STK: ContractContext { fn staker_reward(&mut self, staker_address: Key) -> Result { let amount = self.amount_staked(staker_address)?; let reward: U256 = if runtime::get_blocktime() < BlockTime::new(self.staking_ends()) { - let denom = - U256::from(self.withdraw_ends() - self.staking_ends()) * self.staking_total(); - - U256::from(u64::from(runtime::get_blocktime()) - self.staking_ends()) * amount / denom + let denom = U256::from( + self.withdraw_ends() + .checked_sub(self.staking_ends()) + .ok_or(Error::CheckedSub)?, + ) * self.staked_total(); + + U256::from( + u64::from(runtime::get_blocktime()) + .checked_sub(self.staking_ends()) + .ok_or(Error::CheckedSub)?, + ) * amount + / denom } else { self.reward_balance() * amount / self.staked_balance() }; @@ -322,20 +352,25 @@ pub trait CEP20STK: ContractContext { /// Pays the given amount of tokens directly to the recipient. fn pay_direct(&self, recipient: Address, amount: U256) -> Result<(), Error> { - // modifiers::positive(amount)?; - let erc20_contract_package_hash = self.erc20_metadata(); + modifiers::positive(amount)?; + let erc20_contract_package_hash = self.erc20_contract_package_hash(); let args = runtime_args! { "recipient" => recipient, "amount" => amount, }; - runtime::call_versioned_contract::<()>(erc20_contract_package_hash, None, "transfer", args); + runtime::call_versioned_contract::<()>( + erc20_contract_package_hash, + None, + ENTRY_POINT_TRANSFER, + args, + ); Ok(()) } /// Pays the given amount of tokens to the recipient, transferring them from the given allower. fn pay_to(&self, allower: Address, recipient: Address, amount: U256) { - let erc20_contract_package_hash = self.erc20_metadata(); + let erc20_contract_package_hash = self.erc20_contract_package_hash(); let args = runtime_args! { "owner" => allower, "recipient" => recipient, @@ -352,7 +387,7 @@ pub trait CEP20STK: ContractContext { /// Pays the given amount of tokens to the staking contract, transferring them from the given allower. fn pay_me(&self, payer: Address, amount: U256) { #[allow(clippy::redundant_closure)] - let stacking_contract_package_hash = runtime::get_key("stacking_contract_package_hash") + let stacking_contract_package_hash = runtime::get_key(STACKING_CONTRACT_PACKAGE_HASH) .unwrap_or_revert_with(Error::MissingContractPackageHash) .into_hash() .map(|hash_address| ContractPackageHash::new(hash_address)) @@ -370,9 +405,9 @@ pub trait CEP20STK: ContractContext { } /// Returns `ContractPackageHash` of the ERC-20 type token that is staked - fn erc20_metadata(&self) -> ContractPackageHash { + fn erc20_contract_package_hash(&self) -> ContractPackageHash { #[allow(clippy::redundant_closure)] - runtime::get_key("erc20_contract_package_hash") + runtime::get_key(ERC20_CONTRACT_PACKAGE_HASH) .unwrap_or_revert_with(Error::MissingContractPackageHash) .into_hash() .map(|hash_address| ContractPackageHash::new(hash_address)) diff --git a/staking_contract/staking_contract_tests/src/staking_contract_tests.rs b/staking_contract/staking_contract_tests/src/staking_contract_tests.rs index 725fc76..0b2d315 100644 --- a/staking_contract/staking_contract_tests/src/staking_contract_tests.rs +++ b/staking_contract/staking_contract_tests/src/staking_contract_tests.rs @@ -12,6 +12,8 @@ use casper_types::{ PublicKey, RuntimeArgs, SecretKey, BLAKE2B_DIGEST_LENGTH, U256, }; use once_cell::sync::Lazy; +use std::convert::TryInto; +use std::time::SystemTime; const ADDRESS: &str = "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d"; @@ -74,8 +76,8 @@ fn test_approve_and_stake() { "name" => "FerrumX".to_string(), "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, - "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, + "staking_ends" => 1681708875776u64, + "withdraw_starts" => 1681708875776u64, "withdraw_ends" => 1781708875776u64, "staking_total" => U256::from(500000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash), @@ -292,8 +294,8 @@ fn test_approve_and_stake_and_amount_staked() { "name" => "FerrumX".to_string(), "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, - "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, + "staking_ends" => 1681708875776u64, + "withdraw_starts" => 1681708875776u64, "withdraw_ends" => 1781708875776u64, "staking_total" => U256::from(500000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash), @@ -567,8 +569,8 @@ fn test_approve_and_stake_and_staker_reward() { "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, - "withdraw_ends" => 1781708875776u64, + "withdraw_starts" => 1781708875776u64, + "withdraw_ends" => 1781708875786u64, "staking_total" => U256::from(500000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash), }; @@ -660,6 +662,7 @@ fn test_approve_and_stake_and_staker_reward() { "staker_reward", staker_reward_args, ) + .with_block_time(1781708875776u64) .build(); builder @@ -876,8 +879,8 @@ fn test_approve_and_stake_and_get_current_reward() { "name" => "FerrumX".to_string(), "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, - "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, + "staking_ends" => 1681708875776u64, + "withdraw_starts" => 1681708875776u64, "withdraw_ends" => 1781708875776u64, "staking_total" => U256::from(500000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash), @@ -1025,10 +1028,10 @@ fn test_approve_and_stake_and_withdraw() { "name" => "FerrumX".to_string(), "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, - "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, - "withdraw_ends" => 1781708875776u64, - "staking_total" => U256::from(500000i64), + "staking_ends" => 1781708875779u64, + "withdraw_starts" => 1781708875779u64, + "withdraw_ends" => 1781708875779u64, + "staking_total" => U256::from(50000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash), }; @@ -1101,6 +1104,7 @@ fn test_approve_and_stake_and_withdraw() { "withdraw", withdraw_args, ) + .with_block_time(1781708875779u64) .build(); builder.exec(withdraw_request).expect_success().commit(); @@ -1290,8 +1294,8 @@ fn test_approve_and_stake_and_add_reward() { "name" => "FerrumX".to_string(), "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, - "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, + "staking_ends" => 1681708875776u64, + "withdraw_starts" => 1681708875776u64, "withdraw_ends" => 1781708875776u64, "staking_total" => U256::from(500000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash), @@ -1424,8 +1428,8 @@ fn test_approve_and_stake_and_add_reward_withdrawable_amount_too_big() { "name" => "FerrumX".to_string(), "address" => "9e7283533626d0c7d43fa9ca745af20d8dac7fc3bfe03cdfe50d523a2a0f498d".to_string(), "staking_starts" => 0u64, - "staking_ends" => 1781708875776u64, - "withdraw_starts" => 0u64, + "staking_ends" => 1681708875776u64, + "withdraw_starts" => 1681708875776u64, "withdraw_ends" => 1781708875776u64, "staking_total" => U256::from(500000i64), "erc20_contract_package_hash" => Key::from(erc20_contract_package_hash),