审计报告

Preliminary Comments

Security Assessment

December 9th, 2020

Preliminary Report

For :

Gainswap team @ Gainswap

By :

Guilong Li @ CertiK

[email protected]

Bryan Xu @ CertiK

[email protected]

Disclaimer

CertiK reports are not, nor should be considered, an “endorsement” or “disapproval” of any particular project or team. These reports are not, nor should be considered, an indication of the economics or value of any “product” or “asset” created by any team or project that contracts CertiK to perform a security review.

CertiK Reports do not provide any warranty or guarantee regarding the absolute bug-free nature of the technology analyzed, nor do they provide any indication of the technologies proprietors, business, business model or legal compliance.

CertiK Reports should not be used in any way to make decisions around investment or involvement with any particular project. These reports in no way provide investment advice, nor should be leveraged as investment advice of any sort.

CertiK Reports represent an extensive auditing process intending to help our customers increase the quality of their code while reducing the high level of risk presented by cryptographic tokens and blockchain technology.

Blockchain technology and cryptographic assets present a high level of ongoing risk. CertiK’s position is that each company and individual are responsible for their own due diligence and continuous security. CertiK’s goal is to help reduce the attack vectors and the high level of variance associated with utilizing new and consistently changing technologies, and in no way claims any guarantee of security or functionality of the technology we agree to analyze.

What is a CertiK report?

A document describing in detail an in depth analysis of a particular piece(s) of source code provided to CertiK by a Client.

An organized collection of testing results, analysis and inferences made about the structure, implementation and overall best practices of a particular piece of source code.

Representation that a Client of CertiK has indeed completed a round of auditing with the intention to increase the quality of the company/product’s IT infrastructure and or source code.

Overview

Project Summary

Project Name

Gainswap Protocol

Description

a defi platform with swap and staking

functionalities

Platform

Ethereum; Solidity

Codebase

GitHub Repository

Commit

866ccbe7e1ed9007f5e32c784288537a01d1d29f

Audit Summary

Delivery Date

Dec. 9, 2020

Method of Audit

Static Analysis, Manual Review

Consultants Engaged

2

Timeline

Dec. 4, 2020 - Dec. 9, 2020

Vulnerability Summary

Total Issues

11

Total Critical

0

Total Major

2

Total Minor

2

Total Informational

7

Executive Summary

This report has been prepared for Gainswap protocol to discover issues and vulnerabilities in the

source code of their Smart Contract as well as any contract dependencies that were not part of an

officially recognized library. A comprehensive examination has been performed, utilizing Dynamic

Analysis, Static Analysis, and Manual Review techniques.

The auditing process pays special attention to the following considerations:

Testing the smart contracts against both common and uncommon attack vectors.

Assessing the codebase to ensure compliance with current best practices and industry

standards.

Ensuring contract logic meets the specifications and intentions of the client.

Cross referencing contract structure and implementation against similar smart contracts

produced by industry leaders.

Thorough line-by-line manual review of the entire codebase by industry experts.

The files included in the scope were GainswapFactory and file

GainswapRouter02.

Findings

ID

Title

Type

Severity

EXH-01

Incorrect File Name

Optimization

Informational

EXH-02

Compilation Issues

Compilation

Major

EXH-03

Incorrect Naming Convention Utilization

Coding Style

Informational

EXH-04

Proper Usage of "public" and "external" type

Optimization

Informational

EXH-05

Controversial specifications in whitepaper

Optimization

Informational

EXH-06

Security risk of transferring assets

Security

Major

EXH-07

Incorrect logic for _withdraw0

Optimization

Informational

EXH-08

Math Overflow

Optimization

Minor

EXH-09

Missing Emit Events

Optimization

Minor

EXH-10

Gas Consumption

Optimization

Informational

EXH-11

Check Zero Address

Optimization

Informational

Exhibit-01: Incorrect File Name

Type

Severity

Location

Optimization

Informational

GainswapFactory.sol,GainswapRouter02.sol

Description:

