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

Commit

Permalink
feat(solace): Update Solace Finance integration, add Aurora support (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardishere authored Jun 30, 2022
1 parent abbecd6 commit 0bbdf46
Show file tree
Hide file tree
Showing 25 changed files with 1,667 additions and 5,662 deletions.
69 changes: 69 additions & 0 deletions src/apps/solace/aurora/solace.balance-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Inject } from '@nestjs/common';

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 { SolaceBondBalanceHelper } from '../helpers/SolaceBondBalanceHelper';
import { SolacePolicyBalanceHelper } from '../helpers/SolacePolicyBalanceHelper';
import { SolaceXSBalanceHelper } from '../helpers/SolaceXSBalanceHelper';
import { SOLACE_DEFINITION } from '../solace.definition';

const network = Network.AURORA_MAINNET;

@Register.BalanceFetcher(SOLACE_DEFINITION.id, network)
export class AuroraSolaceBalanceFetcher implements BalanceFetcher {
constructor(
@Inject(SolaceBondBalanceHelper)
private readonly solaceBondBalanceHelper: SolaceBondBalanceHelper,
@Inject(SolacePolicyBalanceHelper)
private readonly solacePolicyBalanceHelper: SolacePolicyBalanceHelper,
@Inject(SolaceXSBalanceHelper)
private readonly solaceXSBalanceHelper: SolaceXSBalanceHelper,
) {}

async getXSLockerBalance(address: string) {
return this.solaceXSBalanceHelper.getBalances({
address,
network,
});
}

async getBondBalance(address: string) {
return this.solaceBondBalanceHelper.getBalances({
address,
network,
});
}

async getPolicyBalance(address: string) {
return this.solacePolicyBalanceHelper.getBalances({
address,
network,
});
}

async getBalances(address: string) {
const [xslockerBal, bondBal, policyBal] = await Promise.all([
this.getXSLockerBalance(address),
this.getBondBalance(address),
this.getPolicyBalance(address),
]);

return presentBalanceFetcherResponse([
{
label: 'xsLocker',
assets: xslockerBal,
},
{
label: 'Bonds',
assets: bondBal,
},
{
label: 'Policies',
assets: policyBal,
},
]);
}
}
90 changes: 90 additions & 0 deletions src/apps/solace/aurora/solace.bonds.contract-position-fetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Inject } from '@nestjs/common';
import { compact } from 'lodash';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present';
import { getImagesFromToken, getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present';
import { ContractType } from '~position/contract.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { ContractPosition } from '~position/position.interface';
import { claimable, supplied } from '~position/position.utils';
import { Network } from '~types/network.interface';

import { SolaceContractFactory } from '../contracts';
import { SOLACE_DEFINITION } from '../solace.definition';

const appId = SOLACE_DEFINITION.id;
const groupId = SOLACE_DEFINITION.groups.bonds.id;
const network = Network.AURORA_MAINNET;

export const SOLACE_ADDRESS = '0x501ace9c35e60f03a2af4d484f49f9b1efde9f40';

const BOND_TELLER_ADDRESSES = [
'0x501ace677634fd09a876e88126076933b686967a', // DAI Bond
'0x501ace95141f3eb59970dd64af0405f6056fb5d8', // ETH Bond
'0x501ace7e977e06a3cb55f9c28d5654c9d74d5ca9', // USDC Bond
'0x501acef0d0c73bd103337e6e9fd49d58c426dc27', // WBTC Bond
'0x501ace5ceec693df03198755ee80d4ce0b5c55fe', // USDT Bond
'0x501acef4f8397413c33b13cb39670ad2f17bfe62', // FRAX Bond
'0x501ace71a83cbe03b1467a6ffeaeb58645d844b4', // NEAR Bond
'0x501ace35f0b7fad91c199824b8fe555ee9037aa3', // AURORA Bond
];

@Register.ContractPositionFetcher({ appId, groupId, network, options: { includeInTvl: true } })
export class AuroraSolaceBondsContractPositionFetcher implements PositionFetcher<ContractPosition> {
constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(SolaceContractFactory) private readonly solaceContractFactory: SolaceContractFactory,
) {}

async getPositions() {
const multicall = this.appToolkit.getMulticall(network);
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const solaceToken = baseTokens.find(t => t.address === SOLACE_ADDRESS)!;

const positions = await Promise.all(
BOND_TELLER_ADDRESSES.map(async bondTellerAddress => {
const bondTellerContract = this.solaceContractFactory.bondTellerErc20({ address: bondTellerAddress, network });

const [underlyingAddressRaw, underWritingPoolAddress, name] = await Promise.all([
multicall.wrap(bondTellerContract).principal(),
multicall.wrap(bondTellerContract).underwritingPool(),
multicall.wrap(bondTellerContract).name(),
]);

const underlyingAddress = underlyingAddressRaw.toLowerCase();

const depositToken = baseTokens.find(v => v.address === underlyingAddress);
if (!depositToken || !solaceToken) return null;
const tokens = [supplied(depositToken), claimable(solaceToken)];

const baseTokenContract = this.solaceContractFactory.erc20({ address: underlyingAddress, network });
const balanceOfRaw = await multicall.wrap(baseTokenContract).balanceOf(underWritingPoolAddress);
const balanceOf = Number(balanceOfRaw) / 10 ** depositToken.decimals;
const liquidity = balanceOf * depositToken.price;

const position: ContractPosition = {
type: ContractType.POSITION,
appId,
groupId,
address: bondTellerAddress,
network,
tokens,
dataProps: {
liquidity,
},
displayProps: {
label: name,
images: getImagesFromToken(depositToken),
statsItems: [{ label: 'Liquidity', value: buildDollarDisplayItem(liquidity) }],
},
};

return position;
}),
);

return compact(positions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Inject } from '@nestjs/common';

import { IAppToolkit, APP_TOOLKIT } 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 { ContractPosition } from '~position/position.interface';
import { supplied } from '~position/position.utils';
import { Network } from '~types/network.interface';

import { SolaceContractFactory } from '../contracts';
import { SOLACE_DEFINITION } from '../solace.definition';

const appId = SOLACE_DEFINITION.id;
const groupId = SOLACE_DEFINITION.groups.policies.id;
const network = Network.AURORA_MAINNET;

const DAI_ADDRESS = '0xe3520349f477a5f6eb06107066048508498a291b';
const SOLACE_COVER_PRODUCT_ADDRESS = '0x501acec83d440c00644ca5c48d059e1840852a64';
const SOLACE_ADDRESS = '0x501ace9c35e60f03a2af4d484f49f9b1efde9f40';

const PREMIUM_POOL_ADDRESS = '0x0436c20030d0c2e278e7e8e4b42d304a6420d3bb';
const PREMIUM_POOL_TOKENS = [
'0x501ace9c35e60f03a2af4d484f49f9b1efde9f40', // solace
'0xe3520349f477a5f6eb06107066048508498a291b', // dai
'0xb12bfca5a55806aaf64e99521918a4bf0fc40802', // usdc
'0x4988a896b1227218e4a686fde5eabdcabd91571f', // usdt
'0xda2585430fef327ad8ee44af8f1f989a2a91a3d2', // frax
'0xdfa46478f9e5ea86d57387849598dbfb2e964b02', // mimatic
];

@Register.ContractPositionFetcher({ appId, groupId, network })
export class AuroraSolacePoliciesContractPositionFetcher implements PositionFetcher<ContractPosition> {
constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(SolaceContractFactory) private readonly solaceContractFactory: SolaceContractFactory,
) {}

async getPositions() {
const multicall = this.appToolkit.getMulticall(network);
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const dai = baseTokens.find(t => t.address === DAI_ADDRESS)!;
const tokens = [supplied(dai)];

let liquidity = 0;
for(var i = 0; i < PREMIUM_POOL_TOKENS.length; ++i) {
const tokenAddr = PREMIUM_POOL_TOKENS[i];
const tokenContract = this.solaceContractFactory.erc20({ address: tokenAddr, network });
const [balanceOfRaw, decimals] = await Promise.all([
multicall.wrap(tokenContract).balanceOf(PREMIUM_POOL_ADDRESS),
multicall.wrap(tokenContract).decimals(),
]);
const balanceOf = Number(balanceOfRaw) / 10 ** decimals;
const tokenZapper = baseTokens.find(t => t.address === tokenAddr)!;
if(!!tokenZapper) liquidity += balanceOf * tokenZapper.price;
}

const position: ContractPosition = {
type: ContractType.POSITION,
appId,
groupId,
address: SOLACE_COVER_PRODUCT_ADDRESS,
network,
tokens,
dataProps: {
liquidity,
},
displayProps: {
label: `Solace Portfolio Insurance`, // @TODO Might be nice to include cover amount!
images: [getTokenImg(SOLACE_ADDRESS, Network.AURORA_MAINNET)],
statsItems: [{ label: 'Liquidity', value: buildDollarDisplayItem(liquidity) }],
},
};

return [position];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Inject } from '@nestjs/common';

import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface';
import { Register } from '~app-toolkit/decorators';
import { buildDollarDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present';
import { getImagesFromToken } from '~app-toolkit/helpers/presentation/image.present';
import { ContractType } from '~position/contract.interface';
import { PositionFetcher } from '~position/position-fetcher.interface';
import { ContractPosition } from '~position/position.interface';
import { claimable, supplied } from '~position/position.utils';
import { Network } from '~types/network.interface';

import { SolaceContractFactory } from '../contracts';
import { SOLACE_DEFINITION } from '../solace.definition';

const appId = SOLACE_DEFINITION.id;
const groupId = SOLACE_DEFINITION.groups.xslocker.id;
const network = Network.AURORA_MAINNET;

const SOLACE_ADDRESS = '0x501ace9c35e60f03a2af4d484f49f9b1efde9f40';
const XSLOCKER_ADDRESS = '0x501ace47c5b0c2099c4464f681c3fa2ecd3146c1';

@Register.ContractPositionFetcher({ appId, groupId, network })
export class AuroraSolaceXslockerContractPositionFetcher implements PositionFetcher<ContractPosition> {
constructor(
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit,
@Inject(SolaceContractFactory) private readonly solaceContractFactory: SolaceContractFactory,
) {}

async getPositions() {
const multicall = this.appToolkit.getMulticall(network);
const baseTokens = await this.appToolkit.getBaseTokenPrices(network);
const solace = baseTokens.find(t => t.address === SOLACE_ADDRESS)!;
if (!solace) return [];

const solaceTokenContract = this.solaceContractFactory.erc20({ address: SOLACE_ADDRESS, network });
const [balanceOfRaw, decimals] = await Promise.all([
multicall.wrap(solaceTokenContract).balanceOf(XSLOCKER_ADDRESS),
multicall.wrap(solaceTokenContract).decimals(),
]);

const balanceOf = Number(balanceOfRaw) / 10 ** decimals;
const liquidity = balanceOf * solace.price;

const position: ContractPosition = {
type: ContractType.POSITION,
address: XSLOCKER_ADDRESS,
appId,
groupId,
network,
tokens: [supplied(solace), claimable(solace)],
dataProps: {
liquidity,
},
displayProps: {
label: `xsLOCK`,
images: getImagesFromToken(solace),
statsItems: [{ label: 'Liquidity', value: buildDollarDisplayItem(liquidity) }],
},
};

return [position];
}
}
Loading

0 comments on commit 0bbdf46

Please sign in to comment.