diff --git a/app/src/lib/components/TransferFrom/index.svelte b/app/src/lib/components/TransferFrom/index.svelte index 3778a1e9b0..508c0f36c8 100644 --- a/app/src/lib/components/TransferFrom/index.svelte +++ b/app/src/lib/components/TransferFrom/index.svelte @@ -1,9 +1,9 @@
intents.updateField('source', event)} /> - {#if $validation.source} - {$validation.source} + {#if $validation.errors.source} + {$validation.errors.source} {/if} @@ -37,12 +37,12 @@ const { intents, context, validation } = createTransferStore() id="destination" name="destination" placeholder="Enter destination chain" - class="w-[300px] p-1 {$validation.destination ? 'border-red-500' : ''}" + class="w-[300px] p-1 {$validation.errors.destination ? 'border-red-500' : ''}" value={$intents.destination} on:input={event => intents.updateField('destination', event)} /> - {#if $validation.destination} - {$validation.destination} + {#if $validation.errors.destination} + {$validation.errors.destination} {/if} @@ -53,12 +53,12 @@ const { intents, context, validation } = createTransferStore() id="asset" name="asset" placeholder="Enter asset" - class="w-[300px] p-1 {$validation.asset ? 'border-red-500' : ''}" + class="w-[300px] p-1 {$validation.errors.asset ? 'border-red-500' : ''}" value={$intents.asset} on:input={event => intents.updateField('asset', event)} /> - {#if $validation.asset} - {$validation.asset} + {#if $validation.errors.asset} + {$validation.errors.asset} {/if} @@ -80,12 +80,12 @@ const { intents, context, validation } = createTransferStore() data-field="amount" autocapitalize="none" pattern="^[0-9]*[.,]?[0-9]*$" - class="w-[300px] p-1 {$validation.amount ? 'border-red-500' : ''}" + class="w-[300px] p-1 {$validation.errors.amount ? 'border-red-500' : ''}" value={$intents.amount} on:input={event => intents.updateField('amount', event)} /> - {#if $validation.amount} - {$validation.amount} + {#if $validation.errors.amount} + {$validation.errors.amount} {/if} @@ -101,13 +101,13 @@ const { intents, context, validation } = createTransferStore() spellcheck="false" autocomplete="off" data-field="receiver" - class="w-[300px] p-1 disabled:bg-black/30 {$validation.receiver ? 'border-red-500' : ''}" + class="w-[300px] p-1 disabled:bg-black/30 {$validation.errors.receiver ? 'border-red-500' : ''}" placeholder="Enter destination address" value={$intents.receiver} on:input={event => intents.updateField('receiver', event)} /> - {#if $validation.receiver} - {$validation.receiver} + {#if $validation.errors.receiver} + {$validation.errors.receiver} {/if} diff --git a/app/src/lib/components/TransferFrom/transfer/context.ts b/app/src/lib/components/TransferFrom/transfer/context.ts index fa113d98bc..102590150c 100644 --- a/app/src/lib/components/TransferFrom/transfer/context.ts +++ b/app/src/lib/components/TransferFrom/transfer/context.ts @@ -13,6 +13,7 @@ export type AddressBalance = { gasToken: boolean address: Address symbol: string + chain_id: string } export type NamedBalance = { @@ -21,22 +22,25 @@ export type NamedBalance = { name: string | null symbol: string gasToken: boolean + chain_id: string } -export type EmptyBalance = {} +export type EmptyBalance = { + chain_id: string +} export type Balance = AddressBalance | NamedBalance | EmptyBalance export interface ContextStore { chains: Array - userAddress: Readable - sourceChain: Readable - destinationChain: Readable - balances: Readable> - assetInfo: Readable + userAddress: UserAddresses + sourceChain: Chain | undefined + destinationChain: Chain | undefined + balances: Array + assetInfo: Balance | undefined } -export function createContextStore(intents: IntentStore): ContextStore { +export function createContextStore(intents: IntentStore): Readable { const queryClient = useQueryClient() function queryData>( @@ -65,29 +69,24 @@ export function createContextStore(intents: IntentStore): ContextStore { chains.find(chain => chain.chain_id === intentsValue.destination) ) - // Balance data const balances = derived( [ intents, userAddress, userBalancesQuery({ chains, connected: true, userAddr: get(userAddress) }) ], - ([intentsValue, _userAddressValue, rawBalances]) => { - const sourceChain = chains.find(chain => chain.chain_id === intentsValue.source) - if (!sourceChain) return [] - - const chainIndex = chains.findIndex(c => c.chain_id === sourceChain.chain_id) - const balanceResult = rawBalances[chainIndex] - - if (!balanceResult?.isSuccess || balanceResult.data instanceof Error) { - console.log("No balances fetched yet for selected chain") - return [] - } - - return balanceResult.data.map(balance => ({ - ...balance, - balance: BigInt(balance.balance) - })) + ([_intentsValue, _userAddressValue, rawBalances]) => { + return rawBalances.flatMap((balanceResult, index) => { + const chain = chains[index] + if (!balanceResult?.isSuccess || balanceResult.data instanceof Error) { + return [] + } + return balanceResult.data.map(balance => ({ + ...balance, + balance: BigInt(balance.balance), + chain_id: chain.chain_id // Add chain_id to each balance + })) + }) } ) @@ -95,12 +94,15 @@ export function createContextStore(intents: IntentStore): ContextStore { balancesValue.find(x => x?.address === intentsValue.asset) ) - return { - chains, - userAddress, - sourceChain, - destinationChain, - balances, - assetInfo - } + return derived( + [userAddress, sourceChain, destinationChain, balances, assetInfo], + ([$userAddress, $sourceChain, $destinationChain, $balances, $assetInfo]) => ({ + chains, + userAddress: $userAddress, + sourceChain: $sourceChain, + destinationChain: $destinationChain, + balances: $balances, + assetInfo: $assetInfo + }) + ) } diff --git a/app/src/lib/components/TransferFrom/transfer/index.ts b/app/src/lib/components/TransferFrom/transfer/index.ts index ce57a11eee..c0b2731e97 100644 --- a/app/src/lib/components/TransferFrom/transfer/index.ts +++ b/app/src/lib/components/TransferFrom/transfer/index.ts @@ -1,23 +1,14 @@ +import { type Readable } from "svelte/store" import { createIntentStore, type IntentStore } from "./intents.ts" -import type { Readable } from "svelte/store" -import type { Chain, UserAddresses } from "$lib/types.ts" -import { type Balance, createContextStore } from "$lib/components/TransferFrom/transfer/context.ts" +import { type ContextStore, createContextStore} from "$lib/components/TransferFrom/transfer/context.ts" import { - createValidationStore, - type ValidationStore + createValidationStore, type ValidationStoreAndMethods } from "$lib/components/TransferFrom/transfer/validation.ts" export interface TransferStore { intents: IntentStore - context: { - chains: Array - userAddress: Readable - sourceChain: Readable - destinationChain: Readable - balances: Readable> - assetInfo: Readable - } - validation: ValidationStore + context: Readable + validation: ValidationStoreAndMethods } export function createTransferStore(): TransferStore { @@ -30,4 +21,4 @@ export function createTransferStore(): TransferStore { context, validation } -} +} \ No newline at end of file diff --git a/app/src/lib/components/TransferFrom/transfer/validation.ts b/app/src/lib/components/TransferFrom/transfer/validation.ts index 55c805a5d1..e9451dfe06 100644 --- a/app/src/lib/components/TransferFrom/transfer/validation.ts +++ b/app/src/lib/components/TransferFrom/transfer/validation.ts @@ -1,14 +1,19 @@ -import type {Readable} from "svelte/store" -import {derived} from "svelte/store" -import type {IntentStore, FormFields, RawTransferIntents} from "./intents.ts" -import type {Chain} from "$lib/types" -import type {Balance, ContextStore} from "$lib/components/TransferFrom/transfer/context" -import {transferSchema} from "./schema.ts" -import {safeParse} from "valibot" +import type { Readable } from "svelte/store" +import { derived } from "svelte/store" +import type { IntentStore, FormFields, RawTransferIntents } from "./intents.ts" +import type { Chain } from "$lib/types" +import type { Balance, ContextStore } from "$lib/components/TransferFrom/transfer/context" +import { transferSchema } from "./schema.ts" +import { safeParse } from "valibot" export type FieldErrors = Partial> -export interface ValidationStore extends Readable { +export interface ValidationStore { + errors: FieldErrors + isValid: boolean +} + +export interface ValidationStoreAndMethods extends Readable { validate: () => Promise } @@ -22,21 +27,12 @@ interface ValidationContext { export function createValidationStore( intents: IntentStore, - context: ContextStore -): ValidationStore { - const errors = derived< - [ - Readable, - Readable>, - Readable, - Readable, - Readable - ], - FieldErrors - >( - [intents, context.balances, context.sourceChain, context.destinationChain, context.assetInfo], - ([$intents, $balances, $sourceChain, $destinationChain, $assetInfo]) => { - return validateAll({ + context: Readable +): ValidationStoreAndMethods { + const store = derived( + [intents, context], + ([$intents, $context]) => { + const errors = validateAll({ formFields: { source: $intents.source, destination: $intents.destination, @@ -44,12 +40,17 @@ export function createValidationStore( receiver: $intents.receiver, amount: $intents.amount }, - balances: $balances, - sourceChain: $sourceChain, - destinationChain: $destinationChain, - assetInfo: $assetInfo, - chains: context.chains + balances: $context.balances, + sourceChain: $context.sourceChain, + destinationChain: $context.destinationChain, + assetInfo: $context.assetInfo, + chains: $context.chains }) + + return { + errors, + isValid: Object.keys(errors).length === 0 + } } ) @@ -104,37 +105,12 @@ export function createValidationStore( }) } - function validateSchema(params: FormFields): FieldErrors { - if (Object.values(params).every(value => !value)) { - return {} - } - - const result = safeParse(transferSchema, params) - - if (!result.success) { - return result.issues.reduce((acc, issue) => { - const fieldName = issue.path?.[0]?.key as keyof FormFields - - if (fieldName && !params[fieldName]) { - return acc - } - - if (fieldName) { - acc[fieldName] = issue.message - } - return acc - }, {} as FieldErrors) - } - - return {} - } - function validateBusinessRules(formFields: FormFields, context: ValidationContext): FieldErrors { if (Object.values(formFields).every(value => !value)) { return {} } const errors: FieldErrors = {} - + if (formFields.source && formFields.destination && formFields.source === formFields.destination) { errors.destination = "Source and destination chains must be different" } @@ -143,18 +119,17 @@ export function createValidationStore( } return { - subscribe: errors.subscribe, + subscribe: store.subscribe, validate: () => { return new Promise(resolve => { - let currentErrors: FieldErrors = {} - const unsubscribe = errors.subscribe(value => { - currentErrors = value + let currentState: ValidationStore + const unsubscribe = store.subscribe(value => { + currentState = value }) - - const isValid = Object.keys(currentErrors).length === 0 + const isValid = currentState!.isValid unsubscribe() resolve(isValid) }) } } -} +} \ No newline at end of file