Skip to content

Latest commit

 

History

History
418 lines (370 loc) · 11.2 KB

YPP-0099.md

File metadata and controls

418 lines (370 loc) · 11.2 KB

Proposal

Introduce USDT as a borrowable asset on Arbitrum.

Background

USDT is a major asset, and there are a number of financial strategies that include it. Enabling it as a borrowable asset is expected to gain some traction.

Details

The summary of the execution is as follows:

  1. Deploy USDT Join (no need for governance approval)
  2. Deploy the FYUSDT2303 and FYUSDT2306 fyToken & pools (no need for governance approval)
  3. Deploy YSUSDT6MMS and YSUSDT6MJD strategies (no need for governance approval)
  4. Governance proposal a. Update chi and rate in accumulator oracle b. Update Chainlink sources c. Add USDT as an asset d. Make USDT a base e. Approve USDT, ETH, DAI and USDC as collaterals for USDT f. Add the FYUSDT2303 and FYUSDT2306 fyToken and pools as series g. Initialize the YSUSDT6MMS and YSUSDT6MJD strategies with 100 USDT each, and burn the strategy tokens h. Invest the YSUSDT6MMS and YSUSDT6MJD strsategies in the FYUSDT2303 and FYUSDT2306 pools

These steps are encoded in the addUSDTSeries scripts.

Proposal Config (Deployments)

import { BigNumber } from 'ethers'
import {
  ACCUMULATOR,
  SAFE_ERC20_NAMER,
  USDT,
  FYUSDT2303,
  FYUSDT2306,
  EOMAR23,
  EOJUN23,
  ONE64,
  secondsInOneYear,
  YSUSDT6MMS,
  YSUSDT6MJD,
} from '../../../../../shared/constants'

import * as base_config from '../../../base.arb_mainnet.config'

export const chainId: number = base_config.chainId
export const developer: string = '0x9152F1f95b0819DA526BF6e0cB800559542b5b25'
export const deployer: string = '0x9152F1f95b0819DA526BF6e0cB800559542b5b25'
export const whales: Map<string, string> = base_config.whales
export const external: Map<string, string> = base_config.external
export const governance: Map<string, string> = base_config.governance
export const assets: Map<string, string> = base_config.assets

import { readAddressMappingIfExists } from '../../../../../shared/helpers'

export const fyTokens = () => readAddressMappingIfExists('fyTokens.json')
export const joins = () => readAddressMappingIfExists('joins.json')
export const pools = () => readAddressMappingIfExists('pools.json')
export const strategies = () => readAddressMappingIfExists('strategies.json')
export const protocol = () => readAddressMappingIfExists('protocol.json')

import { ContractDeployment } from '../../../confTypes'

/// @notice Assets that will be added to the protocol
/// @param Asset identifier (bytes6 tag)
/// @param Address for the asset
export const assetsToAdd: Map<string, string> = new Map([[USDT, assets.get(USDT) as string]])

export const timeStretch: Map<string, BigNumber> = new Map([
  [FYUSDT2303, ONE64.div(secondsInOneYear.mul(45))],
  [FYUSDT2306, ONE64.div(secondsInOneYear.mul(45))],
]) // todo: Allan

// Sell base to the pool fee, as fp4
export const g1: number = 9000 // todo: Allan

// ----- CONTRACT DEPLOYMENTS -----

