-
Notifications
You must be signed in to change notification settings - Fork 359
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Rename file with Math fuzz tests * Turn on fuzz testing on CI * Introduce fuzzing feature to token and utils packages * Add overflow testing helpers * Add fuzz tests for ERC20 * Add fuzz tests for ERC721Enumerable * Move fuzz helpers to test_common * Fix workflow file * Remove specifying empty default features * Lint files * Update fuzz tests * Remove coverage command from CI test run * Improve ERC721Enum fuzz tests * Reduce fuzzer runs from 10k to 1k * Reduce fuzzer runs to 500 * Support unlimited allowance in test case
- Loading branch information
Showing
11 changed files
with
457 additions
and
2 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
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
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,16 @@ | ||
use core::num::traits::ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub}; | ||
|
||
pub fn is_overflow_add<T, +OverflowingAdd<T>, +Drop<T>>(x: T, y: T) -> bool { | ||
let (_, does_overflow) = x.overflowing_add(y); | ||
does_overflow | ||
} | ||
|
||
pub fn is_overflow_mul<T, +OverflowingMul<T>, +Drop<T>>(x: T, y: T) -> bool { | ||
let (_, does_overflow) = x.overflowing_mul(y); | ||
does_overflow | ||
} | ||
|
||
pub fn is_overflow_sub<T, +OverflowingSub<T>, +Drop<T>>(x: T, y: T) -> bool { | ||
let (_, does_overflow) = x.overflowing_sub(y); | ||
does_overflow | ||
} |
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
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 |
---|---|---|
@@ -1,2 +1,5 @@ | ||
mod test_erc20; | ||
mod test_erc20_permit; | ||
|
||
#[cfg(feature: 'fuzzing')] | ||
mod test_fuzz_erc20; |
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,165 @@ | ||
use ERC20Component::InternalTrait; | ||
use core::num::traits::Bounded; | ||
use crate::erc20::ERC20Component; | ||
use crate::erc20::ERC20Component::{ERC20CamelOnlyImpl, ERC20Impl}; | ||
use crate::erc20::ERC20Component::{ERC20MetadataImpl, InternalImpl}; | ||
use openzeppelin_test_common::math::{is_overflow_add, is_overflow_sub}; | ||
use openzeppelin_test_common::mocks::erc20::DualCaseERC20Mock; | ||
use openzeppelin_testing::constants::{NAME, OWNER, RECIPIENT, SPENDER, SYMBOL}; | ||
use snforge_std::{start_cheat_caller_address, test_address}; | ||
use starknet::ContractAddress; | ||
|
||
// | ||
// Setup | ||
// | ||
|
||
type ComponentState = ERC20Component::ComponentState<DualCaseERC20Mock::ContractState>; | ||
|
||
fn COMPONENT_STATE() -> ComponentState { | ||
ERC20Component::component_state_for_testing() | ||
} | ||
|
||
fn setup(supply: u256) -> ComponentState { | ||
let mut state = COMPONENT_STATE(); | ||
state.initializer(NAME(), SYMBOL()); | ||
state.mint(OWNER(), supply); | ||
state | ||
} | ||
|
||
// | ||
// Tests | ||
// | ||
|
||
#[test] | ||
fn test_mint(supply: u256, mint_amount: u256) { | ||
if is_overflow_add(supply, mint_amount) { | ||
return; | ||
} | ||
let mut state = setup(supply); | ||
|
||
assert_total_supply(supply); | ||
assert_balance(OWNER(), supply); | ||
|
||
state.mint(RECIPIENT(), mint_amount); | ||
assert_total_supply(supply + mint_amount); | ||
assert_balance(RECIPIENT(), mint_amount); | ||
} | ||
|
||
#[test] | ||
fn test_burn(supply: u256, burn_amount: u256) { | ||
if is_overflow_sub(supply, burn_amount) { | ||
return; | ||
} | ||
let mut state = setup(supply); | ||
|
||
assert_total_supply(supply); | ||
assert_balance(OWNER(), supply); | ||
|
||
state.burn(OWNER(), burn_amount); | ||
assert_total_supply(supply - burn_amount); | ||
assert_balance(OWNER(), supply - burn_amount); | ||
} | ||
|
||
#[test] | ||
fn test_mint_burn(initial_supply: u256, mint_amount: u256, burn_amount: u256) { | ||
if is_overflow_add(initial_supply, mint_amount) { | ||
return; | ||
} | ||
if is_overflow_sub(mint_amount, burn_amount) { | ||
return; | ||
} | ||
let mut state = setup(initial_supply); | ||
let (owner, recipient) = (OWNER(), RECIPIENT()); | ||
|
||
// Mint | ||
state.mint(recipient, mint_amount); | ||
assert_total_supply(initial_supply + mint_amount); | ||
assert_balance(owner, initial_supply); | ||
assert_balance(recipient, mint_amount); | ||
|
||
// Burn | ||
state.burn(recipient, burn_amount); | ||
assert_total_supply(initial_supply + mint_amount - burn_amount); | ||
assert_balance(owner, initial_supply); | ||
assert_balance(recipient, mint_amount - burn_amount); | ||
} | ||
|
||
#[test] | ||
fn test_transfer(supply: u256, transfer_amount: u256) { | ||
if is_overflow_sub(supply, transfer_amount) { | ||
return; | ||
} | ||
let mut state = setup(supply); | ||
let (owner, recipient) = (OWNER(), RECIPIENT()); | ||
|
||
start_cheat_caller_address(test_address(), owner); | ||
state.transfer(recipient, transfer_amount); | ||
|
||
assert_balance(owner, supply - transfer_amount); | ||
assert_balance(recipient, transfer_amount); | ||
} | ||
|
||
#[test] | ||
fn test_transfer_from(supply: u256, transfer_amount: u256) { | ||
if is_overflow_sub(supply, transfer_amount) { | ||
return; | ||
} | ||
let mut state = setup(supply); | ||
let (owner, spender, recipient) = (OWNER(), SPENDER(), RECIPIENT()); | ||
let contract_address = test_address(); | ||
|
||
// Approve | ||
start_cheat_caller_address(contract_address, owner); | ||
state.approve(spender, transfer_amount); | ||
assert_balance(owner, supply); | ||
assert_allowance(owner, spender, transfer_amount); | ||
|
||
// Transfer from | ||
start_cheat_caller_address(contract_address, spender); | ||
state.transfer_from(owner, recipient, transfer_amount); | ||
assert_allowance(owner, spender, 0); | ||
assert_balance(owner, supply - transfer_amount); | ||
assert_balance(recipient, transfer_amount); | ||
assert_balance(spender, 0); | ||
} | ||
|
||
#[test] | ||
fn test__spend_allowance(supply: u256, spend_amount: u256) { | ||
if is_overflow_sub(supply, spend_amount) { | ||
return; | ||
} | ||
let mut state = setup(supply); | ||
let (owner, spender) = (OWNER(), SPENDER()); | ||
state._approve(owner, spender, supply); | ||
|
||
state._spend_allowance(owner, spender, spend_amount); | ||
|
||
// Allowance doesn't change if it's set to maximum | ||
let expected_allowance = if supply == Bounded::MAX { | ||
supply | ||
} else { | ||
supply - spend_amount | ||
}; | ||
assert_balance(owner, supply); | ||
assert_balance(spender, 0); | ||
assert_allowance(owner, spender, expected_allowance); | ||
} | ||
|
||
// | ||
// Helpers | ||
// | ||
|
||
fn assert_total_supply(expected: u256) { | ||
let state = COMPONENT_STATE(); | ||
assert_eq!(state.total_supply(), expected); | ||
} | ||
|
||
fn assert_allowance(owner: ContractAddress, spender: ContractAddress, expected: u256) { | ||
let state = COMPONENT_STATE(); | ||
assert_eq!(state.allowance(owner, spender), expected); | ||
} | ||
|
||
fn assert_balance(owner: ContractAddress, expected: u256) { | ||
let state = COMPONENT_STATE(); | ||
assert_eq!(state.balance_of(owner), expected); | ||
} |
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 |
---|---|---|
@@ -1,3 +1,6 @@ | ||
mod test_erc721; | ||
mod test_erc721_enumerable; | ||
mod test_erc721_receiver; | ||
|
||
#[cfg(feature: 'fuzzing')] | ||
mod test_fuzz_erc721_enumerable; |
Oops, something went wrong.