Skip to content

Commit

Permalink
refactor: swap ux and business logic improvements (#2358)
Browse files Browse the repository at this point in the history
  • Loading branch information
enesozturk authored Jun 4, 2024
1 parent 0095824 commit 2bb270e
Show file tree
Hide file tree
Showing 19 changed files with 238 additions and 93 deletions.
7 changes: 6 additions & 1 deletion apps/gallery/stories/composites/wui-snackbar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export default {
message: 'Address approved',
backgroundColor: 'success-100',
iconColor: 'success-100',
icon: 'checkmark'
icon: 'checkmark',
loading: false
},

argTypes: {
Expand All @@ -27,6 +28,9 @@ export default {
icon: {
options: iconOptions,
control: { type: 'select' }
},
loading: {
control: { type: 'boolean' }
}
}
} as Component
Expand All @@ -38,6 +42,7 @@ export const Default: Component = {
backgroundColor=${args.backgroundColor}
icon=${args.icon}
message=${args.message}
.loading=${args.loading}
>
</wui-snackbar>`
}
8 changes: 7 additions & 1 deletion packages/core/src/controllers/SnackController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CoreHelperUtil } from '../utils/CoreHelperUtil.js'
// -- Types --------------------------------------------- //
export interface SnackControllerState {
message: string
variant: 'error' | 'success'
variant: 'error' | 'success' | 'loading'
open: boolean
}

Expand All @@ -26,6 +26,12 @@ export const SnackController = {
return subKey(state, key, callback)
},

showLoading(message: SnackControllerState['message']) {
state.message = message
state.variant = 'loading'
state.open = true
},

showSuccess(message: SnackControllerState['message']) {
state.message = message
state.variant = 'success'
Expand Down
92 changes: 49 additions & 43 deletions packages/core/src/controllers/SwapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,17 @@ export interface SwapControllerState {
initializing: boolean
initialized: boolean
loadingPrices: boolean
loading?: boolean
loadingQuote?: boolean
loadingApprovalTransaction?: boolean
loadingBuildTransaction?: boolean
loadingTransaction?: boolean

// Error states
fetchError: boolean

// Approval & Swap transaction states
approvalTransaction: TransactionParams | undefined
swapTransaction: TransactionParams | undefined
transactionLoading?: boolean
transactionError?: string

// Input values
Expand Down Expand Up @@ -106,8 +108,11 @@ const initialState: SwapControllerState = {
// Loading states
initializing: false,
initialized: false,
loading: false,
loadingPrices: false,
loadingQuote: false,
loadingApprovalTransaction: false,
loadingBuildTransaction: false,
loadingTransaction: false,

// Error states
fetchError: false,
Expand All @@ -116,7 +121,6 @@ const initialState: SwapControllerState = {
approvalTransaction: undefined,
swapTransaction: undefined,
transactionError: undefined,
transactionLoading: false,

// Input values
sourceToken: undefined,
Expand Down Expand Up @@ -197,10 +201,6 @@ export const SwapController = {
}
},

setLoading(loading: boolean) {
state.loading = loading
},

setSourceToken(sourceToken: SwapTokenWithBalance | undefined) {
if (!sourceToken) {
state.sourceToken = sourceToken
Expand Down Expand Up @@ -302,6 +302,10 @@ export const SwapController = {
this.setToToken(undefined)
},

getApprovalLoadingState() {
return state.loadingApprovalTransaction
},

clearError() {
state.transactionError = undefined
},
Expand Down Expand Up @@ -345,24 +349,16 @@ export const SwapController = {
const tokens = await SwapApiUtil.getTokenList()

state.tokens = tokens
state.popularTokens = tokens
.sort((aTokenInfo, bTokenInfo) => {
if (aTokenInfo.symbol < bTokenInfo.symbol) {
return -1
}
if (aTokenInfo.symbol > bTokenInfo.symbol) {
return 1
}

return 0
})
.filter(token => {
if (ConstantsUtil.SWAP_POPULAR_TOKENS.includes(token.symbol)) {
return true
}
state.popularTokens = tokens.sort((aTokenInfo, bTokenInfo) => {
if (aTokenInfo.symbol < bTokenInfo.symbol) {
return -1
}
if (aTokenInfo.symbol > bTokenInfo.symbol) {
return 1
}

return false
}, {})
return 0
})
state.suggestedTokens = tokens.filter(token => {
if (ConstantsUtil.SWAP_SUGGESTED_TOKENS.includes(token.symbol)) {
return true
Expand Down Expand Up @@ -467,7 +463,7 @@ export const SwapController = {
return
}

state.loading = true
state.loadingQuote = true

const amountDecimal = NumberUtil.bigNumber(state.sourceTokenAmount).multipliedBy(
10 ** sourceToken.decimals
Expand All @@ -482,6 +478,8 @@ export const SwapController = {
amount: amountDecimal.toString()
})

state.loadingQuote = false

const quoteToAmount = quoteResponse?.quotes?.[0]?.toAmount

if (!quoteToAmount) {
Expand All @@ -505,8 +503,6 @@ export const SwapController = {
state.inputError = undefined
this.setTransactionDetails()
}

state.loading = false
},

// -- Create Transactions -------------------------------------- //
Expand All @@ -515,12 +511,12 @@ export const SwapController = {
const sourceToken = state.sourceToken
const toToken = state.toToken

if (!fromCaipAddress || !availableToSwap || !sourceToken || !toToken || state.loading) {
if (!fromCaipAddress || !availableToSwap || !sourceToken || !toToken || state.loadingQuote) {
return undefined
}

try {
state.loading = true
state.loadingBuildTransaction = true
const hasAllowance = await SwapApiUtil.fetchSwapAllowance({
userAddress: fromCaipAddress,
tokenAddress: sourceToken.address,
Expand All @@ -536,13 +532,14 @@ export const SwapController = {
transaction = await this.createAllowanceTransaction()
}

state.loading = false
state.loadingBuildTransaction = false
state.fetchError = false

return transaction
} catch (error) {
RouterController.goBack()
SnackController.showError('Failed to check allowance')
state.loadingBuildTransaction = false
state.approvalTransaction = undefined
state.swapTransaction = undefined
state.fetchError = true
Expand Down Expand Up @@ -656,11 +653,14 @@ export const SwapController = {
// -- Send Transactions --------------------------------- //
async sendTransactionForApproval(data: TransactionParams) {
const { fromAddress } = this.getParams()
state.transactionLoading = true

state.loadingApprovalTransaction = true
RouterController.pushTransactionStack({
view: null,
goBack: true
goBack: true,
onSuccess() {
SnackController.showLoading('Approving transaction...')
}
})

try {
Expand All @@ -672,13 +672,14 @@ export const SwapController = {
gasPrice: BigInt(data.gasPrice)
})

await this.swapTokens()
await this.getTransaction()
state.approvalTransaction = undefined
state.transactionLoading = false
this.swapTokens()
state.loadingApprovalTransaction = false
} catch (err) {
const error = err as TransactionError
state.transactionError = error?.shortMessage as unknown as string
state.transactionLoading = false
state.loadingTransaction = false
}
},

Expand All @@ -689,20 +690,25 @@ export const SwapController = {

const { fromAddress, toTokenAmount } = this.getParams()

state.transactionLoading = true
state.loadingTransaction = true

const snackbarPendingMessage = `Swapping ${state.sourceToken
?.symbol} to ${NumberUtil.formatNumberToLocalString(toTokenAmount, 3)} ${state.toToken
?.symbol}`
const snackbarSuccessMessage = `Swapped ${state.sourceToken
?.symbol} to ${NumberUtil.formatNumberToLocalString(toTokenAmount, 3)} ${state.toToken
?.symbol}`

RouterController.pushTransactionStack({
view: 'Account',
goBack: false,
onSuccess() {
SnackController.showLoading(snackbarPendingMessage)
SwapController.resetState()
}
})

try {
const successMessage = `Swapped ${state.sourceToken
?.symbol} to ${NumberUtil.formatNumberToLocalString(toTokenAmount, 3)} ${state.toToken
?.symbol}!`
const forceUpdateAddresses = [state.sourceToken?.address, state.toToken?.address].join(',')
const transactionHash = await ConnectionController.sendTransaction({
address: fromAddress as `0x${string}`,
Expand All @@ -712,17 +718,17 @@ export const SwapController = {
gasPrice: BigInt(data.gasPrice),
value: data.value
})
state.transactionLoading = false

SnackController.showSuccess(successMessage)
state.loadingTransaction = false
SnackController.showSuccess(snackbarSuccessMessage)
SwapController.resetState()
SwapController.getMyTokensWithBalance(forceUpdateAddresses)

return transactionHash
} catch (err) {
const error = err as TransactionError
state.transactionError = error?.shortMessage
state.transactionLoading = false
state.loadingTransaction = false
SnackController.showError(error?.shortMessage || 'Transaction error')

return undefined
Expand Down
8 changes: 5 additions & 3 deletions packages/scaffold/src/partials/w3m-snackbar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import styles from './styles.js'

// -- Helpers ------------------------------------------- //
const presets = {
loading: undefined,
success: {
backgroundColor: 'success-100',
iconColor: 'success-100',
Expand Down Expand Up @@ -53,9 +54,10 @@ export class W3mSnackBar extends LitElement {
return html`
<wui-snackbar
message=${message}
backgroundColor=${preset.backgroundColor}
iconColor=${preset.iconColor}
icon=${preset.icon}
backgroundColor=${preset?.backgroundColor}
iconColor=${preset?.iconColor}
icon=${preset?.icon}
.loading=${variant === 'loading'}
></wui-snackbar>
`
}
Expand Down
Loading

0 comments on commit 2bb270e

Please sign in to comment.