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

Commit

Permalink
feat(dhedge-v2): Add dHEDGE V2 (#683)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonzgao committed Jun 21, 2022
1 parent 2801fa0 commit 55045f6
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 0 deletions.
Binary file added src/apps/dhedge-v2/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/apps/dhedge-v2/contracts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Injectable, Inject } from '@nestjs/common';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { ContractFactory } from '~contract/contracts';
import { Network } from '~types/network.interface';
// eslint-disable-next-line
type ContractOpts = { address: string; network: Network };

@Injectable()
export class DhedgeV2ContractFactory extends ContractFactory {
constructor(@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit) {
super((network: Network) => appToolkit.getNetworkProvider(network));
}
}
39 changes: 39 additions & 0 deletions src/apps/dhedge-v2/dhedge-v2.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 { AppAction, AppTag, GroupType } from '~app/app.interface';
import { Network } from '~types/network.interface';

export const DHEDGE_V_2_DEFINITION = appDefinition({
id: 'dhedge-v2',
name: 'dHEDGE V2',
description: 'Non-custodial, decentralized, social asset management on EVM chains.',
url: 'https://www.dhedge.org/',

groups: {
pool: {
id: 'pool',
type: GroupType.TOKEN,
label: 'Pools',
},
},

tags: [AppTag.ASSET_MANAGEMENT],
keywords: [],
links: {},

supportedNetworks: {
[Network.POLYGON_MAINNET]: [AppAction.VIEW],
[Network.OPTIMISM_MAINNET]: [AppAction.VIEW],
},

primaryColor: '#fff',
});

@Register.AppDefinition(DHEDGE_V_2_DEFINITION.id)
export class DhedgeV2AppDefinition extends AppDefinition {
constructor() {
super(DHEDGE_V_2_DEFINITION);
}
}

export default DHEDGE_V_2_DEFINITION;
22 changes: 22 additions & 0 deletions src/apps/dhedge-v2/dhedge-v2.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Register } from '~app-toolkit/decorators';
import { AbstractApp } from '~app/app.dynamic-module';

import { DhedgeV2ContractFactory } from './contracts';
import { DhedgeV2AppDefinition, DHEDGE_V_2_DEFINITION } from './dhedge-v2.definition';
import { OptimismDhedgeV2BalanceFetcher } from './optimism/dhedge-v2.balance-fetcher';
import { OptimismDhedgeV2PoolTokenFetcher } from './optimism/dhedge-v2.pool.token-fetcher';
import { PolygonDhedgeV2BalanceFetcher } from './polygon/dhedge-v2.balance-fetcher';
import { PolygonDhedgeV2PoolTokenFetcher } from './polygon/dhedge-v2.pool.token-fetcher';

@Register.AppModule({
appId: DHEDGE_V_2_DEFINITION.id,
providers: [
DhedgeV2AppDefinition,
DhedgeV2ContractFactory,
OptimismDhedgeV2BalanceFetcher,
OptimismDhedgeV2PoolTokenFetcher,
PolygonDhedgeV2BalanceFetcher,
PolygonDhedgeV2PoolTokenFetcher,
],
})
export class DhedgeV2AppModule extends AbstractApp() {}
3 changes: 3 additions & 0 deletions src/apps/dhedge-v2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { DHEDGE_V_2_DEFINITION, DhedgeV2AppDefinition } from './dhedge-v2.definition';
export { DhedgeV2AppModule } from './dhedge-v2.module';
export { DhedgeV2ContractFactory } from './contracts';
36 changes: 36 additions & 0 deletions src/apps/dhedge-v2/optimism/dhedge-v2.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Inject } from '@nestjs/common';

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

import { DHEDGE_V_2_DEFINITION } from '../dhedge-v2.definition';

const appId = DHEDGE_V_2_DEFINITION.id;
const network = Network.OPTIMISM_MAINNET;

@Register.BalanceFetcher(DHEDGE_V_2_DEFINITION.id, network)
export class OptimismDhedgeV2BalanceFetcher implements BalanceFetcher {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getTokenBalances(address: string) {
return await this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId,
groupId: DHEDGE_V_2_DEFINITION.groups.pool.id,
network,
});
}

async getBalances(address: string) {
const assets = await this.getTokenBalances(address);
return presentBalanceFetcherResponse([
{
label: 'Pools',
assets,
},
]);
}
}
64 changes: 64 additions & 0 deletions src/apps/dhedge-v2/optimism/dhedge-v2.pool.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Inject } from '@nestjs/common';
import { gql } from 'graphql-request';

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

import { DhedgeV2ContractFactory } from '../contracts';
import { DHEDGE_V_2_DEFINITION } from '../dhedge-v2.definition';