There are no extension file names for file GainswapFactory and file

GainswapRouter02.

Recommendation:

We recommend to add the extension file name as below:

GainswapFactory.sol

GainswapRouter02.sol

Exhibit-02: Compilation Issues

Type

Severity

Location

Compilation

Major

GainswapFactory.sol,GainswapRouter02.sol

Description:

There several compilation issues

  1. Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract

may not be deployable on mainnet. Consider enabling the optimizer (with a low "runs"

value!), turning off revert strings, or using libraries.

  1. Duplicate contract names found for IERC20, IGainswapFactory, SafeMathGainswap. This can

cause errors and unknown behavior. Please rename one of your contracts.

Recommendation:

We recommend to split the GainswapFactory contract and GainswapPair contract into different files.

Exhibit-03: Incorrect Naming Convention Utilization

Type

Severity

Location

Coding

Informational

GainswapFactory.sol L440,L449,L575,L581

Style

GainswapRouter02.sol L20,L21,L38

Description:

Solidity defines a naming convention that should be followed. In general, parameters should use mixedCase, refer to: https://solidity.readthedocs.io/en/v0.6.12/style-guide.html#naming-conventi ons

Functions other than constructors should use mixedCase.

Examples:

Functions like: dummy_mint() , dummy_burn() , set_redepositRatio0() ,

set_redepositRatio1() , DOMAIN_SEPARATOR() , PERMIT_TYPEHASH() , MINIMUM_LIQUIDITY()

Parameter shoud use mixedCase.

Examples:

Parameter like: _redpositRatio0 , _redpositRatio1

Inside each contract, library or interface, use the following order:

Type declarations

State variables

Events

Functions

Events definition should be in front of function definitions:

event Sync(uint112 reserve0, uint112 reserve1);

event FeeUpdated(uint8 fee);

event Y0Updated(address indexed token);

event Y1Updated(address indexed token);

event Deposited0Updated(uint deposited);

event Deposited1Updated(uint deposited);

event RedepositRatio0Updated(uint16 ratio);

event RedepositRatio1Updated(uint16 ratio);

Recommendation:

The recommendations outlined here are intended to improve the readability, and thus they are

not rules, but rather guidelines to try and help convey the most information through the names

of things.

Exhibit-04: Proper Usage of "public" and "external" type

Type

Severity

Location

Optimization

Informational

GainswapFactory.sol L305,L310,L522,L525,L528,L533

Description:

"public" functions that are never called by the contract could be declared "external" . When the inputs are arrays "external" functions are more efficient than "public" functions. Examples

Functions like : getDeposited() , getDummy() , unapprove0() , unapprove1() , setY0() , setY1()

Recommendation:

Consider using the "external" attribute for functions never called from the contract.

Exhibit-05: Controversial specifications in whitepaper

Type

Severity

Location

Optimization

Informational

GainswapFactory.sol L501

Description:

According to the chapter 2.3 in doc

gainswap.pdf, the fee is 0.30%.

This is effectively the same as letting anyone flash-borrow any of assets stored in a Gainswap pool (for the same 0.30% fee as Uniswap charges for trading).

But in chapter 3.3 in the doc :

The 30-base-point fee fixed on Uniswap can be negotiated and formulated by the community in Gainswap.That can be changed according to user needs when new situations appear.

This is controversial with chapter 2.3.

Exhibit-06: Security risk of transferring assets

Type

Severity

Location

Security

Major

GainswapFactory.sol L528

Description:

This protocol has an external dependency. User's digital currencies can be deposited to a third-party service (like YFI) via IyToken contract. The system should only be used if the service is appropriately trusted. IyToken contract is not in the scope of audit.

Additionally, the governace privilege should be controlled. yToken0 and yToken1 can be easily set by governace. This will change the address where user's digital currencies to be deposited.

function setY0(address y) public onlyOwner() {

yToken0 = y;

emit Y0Updated(y);

approve0();

}

function setY1(address y) public onlyOwner() {

yToken1 = y;

emit Y1Updated(y);

approve1();

}