export const contractDeployments: ContractDeployment[] = [
  {
    addressFile: 'joins.json',
    name: USDT,
    contract: 'FlashJoin',
    args: [() => assets.getOrThrow(USDT)!],
  },
  {
    addressFile: 'fyTokens.json',
    name: FYUSDT2303,
    contract: 'FYToken',
    args: [
      () => USDT,
      () => protocol().getOrThrow(ACCUMULATOR)!,
      () => joins().getOrThrow(USDT)!,
      () => EOMAR23.toString(),
      () => 'FYUSDT2303',
      () => 'FYUSDT2303',
    ],
    libs: {
      SafeERC20Namer: protocol().get('safeERC20Namer')!,
    },
  },
  {
    addressFile: 'fyTokens.json',
    name: FYUSDT2306,
    contract: 'FYToken',
    args: [
      () => USDT,
      () => protocol().getOrThrow(ACCUMULATOR)!,
      () => joins().getOrThrow(USDT)!,
      () => EOJUN23.toString(),
      () => 'FYUSDT2306',
      () => 'FYUSDT2306',
    ],
    libs: {
      SafeERC20Namer: protocol().get('safeERC20Namer')!,
    },
  },
  {
    addressFile: 'pools.json',
    name: FYUSDT2303, // Starting from the September series, pools get their own identifiers different from the fyToken
    contract: 'PoolNonTv',
    args: [
      () => assets.getOrThrow(USDT)!,
      () => fyTokens().getOrThrow(FYUSDT2303)!,
      () => timeStretch.get(FYUSDT2303)!.toString(),
      () => g1.toString(),
    ],
    libs: {
      YieldMath: protocol().get('yieldMath')!,
    },
  },
  {
    addressFile: 'pools.json',
    name: FYUSDT2306, // Starting from the September series, pools get their own identifiers different from the fyToken
    contract: 'PoolNonTv',
    args: [
      () => assets.getOrThrow(USDT)!,
      () => fyTokens().getOrThrow(FYUSDT2306)!,
      () => timeStretch.getOrThrow(FYUSDT2306)!.toString(),
      () => g1.toString(),
    ],
    libs: {
      YieldMath: protocol().get('yieldMath')!,
    },
  },
  {
    addressFile: 'strategies.json',
    name: YSUSDT6MJD,
    contract: 'Strategy',
    args: [() => 'Yield Strategy USDT 6M Jun Dec', () => 'YSUSDT6MJD', () => fyTokens().getOrThrow(FYUSDT2306)!],
    libs: {
      SafeERC20Namer: protocol().getOrThrow(SAFE_ERC20_NAMER)!,
    },
  },
  {
    addressFile: 'strategies.json',
    name: YSUSDT6MMS,
    contract: 'Strategy',
    args: [() => 'Yield Strategy USDT 6M Mar Sep', () => 'YSUSDT6MJD', () => fyTokens().getOrThrow(FYUSDT2303)!],
    libs: {
      SafeERC20Namer: protocol().getOrThrow(SAFE_ERC20_NAMER)!,
    },
  },
]

Proposal Config (Parameters)

import { BigNumber } from 'ethers'
import { WAD, ONEUSDC, CHI, RATE } from '../../../../../shared/constants'
import { ACCUMULATOR, CHAINLINKUSD } from '../../../../../shared/constants'
import { ETH, DAI, USDC, USDT } from '../../../../../shared/constants'
import { FYUSDT2303, FYUSDT2306, YSUSDT6MMS, YSUSDT6MJD } from '../../../../../shared/constants'

import * as base_config from '../../../base.arb_mainnet.config'

export const chainId: number = base_config.chainId
export const developer: string = '0x9152F1f95b0819DA526BF6e0cB800559542b5b25'
export const whales: Map<string, string> = base_config.whales

export const deployers: Map<string, string> = base_config.deployers
export const governance: Map<string, string> = base_config.governance
export const assets: Map<string, string> = base_config.assets
export const joins: Map<string, string> = base_config.joins
export const fyTokens: Map<string, string> = base_config.fyTokens
export const pools: Map<string, string> = base_config.pools
export const strategies: Map<string, string> = base_config.strategies

import { readAddressMappingIfExists } from '../../../../../shared/helpers'

export const protocol = () => readAddressMappingIfExists('protocol.json')

import { Accumulator, OracleSource, OraclePath, Asset, Base, Ilk, Series, Strategy } from '../../../confTypes'

export const ONEUSDT = ONEUSDC

// ----- ORACLES -----

/// @notice Configuration of the acummulator
/// @param Asset identifier (bytes6 tag)
/// @param Acummulator type (bytes6 tag)
/// @param start Initial value for the acummulator (18 decimal fixed point)
/// @param increasePerSecond Acummulator multiplier, per second (18 decimal fixed point)
export const accumulators: Accumulator[] = [
  {
    baseId: USDT,
    kind: RATE,
    startRate: WAD,
    perSecondRate: BigNumber.from('1000000001546067000'),
  },
  {
    baseId: USDT,
    kind: CHI,
    startRate: WAD,
    perSecondRate: WAD,
  },
]

/// @notice Sources that will be added to the Chainlink Oracle
/// @param Base asset identifier (bytes6 tag)
/// @param Address for the base asset
/// @param Quote asset identifier (bytes6 tag)
/// @param Address for the quote asset
/// @param Address for the chainlink aggregator
export const chainlinkSources: OracleSource[] = [
  {
    baseId: USDT,
    baseAddress: assets.getOrThrow(USDT)!,
    quoteId: ETH,
    quoteAddress: assets.getOrThrow(ETH)!,
    sourceAddress: '0x3f3f5dF88dC9F13eac63DF89EC16ef6e7E25DdE7',
  },
]

// ----- ASSETS, BASES, ILKS -----

export const usdt: Base = {
  assetId: USDT,
  address: assets.getOrThrow(USDT)!,
  rateOracle: protocol().getOrThrow(ACCUMULATOR)!,
}

