Skip to content

Commit

Permalink
Separate CAKE and LP APR (pancakeswap#1632)
Browse files Browse the repository at this point in the history
* feat: Display base and combined apr in calculator

* fix: Tests

* chore: Rename combinedApr to displayApr
  • Loading branch information
Chef-Cheems authored Jul 2, 2021
1 parent efd2288 commit 02662c3
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 37 deletions.
20 changes: 12 additions & 8 deletions src/__tests__/utils/apr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,28 @@ describe('getPoolApr', () => {

describe('getFarmApr', () => {
it(`returns null when parameters are missing`, () => {
const apr = getFarmApr(null, null, null, null)
expect(apr).toBeNull()
const { cakeRewardsApr, lpRewardsApr } = getFarmApr(null, null, null, null)
expect(cakeRewardsApr).toBeNull()
expect(lpRewardsApr).toEqual(0)
})
it(`returns null when APR is infinite`, () => {
const apr = getFarmApr(BIG_ZERO, BIG_ZERO, BIG_ZERO, '')
expect(apr).toBeNull()
const { cakeRewardsApr, lpRewardsApr } = getFarmApr(BIG_ZERO, BIG_ZERO, BIG_ZERO, '')
expect(cakeRewardsApr).toBeNull()
expect(lpRewardsApr).toEqual(0)
})
it(`get the correct pool APR`, () => {
const apr = getFarmApr(BIG_TEN, new BigNumber(1), new BigNumber(100000), '')
expect(apr).toEqual(4204800)
const { cakeRewardsApr, lpRewardsApr } = getFarmApr(BIG_TEN, new BigNumber(1), new BigNumber(100000), '')
expect(cakeRewardsApr).toEqual(4204800)
expect(lpRewardsApr).toEqual(0)
})
it(`get the correct pool APR combined with LP APR`, () => {
const apr = getFarmApr(
const { cakeRewardsApr, lpRewardsApr } = getFarmApr(
BIG_TEN,
new BigNumber(1),
new BigNumber(100000),
'0x0ed7e52944161450477ee417de9cd3a859b14fd0',
)
expect(apr).toEqual(4204810.5)
expect(cakeRewardsApr).toEqual(4204800)
expect(lpRewardsApr).toEqual(10.5)
})
})
22 changes: 16 additions & 6 deletions src/components/ApyCalculatorModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface ApyCalculatorModalProps {
onDismiss?: () => void
tokenPrice: number
apr: number
displayApr?: string
linkLabel: string
linkHref: string
earningTokenSymbol?: string
Expand Down Expand Up @@ -55,6 +56,7 @@ const ApyCalculatorModal: React.FC<ApyCalculatorModalProps> = ({
onDismiss,
tokenPrice,
apr,
displayApr,
linkLabel,
linkHref,
earningTokenSymbol = 'CAKE',
Expand Down Expand Up @@ -102,12 +104,20 @@ const ApyCalculatorModal: React.FC<ApyCalculatorModalProps> = ({
return (
<Modal title={t('ROI')} onDismiss={onDismiss}>
{isFarm && (
<Flex mb="24px" justifyContent="space-between">
<Text small color="textSubtle">
{t('APR (incl. LP rewards)')}
</Text>
<Text small>{apr.toFixed(roundingDecimals)}%</Text>
</Flex>
<Box>
<Flex mb="8px" justifyContent="space-between">
<Text small color="textSubtle">
{t('APR (incl. LP rewards)')}
</Text>
<Text small>{displayApr}%</Text>
</Flex>
<Flex mb="24px" justifyContent="space-between">
<Text small color="textSubtle">
{t('Base APR (yield only)')}
</Text>
<Text small>{apr.toFixed(roundingDecimals)}%</Text>
</Flex>
</Box>
)}
<Grid>
<GridHeaderItem>
Expand Down
3 changes: 2 additions & 1 deletion src/config/localization/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -903,5 +903,6 @@
"Amount to collect": "Amount to collect",
"Your winnings": "Your winnings",
"Includes your original position and your winnings, minus the %fee% fee.": "Includes your original position and your winnings, minus the %fee% fee.",
"Your funds have been staked in the farm": "Your funds have been staked in the farm"
"Your funds have been staked in the farm": "Your funds have been staked in the farm",
"Base APR (yield only)": "Base APR (yield only)"
}
12 changes: 6 additions & 6 deletions src/utils/apr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ export const getFarmApr = (
cakePriceUsd: BigNumber,
poolLiquidityUsd: BigNumber,
farmAddress: string,
): number => {
): { cakeRewardsApr: number; lpRewardsApr: number } => {
const yearlyCakeRewardAllocation = CAKE_PER_YEAR.times(poolWeight)
const cakeRewardsApr = yearlyCakeRewardAllocation.times(cakePriceUsd).div(poolLiquidityUsd).times(100)
if (cakeRewardsApr.isNaN() || !cakeRewardsApr.isFinite()) {
return null
let cakeRewardsAprAsNumber = null
if (!cakeRewardsApr.isNaN() && cakeRewardsApr.isFinite()) {
cakeRewardsAprAsNumber = cakeRewardsApr.toNumber()
}
const lpRewardsApr = lpAprs[farmAddress.toLocaleLowerCase()] ?? 0
const combinedApr = cakeRewardsApr.plus(lpRewardsApr)
return combinedApr.toNumber()
const lpRewardsApr = lpAprs[farmAddress?.toLocaleLowerCase()] ?? 0
return { cakeRewardsApr: cakeRewardsAprAsNumber, lpRewardsApr }
}

export default null
47 changes: 39 additions & 8 deletions src/views/Farms/Farms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ const StyledImage = styled(Image)`
`
const NUMBER_OF_FARMS_VISIBLE = 12

const getDisplayApr = (cakeRewardsApr?: number, lpRewardsApr?: number) => {
if (cakeRewardsApr && lpRewardsApr) {
return (cakeRewardsApr + lpRewardsApr).toLocaleString('en-US', { maximumFractionDigits: 2 })
}
if (cakeRewardsApr) {
return cakeRewardsApr.toLocaleString('en-US', { maximumFractionDigits: 2 })
}
return null
}

const Farms: React.FC = () => {
const { path } = useRouteMatch()
const { pathname } = useLocation()
Expand Down Expand Up @@ -149,11 +159,11 @@ const Farms: React.FC = () => {
return farm
}
const totalLiquidity = new BigNumber(farm.lpTotalInQuoteToken).times(farm.quoteToken.busdPrice)
const apr = isActive
const { cakeRewardsApr, lpRewardsApr } = isActive
? getFarmApr(new BigNumber(farm.poolWeight), cakePrice, totalLiquidity, farm.lpAddresses[ChainId.MAINNET])
: 0
: { cakeRewardsApr: 0, lpRewardsApr: 0 }

return { ...farm, apr, liquidity: totalLiquidity }
return { ...farm, apr: cakeRewardsApr, lpRewardsApr, liquidity: totalLiquidity }
})

if (query) {
Expand Down Expand Up @@ -182,7 +192,7 @@ const Farms: React.FC = () => {
const sortFarms = (farms: FarmWithStakedValue[]): FarmWithStakedValue[] => {
switch (sortOption) {
case 'apr':
return orderBy(farms, (farm: FarmWithStakedValue) => farm.apr, 'desc')
return orderBy(farms, (farm: FarmWithStakedValue) => farm.apr + farm.lpRewardsApr, 'desc')
case 'multiplier':
return orderBy(
farms,
Expand Down Expand Up @@ -255,7 +265,7 @@ const Farms: React.FC = () => {

const row: RowProps = {
apr: {
value: farm.apr && farm.apr.toLocaleString('en-US', { maximumFractionDigits: 2 }),
value: getDisplayApr(farm.apr, farm.lpRewardsApr),
multiplier: farm.multiplier,
lpLabel,
tokenAddress,
Expand Down Expand Up @@ -320,17 +330,38 @@ const Farms: React.FC = () => {
<FlexLayout>
<Route exact path={`${path}`}>
{farmsStakedMemoized.map((farm) => (
<FarmCard key={farm.pid} farm={farm} cakePrice={cakePrice} account={account} removed={false} />
<FarmCard
key={farm.pid}
farm={farm}
displayApr={getDisplayApr(farm.apr, farm.lpRewardsApr)}
cakePrice={cakePrice}
account={account}
removed={false}
/>
))}
</Route>
<Route exact path={`${path}/history`}>
{farmsStakedMemoized.map((farm) => (
<FarmCard key={farm.pid} farm={farm} cakePrice={cakePrice} account={account} removed />
<FarmCard
key={farm.pid}
farm={farm}
displayApr={getDisplayApr(farm.apr, farm.lpRewardsApr)}
cakePrice={cakePrice}
account={account}
removed
/>
))}
</Route>
<Route exact path={`${path}/archived`}>
{farmsStakedMemoized.map((farm) => (
<FarmCard key={farm.pid} farm={farm} cakePrice={cakePrice} account={account} removed />
<FarmCard
key={farm.pid}
farm={farm}
displayApr={getDisplayApr(farm.apr, farm.lpRewardsApr)}
cakePrice={cakePrice}
account={account}
removed
/>
))}
</Route>
</FlexLayout>
Expand Down
4 changes: 3 additions & 1 deletion src/views/Farms/components/FarmCard/ApyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ export interface ApyButtonProps {
lpLabel?: string
cakePrice?: BigNumber
apr?: number
displayApr?: string
addLiquidityUrl?: string
}

const ApyButton: React.FC<ApyButtonProps> = ({ lpLabel, cakePrice, apr, addLiquidityUrl }) => {
const ApyButton: React.FC<ApyButtonProps> = ({ lpLabel, cakePrice, apr, displayApr, addLiquidityUrl }) => {
const { t } = useTranslation()
const [onPresentApyModal] = useModal(
<ApyCalculatorModal
linkLabel={t('Get %symbol%', { symbol: lpLabel })}
tokenPrice={cakePrice.toNumber()}
apr={apr}
displayApr={displayApr}
linkHref={addLiquidityUrl}
isFarm
/>,
Expand Down
16 changes: 11 additions & 5 deletions src/views/Farms/components/FarmCard/FarmCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ApyButton from './ApyButton'

export interface FarmWithStakedValue extends Farm {
apr?: number
lpRewardsApr?: number
liquidity?: BigNumber
}

Expand Down Expand Up @@ -70,12 +71,13 @@ const ExpandingWrapper = styled.div<{ expanded: boolean }>`

interface FarmCardProps {
farm: FarmWithStakedValue
displayApr: string
removed: boolean
cakePrice?: BigNumber
account?: string
}

const FarmCard: React.FC<FarmCardProps> = ({ farm, removed, cakePrice, account }) => {
const FarmCard: React.FC<FarmCardProps> = ({ farm, displayApr, removed, cakePrice, account }) => {
const { t } = useTranslation()

const [showExpandableSection, setShowExpandableSection] = useState(false)
Expand All @@ -88,8 +90,6 @@ const FarmCard: React.FC<FarmCardProps> = ({ farm, removed, cakePrice, account }
const lpLabel = farm.lpSymbol && farm.lpSymbol.toUpperCase().replace('PANCAKE', '')
const earnLabel = farm.dual ? farm.dual.earnLabel : t('CAKE + Fees')

const farmAPR = farm.apr && farm.apr.toLocaleString('en-US', { maximumFractionDigits: 2 })

const liquidityUrlPathParts = getLiquidityUrlPathParts({
quoteTokenAddress: farm.quoteToken.address,
tokenAddress: farm.token.address,
Expand All @@ -114,8 +114,14 @@ const FarmCard: React.FC<FarmCardProps> = ({ farm, removed, cakePrice, account }
<Text bold style={{ display: 'flex', alignItems: 'center' }}>
{farm.apr ? (
<>
<ApyButton lpLabel={lpLabel} addLiquidityUrl={addLiquidityUrl} cakePrice={cakePrice} apr={farm.apr} />
{farmAPR}%
<ApyButton
lpLabel={lpLabel}
addLiquidityUrl={addLiquidityUrl}
cakePrice={cakePrice}
apr={farm.apr}
displayApr={displayApr}
/>
{displayApr}%
</>
) : (
<Skeleton height={24} width={80} />
Expand Down
8 changes: 7 additions & 1 deletion src/views/Farms/components/FarmTable/Apr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ const Apr: React.FC<AprProps> = ({
<>
<AprWrapper>{value}%</AprWrapper>
{!hideButton && (
<ApyButton lpLabel={lpLabel} cakePrice={cakePrice} apr={originalValue} addLiquidityUrl={addLiquidityUrl} />
<ApyButton
lpLabel={lpLabel}
cakePrice={cakePrice}
apr={originalValue}
displayApr={value}
addLiquidityUrl={addLiquidityUrl}
/>
)}
</>
) : (
Expand Down
3 changes: 2 additions & 1 deletion src/views/Home/components/EarnAPRCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ const EarnAPRCard = () => {
// Filter inactive farms, because their theoretical APR is super high. In practice, it's 0.
if (farm.pid !== 0 && farm.multiplier !== '0X' && farm.lpTotalInQuoteToken && farm.quoteToken.busdPrice) {
const totalLiquidity = new BigNumber(farm.lpTotalInQuoteToken).times(farm.quoteToken.busdPrice)
return getFarmApr(
const { cakeRewardsApr, lpRewardsApr } = getFarmApr(
new BigNumber(farm.poolWeight),
cakePrice,
totalLiquidity,
farm.lpAddresses[ChainId.MAINNET],
)
return cakeRewardsApr + lpRewardsApr
}
return null
})
Expand Down

0 comments on commit 02662c3

Please sign in to comment.