This report presents the results of our engagement with Counterparty to review Shell Protocol.
The review was conducted over the course of two weeks and two days, from June 22 to July 7 2020 by
Daniel Luca and Gonçalo Sá. A total of 22 person-days were spent. This review is following another previous 1-day review we provided for the client.
During the first week, we started to learn how the system works by having a few calls with the client and by reading the provided documents and the available source code. We set up a meeting with the development team on Monday to explain our process, understand the system, agree on the overall scope and purpose of the audit, and ask for a commit hash.
We had some initial problems with compiling the code because we were not familiar with the framework, and not all of the changes were pushed to the repository. Over the next few days, we went back and forth, trying to identify the problems and coming up with solutions on how to make the code compilable. Once we agreed on a pretty good version, we locked in the commit hash 2f0f9d6c5a6ba471ae88f14da1f1b3e8470332b0. A complete list of files can be found in the Appendix.
On Tuesday, we had another call with the development team to discuss the high-level overview of the system, roughly getting into the math behind the balancing mechanics. We also asked for a walk-through of the system, to understand how a user is supposed to interact with it. We got familiar with the core functionality of the system, namely, how the balancing is done and how tokens flow within the system. We also discussed why some decisions were made as they are, specifically how internal accounting is done, how external calls are done, and why they exist, how the assimilators work in principle.
On Wednesday, we continued to do our manual review and set up a new meeting with the client to discuss initial findings, ask questions, and continue the code walk-through. Our main focus was to follow the lifetime of a simple token swap transaction throughout the codebase.
Over the following days, we directed our efforts towards understanding the system, trying to find issues and edge cases. A number of issues were filed to be included in the report.
It was clear to us that it is vital to help the development team change the way they are currently developing the application. A number of systemic problems were identified and explained in detail below, in the Action Items section.
On Friday, we set up another meeting with the client where we discussed that our focus would be split into finding more security issues with the system, but also address the overall methodology of the development process.
In the second week we continued to file issues while categorizing them into groups relating to complexity, fragility, and testing. Each of the groups is referred to in the Action Items. Other security issues not relating to the aforementioned groups were filed in the review.
We continued to have daily sync meetings to discuss any issues we might have found and use our group knowledge to validate and test different attack vectors. Most of the time spent in the second week was to file new issues, validate attack vectors, and put the report together in a format that the audit team believes to deliver the most value to the development team.
At the end of the second week, we had a meeting with the development team where we discussed a few other attack vectors, power centralization issues, and other parts of the code that were not completely clear.
1.1 Mitigations Review Update
The Shell Protocol team diligently addressed all of the issues present in the report. This effort entailed huge code transformations that were completed at a fast pace.
The biggest transformation was the creation of a “pool partitioning” mechanism to tackle the lock-up conditions stemming from the pool balancing loop needed for the correct functioning of the system.
Since the beginning of the audit, there were clear improvements in both the quality of the code style and the attention to the product’s security.
The auditing team would also like to make notice of the fact that the codebase was in a developing state by the time of the audit and therefore strong changes were sure to ensue.
Shell Protocol is a conjunction of a liquidity pool and AMM for stablecoins that is designed to have no slippage beyond the liquidity fee and to pass arbitrage profits on to the liquidity providers (LPs, from now on). To achieve this goal, Shell Protocol implements a bonding surface in its core logic made up of several smaller, locally-defined bonding surfaces.
Even though Shell Protocol is made of clearly delineated business logic modules, the codebase under review implements them in a way heavily intertwined way. As such, it is easier to distinguish and categorize these components based on their logical functions rather than specific files or contracts.
Loihi is the name of this version of Shell Protocol’s codebase and the one we will be describing in the next few paragraphs.
The two main logical components of the system are shell s and assimilators. With a shell being the most central part of the system and the assimilators being the middleware connecting the different financial instruments to the core liquidity and swap logic for each pool.
At the time of the audit, a formalism of the bonding surface implemented by Loihi was provided to the audit team and can be found here.
A shell is, as stated, the core, logical part of Shell Protocol. Each instance of a pool will have exactly one shell.
The shell is the data structure present within the smart contract system that ties all the other components together. Encapsulated inside the resulting compiled smart contract that makes up a shell there is logic for:
the core mathematical components for the loss function creating the bonding curve (Shells.sol, Loihi.sol)
ERC20 implementation of the shell token (ShellsExternal.sol, Shells.sol)
depositing liquidity in the pool (Loihi.sol)
withdrawing liquidity from the pool (Loihi.sol)
swapping two tokens supported by the pool (Loihi.sol)
administrative functions that rule the pool’s parameters (Controller.sol, Loihi.sol, LoihiRoot.sol)
Most of the files mentioned above take the form of all-internal-method libraries that get fully appended to Loihi.sol ’s and LoihiRoot.sol ’s bytecode, since they are the only contracts in the set.
In addition to the components, Shell Protocol is using safe math libraries to handle both 256-bit unsigned integer and 128-bit 64.64 fixed-point decimal arithmetics with no over/underflow.
All the mathematical components handling the fixed fee, halt enforcement parameters and slippage calculations are using 64.64 fixed-point decimals, internally, using Solidity’s elementary 128-bit signed integer type.
Liquidity-related And Swap-related Components
All the math-related methods are contained within Shells.sol with some amount of preparation for the calculations being done inside Loihi.sol, as well.
In Loihi.sol, the system prepares the data that is fed into the implementation of the bonding surface in Shells.sol. Loihi queries the token balances for the set of supported stablecoins and their derivatives, creates the necessary derived variables needed for the calculations (mostly sums of all the token balances, through the methods getLiquidityData and getSwapData) and then calls the relevant methods for the calculations (calculateLiquidityMembrane and calculateTrade, related to the prior methods in the respective order) from Shells.sol.
The Loihi contract is then responsible for propagating the changes (and effectively writing them to storage) to the omega parameter of the pool and calling the respective assimilator methods to credit or withdraw the relevant amount of tokens while implementing boundary checks for these values at the same time.
Administrative Functions
Most of the administrative functions logic is implemented in Controller.sol with some of it being implemented in Loihi.sol (more specifically the ability to remove an assimilator and the ability to send tokens from the contract to an address of their choice).
The functions present in the Controller allow the administrator to set new parameters for the pool, include a new supported asset (stablecoin) and include a new assimilator for any supported asset.
3.2 Assimilators
The assimilators are the middleware between a shell and the different DeFi systems it needs to interact in its set of supported assets and their derivatives.
They act, in essence, as a delegatecall proxy system to all the different stablecoins and their derivatives in order for the pool to be able to interactively balance itself and allow LPs to provide liquidity.
In the current architecture, assimilators necessarily need to take the shape of proxies to externally deployed contracts. This is due to the fact that each of the supported assets and its derivatives have differently named methods and even control flows that need to be followed in order to interact with their token implementations properly.
Not only are assimilators an abstraction to the different interfaces and accounting models of each of the supported assets and derivatives but a necessary instrument in the normalization of each of these external tokens’ value internally to Shell Protocol.
In the file Assimilators.sol, all the methods present are merely internal functions meant to delegate execution to the each relevant implementation (given the relevant token) at runtime. The critical part of the assimilator architecture is present in the assimilators/ directory inside the repository under review.
The actual implementations of the assimilators (only meant to be delegatecalled) mostly implement the same interface and are the components responsible for both interfacing with the external DeFi systems and also make the correctness checks about the success of these necessary sub-calls. Assimilators are also keeping the consistency by typecasting between the conventional unsigned integer used for balances in ERC20-compatible tokens and the 64.64 fixed-point decimal used by the shell, internally.
4 Action Items
4.1 Reduce overall complexity
Mitigations Review Update
Comment from the development team:
Previously, we were utilizing libraries with the “using library for type” convention. This made the code difficult to understand.
Now our library use is well named and with the exception of mathematical operations is employed using the normal call syntax like “Library.function(argument, argument)”.
Combined with descriptive names for the libraries, it is easy to see where the code is flowing to.
Although we now make use of a total of 9 non-math libraries (including internal and external libraries), they are well named and easy to reason with.
Complexity comes at the cost of security. Complex systems are harder to understand, harder to test, and harder to maintain.
For smart contract systems, the fault-intolerant environment of the EVM necessarily demands that security is the highest priority. Therefore, it should be a design goal of all smart contract systems to reduce complexity and make logic explicit wherever possible.
The Shell Protocol is a highly complex system:
There is a deep library usage that spans between multiple files, even having libraries include other libraries a few levels deep.
The mathematical model is not completely and clearly defined; the document explaining the math powering the system has not reached a final version.
A large number of assimilators can be included as part of the system. Each of them has to be reviewed in the context of the system, but also in the context of the token being supported because adding an assimilator which is incorrectly implemented can put the system at risk.
Fixed point math operations are used in the system, but the libraries were changed, and some of the implementations are duplicated in multiple contracts.
A common theme throughout the system is to use delegatecall, which creates a huge trust issue since the owner can, at any point in time, add an assimilator that steals all the tokens in the system.
There are inconsistencies in function names and variable names; these should all be addressed. For example “Assimilators” used to be called “Adapters”, and some of the function names still refer to “Adapters”, we have includeAssimilator and excludeAdapter.
Recommendation:
Reducing overall complexity is no simple task, and addressing this system’s complexity will require careful thought and consideration outside of the scope of this review. In general, prioritize the following concepts:
Optimize for readability. Ensure that code is as easy to understand as possible. Implement clear and consistent naming conventions, group similar functions within the same file, and generally attempt to structure and organize the code so that humans can read and understand it best.
Remove commented-out code. Remove old code that was used for tests or for setting up local environments and find other ways to mock or configure the system without the need to change code.
4.2 Increase the overall quality and quantity of testing
Mitigations Review Update
Comment from the development team:
The failing tests existed because we made minute changes to our present model (changes in applying the base fee - “epsilon”), so in a sense, rather than failing they just need updating. Many of them are also an artifact of architecting the tests in such a way that they can be run against arbitrary parameter sets - or in different “suites”.
Several findings of this assessment suggest that Shell Protocol is inadequately tested:
Almost half of the tests fail.
There is no continuous integration system.
There is no report about code coverage. We do not know if the tests cover the whole codebase. This makes it likely that not all functionality is well tested.
Some of the changes implemented in the fork libraries do not implement the intended functionality.
Recommendation:
Implementing a robust, complete test suite requires careful consideration outside of the scope of this review. In general, prioritize the following concepts:
Write tests that encapsulate the specification. Tests should address each of a system’s requirements. A system’s requirements should be clearly defined within the system design specification.
Try to develop new functionality by writing tests first. Test-driven development makes sure that all of the written code is accompanied by a test.
Implement a continuous integration system. Using one of the platforms that offer CI/CD services and implements a list of actions that do compilation, tests, code coverage, and panics when the smallest piece does not check out.
Software is considered “fragile” when issues or changes in one part of the system can have side-effects in conceptually unrelated parts of the codebase. Fragile software tends to break easily and may be challenging to maintain.
Our assessment uncovered that for each swap in the system, all of the enabled assets run code. That means that if one of the enabled tokens blacklists the exchange, all of the tokens are locked in the system unless a backdoor exists.
Recommendation:
Building an anti-fragile system requires careful thought and consideration outside of the scope of this review. In general, prioritize the following concepts:
Follow checks-effects-interactions pattern. The checks-effects-interactions should be implemented throughout the code. Also, functions’ return values should always be checked for correctness.
Follow the single-responsibility principle of functions. This principle states that functions should have responsibility for a single part of the system’s functionality and that their purpose should be narrowly-aligned with that responsibility. Avoid functions that “do everything” (like setGovernanceParameter), and avoid functions that touch every other function (like funding and markPrice).
This section describes, from a security perspective, the expected behavior of the system under audit. It is not a substitute for documentation. The purpose of this section is to identify specific security properties that were validated by the audit team.
5.1 Actors
The relevant actors are listed below with their respective abilities:
Non-privileged access actors
Pool user (i.e., non-privileged user with no shell tokens in their possession)
Can swap assets supported by the pool.
Liquidity provider
All of the above.
Can deposit supported assets into the pool.
Can withdraw its share of supported assets from the pool (relative to the amount of shell tokens they hold).
Privileged access actors
Administrator
All of the above.
Setting all the parameters of the pool at anytime.
Adding supported assets.
Adding supported assimilators (basically setting an address to which execution is delegated, no restrictions).
5.2 Important Security Properties
The following is a non-exhaustive list of security properties that were verified in this audit:
Non-privileged access actors
Pool user
Cannot swap assets that are unsupported by the pool.
Cannot swap an asset bypassing the fee calculation.
Cannot bypass the depositing of the intake token.
Liquidity provider
Cannot bypass the fee calculation when depositing or withdrawing assets.
Cannot mint or burn tokens in a proportion not relative to their held shell tokens.
By repeated action, cannot drain the pool by exploiting a bad implementation of the pre-fee-calculation parameters.
Please note that some other properties were reviewed in addition to these ones. The ones that were proven to be untrue are instead represented as issues in this same report.
6 Issues
Each issue has an assigned severity:
Minor issues are subjective in nature. They are typically suggestions around best practices or readability. Code maintainers should use their own judgment as to whether to address such issues.
Medium issues are objective in nature but are not security vulnerabilities. These should be addressed unless there is a clear reason not to.
Major issues are security vulnerabilities that may not be directly exploitable or may require certain conditions in order to be exploited. All major issues should be addressed.
Critical issues are directly exploitable security vulnerabilities that need to be fixed.
When this was brought to our attention, it made the most sense to look at it from a bird’s eye view. In the event that an assimilator does seize up either due to smart contract malfunctioning or to some type of governance decision in one of our dependencies, then depending on the severity of the event, it could either make it so that that particular dependency is unable to be transacted with or it could brick the pool altogether.
In the case of the latter severity where the pool is bricked altogether for an extended period of time, then this means the end of that particular pool’s life. In this case, we find it prudent to allow for the withdrawal of any asset still functional from the pool. Should such an event transpire, we have instituted functionality to allow users to withdraw individually from the pool’s assets according to their Shell balances without being exposed to the inertia of the incapacitated assets.
In such an event, the owner of the pool can now trigger a partitioned state which is an end of life state for the pool in which users send Shells as normal until they decide to redeem any portion of them, after which they will only be able to redeem the portion of individual asset balances their Shell balance held claims on.
Description
The assimilators, being the “middleware” between a shell and all the external DeFi systems it interacts with, perform several external calls within their methods, as would be expected.
An example of such a contract is mainnetSUsdToASUsdAssimilator.sol (the contract can be found here).
The problem outlined in the title arises from the fact that Solidity automatically checks for the successful execution of the underlying message call (i.e., it bubbles up assertions and reverts) and, therefore, if any of these external systems changes in unexpected ways the call to the shell will revert itself.
This problem is immensely magnified by the fact that all the external methods in Loihi dealing with deposits, withdraws, and swaps rebalance the pool and, as a consequence, all of the assimilators for the reserve tokens get called at some point.
In summary, if any of the reserve tokens start, for some reason, refusing to complete a call to some of their methods, the whole protocol stops working, and the tokens are locked in forever (this is assuming the development team removes the safeApprove function from Loihi, v. https://github.com/ConsenSys/shell-protocol-audit-2020-06/issues/10).
Recommendation
There is no easy solution to this problem since calls to these external systems cannot simply be ignored. Shell needs successful responses from the reserve assimilators to be able to function properly.
One possible mitigation is to create a trustless mechanism based on repeated misbehavior by an external system to be able to remove a reserve asset from the pool.
Such a design could consist of an external function accessible to all actors that needs X confirmations over a period of Y blocks (or days, for that matter) with even spacing between them to be able to remove a reserve asset.
This means that no trust to the owners is implied (since this would require the extreme power to take user’s tokens) and still maintains the healthy option of being able to remove faulty tokens from the pool.
Now all functions in the Orchestrator revert on incorrect arguments.
All functions in Loihi in general revert on incorrect arguments.
Description
The functions should first check if the passed arguments are valid first. The checks-effects-interactions pattern should be implemented throughout the code.
These checks should include, but not be limited to:
uint should be larger than 0 when 0 is considered invalid
uint should be within constraints
int should be positive in some cases
length of arrays should match if more arrays are sent as arguments
addresses should not be 0x0
Examples
The function includeAsset does not do any checks before changing the contract state.
It calls transferByOrigin and we simplify this example and consider we have _o.ix == _t.ix
src/Loihi.sol:L181-L187
functiontransferByOrigin(address_origin,address_target,uint256_dline,uint256_mTAmt,uint256_oAmt,address_rcpnt)publicnotFrozennonReentrantreturns(uint256tAmt_){Assimilators.Assimilatormemory_o=shell.assimilators[_origin];Assimilators.Assimilatormemory_t=shell.assimilators[_target];// TODO: how to include min target amount
if(_o.ix==_t.ix)return_t.addr.outputNumeraire(_rcpnt,_o.addr.intakeRaw(_oAmt));
In which case it can call 2 functions on an assimilatior such as MainnetDaiToDaiAssimilator.
// transfers raw amonut of dai in, wraps it in cDai, returns numeraire amount
functionintakeRaw(uint256_amount)publicreturns(int128amount_,int128balance_){dai.transferFrom(msg.sender,address(this),_amount);amount_=_amount.divu(1e18);}
And its result is used in outputNumeraire that again does not have any checks.
Issue was partly addressed by the development team. However, the feature to add new assimilators is still present and that ultimately means that the administrators have power to run arbitrary bytecode.
Updated remediation response
Since the development team still hadn’t fully settled on a strategy for a mainnet launch, this was left as a residue even after the audit mitigation phase. However, at launch time, this issue was no longer present and all the assimilators are now defined at deploy-time, it is fully resolved.
Description
There are several functions in Loihi that give extreme powers to the shell administrator. The most dangerous set of those is the ones granting the capability to add assimilators.
Since assimilators are essentially a proxy architecture to delegate code to several different implementations of the same interface, the administrator could, intentionally or unintentionally, deploy malicious or faulty code in the implementation of an assimilator.
This means that the administrator is essentially totally trusted to not run code that, for example, drains the whole pool or locks up the users’ and LPs’ tokens.
In addition to these, the function safeApprove allows the administrator to move any of the tokens the contract holds to any address regardless of the balances any of the users have.
This can also be used by the owner as a backdoor to completely drain the contract.
Remove the safeApprove function and, instead, use a trustless escape-hatch mechanism like the one suggested in issue 6.1.
For the assimilator addition functions, our recommendation is that they are made completely internal, only callable in the constructor, at deploy time.
Even though this is not a big structural change (in fact, it reduces the attack surface), it is, indeed, a feature loss. However, this is the only way to make each shell a time-invariant system.
This would not only increase Shell’s security but also would greatly improve the trust the users have in the protocol since, after deployment, the code is now static and auditable.
They now implement the interface in “src/interfaces/IAssimilator.sol”.
Description
The Assimilators are one of the core components within the application. They are used to move the tokens and can be thought of as a “middleware” between the Shell Protocol application and any other supported tokens.
The methods attached to the assimilators are called throughout the application and they are a critical component of the whole system. Because of this fact, it is extremely important that they behave correctly.
A suggestion to restrict the possibility of errors when implementing them and when using them is to make all of the assimilators implement a unique specific interface. This way, any deviation would be immediately observed, right when the compilation happens.
Examples
Consider this example. The user calls swapByOrigin.
// takes raw cdai amount, transfers it in, calculates corresponding numeraire amount and returns it
functionintakeRaw(uint256_amount)publicreturns(int128amount_){boolsuccess=cdai.transferFrom(msg.sender,address(this),_amount);if(!success)revert("CDai/transferFrom-failed");uint256_rate=cdai.exchangeRateStored();_amount=(_amount*_rate)/1e18;cdai.redeemUnderlying(_amount);amount_=_amount.divu(1e18);}
However, with other implementations, the returns do not match. In the case of MainnetDaiToDaiAssimilator, it returns 2 values, which will make the Loihi contract work in this case but can misbehave in other cases, or even fail.
// transfers raw amonut of dai in, wraps it in cDai, returns numeraire amount
functionintakeRaw(uint256_amount)publicreturns(int128amount_,int128balance_){dai.transferFrom(msg.sender,address(this),_amount);amount_=_amount.divu(1e18);}
Making all the assimilators implement one unique interface will enforce the functions to look the same from the outside.
Recommendation
Create a unique interface for the assimilators and make all the contracts implement that interface.
All calls to compliant ERC20s now check for return booleans. Non compliant ERC20s are called with a function that checks for the success of the call.
Description
The assimilators in the codebase make heavy usage of both the transfer and transferFrom methods in the ERC20 standard.
Quoting the relevant parts of the specification of the standard:
Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The function SHOULD throw if the message caller’s account balance does not have enough tokens to spend.
The transferFrom method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD throw unless the _from account has deliberately authorized the sender of the message via some mechanism.
We can see that, even though it is suggested that ERC20-compliant tokens do throw on the lack of authorization from the sender or lack of funds to complete the transfer, the standard does not enforce it.
This means that, in order to make the system both more resilient and future-proof, code in each implementation of current and future assimilators should check for the return value of both transfer and transferFrom call instead of just relying on the external contract to revert execution.
The extent of this issue is only mitigated by the fact that new assets are only added by the shell administrator and could, therefore, be audited prior to their addition.
All retrieval of assimilators now check that the assimilators address is not the zeroth address.
Description
For every method that allows to selectively withdraw, deposit, or swap tokens in Loihi, the user is allowed to specify addresses for the assimilators of said tokens (by inputting the addresses of the tokens themselves).
The shell then performs a lookup on a mapping called assimilators inside its main structure and uses the result of that lookup to delegate call the assimilator deployed by the shell administrator.
However, there are no checks for prior instantiation of a specific, supported token, effectively meaning that we can do a lookup on an all-zeroed-out member of that mapping and delegate call execution to the zeroth address.
The only thing preventing execution from going forward in this unwanted fashion is the fact that the ABI decoder expects a certain return data size from the interface implemented in Assimilator.sol.
For example, the 32 bytes expected as a result of this call:
src/Assimilators.sol:L58-L66
functionviewNumeraireAmount(address_assim,uint256_amt)internalreturns(int128amt_){// amount_ = IAssimilator(_assim).viewNumeraireAmount(_amt); // for production
bytesmemorydata=abi.encodeWithSelector(iAsmltr.viewNumeraireAmount.selector,_amt);// for development
amt_=abi.decode(_assim.delegate(data),(int128));// for development
}
This is definitely an insufficient check since the interface for the assimilators might change in the future to include functions that have no return values.
Recommendation
Check for the prior instantiation of assimilators by including the following requirement:
The problem which was introduced is that unsafe_add ironically is not really unsafe, it is as safe as the original add function. It is, in fact, identical to the safe add function.
src/ABDKMath64x64.sol:L102-L113
/**
* Calculate x + y. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/functionadd(int128x,int128y)internalpurereturns(int128){int256result=int256(x)+y;require(result>=MIN_64x64&&result<=MAX_64x64);returnint128(result);}
src/ABDKMath64x64.sol:L115-L126
/**
* Calculate x + y. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @param y signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/functionunsafe_add(int128x,int128y)internalpurereturns(int128){int256result=int256(x)+y;require(result>=MIN_64x64&&result<=MAX_64x64);returnint128(result);}
Fortunately, unsafe_add is not used anywhere in the code.
However, unsafe_abs was changed from this:
src/ABDKMath64x64.sol:L322-L331
/**
* Calculate |x|. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/functionabs(int128x)internalpurereturns(int128){require(x!=MIN_64x64);returnx<0?-x:x;}
To this:
src/ABDKMath64x64.sol:L333-L341
/**
* Calculate |x|. Revert on overflow.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/functionunsafe_abs(int128x)internalpurereturns(int128){returnx<0?-x:x;}
The check that was removed, is actually an important check:
The problem is that for an int128 variable that is equal to -0x80000000000000000000000000000000, there is no absolute value within the constraints of int128.
Starting from int128 n = -0x80000000000000000000000000000000, the absolute value should be int128 abs_n = -n, however abs_n is equal to the initial value of n. The final value of abs_n is still -0x80000000000000000000000000000000. It’s still not a positive or zero value. The operation 0 - n wraps back to the same initial value.
Recommendation
Remove unused unsafe_* functions and try to find other ways of doing unsafe math (if it is fundamentally important) without changing existing, trusted, already audited code.
The repository contains a lot of contracts and libraries that are added in the same file as another contract or library.
Organizing the code in this manner makes it hard to navigate, develop and audit. It is a best practice to have each contract or library in its own file. The file also needs to bear the name of the hosted contract or library.
Throughout the repository, there is source code from the development stage that was used for debugging the functionality and was not removed.
This should not be present in the source code and even if they are used while functionality is developed, they should be removed after the functionality was implemented.
The failing tests are because we made minute changes to our present model (changes in applying the base fee - “epsilon”), so in a sense, rather than failing they just need updating. Many of them are also an artifact of architecting the tests in such a way that they can be run against arbitrary parameter sets - or in different “suites”.
Description
The role of the tests should be to make sure the application behaves properly. This should include positive tests (functionality that should be implemented) and negative tests (behavior stopped or limited by the application).
The test suite should pass 100% of the tests. After spending time with the development team, we managed to ask for the changes that allowed us to run the tests suite. This revealed that out of the 555 tests, 206 are failing. This staggering number does not allow us to check what the problem is and makes anybody running tests ignore them completely.
Tests should be an integral part of the codebase, and they should be considered as important (or even more important) than the code itself. One should be able to recreate the whole codebase by just having the tests.
Recommendation
Update tests in order for the whole of the test suite to pass.
Having commented out code increases the cognitive load on an already complex system. Also, it hides the important parts of the system that should get the proper attention, but that attention gets to be diluted.
There is no code that is important enough to be left commented out in a repository. Git branching should take care of having different code versions or diffs should show what was before.
If there is commented out code, this also has to be maintained; it will be out of date if other parts of the system are changed, and the tests will not pick that up.
The main problem is that commented code adds confusion with no real benefit. Code should be code, and comments should be comments.
Examples
Commented out code should be removed or dealt with in a separate branch that is later included in the master branch.
src/Assimilators.sol:L48-L56
functionviewRawAmount(address_assim,int128_amt)internalreturns(uint256amount_){// amount_ = IAssimilator(_assim).viewRawAmount(_amt); // for production
bytesmemorydata=abi.encodeWithSelector(iAsmltr.viewRawAmount.selector,_amt.abs());// for development
amount_=abi.decode(_assim.delegate(data),(uint256));// for development
}
src/Assimilators.sol:L58-L66
functionviewNumeraireAmount(address_assim,uint256_amt)internalreturns(int128amt_){// amount_ = IAssimilator(_assim).viewNumeraireAmount(_amt); // for production
bytesmemorydata=abi.encodeWithSelector(iAsmltr.viewNumeraireAmount.selector,_amt);// for development
amt_=abi.decode(_assim.delegate(data),(int128));// for development
}
src/Assimilators.sol:L58-L66
functionviewNumeraireAmount(address_assim,uint256_amt)internalreturns(int128amt_){// amount_ = IAssimilator(_assim).viewNumeraireAmount(_amt); // for production
bytesmemorydata=abi.encodeWithSelector(iAsmltr.viewNumeraireAmount.selector,_amt);// for development
amt_=abi.decode(_assim.delegate(data),(int128));// for development
}
But there is no check to see if the asset already exists in the list. Because the check was not done, shell.numeraires can contain multiple identical instances.
src/Controller.sol:L80
shell.numeraires.push(_numeraireAssimilator);
Recommendation
Check if the _numeraire already exists before invoking includeAsset.
This doesn’t seem feasible.
Checking how much was transferred to/from the contract would pose unacceptable gas costs. Because of these constraints, the value returned by the assimilator methods never touches the outside world. They are just converted into numeraire format and returned, so checking these values would not provide any previously unknown information.
Description
There are some cases where functions which return values are called throughout the source code but the return values are not processed, nor checked.
The returns should in principle be handled and checked for validity to provide more robustness to the code.
Examples
The function intakeNumeraire receives a number of tokens and returns how many tokens were transferred to the contract.
// transfers numeraire amount of dai in, wraps it in cDai, returns raw amount
functionintakeNumeraire(int128_amount)publicreturns(uint256amount_){// truncate stray decimals caused by conversion
amount_=_amount.mulu(1e18)/1e3*1e3;dai.transferFrom(msg.sender,address(this),amount_);}
Similarly, the function outputNumeraire receives a destination address and an amount of token for withdrawal and returns a number of transferred tokens to the specified address.
A sanity check can be done to make sure that more than 0 tokens were transferred to the contract.
unitintakeAmount=shell.numeraires[i].addr.intakeNumeraire(_shells.mul(shell.weights[i]));require(intakeAmount>0,"Must intake a positive number of tokens");
Recommendation
Handle all return values everywhere returns exist and add checks to make sure an expected value was returned.
If the return values are never used, consider not returning them at all.
This is the case for the version we used, solc 0.5.15. Versions 0.5.17 and 0.6.* do not require it.
Description
In Assimilators.sol the interface for the assimilators is implemented in a state variable constant as an interface to the zeroth address in order to make use of it’s selectors.
This pattern is unneeded since you can reference selectors by using the imported interface directly without any implementation. It hinders both gas costs and readability of the code.
Examples
Recommendation
Delete line 37 in Assimilators.sol and instead of getting selectors through:
src/Assimilators.sol:L62
bytesmemorydata=abi.encodeWithSelector(iAsmltr.viewNumeraireAmount.selector,_amt);// for development
In order to reduce the cognitive load on the auditors and developers alike, somehow-related functions should have coherent logic and interfaces. Both of the functions either need to have 2 arguments, with an implied error message passed to require, or both functions need to have 3 arguments, with an error message that can be specified.
Recommendation
Update the functions to be coherent with other related functions.
This is true for all aspects of the bonding curve.
Things that have been tested on Kovan with the frontend dapp but could use a unit test are things relevant to sending shell tokens - issuing approvals, transfers and transferfroms.
The adding of assets and assimilators are tested by proxy because they are dependencies for the entire behavior of the bonding surface.
For this release, we plan on having the assets and the assimilators frozen at launch, so there is not much to test regarding continuous updating/changing of assets and assimilators.
We have, however, considered allowing for the dynamic adjustment of weights in addition to parameters.
Description
Code coverage is a measure used to describe how much of the source code is executed during the automated test suite. A system with high code coverage, measured as lines of code executed, has a lower chance to contain undiscovered bugs.
The codebase does not have any information about the code coverage.
Recommendation
Make the test suite output code coverage and add more tests to handle the lines of code that are not touched by any tests.
This section contains some of the artifacts generated during our review by automated tools, the test suite, etc. If any issues or recommendations were identified by the output presented here, they have been addressed in the appropriate section above.
A.2.1 MythX
MythX is a security analysis API for Ethereum smart contracts. It performs multiple types of analysis, including fuzzing and symbolic execution, to detect many common vulnerability types. The tool was used for automated vulnerability discovery for all audited contracts and libraries. More details on MythX can be found at mythx.io.
The PDF report of the initial MythX vulnerability scan can be found here.
The PDF report for the followup MythX vulnerability scan, after code changes, can be found here.
A.2.2 Ethlint
Ethlint is an open source project for linting Solidity code. Only security-related issues were reviewed by the audit team.
Below is the raw output of the Ethlint vulnerability scan:
src/Assimilators.sol
17:0 warning "pragma solidity >0.4.13;" should be at the top of the file. pragma-on-top
23:60 warning Avoid using low-level function 'delegatecall'. security/no-low-level-calls
25:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/Controller.sol
39:4 warning Line exceeds the limit of 145 characters max-len
77:4 warning Line exceeds the limit of 145 characters max-len
src/Loihi.sol
181:4 warning Line exceeds the limit of 145 characters max-len
271:4 warning Line exceeds the limit of 145 characters max-len
345:32 error Only use indent of 16 spaces. indentation
433:4 warning Line exceeds the limit of 145 characters max-len
434:16 warning Avoid using 'block.timestamp'. security/no-block-members
526:4 warning Line exceeds the limit of 145 characters max-len
527:16 warning Avoid using 'block.timestamp'. security/no-block-members
573:23 error Only use indent of 8 spaces. indentation
596:95 warning Code contains empty block no-empty-blocks
600:116 warning Code contains empty block no-empty-blocks
604:101 warning Code contains empty block no-empty-blocks
608:101 warning Code contains empty block no-empty-blocks
612:106 warning Code contains empty block no-empty-blocks
616:72 warning Code contains empty block no-empty-blocks
624:88 warning Code contains empty block no-empty-blocks
645:23 error Variable 'returndata' is declared but never used. no-unused-vars
645:57 warning Avoid using low-level function 'call'. security/no-low-level-calls
src/LoihiRoot.sol
40:1 warning Line contains trailing whitespace no-trailing-whitespace
67:16 error Only use indent of 4 spaces. indentation
67:30 error Only use indent of 4 spaces. indentation
67:42 error Only use indent of 4 spaces. indentation
68:17 error Only use indent of 4 spaces. indentation
69:23 error Only use indent of 4 spaces. indentation
70:17 error Only use indent of 4 spaces. indentation
72:4 warning Line exceeds the limit of 145 characters max-len
73:20 error Only use indent of 8 spaces. indentation
73:34 error Only use indent of 8 spaces. indentation
73:48 error Only use indent of 8 spaces. indentation
74:22 error Only use indent of 8 spaces. indentation
75:22 error Only use indent of 8 spaces. indentation
76:22 error Only use indent of 8 spaces. indentation
src/Shells.sol
18:0 warning "pragma solidity >0.4.13;" should be at the top of the file. pragma-on-top
129:4 error "calculateTrade": Avoid assigning to function parameters. security/no-assign-params
129:4 error "calculateTrade": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/aaveResources/ILendingPool.sol
12:4 warning Line exceeds the limit of 145 characters max-len
14:4 warning Line contains trailing whitespace no-trailing-whitespace
src/assimilators/kovan/kovanASUsdAssimilator.sol
26:26 warning Code contains empty block no-empty-blocks
41:4 error "intakeNumeraire": Avoid assigning to function parameters. security/no-assign-params
55:4 error "outputNumeraire": Avoid assigning to function parameters. security/no-assign-params
88:4 warning Line contains trailing whitespace no-trailing-whitespace
94:8 warning Provide an error message for require() error-reason
src/assimilators/kovan/kovanAUsdtAssimilator.sol
13:26 warning Code contains empty block no-empty-blocks
28:4 error "intakeNumeraire": Avoid assigning to function parameters. security/no-assign-params
41:4 error "outputNumeraire": Avoid assigning to function parameters. security/no-assign-params
74:4 warning Line contains trailing whitespace no-trailing-whitespace
80:8 warning Provide an error message for require() error-reason
src/assimilators/kovan/kovanCDaiAssimilator.sol
20:26 warning Code contains empty block no-empty-blocks
85:4 warning Line contains trailing whitespace no-trailing-whitespace
91:8 warning Provide an error message for require() error-reason
src/assimilators/kovan/kovanCUsdcAssimilator.sol
21:26 warning Code contains empty block no-empty-blocks
24:4 warning Line contains trailing whitespace no-trailing-whitespace
29:4 warning Line contains trailing whitespace no-trailing-whitespace
31:4 error "intakeNumeraire": Avoid assigning to function parameters. security/no-assign-params
40:4 error "outputNumeraire": Avoid assigning to function parameters. security/no-assign-params
54:4 error "viewRawAmount": Avoid assigning to function parameters. security/no-assign-params
76:26 warning Single space should be either on both sides of '/' or not at all. operator-whitespace
76:41 warning There should be no whitespace or comments between argument and the comma following it. comma-whitespace
src/assimilators/kovan/kovanChaiAssimilator.sol
30:26 warning Code contains empty block no-empty-blocks
66:4 warning Line contains trailing whitespace no-trailing-whitespace
86:20 warning Avoid using 'now' (alias to 'block.timestamp'). security/no-block-members
91:20 warning Avoid using 'now' (alias to 'block.timestamp'). security/no-block-members
93:0 error Only use indent of 8 spaces. indentation
101:4 warning Line contains trailing whitespace no-trailing-whitespace
107:8 warning Provide an error message for require() error-reason
src/assimilators/kovan/kovanDaiAssimilator.sol
22:26 warning Code contains empty block no-empty-blocks
30:8 warning Line contains trailing whitespace no-trailing-whitespace
33:8 warning Line contains trailing whitespace no-trailing-whitespace
39:8 warning Line contains trailing whitespace no-trailing-whitespace
43:8 warning Line contains trailing whitespace no-trailing-whitespace
49:8 warning Line contains trailing whitespace no-trailing-whitespace
52:8 warning Line contains trailing whitespace no-trailing-whitespace
56:8 warning Line contains trailing whitespace no-trailing-whitespace
60:8 warning Line contains trailing whitespace no-trailing-whitespace
88:8 warning Line contains trailing whitespace no-trailing-whitespace
90:8 warning Line contains trailing whitespace no-trailing-whitespace
src/assimilators/kovan/kovanSUsdAssimilator.sol
23:26 warning Code contains empty block no-empty-blocks
45:4 error "intakeNumeraire": Avoid assigning to function parameters. security/no-assign-params
65:4 error "outputNumeraire": Avoid assigning to function parameters. security/no-assign-params
99:4 warning Line contains trailing whitespace no-trailing-whitespace
110:23 error Variable 'returndata' is declared but never used. no-unused-vars
110:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
src/assimilators/kovan/kovanUsdcAssimilator.sol
21:26 warning Code contains empty block no-empty-blocks
35:4 error "intakeNumeraire": Avoid assigning to function parameters. security/no-assign-params
48:4 error "outputNumeraire": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/kovan/kovanUsdtAssimilator.sol
23:26 warning Code contains empty block no-empty-blocks
45:4 error "intakeNumeraire": Avoid assigning to function parameters. security/no-assign-params
63:4 error "outputNumeraire": Avoid assigning to function parameters. security/no-assign-params
95:4 warning Line contains trailing whitespace no-trailing-whitespace
106:23 error Variable 'returndata' is declared but never used. no-unused-vars
106:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
src/assimilators/local/ausdtReserves/localUsdtToAUsdtAssimilator.sol
33:0 error Only use indent of 4 spaces. indentation
125:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
126:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/assimilators/local/cdaiReserves/localChaiToCDaiAssimilator.sol
69:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
117:4 error "outputRaw": Avoid assigning to function parameters. security/no-assign-params
145:4 error "viewNumeraireAmount": Avoid assigning to function parameters. security/no-assign-params
165:4 warning Line contains trailing whitespace no-trailing-whitespace
src/assimilators/local/cusdcReserves/localCUsdcToCUsdcAssimilator.sol
77:8 error Variable '_balanceBefore' is declared but never used. no-unused-vars
src/assimilators/local/cusdcReserves/localUsdcToCUsdcAssimilator.sol
115:8 warning Line contains trailing whitespace no-trailing-whitespace
src/assimilators/local/daiReserves/localCDaiToDaiAssimilator.sol
36:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
55:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/local/daiReserves/localChaiToDaiAssimilator.sol
69:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
87:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
123:4 error "outputRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
138:4 error "outputRaw": Avoid assigning to function parameters. security/no-assign-params
158:4 error "viewNumeraireAmount": Avoid assigning to function parameters. security/no-assign-params
176:4 error "viewNumeraireAmountAndBalance": Avoid assigning to function parameters. security/no-assign-params
179:8 warning Line contains trailing whitespace no-trailing-whitespace
src/assimilators/local/usdcReserves/localCUsdcToUsdcAssimilator.sol
37:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
56:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/local/usdtReserves/localUsdtToUsdtAssimilator.sol
30:4 warning Line contains trailing whitespace no-trailing-whitespace
136:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
137:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/assimilators/mainnet/asusdReserves/mainnetASusdToASusdAssimilator.sol
30:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/asusdReserves/mainnetSUsdToASUsdAssimilator.sol
38:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/ausdtReserves/mainnetAUsdtToAUsdtAssimilator.sol
32:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/ausdtReserves/mainnetUsdtToAUsdtAssimilator.sol
34:26 warning Code contains empty block no-empty-blocks
158:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
159:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/assimilators/mainnet/cdaiReserves/mainnetCDaiToCDaiAssimilator.sol
30:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/cdaiReserves/mainnetChaiToCDaiAssimilator.sol
39:26 warning Code contains empty block no-empty-blocks
42:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
59:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
110:4 error "outputRaw": Avoid assigning to function parameters. security/no-assign-params
125:4 error "outputRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/mainnet/cdaiReserves/mainnetDaiToCDaiAssimilator.sol
33:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/cusdcReserves/mainnetCUsdcToCUsdcAssimilator.sol
30:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/cusdcReserves/mainnetUsdcToCUsdcAssimilator.sol
36:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/daiReserves/mainnetCDaiToDaiAssimilator.sol
25:4 warning Line contains trailing whitespace no-trailing-whitespace
29:26 warning Code contains empty block no-empty-blocks
32:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
53:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/mainnet/daiReserves/mainnetChaiToDaiAssimilator.sol
31:26 warning Code contains empty block no-empty-blocks
65:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
83:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
119:4 error "outputRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
134:4 error "outputRaw": Avoid assigning to function parameters. security/no-assign-params
154:4 error "viewNumeraireAmount": Avoid assigning to function parameters. security/no-assign-params
172:4 error "viewNumeraireAmountAndBalance": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/mainnet/daiReserves/mainnetDaiToDaiAssimilator.sol
27:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/susdReserves/MainnetASusdToSUsdAssimilator.sol
32:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/susdReserves/MainnetSUsdToSUsdAssimilator.sol
27:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/usdcReserves/localCUsdcToUsdcAssimilator.sol
29:26 warning Code contains empty block no-empty-blocks
32:4 error "intakeRawAndGetBalance": Avoid assigning to function parameters. security/no-assign-params
55:4 error "intakeRaw": Avoid assigning to function parameters. security/no-assign-params
src/assimilators/mainnet/usdcReserves/localUsdcToUsdcAssimilator.sol
27:26 warning Code contains empty block no-empty-blocks
src/assimilators/mainnet/usdtReserves/localAUsdtToUsdtAssimilator.sol
33:26 warning Code contains empty block no-empty-blocks
164:4 warning Line contains trailing whitespace no-trailing-whitespace
174:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
175:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/assimilators/mainnet/usdtReserves/localUsdtToUsdtAssimilator.sol
27:26 warning Code contains empty block no-empty-blocks
134:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
135:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/test/continuities/suiteSix.t.sol
13:4 error Using 'l' for a variable name should be avoided. variable-declarations
144:1 warning Line contains trailing whitespace no-trailing-whitespace
src/test/debug.t.sol
20:4 error Using 'l' for a variable name should be avoided. variable-declarations
35:8 error Variable 'p3divu' is declared but never used. no-unused-vars
44:4 warning Line contains trailing whitespace no-trailing-whitespace
49:8 error Variable 'a64' is declared but never used. no-unused-vars
src/test/deposits/depositsTemplate.sol
18:4 error Using 'l' for a variable name should be avoided. variable-declarations
157:4 warning Line exceeds the limit of 145 characters max-len
431:4 warning Line exceeds the limit of 145 characters max-len
452:4 warning Line exceeds the limit of 145 characters max-len
454:8 error Variable 'startingShells' is declared but never used. no-unused-vars
853:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/deposits/suiteOne.t.sol
223:4 warning Line exceeds the limit of 145 characters max-len
231:4 warning Line exceeds the limit of 145 characters max-len
src/test/deposits/suiteTwo.t.sol
207:4 warning Line exceeds the limit of 145 characters max-len
215:4 warning Line exceeds the limit of 145 characters max-len
src/test/deposits/views/depositsViewsTemplate.sol
18:4 error Using 'l' for a variable name should be avoided. variable-declarations
157:4 warning Line exceeds the limit of 145 characters max-len
431:4 warning Line exceeds the limit of 145 characters max-len
452:4 warning Line exceeds the limit of 145 characters max-len
454:8 error Variable 'startingShells' is declared but never used. no-unused-vars
853:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/deposits/views/suiteOneViews.t.sol
87:4 warning Line exceeds the limit of 145 characters max-len
223:4 warning Line exceeds the limit of 145 characters max-len
231:4 warning Line exceeds the limit of 145 characters max-len
src/test/originSwaps/originSwapTemplate.sol
19:4 error Using 'l' for a variable name should be avoided. variable-declarations
158:4 warning Line contains trailing whitespace no-trailing-whitespace
465:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
485:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
500:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
515:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
src/test/originSwaps/suiteFive.t.sol
31:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/originSwaps/suiteTwo.t.sol
209:85 warning Visibility modifier "public" should come before other modifiers. visibility-first
src/test/originSwaps/views/originSwapViewsTemplate.sol
19:4 error Using 'l' for a variable name should be avoided. variable-declarations
158:4 warning Line contains trailing whitespace no-trailing-whitespace
465:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
485:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
500:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
515:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
src/test/setup/assimilators.sol
52:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/setup/loihi.sol
32:7 error Only use indent of 8 spaces. indentation
33:7 error Only use indent of 8 spaces. indentation
34:7 error Only use indent of 8 spaces. indentation
35:7 error Only use indent of 8 spaces. indentation
36:7 error Only use indent of 8 spaces. indentation
38:7 error Only use indent of 8 spaces. indentation
152:35 error Only use indent of 8 spaces. indentation
153:35 error Only use indent of 8 spaces. indentation
154:36 error Only use indent of 8 spaces. indentation
155:36 error Only use indent of 8 spaces. indentation
156:36 error Only use indent of 8 spaces. indentation
169:35 error Only use indent of 8 spaces. indentation
170:35 error Only use indent of 8 spaces. indentation
171:36 error Only use indent of 8 spaces. indentation
172:36 error Only use indent of 8 spaces. indentation
173:36 error Only use indent of 8 spaces. indentation
src/test/setup/methods.sol
78:59 warning Avoid using low-level function 'delegatecall'. security/no-low-level-calls
80:8 error Avoid using Inline Assembly. security/no-inline-assembly
302:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
331:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
356:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
377:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
596:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
625:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
650:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
671:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
699:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
728:39 warning Avoid using low-level function 'call'. security/no-low-level-calls
src/test/setup/mocks/atoken.sol
41:56 warning Avoid using low-level function 'call'. security/no-low-level-calls
42:8 error Avoid using Inline Assembly. security/no-inline-assembly
src/test/setup/mocks/cdai.sol
17:4 warning Line contains trailing whitespace no-trailing-whitespace
30:8 error Variable 'balance' is declared but never used. no-unused-vars
src/test/setup/mocks/chai.sol
47:8 warning Provide an error message for require() error-reason
51:8 warning Provide an error message for require() error-reason
55:8 warning Provide an error message for require() error-reason
src/test/setup/mocks/erc20.sol
10:6 error Only use indent of 8 spaces. indentation
src/test/setup/mocks/erc20NoBool.sol
8:6 error Only use indent of 4 spaces. indentation
10:0 error Only use indent of 4 spaces. indentation
src/test/setup/mocks/pot.sol
10:26 warning Code contains empty block no-empty-blocks
15:4 warning Line contains trailing whitespace no-trailing-whitespace
21:15 warning Avoid using 'now' (alias to 'block.timestamp'). security/no-block-members
src/test/setup/setup.sol
57:8 warning Line contains trailing whitespace no-trailing-whitespace
121:8 warning Line contains trailing whitespace no-trailing-whitespace
167:8 warning Line contains trailing whitespace no-trailing-whitespace
src/test/targetSwaps/suiteFive.t.sol
31:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/targetSwaps/targetSwapTemplate.sol
19:4 error Using 'l' for a variable name should be avoided. variable-declarations
458:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
478:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
493:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
508:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
661:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/targetSwaps/views/targetSwapViewsTemplate.sol
19:4 error Using 'l' for a variable name should be avoided. variable-declarations
458:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
478:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
493:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
508:35 warning Avoid using low-level function 'call'. security/no-low-level-calls
661:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/testAssimilators.t.sol
31:26 warning Code contains empty block no-empty-blocks
34:24 error Variable 'returndata' is declared but never used. no-unused-vars
34:59 warning Avoid using low-level function 'call'. security/no-low-level-calls
326:64 warning Code contains empty block no-empty-blocks
350:67 warning 'undefined': The first argument must not be preceded by any whitespace or comments (only '('). function-whitespace
350:92 warning 'undefined': The last argument must not be succeeded by any whitespace or comments (only ')'). function-whitespace
384:53 warning Code contains empty block no-empty-blocks
388:54 warning Code contains empty block no-empty-blocks
392:53 warning Code contains empty block no-empty-blocks
396:54 warning Code contains empty block no-empty-blocks
423:50 warning Code contains empty block no-empty-blocks
427:51 warning Code contains empty block no-empty-blocks
431:51 warning Code contains empty block no-empty-blocks
435:52 warning Code contains empty block no-empty-blocks
439:53 warning Code contains empty block no-empty-blocks
443:52 warning Code contains empty block no-empty-blocks
447:53 warning Code contains empty block no-empty-blocks
451:52 warning Code contains empty block no-empty-blocks
455:53 warning Code contains empty block no-empty-blocks
src/test/withdraws/suiteOne.t.sol
183:4 warning Line exceeds the limit of 145 characters max-len
src/test/withdraws/suiteTwo.t.sol
169:4 warning Line exceeds the limit of 145 characters max-len
261:4 warning Line contains trailing whitespace no-trailing-whitespace
src/test/withdraws/views/suiteOneViews.t.sol
183:4 warning Line exceeds the limit of 145 characters max-len
src/test/withdraws/views/withdrawViewsTemplate.sol
18:4 error Using 'l' for a variable name should be avoided. variable-declarations
300:8 error Variable '_startingShells' is declared but never used. no-unused-vars
383:4 warning Line exceeds the limit of 145 characters max-len
478:8 error Variable 'startingShells' is declared but never used. no-unused-vars
src/test/withdraws/withdrawTemplate.sol
18:4 error Using 'l' for a variable name should be avoided. variable-declarations
300:8 error Variable '_startingShells' is declared but never used. no-unused-vars
383:4 warning Line exceeds the limit of 145 characters max-len
478:8 error Variable 'startingShells' is declared but never used. no-unused-vars
✖ 109 errors, 177 warnings found.
A.2.3 Surya
Surya is a utility tool for smart contract systems. It provides a number of visual outputs and information about the structure of smart contracts. It also supports querying the function call graph in multiple ways to aid in the manual inspection and control flow analysis of contracts.
Below is a complete list of functions with their visibility and modifiers:
ConsenSys Diligence (“CD”) typically receives compensation from one or more clients (the “Clients”) for performing the analysis contained in these reports (the “Reports”). The Reports may be distributed through other means, including via ConsenSys publications and other distributions.
The Reports are not an endorsement or indictment of any particular project or team, and the Reports do not guarantee the security of any particular project. This Report does not consider, and should not be interpreted as considering or having any bearing on, the potential economics of a token, token sale or any other product, service or other asset. Cryptographic tokens are emergent technologies and carry with them high levels of technical risk and uncertainty. No Report provides any warranty or representation to any Third-Party in any respect, including regarding the bugfree nature of code, the business model or proprietors of any such business model, and the legal compliance of any such business. No third party should rely on the Reports in any way, including for the purpose of making any decisions to buy or sell any token, product, service or other asset. Specifically, for the avoidance of doubt, this Report does not constitute investment advice, is not intended to be relied upon as investment advice, is not an endorsement of this project or team, and it is not a guarantee as to the absolute security of the project. CD owes no duty to any Third-Party by virtue of publishing these Reports.
PURPOSE OF REPORTS The Reports and the analysis described therein are created solely for Clients and published with their consent. The scope of our review is limited to a review of Solidity code and only the Solidity code we note as being within the scope of our review within this report. The Solidity language itself remains under development and is subject to unknown risks and flaws. The review does not extend to the compiler layer, or any other areas beyond Solidity that could present security risks. Cryptographic tokens are emergent technologies and carry with them high levels of technical risk and uncertainty.
CD makes the Reports available to parties other than the Clients (i.e., “third parties”) – on its website. CD hopes that by making these analyses publicly available, it can help the blockchain ecosystem develop technical best practices in this rapidly evolving area of innovation.
LINKS TO OTHER WEB SITES FROM THIS WEB SITE You may, through hypertext or other computer links, gain access to web sites operated by persons other than ConsenSys and CD. Such hyperlinks are provided for your reference and convenience only, and are the exclusive responsibility of such web sites’ owners. You agree that ConsenSys and CD are not responsible for the content or operation of such Web sites, and that ConsenSys and CD shall have no liability to you or any other person or entity for the use of third party Web sites. Except as described below, a hyperlink from this web Site to another web site does not imply or mean that ConsenSys and CD endorses the content on that Web site or the operator or operations of that site. You are solely responsible for determining the extent to which you may use any content at any other web sites to which you link from the Reports. ConsenSys and CD assumes no responsibility for the use of third party software on the Web Site and shall have no liability whatsoever to any person or entity for the accuracy or completeness of any outcome generated by such software.
TIMELINESS OF CONTENT The content contained in the Reports is current as of the date appearing on the Report and is subject to change without notice. Unless indicated otherwise, by ConsenSys and CD.