Skip to content

Commit

Permalink
Merge pull request #4751 from cowprotocol/release/2024-07-31
Browse files Browse the repository at this point in the history
chore: release/2024 07 31
  • Loading branch information
anxolin authored Jul 31, 2024
2 parents 71df987 + 970ec5d commit 6c767e3
Show file tree
Hide file tree
Showing 29 changed files with 249 additions and 120 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ env:
REACT_APP_GOOGLE_ANALYTICS_ID: ${{ secrets.REACT_APP_GOOGLE_ANALYTICS_ID }}
REACT_APP_BLOCKNATIVE_API_KEY: ${{ secrets.REACT_APP_BLOCKNATIVE_API_KEY }}
REACT_APP_BFF_BASE_URL: ${{ secrets.BFF_BASE_URL }}
REACT_APP_CMS_BASE_URL: ${{ secrets.CMS_BASE_URL }}
NEXT_PUBLIC_CMS_BASE_URL: ${{ secrets.CMS_BASE_URL }}

jobs:
setup:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ipfs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ env:
IPFS_DEPLOY_PINATA__API_KEY: ${{ secrets.REACT_APP_PINATA_API_KEY }}
IPFS_DEPLOY_PINATA__SECRET_API_KEY: ${{ secrets.REACT_APP_PINATA_SECRET_API_KEY }}
REACT_APP_BFF_BASE_URL: ${{ secrets.BFF_BASE_URL }}
REACT_APP_CMS_BASE_URL: ${{ secrets.CMS_BASE_URL }}
NEXT_PUBLIC_CMS_BASE_URL: ${{ secrets.CMS_BASE_URL }}
NODE_VERSION: lts/hydrogen

