Skip to content

Commit

Permalink
Merge branch 'develop' into legal_page_links
Browse files Browse the repository at this point in the history
  • Loading branch information
alfetopito authored Jan 9, 2025
2 parents c2900dc + f080ffd commit 9dc2ec4
Show file tree
Hide file tree
Showing 60 changed files with 594 additions and 196 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,21 @@ In case of problems with the service worker cache you force a reset using
`emergency.js` is not cached by browser and loaded before all.
## Vercel preview build
Since this repo includes multiple apps, we do not want to build all of them on each PR because it causes long build queues in Vercel.
Some apps (see the list bellow) are not required to be built on each PR so we run them only a PR is labeled with a specific label.
This label is defined in the project settings on Vercel in `Settings`/`Git`/`Ignored Build Step` script.
For example, the label for the widget-configurator is `preview-widget-cfg`:
```
node tools/scripts/ignore-build-step.js --app=preview-widget-cfg
```
List of applications and their labels:
- widget-configurator: `preview-widget-cfg`
- cosmos: `preview-cosmos`
- sdk-tools: `preview-sdk-tools`
# 📚 Technical Documentation
1. [Oveall Architecture](docs/architecture-overview.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { ReactElement, useEffect, useMemo, useState } from 'react'
import { latest } from '@cowprotocol/app-data'
import { CowHookDetails, HookToDappMatch, matchHooksToDappsRegistry } from '@cowprotocol/hook-dapp-lib'
import { InfoTooltip } from '@cowprotocol/ui'
import { useWalletInfo } from '@cowprotocol/wallet'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'

import { ChevronDown, ChevronUp } from 'react-feather'

Expand All @@ -14,15 +16,27 @@ import { HookItem } from './HookItem'
import * as styledEl from './styled'
import { CircleCount } from './styled'

import { parsePermitData } from '../../utils/parsePermitData'

interface OrderHooksDetailsProps {
appData: string | AppDataInfo
children: (content: ReactElement) => ReactElement
margin?: string
isTradeConfirmation?: boolean
slippageAdjustedSellAmount?: CurrencyAmount<Currency>
isPartialApprove?: boolean
}

export function OrderHooksDetails({ appData, children, margin, isTradeConfirmation }: OrderHooksDetailsProps) {
export function OrderHooksDetails({
appData,
children,
margin,
isTradeConfirmation,
slippageAdjustedSellAmount,
isPartialApprove,
}: OrderHooksDetailsProps) {
const [isOpen, setOpen] = useState(false)
const { account } = useWalletInfo()
const appDataDoc = useMemo(() => {
return typeof appData === 'string' ? decodeAppData(appData) : appData.doc
}, [appData])
Expand All @@ -41,9 +55,32 @@ export function OrderHooksDetails({ appData, children, margin, isTradeConfirmati

const metadata = appDataDoc.metadata as latest.Metadata

/**
* AppData might include a hook with account agnostic permit which is used to fetch a quote.
* This hook should be ignored.
* Moreover, any hook with a permit which has owner !== current account will be excluded.
* We also remove the permit from appData before order signing (see filterPermitSignerPermit).
*/
const preHooks = account
? metadata.hooks?.pre?.filter((hook) => {
try {
const permitHookData = parsePermitData(hook.callData)
const isOwnerMatched = permitHookData.owner.toLowerCase() === account.toLowerCase()

// If the hook is a partial approve, we need to check if the value is equal to the slippageAdjustedSellAmount
// Because there might be a hook with an "infinite" permit from other widget
return isPartialApprove && slippageAdjustedSellAmount
? isOwnerMatched && permitHookData.value.eq(slippageAdjustedSellAmount.quotient.toString())
: isOwnerMatched
} catch {
return true
}
})
: metadata.hooks?.pre

const hasSomeFailedSimulation = isTradeConfirmation && Object.values(data || {}).some((hook) => !hook.status)

const preHooksToDapp = matchHooksToDappsRegistry(metadata.hooks?.pre || [], preCustomHookDapps)
const preHooksToDapp = matchHooksToDappsRegistry(preHooks || [], preCustomHookDapps)
const postHooksToDapp = matchHooksToDappsRegistry(metadata.hooks?.post || [], postCustomHookDapps)

if (!preHooksToDapp.length && !postHooksToDapp.length) return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ export interface TradeApproveButtonProps {
amountToApprove: CurrencyAmount<Currency>
children?: React.ReactNode
isDisabled?: boolean
isPartialApprove?: boolean
}

export function TradeApproveButton(props: TradeApproveButtonProps) {
const { amountToApprove, children, isDisabled } = props
const { amountToApprove, children, isDisabled, isPartialApprove } = props

const currency = amountToApprove.currency

const { state: approvalState } = useApproveState(amountToApprove)
const tradeApproveCallback = useTradeApproveCallback(amountToApprove)
const tradeApproveCallback = useTradeApproveCallback(amountToApprove, isPartialApprove)
const shouldZeroApprove = useShouldZeroApprove(amountToApprove)
const zeroApprove = useZeroApprove(amountToApprove.currency)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ export interface TradeApproveCallback {
(params?: TradeApproveCallbackParams): Promise<TransactionResponse | undefined>
}

export function useTradeApproveCallback(amountToApprove?: CurrencyAmount<Currency>): TradeApproveCallback {
export function useTradeApproveCallback(
amountToApprove?: CurrencyAmount<Currency>,
isPartialApprove?: boolean,
): TradeApproveCallback {
const updateTradeApproveState = useUpdateTradeApproveState()
const spender = useTradeSpenderAddress()
const currency = amountToApprove?.currency
const symbol = currency?.symbol

const approveCallback = useApproveCallback(amountToApprove, spender)
const approveCallback = useApproveCallback(amountToApprove, spender, isPartialApprove)

return useCallback(
async ({ useModals = true }: TradeApproveCallbackParams = { useModals: true }) => {
Expand Down Expand Up @@ -58,6 +61,6 @@ export function useTradeApproveCallback(amountToApprove?: CurrencyAmount<Currenc
return undefined
})
},
[symbol, approveCallback, updateTradeApproveState, currency]
[symbol, approveCallback, updateTradeApproveState, currency],
)
}
15 changes: 10 additions & 5 deletions apps/cowswap-frontend/src/common/hooks/useApproveCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ export async function estimateApprove(
tokenContract: Erc20,
spender: string,
amountToApprove: CurrencyAmount<Currency>,
isPartialApprove?: boolean,
): Promise<{
approveAmount: BigNumber | string
gasLimit: BigNumber
}> {
const approveAmount =
isPartialApprove && amountToApprove ? BigNumber.from(amountToApprove.quotient.toString()) : MaxUint256

try {
return {
approveAmount: MaxUint256,
gasLimit: await tokenContract.estimateGas.approve(spender, MaxUint256),
approveAmount,
gasLimit: await tokenContract.estimateGas.approve(spender, approveAmount),
}
} catch {
// Fallback: Attempt to set an approval for the maximum wallet balance (instead of the MaxUint256).
Expand All @@ -45,7 +49,7 @@ export async function estimateApprove(
)

return {
approveAmount: MaxUint256,
approveAmount,
gasLimit: GAS_LIMIT_DEFAULT,
}
}
Expand All @@ -55,6 +59,7 @@ export async function estimateApprove(
export function useApproveCallback(
amountToApprove?: CurrencyAmount<Currency>,
spender?: string,
isPartialApprove?: boolean,
): (summary?: string) => Promise<TransactionResponse | undefined> {
const { chainId } = useWalletInfo()
const currency = amountToApprove?.currency
Expand All @@ -68,7 +73,7 @@ export function useApproveCallback(
return
}

const estimation = await estimateApprove(tokenContract, spender, amountToApprove)
const estimation = await estimateApprove(tokenContract, spender, amountToApprove, isPartialApprove)
return tokenContract
.approve(spender, estimation.approveAmount, {
gasLimit: calculateGasMargin(estimation.gasLimit),
Expand All @@ -81,5 +86,5 @@ export function useApproveCallback(
})
return response
})
}, [chainId, token, tokenContract, amountToApprove, spender, addTransaction])
}, [chainId, token, tokenContract, amountToApprove, spender, addTransaction, isPartialApprove])
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export function ApproveButton(props: ApproveButtonProps) {
content={
<Trans>
You must give the CoW Protocol smart contracts permission to use your <TokenSymbol token={currency} />.
If you approve the default amount, you will only have to do this once per token.
</Trans>
}
>
Expand Down
15 changes: 12 additions & 3 deletions apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ import { ExplorerDataType, getExplorerLink, getRandomInt, isSellOrder, shortenAd
import { OrderKind, SupportedChainId } from '@cowprotocol/cow-sdk'
import { TokenLogo } from '@cowprotocol/tokens'
import { Command } from '@cowprotocol/types'
import { Confetti, ExternalLink, InfoTooltip, ProductLogo, ProductVariant, TokenAmount, UI } from '@cowprotocol/ui'
import {
Confetti,
ExternalLink,
InfoTooltip,
ProductLogo,
ProductVariant,
TokenAmount,
UI,
UnderlinedLinkStyledButton,
} from '@cowprotocol/ui'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'

import { AnimatePresence, motion } from 'framer-motion'
Expand Down Expand Up @@ -1123,14 +1132,14 @@ function ExpiredStep(props: OrderProgressBarV2Props) {
<h3>The good news</h3>
<p>
Unlike on other exchanges, you won't be charged for this! Feel free to{' '}
<styledEl.Button
<UnderlinedLinkStyledButton
onClick={() => {
props.navigateToNewOrder?.()
trackNewOrderClick()
}}
>
place a new order
</styledEl.Button>{' '}
</UnderlinedLinkStyledButton>{' '}
without worry.
</p>
</styledEl.InfoCard>
Expand Down
12 changes: 2 additions & 10 deletions apps/cowswap-frontend/src/common/pure/OrderProgressBarV2/styled.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import IMAGE_STAR_SHINE from '@cowprotocol/assets/cow-swap/star-shine.svg'
import { SingleLetterLogoWrapper } from '@cowprotocol/tokens'
import { ButtonPrimary, Font, LinkStyledButton, Media, UI } from '@cowprotocol/ui'
import { ButtonPrimary, Font, Media, UI } from '@cowprotocol/ui'

import styled, { css, keyframes } from 'styled-components/macro'

Expand Down Expand Up @@ -66,6 +66,7 @@ export const StepsContainer = styled.div<{ $height: number; $minHeight?: string;
padding: 0;
// implement a gradient to hide the bottom of the steps container using white to opacity white using pseudo element
&::after {
content: ${({ bottomGradient }) => (bottomGradient ? '""' : 'none')};
position: absolute;
Expand Down Expand Up @@ -143,15 +144,6 @@ export const CancelButton = styled(CancelButtonOriginal)`
}
`

export const Button = styled(LinkStyledButton)`
font-size: 14px;
text-decoration: underline;
&:hover {
text-decoration: none;
}
`

export const ProgressImageWrapper = styled.div<{ bgColor?: string; padding?: string; height?: string; gap?: string }>`
width: 100%;
height: ${({ height }) => height || '246px'};
Expand Down
15 changes: 15 additions & 0 deletions apps/cowswap-frontend/src/common/utils/parsePermitData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Erc20__factory } from '@cowprotocol/abis'
import type { BigNumber } from '@ethersproject/bignumber'

const erc20Interface = Erc20__factory.createInterface()

export interface PermitParameters {
owner: string
spender: string
value: BigNumber
deadline: BigNumber
}

export function parsePermitData(callData: string): PermitParameters {
return erc20Interface.decodeFunctionData('permit', callData) as unknown as PermitParameters
}
11 changes: 8 additions & 3 deletions apps/cowswap-frontend/src/legacy/state/orders/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ export type OrderInfoApi = Pick<
| 'executedSellAmount'
| 'executedSellAmountBeforeFees'
| 'executedFeeAmount'
| 'executedSurplusFee'
| 'executedFee'
| 'executedFeeToken'
| 'totalFee'
| 'invalidated'
| 'ethflowData'
| 'onchainOrderData'
Expand Down Expand Up @@ -139,6 +141,7 @@ export interface AddPendingOrderParams {
order: SerializedOrder
isSafeWallet: boolean
}

export type ChangeOrderStatusParams = { id: UID; chainId: ChainId }
export type SetOrderCancellationHashParams = ChangeOrderStatusParams & { hash: string }

Expand Down Expand Up @@ -177,11 +180,13 @@ export interface BatchOrdersUpdateParams {
}

export type PresignedOrdersParams = BatchOrdersUpdateParams

export interface UpdatePresignGnosisSafeTxParams {
orderId: UID
chainId: ChainId
safeTransaction: SafeMultisigTransactionResponse
}

export type ExpireOrdersBatchParams = BatchOrdersUpdateParams
export type InvalidateOrdersBatchParams = BatchOrdersUpdateParams
export type CancelOrdersBatchParams = BatchOrdersUpdateParams
Expand All @@ -196,7 +201,7 @@ export const fulfillOrdersBatch = createAction<FulfillOrdersBatchParams>('order/
export const preSignOrders = createAction<PresignedOrdersParams>('order/presignOrders')

export const updatePresignGnosisSafeTx = createAction<UpdatePresignGnosisSafeTxParams>(
'order/updatePresignGnosisSafeTx'
'order/updatePresignGnosisSafeTx',
)

export const expireOrdersBatch = createAction<ExpireOrdersBatchParams>('order/expireOrdersBatch')
Expand All @@ -214,7 +219,7 @@ export const deleteOrders = createAction<DeleteOrdersParams>('order/deleteOrders
export const clearOrders = createAction<{ chainId: ChainId }>('order/clearOrders')

export const updateLastCheckedBlock = createAction<{ chainId: ChainId; lastCheckedBlock: number }>(
'order/updateLastCheckedBlock'
'order/updateLastCheckedBlock',
)

export const clearOrdersStorage = createAction('order/clearOrdersStorage')
Expand Down
Loading

0 comments on commit 9dc2ec4

Please sign in to comment.