Timeswap contest - sirhashalot's results

Like Uniswap, but for lending & borrowing.

General Information

Platform: Code4rena

Start Date: 04/01/2022

Pot Size: $75,000 USDC

Total HM: 17

Participants: 33

Period: 7 days

Judge: 0xean

Total Solo HM: 14

Id: 74

League: ETH

Timeswap

Findings Distribution

Researcher Performance

Rank: 3/33

Findings: 6

Award: $7,476.43

🌟 Selected for report: 6

🚀 Solo Findings: 4

Findings Information

🌟 Selected for report: sirhashalot

Labels

bug
2 (Med Risk)
sponsor acknowledged

Awards

1703.8454 USDC - $1,703.85

External Links

Handle

sirhashalot

Vulnerability details

Impact

The CollateralizedDebt.sol contract is a ERC721 token. It has a mint() function, which uses the underlying safeMint() function to create an ERC721 token representing a collateral position. The burn() function in CollateralizedDebt.sol should reverse the actions of mint() by burning the ERC721 token, but the ERC721 _burn() function is never called. This means a user can continue to hold their ERC721 token representing their position after receiving their funds. This is unlike the burn() function found in Bond.sol, Insurance.sol, and Liquidity.sol, which all call the _burn() function (though note the _burn() function in these other Timeswap Convenience contracts is the ERC20 _burn()).

Proof of Concept

The problematic burn() function is found in CollareralizedDebt.sol https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Convenience/contracts/CollateralizedDebt.sol#L80-L88

Compare this function to the burn() functions defined in the other Timeswap Convenience contracts, which contain calls to _burn()

Include the following line in the burn() function _burn(id);

#0 - Mathepreneur

2022-01-15T22:39:08Z

If decided not to burn the ERC721 token at all. The burn in this context is burning the debt and collateral locked balance in the ERC721 token.

Findings Information

🌟 Selected for report: sirhashalot

Labels

bug
2 (Med Risk)
resolved
sponsor confirmed

Awards

1703.8454 USDC - $1,703.85

External Links

Handle

sirhashalot

Vulnerability details

Impact

The safeDecimals() function, found in the SafeMetadata.sol contract and called in 3 different Timeswap Convenience contracts, can cause a revert. This is because the safeDecimals function attempts to use abi.decode to return a uint8 when data.length >= 32. However, a data.length value greater than 32 will cause abi.decode to revert.

A similar issue was found in a previoud code4rena contest: https://github.com/code-423n4/2021-05-nftx-findings/issues/46

Proof of Concept

The root cause is line 28 of the safeDecimals() function in SafeMetadata.sol

The following link shows the safeDecimals() function in the BoringCrypto library, which might be where this code was borrowed from, uses the strict equality check data.length == 32 https://github.com/boringcrypto/BoringSolidity/blob/ccb743d4c3363ca37491b87c6c9b24b1f5fa25dc/contracts/libraries/BoringERC20.sol#L54

safeDecimals() is used in multiple functions such as

Modify the safeDecimals() function to change >= 32 to == 32 like this if (success && data.length == 32) return abi.decode(data, (uint8));

Findings Information

🌟 Selected for report: sirhashalot

Labels

bug
2 (Med Risk)
resolved
sponsor confirmed

Awards

1703.8454 USDC - $1,703.85

External Links

Handle

sirhashalot

Vulnerability details

Impact

The safeName() function, found in the SafeMetadata.sol contract and called in 4 Timeswap Convenience contracts in the name() functions, can cause a revert. This could make the 4 contracts not compliant with the ERC20 standard for certain asset pairs, because the name() function should return a string and not revert.

The root cause of the issue is that the safeName() function assumes the return type of any ERC20 token to be a string. If the return value is not a string, abi.decode() will revert, and this will cause the name() functions in the Timeswap ERC20 contracts to revert. There are some tokens that aren't compliant, such as Sai from Maker, which returns a bytes32 value: https://kauri.io/#single/dai-token-guide-for-developers/#token-info

Because this is known to cause issues with tokens that don't fully follow the ERC20 spec, the safeName() function in the BoringCrypto library has a fix for this. The BoringCrypto safeName() function is similar to the one in Timeswap but it has a returnDataToString() function that handles the case of a bytes32 return value for a token name: https://github.com/boringcrypto/BoringSolidity/blob/ccb743d4c3363ca37491b87c6c9b24b1f5fa25dc/contracts/libraries/BoringERC20.sol#L15-L47

