[EURS] ERC20 Token Smart Contract Domain Community Assessment

[EURS] ERC20 Token SC Assessment

Under @befitsandpiper guidance, I reviewed EURS token smart contract. @wil would you be kind enough to review it when you have some time? Thanks.

The MIP6 submission is here. CC @StasisMarketing, @Gregory_Klumov

General Information

Risk Summary

  • Does the contract implement the ERC20 token standards? Yes
  • Risk analysis: MEDIUM
  • The contract can have delegation but the delegate is not public.

Technical Information

  • Compiler version: v0.4.21+commit.dfe3193c
  • Decimals: 2
  • Overflow checks: Yes safemath (custom SafeMath contract)
  • Mitigation against allowance race-condition: No, the approve function does have a race condition
  • Upgradeable contract patterns: yes with a custom delegatable modifier
  • Access control or restriction lists: yes
  • Non-standard features or behaviors: possible delegation to another contract,fee collector, pausing and freezing, minting and burning

Formal Verification Considerations:

  • Does transfer have simple semantics? Yes it seems
  • Does transferFrom have simple semantics? Yes it seems
  • Can balances be arbitrarily modified by some actor? Only the owner can mint and burn tokens on his own address. But the owner can also freeze (and unfreeze) accounts. Fees can also be set to 100% or even higher (with fixed fee). Fees can be waived for some accounts.
  • Are there any external calls? Most methods can be delegated to another contract using a method modifier.

Testnet Information

Contract Logic Summary

How the contract is upgraded

There is a delegate modifier on most methods. The owner of the contract can set the delegate smart contract address with the setDelegate method. A Delegation event is then logged.

When using the delegation call, the owner and the delegate can’t be changed (if they are, there is a revert).

Couldn’t find if delegate == 0x0 currently.

Administrative addresses (EOAs, controller contracts, and similar)

Owner: non public field
feeCollector: non public field but it’s 0x70250fcfef983c9b912c8eefb7021b4b7bae836e
delegate: non public field

All can be changed by the owner using methods.

Fees

The fee structure contains a variable fee (% of the transferred amount) that can range between 0% and 100% plus a fixed fee (by default @ 5 EURS, currently 0). Those fees are on top of the value transferred. It can be changed by the contract owner. There are currently no fees.

fixedFee = 0;
minVariableFee = 0;
maxVariableFee = 0;
variableFeeNumerator = 0; // <= MAX_FEE_NUMERATOR

FEE_DENOMINATOR = 100000;
MAX_FEE_NUMERATOR = FEE_DENOMINATOR;
MIN_FEE_NUMERATIOR = 0; // Never used
DEFAULT_FEE = 5e2; // 5 EURS for fixedFee at contract init

fees = max(minVariableFee, min(maxVariableFee, amount * variableFeeNumerator / FEE_DENOMINATOR)) + fixedFee

A fee event transfer is generated even in the absence of actual fees. That explains why there are always two transfers per transaction. The current feeCollector is 0x70250fcfef983c9b912c8eefb7021b4b7bae836e

When fees parameters are changed (with setFeeParameters) a FeeChange event is logged.

delegatedTransfer

Allows a delegate to pay for the gas by paying him with the token.

delegatedTransfer change some accounts and can fail later. I would hit an assert and revert, but not sure it is clean code by solidity standard.

uint256 balance = accounts [_from];
if (_value > balance) return false;
balance = safeSub (balance, _value);
if (fee > balance) return false;
balance = safeSub (balance, fee);

Contract Risk Summary

This is a medium risk contract. The ERC20 functions are implemented to the industry standard. The ERC20 allowance race condition is not mitigated. The contract makes use a SafeMath contract inheritance to avoid overflows. The owner can freeze some accounts from wallets, mint and burn tokens on it’s own account. These features are similar to other real asset and custodial tokens. Some settings (like the owner, feeCollector or delegate) are not public.

Two majors points should be monitored closely if this token is onboarded:

  • The contract has a complex fees structure that can be 100% (rendering liquidation impossible). No fees currently but should follow FeeChange events.
  • The contract can be updated by delegation. Should follow Delegation events.

Supporting Materials

N/A

Contracts Description Table

Contract Type Bases
└ Function Name Visibility Mutability Modifiers
SafeMath Implementation
└ safeAdd Internal :lock:
└ safeSub Internal :lock:
└ safeMul Internal :lock:
Token Implementation
└ totalSupply Public :exclamation: NO❗️
└ balanceOf Public :exclamation: NO❗️
└ transfer Public :exclamation: :dollar: NO❗️
└ transferFrom Public :exclamation: :dollar: NO❗️
└ approve Public :exclamation: :dollar: NO❗️
└ allowance Public :exclamation: NO❗️
AbstractToken Implementation Token, SafeMath
└ Public :exclamation: :stop_sign: NO❗️
└ balanceOf Public :exclamation: NO❗️
└ transfer Public :exclamation: :dollar: NO❗️
└ transferFrom Public :exclamation: :dollar: NO❗️
└ approve Public :exclamation: :dollar: NO❗️
└ allowance Public :exclamation: NO❗️
EURSToken Implementation AbstractToken
└ Public :exclamation: :stop_sign: NO❗️
└ Public :exclamation: :dollar: delegatable
└ name Public :exclamation: delegatable
└ symbol Public :exclamation: delegatable
└ decimals Public :exclamation: delegatable
└ totalSupply Public :exclamation: delegatable
└ balanceOf Public :exclamation: delegatable
└ transfer Public :exclamation: :dollar: delegatable
└ transferFrom Public :exclamation: :dollar: delegatable
└ approve Public :exclamation: :dollar: delegatable
└ allowance Public :exclamation: delegatable
└ delegatedTransfer Public :exclamation: :dollar: delegatable
└ createTokens Public :exclamation: :dollar: delegatable
└ burnTokens Public :exclamation: :dollar: delegatable
└ freezeTransfers Public :exclamation: :dollar: delegatable
└ unfreezeTransfers Public :exclamation: :dollar: delegatable
└ setOwner Public :exclamation: :stop_sign: NO❗️
└ setFeeCollector Public :exclamation: :dollar: delegatable
└ nonce Public :exclamation: delegatable
└ setFeeParameters Public :exclamation: :dollar: delegatable
└ getFeeParameters Public :exclamation: delegatable
└ calculateFee Public :exclamation: delegatable
└ setFlags Public :exclamation: :dollar: delegatable
└ flags Public :exclamation: delegatable
└ setDelegate Public :exclamation: :stop_sign: NO❗️
└ thisAddress Internal :lock:
└ messageSenderAddress Internal :lock:

Legend

Symbol Meaning
:stop_sign: Function can modify state
:dollar: Function is payable
6 Likes

Great work!

1 Like

Hello from the STASIS Team!

It’s been quite a while and the community is excited to know the latest news. So are we.

Please kindly update our team on the situation. We are ready to provide additional data if needed and in case it can speed up the process. Regards.