Skip to content

Commit

Permalink
Remove increase / decrease and MAX allowance from ERC20
Browse files Browse the repository at this point in the history
  • Loading branch information
tarrencev committed Feb 14, 2024
1 parent 64c8306 commit c0bfc28
Show file tree
Hide file tree
Showing 9 changed files with 8 additions and 643 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ mod erc20_allowance_mock {
#[abi(embed_v0)]
impl ERC20AllowanceImpl =
erc20_allowance_component::ERC20AllowanceImpl<ContractState>;
#[abi(embed_v0)]
impl ERC20SafeAllowanceImpl =
erc20_allowance_component::ERC20SafeAllowanceImpl<ContractState>;
#[abi(embed_v0)]
impl ERC20SafeAllowanceCamelImpl =
erc20_allowance_component::ERC20SafeAllowanceCamelImpl<ContractState>;

impl ERC20AllowanceInternalImpl = erc20_allowance_component::InternalImpl<ContractState>;

Expand Down
195 changes: 1 addition & 194 deletions token/src/components/tests/token/erc20/test_erc20_allowance.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use token::components::token::erc20::erc20_allowance::{
};
use token::components::token::erc20::erc20_allowance::erc20_allowance_component;
use token::components::token::erc20::erc20_allowance::erc20_allowance_component::{
Approval, ERC20AllowanceImpl, ERC20SafeAllowanceImpl, ERC20SafeAllowanceCamelImpl, InternalImpl
Approval, ERC20AllowanceImpl, InternalImpl
};
use token::components::tests::mocks::erc20::erc20_allowance_mock::erc20_allowance_mock;
use token::components::tests::mocks::erc20::erc20_allowance_mock::erc20_allowance_mock::world_dispatcherContractMemberStateTrait;
Expand Down Expand Up @@ -91,34 +91,6 @@ fn test_erc20_allowance_approve_to_zero() {
state.erc20_allowance.approve(ZERO(), VALUE);
}


//
// update_allowance (increase_allowance, decrease_allowance)
//

#[test]
#[available_gas(100000000)]
fn test_erc20_allowance_update_allowance() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc20_allowance.approve(SPENDER(), VALUE);
utils::drop_event(ZERO());

state.erc20_allowance.update_allowance(OWNER(), SPENDER(), 0, SUPPLY);
assert(
state.erc20_allowance.allowance(OWNER(), SPENDER()) == VALUE + SUPPLY,
'should be VALUE+SUPPLY'
);
assert_only_event_approval(ZERO(), OWNER(), SPENDER(), VALUE + SUPPLY);

state.erc20_allowance.update_allowance(OWNER(), SPENDER(), VALUE, 0);
assert(state.erc20_allowance.allowance(OWNER(), SPENDER()) == SUPPLY, 'should be SUPPLY');
assert_only_event_approval(ZERO(), OWNER(), SPENDER(), SUPPLY);
}


//
// spend_allowance
//
Expand All @@ -140,168 +112,3 @@ fn test_erc20_allowance_spend_allowance() {
);
assert_only_event_approval(ZERO(), OWNER(), SPENDER(), SUPPLY - VALUE);
}

#[test]
#[available_gas(100000000)]
fn test_erc20_allowance_spend_allowance_with_max_allowance() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc20_allowance.approve(SPENDER(), BoundedInt::max());
utils::drop_event(ZERO());

state.erc20_allowance.spend_allowance(OWNER(), SPENDER(), VALUE);
assert(
state.erc20_allowance.allowance(OWNER(), SPENDER()) == BoundedInt::max(),
'should be BoundedInt::max()'
);

utils::assert_no_events_left(ZERO());
}


//
// increase_allowance & increaseAllowance
//

#[test]
#[available_gas(25000000)]
fn test_erc20_allowance_increase_allowance() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.approve(SPENDER(), VALUE);
utils::drop_event(ZERO());

assert(state.erc20_allowance.increase_allowance(SPENDER(), VALUE), 'Should return true');