Proof of Concept

The root cause is line 12 of the safeName() function in SafeMetadata.sol

The safeName() function is called in:

Use the BoringCrypto safeName() function code to handle the case of a bytes32 return value: https://github.com/boringcrypto/BoringSolidity/blob/ccb743d4c3363ca37491b87c6c9b24b1f5fa25dc/contracts/libraries/BoringERC20.sol#L15-L47

Findings Information

🌟 Selected for report: sirhashalot

Labels

bug
2 (Med Risk)
resolved
sponsor confirmed

Awards

1703.8454 USDC - $1,703.85

External Links

Handle

sirhashalot

Vulnerability details

Impact

The safeSymbol() function, found in the SafeMetadata.sol contract and called in 4 Timeswap Convenience contracts in the symbol() functions, can cause a revert. This could make the 4 contracts not compliant with the ERC20 standard for certain asset pairs, because the symbol() function should return a string and not revert.

The root cause of the issue is that the safeSymbol() function assumes the return type of any ERC20 token to be a string. If the return value is not a string, abi.decode() will revert, and this will cause the symbol() functions in the Timeswap ERC20 contracts to revert.

Because this is known to cause issues with tokens that don't fully follow the ERC20 spec, the safeSymbol() function in the BoringCrypto library has a fix for this. The BoringCrypto safeSymbol() function is similar to the one in Timeswap but it has a returnDataToString() function that handles the case of a bytes32 return value for a token name: https://github.com/boringcrypto/BoringSolidity/blob/ccb743d4c3363ca37491b87c6c9b24b1f5fa25dc/contracts/libraries/BoringERC20.sol#L15-L39

Proof of Concept

The root cause is line 20 of the safeSymbol() function in SafeMetadata.sol

The safeSymbol() function is called in:

Use the BoringCrypto safeSymbol() function code with the returnDataToString() parsing function to handle the case of a bytes32 return value: https://github.com/boringcrypto/BoringSolidity/blob/ccb743d4c3363ca37491b87c6c9b24b1f5fa25dc/contracts/libraries/BoringERC20.sol#L15-L39

Findings Information

🌟 Selected for report: sirhashalot

Labels

bug
1 (Low Risk)
sponsor acknowledged

Awards

567.9485 USDC - $567.95

External Links

Handle

sirhashalot

Vulnerability details

Impact

The IPair.sol interface has a comment incorrectly referencing UQ0.40 format for a uint16 return value. A UQ0.40 return value would require a uint40 or larger to be stored, so the comment should state UQ0.16 if a uint16 value is returned. https://en.wikipedia.org/wiki/Q_(number_format)#Characteristics

Proof of Concept

UQ0.40 is used incorrectly in two interface contracts: https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Core/contracts/interfaces/IPair.sol#L179 https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Core/contracts/interfaces/IFactory.sol#L39

Change UQ0.40 to UQ0.16 in the code comment

#0 - Mathepreneur

2022-01-15T22:45:49Z

The protocol fee is a very small number, since it gets multiplied by the duration of the transaction (seconds). So it is correct that it it UQ0.40, we only need the last 16 decimal places of the UQ0.40. I will add more explanation in the comments about this.

Findings Information

🌟 Selected for report: sirhashalot

Labels

bug
G (Gas Optimization)
sponsor acknowledged

Awards

93.0809 USDC - $93.08

External Links

Handle

sirhashalot

Vulnerability details

Impact

The createPair() function in TimeswapFactory.sol passes a salt value to when creating a new TimeswapPair. This salt is never used. Most likely the salt is an artifact from Uniswap V2 Core code. However, Uniswap v2 Core only uses a salt for the CREATE2 assembly call, which is not used in the Timeswap project. https://docs.uniswap.org/protocol/V2/guides/smart-contract-integration/getting-pair-addresses

Proof of Concept

The unnecessary salt is found at: https://github.com/code-423n4/2022-01-timeswap/blob/bf50d2a8bb93a5571f35f96bd74af54d9c92a210/Timeswap/Timeswap-V1-Core/contracts/TimeswapFactory.sol#L52

Don't pass a salt when creating a pair. The revised line of code should look like: pair = new TimeswapPair(asset, collateral, fee, protocolFee);

#0 - Mathepreneur

2022-01-18T16:24:54Z

It is currently not in used, but a different convenience contract or proxy contract might find this feature useful.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter