EOS

CertiKCode version: not recorded (see the report)

CertiK reviewed EOS Network (EOS) on 12 Mar 2025: 7 findings, 0 critical; 6 marked fixed, 1 open.

No open critical findings
Open the reportOpen EOS Network’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings7

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High100
Medium002
Low004
Medium`system_contract::retire()` is redundantFixed

The only currency in `statstable` is XYZ added by `system_contract::init()` with `issuer = get_self()`. The contract doesn't have `issue()` and `create()` actions. `system_contract::retire()` allows the issuer (contract itself) to burn `quantity` from the contract balance. This makes no sense and can potentially break the contract invariants. The `owner` argument is also unused.

MediumLack of authorization in `system_contract::swapexFixed

The `system_contract::swap_after_forwarding()` function utilizes the `{account, "active"_n}` permission to transfer EOS tokens from the `account` to the contract itself. This is a private function that is called by the `system_contract::swapexcess()` action. The `system_contract::swapexcess()` action only uses `require_auth(get_self())`, which does not ensure that authorization from `account` is provided. The `swapexcess_action()` is invoked by multiple other actions: `bidrefund`, `sellram`, `powerup`, `refund`, and `claimrewards`. In every instance, the action is called without the `{account, "active"_n}` permission, as shown below: ```cpp swapexcess_action(get_self(), {{get_self(), "active"_n}}) ``` Consequently, the excesses will not be swapped.

Low`swapto()` can be improvedFixed

```cpp=208 if (quantity.symbol == EOS) { // First swap the EOS to XYZ and credit it to the user transfer_action("eosio.token"_n, {{from, "active"_n}}).send(from, get_self(), asset(quantity.amount, EOS), memo); // Then transfer the swapped XYZ to the target account transfer_action(get_self(), {{from, "active"_n}}).send(from, to, asset(quantity.amount, get_token_symbol()), memo); } else if (quantity.symbol == get_token_symbol()) { // First swap the XYZ to EOS and credit it to the user transfer_action(get_self(), {{from, "active"_n}}).send(from, get_self(), asset(quantity.amount, get_token_symbol()), memo); // Then transfer the swapped EOS to the target account transfer_action("eosio.token"_n, {{from, "active"_n}}).send(from, to, asset(quantity.amount, EOS), memo); } ``` 1. `memo` argument can be passed as `std::cref(memo)` to avoid copying. 2. `asset(quantity.amount, EOS)` can be replaced with `quantity` in the `quantity.symbol == EOS` branch. 3. `asset(quantity.amount, get_token_symbol())` can be replaced with `quantity` in the `quantity.symbol == get_token_symbol()` branch. 4. `memo` is used for swapping operation. However, `system_contract::transfer()` ignores `memo`. Also, any non-empty `memo` is only meaningful for the `to` account. An empty string should be used for the first transfer operation.

Low`system_contract::add_balance()` overrides `ram_pFixed

```cpp=124 if (to == to_acnts.end()) { to_acnts.emplace(ram_payer == owner ? owner : get_self(), [&](auto& a) { a.balance = value; a.released = ram_payer == owner; }); ``` The function `system_contract::add_balance()` employs `self` as the `ram_payer` when the balance is not updated by the `owner`. For instance, in the `system_contract::transfer()` function, the `from` account is designated as the `ram_payer` when a new account is created. However, this behavior is not mirrored in the `add_balance()` function, leading to a lack of clarity in its execution.

Low`system_contract::blockswapto()` auth inconsistenFixed

```cpp=180 // The account owner or this contract can block or unblock an account. if (!has_auth(get_self())) { require_auth(account); } ``` When `system_contract::blockswapto()` is called by `self,` it still uses `account` as a `ram_payer` to insert the element into `_blocked`. However, since there's no authorization for an `account`, the action will fail. It is unclear when the action is executed by the contract itself.

Low`system_contract::buyrambytes()` can be simplifieFixed

`system_contract::buyrambytes()` calculates `current_eos_balance = get_eos_balance(payer) + asset(cost_plus_fee, EOS)` and then enforces the balance to be equal to `current_eos_balance - asset(cost_plus_fee, EOS)`. That practically means that the EOS balance doesn't change. The code can be simplified as: ```cpp=364 enforcebal_action( get_self(), {{payer, "active"_n}}).send(payer, get_eos_balance(payer)); ``` The code calculating `cost_plus_fee` mimics the "eosio" calculation. It's reasonable to move it to "include\system\oldsystem.hpp"

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Part of EOS Network’s security record

EOS Network
Audits1 audit by 1 firmLast audit Mar 2025, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open EOS Network’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us