jobs:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/vercel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ jobs:
REACT_APP_SUBGRAPH_URL_ARBITRUM_ONE=${{ secrets.REACT_APP_SUBGRAPH_URL_ARBITRUM_ONE }}
REACT_APP_SUBGRAPH_URL_GNOSIS_CHAIN=${{ secrets.REACT_APP_SUBGRAPH_URL_GNOSIS_CHAIN }}
REACT_APP_BFF_BASE_URL=${{ secrets.BFF_BASE_URL }}
REACT_APP_CMS_BASE_URL=${{ secrets.CMS_BASE_URL }}
NEXT_PUBLIC_CMS_BASE_URL=${{ secrets.CMS_BASE_URL }}
vercel build -t ${{ secrets.VERCEL_TOKEN }} --prod
- name: Get the version
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,29 @@ The API endpoint is configured using the environment variable
REACT_APP_BFF_BASE_URL=https://bff.cow.fi
```
## CMS API Endpoints (Content Management System)
The CMS API is a helper API that provides some additional content to the frontend.
It is not considered a required API for CoW Swap core functionality, the
app will still allow the user to place orders and will have some fallback logic
in case this API is not available.
The reference implementation of the API is
[CMS API](https://github.com/cowprotocol/cms).
The API endpoint is configured using the environment variable
`REACT_APP_CMS_BASE_URL`:
```ini
REACT_APP_CMS_BASE_URL=https://cms.cow.fi/api
```
## Price feeds
CoW Swap tries to find the best price available on-chain using some price feeds.
Expand Down
44 changes: 41 additions & 3 deletions apps/cow-fi/components/AddRpcButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Confetti } from '@cowprotocol/ui'
import styled from 'styled-components/macro'
import { darken, transparentize } from 'polished'
import { useConnectAndAddToWallet } from '../../lib/hooks/useConnectAndAddToWallet'
import { clickOnMevBlocker } from 'modules/analytics'
import { useAccount } from 'wagmi'

import { Link, LinkType } from '@/components/Link'

Expand All @@ -25,14 +27,38 @@ const Message = styled.p<{ state: AddToWalletStateValues }>`
`

export function AddRpcButton() {
const { addWalletState, connectAndAddToWallet } = useConnectAndAddToWallet()
const { addWalletState, connectAndAddToWallet, disconnectWallet } = useConnectAndAddToWallet()
const { errorMessage, state } = addWalletState
const { isConnected } = useAccount()

const handleClick = async () => {
clickOnMevBlocker('click-add-rpc-to-wallet')
try {
if (connectAndAddToWallet) {
// Start the connection process
const connectionPromise = connectAndAddToWallet()

// Wait for the connection process to complete
await connectionPromise
} else {
throw new Error('connectAndAddToWallet is not defined')
}
} catch (error) {
clickOnMevBlocker('click-add-rpc-to-wallet-error')
}
}

// Get the label and enable state of button
const isAdding = state === 'adding'
const isConnecting = state === 'connecting'
const disabledButton = isConnecting || isAdding || !connectAndAddToWallet
const buttonLabel = isConnecting ? 'Connecting Wallet...' : isAdding ? 'Adding to Wallet...' : 'Get protected'
const buttonLabel = isConnecting
? 'Connecting Wallet...'
: isAdding
? 'Adding to Wallet...'
: isConnected
? 'Add MEV Blocker RPC'
: 'Get protected'

return (
<>
Expand All @@ -48,13 +74,25 @@ export function AddRpcButton() {
fontSize={21}
color={'#FEE7CF'}
bgColor="#EC4612"
onClick={connectAndAddToWallet || (() => {})}
onClick={handleClick}
disabled={disabledButton}
asButton
>
{buttonLabel}
</Link>
{errorMessage && <Message state={state}>{errorMessage}</Message>}
{disconnectWallet && (
<Link
linkType={LinkType.TopicButton}
fontSize={21}
color={'#FEE7CF'}
bgColor="#333"
onClick={disconnectWallet}
asButton
>
Disconnect
</Link>
)}
</>
)}
</>
Expand Down
1 change: 1 addition & 0 deletions apps/cow-fi/const/cms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

55 changes: 30 additions & 25 deletions apps/cow-fi/lib/hooks/useConnect.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
import { useAccount, useConnect as useConnectWagmi } from 'wagmi'
import { useCallback, useMemo } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useConnectModal } from '@rainbow-me/rainbowkit'
import { ConnectResult, PublicClient } from '@wagmi/core'
import { clickOnMevBlocker } from '../../modules/analytics'

export function useConnect() {
const { isConnected } = useAccount()
const { openConnectModal } = useConnectModal()
const { connectAsync, connectors } = useConnectWagmi()
const [connectionPromise, setConnectionPromise] = useState<Promise<ConnectResult<PublicClient> | undefined> | null>(
null
)

const [injectedConnector, hasInjectedProviderPromise] = useMemo(() => {
const connector = connectors.find((c) => c.id === 'injected')
const injectedConnector = connectors.find((c) => c.id === 'injected')

if (!connector || typeof connector.getProvider !== 'function') {
return [undefined, Promise.resolve(false)] as const
useEffect(() => {
if (isConnected && connectionPromise) {
clickOnMevBlocker('wallet-connected')
setConnectionPromise(null)
}
}, [isConnected, connectionPromise])

return [connector, connector.getProvider().then((p) => !!p)] as const
}, [connectors])
const connect = useCallback((): Promise<ConnectResult<PublicClient> | undefined> => {
console.debug('[useConnect] Initiating connection')

const connect = useCallback(async (): Promise<ConnectResult<PublicClient> | undefined> => {
const hasInjectedProvider = await hasInjectedProviderPromise

// Shows connect modal if there's no injected wallet
if (!hasInjectedProvider || !injectedConnector) {
console.debug('[useConnect] No injected connector or provider. Using connect modal')
const promise = new Promise<ConnectResult<PublicClient> | undefined>((resolve, reject) => {
if (openConnectModal) {
console.debug('[useConnect] Showing connect modal')
openConnectModal()
}
return undefined
}

if (!connectAsync) {
console.debug('[useConnect] connectAsync is undefined')
return undefined
}

// Connects with injected wallet (if available)
console.debug('[useConnect] Connect using injected wallet')
return connectAsync({
connector: injectedConnector,
const checkConnection = setInterval(async () => {
if (isConnected) {
clearInterval(checkConnection)
try {
const result = await connectAsync({ connector: injectedConnector })
resolve(result)
} catch (error) {
reject(error)
}
}
}, 500)
})
}, [connectAsync, injectedConnector, hasInjectedProviderPromise, openConnectModal])

setConnectionPromise(promise)
return promise
}, [connectAsync, injectedConnector, openConnectModal, isConnected])

return {
isConnected,
Expand Down
97 changes: 57 additions & 40 deletions apps/cow-fi/lib/hooks/useConnectAndAddToWallet.ts
Original file line number Diff line number Diff line change
@@ -1,101 +1,118 @@
import { useCallback, useEffect, useState } from 'react'
import { useCallback, useState } from 'react'
import { useConnect } from './useConnect'
import { useWalletClient } from 'wagmi'
import { useDisconnect, useWalletClient } from 'wagmi'
import { handleRpcError } from '@/util/handleRpcError'
import { useAddRpcWithTimeout } from './useAddRpcWithTimeout'
import { AddToWalletState, AddToWalletStateValues } from '@/components/AddRpcButton'
import { clickOnMevBlocker } from '../../modules/analytics'

const DEFAULT_STATE: AddToWalletState = { state: 'unknown', autoConnect: false }
const ADDING_STATE: AddToWalletState = { state: 'adding', autoConnect: false }
const ADDED_STATE: AddToWalletState = { state: 'added', autoConnect: false }

export interface UseConnectAndAddToWalletPros {
export interface UseConnectAndAddToWalletProps {
addWalletState: AddToWalletState
connectAndAddToWallet: (() => void) | null
connectAndAddToWallet: (() => Promise<void>) | null
disconnectWallet: (() => void) | null
}

export function useConnectAndAddToWallet(): UseConnectAndAddToWalletPros {
export function useConnectAndAddToWallet(): UseConnectAndAddToWalletProps {
const { isConnected, connect } = useConnect()
const { disconnect } = useDisconnect()
const { data: walletClient } = useWalletClient()
const [addWalletState, setState] = useState<AddToWalletState>(DEFAULT_STATE)
const [addingPromise, setAddRpcPromise] = useState<Promise<boolean> | null>(null)
const { state, autoConnect } = addWalletState

// Handle RPC errors
const handleError = useCallback(
(error: unknown) => {
console.error(`[connectAndAddToWallet] handleError`, error)

const { errorMessage: message, isError, isUserRejection } = handleRpcError(error)

if (isError || isUserRejection) {
// Display the error
if (isUserRejection) {
clickOnMevBlocker('click-add-rpc-to-wallet-user-rejected')
setState({ state: 'unknown', errorMessage: 'User rejected the request', autoConnect: false })
} else if (isError) {
clickOnMevBlocker('click-add-rpc-to-wallet-error')
setState({ state: 'error', errorMessage: message || undefined, autoConnect: false })
} else {
// Not an error: i.e The user is connecting
setState(DEFAULT_STATE)
}
setAddRpcPromise(null)
},
[setState]
)

// Add RPC endpoint to wallet (with analytics + handle timeout state)
const addToWallet = useAddRpcWithTimeout({
isAdding: state === 'adding',
isAdding: addWalletState.state === 'adding',
addingPromise,
onAdding(newAddRpcPromise) {
console.debug('[connectAndAddToWallet] Adding RPC...')
clickOnMevBlocker('click-add-rpc-to-wallet-adding')
setAddRpcPromise(newAddRpcPromise)
setState(ADDING_STATE)
},
onAdded() {
console.debug('[connectAndAddToWallet] 🎉 RPC has been added!')
clickOnMevBlocker('click-add-rpc-to-wallet-added-success')
setState(ADDED_STATE)
setAddRpcPromise(null)
},
onTimeout(errorMessage: string, newState: AddToWalletStateValues) {
console.debug(`[connectAndAddToWallet] New State: ${newState}. Message`, errorMessage)
clickOnMevBlocker('click-add-rpc-to-wallet-timeout')
setState({
state: newState,
errorMessage: errorMessage || undefined,
autoConnect,
autoConnect: addWalletState.autoConnect,
})
},
walletClient: walletClient ?? undefined,
handleError,
})

// Connect and auto-add the RPC endpoint
const allowToConnectAndAddToWallet = !isConnected || walletClient // allow to connectAndAddToWallet if not connected, or if the client is ready
const connectAndAddToWallet = useCallback(() => {
if (!allowToConnectAndAddToWallet) {
return
const connectAndAddToWallet = useCallback((): Promise<void> => {
if (!walletClient && isConnected) {
return Promise.reject(new Error('Connection not allowed'))
}

if (!isConnected) {
console.debug('[useConnectAndAddToWallet] Connecting...')
connect()
.then(() => {
console.debug('[useConnectAndAddToWallet] 🔌 Connected!')
addToWallet()
})
.catch(handleError)
} else {
console.debug('[useConnectAndAddToWallet] Already connected. Adding RPC endpoint...')
addToWallet()
}
}, [allowToConnectAndAddToWallet, isConnected, handleError, connect, addToWallet])
return new Promise<void>((resolve, reject) => {
if (!isConnected) {
console.debug('[useConnectAndAddToWallet] Connecting...')
clickOnMevBlocker('click-add-rpc-to-wallet-connecting')
connect()
.then((result) => {
if (result) {
console.debug('[useConnectAndAddToWallet] 🔌 Connected!')
clickOnMevBlocker('click-add-rpc-to-wallet-connected')
addToWallet()
resolve()
} else {
console.debug('[useConnectAndAddToWallet] Connection process incomplete')
setState(DEFAULT_STATE)
resolve()
}
})
.catch((error: unknown) => {
handleError(error)
reject(error)
})
} else {
console.debug('[useConnectAndAddToWallet] Already connected. Adding RPC endpoint...')
clickOnMevBlocker('click-add-rpc-to-wallet-connected')
addToWallet()
resolve()
}
})
}, [isConnected, connect, addToWallet, handleError, walletClient])

// Auto-connect (once we have )
useEffect(() => {
if (isConnected && walletClient && autoConnect) {
addToWallet()
}
}, [isConnected, walletClient, autoConnect, addToWallet])
const disconnectWallet = useCallback(() => {
clickOnMevBlocker('click-disconnect-wallet')
disconnect()
setState(DEFAULT_STATE)
}, [disconnect])

return {
connectAndAddToWallet: allowToConnectAndAddToWallet ? connectAndAddToWallet : null,
connectAndAddToWallet: walletClient || !isConnected ? connectAndAddToWallet : null,
disconnectWallet: isConnected ? disconnectWallet : null,
addWalletState,
}
}
3 changes: 2 additions & 1 deletion apps/cow-fi/pages/learn/[article].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { Link, LinkType } from '@/components/Link'

import { CONFIG, DATA_CACHE_TIME_SECONDS } from '@/const/meta'
import { clickOnKnowledgeBase } from 'modules/analytics'
import { CmsImage } from '@cowprotocol/ui'

interface ArticlePageProps {
siteConfigData: typeof CONFIG
Expand Down Expand Up @@ -281,7 +282,7 @@ export default function ArticlePage({
>
{imageUrl && (
<ArticleImage>
<img src={imageUrl} alt={article.attributes?.title ?? 'Article Image'} />
<CmsImage src={imageUrl} alt={article.attributes?.title ?? 'Article Image'} />
</ArticleImage>
)}
<ArticleTitle>{article.attributes?.title}</ArticleTitle>
Expand Down
Loading

0 comments on commit 6c767e3

Please sign in to comment.