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
196 changes: 196 additions & 0 deletions src/bwc-staking-contract.cairo
Original file line number Diff line number Diff line change
@@ -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<TContractState> {
// 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::<ContractAddress, StakeDetail>
}


//////////////////
// 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<ContractState> {
// 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;
}
}
}
41 changes: 41 additions & 0 deletions src/bwc_erc20_token.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ trait IERC20<TContractState> {
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;
Expand All @@ -16,6 +17,10 @@ trait IERC20<TContractState> {
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]
Expand All @@ -31,6 +36,7 @@ mod BWCERC20Token {
#[storage]
struct Storage {
name: felt252,
owner: ContractAddress,
symbol: felt252,
decimals: u8,
total_supply: u256,
Expand Down Expand Up @@ -67,6 +73,7 @@ mod BWCERC20Token {
#[constructor]
fn constructor(
ref self: ContractState,
_owner: ContractAddress,
_name: felt252,
_symbol: felt252,
_decimal: u8,
Expand All @@ -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);
Expand All @@ -90,6 +99,7 @@ mod BWCERC20Token {
);
}


#[external(v0)]
impl IERC20Impl of IERC20<ContractState> {
fn get_name(self: @ContractState) -> felt252 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]
Expand Down