Skip to content
This repository has been archived by the owner on Jan 24, 2024. It is now read-only.

feat: Migrate Klima to Studio #386

Merged
merged 1 commit into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/apps/klima/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { KLIMA_DEFINITION, KlimaAppDefinition } from './klima.definition';
export { KlimaAppModule } from './klima.module';
39 changes: 39 additions & 0 deletions src/apps/klima/klima.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Register } from '~app-toolkit/decorators';
import { appDefinition, AppDefinition } from '~app/app.definition';
import { GroupType, AppAction, AppTag } from '~app/app.interface';
import { Network } from '~types/network.interface';

export const KLIMA_DEFINITION = appDefinition({
id: 'klima',
name: 'Klima',
description: `As a matter of course, Klima DAO will solve the critical problems of the carbon markets: illiquidity, opacity and inefficiency.`,
groups: {
bond: { id: 'bond', type: GroupType.POSITION },
sKlima: { id: 'sKlima', type: GroupType.TOKEN },
wsKlima: { id: 'wsKlima', type: GroupType.TOKEN },
},
url: 'https://www.klimadao.finance/',
links: {
github: 'https://github.com/KlimaDAO',
twitter: 'https://twitter.com/KlimaDAO',
discord: 'https://discord.com/invite/klimadao',
telegram: 'https://t.me/joinchat/Zb06f_mnMosyYTYy',
medium: 'https://klimadao.medium.com',
learn: 'https://docs.klimadao.finance',
},
tags: [AppTag.ELASTIC_FINANCE],
supportedNetworks: {
[Network.POLYGON_MAINNET]: [AppAction.VIEW],
},
token: {
address: '0x4e78011ce80ee02d2c3e649fb657e45898257815', // KLIMA
network: Network.POLYGON_MAINNET,
},
});

@Register.AppDefinition(KLIMA_DEFINITION.id)
export class KlimaAppDefinition extends AppDefinition {
constructor() {
super(KLIMA_DEFINITION);
}
}
23 changes: 23 additions & 0 deletions src/apps/klima/klima.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Register } from '~app-toolkit/decorators';
import { AbstractApp } from '~app/app.dynamic-module';
import { OlympusAppModule } from '~apps/olympus';

import { KlimaAppDefinition, KLIMA_DEFINITION } from './klima.definition';
import { PolygonKlimaBalanceFetcher } from './polygon/klima.balance-fetcher';
import { PolygonKlimaBondContractPositionFetcher } from './polygon/klima.bond.contract-position';
import { PolygonKlimaSTokenFetcher } from './polygon/klima.s-klima.token-fetcher';
import { PolygonKlimaWsTokenFetcher } from './polygon/klima.ws-klima.token-fetcher';

@Register.AppModule({
appId: KLIMA_DEFINITION.id,
imports: [OlympusAppModule],
providers: [
KlimaAppDefinition,
// Polygon
PolygonKlimaBalanceFetcher,
PolygonKlimaBondContractPositionFetcher,
PolygonKlimaSTokenFetcher,
PolygonKlimaWsTokenFetcher,
],
})
export class KlimaAppModule extends AbstractApp() {}
81 changes: 81 additions & 0 deletions src/apps/klima/polygon/klima.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Inject } from '@nestjs/common';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { presentBalanceFetcherResponse } from '~app-toolkit/helpers/presentation/balance-fetcher-response.present';
import {
OlympusBondV1ContractPositionBalanceHelper,
OlympusContractFactory,
OlympusV1BondDepository,
} from '~apps/olympus';
import { BalanceFetcher } from '~balance/balance-fetcher.interface';
import { Network } from '~types/network.interface';

import { KLIMA_DEFINITION } from '../klima.definition';

const appId = KLIMA_DEFINITION.id;
const network = Network.POLYGON_MAINNET;

