✨ New!✨  C4 Cosmos leagueRead more »

Contest ran May 27June 2

7 day contest

Yield contest

Fixed-rate borrowing and lending on Ethereum

$100,000 USDC

Total Awards

Yield contest details

Contest scoping

Yield v2 is a collateralized debt engine paired with a custom automated market maker.

The product offered by Yield v2 is fixed rate borrowing and lending.

  • Borrowing: Users deposit collateral to borrow fyTokens, which are immediately sold in YieldSpace for underlying. The combined effect is collateralized borrowing of underlying at a fixed rate, if they maintain ther debt until maturity.
  • Lending: Users sell underlying in YieldSpace to buy fyToken. They are effectively lending underlying at a fixed rate if they hold the fyToken to maturity and redeem them.
  • Liquidity Providing: The YieldSpace AMM requires liquidity, that Liquidity Providers add in exchange for fees generated by the use of YieldSpace by borrowers and lenders.

Smart Contracts

All the contracts in this section are to be reviewed. Any contracts not in this list are to be ignored.

Cast*.sol (9 sloc each)

The Cast*.sol libraries convert safely between different types.

WDiv.sol (9 sloc)

Fixed point division.

WDivUp.sol (11 sloc)

Fixed point division, rounding up.

WMul.sol (9 sloc)

Fixed point multiplication.

ERC20.sol (84 sloc)

Custom implementation of the ERC20 specifications. Differs from ERC20 standard on when allowances are required and decreased, same as DS-Token.sol.

ERC20Permit.sol (67 sloc)

Extension of ERC20 to accept ERC2612 off-chain approvals.

SafeERC20Namer.sol (91 sloc)

Produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32

TransferHelper.sol (29 sloc)

Adapted from Uniswap & BoringSolidity. Safe transferring of ERC20 tokens and Ether, regardless of reverts or return values.

MinimalTransferHelper.sol (29 sloc)

Adapted from Uniswap & BoringSolidity. Safe transferring of ERC20 tokens, regardless of reverts or return values.

AddressStringUtil.sol (31 sloc)

String utilities.

RevertMsgExtractor.sol (19 sloc)

Extractor or revert messages from return data.

AccessControl.sol (226 sloc)

Access control contract adapted from OpenZeppelin's AccessControl.sol. A role exists for each function in a contract, and if the auth modifier is present in a function, access must have been granted by the root account. The privileged account can grant and revoke roles, as well as root privileges. Root can lock functions, disabling any further changes in their access control, except for existing users renouncing to granted roles.

Ownable.sol (21 sloc)

Contract to create a single privileged role that can be held by a single address

ChainlinkMultiOracle.sol (89 sloc)

Calls Chainlink aggregators to return the value of an asset amount. Two contracts deployed, one for 18 and one for 8 decimals.

UniswapV3Oracle.sol (92 sloc)

Calls Uniswap V3 pools to return the value of an asset amount. One contract deployed.

CompoundMultiOracle.sol (60 sloc)

Calls Compound cTokens to return the borrowing and lending rate. One contract deployed.

Join.sol (121 sloc)

Asset holding. Only privileged accounts or contracts can move assets out of the Join, or ask the Join to take assets. Can serve ERC3156 flash loans. One contract per asset deployed.

JoinFactory.sol (67 sloc)

Deployment of Joins. One contract.

FYToken.sol (204 sloc)

ERC20 zero coupon bond, redeemable at maturity for underlying. Calls Join.sol to obtain funds to serve redemptions, and an Oracle to determine the savings rate, which will be applied to redemptions as well. Can be flash minted with no fees following the ERC3156 standard. Numerous contracts deployed.

Cauldron.sol (416 sloc)

Accounting system for Yield v2. The only external dependencies are towards rate oracles and spot oracles. All transactional functions require privileged access. The main function besides accounting is to reveal whether a vault is collateralized using the ink * price * ratio >= art * accrual * rate formula. One contract deployed.

Ladle.sol (495 sloc)