const appId = DHEDGE_V_2_DEFINITION.id;
const groupId = DHEDGE_V_2_DEFINITION.groups.pool.id;
const network = Network.OPTIMISM_MAINNET;

const query = gql`
query getPools {
pools {
id
name
tokenPrice
}
}
`;

interface DHedgeResponse {
pools: {
id: string;
name: string;
tokenPrice: string;
}[];
}

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

async getPositions() {
const endpoint = 'https://api.thegraph.com/subgraphs/name/dhedge/dhedge-v2-optimism';
const { pools } = await this.appToolkit.helpers.theGraphHelper.request<DHedgeResponse>({ endpoint, query });

const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const sUSD = baseTokens.find(t => t.symbol === 'sUSD')!;

return await this.appToolkit.helpers.vaultTokenHelper.getTokens<Erc20>({
appId,
groupId,
network,
resolveVaultAddresses: () => pools.map(p => p.id.toLowerCase()),
resolveContract: ({ address, network }) => this.contractFactory.erc20({ address, network }),
resolveUnderlyingTokenAddress: () => sUSD.address, // TODO: get actual underlying tokens
resolveReserve: () => 0,
resolvePricePerShare: async ({ underlyingToken, contract }) => {
const pool = pools.find(p => p.id.toLowerCase() === contract.address)!;
return Number(pool.tokenPrice) / 10 ** underlyingToken.decimals;
},
});
}
}
36 changes: 36 additions & 0 deletions src/apps/dhedge-v2/polygon/dhedge-v2.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Inject } from '@nestjs/common';

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

import { DHEDGE_V_2_DEFINITION } from '../dhedge-v2.definition';

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

@Register.BalanceFetcher(DHEDGE_V_2_DEFINITION.id, network)
export class PolygonDhedgeV2BalanceFetcher implements BalanceFetcher {
constructor(@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit) {}

async getTokenBalances(address: string) {
return await this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId,
groupId: DHEDGE_V_2_DEFINITION.groups.pool.id,
network,
});
}

async getBalances(address: string) {
const assets = await this.getTokenBalances(address);
return presentBalanceFetcherResponse([
{
label: 'Pools',
assets,
},
]);
}
}
102 changes: 102 additions & 0 deletions src/apps/dhedge-v2/polygon/dhedge-v2.pool.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Inject } from '@nestjs/common';
import { gql } from 'graphql-request';
import { filter } from 'lodash';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { getAppImg } from '~app-toolkit/helpers/presentation/image.present';
import { ContractType } from '~position/contract.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { AppTokenPosition } from '~position/position.interface';
import { BaseToken } from '~position/token.interface';
import { Network } from '~types/network.interface';

import { DhedgeV2ContractFactory } from '../contracts';
import { DHEDGE_V_2_DEFINITION } from '../dhedge-v2.definition';

const appId = DHEDGE_V_2_DEFINITION.id;
const groupId = DHEDGE_V_2_DEFINITION.groups.pool.id;
const network = Network.POLYGON_MAINNET;

const query = gql`
query getPools {
pools {
id
name
tokenPrice
assets {
id
}
}
}
`;

interface DHedgeResponse {
pools: {
id: string;
name: string;
tokenPrice: string;
assets: { id: string }[];
}[];
}

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

async getPositions() {
const endpoint = 'https://api.thegraph.com/subgraphs/name/dhedge/dhedge-v2-polygon';
const { pools } = await this.appToolkit.helpers.theGraphHelper.request<DHedgeResponse>({ endpoint, query });

const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const dUSD = baseTokens.find(t => t.address === '0xbae28251b2a4e621aa7e20538c06dee010bc06de')!;

const multicall = this.appToolkit.getMulticall(network);

const tokens = await Promise.all(
pools.map(async pool => {
const address = pool.id.toLowerCase();
const contract = this.contractFactory.erc20({ address, network });

const [symbol, decimals, supplyRaw] = await Promise.all([
multicall.wrap(contract).symbol(),
multicall.wrap(contract).decimals(),
multicall.wrap(contract).totalSupply(),
]);
const supply = Number(supplyRaw) / 10 ** decimals;
const tokens = filter(
pool.assets.map(asset => baseTokens.find(t => t.address === asset.id.toLowerCase())),
) as BaseToken[];
const pricePerShare = Number(pool.tokenPrice) / 10 ** decimals;
const price = pricePerShare * dUSD.price;
const images = [getAppImg(DHEDGE_V_2_DEFINITION.id)];

const token: AppTokenPosition = {
type: ContractType.APP_TOKEN,
appId,
groupId,
address,
network,
symbol,
decimals,
supply,
tokens,
price,
pricePerShare,
dataProps: {},
displayProps: {
label: pool.name,
images,
},
};

return token;
}),
);

return tokens;
}
}

0 comments on commit 55045f6

Please sign in to comment.