审计报告
Last updated
Last updated
Preliminary Comments
Security Assessment
December 9th, 2020
Preliminary Report
For :
Gainswap team @ Gainswap
By :
Guilong Li @ CertiK
Bryan Xu @ CertiK
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.
Project Summary
Project Name
Description
a defi platform with swap and staking
functionalities
Platform
Ethereum; Solidity
Codebase
Commit
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
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
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
Type
Severity
Location
Optimization
Informational
GainswapFactory.sol,GainswapRouter02.sol
Description:
There are no extension file names for file GainswapFactory and file
Recommendation:
We recommend to add the extension file name as below:
GainswapFactory.sol
GainswapRouter02.sol
Type
Severity
Location
Compilation
Major
GainswapFactory.sol,GainswapRouter02.sol
Description:
There several compilation issues
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.
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.
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.
Type
Severity
Location
Optimization
Informational
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.
Type
Severity
Location
Security
Major
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.
Type
Severity
Location
Optimization
Informational
Description:
There are some corner cases not included by the logic in function _withdraw0() .
delta could be greater than deposited0 even not withdraw all, since there is staking
rewards.
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);
}
...
}
Type
Severity
Location
Optimization
Minor
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.
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.
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();
}
}
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();
}
Disclaimer
Overview
Executive Summary
Findings
Exhibit-01: Incorrect File Name
Exhibit-02: Compilation Issues
Exhibit-04: Proper Usage of "public" and "external" type
Exhibit-05: Controversial specifications in whitepaper
Exhibit-06: Security risk of transferring assets
Exhibit-07: Incorrect logic for _withdraw0()
Exhibit-08: Math Overflow
Exhibit-09: Missing Emit Events
Exhibit-10: Gas Consumption
Exhibit-11: Check Zero Address