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
227 changes: 74 additions & 153 deletions src/bwc_erc20_token.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -33,188 +33,109 @@ mod BWCERC20Token {
name: felt252,
symbol: felt252,
decimals: u8,
total_supply: u256,
balances: LegacyMap<ContractAddress, u256>,
allowances: LegacyMap<
(ContractAddress, ContractAddress), u256
>, //similar to mapping(address => mapping(address => uint256))
totalSupply: u256,
allowances: LegacyMap::<(ContractAddress, ContractAddress), u256>,
balances: LegacyMap::<ContractAddress, u256>
}
// Event

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
Approval: Approval,
Transfer: Transfer
transfer: Transfer,
approval: Approval
}

#[derive(Drop, starknet::Event)]
struct Transfer {
from: ContractAddress,
to: ContractAddress,
sender: ContractAddress,
reciever: ContractAddress,
value: u256
}

#[derive(Drop, starknet::Event)]
struct Approval {
owner: ContractAddress,
spender: ContractAddress,
value: u256,
value: u256
}

// Note: The contract constructor is not part of the interface. Nor are internal functions part of the interface.

// Constructor
// contruct value to insert important perequsite values
#[constructor]
fn constructor(
ref self: ContractState,
_owner: ContractAddress,
_name: felt252,
_symbol: felt252,
_decimal: u8,
_initial_supply: u256,
recipient: ContractAddress
_decimals: u8
) {
// 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');
self.name.write(_name);
self.symbol.write(_symbol);
self.decimals.write(_decimal);
self.total_supply.write(_initial_supply);
self.balances.write(recipient, _initial_supply);

self
.emit(
Transfer {
//Here, `contract_address_const::<0>()` is similar to address(0) in Solidity
from: contract_address_const::<0>(), to: recipient, value: _initial_supply
}
);
self.decimals.write(_decimals);
}

// errors
mod Errors {
const ZERO_ADDRESS_ERROR: felt252 = 'This address is not allowed';
const ZERO_VALUE: felt252 = 'this value is below minimum';
const INSUFFICIENT_BALANCE: felt252 = 'insufficient balance';
const ONLY_OWNER_ERROR: felt252 = 'caller not owner';
}

// fn to get total supply
#[external(v0)]
impl IERC20Impl of IERC20<ContractState> {
fn get_name(self: @ContractState) -> felt252 {
self.name.read()
}
fn get_symbol(self: @ContractState) -> felt252 {
self.symbol.read()
}

fn get_decimals(self: @ContractState) -> u8 {
self.decimals.read()
}

fn get_total_supply(self: @ContractState) -> u256 {
self.total_supply.read()
}


fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {
self.balances.read(account)
}

fn allowance(
self: @ContractState, owner: ContractAddress, spender: ContractAddress
) -> u256 {
self.allowances.read((owner, spender))
}

fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) {
let caller = get_caller_address();
self.transfer_helper(caller, recipient, amount);
}

fn transfer_from(
ref self: ContractState,
sender: ContractAddress,
recipient: ContractAddress,
amount: u256
) {
let caller = get_caller_address();
let my_allowance = self.allowances.read((sender, recipient));
assert(my_allowance <= amount, 'Amount Not Allowed');
self
.spend_allowance(
sender, caller, amount
); //responsible for deduction of the amount allowed to spend
self.transfer_helper(sender, recipient, amount);
}

fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) {
let caller = get_caller_address();
self.approve_helper(caller, spender, amount);
}

fn increase_allowance(
ref self: ContractState, spender: ContractAddress, added_value: u256
) {
let caller = get_caller_address();
self
.approve_helper(
caller, spender, self.allowances.read((caller, spender)) + added_value
);
}

fn decrease_allowance(
ref self: ContractState, spender: ContractAddress, subtracted_value: u256
) {
let caller = get_caller_address();
self
.approve_helper(
caller, spender, self.allowances.read((caller, spender)) - subtracted_value
);
}
fn get_total_supply(self: @ContractState) -> u256 {
self.totalSupply.read()
}

#[generate_trait]
impl HelperImpl of HelperTrait {
fn transfer_helper(
ref self: ContractState,
sender: ContractAddress,
recipient: ContractAddress,
amount: u256
) {
let sender_balance = self.balance_of(sender);

assert(!sender.is_zero(), 'transfer from 0');
assert(!recipient.is_zero(), 'transfer to 0');
assert(sender_balance >= amount, 'Insufficient fund');
self.balances.write(sender, self.balances.read(sender) - amount);
self.balances.write(recipient, self.balances.read(recipient) + amount);
true;

self.emit(Transfer { from: sender, to: recipient, value: amount, });
}

fn approve_helper(
ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256
) {
assert(!owner.is_zero(), 'approve from 0');
assert(!spender.is_zero(), 'approve to 0');

self.allowances.write((owner, spender), amount);

self.emit(Approval { owner, spender, value: amount, })
}

fn spend_allowance(
ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256
) {
// First, read the amount authorized by owner to spender
let current_allowance = self.allowances.read((owner, spender));

// define a variable ONES_MASK of type u128
let ONES_MASK = 0xfffffffffffffffffffffffffffffff_u128;

// to determine whether the authorization is unlimited,

let is_unlimited_allowance = current_allowance.low == ONES_MASK
&& current_allowance
.high == ONES_MASK; //equivalent to type(uint256).max in Solidity.

// This is also a way to save gas, because if the authorized amount is the maximum value of u256, theoretically, this amount cannot be spent.
if !is_unlimited_allowance {
self.approve_helper(owner, spender, current_allowance - amount);
}
}
// fn to get balance of any user using address as key
#[external(v0)]
fn balanceOf(self: @ContractState, user_bal: ContractAddress) -> u256 {
self.balances.read(user_bal)
}

// fn to transfer funds
#[external(v0)]
fn transfer(ref self: ContractState, _to: ContractAddress, _amount: u256) {
let caller = get_caller_address();
let caller_balance = self.balances.read(caller);
let recievers_balance = self.balances.read(_to);

// asserting amount is greatthan zero
assert(_amount > 0, Errors::ZERO_VALUE);
// asserting if caller_balance greater than amount to be sent
assert(caller_balance > _amount, Errors::INSUFFICIENT_BALANCE);
// asserting caller is not address zero
assert(!caller.is_zero(), Errors::ZERO_ADDRESS_ERROR);

// deduct amount from caller
self.balances.write(caller, caller_balance - _amount);
// adding amount to reciever
self.balances.write(_to, recievers_balance + _amount);

// emmit success event
self.emit(Transfer { sender: caller, reciever: _to, value: _amount });
}

// fn transfer from
#[external(v0)]
fn transferFrom(
ref self: ContractState, to: ContractAddress, from: ContractAddress, amount: u256
) {
let caller = get_caller_address();
let sender_balance = self.balances.read(from);
let recievers_balance = self.balances.read(to);

// asserting amount is greatthan zero
assert(amount > 0, Errors::ZERO_VALUE);
// asserting if caller_balance greater than amount to be sent
assert(sender_balance > amount, Errors::INSUFFICIENT_BALANCE);
// asserting caller is not address zero
assert(!caller.is_zero(), Errors::ZERO_ADDRESS_ERROR);

self.balances.write(from, sender_balance - amount);
self.balances.write(to, recievers_balance + amount);

// emmit success event
self.emit(Transfer { sender: from, reciever: to, value: amount });
}
}
Loading