Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into 2440/uni-merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Leandro committed Mar 11, 2022
2 parents 108af03 + fe71f05 commit fffc31c
Show file tree
Hide file tree
Showing 32 changed files with 493 additions and 173 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ REACT_APP_NETWORK_URL_100=https://rpc.xdaichain.com

# Wallets
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_FORTMATIC_KEY="pk_live_6AED76CA755EFDC7"
REACT_APP_FORTMATIC_SITE_VERIFICATION="LzjrtdM7hqVJfvvA"

# Domain regex (to detect environment)
Expand Down
4 changes: 3 additions & 1 deletion .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ REACT_APP_NETWORK_URL_100=https://rpc.xdaichain.com

# Wallets
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_FORTMATIC_KEY="pk_live_9E53F9A29112A9FC"
REACT_APP_FORTMATIC_KEY_BARN="pk_live_C0DA025E15EBF895"
REACT_APP_FORTMATIC_KEY_PROD="pk_live_7BD8004CBBF5CDD6"

# Domain regex (to detect environment)
REACT_APP_DOMAIN_REGEX_LOCAL="^(:?localhost:\d{2,5}|(?:127|192)(?:\.[0-9]{1,3}){3})"
Expand Down
10 changes: 10 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,14 @@ module.exports = {
},
}),
},
devServer: (config) => {
// Add CORS headers, to enable to add the local served website in Gnosis Safe
config.headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
}

