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 383
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(camelot): Extract to Studio (#2707)
- Loading branch information
Showing
28 changed files
with
15,871 additions
and
0 deletions.
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
src/apps/camelot/arbitrum/camelot.dividend.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,93 @@ | ||
import { Inject } from '@nestjs/common'; | ||
import { range } from 'lodash'; | ||
|
||
import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; | ||
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; | ||
import { DefaultDataProps } from '~position/display.interface'; | ||
import { MetaType } from '~position/position.interface'; | ||
import { isClaimable } from '~position/position.utils'; | ||
import { GetDefinitionsParams } from '~position/template/app-token.template.types'; | ||
import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher'; | ||
import { GetTokenBalancesParams, GetTokenDefinitionsParams } from '~position/template/contract-position.template.types'; | ||
|
||
import { CamelotContractFactory, CamelotDividend } from '../contracts'; | ||
|
||
export type CamelotDividendDefinition = { | ||
address: string; | ||
suppliedTokenAddress: string; | ||
rewardTokenAddresses: string[]; | ||
}; | ||
|
||
@PositionTemplate() | ||
export class ArbitrumCamelotDividendContractPositionFetcher extends ContractPositionTemplatePositionFetcher< | ||
CamelotDividend, | ||
DefaultDataProps, | ||
CamelotDividendDefinition | ||
> { | ||
groupLabel = 'Dividend'; | ||
|
||
dividendContractAddress = '0x5422aa06a38fd9875fc2501380b40659feebd3bb'; | ||
|
||
constructor( | ||
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, | ||
@Inject(CamelotContractFactory) protected readonly contractFactory: CamelotContractFactory, | ||
) { | ||
super(appToolkit); | ||
} | ||
|
||
getContract(address: string): CamelotDividend { | ||
return this.contractFactory.camelotDividend({ address, network: this.network }); | ||
} | ||
|
||
async getDefinitions({ multicall }: GetDefinitionsParams): Promise<CamelotDividendDefinition[]> { | ||
const dividenContract = this.contractFactory.camelotDividend({ | ||
address: this.dividendContractAddress, | ||
network: this.network, | ||
}); | ||
|
||
const numRewardToken = await multicall.wrap(dividenContract).distributedTokensLength(); | ||
|
||
const rewardTokenAddresses = await Promise.all( | ||
range(0, numRewardToken.toNumber()).map(async index => { | ||
const rewardTokenAddressRaw = await multicall.wrap(dividenContract).distributedToken(index); | ||
return rewardTokenAddressRaw.toLowerCase(); | ||
}), | ||
); | ||
|
||
const suppliedTokenAddress = await multicall.wrap(dividenContract).xGrailToken(); | ||
|
||
return [{ address: this.dividendContractAddress, suppliedTokenAddress, rewardTokenAddresses }]; | ||
} | ||
|
||
async getTokenDefinitions({ definition }: GetTokenDefinitionsParams<CamelotDividend, CamelotDividendDefinition>) { | ||
return [ | ||
{ | ||
metaType: MetaType.SUPPLIED, | ||
address: definition.suppliedTokenAddress, | ||
network: this.network, | ||
}, | ||
...definition.rewardTokenAddresses.map(address => ({ | ||
metaType: MetaType.CLAIMABLE, | ||
address, | ||
network: this.network, | ||
})), | ||
]; | ||
} | ||
|
||
async getLabel() { | ||
return `Dividends`; | ||
} | ||
|
||
async getTokenBalancesPerPosition({ address, contract, contractPosition }: GetTokenBalancesParams<CamelotDividend>) { | ||
const allocation = await contract.usersAllocation(address); | ||
const claimableTokens = contractPosition.tokens.filter(isClaimable); | ||
|
||
const claimableBalances = await Promise.all( | ||
claimableTokens.map(token => { | ||
return contract.pendingDividendsAmount(token.address, address); | ||
}), | ||
); | ||
|
||
return [allocation, ...claimableBalances]; | ||
} | ||
} |
144 changes: 144 additions & 0 deletions
144
src/apps/camelot/arbitrum/camelot.farm.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,144 @@ | ||
import { Inject, NotImplementedException } from '@nestjs/common'; | ||
import { range } from 'lodash'; | ||
|
||
import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; | ||
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; | ||
import { drillBalance } from '~app-toolkit/helpers/drill-balance.helper'; | ||
import { getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present'; | ||
import { DefaultDataProps } from '~position/display.interface'; | ||
import { ContractPositionBalance } from '~position/position-balance.interface'; | ||
import { MetaType } from '~position/position.interface'; | ||
import { GetDefinitionsParams } from '~position/template/app-token.template.types'; | ||
import { GetDisplayPropsParams, GetTokenDefinitionsParams } from '~position/template/contract-position.template.types'; | ||
import { CustomContractPositionTemplatePositionFetcher } from '~position/template/custom-contract-position.template.position-fetcher'; | ||
|
||
import { CamelotContractFactory, CamelotNftPool } from '../contracts'; | ||
|
||
type CamelotFarmContractPositionDefinition = { | ||
address: string; | ||
stakingTokenAddress: string; | ||
rewardTokenAddresses: string[]; | ||
}; | ||
|
||
@PositionTemplate() | ||
export class ArbitrumCamelotFarmContractPositionFetcher extends CustomContractPositionTemplatePositionFetcher<CamelotNftPool> { | ||
groupLabel = 'Farm'; | ||
|
||
masterContractAddress = '0x55401a4f396b3655f66bf6948a1a4dc61dfc21f4'; | ||
|
||
constructor( | ||
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, | ||
@Inject(CamelotContractFactory) protected readonly contractFactory: CamelotContractFactory, | ||
) { | ||
super(appToolkit); | ||
} | ||
|
||
async getDefinitions({ multicall }: GetDefinitionsParams): Promise<CamelotFarmContractPositionDefinition[]> { | ||
const masterContract = this.contractFactory.camelotMaster({ | ||
address: this.masterContractAddress, | ||
network: this.network, | ||
}); | ||
const poolLength = await multicall.wrap(masterContract).poolsLength(); | ||
|
||
const poolAddresses = await Promise.all( | ||
range(0, Number(poolLength)).map(async i => { | ||
const poolAddressRaw = await multicall.wrap(masterContract).getPoolAddressByIndex(i); | ||
return poolAddressRaw.toLowerCase(); | ||
}), | ||
); | ||
|
||
const farmDefinitions = await Promise.all( | ||
poolAddresses.map(async address => { | ||
const nftPoolContract = this.contractFactory.camelotNftPool({ address, network: this.network }); | ||
const poolInfo = await multicall.wrap(nftPoolContract).getPoolInfo(); | ||
const { lpToken, grailToken, xGrailToken } = poolInfo; | ||
|
||
return { | ||
address, | ||
stakingTokenAddress: lpToken.toLowerCase(), | ||
rewardTokenAddresses: [grailToken.toLowerCase(), xGrailToken.toLowerCase()], | ||
}; | ||
}), | ||
); | ||
|
||
return farmDefinitions; | ||
} | ||
|
||
async getTokenDefinitions({ | ||
definition, | ||
}: GetTokenDefinitionsParams<CamelotNftPool, CamelotFarmContractPositionDefinition>) { | ||
return [ | ||
{ | ||
metaType: MetaType.SUPPLIED, | ||
address: definition.stakingTokenAddress, | ||
network: this.network, | ||
}, | ||
...definition.rewardTokenAddresses.map(address => ({ | ||
metaType: MetaType.CLAIMABLE, | ||
address, | ||
network: this.network, | ||
})), | ||
]; | ||
} | ||
|
||
getContract(address: string): CamelotNftPool { | ||
return this.contractFactory.camelotNftPool({ network: this.network, address }); | ||
} | ||
|
||
async getLabel({ contractPosition }: GetDisplayPropsParams<CamelotNftPool>) { | ||
return `${getLabelFromToken(contractPosition.tokens[0])}`; | ||
} | ||
|
||
getTokenBalancesPerPosition(): never { | ||
throw new NotImplementedException(); | ||
} | ||
|
||
async getBalances(address: string): Promise<ContractPositionBalance<DefaultDataProps>[]> { | ||
const multicall = this.appToolkit.getMulticall(this.network); | ||
|
||
const contractPositions = await this.appToolkit.getAppContractPositions({ | ||
appId: this.appId, | ||
network: this.network, | ||
groupIds: [this.groupId], | ||
}); | ||
|
||
const balances = await Promise.all( | ||
contractPositions.map(async contractPosition => { | ||
const nftPoolContract = this.contractFactory.camelotNftPool({ | ||
address: contractPosition.address, | ||
network: this.network, | ||
}); | ||
const [numPositionsRaw, xGrailRewardsShareRaw] = await Promise.all([ | ||
multicall.wrap(nftPoolContract).balanceOf(address), | ||
multicall.wrap(nftPoolContract).xGrailRewardsShare(), | ||
]); | ||
|
||
return await Promise.all( | ||
range(0, Number(numPositionsRaw)).map(async i => { | ||
const tokenId = await multicall.wrap(nftPoolContract).tokenOfOwnerByIndex(address, i); | ||
const [stakingPosition, rewardAmountsCombined] = await Promise.all([ | ||
multicall.wrap(nftPoolContract).getStakingPosition(tokenId), | ||
multicall.wrap(nftPoolContract).pendingRewards(tokenId), | ||
]); | ||
const xGrailRewardsShare = Number(xGrailRewardsShareRaw) / 10 ** 4; | ||
|
||
const xGrailBalance = Number(rewardAmountsCombined) * xGrailRewardsShare; | ||
const grailBalance = Number(rewardAmountsCombined) - xGrailBalance; | ||
|
||
const suppliedtAmount = drillBalance(contractPosition.tokens[0], stakingPosition.amount.toString()); | ||
const grailAmount = drillBalance(contractPosition.tokens[1], grailBalance.toString()); | ||
const xGrailAmount = drillBalance(contractPosition.tokens[2], xGrailBalance.toString()); | ||
|
||
return { | ||
...contractPosition, | ||
tokens: [suppliedtAmount, grailAmount, xGrailAmount], | ||
balanceUSD: suppliedtAmount.balanceUSD + grailAmount.balanceUSD + xGrailAmount.balanceUSD, | ||
}; | ||
}), | ||
); | ||
}), | ||
); | ||
|
||
return balances.flat(); | ||
} | ||
} |
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,10 @@ | ||
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; | ||
import { UniswapV2DefaultPoolSubgraphTemplateTokenFetcher } from '~apps/uniswap-v2/common/uniswap-v2.default.subgraph.template.token-fetcher'; | ||
|
||
@PositionTemplate() | ||
export class ArbitrumCamelotPoolTokenFetcher extends UniswapV2DefaultPoolSubgraphTemplateTokenFetcher { | ||
groupLabel = 'Pools'; | ||
|
||
subgraphUrl = 'https://api.thegraph.com/subgraphs/name/camelotlabs/camelot-amm'; | ||
factoryAddress = '0x6eccab422d763ac031210895c81787e87b43a652'; | ||
} |
36 changes: 36 additions & 0 deletions
36
src/apps/camelot/arbitrum/camelot.x-grail.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,36 @@ | ||
import { Inject } from '@nestjs/common'; | ||
|
||
import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; | ||
import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; | ||
import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher'; | ||
import { GetUnderlyingTokensParams } from '~position/template/app-token.template.types'; | ||
|
||
import { CamelotContractFactory, CamelotXGrail } from '../contracts'; | ||
|
||
@PositionTemplate() | ||
export class ArbitrumXGrailTokenFetcher extends AppTokenTemplatePositionFetcher<CamelotXGrail> { | ||
groupLabel = 'xGRAIL'; | ||
|
||
constructor( | ||
@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, | ||
@Inject(CamelotContractFactory) private readonly contractFactory: CamelotContractFactory, | ||
) { | ||
super(appToolkit); | ||
} | ||
|
||
getContract(address: string): CamelotXGrail { | ||
return this.contractFactory.camelotXGrail({ address, network: this.network }); | ||
} | ||
|
||
async getAddresses(): Promise<string[]> { | ||
return ['0x3caae25ee616f2c8e13c74da0813402eae3f496b']; | ||
} | ||
|
||
async getUnderlyingTokenDefinitions({ contract }: GetUnderlyingTokensParams<CamelotXGrail>) { | ||
return [{ address: await contract.grailToken(), network: this.network }]; | ||
} | ||
|
||
async getPricePerShare() { | ||
return [1]; | ||
} | ||
} |
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,23 @@ | ||
import { Module } from '@nestjs/common'; | ||
|
||
import { AbstractApp } from '~app/app.dynamic-module'; | ||
import { UniswapV2ContractFactory } from '~apps/uniswap-v2/contracts'; | ||
|
||
import { ArbitrumCamelotDividendContractPositionFetcher } from './arbitrum/camelot.dividend.contract-position-fetcher'; | ||
import { ArbitrumCamelotFarmContractPositionFetcher } from './arbitrum/camelot.farm.contract-position-fetcher'; | ||
import { ArbitrumCamelotPoolTokenFetcher } from './arbitrum/camelot.pool.token-fetcher'; | ||
import { ArbitrumXGrailTokenFetcher } from './arbitrum/camelot.x-grail.token-fetcher'; | ||
import { CamelotContractFactory } from './contracts'; | ||
|
||
@Module({ | ||
providers: [ | ||
CamelotContractFactory, | ||
UniswapV2ContractFactory, | ||
// Arbitrum | ||
ArbitrumCamelotPoolTokenFetcher, | ||
ArbitrumXGrailTokenFetcher, | ||
ArbitrumCamelotDividendContractPositionFetcher, | ||
ArbitrumCamelotFarmContractPositionFetcher, | ||
], | ||
}) | ||
export class CamelotAppModule extends AbstractApp() {} |
Oops, something went wrong.