Skip to content

Latest commit

 

History

History
199 lines (168 loc) · 7.17 KB

YPP-0112.md

File metadata and controls

199 lines (168 loc) · 7.17 KB

Proposal

Configure series for Contango September 2023 instruments Update CRs for Contango June 2023 instruments

Background

Contango exchange uses fixed rate markets to synthesise expirable futures. In order to allow for a smoother interaction, Yield holds a separate copy of some key contracts (Ladle, Cauldron, & Witch) and so they need to be updated in a similar fashion as the "public" ones

Details

Here are the steps:

  1. Deploy audited PoolOracle (no need for governance approval)
  2. Deploy YieldSpaceMultiOracle pointing to the new PoolOracle (no need for governance approval)
  3. Deploy new Joins (no need for governance approval)
  4. Initialise PoolOracle (no need for governance approval)
  5. Governance proposal
    1. Configure each new Join's permissions
    2. Add YieldSpaceMultiOracle oracle sources
    3. Add Composite oracle sources
    4. Add Composite oracle paths
    5. Add assets to the Contango cauldron
    6. Add series to the Contango cauldron
    7. Create ilks on the Contango cauldron
    8. Enable ilks for the created series
    9. Enable borrowing with the same ilk as collateral at 100% CR
    10. Increase debt ceiling for all instruments to $1M / 1000 ETH
    11. Lower CR for all series to match the Aave CRs
    12. Update PoolOracle & YieldSpaceMultiOracle for all series

Script that performed said actions: https://github.com/yieldprotocol/environments-v2/blob/c1c9f21020152513b87741d3079f541d022b61f0/scripts/governance/contango/arbitrum/sept23Instruments/orchestrate.ts

The contracts have been deployed to Arbitrum mainnet and the proposal sent to the multisig.

Configuration applied:

export const ASSETS_ARBITRUM: Asset[] = [
  new Asset(ETH, 'ETH', false, 1_000e6, 0.025e6, 12, 1.176e6),
  new Asset(DAI, 'DAI', true, 1_000_000, 40, 18, 1.22e6),
  new Asset(USDC, 'USDC', true, 1_000_000, 40, 6, 1.163e6),
  new Asset(USDT, 'USDT', true, 1_000_000, 40, 6, 1.25e6, false),
]

export const ASSETS_ARBITRUM_MAP: Map<string, Asset> = new Map(ASSETS_ARBITRUM.map((asset) => [asset.bytes, asset]))

export const EXPIRIES: number[] = [EODEC22, EOMAR23, EOJUN23, EOSEP23]

export const JUNE_SERIES_ARBITRUM: Array<Series> = ASSETS_ARBITRUM.map((asset) => new Series(asset, EOJUN23))

export const NEW_SERIES_ARBITRUM: Array<Series> = [
  ...ASSETS_ARBITRUM.map((asset) => new Series(asset, EOSEP23)),
  new Series(ASSETS_ARBITRUM_MAP.getOrThrow(USDT), EOJUN23),
]

export const SERIES_ARBITRUM: Array<Series> = ASSETS_ARBITRUM.filter((asset) => asset.bytes !== USDT)
  .map((asset) => EXPIRIES.map((expiry) => new Series(asset, expiry)))
  .flat()
  .concat([EOJUN23, EOSEP23].map((expiry) => new Series(ASSETS_ARBITRUM_MAP.getOrThrow(USDT), expiry)))

export const newJoins: string[] = NEW_SERIES_ARBITRUM.map(({ bytes: seriesId }) => joins.getOrThrow(seriesId))

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

export const assetsToAdd: Asset[] = NEW_SERIES_ARBITRUM.filter(({ timestamp }) => timestamp > EOMAR23)
  .map(({ bytes: base }) => ({
    assetId: base,
    address: fyTokens.getOrThrow(base),
  }))
  .concat(usdt)

export const basesToAdd: Base[] = [usdt]

export const compositeSources: OracleSource[] = SERIES_ARBITRUM.map((series) => ({
  baseId: series.bytes,
  baseAddress: fyTokens.getOrThrow(series.bytes),
  quoteId: series.asset.bytes,
  quoteAddress: assets.getOrThrow(series.asset.bytes),
  sourceAddress: protocol.getOrThrow(YIELD_SPACE_MULTI_ORACLE), // All fyTokens as collateral use the same oracle
})).concat(
  ASSETS_ARBITRUM.map((asset) => ({
    baseId: ASSETS_ARBITRUM_MAP.getOrThrow(USDT).bytes,
    baseAddress: assets.getOrThrow(USDT),
    quoteId: asset.bytes,
    quoteAddress: assets.getOrThrow(asset.bytes),
    sourceAddress: protocol.getOrThrow(CHAINLINKUSD),
  }))
)

