-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
11b50e2
commit 9443a02
Showing
11 changed files
with
422 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,6 @@ | ||
# Code generated by scarb DO NOT EDIT. | ||
version = 1 | ||
|
||
[[package]] | ||
name = "starkcashv1" | ||
version = "0.1.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,13 @@ | ||
[package] | ||
name = "starkcashv1" | ||
version = "0.1.0" | ||
|
||
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html | ||
|
||
[dependencies] | ||
starknet = ">=2.3.0-rc0" | ||
|
||
[[target.starknet-contract]] | ||
# Enable Sierra codegen. | ||
sierra = true | ||
casm = true |
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,316 @@ | ||
// The ERC20 and Ownable components are duplicates of the ones in `with_erc20` and `with_ownable` | ||
// files. The duplication is done since the expand test which also use this file is not aware to | ||
// the rest of the crate. The contract using these components is at the end of the file. | ||
// TODO(Gil): Add the capability to read multiple files in the test framework and remove this | ||
// duplication. | ||
|
||
use starknet::ContractAddress; | ||
|
||
#[starknet::interface] | ||
trait TransferTrait<TContractState> { | ||
fn owner(self: @TContractState) -> ContractAddress; | ||
fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); | ||
} | ||
|
||
#[starknet::component] | ||
mod ownable { | ||
use starknet::ContractAddress; | ||
#[storage] | ||
struct Storage { | ||
owner: ContractAddress, | ||
} | ||
|
||
#[embeddable_as(Transfer)] | ||
impl TransferImpl< | ||
TContractState, impl X: HasComponent<TContractState> | ||
> of super::TransferTrait<ComponentState<TContractState>> { | ||
fn owner(self: @ComponentState<TContractState>) -> ContractAddress { | ||
self.owner.read() | ||
} | ||
|
||
fn transfer_ownership( | ||
ref self: ComponentState<TContractState>, new_owner: ContractAddress | ||
) { | ||
self.validate_ownership(); | ||
self.owner.write(new_owner); | ||
} | ||
} | ||
|
||
#[generate_trait] | ||
impl OwnableHelperImpl< | ||
TContractState, impl X: HasComponent<TContractState> | ||
> of OwnableHelperTrait<TContractState, X> { | ||
fn init_ownable(ref self: ComponentState<TContractState>, owner: ContractAddress) { | ||
self.owner.write(owner); | ||
} | ||
fn validate_ownership(self: @ComponentState<TContractState>) { | ||
assert(self.owner.read() == starknet::get_caller_address(), 'Wrong owner.'); | ||
} | ||
} | ||
} | ||
|
||
#[starknet::interface] | ||
trait ERC20Trait<TCS> { | ||
fn get_name(self: @TCS) -> felt252; | ||
fn get_symbol(self: @TCS) -> felt252; | ||
fn get_decimals(self: @TCS) -> u8; | ||
fn get_total_supply(self: @TCS) -> u256; | ||
fn balance_of(self: @TCS, account: ContractAddress) -> u256; | ||
fn allowance(self: @TCS, owner: ContractAddress, spender: ContractAddress) -> u256; | ||
fn transfer(ref self: TCS, recipient: ContractAddress, amount: u256); | ||
fn transfer_from( | ||
ref self: TCS, sender: ContractAddress, recipient: ContractAddress, amount: u256 | ||
); | ||
fn mint(ref self: TCS, recipient: ContractAddress, amount: u256); | ||
fn approve(ref self: TCS, spender: ContractAddress, amount: u256); | ||
fn increase_allowance(ref self: TCS, spender: ContractAddress, added_value: u256); | ||
fn decrease_allowance(ref self: TCS, spender: ContractAddress, subtracted_value: u256); | ||
} | ||
|
||
#[starknet::component] | ||
mod erc20 { | ||
use starknet::{ContractAddress, get_caller_address, contract_address_const}; | ||
#[storage] | ||
struct Storage { | ||
name: felt252, | ||
symbol: felt252, | ||
decimals: u8, | ||
total_supply: u256, | ||
balances: LegacyMap::<ContractAddress, u256>, | ||
allowances: LegacyMap::<(ContractAddress, ContractAddress), u256>, | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
Transfer: TransferEvent, | ||
Approval: ApprovalEvent, | ||
} | ||
#[derive(Drop, starknet::Event)] | ||
struct TransferEvent { | ||
from: ContractAddress, | ||
to: ContractAddress, | ||
value: u256, | ||
} | ||
#[derive(Drop, starknet::Event)] | ||
struct ApprovalEvent { | ||
owner: ContractAddress, | ||
spender: ContractAddress, | ||
value: u256, | ||
} | ||
|
||
#[embeddable_as(IERC20)] | ||
impl ERC20Impl< | ||
TContractState, +HasComponent<TContractState> | ||
> of super::ERC20Trait<ComponentState<TContractState>> { | ||
fn get_name(self: @ComponentState<TContractState>) -> felt252 { | ||
self.name.read() | ||
} | ||
|
||
fn get_symbol(self: @ComponentState<TContractState>) -> felt252 { | ||
self.symbol.read() | ||
} | ||
|
||
fn get_decimals(self: @ComponentState<TContractState>) -> u8 { | ||
self.decimals.read() | ||
} | ||
|
||
fn get_total_supply(self: @ComponentState<TContractState>) -> u256 { | ||
self.total_supply.read() | ||
} | ||
|
||
fn balance_of(self: @ComponentState<TContractState>, account: ContractAddress) -> u256 { | ||
self.balances.read(account) | ||
} | ||
|
||
fn allowance( | ||
self: @ComponentState<TContractState>, owner: ContractAddress, spender: ContractAddress | ||
) -> u256 { | ||
self.allowances.read((owner, spender)) | ||
} | ||
|
||
fn transfer( | ||
ref self: ComponentState<TContractState>, recipient: ContractAddress, amount: u256 | ||
) { | ||
let sender = get_caller_address(); | ||
self.transfer_helper(sender, recipient, amount); | ||
} | ||
|
||
fn transfer_from( | ||
ref self: ComponentState<TContractState>, | ||
sender: ContractAddress, | ||
recipient: ContractAddress, | ||
amount: u256 | ||
) { | ||
let caller = get_caller_address(); | ||
self.spend_allowance(sender, caller, amount); | ||
self.transfer_helper(sender, recipient, amount); | ||
} | ||
|
||
|
||
fn approve( | ||
ref self: ComponentState<TContractState>, spender: ContractAddress, amount: u256 | ||
) { | ||
let caller = get_caller_address(); | ||
self.approve_helper(caller, spender, amount); | ||
} | ||
|
||
fn increase_allowance( | ||
ref self: ComponentState<TContractState>, spender: ContractAddress, added_value: u256 | ||
) { | ||
let caller = get_caller_address(); | ||
self | ||
.approve_helper( | ||
caller, spender, self.allowances.read((caller, spender)) + added_value | ||
); | ||
} | ||
|
||
fn mint( | ||
ref self: ComponentState<TContractState>, recipient: ContractAddress, amount: u256 | ||
) { | ||
self._mint(recipient, amount); | ||
} | ||
|
||
|
||
fn decrease_allowance( | ||
ref self: ComponentState<TContractState>, | ||
spender: ContractAddress, | ||
subtracted_value: u256 | ||
) { | ||
let caller = get_caller_address(); | ||
self | ||
.approve_helper( | ||
caller, spender, self.allowances.read((caller, spender)) - subtracted_value | ||
); | ||
} | ||
} | ||
|
||
#[generate_trait] | ||
impl ERC20HelperImpl< | ||
TContractState, impl X: HasComponent<TContractState> | ||
> of ERC20HelperTrait<TContractState, X> { | ||
fn transfer_helper( | ||
ref self: ComponentState<TContractState>, | ||
sender: ContractAddress, | ||
recipient: ContractAddress, | ||
amount: u256 | ||
) { | ||
assert(!sender.is_zero(), 'ERC20: transfer from 0'); | ||
assert(!recipient.is_zero(), 'ERC20: transfer to 0'); | ||
self.balances.write(sender, self.balances.read(sender) - amount); | ||
self.balances.write(recipient, self.balances.read(recipient) + amount); | ||
self.emit(TransferEvent { from: sender, to: recipient, value: amount }); | ||
} | ||
|
||
fn spend_allowance( | ||
ref self: ComponentState<TContractState>, | ||
owner: ContractAddress, | ||
spender: ContractAddress, | ||
amount: u256 | ||
) { | ||
let current_allowance: u256 = self.allowances.read((owner, spender)); | ||
let ONES_MASK = 0xffffffffffffffffffffffffffffffff_u128; | ||
let is_unlimited_allowance = current_allowance.low == ONES_MASK | ||
&& current_allowance.high == ONES_MASK; | ||
if !is_unlimited_allowance { | ||
self.approve_helper(owner, spender, current_allowance - amount); | ||
} | ||
} | ||
|
||
fn approve_helper( | ||
ref self: ComponentState<TContractState>, | ||
owner: ContractAddress, | ||
spender: ContractAddress, | ||
amount: u256 | ||
) { | ||
assert(!spender.is_zero(), 'ERC20: approve from 0'); | ||
self.allowances.write((owner, spender), amount); | ||
self.emit(ApprovalEvent { owner, spender, value: amount }); | ||
} | ||
fn init( | ||
ref self: ComponentState<TContractState>, | ||
name: felt252, | ||
symbol: felt252, | ||
decimals: u8, | ||
initial_supply: u256, | ||
recipient: ContractAddress | ||
) { | ||
self.name.write(name); | ||
self.symbol.write(symbol); | ||
self.decimals.write(decimals); | ||
assert(!recipient.is_zero(), 'ERC20: mint to the 0 address'); | ||
self.total_supply.write(initial_supply); | ||
self.balances.write(recipient, initial_supply); | ||
self | ||
.emit( | ||
Event::Transfer( | ||
TransferEvent { | ||
from: contract_address_const::<0>(), | ||
to: recipient, | ||
value: initial_supply | ||
} | ||
) | ||
); | ||
} | ||
fn _mint( | ||
ref self: ComponentState<TContractState>, recipient: ContractAddress, amount: u256 | ||
) { | ||
assert(!recipient.is_zero(), 'Errors::MINT_TO_ZERO'); | ||
self.total_supply.write(self.total_supply.read() + amount); | ||
self.balances.write(recipient, self.balances.read(recipient) + amount); | ||
self.emit(TransferEvent { from: Zeroable::zero(), to: recipient, value: amount }); | ||
} | ||
} | ||
} | ||
|
||
// End of copied components. | ||
|
||
#[starknet::contract] | ||
mod erc20_contract { | ||
use starknet::ContractAddress; | ||
#[storage] | ||
struct Storage { | ||
#[substorage(v0)] | ||
ownable_storage: super::ownable::Storage, | ||
#[substorage(v0)] | ||
erc20_storage: super::erc20::Storage, | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
ERC20: super::erc20::Event, | ||
Ownable: super::ownable::Event, | ||
} | ||
|
||
component!(path: super::erc20, storage: erc20_storage, event: ERC20); | ||
component!(path: super::ownable, storage: ownable_storage, event: Ownable); | ||
|
||
#[abi(embed_v0)] | ||
impl ERC20Impl = super::erc20::IERC20<ContractState>; | ||
#[abi(embed_v0)] | ||
impl OwnershipTransfer = super::ownable::Transfer<ContractState>; | ||
|
||
|
||
impl ERC20HelperImpl = super::erc20::ERC20HelperImpl<ContractState>; | ||
impl OwnershipHelper = super::ownable::OwnableHelperImpl<ContractState>; | ||
|
||
|
||
#[abi(per_item)] | ||
#[generate_trait] | ||
impl OwnableERC20Impl of OwnableERC20 { | ||
#[constructor] | ||
fn constructor( | ||
ref self: ContractState, | ||
name: felt252, | ||
symbol: felt252, | ||
decimals: u8, | ||
// initial_supply: u256, | ||
recipient: ContractAddress, | ||
owner: ContractAddress, | ||
) { | ||
self.erc20_storage.init(name, symbol, decimals, 10000_u256, recipient); | ||
self.ownable_storage.init_ownable(owner); | ||
} | ||
} | ||
} |
Oops, something went wrong.