Skip to content

Instantly share code, notes, and snippets.

@RideSolo
Last active August 18, 2020 21:17
Show Gist options
  • Save RideSolo/3fd4c650f32da42d618143dde639238c to your computer and use it in GitHub Desktop.
Save RideSolo/3fd4c650f32da42d618143dde639238c to your computer and use it in GitHub Desktop.

Court Reward Audit Report.

1. Summary

This document is a security audit report performed by ridesolo, where Court Reward has been reviewed.

2. In scope

  • RewardCourts.sol github commit hash 7115b6f45ed5ba923ce4fea3f26a115b29c946df.
  • Common.sol github commit hash 8ebabf945595890c3f2f77d6c5b320aff17111fc.
  • Address.sol github commit hash 8ebabf945595890c3f2f77d6c5b320aff17111fc.
  • ERC165.sol github commit hash 8ebabf945595890c3f2f77d6c5b320aff17111fc.
  • IERC1155.sol github commit hash 8ebabf945595890c3f2f77d6c5b320aff17111fc.
  • IERC1155TokenReceiver.sol github commit hash 8ebabf945595890c3f2f77d6c5b320aff17111fc.

3. Findings

** 8 ** were reported including:

  • 1 high severity issue.
  • 3 medium severity issues.
  • 2 low severity issues.
  • 2 notes.

3.1. Trusted Court Chain

Severity: High

Description

A chain of trust is implemented when owners trust each other courts, however this logic represent a lot of risks since if a single malicious owner (or a private key hack) access to any chain of trust he will be able to mint internal tokens in other courts.

This logic will allow different kind of attacks (especially if the tokens have a significant price or usage).

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L535

3.2. Trusted/Untrust Courts

Severity: medium

Description

The developers used trustedCourtsList to save and get the trusted court ids through an array, when untrusting a court the id is correctly set to false inside the mapping but when removing the id from the array, id i is replaced by the last element of the array which is wrong. i does not represent the index position of the court id to untrust, it represent the position of the id in the _trustees input court list.

Another note is that trust function uses two for loops instead of one. When a court owner adds trusted courts using trustCourts function instead of checking the existance of _trustees list elements in trustedCourtsList array and compute a new length, the developers can directly push the new court ids to the array in the same loop without requiring .

To solve this issue please follow the recommendation below.

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L478

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L505

Recommendation

Please note that this is just recommendation and should be cross checked by the developers before implementation.

    mapping (uint128 => mapping (uint128 => uint256)) internal trustedCourtsIndex;
    mapping (uint128 => mapping (uint128 => bool)) internal trustedCourtstrustedCourtsIndex;
    mapping (uint128 => uint128[]) public trustedCourtsList;

    function trustCourts(uint128 _truster, uint128[] _trustees) external {
        require(courtOwners[_truster] == msg.sender, "We are not court owner");
        for (uint i = 0; i < _trustees.length; ++i) {
            require(_trustees[i] > 0 && _trustees[i] <= courtNonce, "Court does not exist.");
            if(!trustedCourts[_truster][_trustees[i]]) {
                trustedCourts[_truster][_trustees[i]] = true;
                trustedCourtsIndex[_truster][_trustees[i]] = trustedCourtsList[_truster].length;
                trustedCourtsList[_truster].push(_trustees[i]);
            }
        }
        emit TrustCourts(_truster, _trustees);
    }

    function untrustCourts(uint128 _truster, uint128[] _trustees) external {
        require(courtOwners[_truster] == msg.sender, "We are not court owner");
        uint index;
        uint length = trustedCourtsList[_truster].length;
        for (uint i = 0; i < _trustees.length; ++i) {
            require(_trustees[i] > 0 && _trustees[i] <= courtNonce, "Court does not exist.");
            if (trustedCourts[_truster][_trustees[i]]) {
                trustedCourts[_truster][_trustees[i]] = false;
                index = trustedCourtsIndex[_truster][_trustees[i]];
                trustedCourtsList[_truster][index] = trustedCourtsList[_truster][--length];
                delete trustedCourtsIndex[_truster][_trustees[i]];
            }
        }
        trustedCourtsList[_truster].length = length;
        emit UntrustCourts(_truster, _trustees);
    }

3.3. Between Courts Transfers

Severity: medium

Description

_doIntercourtTransferBatch function does transfers between courts with a conversion ratio of 1:1. For example if tokens in court x have a higher price than tokens in a court y, users will be able to transfer their tokens toward that court if the chain of trust is established by the owners.

This issue goes against what is described in intercourtTransfer and intercourtTransferBatch "Transfer money through several courts with automatic currency conversion between court tokens", since no conversion mechanism is applied.

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L226

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L255

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L592

3.4. ERC-1155 Compliance

Severity: medium

Description

Following ERC-1155 standard "If the optional extension is included: The ERC-165 supportsInterface function MUST return the constant value true if 0x0e89341c is passed through the interfaceID argument". This impelemntation does not contain the ERC1155Metadata_URI extension, meaning that the developers must set 0x0e89341c when passed through supportsInterface to false or implement the extension.

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L67

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L75

3.5. Ownership Transfer

Severity: low

Description

When setting the new owner address for a court:

  • The new owner address should be verified to be different than zero address.
  • Two step approvals must be set to avoid setting a wrong addresses. A first function will save an address in a global variable, the second function will confirm the new address if the msg.sender is equal to the new address proving that the new owner has access to the correct private key.

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L436

3.6. ERC-165

Severity: low

Description

EIP-165 intend to "creates a standard method to publish and detect what interfaces a smart contract implements", however this implemented version standard does not set all the functions except the required ERC-1155 functions and ERC-165, a mapping can be used to set all functions and avoid consuming more gas than the intended 10K of EIP-1155 (the requirement for EIP-165 is 30K). Please refer to the recommendation.

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L50#L80

Recommendation

	mapping(bytes4 => bool) internal supportedInterfaces;
	function setSupportedInterfaces() internal {
		// example
		supportedInterfaces[bytes4(keccak256('supportsInterface(bytes4)'))] = true;
		// list all public function here, following the previous example.
	}

	function supportsInterface(bytes4 interfaceID) external view returns (bool) {
		return supportedInterfaces[interfaceID];
	}

	constructor() {
		setSupportedInterfaces();
	}

3.7. Intercourt Token Creation

Severity: note

Description

No owner is constrained to follow the id returned by createIntercourtToken, this function can simply be removed if the developers do not intend to use it.

Code snippet

https://github.com/ridesoloAudit/courts/blob/ef329b3122615e8a3f93613ee14b702cd1b76623/contracts/RewardCourts.sol#L457

3.8. Naming Confusion

Severity: note

Description

Using "inter" as a prefix is confusing since it can means both "between" or "within", a clear distinction should be done when using it in function or variables.

4. Conclusion

All highlighted issues should be solved before deployment for users and team benefits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment