From 976fd7e12acb4cb5de439aa2181e7086bb6e1d38 Mon Sep 17 00:00:00 2001 From: estheroche Date: Sun, 14 Jan 2024 05:41:53 -0600 Subject: [PATCH 1/2] feat: add mint and burn function --- src/bwc_erc20_token.cairo | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/bwc_erc20_token.cairo b/src/bwc_erc20_token.cairo index 17ef726..cb03779 100644 --- a/src/bwc_erc20_token.cairo +++ b/src/bwc_erc20_token.cairo @@ -4,6 +4,7 @@ trait IERC20 { fn get_name(self: @TContractState) -> felt252; fn get_symbol(self: @TContractState) -> felt252; fn get_decimals(self: @TContractState) -> u8; + //fn totalSupply(ref self: TContractState, to: ContractAddress, amount: u256); fn get_total_supply(self: @TContractState) -> u256; fn balance_of(self: @TContractState, account: ContractAddress) -> u256; fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; @@ -16,6 +17,10 @@ trait IERC20 { fn decrease_allowance( ref self: TContractState, spender: ContractAddress, subtracted_value: u256 ); + + fn mint(ref self: TContractState, recipient: ContractAddress, amount: u256) -> bool; + + fn burn(ref self: TContractState, to: ContractAddress, amount: u256) -> bool; } #[starknet::contract] @@ -31,6 +36,7 @@ mod BWCERC20Token { #[storage] struct Storage { name: felt252, + owner: ContractAddress, symbol: felt252, decimals: u8, total_supply: u256, @@ -67,6 +73,7 @@ mod BWCERC20Token { #[constructor] fn constructor( ref self: ContractState, + _owner: ContractAddress, _name: felt252, _symbol: felt252, _decimal: u8, @@ -75,6 +82,8 @@ mod BWCERC20Token { ) { // The .is_zero() method here is used to determine whether the address type recipient is a 0 address, similar to recipient == address(0) in Solidity. assert(!recipient.is_zero(), 'transfer to zero address'); + assert(!_owner.is_zero(), 'owner cant be zero addr'); + self.owner.write(_owner); self.name.write(_name); self.symbol.write(_symbol); self.decimals.write(_decimal); @@ -90,6 +99,7 @@ mod BWCERC20Token { ); } + #[external(v0)] impl IERC20Impl of IERC20 { fn get_name(self: @ContractState) -> felt252 { @@ -118,6 +128,21 @@ mod BWCERC20Token { self.allowances.read((owner, spender)) } + + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool { + let owner = self.owner.read(); + let caller = get_caller_address(); + assert(owner == caller, 'caller not owner'); + assert(!recipient.is_zero(), 'ERC20: Adddress zero'); + assert(self.balances.read(recipient) >= amount, 'Insufficient fund'); + self.balances.write(recipient, self.balances.read(recipient) + amount); + self.total_supply.write(self.total_supply.read() - amount); + // call tranfer + // Transfer(Zeroable::zero(), recipient, amount); + + true + } + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) { let caller = get_caller_address(); self.transfer_helper(caller, recipient, amount); @@ -163,6 +188,22 @@ mod BWCERC20Token { caller, spender, self.allowances.read((caller, spender)) - subtracted_value ); } + + fn burn(ref self: ContractState, to: ContractAddress, amount: u256) -> bool { + let owner = self.owner.read(); + let msg_sender = get_caller_address(); + assert(owner == msg_sender, 'msg_sender not owner'); + + assert(self.balances.read(to) >= amount, 'Insufficient fund'); + + self.balances.write(msg_sender, self.balances.read(msg_sender) - amount); + + // call transfer + + // Transfer(Zeroable::zero(), to, amount); + + true + } } #[generate_trait] From 099a1b5b1a941b7f63b235333c720789c1945167 Mon Sep 17 00:00:00 2001 From: estheroche Date: Sun, 14 Jan 2024 06:33:17 -0600 Subject: [PATCH 2/2] feat: add staking contract --- src/bwc-staking-contract.cairo | 196 +++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/bwc-staking-contract.cairo diff --git a/src/bwc-staking-contract.cairo b/src/bwc-staking-contract.cairo new file mode 100644 index 0000000..98a603b --- /dev/null +++ b/src/bwc-staking-contract.cairo @@ -0,0 +1,196 @@ +use starknet::ContractAddress; +use bwc_erc20_token::bwc_staking_contract; + +// #[derive(Drop,storage_access::StorageAccess)] +// struct StakeDetail { +// timeStaked: u64, +// amount: u256, +// status:bool, +// } + +#[starknet::interface] +trait StakingTokenTrait { + // fn depositBWC(ref self: TContractState, amount: u256) -> bool; + fn stakeBWCToken(ref self: TContractState, amount: u256, BWCERC20TokenAddr: ContractAddress); + fn withdrawBWCToken(ref self: TContractState, amount: u256, BWCERC20TokenAddr: ContractAddress); + fn getUserBalance(self: @TContractState) -> u256; +// fn getStakeDetailsByAddress(self: @ContractState, account:ContractAddress) -> StakeDetail; +} + +#[starknet::contract] +mod BWCStakingContract { + ///////////////////////////// + //LIBRARY IMPORTS + ///////////////////////////// + + use starknet::ContractAddress; + use starknet::{get_caller_address, get_contract_address, get_block_timestamp}; + use new_syntax::interfaces::IBWCERC20TokenDispatcherTrait; + use new_syntax::interfaces::IBWCERC20TokenDispatcher; + use integer::Into; + use core::serde::Serde; + use core::integer::u64; + + ///////////////////// + //STAKING DETAIL + ///////////////////// + // #[derive(Drop)] + #[derive(Drop, storage_access::StorageAccess)] + struct StakeDetail { + timeStaked: u64, + amount: u256, + status: bool, + } + + //////////////////// + //STORAGE + //////////////////// + #[storage] + struct Storage { + staker: LegacyMap:: + } + + + ////////////////// + // CONSTANT + ////////////////// + const minStakeTime: u64 = 259200_u64; + + + ///////////////// + //EVENTS + ///////////////// + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + TokenStaked: TokenStaked, + TokenWithdraw: TokenWithdraw + } + + #[derive(Drop, starknet::Event)] + struct TokenStaked { + staker: ContractAddress, + amount: u256, + time: u64 + } + + #[derive(Drop, starknet::Event)] + struct TokenWithdraw { + staker: ContractAddress, + amount: u256, + time: u64 + } + + + #[external(v0)] + impl StakingTokenTraitImpl of super::StakingTokenTrait { + // fn depositBWC(ref self: ContractState, amount: u256) -> bool { + // IBWCERC20TokenDispatcher { contract_address: BWCERC20TokenAddr } + // .transfer_from( + // contract_address = reward_token_address, + // sender = caller_address, + // recipient = contract_address, + // amount: u256 + // ) + // } + fn stakeBWCToken( + ref self: ContractState, amount: u256, BWCERC20TokenAddr: ContractAddress + ) { + let caller: ContractAddress = get_caller_address(); + let address_this = get_contract_address(); + assert( + (IBWCERC20TokenDispatcher { contract_address: BWCERC20TokenAddr } + .get_balance_of(caller) >= amount), + 'BWCERC20Token:Insufficient Balance' + ); + IBWCERC20TokenDispatcher { contract_address: BWCERC20TokenAddr } + .transfer_from(caller, address_this, amount); + let stake_status: bool = self.staker.read(caller).status; + let stake_time: u64 = self.staker.read(caller).timeStaked; + let mut stake: StakeDetail = self.staker.read(caller); + if stake_status == true { + let day_spent = get_block_timestamp() - stake_time; + if day_spent > minStakeTime { + let reward = self.calculateReward(caller); + stake.amount += reward; + stake.amount -= amount; + stake.timeStaked += get_block_timestamp(); + } else { + stake.amount -= amount; + stake.timeStaked = get_block_timestamp(); + } + IBWCERC20TokenDispatcher { contract_address: BWCERC20TokenAddr } + .transfer(caller, amount); + stake.timeStaked = get_block_timestamp(); + + if stake.amount > 0 { + stake.status = true; + } else { + stake.status = false; + } + } + self.emit(Event::TokenStaked(TokenStaked { staker: caller, amount, time: stake_time })); + } + + fn withdrawBWCToken( + ref self: ContractState, amount: u256, BWCERC20TokenAddr: ContractAddress + ) { + let caller = get_caller_address(); + let stake_amount = self.staker.read(caller).amount; + let stake_time = self.staker.read(caller).timeStaked; + let day_spent = get_block_timestamp() - stake_time; + let mut stake: StakeDetail = self.staker.read(caller); + assert(amount <= stake_amount, 'Insufficient fund'); + if day_spent > minStakeTime { + let reward = self.calculateReward(caller); + stake.amount += reward; + stake.amount -= amount; + stake.timeStaked = get_block_timestamp(); + } else { + stake.amount = stake.amount - amount; + stake.timeStaked = get_block_timestamp(); + } + IBWCERC20TokenDispatcher { contract_address: BWCERC20TokenAddr } + .transfer(caller, amount); + stake.timeStaked = get_block_timestamp(); + + if stake.amount > 0 { + stake.status = true; + } else { + stake.status = false; + } + self + .emit( + Event::TokenWithdraw(TokenWithdraw { staker: caller, amount, time: stake_time }) + ); + } + + fn getUserBalance(self: @ContractState) -> u256 { + let caller: ContractAddress = get_caller_address(); + return self.staker.read(caller).amount; + } + // getStakeDetailsByAddress + // fn getStakeDetailsByAddress(self: @ContractState, account:ContractAddress) ->super::StakeDetail{ + // return self.staker.read(account); + // } + } + + + #[generate_trait] + impl calculateRewardTrait of calculateReward { + fn calculateReward(self: ContractState, account: ContractAddress) -> u256 { + let caller = get_caller_address(); + let stake_status: bool = self.staker.read(caller).status; + let stake_amount = self.staker.read(caller).amount; + let stake_time: u64 = self.staker.read(caller).timeStaked; + if stake_status == false { + return 0; + } + let reward_per_month = (stake_amount * 10); + let time = get_block_timestamp() - stake_time; + let reward = (reward_per_month * time.into() * 1000) / minStakeTime.into(); + return reward; + } + } +}