return config
},
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
}
},
"types": "./dist/widgets.d.ts",
"version": "1.10.4",
"version": "1.11.1",
"private": true,
"engines": {
"node": ">=14.0.0"
},
Expand Down Expand Up @@ -127,6 +128,7 @@
"scripts": {
"start:default": "craco start",
"start": "REACT_APP_DISABLE_TOKEN_WARNING=true yarn start:default",
"start:ssl": "HTTPS=true yarn start",
"start:service-worker": "yarn build && yarn serve",
"mock": "REACT_APP_MOCK=true yarn start",
"build": "yarn i18n:compile && craco build && yarn writeVersion",
Expand Down Expand Up @@ -257,6 +259,7 @@
"ethers": "^5.4.6",
"fast-safe-stringify": "^2.0.8",
"firebase": "^9.1.3",
"fortmatic": "^2.2.1",
"immer": "^9.0.6",
"ipfs-deploy": "^8.0.1",
"ipfs-http-client": "^52.0.3",
Expand Down
2 changes: 2 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"background_color": "#fff",
"display": "standalone",
"homepage_url": "https://cowswap.exchange",
"description": "Gasless MetaDEX Aggregator with front-running and Peer-to-Peer trades",
"iconPath": "favicon.png",
"icons": [
{
"src": "./favicon.png",
Expand Down
75 changes: 48 additions & 27 deletions src/custom/api/gnosisProtocol/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { FeeQuoteParams, PriceInformation, PriceQuoteParams, SimpleGetQuoteRespo

import { DEFAULT_NETWORK_FOR_LISTS } from 'constants/lists'
import * as Sentry from '@sentry/browser'
import { constructSentryError } from 'utils/logging'
import { ZERO_ADDRESS } from 'constants/misc'
import { getAppDataHash } from 'constants/appDataHash'
import { GpPriceStrategy } from 'hooks/useGetGpPriceStrategy'
Expand Down Expand Up @@ -274,36 +275,52 @@ const UNHANDLED_ORDER_ERROR: ApiErrorObject = {
description: ApiErrorCodeDetails.UNHANDLED_CREATE_ERROR,
}

async function _handleQuoteResponse<T = any, P extends QuoteQuery = QuoteQuery>(
async function _handleQuoteResponse<T = any, P extends FeeQuoteParams = FeeQuoteParams>(
response: Response,
params?: P
params: P
): Promise<T> {
if (!response.ok) {
const errorObj: ApiErrorObject = await response.json()

// we need to map the backend error codes to match our own for quotes
const mappedError = mapOperatorErrorToQuoteError(errorObj)
const quoteError = new QuoteError(mappedError)

if (params) {
const { sellToken, buyToken } = params

const sentryError = new Error()
Object.assign(sentryError, quoteError, {
message: `Error querying fee from API - sellToken: ${sellToken}, buyToken: ${buyToken}`,
name: 'FeeErrorObject',
})

// report to sentry
Sentry.captureException(sentryError, {
tags: { errorType: 'getFeeQuote' },
contexts: { params: { ...params } },
try {
if (!response.ok) {
// don't attempt json parse if not json response...
if (response.headers.get('Content-Type') !== 'application/json') {
throw new Error(`${response.status} error occurred. ${response.statusText}`)
}
const errorObj: ApiErrorObject = await response.json()

// we need to map the backend error codes to match our own for quotes
const mappedError = mapOperatorErrorToQuoteError(errorObj)
const quoteError = new QuoteError(mappedError)

// we need to create a sentry error and keep the original mapped quote error
throw constructSentryError(quoteError, response, {
message: `${quoteError.description} [sellToken: ${params.sellToken}]//[buyToken: ${params.buyToken}]`,
name: `[${quoteError.name}] - ${quoteError.type}`,
optionalTags: {
quoteErrorType: quoteError.type,
},
})
} else {
return response.json()
}
} catch (error) {
// Create a new sentry error OR
// use the previously created and rethrown error from the try block
const sentryError =
error?.sentryError ||
constructSentryError(error, response, {
message: `Potential backend error detected - status code: ${response.status}`,
name: '[HandleQuoteResponse] - Unmapped Quote Error',
})
// Create the error tags or use the previously constructed ones from the try block
const tags = error?.tags || { errorType: 'handleQuoteResponse', backendErrorCode: response.status }

throw quoteError
} else {
return response.json()
// report to sentry
Sentry.captureException(sentryError, {
tags,
contexts: { params: { ...params } },
})

throw error?.baseError || error
}
}

Expand Down Expand Up @@ -344,7 +361,7 @@ export async function getQuote(params: FeeQuoteParams) {
const quoteParams = _mapNewToLegacyParams(params)
const response = await _post(chainId, '/quote', quoteParams)

return _handleQuoteResponse<SimpleGetQuoteResponse>(response)
return _handleQuoteResponse<SimpleGetQuoteResponse>(response, params)
}

export async function getPriceQuoteLegacy(params: PriceQuoteParams): Promise<PriceInformation | null> {
Expand All @@ -363,7 +380,11 @@ export async function getPriceQuoteLegacy(params: PriceQuoteParams): Promise<Pri
throw new QuoteError(UNHANDLED_QUOTE_ERROR)
})

return _handleQuoteResponse<PriceInformation | null>(response)
return _handleQuoteResponse<PriceInformation | null>(response, {
...params,
buyToken: baseToken,
sellToken: quoteToken,
})
}

export async function getOrder(chainId: ChainId, orderId: string): Promise<OrderMetaData | null> {
Expand Down
44 changes: 28 additions & 16 deletions src/custom/api/gnosisProtocol/errors/OperatorError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export interface ApiErrorObject {
}

// Conforms to backend API
// https://github.com/gnosis/gp-v2-services/blob/d932e11c9a2125fdba239530be7684799f694909/crates/orderbook/openapi.yml#L801
// https://github.com/gnosis/gp-v2-services/blob/main/crates/orderbook/openapi.yml#L801
// and
// https://github.com/gnosis/gp-v2-services/blob/d932e11c9a2125fdba239530be7684799f694909/crates/orderbook/openapi.yml#L740
// https://github.com/gnosis/gp-v2-services/blob/main/crates/orderbook/openapi.yml#L740
export enum ApiErrorCodes {
DuplicateOrder = 'DuplicateOrder',
InvalidSignature = 'InvalidSignature',
Expand All @@ -29,6 +29,12 @@ export enum ApiErrorCodes {
AmountIsZero = 'AmountIsZero',
SellAmountDoesNotCoverFee = 'SellAmountDoesNotCoverFee',
TransferEthToContract = 'TransferEthToContract',
TransferSimulationFailed = 'TransferSimulationFailed',
SameBuyAndSellToken = 'SameBuyAndSellToken',
ZeroAmount = 'ZeroAmount',
UnsupportedBuyTokenDestination = 'UnsupportedBuyTokenDestination',
UnsupportedSellTokenSource = 'UnsupportedSellTokenSource',
UnsupportedOrderType = 'UnsupportedOrderType',
UNHANDLED_GET_ERROR = 'UNHANDLED_GET_ERROR',
UNHANDLED_CREATE_ERROR = 'UNHANDLED_CREATE_ERROR',
UNHANDLED_DELETE_ERROR = 'UNHANDLED_DELETE_ERROR',
Expand All @@ -38,24 +44,30 @@ export enum ApiErrorCodeDetails {
DuplicateOrder = 'There was another identical order already submitted. Please try again.',
InsufficientFee = "The signed fee is insufficient. It's possible that is higher now due to a change in the gas price, ether price, or the sell token price. Please try again to get an updated fee quote.",
InvalidSignature = 'The order signature is invalid. Check whether your Wallet app supports off-chain signing.',
MissingOrderData = 'The order has missing information',
MissingOrderData = 'The order has missing information.',
InsufficientValidTo = 'The order you are signing is already expired. This can happen if you set a short expiration in the settings and waited too long before signing the transaction. Please try again.',
InsufficientAllowance = "The account doesn't have enough funds",
InsufficientBalance = 'The account needs to approve the selling token in order to trade',
InsufficientAllowance = "The account doesn't have enough funds.",
InsufficientBalance = 'The account needs to approve the selling token in order to trade.',
WrongOwner = "The signature is invalid.\n\nIt's likely that the signing method provided by your wallet doesn't comply with the standards required by CowSwap.\n\nCheck whether your Wallet app supports off-chain signing (EIP-712 or ETHSIGN).",
NotFound = 'Token pair selected has insufficient liquidity',
OrderNotFound = 'The order you are trying to cancel does not exist',
AlreadyCancelled = 'Order is already cancelled',
OrderFullyExecuted = 'Order is already filled',
OrderExpired = 'Order is expired',
NoLiquidity = 'Token pair selected has insufficient liquidity',
NotFound = 'Token pair selected has insufficient liquidity.',
OrderNotFound = 'The order you are trying to cancel does not exist.',
AlreadyCancelled = 'Order is already cancelled.',
OrderFullyExecuted = 'Order is already filled.',
OrderExpired = 'Order is expired.',
NoLiquidity = 'Token pair selected has insufficient liquidity.',
UnsupportedToken = 'One of the tokens you are trading is unsupported. Please read the FAQ for more info.',
AmountIsZero = 'Amount is zero',
SellAmountDoesNotCoverFee = 'Sell amount does not sufficiently cover the current fee',
TransferEthToContract = 'Sending the native currency to smart contract wallets is not currently supported',
AmountIsZero = 'Amount is zero.',
SellAmountDoesNotCoverFee = 'Sell amount does not sufficiently cover the current fee.',
TransferEthToContract = 'Sending native currency to smart contract wallets is not currently supported.',
TransferSimulationFailed = 'Transfer simulation of native currency failed. This is likely due to the current unsupported state of smart contract wallets.',
SameBuyAndSellToken = 'Sell and buy token can not be the same.',
ZeroAmount = 'Order amount cannot be zero.',
UnsupportedBuyTokenDestination = 'Buy token destination is unsupported. Please try again with a different destination.',
UnsupportedSellTokenSource = 'Sell token source is unsupported. Please try again with a different source.',
UnsupportedOrderType = 'Order type unsupported. Please try again with a different order type.',
UNHANDLED_GET_ERROR = 'Order fetch failed. This may be due to a server or network connectivity issue. Please try again later.',
UNHANDLED_CREATE_ERROR = 'The order was not accepted by the network',
UNHANDLED_DELETE_ERROR = 'The order cancellation was not accepted by the network',
UNHANDLED_CREATE_ERROR = 'The order was not accepted by the network.',
UNHANDLED_DELETE_ERROR = 'The order cancellation was not accepted by the network.',
}

function _mapActionToErrorDetail(action?: ApiActionType) {
Expand Down
15 changes: 12 additions & 3 deletions src/custom/api/gnosisProtocol/errors/QuoteError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ export interface GpQuoteErrorObject {
}

// Conforms to backend API
// https://github.com/gnosis/gp-v2-services/blob/0bd5f7743bebaa5acd3be13e35ede2326a096f14/orderbook/openapi.yml#L562
// https://github.com/gnosis/gp-v2-services/blob/main/crates/orderbook/openapi.yml
export enum GpQuoteErrorCodes {
UnsupportedToken = 'UnsupportedToken',
InsufficientLiquidity = 'InsufficientLiquidity',
FeeExceedsFrom = 'FeeExceedsFrom',
ZeroPrice = 'ZeroPrice',
TransferEthToContract = 'TransferEthToContract',
UNHANDLED_ERROR = 'UNHANDLED_ERROR',
}

export enum GpQuoteErrorDetails {
UnsupportedToken = 'One of the tokens you are trading is unsupported. Please read the FAQ for more info.',
InsufficientLiquidity = 'Token pair selected has insufficient liquidity',
FeeExceedsFrom = 'Current fee exceeds entered "from" amount',
InsufficientLiquidity = 'Token pair selected has insufficient liquidity.',
FeeExceedsFrom = 'Current fee exceeds entered "from" amount.',
ZeroPrice = 'Quoted price is zero. This is likely due to a significant price difference between the two tokens. Please try increasing amounts.',
TransferEthToContract = 'Buying native currencies using smart contract wallets is not currently supported.',
UNHANDLED_ERROR = 'Quote fetch failed. This may be due to a server or network connectivity issue. Please try again later.',
}

Expand All @@ -32,17 +34,24 @@ export function mapOperatorErrorToQuoteError(error?: ApiErrorObject): GpQuoteErr
errorType: GpQuoteErrorCodes.InsufficientLiquidity,
description: GpQuoteErrorDetails.InsufficientLiquidity,
}

case ApiErrorCodes.SellAmountDoesNotCoverFee:
return {
errorType: GpQuoteErrorCodes.FeeExceedsFrom,
description: GpQuoteErrorDetails.FeeExceedsFrom,
data: error?.data,
}

case ApiErrorCodes.UnsupportedToken:
return {
errorType: GpQuoteErrorCodes.UnsupportedToken,
description: error.description,
}
case ApiErrorCodes.TransferEthToContract:
return {
errorType: GpQuoteErrorCodes.TransferEthToContract,
description: error.description,
}
default:
return { errorType: GpQuoteErrorCodes.UNHANDLED_ERROR, description: GpQuoteErrorDetails.UNHANDLED_ERROR }
}
Expand Down
Binary file removed src/custom/assets/cow-swap/cows-side-by-side.png
Binary file not shown.
11 changes: 7 additions & 4 deletions src/custom/components/AccountDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { groupActivitiesByDay, useMultipleActivityDescriptors } from 'hooks/useR
import { CreationDateText } from 'components/AccountDetails/Transaction/styled'
import { ExternalLink } from 'theme'
import { getExplorerAddressLink } from 'utils/explorer'
import { gnosisSafe } from 'connectors'

const DATE_FORMAT_OPTION: Intl.DateTimeFormatOptions = {
dateStyle: 'long',
Expand Down Expand Up @@ -195,14 +196,16 @@ export default function AccountDetails({
<AccountGroupingRow>
<AccountControl>
<WalletSecondaryActions>
{connector !== injected && connector !== walletlink && (
{connector !== injected && connector !== walletlink && connector !== gnosisSafe && (
<WalletAction onClick={handleDisconnectClick}>
<Trans>Disconnect</Trans>
</WalletAction>
)}
<WalletAction onClick={toggleWalletModal}>
<Trans>Change Wallet</Trans>
</WalletAction>
{connector !== gnosisSafe && (
<WalletAction onClick={toggleWalletModal}>
<Trans>Change Wallet</Trans>
</WalletAction>
)}
{chainId && account && (
<AddressLink
hasENS={!!ENSName}
Expand Down
8 changes: 5 additions & 3 deletions src/custom/components/AffiliateStatusCheck/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export default function AffiliateStatusCheck() {
const [affiliateState, setAffiliateState] = useState<AffiliateStatus | null>()
const [error, setError] = useState('')
const isFirstTrade = useRef(false)
const fulfilledActivity = allRecentActivity.filter((data) => data.status === OrderStatus.FULFILLED)
const fulfilledOrders = allRecentActivity.filter((data) => {
return 'appData' in data && data.status === OrderStatus.FULFILLED
})

const notificationBannerId = useMemo(() => {
if (!referralAddress?.value) {
Expand All @@ -61,7 +63,7 @@ export default function AffiliateStatusCheck() {
return
}

if (fulfilledActivity.length >= 1 && isFirstTrade.current) {
if (fulfilledOrders.length >= 1 && isFirstTrade.current) {
setAffiliateState(null)
isFirstTrade.current = false
history.replace({ search: '' })
Expand All @@ -83,7 +85,7 @@ export default function AffiliateStatusCheck() {

setAffiliateState('ACTIVE')
isFirstTrade.current = true
}, [referralAddress, chainId, account, fulfilledActivity.length, history, resetReferralAddress])
}, [referralAddress, chainId, account, fulfilledOrders.length, history, resetReferralAddress])

useEffect(() => {
async function handleReferralAddress(referralAddress: { value: string; isValid: boolean } | undefined) {
Expand Down
8 changes: 0 additions & 8 deletions src/custom/components/TransactionConfirmationModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -540,19 +540,11 @@ export function TransactionSubmittedContent({
</ButtonCustom>
)}

{/*
<ButtonCustom>
<InternalLink to="/play/cow-runner" onClick={onDismiss}>
<StyledIcon src={GameIcon} alt="Play CowGame" />
Play the Cow Runner Game!
</InternalLink>
</ButtonCustom>
*/}
<ButtonCustom>
<InternalLink to="/play/mev-slicer" onClick={onDismiss}>
<StyledIcon src={GameIcon} alt="Play Cow Slicer Game" />
Play the Cow Slicer Game!
</InternalLink>
</ButtonCustom>
</ButtonGroup>

Expand Down
Loading

0 comments on commit fffc31c

Please sign in to comment.