If you notice some outdated information please let us know!
FAIL
The final review score is indicated as a percentage. The percentage is calculated as Achieved Points due to MAX Possible Points. For each element the answer can be either Yes/No or a percentage. For a detailed breakdown of the individual weights of each question, please consult this document.
Very simply, the audit looks for the following declarations from the developer's site. With these declarations, it is reasonable to trust the smart contracts.
This report is for informational purposes only and does not constitute investment advice of any kind, nor does it constitute an offer to provide investment advisory or other services. Nothing in this report shall be considered a solicitation or offer to buy or sell any security, token, future, option or other financial instrument or to offer or provide any investment advice or service to any person in any jurisdiction. Nothing contained in this report constitutes investment advice or offers any opinion with respect to the suitability of any security, and the views expressed in this report should not be taken as advice to buy, sell or hold any security. The information in this report should not be relied upon for the purpose of investing. In preparing the information contained in this report, we have not taken into account the investment needs, objectives and financial circumstances of any particular investor. This information has no regard to the specific investment objectives, financial situation and particular needs of any specific recipient of this information and investments discussed may not be suitable for all investors.
Any views expressed in this report by us were prepared based upon the information available to us at the time such views were written. The views expressed within this report are limited to DeFiSafety and the author and do not reflect those of any additional or third party and are strictly based upon DeFiSafety, its authors, interpretations and evaluation of relevant data. Changed or additional information could cause such views to change. All information is subject to possible correction. Information may quickly become unreliable for various reasons, including changes in market conditions or economic circumstances.
This completed report is copyright (c) DeFiSafety 2023. Permission is given to copy in whole, retaining this copyright label.
This section looks at the code deployed on the Mainnet that gets reviewed and its corresponding software repository. The document explaining these questions is here.
1. Are the executing code addresses readily available? (%)
They are available at website https://docs.o3swap.com/resources, as indicated in the Appendix.
2. Is the code actively being used? (%)
Activity is 25 transactions a day on contract O3SwapETHUniswapBridge.sol, as indicated in the Appendix.
3. Is there a public software repository? (Y/N)
GitHub: https://github.com/O3Labs.
Is there a public software repository with the code at a minimum, but also normally test and scripts. Even if the repository was created just to hold the files and has just 1 transaction, it gets a "Yes". For teams with private repositories, this answer is "No"
4. Is there a development history visible? (%)
With 26 commits and 2 branches in their aggregator contracts folder, this is an unhealthy software repository.
This metric checks if the software repository demonstrates a strong steady history. This is normally demonstrated by commits, branches and releases in a software repository. A healthy history demonstrates a history of more than a month (at a minimum).
5. Is the team public (not anonymous)? (Y/N)
There are public and anonymous team members as seen from this article.
For a "Yes" in this question, the real names of some team members must be public on the website or other documentation (LinkedIn, etc). If the team is anonymous, then this question is a "No".
This section looks at the software documentation. The document explaining these questions is here.
7. Are the basic software functions documented? (Y/N)
The are no basic software functions (code) documented in any of the O3 Swap documentation.
8. Does the software function documentation fully (100%) cover the deployed contracts? (%)
The are no software functions (code) documented in any of the O3 Swap documentation.
9. Are there sufficiently detailed comments for all functions within the deployed contract code (%)
The Comments to Code (CtC) ratio is the primary metric for this score.
10. Is it possible to trace from software documentation to the implementation in code (%)
Are there is no software function (code) documentation in O3 Swap's documentation, we cannot verify the explicitness of the traceability towards the functions' source code implementation.
11. Full test suite (Covers all the deployed code) (%)
Although O3 Swap has multiple test repositories, they are all filled with only an empty .gitkeep file, making them essentially non-existent.
This score is guided by the Test to Code ratio (TtC). Generally a good test to code ratio is over 100%. However the reviewers best judgement is the final deciding factor.
12. Code coverage (Covers all the deployed lines of code, or explains misses) (%)
There is no evidence of a O3 Swap code coverage in their audit reports by SlowMist and Certik, nor in their own documentation.
13. Scripts and instructions to run the tests? (Y/N)
Scrips/Instructions location: There are no scripts or instructions to run tests.
14. Report of the results (%)
There is no evidence of a test result report in the O3 Swap GitHub repository.
15. Formal Verification test done (%)
No evidence of a O3 Swap Formal Verification test was found in their own documentation or on the web.
16. Stress Testing environment (%)
No evidence of any O3 Swap test-net smart contract usage was found in their documentation or in their GitHub repository.
This section looks at the 3rd party software audits done. It is explained in this document.
17. Did 3rd Party audits take place? (%)
18. Is the bug bounty acceptable high? (%)
O3Swap has a Bug bounty program offering up to 200,000$. it is not an active program.
This section covers the documentation of special access controls for a DeFi protocol. The admin access controls are the contracts that allow updating contracts or coefficients in the protocol. Since these contracts can allow the protocol admins to "change the rules", complete disclosure of capabilities is vital for user's transparency. It is explained in this document.
19. Can a user clearly and quickly find the status of the access controls (%)
There are no admin access controls in the O3 Swap documentation.
20. Is the information clear and complete (%)
There are no admin access controls in the O3 Swap documentation.
21. Is the information in non-technical terms that pertain to the investments (%)
There are no admin access controls in the O3 Swap documentation.
22. Is there Pause Control documentation including records of tests (%)
Pause Control function is explained at https://github.com/O3Labs/o3swap-contracts/blob/main/contracts/libs/lifecycle/Pausable.sol.
1contract O3 is Context, ERC20, Ownable, IO3, ReentrancyGuard {
2 using SafeMath for uint256;
3 using SafeERC20 for IERC20;
4
5 struct LpStakeInfo {
6 uint256 amountStaked;
7 uint256 blockNumber;
8 }
9
10 event LOG_UNLOCK_TRANSFER (
11 address indexed from,
12 address indexed to,
13 uint256 amount
14 );
15
16 event LOG_STAKE (
17 address indexed staker,
18 address indexed token,
19 uint256 stakeAmount
20 );
21
22 event LOG_UNSTAKE (
23 address indexed staker,
24 address indexed token,
25 uint256 unstakeAmount
26 );
27
28 event LOG_CLAIM_UNLOCKED (
29 address indexed staker,
30 uint256 claimedAmount
31 );
32
33 event LOG_SET_UNLOCK_FACTOR (
34 address indexed token,
35 uint256 factor
36 );
37
38 event LOG_SET_UNLOCK_BLOCK_GAP (
39 address indexed token,
40 uint256 blockGap
41 );
42
43 uint256 public constant FACTOR_DENOMINATOR = 10 ** 8;
44
45 mapping (address => uint256) private _unlocks;
46 mapping (address => mapping(address => LpStakeInfo)) private _stakingRecords;
47 mapping (address => uint256) private _unlockFactor;
48 mapping (address => uint256) private _unlockBlockGap;
49 mapping (address => bool) private _authorizedMintCaller;
50
51 uint256 private _totalUnlocked;
52
53 modifier onlyAuthorizedMintCaller() {
54 require(_msgSender() == owner() || _authorizedMintCaller[_msgSender()],"O3: MINT_CALLER_NOT_AUTHORIZED");
55 _;
56 }
57
58 constructor () public ERC20("O3 Swap Token", "O3") {}
59
60 function getUnlockFactor(address token) external view override returns (uint256) {
61 return _unlockFactor[token];
62 }
63
64 function getUnlockBlockGap(address token) external view override returns (uint256) {
65 return _unlockBlockGap[token];
66 }
67
68 function totalUnlocked() external view override returns (uint256) {
69 return _totalUnlocked;
70 }
71
72 function unlockedOf(address account) external view override returns (uint256) {
73 return _unlocks[account];
74 }
75
76 function lockedOf(address account) public view override returns (uint256) {
77 return balanceOf(account).sub(_unlocks[account]);
78 }
79
80 function getStaked(address token) external view override returns (uint256) {
81 return _stakingRecords[_msgSender()][token].amountStaked;
82 }
83
84 function getUnlockSpeed(address staker, address token) external view override returns (uint256) {
85 LpStakeInfo storage info = _stakingRecords[staker][token];
86 return _getUnlockSpeed(token, staker, info.amountStaked);
87 }
88
89 function claimableUnlocked(address token) external view override returns (uint256) {
90 LpStakeInfo storage info = _stakingRecords[_msgSender()][token];
91 return _settleUnlockAmount(_msgSender(), token, info.amountStaked, info.blockNumber);
92 }
93
94 function transfer(address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
95 _transfer(_msgSender(), recipient, amount);
96 _unlockTransfer(_msgSender(), recipient, amount);
97 return true;
98 }
99
100 function transferFrom(address sender, address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
101 _transfer(sender, recipient, amount);
102 _unlockTransfer(sender, recipient, amount);
103 uint256 allowance = allowance(sender, _msgSender());
104 _approve(sender, _msgSender(), allowance.sub(amount, "O3: TRANSFER_AMOUNT_EXCEEDED"));
105 return true;
106 }
107
108 function setUnlockFactor(address token, uint256 _factor) external override onlyOwner {
109 _unlockFactor[token] = _factor;
110 emit LOG_SET_UNLOCK_FACTOR(token, _factor);
111 }
112
113 function setUnlockBlockGap(address token, uint256 _blockGap) external override onlyOwner {
114 _unlockBlockGap[token] = _blockGap;
115 emit LOG_SET_UNLOCK_BLOCK_GAP(token, _blockGap);
116 }
117
118 function stake(address token, uint256 amount) external override nonReentrant returns (bool) {
119 require(_unlockFactor[token] > 0, "O3: FACTOR_NOT_SET");
120 require(_unlockBlockGap[token] > 0, "O3: BLOCK_GAP_NOT_SET");
121 _pullToken(token, _msgSender(), amount);
122 LpStakeInfo storage info = _stakingRecords[_msgSender()][token];
123 uint256 unlockedAmount = _settleUnlockAmount(_msgSender(), token, info.amountStaked, info.blockNumber);
124 _updateStakeRecord(_msgSender(), token, info.amountStaked.add(amount));
125 _mintUnlocked(_msgSender(), unlockedAmount);
126 emit LOG_STAKE(_msgSender(), token, amount);
127 return true;
128 }
129
130 function unstake(address token, uint256 amount) external override nonReentrant returns (bool) {
131 require(amount > 0, "O3: ZERO_UNSTAKE_AMOUNT");
132 LpStakeInfo storage info = _stakingRecords[_msgSender()][token];
133 require(amount <= info.amountStaked, "O3: UNSTAKE_AMOUNT_EXCEEDED");
134 uint256 unlockedAmount = _settleUnlockAmount(_msgSender(), token, info.amountStaked, info.blockNumber);
135 _updateStakeRecord(_msgSender(), token, info.amountStaked.sub(amount));
136 _mintUnlocked(_msgSender(), unlockedAmount);
137 _pushToken(token, _msgSender(), amount);
138 emit LOG_UNSTAKE(_msgSender(), token, amount);
139 return true;
140 }
141
142 function claimUnlocked(address token) external override nonReentrant returns (bool) {
143 LpStakeInfo storage info = _stakingRecords[_msgSender()][token];
144 uint256 unlockedAmount = _settleUnlockAmount(_msgSender(), token, info.amountStaked, info.blockNumber);
145 _updateStakeRecord(_msgSender(), token, info.amountStaked);
146 _mintUnlocked(_msgSender(), unlockedAmount);
147 emit LOG_CLAIM_UNLOCKED(_msgSender(), unlockedAmount);
148 return true;
149 }
150
151 function _updateStakeRecord(address staker, address token, uint256 _amountStaked) internal {
152 _stakingRecords[staker][token].amountStaked = _amountStaked;
153 _stakingRecords[staker][token].blockNumber = block.number;
154 }
155
156 function mintUnlockedToken(address to, uint256 amount) onlyAuthorizedMintCaller external override {
157 _mint(to, amount);
158 _mintUnlocked(to, amount);
159 require(totalSupply() <= 10**26, "O3: TOTAL_SUPPLY_EXCEEDED");
160 }
161
162 function mintLockedToken(address to, uint256 amount) onlyAuthorizedMintCaller external override {
163 _mint(to, amount);
164 require(totalSupply() <= 10**26, "O3: TOTAL_SUPPLY_EXCEEDED");
165 }
166
167 function setAuthorizedMintCaller(address caller) onlyOwner external override {
168 _authorizedMintCaller[caller] = true;
169 }
170
171 function removeAuthorizedMintCaller(address caller) onlyOwner external override {
172 _authorizedMintCaller[caller] = false;
173 }
174
175 function _settleUnlockAmount(address staker, address token, uint256 lpStaked, uint256 upToBlockNumber) internal view returns (uint256) {
176 uint256 unlockSpeed = _getUnlockSpeed(token, staker, lpStaked);
177 uint256 blocks = block.number.sub(upToBlockNumber);
178 uint256 unlockedAmount = unlockSpeed.mul(blocks).div(FACTOR_DENOMINATOR);
179 uint256 lockedAmount = lockedOf(staker);
180 if (unlockedAmount > lockedAmount) {
181 unlockedAmount = lockedAmount;
182 }
183 return unlockedAmount;
184 }
185
186 function _mintUnlocked(address recipient, uint256 amount) internal {
187 _unlocks[recipient] = _unlocks[recipient].add(amount);
188 _totalUnlocked = _totalUnlocked.add(amount);
189 emit LOG_UNLOCK_TRANSFER(address(0), recipient, amount);
190 }
191
192 function _getUnlockSpeed(address token, address staker, uint256 lpStaked) internal view returns (uint256) {
193 uint256 toBeUnlocked = lockedOf(staker);
194 uint256 unlockSpeed = _unlockFactor[token].mul(lpStaked);
195 uint256 maxUnlockSpeed = toBeUnlocked.mul(FACTOR_DENOMINATOR).div(_unlockBlockGap[token]);
196 if(unlockSpeed > maxUnlockSpeed) {
197 unlockSpeed = maxUnlockSpeed
198 }
199 return unlockSpeed
Comments to Code: 45 / 991 = 5 %