const ilkUSDTUSDT: Ilk = {
  baseId: USDT,
  ilkId: USDT,
  asset: {
    assetId: USDT,
    address: assets.getOrThrow(USDT)!,
  },
  collateralization: {
    baseId: USDT,
    ilkId: USDT,
    oracle: protocol().getOrThrow(CHAINLINKUSD)!,
    ratio: 1000000,
  },
  debtLimits: {
    baseId: USDT,
    ilkId: USDT,
    line: 100000000,
    dust: 0,
    dec: 6,
  },
  // No auction line and limit for USDT/USDT
}

const ilkUSDTETH: Ilk = {
  baseId: USDT,
  ilkId: ETH,
  asset: {
    assetId: ETH,
    address: assets.getOrThrow(ETH)!,
  },
  collateralization: {
    baseId: USDT,
    ilkId: ETH,
    oracle: protocol().getOrThrow(CHAINLINKUSD)!,
    ratio: 1400000,
  },
  debtLimits: {
    baseId: USDT,
    ilkId: ETH,
    line: 100000,
    dust: 1000,
    dec: 6,
  },
  auctionLineAndLimit: {
    baseId: USDT,
    ilkId: ETH,
    duration: 3600,
    vaultProportion: WAD.div(2),
    collateralProportion: WAD.mul(1050000).div(1400000),
    max: WAD.mul(1000), // $10M
  },
}

const ilkUSDTDAI: Ilk = {
  baseId: USDT,
  ilkId: DAI,
  asset: {
    assetId: DAI,
    address: assets.getOrThrow(DAI)!,
  },
  collateralization: {
    baseId: USDT,
    ilkId: DAI,
    oracle: protocol().getOrThrow(CHAINLINKUSD)!,
    ratio: 1100000,
  },
  debtLimits: {
    baseId: USDT,
    ilkId: DAI,
    line: 100000,
    dust: 1000,
    dec: 6,
  },
  auctionLineAndLimit: {
    baseId: USDT,
    ilkId: DAI,
    duration: 3600,
    vaultProportion: WAD,
    collateralProportion: WAD.mul(1050000).div(1100000),
    max: WAD.mul(10000000),
  },
}

const ilkUSDTUSDC: Ilk = {
  baseId: USDT,
  ilkId: USDC,
  asset: {
    assetId: USDC,
    address: assets.getOrThrow(USDC)!,
  },
  collateralization: {
    baseId: USDT,
    ilkId: USDC,
    oracle: protocol().getOrThrow(CHAINLINKUSD)!,
    ratio: 1100000,
  },
  debtLimits: {
    baseId: USDT,
    ilkId: USDC,
    line: 100000,
    dust: 1000,
    dec: 6,
  },
  auctionLineAndLimit: {
    baseId: USDT,
    ilkId: USDC,
    duration: 3600,
    vaultProportion: WAD,
    collateralProportion: WAD.mul(1050000).div(1100000),
    max: ONEUSDT.mul(10000000),
  },
}

export const ilks: Ilk[] = [ilkUSDTUSDT, ilkUSDTETH, ilkUSDTDAI, ilkUSDTUSDC]

/// ----- SERIES -----

const fyUSDT2303: Series = {
  seriesId: FYUSDT2303,
  base: usdt,
  fyToken: {
    assetId: FYUSDT2303,
    address: fyTokens.getOrThrow(FYUSDT2303)!,
  },
  chiOracle: protocol().getOrThrow(ACCUMULATOR)!,
  pool: {
    assetId: FYUSDT2303,
    address: pools.getOrThrow(FYUSDT2303)!,
  },
  ilks: ilks,
}

const fyUSDT2306: Series = {
  seriesId: FYUSDT2306,
  base: usdt,
  fyToken: {
    assetId: FYUSDT2306,
    address: fyTokens.getOrThrow(FYUSDT2306)!,
  },
  chiOracle: protocol().getOrThrow(ACCUMULATOR)!,
  pool: {
    assetId: FYUSDT2306,
    address: pools.getOrThrow(FYUSDT2306)!,
  },
  ilks: ilks,
}

export const newSeries: Series[] = [fyUSDT2303, fyUSDT2306]

/// ----- STRATEGIES -----

const ysUSDT6MMS: Strategy = {
  assetId: YSUSDT6MMS,
  address: strategies.getOrThrow(YSUSDT6MMS)!,
  base: usdt,
  initAmount: ONEUSDT.mul(100),
  seriesToInvest: fyUSDT2303,
}

const ysUSDT6MJD: Strategy = {
  assetId: YSUSDT6MJD,
  address: strategies.getOrThrow(YSUSDT6MJD)!,
  base: usdt,
  initAmount: ONEUSDT.mul(100),
  seriesToInvest: fyUSDT2306,
}

export const newStrategies: Strategy[] = [ysUSDT6MMS, ysUSDT6MJD]

Testing

TBA