Platform: Code4rena
Start Date: 30/09/2021
Pot Size: $75,000 ETH
Total HM: 9
Participants: 15
Period: 7 days
Judge: 0xean
Total Solo HM: 2
Id: 39
League: ETH
Rank: 7/15
Findings: 4
Award: $2,892.70
π Selected for report: 3
π Solo Findings: 0
leastwood
There are a number of functions which interact with uToken
in Swivel.sol
but do not check the return value of the associated transfer()
, approve()
or transferFrom()
call. As a result, if a market is created using a non-standard token, a failed call will be reflected with a boolean
return value instead of a revert. As a result, a failed transfer may be treated as a successful action.
https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L103 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L104 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L109 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L137 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L139 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L144 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L253 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L255 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L280 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L283 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L313 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L315 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L346 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L347 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L416 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L419 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L396
Manual code review
Consider using Openzeppelin's SafeERC20
library to ensure all approve()
, transfer()
and transferFrom()
are handled correctly.
0.2678 ETH - $793.82
leastwood
The onlyAdmin
role points to an EOA account managed by the Swivel team. Withdrawals from the Swivel protocol have to be scheduled by the onlyAdmin
role, ensuring that users have sufficient time to withdraw, if required.
The setFee()
function in Swivel.sol
does not have the same timelock, as seen in Swivel.withdraw()
, enabling the onlyAdmin
role to extract users' funds by intentionally updating the fenominator
array in an instantaneous fashion. However, it is more likely that the admin role unintentionally makes an update while other transactions are in flight, resulting in unexpected fees for all users that are frontrun. This may also result in a number of reverted transactions due to insufficient uToken
approvals.
Similarly, the setSwivelAddress()
function in MarketPlace.sol
can be used by the onlyAdmin
role to drain the protocol by directly stealing funds or through ZcToken
/nToken
dilution. This is made possible via a sort of privilege escalation whereby the onlyAdmin
role is able to take control of the onlySwivel
role, giving access to any of the role's restricted functions.
https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L374-L397 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L402-L405 https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L42-L45
Consider the following attack vector:
onlyAdmin
account calls setSwivelAddress()
and updates the onlySwivel
role to point to their account.onlyAdmin
account is able to call any function with the onlySwivel
role, effectively giving them the ability to transfer vault holdings, mint and burn ZcTokens
and add/remove nTokens
.mintZcTokenAddingNotional
, burnZcTokenRemovingNotional
, redeemZcToken
, redeemVaultInterest
, custodialInitiate
, custodialExit
, p2pZcTokenExchange
, p2pVaultExchange
, and transferVaultNotionalFee
.ZcToken
and nToken
holdings, the admin account is able to claim the underlying collateral locked up on the Compound protocol.Manual code review
Consider adding a similar timelock to the setFee()
and setSwivelAddress()
functions as seen in Swivel.withdraw()
. This should provide users with sufficient time to exit the protocol if for whatever reason the onlyAdmin
role attempts to maliciously update the protocol or users want to avoid being frontrun.
#0 - JTraversa
2021-10-07T17:53:37Z
We will consider / likely add a timelock on the setFee function as well as ensure swivel can only be set once.
That said, this is most definitely not a high severity issue, and I would place it as low-severity given the gated admin access, medium at a maximum.
#1 - 0xean
2021-10-15T00:15:49Z
Assigning to medium risk given the inconsistent application of timelocks and the need to add guards for a single call to set the swivel address.
Given the incredible power of the admin address, the swivel team should strongly consider using a multi-sig solution to secure this address.
#2 - 0xean
2021-10-16T22:49:27Z
close as dupe of #95
π Selected for report: leastwood
leastwood
A single Swivel.sol
contract is used as an interface for users to lend and borrow tokens. Only supported markets, created by the admin are accessable by Swivel's users. Principal tokens are deposited into Compound and are withdrawable only by the Swivel.sol
contract. An issue with this contract will potentially affect all markets as the single contract controls deposited assets for all markets.
https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol
Manual code review
Consider isolating markets such that potential vulnerabilities in the future do not create a widespread contagion affecting all markets.
#0 - JTraversa
2021-10-07T17:56:58Z
This poses no immediate issue and markets are technically isolated from one another, just not from potentially different Swivel.sol's.
That said, future integrations, aave, yearn, etc., all would also have their own isolated Swivel.sol's using the current architecture.
#1 - 0xean
2021-10-15T00:18:51Z
Given the fact that if there is a vulnerability in a single Swivel.sol they will all most likely have the same vulnerability I do not think this qualifies as high-risk.
There may be some benefit from segregating swivel's so they only have access to a certain portion of funds ( a single ERC20), but its probably minimal. Assigning as low-risk.
leastwood
The initiate()
and exit()
function iterate over an array of orders. The length of these inputs are not validated to ensure they are of the same size, potentially reverting after consuming considerable gas. Similarly, the amount to fill an order by is not checked to be > 0
and therefore will emit an event when no change has been made to the protocol. It is also possible for the contract to be put in an unexpected state during deployment, incurring additional costs upon redeployment.
https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L55 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L209 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Swivel.sol#L42-L47 https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L42-L45 https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L53-L70 https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L75-L91 https://github.com/Swivel-Finance/gost/blob/v2/test/vaulttracker/VaultTracker.sol#L26-L31
Manual code review Slither
Consider checking address
inputs if they are equal to the zero address and if any amount inputs are > 0
.
leastwood
There are a number of open TODOs throughout Swivel's smart contract suite, potentially highlighting issues in the underlying architecture.
https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L3 https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L62 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Sig.sol#L41 https://github.com/Swivel-Finance/gost/blob/v2/test/swivel/Hash.sol#L93
Manual code review.
Consider updating the codebase such that these TODOs have been compelted before deploying v2.
π Selected for report: leastwood
0.0941 ETH - $278.98
leastwood
The owner is able to deploy new markets by interacting with MarketPlace.sol
and calling createMarket()
. A corresponding ZcToken
and vault are deployed at a fixed cost which is rather high due to the associated bytecode storage costs. Openzeppelin's Clones library offers a cheap alternative to standard deployment setups by cloning the contract functionality while still utilising the same bytecode. This is made possible by delegating all calls to a base contract
https://github.com/Swivel-Finance/gost/blob/v2/test/marketplace/MarketPlace.sol#L53-L70 https://docs.openzeppelin.com/contracts/3.x/api/proxy#minimal_clones https://eips.ethereum.org/EIPS/eip-1167
Manual code review
Consider utilising Openzeppelin's Clones library to considerably reduce market deployment costs.
#0 - JTraversa
2021-10-07T17:30:21Z
Will review!