assert_only_event_approval(ZERO(), OWNER(), SPENDER(), VALUE * 2);
assert(
state.erc20_allowance.allowance(OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'
);
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('ERC20: approve to 0',))]
fn test_erc20_allowance_increase_allowance_to_zero_address() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.increase_allowance(ZERO(), VALUE);
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('ERC20: approve from 0',))]
fn test_erc20_allowance_increase_allowance_from_zero_address() {
let (world, mut state) = STATE();
state.erc20_allowance.increase_allowance(SPENDER(), VALUE);
}

#[test]
#[available_gas(25000000)]
fn test_erc20_allowance_increaseAllowance() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.approve(SPENDER(), VALUE);
utils::drop_event(ZERO());

assert(state.erc20_allowance.increaseAllowance(SPENDER(), VALUE), 'Should return true');

assert_only_event_approval(ZERO(), OWNER(), SPENDER(), 2 * VALUE);
assert(
state.erc20_allowance.allowance(OWNER(), SPENDER()) == VALUE * 2, 'Should be amount * 2'
);
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('ERC20: approve to 0',))]
fn test_erc20_allowance_increaseAllowance_to_zero_address() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.increaseAllowance(ZERO(), VALUE);
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('ERC20: approve from 0',))]
fn test_erc20_allowance_increaseAllowance_from_zero_address() {
let (world, mut state) = STATE();
state.erc20_allowance.increaseAllowance(SPENDER(), VALUE);
}

//
// decrease_allowance & decreaseAllowance
//

#[test]
#[available_gas(25000000)]
fn test_erc20_allowance_decrease_allowance() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.approve(SPENDER(), VALUE);
utils::drop_event(ZERO());

assert(state.erc20_allowance.decrease_allowance(SPENDER(), VALUE), 'Should return true');

assert_only_event_approval(ZERO(), OWNER(), SPENDER(), 0);
assert(state.erc20_allowance.allowance(OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0');
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('u256_sub Overflow',))]
fn test_erc20_allowance_decrease_allowance_to_zero_address() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.decrease_allowance(ZERO(), VALUE);
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('u256_sub Overflow',))]
fn test_erc20_allowance_decrease_allowance_from_zero_address() {
let (world, mut state) = STATE();
state.erc20_allowance.decrease_allowance(SPENDER(), VALUE);
}

#[test]
#[available_gas(25000000)]
fn test_erc20_allowance_decreaseAllowance() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.approve(SPENDER(), VALUE);
utils::drop_event(ZERO());

assert(state.erc20_allowance.decreaseAllowance(SPENDER(), VALUE), 'Should return true');

assert_only_event_approval(ZERO(), OWNER(), SPENDER(), 0);
assert(state.erc20_allowance.allowance(OWNER(), SPENDER()) == VALUE - VALUE, 'Should be 0');
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('u256_sub Overflow',))]
fn test_erc20_allowance_decreaseAllowance_to_zero_address() {
let (world, mut state) = STATE();

testing::set_caller_address(OWNER());
state.erc20_allowance.decreaseAllowance(ZERO(), VALUE);
}

#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('u256_sub Overflow',))]
fn test_erc20_allowance_decreaseAllowance_from_zero_address() {
let (world, mut state) = STATE();
state.erc20_allowance.decreaseAllowance(SPENDER(), VALUE);
}

28 changes: 0 additions & 28 deletions token/src/components/tests/token/erc20/test_erc20_balance.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -222,34 +222,6 @@ fn test_transfer_from() {
// assert(erc20_balance_mock.total_supply() == SUPPLY, 'Total supply should not change');
}

#[test]
#[available_gas(25000000)]
fn test_transfer_from_doesnt_consume_infinite_allowance() {
let (world, mut erc20_balance_mock) = setup();

utils::impersonate(OWNER());
erc20_balance_mock.approve(SPENDER(), BoundedInt::max());

utils::drop_all_events(erc20_balance_mock.contract_address);
utils::drop_all_events(world.contract_address);

utils::impersonate(SPENDER());
erc20_balance_mock.transfer_from(OWNER(), RECIPIENT(), VALUE);

assert_only_event_transfer(erc20_balance_mock.contract_address, OWNER(), RECIPIENT(), VALUE);

// drop StoreSetRecord ERC20BalanceModel x2
utils::drop_event(world.contract_address);
utils::drop_event(world.contract_address);
assert_only_event_transfer(world.contract_address, OWNER(), RECIPIENT(), VALUE);

assert(
erc20_balance_mock.allowance(OWNER(), SPENDER()) == BoundedInt::max(),
'Allowance should not change'
);
}