@Register.BalanceFetcher(appId, network)
export class PolygonKlimaBalanceFetcher implements BalanceFetcher {
constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(OlympusContractFactory) private readonly olympusContractFactory: OlympusContractFactory,
@Inject(OlympusBondV1ContractPositionBalanceHelper)
private readonly olympusContractPositionBalanceHelper: OlympusBondV1ContractPositionBalanceHelper,
) {}

private async getStakedBalances(address: string) {
return await this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
network,
appId,
groupId: KLIMA_DEFINITION.groups.sKlima.id,
address,
});
}

private async getWrappedStakedBalances(address: string) {
return await this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
network,
appId,
groupId: KLIMA_DEFINITION.groups.wsKlima.id,
address,
});
}

private async getBondBalances(address: string) {
return this.olympusContractPositionBalanceHelper.getBalances<OlympusV1BondDepository>({
network,
groupId: KLIMA_DEFINITION.groups.bond.id,
appId,
address,
resolveDepositoryContract: ({ depositoryAddress: address }) =>
this.olympusContractFactory.olympusV1BondDepository({ network, address }),
resolveClaimablePayout: ({ multicall, contract, address }) => multicall.wrap(contract).pendingPayoutFor(address),
resolveTotalPayout: ({ multicall, contract, address }) =>
multicall
.wrap(contract)
.bondInfo(address)
.then(v => v.payout),
});
}

async getBalances(address: string) {
const [stakedBalances, wrappedStakedBalances, bondBalances] = await Promise.all([
this.getStakedBalances(address),
this.getWrappedStakedBalances(address),
this.getBondBalances(address),
]);

return presentBalanceFetcherResponse([
{
label: 'Staked',
assets: [...stakedBalances, ...wrappedStakedBalances],
},
{
label: 'Bonds',
assets: bondBalances,
},
]);
}
}
77 changes: 77 additions & 0 deletions src/apps/klima/polygon/klima.bond.contract-position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Inject } from '@nestjs/common';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { getAppImg, getTokenImg } from '~app-toolkit/helpers/presentation/image.present';
import { OlympusBondContractPositionHelper } from '~apps/olympus';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { ContractPosition } from '~position/position.interface';
import { Network } from '~types/network.interface';

import { KLIMA_DEFINITION } from '../klima.definition';

const appId = KLIMA_DEFINITION.id;
const groupId = KLIMA_DEFINITION.groups.bond.id;
const network = Network.POLYGON_MAINNET;

const BCT_ADDRESS = '0x2f800db0fdb5223b3c3f354886d907a671414a7f';
const USDC_ADDRESS = '0x2791bca1f2de4661ed88a30c99a7a9449aa84174';
const MC02_ADDRESS = '0xfc98e825a2264d890f9a1e68ed50e1526abccacd';

@Register.ContractPositionFetcher({ appId, groupId, network })
export class PolygonKlimaBondContractPositionFetcher implements PositionFetcher<ContractPosition> {
constructor(
@Inject(OlympusBondContractPositionHelper)
private readonly olympusContractPositionHelper: OlympusBondContractPositionHelper,
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
) {}
async getPositions(): Promise<ContractPosition[]> {
const klimaToken = await this.appToolkit.getBaseTokenPrice({
address: KLIMA_DEFINITION.token!.address,
network,
});

if (!klimaToken) return [];

const depositories = [
{
depositoryAddress: '0x7De627C56D26529145a5f9D85948ecBeAF9a4b34',
symbol: 'BCT LP',
images: [getTokenImg(BCT_ADDRESS, network)],
},
{
depositoryAddress: '0x27217c3F5bEc4c12Fa506A101bC4bd15417AEAa8',
symbol: 'MC02 LP',
images: [getTokenImg(MC02_ADDRESS, network)],
},
{
depositoryAddress: '0x1e0dd93c81ac7af2974cdb326c85b87dd879389b',
symbol: 'KLIMA-BCT LP',
images: [getAppImg(appId), getTokenImg(BCT_ADDRESS, network)],
},
{
depositoryAddress: '0xbf2a35efcd85e790f02458db4a3e2f29818521c5',
symbol: 'BCT-USDC LP',
images: [getTokenImg(BCT_ADDRESS, network), getTokenImg(USDC_ADDRESS, network)],
},
{
depositoryAddress: '0xb5aF101742EcAe095944F60C384d09453006bFde',
symbol: 'KLIMA-USDC LP',
images: [getAppImg(appId), getTokenImg(USDC_ADDRESS, network)],
},
{
depositoryAddress: '0xf9c3FC299dE5f86d9CD6a724e6B44933720f5e6D',
symbol: 'KLIMA-MC02 LP',
images: [getAppImg(appId), getTokenImg(MC02_ADDRESS, network)],
},
];

return this.olympusContractPositionHelper.getPositions({
appId: KLIMA_DEFINITION.id,
network,
groupId,
depositories,
mintedTokenAddress: klimaToken.address,
});
}
}
35 changes: 35 additions & 0 deletions src/apps/klima/polygon/klima.s-klima.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Inject } from '@nestjs/common';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { Network } from '~types/network.interface';

