diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fadb9b2beb..efbd4e0875 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ env: REACT_APP_PINATA_SECRET_API_KEY: ${{ secrets.REACT_APP_PINATA_SECRET_API_KEY }} 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 }} jobs: setup: diff --git a/.github/workflows/ipfs.yml b/.github/workflows/ipfs.yml index 630362c127..dd156fef74 100644 --- a/.github/workflows/ipfs.yml +++ b/.github/workflows/ipfs.yml @@ -13,6 +13,7 @@ env: # Duplicated keys as these are required by `ipfs-deploy` 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 }} NODE_VERSION: lts/hydrogen jobs: diff --git a/.github/workflows/vercel.yml b/.github/workflows/vercel.yml index df7a85b4ad..8065c73259 100644 --- a/.github/workflows/vercel.yml +++ b/.github/workflows/vercel.yml @@ -55,6 +55,7 @@ jobs: REACT_APP_SUBGRAPH_URL_MAINNET=${{ secrets.REACT_APP_SUBGRAPH_URL_MAINNET }} 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 }} vercel build -t ${{ secrets.VERCEL_TOKEN }} --prod - name: Get the version diff --git a/README.md b/README.md index 0624c40460..d927afba70 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ CoW Swap is the first trading interface built on top of CoW Protocol. -It allows you to buy and sell tokens using gasless orders that are settled peer-to-peer among its users or into any on-chain liquidity source while providing MEV protection. +It allows you to buy and sell tokens using gasless orders that are settled +peer-to-peer among its users or into any on-chain liquidity source while +providing MEV protection. | **Platform** | **Link** | | --------------------- | ------------------------------------------------------------------------------------------------------------- | @@ -54,11 +56,13 @@ Start the Explorer on http://localhost:4200 [Read more about the Explorer](apps/explorer/README.md) ### Start + ```bash yarn start:explorer ``` ### Build + ```bash yarn build:explorer ``` @@ -68,16 +72,17 @@ yarn build:explorer Start CoW.fi on http://localhost:3001 ### Start + ```bash yarn start:cowfi ``` ### Build + ```bash yarn build:cowfi ``` - # 🖼️ Widget Configurator Start the Widget Configurator on http://127.0.0.1:4200/widget-configurator @@ -102,7 +107,8 @@ yarn run cosmos ## Integration test -> ⚠️ To run the tests. Make sure you add the required environment varianbles to your `.env.local` file with: +> ⚠️ To run the tests. Make sure you add the required environment varianbles to +> your `.env.local` file with: > > - `INTEGRATION_TEST_PRIVATE_KEY=`: Private key > - `INTEGRATION_TESTS_INFURA_KEY=`: Infura key @@ -157,9 +163,11 @@ ANALYZE_BUNDLE=true ANALYZE_BUNDLE_TEMPLATE=sunburst yarn build You should set your own RPC endpoints. -One simple way to do this, is by defining your own `REACT_APP_INFURA_KEY` environment var. +One simple way to do this, is by defining your own `REACT_APP_INFURA_KEY` +environment var. -Alternatively you can define the RPC URLs directly with the following environment variables: +Alternatively you can define the RPC URLs directly with the following +environment variables: ```ini REACT_APP_NETWORK_URL_1: https://... @@ -177,22 +185,43 @@ INTEGRATION_TESTS_PRIVATE_KEY: YOUR_TEST_WALLET_PRIVATE_KEY ## Orderbook API Endpoints -Fee quote requests and posting orders are sent to the Orderbook API. This API has the responsibility of collecting orders and -handing them to the solvers. +Fee quote requests and posting orders are sent to the Orderbook API. This API +has the responsibility of collecting orders and handing them to the solvers. -The reference implementation of the API is [CoW Protocol Services](https://github.com/cowprotocol/services). +The reference implementation of the API is +[CoW Protocol Services](https://github.com/cowprotocol/services). -The API endpoint is configured using the environment variable `REACT_APP_ORDER_BOOK_URLS`: +The API endpoint is configured using the environment variable +`REACT_APP_ORDER_BOOK_URLS`: ```ini REACT_APP_ORDER_BOOK_URLS='{"1":"https://YOUR_HOST","100":"https://YOUR_HOST","5":"https://YOUR_HOST"} ``` +## BFF API Endpoints (Backend for Frontend) + +The BFF API is a helper API that provides some additional data to the frontend. +It is a API that is used to enhance the frontend experience enabling some +features. It is not consider a required API for CoW Swap core functionality, the +app will still allow the user to place order and will have some fallback logics +in case this API is not available. + +The reference implementation of the API is +[BFF (Backend For Frontend)](https://github.com/cowprotocol/bff). + +The API endpoint is configured using the environment variable +`REACT_APP_BFF_BASE_URL`: + +```ini +REACT_APP_BFF_BASE_URL=https://bff.cow.fi +``` + ## Price feeds CoW Swap tries to find the best price available on-chain using some price feeds. -All price feeds are enabled by default, but they can be individually disabled by using an environment variable: +All price feeds are enabled by default, but they can be individually disabled by +using an environment variable: | Name | Environment variable | Type | Description | | --------- | ------------------------------------ | ---------------------------- | ------------------------------------------------------------------------------------ | @@ -203,11 +232,16 @@ All price feeds are enabled by default, but they can be individually disabled by The app will attach some metadata to all orders. -This metadata will be sent to the smart contract as a hexadecimal value in an order field called `AppData`. This value comes from hashing the content of a metadata JSON containing some information about the trade (using `keccak256` on the `UTF-8` bytes). +This metadata will be sent to the smart contract as a hexadecimal value in an +order field called `AppData`. This value comes from hashing the content of a +metadata JSON containing some information about the trade (using `keccak256` on +the `UTF-8` bytes). -The format of the JSON follows the format defined in [@cowprotocol/app-data](https://github.com/cowprotocol/app-data). +The format of the JSON follows the format defined in +[@cowprotocol/app-data](https://github.com/cowprotocol/app-data). -To set your own `AppData`, change `REACT_APP_FULL_APP_DATA_` environment variable. For more details, check out the environment file (<.env>) +To set your own `AppData`, change `REACT_APP_FULL_APP_DATA_` +environment variable. For more details, check out the environment file (<.env>) # 🔎 SEO @@ -225,8 +259,8 @@ To update its content: ## Service worker -In case of problems with the service worker cache you force a reset using [emergency.js](apps/cowswap-frontend/public/emergency.js) -The plan: +In case of problems with the service worker cache you force a reset using +[emergency.js](apps/cowswap-frontend/public/emergency.js) The plan: 1. `const resetCacheInCaseOfEmergency = false` - change `false` to `true` 2. Deploy a new version to production diff --git a/apps/cow-fi/.env b/apps/cow-fi/.env index 0d7cddd4a4..24de7c2cc6 100644 --- a/apps/cow-fi/.env +++ b/apps/cow-fi/.env @@ -1,4 +1,4 @@ DUNE_API_KEY=tuBkOcda6ymMvmnpIX9Qer9Xb5hJ2rf8 NEXT_PUBLIC_GOOGLE_ANALYTICS= -NEXT_PUBLIC_AWS_API_ENDPOINT="https://bff.barn.cow.fi/proxy" +NEXT_PUBLIC_AWS_API_ENDPOINT="https://bff.cow.fi/proxies/tokens" NEXT_PUBLIC_LAUNCH_DARKLY_KEY= \ No newline at end of file diff --git a/apps/cow-fi/services/uniswap-price/apollo-client.ts b/apps/cow-fi/services/uniswap-price/apollo-client.ts index ea26015323..a8c5d72ced 100644 --- a/apps/cow-fi/services/uniswap-price/apollo-client.ts +++ b/apps/cow-fi/services/uniswap-price/apollo-client.ts @@ -10,10 +10,6 @@ if (!GRAPHQL_URL) { export const apolloClient = new ApolloClient({ connectToDevTools: true, uri: GRAPHQL_URL, - headers: { - 'Content-Type': 'application/json', - Origin: 'https://app.uniswap.org', - }, cache: new InMemoryCache({ typePolicies: { Query: { diff --git a/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx b/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx index 1099b3ec0b..c44a230d77 100644 --- a/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx +++ b/apps/cowswap-frontend/src/common/pure/CurrencyInputPanel/CurrencyInputPanel.tsx @@ -144,6 +144,7 @@ export function CurrencyInputPanel(props: CurrencyInputPanelProps) { { return tokensData ? tokensData - .filter((x) => !!x) - .sort((tokenA, tokenB) => { - if (!sortField) { - // If there is no sort field selected (default) - return tokenComparator(tokenA, tokenB) - } else if (sortField === SORT_FIELD.BALANCE) { - // If the sort field is Balance - if (!balances) return 0 - - const balanceA = balances[tokenA.address.toLowerCase()] - const balanceB = balances[tokenB.address.toLowerCase()] - const balanceComp = balanceComparator(balanceA, balanceB) - - return applyDirection(balanceComp > 0, sortDirection) - } else { - // If the sort field is something else - const sortA = tokenA[sortField] - const sortB = tokenB[sortField] - - if (!sortA || !sortB) return 0 - return applyDirection(sortA > sortB, sortDirection) - } - }) - .slice(maxItems * (page - 1), page * maxItems) + .filter((x) => !!x) + .sort((tokenA, tokenB) => { + if (!sortField) { + // If there is no sort field selected (default) + return tokenComparator(tokenA, tokenB) + } else if (sortField === SORT_FIELD.BALANCE) { + // If the sort field is Balance + if (!balances) return 0 + + const balanceA = balances[tokenA.address.toLowerCase()] + const balanceB = balances[tokenB.address.toLowerCase()] + const balanceComp = balanceComparator(balanceA, balanceB) + + return applyDirection(balanceComp > 0, sortDirection) + } else { + // If the sort field is something else + const sortA = tokenA[sortField] + const sortB = tokenB[sortField] + + if (!sortA || !sortB) return 0 + return applyDirection(sortA > sortB, sortDirection) + } + }) + .slice(maxItems * (page - 1), page * maxItems) : [] }, [tokensData, maxItems, page, sortField, tokenComparator, balances, applyDirection, sortDirection]) @@ -209,7 +209,7 @@ export default function TokenTable({ if (data) { return ( - + = { [SupportedChainId.SEPOLIA]: null, } -const BASE_URL = 'https://cow-web-services.vercel.app/api/serverless/proxies/coingecko' +const BASE_URL = `${BFF_BASE_URL}/proxies/coingecko` const VS_CURRENCY = 'usd' /** * This is a text of 429 HTTP code diff --git a/libs/common-const/src/bff.ts b/libs/common-const/src/bff.ts new file mode 100644 index 0000000000..434b9c8ebb --- /dev/null +++ b/libs/common-const/src/bff.ts @@ -0,0 +1 @@ +export const BFF_BASE_URL = process.env.REACT_APP_BFF_BASE_URL || 'https://bff.cow.fi' \ No newline at end of file diff --git a/libs/common-const/src/index.ts b/libs/common-const/src/index.ts index b9bfd0ed15..096c7385b7 100644 --- a/libs/common-const/src/index.ts +++ b/libs/common-const/src/index.ts @@ -12,3 +12,4 @@ export * from './cowprotocolTokenLogoUrl' export * from './nativeAndWrappedTokens' export * from './theme' export * from './launchDarkly' +export * from './bff' diff --git a/libs/common-hooks/src/useAvailableChains.ts b/libs/common-hooks/src/useAvailableChains.ts index 2a6d79cbef..1a7892ba50 100644 --- a/libs/common-hooks/src/useAvailableChains.ts +++ b/libs/common-hooks/src/useAvailableChains.ts @@ -3,13 +3,21 @@ import { useMemo } from 'react' import { getAvailableChains } from '@cowprotocol/common-utils' import { SupportedChainId } from '@cowprotocol/cow-sdk' -import { useFeatureFlags } from './index' - +/** + * Hook to get a list of SupportedChainId currently available/enabled + * + * Normally it is the exact same list of chains in the SupportChainId enum. + * It'll be different when a chain is behind a feature flag. + * Set the flag in this hook. + */ export function useAvailableChains(): SupportedChainId[] { - const { isArbitrumOneEnabled } = useFeatureFlags() + // 1. Load feature flag for chain being enabled + // const { isArbitrumOneEnabled } = useFeatureFlags() return useMemo( - () => getAvailableChains(isArbitrumOneEnabled ? undefined : [SupportedChainId.ARBITRUM_ONE]), - [isArbitrumOneEnabled] + // 2. Conditionally build a list of chain ids to exclude + // () => getAvailableChains(isArbitrumOneEnabled ? undefined : [SupportedChainId.ARBITRUM_ONE]), + () => getAvailableChains(), + [] ) } diff --git a/libs/tokens/src/services/searchTokensInApi.ts b/libs/tokens/src/services/searchTokensInApi.ts index 80b22dbd8a..f21e5707eb 100644 --- a/libs/tokens/src/services/searchTokensInApi.ts +++ b/libs/tokens/src/services/searchTokensInApi.ts @@ -1,3 +1,4 @@ +import { BFF_BASE_URL } from '@cowprotocol/common-const' import { SupportedChainId } from '@cowprotocol/cow-sdk' import { gql, GraphQLClient } from 'graphql-request' @@ -55,7 +56,7 @@ const SEARCH_TOKENS = gql` } ` -const BASE_URL = 'https://cow-web-services.vercel.app/api/serverless/proxy' +const BASE_URL = `${BFF_BASE_URL}/proxies/tokens` const GQL_CLIENT = new GraphQLClient(BASE_URL) export async function searchTokensInApi(searchQuery: string): Promise {