Skip to content

Commit

Permalink
🐷 Add Place Bid transaction flow (#36)
Browse files Browse the repository at this point in the history
Co-authored-by: Bartlomiej Tarczynski <bartlomiej.tarczynski1@gmail.com>
  • Loading branch information
p-sad and b-tarczynski authored Apr 26, 2024
1 parent 75861c0 commit 28286ec
Show file tree
Hide file tree
Showing 21 changed files with 429 additions and 59 deletions.
13 changes: 0 additions & 13 deletions packages/frontend/src/blockchain/hooks/useExplorerAddressLink.ts

This file was deleted.

19 changes: 19 additions & 0 deletions packages/frontend/src/blockchain/hooks/useExplorerLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Hex } from 'viem'
import { useChainId, useChains } from 'wagmi'

export function useExplorerAddressLink(address: Hex | undefined): string | undefined {
const url = useExplorerUrl()
return url && `${url}/address/${address}`
}

export function useExplorerTxLink(txHash: string) {
const url = useExplorerUrl()
return `${url}/tx/${txHash}`
}

function useExplorerUrl() {
const chains = useChains()
const chainId = useChainId()
const currentChain = chains.find((chain) => chain.id === chainId)
return currentChain?.blockExplorers?.default.url
}
11 changes: 11 additions & 0 deletions packages/frontend/src/blockchain/transaction/TransactionAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MutationStatus } from '@tanstack/react-query'
import { Transactions } from '.'
import { Hex } from 'viem'

export interface TransactionAction {
type: Transactions
send: () => Promise<Hex>
status: MutationStatus
resetStatus: () => void
transactionHash: Hex | undefined
}
5 changes: 5 additions & 0 deletions packages/frontend/src/blockchain/transaction/Transactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum Transactions {
Place,
Bump,
Withdraw,
}
2 changes: 2 additions & 0 deletions packages/frontend/src/blockchain/transaction/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './TransactionAction'
export * from './Transactions'
72 changes: 72 additions & 0 deletions packages/frontend/src/components/auction/AuctionTransaction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { TransactionAction, Transactions } from '@/blockchain/transaction'
import styled from 'styled-components'
import { TxFlowSteps } from './TxFlowSteps'
import { BackButton } from '../buttons/BackButton'
import { FormSubHeading, FormWrapper } from '../form'
import { TransactionStepper } from '../stepper/TransactionStepper'
import { ReviewForm } from '../form/ReviewForm'
import { TransactionSuccess } from './TransactionSuccess'

export const heading = {
[Transactions.Place]: 'Place bid',
[Transactions.Bump]: 'Bump your Bid',
[Transactions.Withdraw]: 'Withdraw',
}

interface AuctionTransactionProps {
action: TransactionAction
amount: bigint
impact?: bigint
view: TxFlowSteps
setView: (state: TxFlowSteps) => void
}

export const AuctionTransaction = ({ action, amount, impact, view, setView }: AuctionTransactionProps) => {
const isFailed = action.status === 'error'

return (
<Transaction>
<TransactionWrapper>
<TransactionHeading>
{view !== TxFlowSteps.Confirmation && (
<BackButton view={view} setView={setView} resetState={action.resetStatus} />
)}
<FormSubHeading>{heading[action.type]}</FormSubHeading>
</TransactionHeading>
{view === TxFlowSteps.Review && (
<ReviewForm action={action} amount={amount} impact={impact} view={view} setView={setView} />
)}
{view === TxFlowSteps.Confirmation && (
<TransactionSuccess
action={action.type}
txHash={action.transactionHash}
setView={setView}
resetStatus={action.resetStatus}
/>
)}
</TransactionWrapper>
<TransactionStepper
action={action.type}
current={view === TxFlowSteps.Confirmation || isFailed ? 'Finalized' : `${heading[action.type]}`}
isFailed={isFailed}
/>
</Transaction>
)
}

const Transaction = styled.div`
display: flex;
width: 100%;
`

const TransactionWrapper = styled(FormWrapper)`
flex: 1;
row-gap: 24px;
padding: 82px 54px;
width: fit-content;
`
const TransactionHeading = styled.div`
display: flex;
align-items: center;
column-gap: 16px;
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Transactions } from '@/blockchain/transaction'
import styled from 'styled-components'
import { TxFlowSteps } from '../TxFlowSteps'
import { TransactionSuccessHeader } from './TransactionSuccessHeader'
import { useExplorerTxLink } from '@/blockchain/hooks/useExplorerLinks'
import { CopyButton } from '@/components/buttons/CopyButton'
import { RedirectButton } from '@/components/buttons/RedirectButton'
import { Button } from '@/components/buttons'
import { Form, InputLabel } from '@/components/form'
import { Colors } from '@/styles/colors'
import { shortenHexString } from '@/utils/formatters/shortenHexString'
import { Hex } from 'viem'

interface Props {
txHash: Hex | undefined
action: Transactions
setView: (state: TxFlowSteps) => void
resetStatus: () => void
}

export const TransactionSuccess = ({ txHash, action, setView, resetStatus }: Props) => {
const transactionLink = useExplorerTxLink(txHash ?? '0x')

const goHome = () => {
setView(0)
resetStatus()
}

if (!txHash) {
return null
}

return (
<Container>
<TransactionSuccessHeader action={action} />
<TransactionIdWrapper>
<TransactionIdLabel>Your transaction hash</TransactionIdLabel>
<TransactionIdBox>
<TransactionIdText>{shortenHexString(txHash, 12)}</TransactionIdText>
<CopyButton value={txHash} side="top" text="Copy transaction hash" />
<RedirectButton link={transactionLink} side="top" tooltip="View on Arbiscan" />
</TransactionIdBox>
</TransactionIdWrapper>
<Button view="primary" onClick={goHome}>
Back to home
</Button>
</Container>
)
}

const Container = styled(Form)`
row-gap: 24px;
`

const TransactionIdWrapper = styled.div`
display: flex;
width: 100%;
flex-direction: column;
`

const TransactionIdLabel = styled(InputLabel)`
justify-content: flex-start;
margin-bottom: 8px;
`

const TransactionIdBox = styled.div`
display: flex;
align-items: center;
width: 100%;
background-color: ${Colors.White};
padding: 8px 12px;
height: 40px;
`

const TransactionIdText = styled.span`
flex: 1;
color: ${Colors.Grey};
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Transactions } from '@/blockchain/transaction'
import { CheckIcon } from '@/components/icons'
import { Colors } from '@/styles/colors'
import styled from 'styled-components'