export const compositePaths: OraclePath[] = ASSETS_ARBITRUM.map((asset) =>
  SERIES_ARBITRUM.filter((series) => series.asset.bytes !== asset.bytes).map((series) => ({
    baseId: asset.bytes,
    quoteId: series.bytes,
    path: [series.asset.bytes],
  }))
).flat()

export const newSeries: Series[] = createSeries(NEW_SERIES_ARBITRUM)
export const juneSeries: Series[] = createSeries(JUNE_SERIES_ARBITRUM)

function createSeries(seed: SeriesSeed[]): Series[] {
  return seed.map((series: SeriesSeed) => {
    const { bytes: seriesId, asset } = series
    return {
      seriesId,
      base: { assetId: asset.bytes, address: assets.getOrThrow(asset.bytes) },
      fyToken: { assetId: seriesId, address: fyTokens.getOrThrow(seriesId) },
      chiOracle: protocol.getOrThrow(ACCUMULATOR),
      pool: { assetId: seriesId, address: pools.getOrThrow(seriesId) },
      ilks: createIlks(series),
    }
  })
}

function createIlks(baseSeries: SeriesSeed): Ilk[] {
  return SERIES_ARBITRUM.filter(
    (ilkSeries) => ilkSeries.asset.bytes !== baseSeries.asset.bytes && ilkSeries.timestamp === baseSeries.timestamp
  )
    .map((ilkSeries) => {
      const stablePair = ilkSeries.asset.stable && baseSeries.asset.stable
      const vaultProportion = stablePair ? parseUnits('1') : parseUnits('0.5')
      const collateralisationRatio = stablePair ? 1.026e6 : baseSeries.asset.cr
      const initialDiscount = stablePair ? 1.01e6 : 1.05e6
      const duration = stablePair ? 30 : 300
      const collateralProportion = parseUnits((initialDiscount / collateralisationRatio).toString())

      console.log(`Generating config for ${getName(baseSeries.bytes)}/${getName(ilkSeries.bytes)}`)

      const ilk: Ilk = {
        baseId: baseSeries.asset.bytes,
        ilkId: ilkSeries.bytes,
        asset: {
          assetId: baseSeries.asset.bytes,
          address: assets.getOrThrow(baseSeries.asset.bytes),
        },
        collateralization: {
          baseId: baseSeries.asset.bytes,
          ilkId: ilkSeries.bytes,
          oracle: protocol.getOrThrow(COMPOSITE),
          ratio: collateralisationRatio,
        },
        debtLimits: {
          baseId: baseSeries.asset.bytes,
          ilkId: ilkSeries.bytes,
          line: baseSeries.asset.maxDebt,
          dust: baseSeries.asset.minDebt,
          dec: baseSeries.asset.decimals,
        },
        auctionLineAndLimit: {
          baseId: baseSeries.asset.bytes,
          ilkId: ilkSeries.bytes,
          duration,
          vaultProportion,
          collateralProportion,
          max: parseUnits((baseSeries.asset.maxDebt * 10).toString(), baseSeries.asset.decimals),
        },
      }

      return ilk
    })
    .concat(createSelfIlk(baseSeries))
}

function createSelfIlk(series: SeriesSeed): Ilk {
  const ilk: Ilk = {
    baseId: series.asset.bytes,
    ilkId: series.asset.bytes,
    asset: {
      assetId: series.asset.bytes,
      address: assets.getOrThrow(series.asset.bytes),
    },
    collateralization: {
      baseId: series.asset.bytes,
      ilkId: series.asset.bytes,
      oracle: protocol.getOrThrow(IDENTITY_ORACLE),
      ratio: 1e6,
    },
    debtLimits: {
      baseId: series.asset.bytes,
      ilkId: series.asset.bytes,
      line: series.asset.maxDebt * 10,
      dust: 0,
      dec: series.asset.decimals,
    },
  }

  return ilk
}

Testing

The proposal approval & execution were tested on tenderly: https://dashboard.tenderly.co/Yield/v2-arbitrum/fork/5c70be65-97e6-4968-a087-a56818671606