Configure series for Contango September 2023 instruments Update CRs for Contango June 2023 instruments
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
Here are the steps:
- Deploy audited PoolOracle (no need for governance approval)
- Deploy YieldSpaceMultiOracle pointing to the new PoolOracle (no need for governance approval)
- Deploy new Joins (no need for governance approval)
- Initialise PoolOracle (no need for governance approval)
- Governance proposal
- Configure each new Join's permissions
- Add YieldSpaceMultiOracle oracle sources
- Add Composite oracle sources
- Add Composite oracle paths
- Add assets to the Contango cauldron
- Add series to the Contango cauldron
- Create ilks on the Contango cauldron
- Enable ilks for the created series
- Enable borrowing with the same ilk as collateral at 100% CR
- Increase debt ceiling for all instruments to $1M / 1000 ETH
- Lower CR for all series to match the Aave CRs
- 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
}
The proposal approval & execution were tested on tenderly: https://dashboard.tenderly.co/Yield/v2-arbitrum/fork/5c70be65-97e6-4968-a087-a56818671606