Skip to content

Commit

Permalink
calculate minExpectedTokens for bbd
Browse files Browse the repository at this point in the history
  • Loading branch information
peripheralist committed Oct 15, 2023
1 parent 75a9317 commit 02fee4e
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 17 deletions.
8 changes: 7 additions & 1 deletion src/components/AMMPrices/hooks/useERC20UniswapPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Token } from '@uniswap/sdk-core'
import IUniswapV3FactoryABI from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Factory.sol/IUniswapV3Factory.json'
import IUniswapV3PoolABI from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json'
import {
FeeAmount,
Pool,
FACTORY_ADDRESS as UNISWAP_V3_FACTORY_ADDRESS,
} from '@uniswap/v3-sdk'
Expand Down Expand Up @@ -43,7 +44,12 @@ type Props = {
* Pools are created at a specific fee tier.
* https://docs.uniswap.org/protocol/concepts/V3-overview/fees#pool-fees-tiers
*/
const UNISWAP_FEES_BPS = [10000, 3000, 500]
const UNISWAP_FEES_BPS = [
FeeAmount.LOWEST,
FeeAmount.LOW,
FeeAmount.MEDIUM,
FeeAmount.HIGH,
]
const networkId = readNetwork.chainId

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Trans } from '@lingui/macro'
import TooltipIcon from 'components/TooltipIcon'
import { useProjectHasErc20Token } from 'components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectHasErc20Token'
import { useProjectPaymentTokens } from 'components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectPaymentTokens'
import { BUYBACK_DELEGATE_ENABLED_PROJECT_IDS } from 'constants/buybackDelegateEnabledProjectIds'
import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
import { useContext } from 'react'
import { twMerge } from 'tailwind-merge'
import { CartItemBadge } from '../../Cart/components/CartItem/CartItemBadge'
import { ProjectHeaderLogo } from '../../ProjectHeader/components/ProjectHeaderLogo'

export const ReceiveTokensItem = ({ className }: { className?: string }) => {
const { projectId } = useContext(ProjectMetadataContext)
const { receivedTickets, receivedTokenSymbolText } = useProjectPaymentTokens()
const projectHasErc20Token = useProjectHasErc20Token()

Expand All @@ -19,6 +24,9 @@ export const ReceiveTokensItem = ({ className }: { className?: string }) => {
return null
}

const buybackDelegateEnabled =
projectId && BUYBACK_DELEGATE_ENABLED_PROJECT_IDS.includes(projectId)

return (
<div className={twMerge('flex flex-col gap-4', className)}>
<div className="flex items-center justify-between gap-3">
Expand All @@ -31,7 +39,18 @@ export const ReceiveTokensItem = ({ className }: { className?: string }) => {
<Trans>{badgeTitle} Token</Trans>
</CartItemBadge>
</div>
<div>{receivedTickets}</div>
{buybackDelegateEnabled ? (
<div>
{receivedTickets}
<TooltipIcon
tip={
'This project may purchase tokens from a secondary market instead of minting new tokens, which may result in more tokens distributed depending on the swap price.'
}
/>
</div>
) : (
<div>{receivedTickets}</div>
)}
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const usePayProjectTx = ({
})
const beneficiary = values.beneficiaryAddress ?? userAddress

let weiAmount
let weiAmount: BigNumber
if (!totalAmount) {
weiAmount = parseWad(0)
} else if (totalAmount.currency === V2V3_CURRENCY_ETH) {
Expand All @@ -111,18 +111,33 @@ export const usePayProjectTx = ({
.flat()

// Total tokens that should be minted by pay() tx, including reserved tokens
const totalMintedTokens = fundingCycleMetadata
? BigNumber.from(receivedTickets)
const requiredTokens = fundingCycleMetadata
? parseWad(receivedTickets)
.mul(MAX_RESERVED_RATE)
.div(fundingCycleMetadata.reservedRate)
: undefined

const expectedTokensFromSwap = weiAmount.div(
BigNumber.from(priceQuery?.projectTokenPrice.numerator.toString()).div(
BigNumber.from(priceQuery?.projectTokenPrice.denominator.toString()),
),
)

const shouldUseBuybackDelegate =
projectId &&
priceQuery &&
totalMintedTokens &&
requiredTokens &&
BUYBACK_DELEGATE_ENABLED_PROJECT_IDS.includes(projectId) &&
BigNumber.from(totalMintedTokens).mul(2).lt(priceQuery.liquidity) // total token amount must be less than half of available liquidity (arbitrary)
expectedTokensFromSwap.gte(requiredTokens) &&
BigNumber.from(requiredTokens).mul(2).lt(priceQuery.liquidity) // total token amount must be less than half of available liquidity (arbitrary) to avoid slippage

// Expect whichever is greater: 95% of expected tokens from swap, or minimum amount of tokens required to be minted
// We don't really care if less tokens are returned from the swap than what we've estimated (as long as we're getting at least the minimum amount that would otherwise be minted). The main concern is sending as high a minimum as possible to limit arbitrage possibility
const minExpectedTokens =
requiredTokens &&
expectedTokensFromSwap.mul(95).div(100).gt(requiredTokens)
? expectedTokensFromSwap.mul(95).div(100)
: requiredTokens

// Encode metadata for jb721Delegate AND/OR jbBuybackDelegate
const delegateMetadata = encodeDelegateMetadata({
Expand All @@ -134,12 +149,13 @@ export const usePayProjectTx = ({
},
version: JB721DelegateVersion,
},
jbBuybackDelegate: shouldUseBuybackDelegate
? {
amountToSwap: 0, // use all ETH
minExpectedTokens: totalMintedTokens, // must receive minimum of expected tokens
}
: undefined,
jbBuybackDelegate:
shouldUseBuybackDelegate && minExpectedTokens !== undefined
? {
amountToSwap: 0, // use all ETH
minExpectedTokens,
}
: undefined,
})

let onError = undefined
Expand Down
10 changes: 6 additions & 4 deletions src/utils/delegateMetadata/encodeDelegateMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ export function encodeDelegateMetadata({
)
}

const ids: string[] = []
const delegateIds: string[] = []
const metadatas: string[] = []

if (jbBuybackDelegate) {
ids.push(IJBBuybackDelegate_INTERFACE_ID)
delegateIds.push(IJBBuybackDelegate_INTERFACE_ID)
metadatas.push(
utils.defaultAbiCoder.encode(
['uint256', 'uint256'],
Expand All @@ -78,12 +78,14 @@ export function encodeDelegateMetadata({
)

if (encoded) {
ids.push(IJBTiered721Delegate_V3_4_PAY_ID)
delegateIds.push(IJBTiered721Delegate_V3_4_PAY_ID)
metadatas.push(encoded)
}
}

return createMetadata(ids, metadatas)
if (!delegateIds.length || !metadatas.length) return

return createMetadata(delegateIds, metadatas)
}

/**
Expand Down

0 comments on commit 02fee4e

Please sign in to comment.