forked from Zapper-fi/studio
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(steak-hut): Add SteakHut integration (Zapper-fi#890)
- Loading branch information
Showing
21 changed files
with
4,972 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,91 @@ | ||
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 { SteakHutContractFactory, SteakHutStaking, SteakHutPool } from '../contracts'; | ||
import { STEAK_HUT_DEFINITION } from '../steak-hut.definition'; | ||
|
||
const appId = STEAK_HUT_DEFINITION.id; | ||
const network = Network.AVALANCHE_MAINNET; | ||
|
||
@Register.BalanceFetcher(STEAK_HUT_DEFINITION.id, network) | ||
export class AvalancheSteakHutBalanceFetcher implements BalanceFetcher { | ||
constructor( | ||
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, | ||
@Inject(SteakHutContractFactory) private readonly contractFactory: SteakHutContractFactory, | ||
) {} | ||
|
||
private async getTokenBalances(address: string) { | ||
return this.appToolkit.helpers.tokenBalanceHelper.getTokenBalances({ | ||
appId, | ||
groupId: STEAK_HUT_DEFINITION.groups.ve.id, | ||
network, | ||
address, | ||
}); | ||
} | ||
|
||
private async getStakedBalances(address: string) { | ||
return this.appToolkit.helpers.singleStakingContractPositionBalanceHelper.getBalances<SteakHutStaking>({ | ||
appId, | ||
network, | ||
address, | ||
groupId: STEAK_HUT_DEFINITION.groups.staking.id, | ||
resolveContract: ({ address, network }) => this.contractFactory.steakHutStaking({ address, network }), | ||
resolveStakedTokenBalance: ({ contract, address, multicall }) => | ||
multicall | ||
.wrap(contract) | ||
.userInfo(address) | ||
.then(x => x.amount), | ||
resolveRewardTokenBalances: ({ contract, address, multicall }) => multicall.wrap(contract).pendingTokens(address), | ||
}); | ||
} | ||
|
||
private async getPoolBalances(address: string) { | ||
return this.appToolkit.helpers.masterChefContractPositionBalanceHelper.getBalances<SteakHutPool>({ | ||
address, | ||
network, | ||
appId, | ||
groupId: STEAK_HUT_DEFINITION.groups.pool.id, | ||
resolveChefContract: ({ contractAddress }) => | ||
this.contractFactory.steakHutPool({ network, address: contractAddress }), | ||
resolveStakedTokenBalance: this.appToolkit.helpers.masterChefDefaultStakedBalanceStrategy.build({ | ||
resolveStakedBalance: ({ contract, multicall, contractPosition }) => | ||
multicall | ||
.wrap(contract) | ||
.userInfo(contractPosition.dataProps.poolIndex, address) | ||
.then(v => v.amount), | ||
}), | ||
resolveClaimableTokenBalances: this.appToolkit.helpers.masterChefDefaultClaimableBalanceStrategy.build({ | ||
resolveClaimableBalance: ({ multicall, contract, contractPosition, address }) => | ||
multicall.wrap(contract).pendingJoe(contractPosition.dataProps.poolIndex, address), | ||
}), | ||
}); | ||
} | ||
|
||
async getBalances(address: string) { | ||
const [tokenBalances, stakedBalances, poolBalances] = await Promise.all([ | ||
this.getTokenBalances(address), | ||
this.getStakedBalances(address), | ||
this.getPoolBalances(address), | ||
]); | ||
|
||
return presentBalanceFetcherResponse([ | ||
{ | ||
label: 'Tokens', | ||
assets: tokenBalances, | ||
}, | ||
{ | ||
label: 'Staking', | ||
assets: stakedBalances, | ||
}, | ||
{ | ||
label: 'Pools', | ||
assets: poolBalances, | ||
}, | ||
]); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/apps/steak-hut/avalanche/steak-hut.pool.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,41 @@ | ||
import { Inject } from '@nestjs/common'; | ||
|
||
import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; | ||
import { Register } from '~app-toolkit/decorators'; | ||
import { TRADER_JOE_DEFINITION } from '~apps/trader-joe/trader-joe.definition'; | ||
import { PositionFetcher } from '~position/position-fetcher.interface'; | ||
import { ContractPosition } from '~position/position.interface'; | ||
import { Network } from '~types/network.interface'; | ||
|
||
import { SteakHutContractFactory, SteakHutPool } from '../contracts'; | ||
import { STEAK_HUT_DEFINITION } from '../steak-hut.definition'; | ||
|
||
const appId = STEAK_HUT_DEFINITION.id; | ||
const groupId = STEAK_HUT_DEFINITION.groups.pool.id; | ||
const network = Network.AVALANCHE_MAINNET; | ||
|
||
@Register.ContractPositionFetcher({ appId, groupId, network }) | ||
export class AvalancheSteakHutPoolContractPositionFetcher implements PositionFetcher<ContractPosition> { | ||
constructor( | ||
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, | ||
@Inject(SteakHutContractFactory) private readonly contractFactory: SteakHutContractFactory, | ||
) {} | ||
|
||
async getPositions() { | ||
return this.appToolkit.helpers.masterChefContractPositionHelper.getContractPositions<SteakHutPool>({ | ||
address: '0xddBfBd5dc3BA0FeB96Cb513B689966b2176d4c09'.toLowerCase(), | ||
appId, | ||
groupId, | ||
network, | ||
dependencies: [{ appId: TRADER_JOE_DEFINITION.id, groupIds: [TRADER_JOE_DEFINITION.groups.pool.id], network }], | ||
resolveContract: ({ address, network }) => this.contractFactory.steakHutPool({ address, network }), | ||
resolvePoolLength: ({ multicall, contract }) => multicall.wrap(contract).poolLength(), | ||
resolveDepositTokenAddress: ({ poolIndex, contract, multicall }) => | ||
multicall | ||
.wrap(contract) | ||
.poolInfo(poolIndex) | ||
.then(v => v.lpToken), | ||
resolveRewardTokenAddresses: ({ multicall, contract }) => multicall.wrap(contract).JOE(), | ||
}); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
src/apps/steak-hut/avalanche/steak-hut.staking.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,55 @@ | ||
import { Inject } from '@nestjs/common'; | ||
import { flatten } from 'lodash'; | ||
|
||
import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; | ||
import { Register } from '~app-toolkit/decorators'; | ||
import { TRADER_JOE_DEFINITION } from '~apps/trader-joe'; | ||
import { PositionFetcher } from '~position/position-fetcher.interface'; | ||
import { ContractPosition } from '~position/position.interface'; | ||
import { Network } from '~types/network.interface'; | ||
|
||
import { SteakHutContractFactory, SteakHutStaking } from '../contracts'; | ||
import { STEAK_HUT_DEFINITION } from '../steak-hut.definition'; | ||
|
||
const appId = STEAK_HUT_DEFINITION.id; | ||
const groupId = STEAK_HUT_DEFINITION.groups.staking.id; | ||
const network = Network.AVALANCHE_MAINNET; | ||
|
||
@Register.ContractPositionFetcher({ appId, groupId, network }) | ||
export class AvalancheSteakHutStakingContractPositionFetcher implements PositionFetcher<ContractPosition> { | ||
constructor( | ||
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, | ||
@Inject(SteakHutContractFactory) private readonly contractFactory: SteakHutContractFactory, | ||
) {} | ||
|
||
async getSingleStakingPosition(address: string) { | ||
return this.appToolkit.helpers.singleStakingFarmContractPositionHelper.getContractPositions<SteakHutStaking>({ | ||
appId, | ||
groupId, | ||
network, | ||
dependencies: [ | ||
{ appId, groupIds: [STEAK_HUT_DEFINITION.groups.ve.id], network }, | ||
{ appId: TRADER_JOE_DEFINITION.id, groupIds: [TRADER_JOE_DEFINITION.groups.xJoe.id], network }, | ||
], | ||
resolveFarmAddresses: async () => [address], | ||
resolveStakedTokenAddress: async ({ multicall, contract }) => multicall.wrap(contract).inputToken(), | ||
resolveFarmContract: opts => this.contractFactory.steakHutStaking(opts), | ||
resolveRewardTokenAddresses: async ({ multicall, contract }) => multicall.wrap(contract).rewardToken(), | ||
resolveIsActive: async ({ multicall, contract }) => multicall.wrap(contract).isRewarderEnabled(), | ||
resolveRois: () => ({ dailyROI: 0, weeklyROI: 0, yearlyROI: 0 }), // TODO: calculated based on tokenPerSec | ||
}); | ||
} | ||
|
||
async getHJoePositions() { | ||
return this.getSingleStakingPosition('0x4E664284B7fbD10633768D59c17D959D9cB8deE2'.toLowerCase()); | ||
} | ||
|
||
async getSteakPosition() { | ||
return this.getSingleStakingPosition('0x1f6866E1A684247886545503F8E6e76e328aDE34'.toLowerCase()); | ||
} | ||
|
||
async getPositions() { | ||
const positions = await Promise.all([this.getHJoePositions(), this.getSteakPosition()]); | ||
return flatten(positions); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/apps/steak-hut/avalanche/steak-hut.ve.token-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,68 @@ | ||
import { Inject } from '@nestjs/common'; | ||
|
||
import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; | ||
import { Register } from '~app-toolkit/decorators'; | ||
import { getImagesFromToken } 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 { SteakHutContractFactory } from '../contracts'; | ||
import { STEAK_HUT_DEFINITION } from '../steak-hut.definition'; | ||
|
||
const appId = STEAK_HUT_DEFINITION.id; | ||
const groupId = STEAK_HUT_DEFINITION.groups.ve.id; | ||
const network = Network.AVALANCHE_MAINNET; | ||
|
||
const address = '0xe7250b05bd8DEE615ecC681eda1196aDD5156F2B'.toLowerCase(); | ||
|
||
// TODO: remove when this becomes a baseToken | ||
|
||
@Register.TokenPositionFetcher({ appId, groupId, network }) | ||
export class AvalancheSteakHutVeTokenFetcher implements PositionFetcher<AppTokenPosition> { | ||
constructor( | ||
@Inject(APP_TOOLKIT) private readonly appToolkit: IAppToolkit, | ||
@Inject(SteakHutContractFactory) private readonly contractFactory: SteakHutContractFactory, | ||
) {} | ||
|
||
async getPositions() { | ||
const baseTokens = await this.appToolkit.getBaseTokenPrices(network); | ||
const multicall = this.appToolkit.getMulticall(network); | ||
const contract = this.contractFactory.steakHutHjoe({ network, address }); | ||
|
||
const [symbol, decimals, supplyRaw, underlyingTokenAddressRaw] = await Promise.all([ | ||
multicall.wrap(contract).symbol(), | ||
multicall.wrap(contract).decimals(), | ||
multicall.wrap(contract).totalSupply(), | ||
multicall.wrap(contract).JOE(), | ||
]); | ||
|
||
const supply = Number(supplyRaw) / 10 ** decimals; | ||
const underlyingTokenAddress = underlyingTokenAddressRaw.toLowerCase(); | ||
const underlyingToken = baseTokens.find(v => v.address === underlyingTokenAddress)!; | ||
|
||
const images = getImagesFromToken(underlyingToken); | ||
|
||
const token: AppTokenPosition = { | ||
type: ContractType.APP_TOKEN, | ||
address, | ||
network, | ||
appId, | ||
groupId, | ||
symbol, | ||
decimals, | ||
supply, | ||
price: underlyingToken.price, | ||
pricePerShare: 1, | ||
tokens: [underlyingToken], | ||
dataProps: {}, | ||
displayProps: { | ||
label: symbol, | ||
images, | ||
}, | ||
}; | ||
|
||
return [token]; | ||
} | ||
} |
Oops, something went wrong.