Routing and asset management for Yield v2. It has considerable privileges:

  • Moves assets in and out of the Joins
  • Mints and burns fyToken
  • Updates accounting in the Cauldron
  • Collects approvals from users for transfers to pools and fyToken
  • Calls YieldSpace functions It also implements a batching system to group several calls together.

LadleStorage.sol (44 sloc)

Storage variables for Ladle, so that Modules can inherit them and align their storage with the Ladle.

Wand.sol (117)

Governance router. The Wand bundles governance calls into governance actions such as adding an asset or a series.

Witch.sol (73 sloc)

Liquidations engine, same implementation from Yield v1, refactored. Calls the Ladle to move assets, and the Cauldron to obtain and release control of undercollateralized vaults.

Math64x64.sol (693 lines)

ABDK's Math64x64, upgraded to 0.8. Very complex math library, for which only a series of changes were made to use the 0.8 compiler.

YieldMath.sol (507 lines)

YieldSpace AMM curve implementation. Same curve implementation from Yield v1. It was refactored to make the math clearer. Uses Math64x64.sol.

Pool.sol (613 sloc)

YieldSpace AMM implementation. Refactored from Yield v1, to add a TWAR oracle, single-asset mint and burn, and remove all transferFrom in favour of keeping track of balances.

PoolFactory.sol (77 sloc)

Deployment of YieldSpace Pools using CREATE2.

PoolRouter.sol (142 sloc)

Batching of calls to Pools, along with wrapping/unwrapping of Ether, management of off-chain approvals, and transfers of tokens from users to pools to kickstart transactions.

Areas of concern

The Cauldron is the only contract in the Yield Protocol that is not replaceable, and only for that reason any bug in it would be a cause for serious concern.

The Ladle is a complex contract, with wide-ranging powers over the protocol. It is the riskiest contract, and at the same time the one that has more room for a bug to hide. It is of the highest importance to find as many bugs as we can in it.

The mechanism by which the Witch settles debt is not elegant, specifically in using settle through the Ladle to move assets. It is done this way to avoid governance overhead (and possibility of errors) in giving permission to the Witch to move tokens.

The access control for the Yield Protocol is based on AccessControl.sol, which is a modification of the OpenZeppelin contract of the same name. A bug in this contract would be most likely fatal.

The batching mechanism in Ladle is complex, and therefore risky. So is the vault caching mechanism.

The _mintInternal function in Pool.sol serves all possible ways of adding liquidity. It is complex, in pathways and in math, and therefore it is an area of concern.

The Math64x64.sol and YieldMath.sol contracts are very complex, but they have been battle tested in Yield v1. For Yield v2 we just refactored them to make them easier to follow, and upgraded them to solidity 0.8. They are not an area of concern, but you are welcome to try to find any bugs in them.

Testnet deployment

A working instance of the Yield v2 Protocol has been deployed to Kovan for this contest. Feel free to poke around, but if you are going to do something that might have a large impact on the contracts, please do so on a fork so that we don't have to redeploy them. All contracts have been verified on Etherscan.

All the contracts external to Yield have been mocked. In particular, there are mock instances for DAI, USDC, WETH9 and a vanilla ERC20 called TST. These have an open mint function that anyone can call to receive tokens. There are also mock contracts for what would be Chainlink aggregators and Compound cTokens, used as spot price and rate/chi oracles.

DAI and USDC have been configured as underlying. DAI, USDC, ETH and TST have been configured as collaterals. For each underlying we have deployed two series.

