-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c0d468e
commit 1e09b19
Showing
1 changed file
with
97 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Token interface inference | ||
|
||
## Description | ||
|
||
- Category: `Best practices` | ||
- Severity: `Enhancement` | ||
- Detector: [`token-interface-inference`](https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/token-interface-inference) | ||
- Test Cases: [`token-interface-inference-1`](https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/token-interface-inference/token-interface-inference-1) | ||
|
||
In Rust, the use of tokens requires strictly following a set of specifications. If any of these are not met, the contract will have errors and fail. Therefore, the use of the `soroban_sdk::token::TokenInterface` trait is really useful because it warns if any of the specifications are not being fulfilled. | ||
|
||
## Why is this bad? | ||
|
||
Not using the `soroban_sdk::token::TokenInterface` trait makes it more difficult to comply with the token interface. | ||
|
||
## Issue example | ||
|
||
Consider the following `Soroban` contract: | ||
|
||
```rust | ||
|
||
#[contract] | ||
pub struct TokenInterfaceInference; | ||
|
||
#[contractimpl] | ||
impl TokenInterfaceInference { | ||
pub fn initialize( | ||
env: Env, | ||
admin: Address, | ||
decimals: u32, | ||
name: String, | ||
symbol: String, | ||
) -> Result<(), TIIError> { | ||
let current_token_metadata: Option<TokenMetadata> = | ||
env.storage().instance().get(&DataKey::TokenMetadata); | ||
if current_token_metadata.is_some() { | ||
return Err(TIIError::AlreadyInitialized); | ||
} else { | ||
env.storage().instance().set( | ||
&DataKey::TokenMetadata, | ||
&TokenMetadata { | ||
decimals, | ||
name, | ||
symbol, | ||
admin, | ||
}, | ||
); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
``` | ||
|
||
|
||
In this example, the trait `soroban_sdk::token::TokenInterface` is not implemented; instead, a contract named "TokenInterfaceInference" is implemented. | ||
|
||
The code example can be found [here](https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/token-interface-inference/token-interface-inference-1/vulnerable-example). | ||
|
||
|
||
## Remediated example | ||
|
||
Consider the following `Soroban` contract: | ||
|
||
```rust | ||
|
||
#[contractimpl] | ||
impl token::TokenInterface for TokenInterfaceEvents { | ||
fn allowance(env: Env, from: Address, spender: Address) -> i128 { | ||
let allowance = Self::get_allowance(env.clone(), from, spender); | ||
if allowance.expiration_ledger < env.ledger().sequence() { | ||
0 | ||
} else { | ||
allowance.amount | ||
} | ||
} | ||
|
||
``` | ||
|
||
In this example, the trait `soroban_sdk::token::TokenInterface` is implemented on a contract called "TokenInterfaceEvents". | ||
|
||
The remediated code example can be found [here](https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/token-interface-inference/token-interface-inference-1/remediated-example). | ||
|
||
## How is it detected? | ||
|
||
Checks if token handling is being implemented in the contract and warns if the trait `soroban_sdk::token::TokenInterface` is not being used. | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|