import { KLIMA_DEFINITION } from '../klima.definition';

const appId = KLIMA_DEFINITION.id;
const groupId = KLIMA_DEFINITION.groups.sKlima.id;
const network = Network.POLYGON_MAINNET;

@Register.TokenPositionFetcher({ appId, groupId, network })
export class PolygonKlimaSTokenFetcher implements PositionFetcher<AppTokenPosition> {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getPositions(): Promise<AppTokenPosition[]> {
return this.appToolkit.helpers.singleVaultTokenHelper.getTokens({
network,
appId,
groupId,
address: '0xb0c22d8d350c67420f06f48936654f567c73e8c8', // sKLIMA
resolveContract: ({ address, network }) => this.appToolkit.globalContracts.erc20({ address, network }),
resolveUnderlyingTokenAddress: () => '0x4e78011ce80ee02d2c3e649fb657e45898257815', // KLIMA
resolveReserve: ({ underlyingToken, network }) =>
this.appToolkit.globalContracts
.erc20({ address: underlyingToken.address, network })
.balanceOf('0x25d28a24ceb6f81015bb0b2007d795acac411b4d')
.then(v => Number(v) / 10 ** underlyingToken.decimals),
resolvePricePerShare: () => 1,
});
}
}
35 changes: 35 additions & 0 deletions src/apps/klima/polygon/klima.ws-klima.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Inject } from '@nestjs/common';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { Network } from '~types/network.interface';

import { KLIMA_DEFINITION } from '../klima.definition';

const appId = KLIMA_DEFINITION.id;
const groupId = KLIMA_DEFINITION.groups.wsKlima.id;
const network = Network.POLYGON_MAINNET;

@Register.TokenPositionFetcher({ appId, groupId, network })
export class PolygonKlimaWsTokenFetcher implements PositionFetcher<AppTokenPosition> {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getPositions(): Promise<AppTokenPosition[]> {
return this.appToolkit.helpers.singleVaultTokenHelper.getTokens({
network,
appId,
groupId,
dependencies: [{ appId, groupIds: [KLIMA_DEFINITION.groups.sKlima.id], network }],
address: '0x6f370dba99e32a3cad959b341120db3c9e280ba6', // wsKLIMA
resolveContract: ({ address, network }) => this.appToolkit.globalContracts.erc20({ address, network }),
resolveUnderlyingTokenAddress: () => '0xb0c22d8d350c67420f06f48936654f567c73e8c8', // sKLIMA
resolveReserve: ({ underlyingToken, network }) =>
this.appToolkit.globalContracts
.erc20({ address: underlyingToken.address, network })
.balanceOf('0x6f370dba99e32a3cad959b341120db3c9e280ba6')
.then(v => Number(v) / 10 ** underlyingToken.decimals),
});
}
}