diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index feb3e35a3..320cda231 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -55,7 +55,7 @@ const StyledMenuButton = styled.button` } ` -const UNIbutton = styled(ButtonPrimary)` +export const UNIbutton = styled(ButtonPrimary)` background-color: ${({ theme }) => theme.bg3}; background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2; border: none; diff --git a/src/custom/components/Header/HeaderMod.tsx b/src/custom/components/Header/HeaderMod.tsx index bc8941511..30cf5fea2 100644 --- a/src/custom/components/Header/HeaderMod.tsx +++ b/src/custom/components/Header/HeaderMod.tsx @@ -121,8 +121,7 @@ export const AccountElement = styled.div<{ active: boolean }>` } ` -/* -const UNIAmount = styled(AccountElement)` +export const UNIAmount = styled(AccountElement)` color: white; padding: 4px 8px; height: 36px; @@ -131,7 +130,7 @@ const UNIAmount = styled(AccountElement)` background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2; ` -const UNIWrapper = styled.span` +export const UNIWrapper = styled.span` width: fit-content; position: relative; cursor: pointer; @@ -144,7 +143,6 @@ const UNIWrapper = styled.span` opacity: 0.9; } ` -*/ export const HideSmall = styled.span` ${({ theme }) => theme.mediaWidth.upToSmall` diff --git a/src/custom/components/Header/index.tsx b/src/custom/components/Header/index.tsx index bf2718a41..f21b74542 100644 --- a/src/custom/components/Header/index.tsx +++ b/src/custom/components/Header/index.tsx @@ -1,7 +1,10 @@ +import { Trans } from '@lingui/macro' import { useState, useEffect } from 'react' import { SupportedChainId as ChainId } from 'constants/chains' +import { Dots } from 'components/swap/styleds' import Web3Status from 'components/Web3Status' -import { ExternalLink } from 'theme' +import { CardNoise } from 'components/earn/styled' +import { ExternalLink, TYPE } from 'theme' import HeaderMod, { Title, @@ -15,6 +18,8 @@ import HeaderMod, { StyledNavLink as StyledNavLinkUni, StyledMenuButton, HeaderFrame, + UNIAmount, + UNIWrapper, } from './HeaderMod' import Menu from 'components/Menu' import { Moon, Sun } from 'react-feather' @@ -27,12 +32,18 @@ import { darken } from 'polished' import TwitterImage from 'assets/cow-swap/twitter.svg' import OrdersPanel from 'components/OrdersPanel' import { ApplicationModal } from 'state/application/actions' -import { useModalOpen } from 'state/application/hooks' import { supportedChainId } from 'utils/supportedChainId' import { formatSmart } from 'utils/format' import NetworkCard, { NetworkInfo } from './NetworkCard' import SVG from 'react-inlinesvg' +import { useModalOpen, useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks' +import { useUserHasAvailableClaim } from 'state/claim/hooks' +import { useUserHasSubmittedClaim } from 'state/transactions/hooks' + +import Modal from 'components/Modal' +import ClaimModal from 'components/claim/ClaimModal' +import UniBalanceContent from 'components/Header/UniBalanceContent' export const NETWORK_LABELS: { [chainId in ChainId]?: string } = { [ChainId.RINKEBY]: 'Rinkeby', @@ -196,6 +207,13 @@ export default function Header() { const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? ''] const nativeToken = chainId && (CHAIN_CURRENCY_LABELS[chainId] || 'ETH') const [darkMode, toggleDarkMode] = useDarkModeManager() + + const toggleClaimModal = useToggleSelfClaimModal() + const availableClaim: boolean = useUserHasAvailableClaim(account) + const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined) + const [showUniBalanceModal, setShowUniBalanceModal] = useState(false) + const showClaimPopup = useShowClaimPopup() + const [isOrdersPanelOpen, setIsOrdersPanelOpen] = useState(false) const closeOrdersPanel = () => setIsOrdersPanelOpen(false) const openOrdersPanel = () => setIsOrdersPanelOpen(true) @@ -213,6 +231,10 @@ export default function Header() { + + setShowUniBalanceModal(false)}> + + <UniIcon> <LogoImage /> @@ -226,6 +248,22 @@ export default function Header() { <HeaderControls> <NetworkCard /> <HeaderElement> + {availableClaim && !showClaimPopup && ( + <UNIWrapper onClick={toggleClaimModal}> + <UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}> + <TYPE.white padding="0 2px"> + {claimTxn && !claimTxn?.receipt ? ( + <Dots> + <Trans>Claiming vCOW</Trans> + </Dots> + ) : ( + <Trans>Claim vCOW</Trans> + )} + </TYPE.white> + </UNIAmount> + <CardNoise /> + </UNIWrapper> + )} <AccountElement active={!!account} style={{ pointerEvents: 'auto' }}> {account && userEthBalance && ( <BalanceText style={{ flexShrink: 0, userSelect: 'none' }} pl="0.75rem" pr="0.5rem" fontWeight={500}> diff --git a/src/custom/components/Menu/MenuMod.tsx b/src/custom/components/Menu/MenuMod.tsx index b457685d2..011d81944 100644 --- a/src/custom/components/Menu/MenuMod.tsx +++ b/src/custom/components/Menu/MenuMod.tsx @@ -10,7 +10,7 @@ import { ApplicationModal } from 'state/application/actions' import { useModalOpen, useToggleModal } from 'state/application/hooks' // import { Trans } from '@lingui/macro' import { ExternalLink } from 'theme' -// import { ButtonPrimary } from 'components/Button' +import { ButtonPrimary } from 'components/Button' /* import { useDarkModeManager } from 'state/user/hooks' import { L2_CHAIN_IDS, CHAIN_INFO, SupportedChainId } from 'constants/chains' @@ -55,11 +55,11 @@ export const StyledMenuButton = styled.button` } ` -/* const UNIbutton = styled(ButtonPrimary)` +export const UNIbutton = styled(ButtonPrimary)` background-color: ${({ theme }) => theme.bg3}; background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2; border: none; -` */ +` export const StyledMenu = styled.div` margin-left: 0.5rem; diff --git a/src/custom/components/Menu/index.tsx b/src/custom/components/Menu/index.tsx index 5ae950583..3138df69b 100644 --- a/src/custom/components/Menu/index.tsx +++ b/src/custom/components/Menu/index.tsx @@ -1,4 +1,5 @@ import { Code, HelpCircle, BookOpen, PieChart, Moon, Sun, Repeat, Star, User, ExternalLink } from 'react-feather' +import { Trans } from '@lingui/macro' import MenuMod, { MenuItem, @@ -6,6 +7,7 @@ import MenuMod, { MenuFlyout as MenuFlyoutUni, MenuItemBase, StyledMenuButton, + UNIbutton, } from './MenuMod' import { useToggleModal } from 'state/application/hooks' import styled from 'styled-components/macro' @@ -225,6 +227,9 @@ export function Menu({ darkMode, toggleDarkMode }: MenuProps) { const hasOrders = useHasOrders(account) const showOrdersLink = account && hasOrders + const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM) + const showUNIClaimOption = Boolean(!!account && !!chainId) + return ( <StyledMenu> <MenuFlyout> @@ -317,6 +322,11 @@ export function Menu({ darkMode, toggleDarkMode }: MenuProps) { <Policy to="/privacy-policy">Privacy policy</Policy> <Policy to="/cookie-policy">Cookie policy</Policy> */} + {showUNIClaimOption && ( + <UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" $borderRadius="12px" mt="0.5rem"> + <Trans>Claim vCOW</Trans> + </UNIbutton> + )} </MenuFlyout> </StyledMenu> ) diff --git a/src/custom/pages/App/AppMod.tsx b/src/custom/pages/App/AppMod.tsx index b2f523ae9..8f7a913a3 100644 --- a/src/custom/pages/App/AppMod.tsx +++ b/src/custom/pages/App/AppMod.tsx @@ -3,14 +3,14 @@ import { Suspense, /* PropsWithChildren, */ ReactNode, useState, useEffect } fro import { Route, Switch, useLocation } from 'react-router-dom' import styled from 'styled-components/macro' import GoogleAnalyticsReporter from 'components/analytics/GoogleAnalyticsReporter' -// import AddressClaimModal from '../components/claim/AddressClaimModal' +import AddressClaimModal from 'components/claim/AddressClaimModal' import ErrorBoundary from 'components/ErrorBoundary' import Header from 'components/Header' import Polling from 'components/Header/Polling' import Popups from 'components/Popups' import Web3ReactManager from 'components/Web3ReactManager' -// import { ApplicationModal } from '../../state/application/actions' -// import { useModalOpen, useToggleModal } from '../state/application/hooks' +import { ApplicationModal } from 'state/application/actions' +import { useModalOpen, useToggleModal } from 'state/application/hooks' import DarkModeQueryParamReader from 'theme' /* import AddLiquidity from './AddLiquidity' import { @@ -94,11 +94,11 @@ const Marginer = styled.div` margin-top: 5rem; ` -// function TopLevelModals() { -// const open = useModalOpen(ApplicationModal.ADDRESS_CLAIM) -// const toggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM) -// return <AddressClaimModal isOpen={open} onDismiss={toggle} /> -// } +function TopLevelModals() { + const open = useModalOpen(ApplicationModal.ADDRESS_CLAIM) + const toggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM) + return <AddressClaimModal isOpen={open} onDismiss={toggle} /> +} export default function App(props?: { children?: ReactNode }) { const [bgBlur, setBgBlur] = useState(false) @@ -121,7 +121,7 @@ export default function App(props?: { children?: ReactNode }) { </HeaderWrapper> <BodyWrapper> <Polling /> - {/* <TopLevelModals /> */} + <TopLevelModals /> <ReferralLinkUpdater /> <Switch> {props && props.children} diff --git a/src/custom/pages/App/index.tsx b/src/custom/pages/App/index.tsx index 45a64b189..cdd6f5480 100644 --- a/src/custom/pages/App/index.tsx +++ b/src/custom/pages/App/index.tsx @@ -3,6 +3,7 @@ import styled from 'styled-components/macro' import { RedirectPathToSwapOnly, RedirectToSwap } from 'pages/Swap/redirects' import { Route, Switch } from 'react-router-dom' import Swap from 'pages/Swap' +import Claim from 'pages/Claim' import PrivacyPolicy from 'pages/PrivacyPolicy' import CookiePolicy from 'pages/CookiePolicy' import TermsAndConditions from 'pages/TermsAndConditions' @@ -70,6 +71,7 @@ export default function App() { <Route exact strict path="/swap" component={Swap} /> <Route exact strict path="/swap/:outputCurrency" component={RedirectToSwap} /> <Route exact strict path="/send" component={RedirectPathToSwapOnly} /> + <Route exact strict path="/claim" component={Claim} /> <Route exact strict path="/about" component={About} /> <Route exact strict path="/profile" component={Profile} /> <Route exact strict path="/faq" component={Faq} /> @@ -78,7 +80,6 @@ export default function App() { <Route exact strict path="/privacy-policy" component={PrivacyPolicy} /> <Route exact strict path="/cookie-policy" component={CookiePolicy} /> <Route exact strict path="/terms-and-conditions" component={TermsAndConditions} /> - <Route exact strict path="/chat" component={createRedirectExternal('https://chat.cowswap.exchange')} /> <Route exact strict path="/docs" component={createRedirectExternal('https://docs.cowswap.exchange')} /> <Route @@ -88,7 +89,6 @@ export default function App() { component={createRedirectExternal('https://dune.xyz/gnosis.protocol/Gnosis-Protocol-V2')} /> <Route exact strict path="/twitter" component={createRedirectExternal('https://twitter.com/MEVprotection')} /> - <Route exact strict path="/" component={RedirectPathToSwapOnly} /> <Route component={NotFound} /> </Switch> diff --git a/src/custom/pages/Claim/index.tsx b/src/custom/pages/Claim/index.tsx new file mode 100644 index 000000000..baedf67c3 --- /dev/null +++ b/src/custom/pages/Claim/index.tsx @@ -0,0 +1,121 @@ +import JSBI from 'jsbi' +import { AutoColumn } from 'components/Column' +import styled from 'styled-components/macro' +import { STAKING_REWARDS_INFO, useStakingInfo } from 'state/stake/hooks' +import { TYPE, ExternalLink } from 'theme' +import PoolCard from 'components/earn/PoolCard' +import { RowBetween } from 'components/Row' +import { CardSection, DataCard, CardNoise, CardBGImage } from 'components/earn/styled' +import { Countdown } from 'pages/Earn/Countdown' +import Loader from 'components/Loader' +import { useActiveWeb3React } from 'hooks/web3' +import { BIG_INT_ZERO } from 'constants/misc' +import { OutlineCard } from 'components/Card' +import { Trans } from '@lingui/macro' + +const PageWrapper = styled(AutoColumn)` + max-width: 640px; + width: 100%; +` + +const TopSection = styled(AutoColumn)` + max-width: 720px; + width: 100%; +` + +const PoolSection = styled.div` + display: grid; + grid-template-columns: 1fr; + column-gap: 10px; + row-gap: 15px; + width: 100%; + justify-self: center; +` + +const DataRow = styled(RowBetween)` + ${({ theme }) => theme.mediaWidth.upToSmall` +flex-direction: column; +`}; +` + +export default function Earn() { + const { chainId } = useActiveWeb3React() + + // staking info for connected account + const stakingInfos = useStakingInfo() + + /** + * only show staking cards with balance + * @todo only account for this if rewards are inactive + */ + const stakingInfosWithBalance = stakingInfos?.filter((s) => JSBI.greaterThan(s.stakedAmount.quotient, BIG_INT_ZERO)) + + // toggle copy if rewards are inactive + const stakingRewardsExist = Boolean(typeof chainId === 'number' && (STAKING_REWARDS_INFO[chainId]?.length ?? 0) > 0) + + return ( + <PageWrapper gap="lg" justify="center"> + <TopSection gap="md"> + <DataCard> + <CardBGImage /> + <CardNoise /> + <CardSection> + <AutoColumn gap="md"> + <RowBetween> + <TYPE.white fontWeight={600}> + <Trans>Uniswap liquidity mining</Trans> + </TYPE.white> + </RowBetween> + <RowBetween> + <TYPE.white fontSize={14}> + <Trans> + Deposit your Liquidity Provider tokens to receive UNI, the Uniswap protocol governance token. + </Trans> + </TYPE.white> + </RowBetween>{' '} + <ExternalLink + style={{ color: 'white', textDecoration: 'underline' }} + href="https://uniswap.org/blog/uni/" + target="_blank" + > + <TYPE.white fontSize={14}> + <Trans>Read more about UNI</Trans> + </TYPE.white> + </ExternalLink> + </AutoColumn> + </CardSection> + <CardBGImage /> + <CardNoise /> + </DataCard> + </TopSection> + + <AutoColumn gap="lg" style={{ width: '100%', maxWidth: '720px' }}> + <DataRow style={{ alignItems: 'baseline' }}> + <TYPE.mediumHeader style={{ marginTop: '0.5rem' }}> + <Trans>Participating pools</Trans> + </TYPE.mediumHeader> + <Countdown exactEnd={new Date(Date.now() + 100000000)} /> + </DataRow> + + <PoolSection> + {stakingRewardsExist && stakingInfos?.length === 0 ? ( + <Loader style={{ margin: 'auto' }} /> + ) : !stakingRewardsExist ? ( + <OutlineCard> + <Trans>No active pools</Trans> + </OutlineCard> + ) : stakingInfos?.length !== 0 && stakingInfosWithBalance.length === 0 ? ( + <OutlineCard> + <Trans>No active pools</Trans> + </OutlineCard> + ) : ( + stakingInfosWithBalance?.map((stakingInfo) => { + // need to sort by added liquidity here + return <PoolCard key={stakingInfo.stakingRewardAddress} stakingInfo={stakingInfo} /> + }) + )} + </PoolSection> + </AutoColumn> + </PageWrapper> + ) +} diff --git a/src/custom/state/claim/hooks/hooksMod.ts b/src/custom/state/claim/hooks/hooksMod.ts new file mode 100644 index 000000000..e8feda29d --- /dev/null +++ b/src/custom/state/claim/hooks/hooksMod.ts @@ -0,0 +1,204 @@ +import JSBI from 'jsbi' +import { CurrencyAmount, Token } from '@uniswap/sdk-core' +import { TransactionResponse } from '@ethersproject/providers' +// import { useEffect, useState } from 'react' +import { UNI } from 'constants/tokens' +import { useActiveWeb3React } from 'hooks/web3' +import { useMerkleDistributorContract } from 'hooks/useContract' +import { calculateGasMargin } from 'utils/calculateGasMargin' +// import { useSingleCallResult } from 'state/multicall/hooks' +import { isAddress } from 'utils/index' +import { useTransactionAdder } from 'state/enhancedTransactions/hooks' +import { UserClaims } from '.' + +// interface UserClaimData { +// index: number +// amount: string +// proof: string[] +// flags?: { +// isSOCKS: boolean +// isLP: boolean +// isUser: boolean +// } +// } + +type LastAddress = string +type ClaimAddressMapping = { [firstAddress: string]: LastAddress } +let FETCH_CLAIM_MAPPING_PROMISE: Promise<ClaimAddressMapping> | null = null +function fetchClaimsMapping(): Promise<ClaimAddressMapping> { + return ( + FETCH_CLAIM_MAPPING_PROMISE ?? + (FETCH_CLAIM_MAPPING_PROMISE = fetch( + `https://raw.githubusercontent.com/gnosis/cow-mrkl-drop-data-chunks/final/chunks/mapping.json` + ) + .then((res) => res.json()) + .catch((error) => { + console.error('Failed to get claims mapping', error) + FETCH_CLAIM_MAPPING_PROMISE = null + })) + ) +} + +const FETCH_CLAIM_FILE_PROMISES: { [startingAddress: string]: Promise<{ [address: string]: UserClaims }> } = {} +function fetchClaimsFile(key: string): Promise<{ [address: string]: UserClaims }> { + return ( + FETCH_CLAIM_FILE_PROMISES[key] ?? + (FETCH_CLAIM_FILE_PROMISES[key] = fetch( + `https://raw.githubusercontent.com/gnosis/cow-mrkl-drop-data-chunks/final/chunks/${key}.json` + ) + .then((res) => res.json()) + .catch((error) => { + console.error(`Failed to get claim file mapping for starting address ${key}`, error) + delete FETCH_CLAIM_FILE_PROMISES[key] + })) + ) +} + +const FETCH_CLAIM_PROMISES: { [key: string]: Promise<UserClaims> } = {} + +// returns the claim for the given address, or null if not valid +export function fetchClaims(account: string): Promise<UserClaims> { + const formatted = isAddress(account) + if (!formatted) return Promise.reject(new Error('Invalid address')) + + return ( + FETCH_CLAIM_PROMISES[account] ?? + (FETCH_CLAIM_PROMISES[account] = fetchClaimsMapping() + .then((mapping) => { + const sorted = Object.keys(mapping).sort((a, b) => (a.toLowerCase() < b.toLowerCase() ? -1 : 1)) + + for (const startingAddress of sorted) { + const lastAddress = mapping[startingAddress] + if (startingAddress.toLowerCase() <= formatted.toLowerCase()) { + if (formatted.toLowerCase() <= lastAddress.toLowerCase()) { + return startingAddress + } + } else { + throw new Error(`Claim for ${formatted} was not found in partial search`) + } + } + throw new Error(`Claim for ${formatted} was not found after searching all mappings`) + }) + .then(fetchClaimsFile) + .then((result) => { + if (result[formatted]) return result[formatted] + throw new Error(`Claim for ${formatted} was not found in claim file!`) + }) + .catch((error) => { + console.debug('Claim fetch failed', error) + throw error + })) + ) +} + +// parse distributorContract blob and detect if user has claim data +// null means we know it does not +export function useUserClaims(account: string | null | undefined): UserClaims | null { + console.log('[useUserClaims] ', account) + return [ + { + index: 0, + amount: '100000000000000000', + proof: ['this', 'proofs', 'nothing'], + }, + { + index: 1, + amount: '2000000000000000000', + proof: ['this', 'proofs', 'even', 'less'], + }, + ] + // const { chainId } = useActiveWeb3React() + // const [claimInfo, setClaimInfo] = useState<{ [account: string]: UserClaims | null }>({}) + + // useEffect(() => { + // if (!account || chainId !== 1) return + + // fetchClaims(account) + // .then((accountClaimInfo) => + // setClaimInfo((claimInfo) => { + // return { + // ...claimInfo, + // [account]: accountClaimInfo, + // } + // }) + // ) + // .catch(() => { + // setClaimInfo((claimInfo) => { + // return { + // ...claimInfo, + // [account]: null, + // } + // }) + // }) + // }, [account, chainId]) + + // return account && chainId === 1 ? claimInfo[account] : null +} + +// check if user is in blob and has not yet claimed UNI +export function useUserHasAvailableClaim(account: string | null | undefined): boolean { + const userClaims = useUserClaims(account) + // const distributorContract = useMerkleDistributorContract() + + // TODO: Go claiming by claiming, and check if claimed or not + // TODO: Should we do a multicall instead, or the contract allows to check multiple claimings at once? + const isClaimedResult = { loading: false, result: [false] } //useSingleCallResult(distributorContract, 'isClaimed', [userClaimData?.index]) + + // user is in blob and contract marks as unclaimed + return Boolean(userClaims && !isClaimedResult.loading && isClaimedResult.result?.[0] === false) +} + +export function useUserUnclaimedAmount(account: string | null | undefined): CurrencyAmount<Token> | undefined { + const { chainId } = useActiveWeb3React() + const claims = useUserClaims(account) + const canClaim = useUserHasAvailableClaim(account) + + const uni = chainId ? UNI[chainId] : undefined + if (!uni) return undefined + if (!canClaim || !claims) { + return CurrencyAmount.fromRawAmount(uni, JSBI.BigInt(0)) + } + const totalAmount = claims.reduce((acc, claim) => { + return JSBI.add(acc, JSBI.BigInt(claim.amount)) + }, JSBI.BigInt('0')) + + return CurrencyAmount.fromRawAmount(uni, JSBI.BigInt(totalAmount)) +} + +export function useClaimCallback(account: string | null | undefined): { + claimCallback: () => Promise<string> +} { + // get claim data for this account + const { library, chainId } = useActiveWeb3React() + const claimData = useUserClaims(account) + + // used for popup summary + const unclaimedAmount: CurrencyAmount<Token> | undefined = useUserUnclaimedAmount(account) + const addTransaction = useTransactionAdder() + const distributorContract = useMerkleDistributorContract() + + const claimCallback = async function () { + if (!claimData || !account || !library || !chainId || !distributorContract) return + + // const args = [claimData.index, account, claimData.amount, claimData.proof] + + // TODO: Reduce Claimings into a bunch of arrays with all the claimings + // const args = claimData.reduce(...) + const args: string[] = [] + + return distributorContract.estimateGas['claim'](...args, {}).then((estimatedGasLimit) => { + return distributorContract + .claim(...args, { value: null, gasLimit: calculateGasMargin(chainId, estimatedGasLimit) }) + .then((response: TransactionResponse) => { + addTransaction({ + hash: response.hash, + summary: `Claimed ${unclaimedAmount?.toSignificant(4)} CoW`, + claim: { recipient: account }, + }) + return response.hash + }) + }) + } + + return { claimCallback } +} diff --git a/src/custom/state/claim/hooks/index.ts b/src/custom/state/claim/hooks/index.ts new file mode 100644 index 000000000..a0039292e --- /dev/null +++ b/src/custom/state/claim/hooks/index.ts @@ -0,0 +1,10 @@ +export * from './hooksMod' + +export interface UserClaimData { + index: number + amount: string + proof: string[] + // TODO: Either add the missing fields, or add https://github.com/gnosis/gp-v2-token type +} + +export type UserClaims = UserClaimData[] diff --git a/src/custom/state/enhancedTransactions/actions.ts b/src/custom/state/enhancedTransactions/actions.ts index d1a77cfa5..397a195c4 100644 --- a/src/custom/state/enhancedTransactions/actions.ts +++ b/src/custom/state/enhancedTransactions/actions.ts @@ -8,7 +8,7 @@ type WithChainId = { chainId: number } export type AddTransactionParams = WithChainId & Pick< EnhancedTransactionDetails, - 'hash' | 'hashType' | 'from' | 'approval' | 'presign' | 'summary' | 'safeTransaction' + 'hash' | 'hashType' | 'from' | 'approval' | 'presign' | 'claim' | 'summary' | 'safeTransaction' > export const addTransaction = createAction<AddTransactionParams>('enhancedTransactions/addTransaction') diff --git a/src/custom/state/enhancedTransactions/reducer.ts b/src/custom/state/enhancedTransactions/reducer.ts index 4b41cbb52..dd0e1151f 100644 --- a/src/custom/state/enhancedTransactions/reducer.ts +++ b/src/custom/state/enhancedTransactions/reducer.ts @@ -34,6 +34,7 @@ export interface EnhancedTransactionDetails { // Operations approval?: { tokenAddress: string; spender: string } presign?: { orderId: string } + claim?: { recipient: string; cowAmountRaw?: string } // Wallet specific safeTransaction?: SafeMultisigTransactionResponse // Gnosis Safe transaction info