Recommendation:

We recommend to move the governace to Timelock or community after the protocol deployed.

Exhibit-07: Incorrect logic for _withdraw0()

Type

Severity

Location

Optimization

Informational

GainswapFactory.sol L528

Description:

There are some corner cases not included by the logic in function _withdraw0() .

  1. delta could be greater than deposited0 even not withdraw all, since there is staking

rewards.

  1. This function will transfer the staking rewards gained from IyToken to address feeTo .

Before using function setFeeTo() to set the destination, its initial value is the

GainswapFactory contract. Then liquidity providers could not share the rewards.

function _withdraw0(uint s) internal {

...

if (delta <= deposited0) {

deposited0 -= delta;

} else {

delta -= deposited0; deposited0 = 0;

_safeTransfer(token0, owner(), delta);

}

...

}

Exhibit-08: Math Overflow

Type

Severity

Location

Optimization

Minor

GainswapFactory.sol L451

Description:

Function dummy_burn() did not use safe math. This can cause sub overflow issue.

function dummy_burn(uint amount0, uint amount1) external onlyOwner() lock {

(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings

dummy0 -= uint112(amount0);

dummy1 -= uint112(amount1);

emit DummyBurn(amount0, amount1);

_update(b0(), b1(), _reserve0, _reserve1);

}

Recommendation:

We recommend to use safe math instead of arithmetic operators.

Exhibit-09: Missing Emit Events

Type

Severity

Location

Optimization

Minor

GainswapFactory.sol L688,L693

Description:

Several sensitive actions are defined without event declarations.

Examples:

Fucntions like setFeeTo() , setFeeToSetter() .

Recommendation:

Consider adding events for sensitive actions, and emit it in the function.

Exhibit-10: Gas Consumption

Type

Severity

Location

Optimization

Informational

[GainswapFactory.sol L322](#)

Description:

In function _safeTransfer() , require statements shoud be in front of the if

(redepositRatio0 > 0){} code snipit.

if (token == token0) {

_withdrawAll0();

(bool success, bytes memory data) =

token.call(abi.encodeWithSelector(SELECTOR, to, value));

if (redepositRatio0 > 0) {

redeposit0();

}

require(success && (data.length == 0 || abi.decode(data, (bool))),

'Gainswap: TRANSFER_FAILED');

} else {

_withdrawAll1();

(bool success, bytes memory data) =

token.call(abi.encodeWithSelector(SELECTOR, to, value));

if (redepositRatio1 > 0) {

redeposit1();

}

require(success && (data.length == 0 || abi.decode(data, (bool))),

'Gainswap: TRANSFER_FAILED');

}

Recommendation:

We recommend to put the if (redepositRatio0 > 0){} code snipit in front of require statement.

if (token == token0) {

_withdrawAll0();

(bool success, bytes memory data) =

token.call(abi.encodeWithSelector(SELECTOR, to, value));

require(success && (data.length == 0 || abi.decode(data, (bool))),

'Gainswap: TRANSFER_FAILED');

if (redepositRatio0 > 0) {

redeposit0();

}

} else {

_withdrawAll1();

(bool success, bytes memory data) =

token.call(abi.encodeWithSelector(SELECTOR, to, value));

require(success && (data.length == 0 || abi.decode(data, (bool))),

'Gainswap: TRANSFER_FAILED');

if (redepositRatio1 > 0) {

redeposit1();

}

}

Exhibit-11: Check Zero Address

Type

Severity

Location

Optimization

Informational

[GainswapFactory.sol L528,L533](#)

Description:

The parameter of address y is not checked for zero address in function setY0 and setY1 .

function setY0(address y) public onlyOwner() {

yToken0 = y;

emit Y0Updated(y);

approve0();

}

Recommendation:

We recommend to add below checks.

function setY0(address y) public onlyOwner() {

require(y != address(0), "Address zero is forbbiden");

yToken0 = y;

emit Y0Updated(y);

approve0();

}