#[test]
#[available_gas(25000000)]
#[should_panic(expected: ('u256_sub Overflow', 'ENTRYPOINT_FAILED'))]
Expand Down
83 changes: 3 additions & 80 deletions token/src/components/token/erc20/erc20_allowance.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,13 @@ trait IERC20Allowance<TState> {
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}

#[starknet::interface]
trait IERC20SafeAllowance<TState> {
fn increase_allowance(ref self: TState, spender: ContractAddress, added_value: u256) -> bool;
fn decrease_allowance(
ref self: TState, spender: ContractAddress, subtracted_value: u256
) -> bool;
}

#[starknet::interface]
trait IERC20SafeAllowanceCamel<TState> {
fn increaseAllowance(ref self: TState, spender: ContractAddress, addedValue: u256) -> bool;
fn decreaseAllowance(ref self: TState, spender: ContractAddress, subtractedValue: u256) -> bool;
}

///
/// ERC20Allowance Component
///
#[starknet::component]
mod erc20_allowance_component {
use super::ERC20AllowanceModel;
use super::IERC20Allowance;
use super::IERC20SafeAllowance;
use super::IERC20SafeAllowanceCamel;
use integer::BoundedInt;
use starknet::ContractAddress;
use starknet::{get_contract_address, get_caller_address};
Expand Down Expand Up @@ -100,52 +84,6 @@ mod erc20_allowance_component {
}
}

#[embeddable_as(ERC20SafeAllowanceImpl)]
impl ERC20SafeAllowance<
TContractState,
+HasComponent<TContractState>,
+IWorldProvider<TContractState>,
+Drop<TContractState>
> of IERC20SafeAllowance<ComponentState<TContractState>> {
fn increase_allowance(
ref self: ComponentState<TContractState>, spender: ContractAddress, added_value: u256
) -> bool {
self.update_allowance(get_caller_address(), spender, 0, added_value);
true
}

fn decrease_allowance(
ref self: ComponentState<TContractState>,
spender: ContractAddress,
subtracted_value: u256
) -> bool {
self.update_allowance(get_caller_address(), spender, subtracted_value, 0);
true
}
}

#[embeddable_as(ERC20SafeAllowanceCamelImpl)]
impl ERC20SafeAllowanceCamel<
TContractState,
+HasComponent<TContractState>,
+IWorldProvider<TContractState>,
+Drop<TContractState>
> of IERC20SafeAllowanceCamel<ComponentState<TContractState>> {
fn increaseAllowance(
ref self: ComponentState<TContractState>, spender: ContractAddress, addedValue: u256
) -> bool {
self.increase_allowance(spender, addedValue)
}

fn decreaseAllowance(
ref self: ComponentState<TContractState>,
spender: ContractAddress,
subtractedValue: u256
) -> bool {
self.decrease_allowance(spender, subtractedValue)
}
}

///
/// Internal
///
Expand Down Expand Up @@ -179,31 +117,16 @@ mod erc20_allowance_component {
self.emit_event(approval_event);
}

fn update_allowance(
ref self: ComponentState<TContractState>,
owner: ContractAddress,
spender: ContractAddress,
subtract: u256,
add: u256
) {
let mut allowance = self.get_allowance(owner, spender);
// adding and subtracting is fewer steps than if
allowance.amount = allowance.amount - subtract;
allowance.amount = allowance.amount + add;
self.set_allowance(allowance);
}

// use in transfer_from
fn spend_allowance(
ref self: ComponentState<TContractState>,
owner: ContractAddress,
spender: ContractAddress,
amount: u256
) {
let current_allowance = self.get_allowance(owner, spender).amount;
if current_allowance != BoundedInt::max() {
self.update_allowance(owner, spender, amount, 0);
}
let mut allowance = self.get_allowance(owner, spender);
allowance.amount = allowance.amount - amount;
self.set_allowance(allowance);
}

fn emit_event<S, +traits::Into<S, Event>, +Drop<S>, +Clone<S>>(
Expand Down
Loading

0 comments on commit c0bfc28

Please sign in to comment.