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

Commit

Permalink
feat(phuture): Support Avalanche CAI index (#1382)
Browse files Browse the repository at this point in the history
  • Loading branch information
jn-lp authored Sep 12, 2022
1 parent c4d346f commit 9b46c40
Show file tree
Hide file tree
Showing 19 changed files with 2,291 additions and 183 deletions.
Binary file modified src/apps/phuture/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.
36 changes: 36 additions & 0 deletions src/apps/phuture/avalanche/phuture.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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 { BalanceFetcher } from '~balance/balance-fetcher.interface';
import { Network } from '~types/network.interface';

import { PHUTURE_DEFINITION } from '../phuture.definition';

const network = Network.AVALANCHE_MAINNET;

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

async getIndexTokenBalances(address: string) {
return this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({
address,
appId: PHUTURE_DEFINITION.id,
groupId: PHUTURE_DEFINITION.groups.index.id,
network,
});
}

async getBalances(address: string) {
const [indexTokenBalances] = await Promise.all([this.getIndexTokenBalances(address)]);

return presentBalanceFetcherResponse([
{
label: 'Indexes',
assets: indexTokenBalances,
},
]);
}
}
111 changes: 111 additions & 0 deletions src/apps/phuture/avalanche/phuture.index.token-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Inject } from '@nestjs/common';
import { utils } from 'ethers';
import _ from 'lodash';

import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present';
import { getTokenImg } 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 { Network } from '~types/network.interface';

import { PhutureContractFactory } from '../contracts';
import { PHUTURE_DEFINITION } from '../phuture.definition';

const appId = PHUTURE_DEFINITION.id;
const groupId = PHUTURE_DEFINITION.groups.index.id;
const network = Network.AVALANCHE_MAINNET;

const addresses = {
CAI: '0x48f88a3fe843ccb0b5003e70b4192c1d7448bef0',
vTokenFactory: '0xa654211ae2fac7e029df45fcdc0acfa77e174134',
};

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

async getPositions() {
const multicall = this.appToolkit.getMulticall(network);

const baseTokens = await this.appToolkit.getBaseTokenPrices(network);

const indexContract = this.phutureContractFactory.managedIndex({ network, address: addresses.CAI });
const vTokenFactoryContract = this.phutureContractFactory.vTokenFactory({
network,
address: addresses.vTokenFactory,
});

const [label, symbol, decimals, supplyRaw, anatomy] = await Promise.all([
multicall.wrap(indexContract).name(),
multicall.wrap(indexContract).symbol(),
multicall.wrap(indexContract).decimals(),
multicall.wrap(indexContract).totalSupply(),
multicall.wrap(indexContract).anatomy(),
]);

const tokensWithLiquidityRaw = await Promise.all(
anatomy._assets.map(async underlyingAddressRaw => {
const underlyingAddress = underlyingAddressRaw.toLowerCase();

const underlyingToken = baseTokens.find(v => v.address === underlyingAddress);
if (!underlyingToken) return null;

const vTokenAddress = await multicall.wrap(vTokenFactoryContract).vTokenOf(underlyingAddress);
const vTokenContract = this.phutureContractFactory.vToken({ network, address: vTokenAddress });
const balanceOfRaw = await multicall.wrap(vTokenContract).assetBalanceOf(indexContract.address);
const balanceOf = utils.formatUnits(balanceOfRaw, underlyingToken.decimals);

return {
liquidity: Number(balanceOf) * underlyingToken.price,
baseToken: underlyingToken,
};
}),
);

const tokensWithLiquidity = _.compact(tokensWithLiquidityRaw);

const tokens = tokensWithLiquidity.map(x => x.baseToken);
const liquidityPerToken = tokensWithLiquidity.map(x => x.liquidity);

const liquidity = _.sum(liquidityPerToken);
const supply = utils.formatEther(supplyRaw);
const pricePerShare = 1;
const price = liquidity / Number(supply);

const statsItems = [{ label: 'Liquidity', value: buildDollarDisplayItem(liquidity) }];

const indexPosition: AppTokenPosition = {
type: ContractType.APP_TOKEN,
appId,
groupId,
address: indexContract.address,
network,
supply: Number(supply),
decimals,
symbol,
price,
pricePerShare,
tokens,

dataProps: {
liquidity,
exchangeable: true,
},

displayProps: {
label,
secondaryLabel: symbol,
images: [getTokenImg(indexContract.address, network)],
statsItems,
},
};

return [indexPosition];
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
Expand Down
40 changes: 40 additions & 0 deletions src/apps/phuture/contracts/abis/v-token-factory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "vToken",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "asset",
"type": "address"
}
],
"name": "VTokenCreated",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "vTokenOf",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]
Loading

0 comments on commit 9b46c40

Please sign in to comment.