This repository has been archived by the owner on Jan 24, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 381
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(solace): Update Solace Finance integration, add Aurora support (#…
…778)
- Loading branch information
1 parent
abbecd6
commit 0bbdf46
Showing
25 changed files
with
1,667 additions
and
5,662 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
90
src/apps/solace/aurora/solace.bonds.contract-position-fetcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
src/apps/solace/aurora/solace.policies.contract-position-fetcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/apps/solace/aurora/solace.xslocker.contract-position-fetcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
} | ||
} |
Oops, something went wrong.