Skip to content

Commit

Permalink
Merge pull request #26 from keep-starknet-strange/refactor/components
Browse files Browse the repository at this point in the history
refactor: use ERC20 component
  • Loading branch information
AbdelStark authored Dec 13, 2023
2 parents 7838f10 + c01158a commit e4153cc
Show file tree
Hide file tree
Showing 6 changed files with 425 additions and 177 deletions.
2 changes: 2 additions & 0 deletions contracts/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scarb 2.3.1
starknet-foundry 0.12.0
3 changes: 3 additions & 0 deletions contracts/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ allowed-libfuncs-list.name = "experimental"

[cairo]
sierra-replace-ids = true

[tool.fmt]
sort-module-level-items = true
1 change: 1 addition & 0 deletions contracts/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod tokens {
mod interface;
mod memecoin;
}
65 changes: 65 additions & 0 deletions contracts/src/tokens/interface.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use openzeppelin::token::erc20::interface::{IERC20Metadata, IERC20, IERC20Camel};
use starknet::ContractAddress;


#[starknet::interface]
trait IUnruggableMemecoin<TState> {
// ************************************
// * Metadata
// ************************************
fn name(self: @TState) -> felt252;
fn symbol(self: @TState) -> felt252;
fn decimals(self: @TState) -> u8;

// ************************************
// * snake_case
// ************************************
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;

// ************************************
// * camelCase
// ************************************
fn totalSupply(self: @TState) -> u256;
fn balanceOf(self: @TState, account: ContractAddress) -> u256;
fn transferFrom(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;

// ************************************
// * Additional functions
// ************************************
fn launch_memecoin(ref self: TState);
}

#[starknet::interface]
trait IUnruggableMemecoinCamel<TState> {
fn totalSupply(self: @TState) -> u256;
fn balanceOf(self: @TState, account: ContractAddress) -> u256;
fn transferFrom(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
}

#[starknet::interface]
trait IUnruggableMemecoinSnake<TState> {
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}

#[starknet::interface]
trait IUnruggableAdditional<TState> {
fn launch_memecoin(ref self: TState);
}
214 changes: 53 additions & 161 deletions contracts/src/tokens/memecoin.cairo
Original file line number Diff line number Diff line change
@@ -1,48 +1,32 @@
//! `UnruggableMemecoin` is an ERC20 token has additional features to prevent rug pulls.
use starknet::ContractAddress;

#[starknet::interface]
trait IUnruggableMemecoin<TState> {
// ************************************
// * Standard ERC20 functions
// ************************************
fn name(self: @TState) -> felt252;
fn symbol(self: @TState) -> felt252;
fn decimals(self: @TState) -> u8;
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
// ************************************
// * Additional functions
// ************************************
fn launch_memecoin(ref self: TState);
}

#[starknet::contract]
mod UnruggableMemecoin {
// Core dependencies.
use openzeppelin::access::ownable::ownable::OwnableComponent::InternalTrait;
use integer::BoundedInt;
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::access::ownable::ownable::OwnableComponent::InternalTrait;
use openzeppelin::token::erc20::ERC20Component;
use starknet::{ContractAddress, get_caller_address};
use unruggable::tokens::interface::{
IUnruggableMemecoinSnake, IUnruggableMemecoinCamel, IUnruggableAdditional
};
use zeroable::Zeroable;

// External dependencies.
use openzeppelin::access::ownable::OwnableComponent;

// Internal dependencies.
use super::IUnruggableMemecoin;

// Components.
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

component!(path: ERC20Component, storage: erc20, event: ERC20Event);
// Internals
impl ERC20InternalImpl = ERC20Component::InternalImpl<ContractState>;

// ERC20 entrypoints.
#[abi(embed_v0)]
impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl<ContractState>;

// Constants.
const DECIMALS: u8 = 18;
/// The maximum number of holders allowed before launch.
/// This is to prevent the contract from being launched with a large number of holders.
/// Once reached, transfers are disabled until the memecoin is launched.
Expand All @@ -56,38 +40,22 @@ mod UnruggableMemecoin {
#[storage]
struct Storage {
marker_v_0: (),
name: felt252,
symbol: felt252,
total_supply: u256,
balances: LegacyMap<ContractAddress, u256>,
allowances: LegacyMap<(ContractAddress, ContractAddress), u256>,
// Components.
#[substorage(v0)]
ownable: OwnableComponent::Storage
ownable: OwnableComponent::Storage,
#[substorage(v0)]
erc20: ERC20Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
Transfer: Transfer,
Approval: Approval,
#[flat]
OwnableEvent: OwnableComponent::Event
OwnableEvent: OwnableComponent::Event,
#[flat]
ERC20Event: ERC20Component::Event
}

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

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

/// Constructor called once when the contract is deployed.
/// # Arguments
Expand All @@ -106,20 +74,20 @@ mod UnruggableMemecoin {
initial_supply: u256
) {
// Initialize the ERC20 token.
self.initializer(name, symbol);
self.erc20.initializer(name, symbol);

// Initialize the owner.
self.ownable.initializer(owner);

// Mint initial supply to the initial recipient.
self._mint(initial_recipient, initial_supply);
self.erc20._mint(initial_recipient, initial_supply);
}

//
// External
//
#[abi(embed_v0)]
impl UnruggableMemecoinImpl of IUnruggableMemecoin<ContractState> {
impl UnruggableEntrypoints of IUnruggableAdditional<ContractState> {
// ************************************
// * UnruggableMemecoin functions
// ************************************
Expand All @@ -130,34 +98,25 @@ mod UnruggableMemecoin {

// Interactions.
}
}

#[abi(embed_v0)]
impl SnakeEntrypoints of IUnruggableMemecoinSnake<ContractState> {
// ************************************
// * Standard ERC20 functions
// * snake_case functions
// ************************************
fn name(self: @ContractState) -> felt252 {
self.name.read()
}

fn symbol(self: @ContractState) -> felt252 {
self.symbol.read()
}

fn decimals(self: @ContractState) -> u8 {
DECIMALS
}

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

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

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

fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {
Expand All @@ -173,119 +132,52 @@ mod UnruggableMemecoin {
amount: u256
) -> bool {
let caller = get_caller_address();
self._spend_allowance(sender, caller, amount);
self._transfer(sender, recipient, amount);
self.erc20._spend_allowance(sender, caller, amount);
self.erc20._transfer(sender, recipient, amount);
true
}

fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {
let caller = get_caller_address();
self._approve(caller, spender, amount);
self.erc20._approve(caller, spender, amount);
true
}
}

#[external(v0)]
fn increase_allowance(
ref self: ContractState, spender: ContractAddress, added_value: u256
) -> bool {
self._increase_allowance(spender, added_value)
}

#[external(v0)]
fn increaseAllowance(
ref self: ContractState, spender: ContractAddress, addedValue: u256
) -> bool {
increase_allowance(ref self, spender, addedValue)
}

#[external(v0)]
fn decrease_allowance(
ref self: ContractState, spender: ContractAddress, subtracted_value: u256
) -> bool {
self._decrease_allowance(spender, subtracted_value)
}

#[external(v0)]
fn decreaseAllowance(
ref self: ContractState, spender: ContractAddress, subtractedValue: u256
) -> bool {
decrease_allowance(ref self, spender, subtractedValue)
}

//
// Internal
//

#[generate_trait]
impl UnruggableMemecoinInternalImpl of UnruggableMemecoinInternalTrait {
fn initializer(ref self: ContractState, name_: felt252, symbol_: felt252) {
self.name.write(name_);
self.symbol.write(symbol_);
#[abi(embed_v0)]
impl CamelEntrypoints of IUnruggableMemecoinCamel<ContractState> {
fn totalSupply(self: @ContractState) -> u256 {
self.erc20.ERC20_total_supply.read()
}

fn _increase_allowance(
ref self: ContractState, spender: ContractAddress, added_value: u256
) -> bool {
let caller = get_caller_address();
self._approve(caller, spender, self.allowances.read((caller, spender)) + added_value);
true
fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {
self.erc20.ERC20_balances.read(account)
}

fn _decrease_allowance(
ref self: ContractState, spender: ContractAddress, subtracted_value: u256
fn transferFrom(
ref self: ContractState,
sender: ContractAddress,
recipient: ContractAddress,
amount: u256
) -> bool {
let caller = get_caller_address();
self
._approve(
caller, spender, self.allowances.read((caller, spender)) - subtracted_value
);
self.erc20._spend_allowance(sender, caller, amount);
self._transfer(sender, recipient, amount);
true
}
}

fn _mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {
assert(!recipient.is_zero(), 'ERC20: mint to 0');
self.total_supply.write(self.total_supply.read() + amount);
self.balances.write(recipient, self.balances.read(recipient) + amount);
self.emit(Transfer { from: Zeroable::zero(), to: recipient, value: amount });
}

fn _burn(ref self: ContractState, account: ContractAddress, amount: u256) {
assert(!account.is_zero(), 'ERC20: burn from 0');
self.total_supply.write(self.total_supply.read() - amount);
self.balances.write(account, self.balances.read(account) - amount);
self.emit(Transfer { from: account, to: Zeroable::zero(), value: amount });
}

fn _approve(
ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256
) {
assert(!owner.is_zero(), 'ERC20: approve from 0');
assert(!spender.is_zero(), 'ERC20: approve to 0');
self.allowances.write((owner, spender), amount);
self.emit(Approval { owner, spender, value: amount });
}

//
// Internal
//
#[generate_trait]
impl UnruggableMemecoinInternalImpl of UnruggableMemecoinInternalTrait {
fn _transfer(
ref self: ContractState,
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(Transfer { from: sender, to: recipient, value: amount });
}

fn _spend_allowance(
ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256
) {
let current_allowance = self.allowances.read((owner, spender));
if current_allowance != BoundedInt::max() {
self._approve(owner, spender, current_allowance - amount);
}
self.erc20._transfer(sender, recipient, amount);
}
}
}
Loading

0 comments on commit e4153cc

Please sign in to comment.