Skip to content

Commit

Permalink
added erc20 burnable preset
Browse files Browse the repository at this point in the history
  • Loading branch information
koloz193 committed May 3, 2022
1 parent 6b23ef9 commit 80a904c
Show file tree
Hide file tree
Showing 2 changed files with 281 additions and 8 deletions.
212 changes: 212 additions & 0 deletions src/openzeppelin/token/erc20/ERC20_Burnable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# SPDX-License-Identifier: MIT
# OpenZeppelin Cairo Contracts v0.1.0 (token/erc20/ERC20_Burnable.cairo)

%lang starknet

from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.uint256 import Uint256
from starkware.starknet.common.syscalls import get_caller_address
from starkware.cairo.common.bool import TRUE

from openzeppelin.token.erc20.library import ERC20

from openzeppelin.access.ownable import (
Ownable_initializer,
Ownable_owner,
Ownable_transfer_ownership
)

from openzeppelin.security.safemath import uint256_checked_sub_le

from openzeppelin.introspection.ERC165 import ERC165_supports_interface


@constructor
func constructor{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(
name : felt,
symbol : felt,
decimals : felt,
initial_supply : Uint256,
recipient : felt,
owner : felt
):
ERC20.constructor(name, symbol, decimals)
ERC20._mint(recipient, initial_supply)
Ownable_initializer(owner)
return ()
end

#
# Getters
#

@view
func name{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (name : felt):
let (name) = ERC20.name()
return (name)
end

@view
func symbol{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (symbol : felt):
let (symbol) = ERC20.symbol()
return (symbol)
end

@view
func totalSupply{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (totalSupply : Uint256):
let (totalSupply : Uint256) = ERC20.total_supply()
return (totalSupply)
end

@view
func decimals{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (decimals : felt):
let (decimals) = ERC20.decimals()
return (decimals)
end

@view
func balanceOf{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(account : felt) -> (balance : Uint256):
let (balance : Uint256) = ERC20.balance_of(account)
return (balance)
end

@view
func allowance{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(owner : felt, spender : felt) -> (remaining : Uint256):
let (remaining : Uint256) = ERC20.allowance(owner, spender)
return (remaining)
end

@external
func transfer{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(recipient : felt, amount : Uint256) -> (success : felt):
ERC20.transfer(recipient, amount)
return (TRUE)
end

@external
func transferFrom{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(
sender : felt,
recipient : felt,
amount : Uint256
) -> (success : felt):
ERC20.transfer_from(sender, recipient, amount)
return (TRUE)
end

@external
func approve{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(spender : felt, amount : Uint256) -> (success : felt):
ERC20.approve(spender, amount)
return (TRUE)
end

@external
func increaseAllowance{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(spender : felt, added_value : Uint256) -> (success : felt):
ERC20.increase_allowance(spender, added_value)
return (TRUE)
end

@external
func decreaseAllowance{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(spender : felt, subtracted_value : Uint256) -> (success : felt):
ERC20.decrease_allowance(spender, subtracted_value)
return (TRUE)
end

@external
func burn{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(amount : Uint256):
let (caller) = get_caller_address()
ERC20._burn(caller, amount)
return ()
end

@external
func burnFrom{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(account : felt, amount : Uint256):
let (caller) = get_caller_address()
ERC20._spend_allowance(account, caller, amount)
ERC20._burn(account, amount)
return ()
end

@view
func owner{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}() -> (owner : felt):
let (owner) = Ownable_owner.read()
return (owner=owner)
end

@external
func transferOwnership{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}(new_owner : felt):
Ownable_transfer_ownership(new_owner)
return ()
end

@external
func renounceOwnership{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr
}():
Ownable_transfer_ownership(0)
return ()
end
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
def contract_defs():
account_def = get_contract_def('openzeppelin/account/Account.cairo')
erc20_def = get_contract_def(
'tests/mocks/ERC20_Burnable_mock.cairo')
'openzeppelin/token/erc20/ERC20_Burnable.cairo')

return account_def, erc20_def

Expand All @@ -37,6 +37,10 @@ async def erc20_init(contract_defs):
contract_def=account_def,
constructor_calldata=[signer.public_key]
)
account2 = await starknet.deploy(
contract_def=account_def,
constructor_calldata=[signer.public_key]
)
erc20 = await starknet.deploy(
contract_def=erc20_def,
constructor_calldata=[
Expand All @@ -45,29 +49,32 @@ async def erc20_init(contract_defs):
DECIMALS,
*INIT_SUPPLY,
account1.contract_address, # recipient
account1.contract_address, # owner
]
)
return (
starknet.state,
account1,
account2,
erc20
)


@pytest.fixture
def erc20_factory(contract_defs, erc20_init):
account_def, erc20_def = contract_defs
state, account1, erc20 = erc20_init
state, account1, account2, erc20 = erc20_init
_state = state.copy()
account1 = cached_contract(_state, account_def, account1)
account2 = cached_contract(_state, account_def, account2)
erc20 = cached_contract(_state, erc20_def, erc20)

return erc20, account1
return erc20, account1, account2


@pytest.mark.asyncio
async def test_burn(erc20_factory):
erc20, account = erc20_factory
erc20, account, _ = erc20_factory

await signer.send_transaction(
account, erc20.contract_address, 'burn', [
Expand All @@ -82,7 +89,7 @@ async def test_burn(erc20_factory):

@pytest.mark.asyncio
async def test_burn_emits_event(erc20_factory):
erc20, account = erc20_factory
erc20, account, _ = erc20_factory

tx_exec_info = await signer.send_transaction(
account, erc20.contract_address, 'burn', [
Expand All @@ -103,7 +110,7 @@ async def test_burn_emits_event(erc20_factory):

@pytest.mark.asyncio
async def test_burn_not_enough_balance(erc20_factory):
erc20, account = erc20_factory
erc20, account, _ = erc20_factory

balance_plus_one = add_uint(INIT_SUPPLY, UINT_ONE)

Expand All @@ -117,7 +124,7 @@ async def test_burn_not_enough_balance(erc20_factory):

@pytest.mark.asyncio
async def test_burn_from_zero_address(erc20_factory):
erc20, _ = erc20_factory
erc20, _, _ = erc20_factory

await assert_revert(
erc20.burn(UINT_ONE).invoke(),
Expand All @@ -127,9 +134,63 @@ async def test_burn_from_zero_address(erc20_factory):

@pytest.mark.asyncio
async def test_burn_invalid_uint256(erc20_factory):
erc20, _ = erc20_factory
erc20, _, _ = erc20_factory

await assert_revert(
erc20.burn(INVALID_UINT256).invoke(),
reverted_with="ERC20: amount is not a valid Uint256"
)


@pytest.mark.asyncio
async def test_burn_from(erc20_factory):
erc20, account1, account2 = erc20_factory

await signer.send_transaction(
account1, erc20.contract_address, 'increaseAllowance', [
account2.contract_address,
*AMOUNT
])

await signer.send_transaction(
account2, erc20.contract_address, 'burnFrom', [
account1.contract_address,
*AMOUNT
])

new_balance = sub_uint(INIT_SUPPLY, AMOUNT)

execution_info = await erc20.balanceOf(account1.contract_address).invoke()
assert execution_info.result.balance == new_balance


@pytest.mark.asyncio
async def test_burn_from_over_allowance(erc20_factory):
erc20, account1, account2 = erc20_factory

await signer.send_transaction(
account1, erc20.contract_address, 'increaseAllowance', [
account2.contract_address,
*AMOUNT
])

await assert_revert(signer.send_transaction(
account2, erc20.contract_address, 'burnFrom', [
account1.contract_address,
*INIT_SUPPLY
]),
reverted_with="ERC20: insufficient allowance"
)


@pytest.mark.asyncio
async def test_burn_from_no_allowance(erc20_factory):
erc20, account1, account2 = erc20_factory

await assert_revert(signer.send_transaction(
account2, erc20.contract_address, 'burnFrom', [
account1.contract_address,
*AMOUNT
]),
reverted_with="ERC20: insufficient allowance"
)

0 comments on commit 80a904c

Please sign in to comment.