const text = {
[Transactions.Place]: 'placed your bid',
[Transactions.Bump]: 'bumped your bid',
[Transactions.Withdraw]: 'withdrawn your funds',
}

interface SuccessHeaderProps {
action: Transactions
}

export function TransactionSuccessHeader({ action }: SuccessHeaderProps) {
return (
<SuccessHeaderWrap>
<HeaderRowIconContainer>
<CheckIcon color={Colors.Black} size={32} />
</HeaderRowIconContainer>
<SuccessText>You&apos;ve successfully {text[action]}!</SuccessText>
</SuccessHeaderWrap>
)
}

const SuccessHeaderWrap = styled.div`
display: flex;
align-items: center;
column-gap: 8px;
`

const HeaderRowIconContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 38px;
height: 38px;
border-radius: 50%;
border: 2px solid ${Colors.Black};
`

const SuccessText = styled.span`
font-weight: 600;
font-size: 20px;
line-height: 26px;
`
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './TransactionSuccess'
6 changes: 3 additions & 3 deletions packages/frontend/src/components/bids/BidsListEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import styled, { css } from 'styled-components'
import { Bid } from '@/types/bid'
import { Colors } from '@/styles/colors'
import { formatEther } from 'viem'
import { useExplorerAddressLink } from '@/blockchain/hooks/useExplorerAddressLink'
import { shortenEthAddress } from '@/utils/formatters/shortenEthAddress'
import { useExplorerAddressLink } from '@/blockchain/hooks/useExplorerLinks'
import { shortenHexString } from '@/utils/formatters/shortenHexString'

interface Props {
bid: Bid
Expand All @@ -23,7 +23,7 @@ export const BidsListEntry = ({ bid, isUser, view = 'full' }: Props) => {
</BidColumn>
<AddressColumn>
<AddressLink href={explorerAddressLink} target="_blank" rel="noopener noreferrer">
{view === 'short' ? shortenEthAddress(bid.address) : bid.address}
{view === 'short' ? shortenHexString(bid.address) : bid.address}
</AddressLink>
</AddressColumn>
</BidsEntryRow>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled from 'styled-components'
import { Colors } from '@/styles/colors'
import { useExplorerAddressLink } from '@/blockchain/hooks/useExplorerAddressLink'
import { useExplorerAddressLink } from '@/blockchain/hooks/useExplorerLinks'
import { Hex } from 'viem'

interface Props {
Expand Down
60 changes: 60 additions & 0 deletions packages/frontend/src/components/form/ReviewForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { TransactionAction, Transactions } from '@/blockchain/transaction'
import { TxFlowSteps } from '../auction/TxFlowSteps'
import { useAccount, useBalance } from 'wagmi'
import { Form, FormRow } from '.'
import { formatEther } from 'viem'
import { Button } from '../buttons'
import { heading } from '../auction/AuctionTransaction'

const amountLabel = {
[Transactions.Place]: 'Your Bid',
[Transactions.Bump]: 'Your Bid Bump',
[Transactions.Withdraw]: 'Withdraw amount',
}

interface ReviewFormProps {
action: TransactionAction
amount: bigint
impact?: bigint
view: TxFlowSteps
setView: (state: TxFlowSteps) => void
}

export const ReviewForm = ({
action: { status, resetStatus, ...action },
amount,
impact,
view,
setView,
}: ReviewFormProps) => {
const { address } = useAccount()
const etherBalance = useBalance({ address }).data?.value
const isPending = status === 'pending'

const sendTransaction = async () => {
await action.send()
setView(view + 1)
}

return (
<Form>
<FormRow>
<span>{amountLabel[action.type]}</span>
<span>{formatEther(amount)} ETH</span>
</FormRow>
{!!impact && (
<FormRow>
<span>Your Bid after the bump</span>
<span>{formatEther(impact)} ETH</span>
</FormRow>
)}
<FormRow>
<span>Wallet Balance</span>
<span>{!!etherBalance && formatEther(etherBalance)} ETH</span>
</FormRow>
<Button view="primary" isLoading={isPending} onClick={sendTransaction}>
{heading[action.type]}
</Button>
</Form>
)
}
16 changes: 6 additions & 10 deletions packages/frontend/src/components/stepper/Stepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ interface StepContent {
description?: string
}

type StepDescription = 'default' | 'failed'

type Step = Record<StepDescription, StepContent>
interface Step {
default: StepContent
failed?: StepContent
}

type Steps = Step[]

Expand Down Expand Up @@ -63,13 +64,8 @@ const StepperList = styled.ul`
margin: 0;
`

function getItemColor(props: DisplayTypeProps) {
switch (props.status) {
case 'current':
return typeToItemColor[props.type]
default:
return Colors.Black
}
function getItemColor({ status, type }: DisplayTypeProps) {
return status === 'current' ? typeToItemColor[type] : Colors.Black
}

const typeToItemColor: Record<StepType, string> = {
Expand Down
Loading

0 comments on commit 28286ec

Please sign in to comment.