Skip to content

Commit

Permalink
Error Numbers (#25)
Browse files Browse the repository at this point in the history
* fix param types in burnable and mintable traits

* fungible example with our our traits

* fungible example with TokenInterface

* typo

* improve docs

* typo

* assign meaningful numbers in error enums

* typo

* remove redundant over- and underflow checks
  • Loading branch information
brozorec authored Jan 30, 2025
1 parent bb6e79e commit 3ae7d20
Show file tree
Hide file tree
Showing 11 changed files with 293 additions and 30 deletions.
6 changes: 3 additions & 3 deletions contracts/token/fungible/src/extensions/burnable/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn burn_with_allowance_works() {
}

#[test]
#[should_panic(expected = "Error(Contract, #1)")]
#[should_panic(expected = "Error(Contract, #200)")]
fn burn_with_insufficient_balance_panics() {
let e = Env::default();
e.mock_all_auths();
Expand All @@ -62,7 +62,7 @@ fn burn_with_insufficient_balance_panics() {
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
#[should_panic(expected = "Error(Contract, #201)")]
fn burn_with_no_allowance_panics() {
let e = Env::default();
e.mock_all_auths();
Expand All @@ -78,7 +78,7 @@ fn burn_with_no_allowance_panics() {
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
#[should_panic(expected = "Error(Contract, #201)")]
fn burn_with_insufficient_allowance_panics() {
let e = Env::default();
e.mock_all_auths();
Expand Down
12 changes: 9 additions & 3 deletions contracts/token/fungible/src/fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,19 @@ pub trait FungibleToken {
pub enum FungibleTokenError {
/// Indicates an error related to the current balance of account from which
/// tokens are expected to be transferred.
InsufficientBalance = 1,
InsufficientBalance = 200,
/// Indicates a failure with the allowance mechanism when a given spender
/// doesn't have enough allowance.
InsufficientAllowance = 2,
InsufficientAllowance = 201,
/// Indicates an invalid value for `live_until_ledger` when setting an
/// allowance.
InvalidLiveUntilLedger = 3,
InvalidLiveUntilLedger = 202,
/// Indicates an error when an input that must be >= 0
LessThanZero = 203,
/// Indicates an error when an input that must be > 0
LessThanOrEqualToZero = 204,
/// Indicates overflow when adding two values
MathOverflow = 205,
}

// ################## EVENTS ##################
Expand Down
21 changes: 14 additions & 7 deletions contracts/token/fungible/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ pub fn approve(e: &Env, owner: &Address, spender: &Address, amount: i128, live_u
/// * [`FungibleTokenError::InvalidLiveUntilLedger`] - Occurs when attempting to
/// set `live_until_ledger` that is less than the current ledger number and
/// greater than `0`.
/// * [`FungibleTokenError::LessThanZero`] - Occurs when `amount < 0`.
///
/// # Events
///
Expand All @@ -180,7 +181,7 @@ pub fn set_allowance(
emit: bool,
) {
if amount < 0 {
panic!("amount cannot be negative")
panic_with_error!(e, FungibleTokenError::LessThanZero)
}

let allowance = AllowanceData { amount, live_until_ledger };
Expand Down Expand Up @@ -346,13 +347,15 @@ pub fn do_transfer(e: &Env, from: &Address, to: &Address, amount: i128) {
///
/// * [`FungibleTokenError::InsufficientBalance`] - When attempting to transfer
/// more tokens than `from` current balance.
/// * [`FungibleTokenError::LessThanOrEqualToZero`] - When `amount <= 0`.
/// * [`FungibleTokenError::MathOverflow`] - When `total_supply` overflows.
///
/// # Notes
///
/// No authorization is required.
pub fn update(e: &Env, from: Option<&Address>, to: Option<&Address>, amount: i128) {
if amount <= 0 {
panic!("amount must be > 0")
panic_with_error!(e, FungibleTokenError::LessThanOrEqualToZero)
}

if let Some(account) = from {
Expand All @@ -365,17 +368,21 @@ pub fn update(e: &Env, from: Option<&Address>, to: Option<&Address>, amount: i12
e.storage().persistent().set(&StorageKey::Balance(account.clone()), &from_balance);
} else {
let mut total_supply = total_supply(e);
total_supply = total_supply.checked_add(amount).expect("total_supply overflow");
total_supply = match total_supply.checked_add(amount) {
Some(num) => num,
_ => panic_with_error!(e, FungibleTokenError::MathOverflow),
};
e.storage().instance().set(&StorageKey::TotalSupply, &total_supply);
}

if let Some(account) = to {
let mut to_balance = balance(e, account);
to_balance = to_balance.checked_add(amount).expect("to_balance overflow");
// NOTE: can't overflow because balance + amoount is at most total_supply.
let to_balance = balance(e, account) + amount;
e.storage().persistent().set(&StorageKey::Balance(account.clone()), &to_balance);
} else {
let mut total_supply = total_supply(e);
total_supply = total_supply.checked_sub(amount).expect("total_supply underflow");
// NOTE: can't overflow because amount <= total_supply or amount <= from_balance
// <= total_supply.
let total_supply = total_supply(e) - amount;
e.storage().instance().set(&StorageKey::TotalSupply, &total_supply);
}
}
38 changes: 32 additions & 6 deletions contracts/token/fungible/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn spend_allowance_reduces_amount() {
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
#[should_panic(expected = "Error(Contract, #201)")]
fn spend_allowance_insufficient_allowance_fails() {
let e = Env::default();
e.mock_all_auths();
Expand All @@ -147,7 +147,7 @@ fn spend_allowance_insufficient_allowance_fails() {
}

#[test]
#[should_panic(expected = "Error(Contract, #3)")]
#[should_panic(expected = "Error(Contract, #202)")]
fn set_allowance_with_expired_ledger_fails() {
let e = Env::default();
let address = e.register(MockContract, ());
Expand All @@ -160,6 +160,19 @@ fn set_allowance_with_expired_ledger_fails() {
});
}

#[test]
#[should_panic(expected = "Error(Contract, #203)")]
fn set_allowance_with_neg_amount_fails() {
let e = Env::default();
let address = e.register(MockContract, ());
let owner = Address::generate(&e);
let spender = Address::generate(&e);

e.as_contract(&address, || {
set_allowance(&e, &owner, &spender, -1, 5, true);
});
}

#[test]
fn set_allowance_with_zero_amount() {
let e = Env::default();
Expand Down Expand Up @@ -249,7 +262,7 @@ fn approve_and_transfer_from() {
}

#[test]
#[should_panic(expected = "Error(Contract, #1)")]
#[should_panic(expected = "Error(Contract, #200)")]
fn transfer_insufficient_balance_fails() {
let e = Env::default();
e.mock_all_auths();
Expand All @@ -264,7 +277,7 @@ fn transfer_insufficient_balance_fails() {
}

#[test]
#[should_panic(expected = "Error(Contract, #2)")]
#[should_panic(expected = "Error(Contract, #201)")]
fn transfer_from_insufficient_allowance_fails() {
let e = Env::default();
e.mock_all_auths();
Expand Down Expand Up @@ -323,7 +336,7 @@ fn update_burns_tokens() {
}

#[test]
#[should_panic(expected = "amount must be > 0")]
#[should_panic(expected = "Error(Contract, #204)")]
fn update_with_invalid_amount_panics() {
let e = Env::default();
let address = e.register(MockContract, ());
Expand All @@ -336,7 +349,20 @@ fn update_with_invalid_amount_panics() {
}

#[test]
#[should_panic(expected = "Error(Contract, #1)")]
#[should_panic(expected = "Error(Contract, #205)")]
fn update_overflow_panics() {
let e = Env::default();
let address = e.register(MockContract, ());
let account = Address::generate(&e);

e.as_contract(&address, || {
mint(&e, &account, i128::MAX);
update(&e, None, Some(&account), 1);
});
}

#[test]
#[should_panic(expected = "Error(Contract, #200)")]
fn update_with_insufficient_balance_panics() {
let e = Env::default();
let address = e.register(MockContract, ());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"generators": {
"address": 3,
"nonce": 0
},
"auth": [
[]
],
"ledger": {
"protocol_version": 22,
"sequence_number": 0,
"timestamp": 0,
"network_id": "0000000000000000000000000000000000000000000000000000000000000000",
"base_reserve": 0,
"min_persistent_entry_ttl": 4096,
"min_temp_entry_ttl": 16,
"max_entry_ttl": 6312000,
"ledger_entries": [
[
{
"contract_data": {
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"key": "ledger_key_contract_instance",
"durability": "persistent"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"key": "ledger_key_contract_instance",
"durability": "persistent",
"val": {
"contract_instance": {
"executable": {
"wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"storage": null
}
}
}
},
"ext": "v0"
},
4095
]
],
[
{
"contract_code": {
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_code": {
"ext": "v0",
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"code": ""
}
},
"ext": "v0"
},
4095
]
]
]
},
"events": []
}
Loading

0 comments on commit 3ae7d20

Please sign in to comment.