Mocks Address
DAI [DAI, '0xA03ae0f02131830d3571dBCD6056BD64939264D0'],
USDC [USDC, '0xA48EAd4ad4F29154fD2b1E98f36052547FA1A3Bf'],
ETH [ETH, '0xDd848d3DBCFAB61bE5B52A53AB3218F19038eD80'],
TST [TST, '0x3e5e276616BD400fFcD6e2333B0d324e0a7D3167'],
rate source for DAI [DAI, '0x61382858544B5406B58AEE565D5ba8747902986C'],
rate source for USDC [USDC, '0x8c62d6a12b2D4B125240d61554D85C23515a8114'],
chi source for DAI [DAI, '0x24331D91d9AF5ba016C34Da73DB7264f290c55Ce'],
chi source for USDC [USDC, '0x1F6165Df6e82F4DA9211c1f4861704Ff1De55eBc'],
spot source for DAI/USDC [USDC, '0x30bB694F8d6f86b84E12b7b15c8630efA262E6e3'],
spot source for DAI/ETH [ETH, '0x50e87B5A48CE4b2aB10014312ea65aE2d649d19B'],
spot source for DAI/TST [TST, '0xF1BD9A0AB8C517B590AA39d1C6D46133Baa662df'],
spot source for USDC/DAI [DAI, '0x317a9d49f3DbdFb7b76492EAA26156C29D3A8927'],
spot source for USDC/ETH [ETH, '0xe99CC1219B927067183e93Ba77bc80a007A76cb6'],
spot source for USDC/TST [TST, '0xDa64606a3CB5A4410DF4918Fe35cb89D432A15e6'],
Protocol Address
Cauldron [Cauldron, '0xDA0e6b29405bc6DB9E3414137B716f92a3A23D4e],
Ladle [Ladle, '0x6955612C6A972Af16c51764F0756BED8BA504FaB],
Witch [Witch, '0x3813A7153D8F46A753928Ddd36c91F085dD30b53],
Compound MultiOracle [CompoundMultiOracle, '0xb3AAa9D8EECa58DDb7Ee810EC26B9B8F2815CCcA],
Chainlink MultiOracle [ChainlinkMultiOracle, '0xF634dD49Fd6B06B92dFDa4cF70c1F1ed15453269],
Join Factory [JoinFactory, '0x59a80F7d9b5e9d93De14140062778a0d5a4Dee22],
YieldMath [YieldMath, '0x94578917e07eDc53854b2CEbe57d2818ea69976B'],
SafeERC20Namer [SafeERC20Namer, '0xE9AaA1034057Dbf78e6f4366a6792263DA58433F'],
Pool Factory [PoolFactory, '0xA554da08CfB85f22fa2a4c7c0A5E7305E7249459'],
Pool Router [PoolRouter, '0x0575A2221cb3d524CE105089Eb05BD3C7f685E14'],
Wand [Wand, '0x3Cf497A383a4a9D905F76351B9E9620a113eD6E3],
Joins, FYTokens, Pools Address
Join for DAI [DAIJoin, '0xcFe26861baCa98c5a4ECf7923B290894D3aDB208],
Join for USDC [USDCJoin, '0x0286Fe190eF0cC0ca10f2E74929c9dC4CC0E61Ef],
Join for ETH [ETHJoin, '0x08e5fad1Ce1326c4f75C061494009fE3336A5385],
Join for TST [TSTJoin, '0xE343E0b1960A7536749BfDca3eaa866D0d0a953E],
FYToken DAI1 [DAI1, '0x189905c0f51Af30aeEd19e4287231a316E77FAd4'],
FYToken DAI2 [DAI2, '0xee2dA60685648f2F3D0354dcf59b64FAe08db1B5'],
FYToken USDC1 [USDC1, '0x5E08D166092B5fdF281304fCF7D81DD470BF074b'],
FYToken USDC2 [USDC2, '0xFC1C50C0eBf217824c379ECB996453074c0ab6Db'],
Pool for DAI and DAI1 [DAI1Pool, '0x97412638C270874f9e9fE539e3efAAf49B1d63F7'],
Pool for DAI and DAI2 [DAI2Pool, '0x46f41601456EF07e92Fa28d377b62562166F6319'],
Pool for USDC and USDC1 [USDC1Pool, '0xa6c8e20D2802a11aCb0faD6273A436A8aDaf8aC9'],
Pool for USDC and USDC2 [USDC2Pool, '0x26A0BeD6eFFF9C13eE9564A3153f73Dab028C0f8'],