We have four main contracts in our Protocol, and they are SavingAccount, Accounts, Bank, TokenRegistry, and GlobalConfig.

The first three contracts are upgradable contracts, and they contain the main business logic of our Protocol. The overall structure of our Protocol is monolithic, which means that SavingAccount, Accounts, and Bank contracts work closely with each other. We decoupled this into three contracts only for code size limit reasons. The naming here maybe a little counter intuitive. Normally when we want to deposit money to a bank, we first go to the bank and open a saving account, then we deposit money to this account. Here, SavingAccount contract behaves like a traditional bank, which is the entry point for you to deposit or borrow money from DeFiner. The Bank contract records the state of the pool of DeFiner's protocol, like the total deposit, total borrow, and the current rate. The Accounts contract records the deposit and borrow balances for each specific user.

The workflow of a transaction to DeFiner works as the following diagram. The user sends transactions by calling functions on SavingAccount contract, then the SavingAccount contract will call the Bank contract to create a rate index checkpoint, which is used to compute the accumulated interests for all the user. Then the Bank contract will call the functions in Account contract to compute the balance change in the user account.

The workflow of a transaction

The TokenRegistry and GlobalConfig contracts are used to save data that will be used among the three main contracts. Like all three contracts' addresses, so in these three contract, they only need to keep global config's address in contract's memory space.


This is the interface contract that our users will mainly interact with. Users will send transactions to this contract to do deposit, withdraw, borrow, repay, withdrawAll, and liquidate functions to this contract to interact with our protocol. For each operation, this contract is the contract that receives and sends out the tokens. Whenever there is a transaction sent to SavingAccount contract, it will emit an event to log this transaction.

Borrowing Power

The borrowing power of a user is related to the collateral that the user deposited into our system. The price is obtained from Chainlink's oracle. If the LTV of a user is too large then it is at the risk of being liquidated. Currently, the liquidation threshold is set to around 0.8.


This contract is used to create rate indexes whenever there is an interaction with our SavingAccount contract. We are using rate indexes to compute the borrow and deposit interests. Given the different indexes, we compute the user balances here. Whenever an index is created, this contract will emit an event.

Rate Index

i and j here represent two different blocks.

RateIndexi=RateIndexj(1+RatePerBlockij(ij))RateIndex_i = RateIndex_j * (1 + RatePerBlock_{ij} * (i - j))


i and j here represent two different blocks.

Balancei=Balancej×RateIndexj÷RateIndexiBalance_i = Balance_j \times RateIndex_j \div RateIndex_i

Borrow balances and deposit balances both use this method to compute.


This contract is used to record the balances of each user account for different tokens. This contract also contains a BitMap which will quickly show whether a user has depositings/borrowings for each token while costs less gas.


This contract is used to store all the contract's addresses, so all other contracts can only keep GlobalConfig's address to call functions of other contracts. It also used to config the reserve ratio and community fund ratio of the protocol.


This contract records the meta-data about each contract supported token. We can add new supported tokens using this contract.

Current Supported Tokens

  1. DAI

    1. Token Address: 0x6b175474e89094c44da98b954eedeac495271d0f

    2. cToken Address: 0x5d3a536e4d6dbd6114cc1ead35777bab948e3643

  2. USDC

    1. Token Address: 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48

    2. cToken Address: 0x39aa39c021dfbae8fac545936693ac917d5e7563

  3. USDT

    1. Token Address: 0xdac17f958d2ee523a2206206994597c13d831ec7

    2. cToken Address: 0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9

  4. TUSD

    1. Token Address: 0x0000000000085d4780B73119b644AE5ecd22b376

    2. cToken Address: Not Compound supported.

  5. MKR

    1. Token Address: 0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2

    2. cToken Address: Not Compound supported.

  6. BAT

    1. Token Address: 0x0d8775f648430679a709e98d2b0cb6250d2887ef

    2. cToken Address: 0x6c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e

  7. ZRX

    1. Token Address: 0xe41d2489571d322189246dafa5ebde1f4699f498

    2. cToken Address: 0xb3319f5d18bc0d84dd1b4825dcde5d5f7266d407

  8. REP

    1. Token Address: 0x1985365e9f78359a9B6AD760e32412f4a445E862

    2. cToken Address: 0x158079ee67fce2f58472a96584a73c7ab9ac95c1

  9. WBTC

    1. Token Address: 0x2260fac5e5542a773aa44fbcfedf7c193bc2c599

    2. cToken Address: 0xc11b1268c1a384e55c48c2391d8d480264a3a7f4

  10. FIN

    1. Token Address: 0x054f76beED60AB6dBEb23502178C52d6C5dEbE40

    2. cToken Address: Not Compound supported

  11. FIN LPToken

    1. Token Address: 0x054f76beED60AB6dBEb23502178C52d6C5dEbE40

    2. cToken Address: Not Compound supported


For SavingAccount, Accounts, and Bank contracts, we are using OpenZeppelin proxies to conduct the upgrading process. So other than the contracts, we also have an OpenZeppelin proxy contract for each of these three main contracts. The proxy contract addresses can never be changed, but the contract of the underlying implementation is changeable.

The workflow of a proxy contract.

For the GlobalConfig and TokenRegistry contracts, we can deploy a new contract and call the setter function in three main contracts to point to the new contracts, since they don't save any transactional data.