Giter Club home page Giter Club logo

hifi's Introduction

Hifi Coverage Status Yarn Styled with Prettier Commitizen Friendly license: BUSL1.1

Monorepo implementing the Hifi fixed-rate, fixed-term lending protocol. In-depth documentation is available at docs.hifi.finance.

Packages

The Hifi monorepo is maintained with yarn workspaces. Check out the README associated to each package for detailed usage instructions.

Public

Package Description
@hifi/amm Dedicated AMM for market-making hTokens
@hifi/flash-swap Flash swap implementations for liquidating underwater accounts
@hifi/protocol The core Hifi fixed-rate, fixed-term lending protocol
@hifi/proxy-target DSProxy target contract with stateless scripts

Private

Package Description
@hifi/constants Constants shared across Hifi packages
@hifi/errors Errors emitted in the Hifi protocol
@hifi/helpers Helper functions shared across Hifi packages

Contributing

Feel free to dive in! Open an issue, start a discussion or submit a PR. For any concerns or feedback, join us on Discord.

Pre Requisites

You will need the following software on your machine:

In addition, familiarity with Solidity, TypeScript and Hardhat is requisite.

Set Up

Set the version of Node to use:

$ nvm use

Install the dependencies:

$ yarn install

Build the packages:

$ yarn build

Create a .env file and follow the .env.example file to add the requisite environment variables, then run the watcher:

$ yarn watch:ts

Now you can start making changes.

Security

For security concerns, please email [email protected]. This repository is subject to the Hifi bug bounty program, per the terms defined here.

License

BUSL v1.1 © Mainframe Group Inc.

hifi's People

Contributors

clemlak avatar dleonard00 avatar hroussille avatar paulrberg avatar scorpion9979 avatar surbhiaudichya avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hifi's Issues

Avoid the endline when the function does not have params

Related to #70, but in this case the issue is that even if the function does not params, the generate docs still add an empty line in the code snippet:

function balanceSheet(
) external returns (contract IBalanceSheetV1)

Which should instead be:

function balanceSheet() external returns (contract IBalanceSheetV1)

Write READMEs

TODOs:

  • README in @hifi/amm
  • README in @hifi/constants
  • README in @hifi/deployers
  • README in @hifi/flash-swap
  • README in @hifi/helpers
  • README in @hifi/protocol
  • README in @hifi/proxy-target
  • Root-level README

[flash-swap] Increase test coverage by testing both token orderings

@scorpion9979's #39 made the HifiFlashSwapUniswapV2 contract more general purpose, but it came at a cost of reducing test coverage.

Capture d’écran 2021-09-15 à 20 37 19

Prior to the PR, we had a GodModeUniswapV2Pair contract that we were deploying via waffle.deployContract. Now we're using the original UniswapV2Pair contracts and we're deploying them via UniswapV2Factory. This makes it hard to know the order of the two pool tokens in advance (read about how ordering works here).

Two potential solutions, ordered by how good they would be:

  1. Use the hardhat_setCode JSON-RPC method to deploy the tokens at known addresses.
  2. Run a while loop and make new GodModeErc20 deployments until a new token ordering is found.

The 1st solution would be ideal, but hardhat_setCode has bugs: NomicFoundation/hardhat#1883. The 2nd is not nice because it involves non-determinism.

[flash-swap] Make the order of tokens when testing the "UniswapV2Pair" contract an invariant

As explained in #45, the order of the tokens when deploying the UniswapV2Pair contract via UniswapV2Factory depends upon the MNEMONIC used to run the tests.

This is not great, because the tests must be aware that the USDC<>WBTC pair could be ordered in any of the two ways possible. Look at how ugly this blob of code is:

const token0: string = await this.contracts.uniswapV2Pair.token0();
if (token0 == this.contracts.wbtc.address) {
await this.contracts.uniswapV2Pair.__godMode_setToken0(this.contracts.usdc.address);
await this.contracts.uniswapV2Pair.__godMode_setToken1(this.contracts.wbtc.address);
localToken0Amount = underlyingAmount;
localToken1Amount = collateralAmount;
} else {
await this.contracts.uniswapV2Pair.__godMode_setToken0(this.contracts.wbtc.address);
await this.contracts.uniswapV2Pair.__godMode_setToken1(this.contracts.usdc.address);
localToken0Amount = collateralAmount;
localToken1Amount = underlyingAmount;
}
await this.contracts.uniswapV2Pair.sync();

After Hardhat fixes NomicFoundation/hardhat#1883, we should switch to using their hardhat_setCode JSON-RPC method.

Move errors to a single package

Errors are currently defined in each respective package, not a package shared by all other packages.

Moving them to a single package would be beneficial due to two reasons:

  1. Centralising errors would make it easier to inspect them when they occur in production.
  2. In certain cases, this would DRY-ify errors. Some @hifi/protocol errors are tested for in other packages.

[flash-swap] Improve pair contract assertion logic

In HifiFlashUniswapV2.sol, there's this line asserting that the sender address is the pair contract that was initialized at the constructor. Having the pairs set at the constructor makes the flash swap contract very difficult to adapt to new listed collateral tokens. Instead, I would suggest initializing the constructor with the Uniswap V2 init hash and factory contract address (they would change depending on which chain and Uniswap clone is used). The pair contract address to be used in the assertion would then be computed from the collateral and underlying token addresses as follows:

(address token0, address token1) = sortTokens(underlying, collateral);
address pair = address(uint(keccak256(abi.encodePacked(
  hex'ff',
  factory,
  keccak256(abi.encodePacked(token0, token1)),
  initCodeHash
))));

This might be more costly in terms of gas, but it's a lot more dynamic and doesn't require any maintenance.

[protocol] Use "upgrades.erc1967.getImplementationAddress" to read the implementation address

Use the newly implemented getImplementationAddress function in the Hardhat plugin to read the implementation address. This is to replace the current workaround to use a locally defined storage slot:

const balanceSheetImplementation: string = hexStripZeros(
await ethers.provider.getStorageAt(balanceSheetProxy.address, ERC1967_IMPLEMENTATION_STORAGE_SLOT),
);

[flash-swap] Add token withdraw method

One implication of #60 being merged is that the flash-swap contract is going to have to contain collateral token balances to insure that it would always have enough collateral to repay the 0.3% Uniswap V2 fee, especially in the case where USDC is used as collateral. Thus, we will need to add a token withdraw method in order for the contract owner to be able to withdraw tokens contained by the flash-swap contract.

[flash-swap] Handle division by zero in "getRepayAmount" function

While working on #67, I realized that there is an edge case which is currently not handled by the Uniswap v2 flash swap contract:

AssertionError: Expected transaction to be reverted with FlashUniswapV2__TurnoutNotSatisfied, but other exception was thrown: Error: VM Exception while processing transaction: reverted with panic code 0x12 (Division or modulo division by zero)

That error originates in the getRepayAmount function, when the underlyingReserves happens to match underlyingAmount. The Uniswap v2 pair contract will check for the latter to not be greater than the former, but it will allow it to pass when they are equal. That is because it is possible to have a big amount of one token and a zero amount of the other token in a Uniswap v2 pool.

I can't think off a solution off the top of my head.

[flash-swap] Bond dust left behind upon liquidation

The accounts in the balance sheet are collecting bond and collateral dust, even after bond maturation and flash-swap liquidation. This is due to the way flash liquidation works. The liquidator bot flash borrows USDC in order to mint the htokens used to liquidate positions. When the 18-decimal repay amount is scaled down to 6 decimals, there will be some bond dust in most cases as 12 decimal places would be cut off. In order to liquidate the entire bond, we will need to:

  1. Offset the USDC amount to be borrowed by 1e-6 from the client side.
  2. Enable minting slightly more htokens than needed as a result of scaling up the borrowed USDC amount. The end result is that bonds would be fully liquidated, but the flash-swap contract would accumulate htoken dust. This is a compromise we're willing to make.

[amm] Prove construction safety for "hTokenOutForUnderlyingIn" and "underlyingOutForHTokenIn"

Problem Statement

In the hTokenOutForUnderlyingIn and the underlyingOutForHTokenIn functions, the last operation is a subtraction:

hTokenOut = hTokenReserves - newHTokenReserves;

And:

normalizedUnderlyingOut = normalizedUnderlyingReserves - newNormalizedUnderlyingReserves;

The task is to derive a mathematical proof that it is not possible for this subtraction to revert.

Suggestions

  • Hack around with Mathematica. Build on top of my set of inequalities.
  • Use Echidna to fuzz the inputs and see if the fuzzer can find anything. Add an "assert" statement to let Echidna know that "hTokenReserves" must be greater than or equal to "newHTokenReserves", ditto for the other two vars.

Fix concurrent cache writing issue in CI

The @actions/setup-node@v2 action is used in four jobs in the CI workflow, which causes the following error:

Unable to reserve cache with key node-cache-Linux-yarn-ed73988f6a7bd1a0270433a2d2ff89c15016643cf8f05a2fb87a270786528399, another job may be creating this cache.

All of the jobs attempt to write to the cache using the same key.

Exclude the "Name" column when the returned value does not have a name

The generate-docs scripts generates a pretty Markdown report that we use in our official docs. The problem is that this report needs some polishing before is easy for use.

One issue is related to the "Name" column in table under the "Returned Values" section. When the returned value does not have a name, but the NatSpec comments make a reference to it, in the table we find the first word after the @return keyword, which often is "The".

To solve this, we should add an #if statement, and check whether the returned value has a name or not.

[flash-swap] Repay the 0.3% Uniswap V2 in USDC

An implication of #60 being merged is that the flash-swap contract will have to contain collateral tokens to ensure it's able to pay the 0.3% Uniswap fee in some edge cases where the entire liquidated collateral is not enough to repay the flash swap. Instead of having the flash-swap contain multiple token balances, it would be better to only contain the token shared by all pairs, which is the underlying (USDC), and use it to pay the swap fee when those edge cases are met. This would make liquidation a lot easier to manage.

[amm] Normalize the "timeToMaturity" argument in "YieldSpace.sol"

All arguments come normalized in the functions defined in YieldSpace.sol, except for the timeToMaturity argument, which is normalized internally in the library. The style would be more consistent if we asked users to normalize all function arguments before they call any function.

[tasks] Modify `HifiPool` deployer script to track pool upon deployment

Modify HifiPool deployer script to call HifiPoolRegistry.trackPool(pool) as soon as the pool is deployed.
This is critical for the subgraph, as any pool events that may have been emitted before the pool was added to registry would not be captured during subgraph sync. This is outlined in The Graph docs:

Note: A new data source will only process the calls and events for the block in which it was created and all following blocks, but will not process historical data, i.e., data that is contained in prior blocks.

Upgrade dependencies

  • @paulrberg/contracts
  • mathjs (blocked by prb-math)
  • prb-math
  • typechain and its plugins

Upgrading from prb-math v2.4.0 to v2.4.2 is safe because there is no Solidity code change between these two versions.

But I'm not sure it would be okay to upgrade the OpenZeppelin packages since we used the current versions to ship our upgradeable proxies to production (on Polygon, that is).

Export the types and the factories from an index file

The current approach is to expect the end user to know the path of the Hifi-related contract they want to interact with. Refer to the code snippets in the README to see how verbose the import path is.

It would be a much nicer API if we exposed all types and factories via an index.ts file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.