From 2ee0656f5d96a0173fd6fff6725756d75eaf8d18 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 13:25:48 +0100 Subject: [PATCH 01/17] remove all use of args --- .../src/__tests__/OrderDetail.test.svelte | 19 ++++++--- .../src/__tests__/OrderDetail.test.ts | 33 +++++++++++++-- .../detail/DepositOrWithdrawButtons.svelte | 11 +---- .../lib/components/detail/OrderDetail.svelte | 40 +++++-------------- .../lib/components/detail/VaultDetail.svelte | 10 +---- packages/ui-components/src/lib/index.ts | 9 ++++- .../src/lib/types/transaction.ts | 36 +++++++++++++++++ packages/webapp/src/lib/services/modal.ts | 19 ++++----- 8 files changed, 111 insertions(+), 66 deletions(-) diff --git a/packages/ui-components/src/__tests__/OrderDetail.test.svelte b/packages/ui-components/src/__tests__/OrderDetail.test.svelte index a1b46623b..7b5bd8c91 100644 --- a/packages/ui-components/src/__tests__/OrderDetail.test.svelte +++ b/packages/ui-components/src/__tests__/OrderDetail.test.svelte @@ -9,14 +9,16 @@ import type { Readable } from 'svelte/store'; import { Button } from 'flowbite-svelte'; import DepositOrWithdrawButtons from '../lib/components/detail/DepositOrWithdrawButtons.svelte'; - + import type { OrderRemoveModalArgs } from '../lib/types/transaction'; + import type { Config } from 'wagmi'; export let walletAddressMatchesOrBlank: Readable<(address: string) => boolean> | undefined = undefined; - export let handleOrderRemoveModal: - | ((order: OrderSubgraph, refetch: () => void) => void) - | undefined = undefined; + export let handleOrderRemoveModal: ((args: OrderRemoveModalArgs) => void) | undefined = undefined; export let id: string; export let subgraphUrl: string; + export let chainId: number; + export let orderbookAddress: string; + export let wagmiConfig: Config; $: orderDetailQuery = createQuery({ queryKey: [id, QKEY_ORDER + id], @@ -32,7 +34,14 @@ diff --git a/packages/ui-components/src/lib/components/detail/OrderDetail.svelte b/packages/ui-components/src/lib/components/detail/OrderDetail.svelte index e5023ce46..f791ed203 100644 --- a/packages/ui-components/src/lib/components/detail/OrderDetail.svelte +++ b/packages/ui-components/src/lib/components/detail/OrderDetail.svelte @@ -21,23 +21,24 @@ import { page } from '$app/stores'; import DepositOrWithdrawButtons from './DepositOrWithdrawButtons.svelte'; import type { Config } from 'wagmi'; + import type { Hex } from 'viem'; import type { - DepositOrWithdrawModalArgs, - OrderRemoveModalArgs, + DepositOrWithdrawModalProps, + OrderRemoveModalProps, QuoteDebugModalHandler, DebugTradeModalHandler - } from '../../types/transaction'; - + } from '../../types/modal'; export let handleDepositOrWithdrawModal: - | ((args: DepositOrWithdrawModalArgs) => void) + | ((props: DepositOrWithdrawModalProps) => void) | undefined = undefined; - export let handleOrderRemoveModal: ((args: OrderRemoveModalArgs) => void) | undefined = undefined; + export let handleOrderRemoveModal: ((props: OrderRemoveModalProps) => void) | undefined = + undefined; export let handleQuoteDebugModal: QuoteDebugModalHandler | undefined = undefined; export const handleDebugTradeModal: DebugTradeModalHandler | undefined = undefined; export let colorTheme; export let codeMirrorTheme; export let lightweightChartsTheme; - export let orderbookAddress: string | undefined = undefined; + export let orderbookAddress: Hex | undefined = undefined; export let id: string; export let rpcUrl: string; export let subgraphUrl: string; @@ -88,11 +89,13 @@ color="dark" on:click={() => handleOrderRemoveModal({ - order: data.order, - onRemove: $orderDetailQuery.refetch, - wagmiConfig: $wagmiConfig, - chainId, - orderbookAddress + open: true, + args: { + order: data.order, + onRemove: $orderDetailQuery.refetch, + chainId, + orderbookAddress + } })} disabled={!handleOrderRemoveModal} > diff --git a/packages/ui-components/src/lib/components/detail/TanstackOrderQuote.svelte b/packages/ui-components/src/lib/components/detail/TanstackOrderQuote.svelte index e2db7ef67..684916537 100644 --- a/packages/ui-components/src/lib/components/detail/TanstackOrderQuote.svelte +++ b/packages/ui-components/src/lib/components/detail/TanstackOrderQuote.svelte @@ -4,6 +4,7 @@ import { getOrderQuote, type BatchOrderQuotesResponse } from '@rainlanguage/orderbook/quote'; import { QKEY_ORDER_QUOTE } from '../../queries/keys'; import { formatUnits, hexToNumber, isHex } from 'viem'; + import type { Hex } from 'viem'; import { createQuery } from '@tanstack/svelte-query'; import type { OrderSubgraph } from '@rainlanguage/orderbook/js_api'; import { @@ -20,13 +21,13 @@ export let id: string; export let order: OrderSubgraph; export let rpcUrl: string; - export let orderbookAddress: string = ''; + export let orderbookAddress: Hex; export let handleQuoteDebugModal: | undefined | (( order: OrderSubgraph, rpcUrl: string, - orderbookAddress: string, + orderbookAddress: Hex, inputIndex: number, outputIndex: number, pairName: string, diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 73786c31f..42c317b45 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -75,12 +75,17 @@ export { } from './stores/transactionStore'; export type { DeploymentArgs, - DepositOrWithdrawModalArgs, - OrderRemoveModalArgs, + DepositOrWithdrawArgs, + OrderRemoveArgs, +} from './types/transaction'; +export type { + DepositOrWithdrawModalProps, + OrderRemoveModalProps, QuoteDebugModalHandler, DebugTradeModalHandler, - DisclaimerModalArgs -} from './types/transaction'; + DeployModalProps, + DisclaimerModalProps +} from './types/modal'; // Functions export { createResolvableQuery, createResolvableInfiniteQuery } from './__mocks__/queries'; diff --git a/packages/ui-components/src/lib/types/modal.ts b/packages/ui-components/src/lib/types/modal.ts new file mode 100644 index 000000000..10f2394cf --- /dev/null +++ b/packages/ui-components/src/lib/types/modal.ts @@ -0,0 +1,34 @@ +import type { OrderSubgraph } from '@rainlanguage/orderbook/js_api'; +import type { DepositOrWithdrawArgs, OrderRemoveArgs, DeploymentArgs } from './transaction'; + +export type DepositOrWithdrawModalProps = { + open: boolean; + args: DepositOrWithdrawArgs; +}; + +export type OrderRemoveModalProps = { + open: boolean; + args: OrderRemoveArgs +}; + +export type DeployModalProps = { + open: boolean; + args: DeploymentArgs; +}; + +export type DisclaimerModalProps = { + open: boolean; + onAccept: () => void; +}; +export type QuoteDebugModalHandler = ( + order: OrderSubgraph, + rpcUrl: string, + orderbook: string, + inputIOIndex: number, + outputIOIndex: number, + pair: string, + blockNumber?: number +) => void; + +export type DebugTradeModalHandler = (hash: string, rpcUrl: string) => void; + diff --git a/packages/ui-components/src/lib/types/transaction.ts b/packages/ui-components/src/lib/types/transaction.ts index ecf177bd9..b24f0f41f 100644 --- a/packages/ui-components/src/lib/types/transaction.ts +++ b/packages/ui-components/src/lib/types/transaction.ts @@ -1,7 +1,6 @@ import type { ExtendedApprovalCalldata } from '$lib/stores/transactionStore'; import type { DepositAndAddOrderCalldataResult } from '@rainlanguage/orderbook/js_api'; import type { Hex } from 'viem'; -import type { Config } from 'wagmi'; import type { OrderSubgraph, Vault } from '@rainlanguage/orderbook/js_api'; export type DeploymentArgs = { @@ -13,7 +12,7 @@ export type DeploymentArgs = { network: string; }; -export type DepositOrWithdrawModalArgs = { +export type DepositOrWithdrawArgs = { vault: Vault; onDepositOrWithdraw: () => void; action: 'deposit' | 'withdraw'; @@ -22,26 +21,9 @@ export type DepositOrWithdrawModalArgs = { subgraphUrl: string; }; -export type OrderRemoveModalArgs = { +export type OrderRemoveArgs = { order: OrderSubgraph; onRemove: () => void; - wagmiConfig: Config; chainId: number; - orderbookAddress: string; -}; - -export type QuoteDebugModalHandler = ( - order: OrderSubgraph, - rpcUrl: string, - orderbook: string, - inputIOIndex: number, - outputIOIndex: number, - pair: string, - blockNumber?: number -) => void; - -export type DebugTradeModalHandler = (hash: string, rpcUrl: string) => void; - -export type DisclaimerModalArgs = { - onAccept: () => void; + orderbookAddress: Hex; }; diff --git a/packages/webapp/src/lib/components/DeployModal.svelte b/packages/webapp/src/lib/components/DeployModal.svelte index 1c4e9870b..8a7034950 100644 --- a/packages/webapp/src/lib/components/DeployModal.svelte +++ b/packages/webapp/src/lib/components/DeployModal.svelte @@ -1,17 +1,10 @@ diff --git a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte index 4f00e36d4..0c8ee068d 100644 --- a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte +++ b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte @@ -1,11 +1,15 @@ - + diff --git a/packages/webapp/src/lib/components/TransactionModal.svelte b/packages/webapp/src/lib/components/TransactionModal.svelte index 182fe024a..e17e9eebc 100644 --- a/packages/webapp/src/lib/components/TransactionModal.svelte +++ b/packages/webapp/src/lib/components/TransactionModal.svelte @@ -14,11 +14,6 @@ function handleClose() { open = false; } - const dispatch = createEventDispatcher(); - - $: if ($transactionStore.status === TransactionStatus.SUCCESS) { - dispatch('success'); - } $: if (!open) { transactionStore.reset(); @@ -79,7 +74,7 @@ {:else}
diff --git a/packages/webapp/src/lib/services/modal.ts b/packages/webapp/src/lib/services/modal.ts index 1cd36aa60..0c8240546 100644 --- a/packages/webapp/src/lib/services/modal.ts +++ b/packages/webapp/src/lib/services/modal.ts @@ -4,23 +4,24 @@ import OrderRemoveModal from '$lib/components/OrderRemoveModal.svelte'; import { DisclaimerModal, type DeploymentArgs, - type DepositOrWithdrawModalArgs, - type OrderRemoveModalArgs, - type DisclaimerModalArgs + type DepositOrWithdrawModalProps, + type OrderRemoveModalProps, + type DisclaimerModalProps, + type DeployModalProps } from '@rainlanguage/ui-components'; -export const handleDeployModal = (args: Omit) => { - new DeployModal({ target: document.body, props: { ...args, open: true } }); +export const handleDeployModal = (props: DeployModalProps) => { + new DeployModal({ target: document.body, props }); }; -export const handleDepositOrWithdrawModal = (args: Omit) => { - new DepositOrWithdrawModal({ target: document.body, props: { ...args, open: true } }); +export const handleDepositOrWithdrawModal = (props: DepositOrWithdrawModalProps) => { + new DepositOrWithdrawModal({ target: document.body, props}); }; -export const handleOrderRemoveModal = (args: Omit) => { - new OrderRemoveModal({ target: document.body, props: { ...args, open: true } }); +export const handleOrderRemoveModal = (props: OrderRemoveModalProps) => { + new OrderRemoveModal({ target: document.body, props}); }; -export const handleDisclaimerModal = (args: Omit) => { - new DisclaimerModal({ target: document.body, props: { ...args, open: true } }); +export const handleDisclaimerModal = (props: DisclaimerModalProps) => { + new DisclaimerModal({ target: document.body, props }); }; From d88dd5419b86c1c3d2d2a661afa07e5f06e8ec23 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 17:41:09 +0100 Subject: [PATCH 06/17] add --- .../components/DepositOrWithdrawModal.svelte | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte index 0c8ee068d..7296322cf 100644 --- a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte +++ b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte @@ -37,6 +37,9 @@ const { action, vault, chainId, rpcUrl, subgraphUrl } = args; + type Action = 'deposit' | 'withdraw'; + const actionType = action as Action; + let currentStep = 1; let amount: bigint = 0n; let userBalance: bigint = 0n; @@ -83,11 +86,8 @@ transactionStore.handleDepositOrWithdrawTransaction({ config: $wagmiConfig, transactionCalldata: depositCalldata, - action, approvalCalldata, - chainId, - vault, - subgraphUrl + ...args }); } else if (action === 'withdraw') { const withdrawCalldata: WithdrawCalldataResult = await getVaultWithdrawCalldata( @@ -98,10 +98,7 @@ transactionStore.handleDepositOrWithdrawTransaction({ config: $wagmiConfig, transactionCalldata: withdrawCalldata, - action, - chainId, - vault, - subgraphUrl + ...args }); } } @@ -139,7 +136,7 @@ @@ -151,7 +148,7 @@ {#if switchChainError}

{switchChainError}

{/if} - {#if amountGreaterThanBalance[action]} + {#if amountGreaterThanBalance[actionType]}

Amount cannot exceed available balance.

{/if} From e6d72c79c816e08d9b04ec28e25f1ea95b2a3b36 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 18:14:53 +0100 Subject: [PATCH 07/17] formatted --- .../webapp/src/__tests__/DeployModal.test.ts | 45 ++- .../__tests__/DepositOrWithdrawModal.test.ts | 292 +++++++++--------- .../src/__tests__/OrderRemoveModal.test.ts | 23 +- .../components/DepositOrWithdrawModal.svelte | 10 +- .../lib/components/OrderRemoveModal.svelte | 9 +- .../lib/components/TransactionModal.svelte | 5 + 6 files changed, 206 insertions(+), 178 deletions(-) diff --git a/packages/webapp/src/__tests__/DeployModal.test.ts b/packages/webapp/src/__tests__/DeployModal.test.ts index f42f444e6..c4d7d60c4 100644 --- a/packages/webapp/src/__tests__/DeployModal.test.ts +++ b/packages/webapp/src/__tests__/DeployModal.test.ts @@ -1,11 +1,9 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render } from '@testing-library/svelte'; import DeployModal from '../lib/components/DeployModal.svelte'; -import { transactionStore } from '@rainlanguage/ui-components'; -import type { ComponentProps } from 'svelte'; import { get } from 'svelte/store'; +import type { DeployModalProps } from '@rainlanguage/ui-components'; -export type DeployModalProps = ComponentProps; const { mockTransactionStore } = await vi.hoisted(() => import('@rainlanguage/ui-components')); const { mockWagmiConfigStore } = await vi.hoisted(() => import('$lib/__mocks__/stores')); vi.mock('@rainlanguage/ui-components', async (importOriginal) => { @@ -24,36 +22,35 @@ vi.mock('$lib/stores/wagmi', () => { describe('DeployModal', () => { const mockProps = { open: true, - approvals: { - approvalCalldata: '0x', - token: '0x', - spender: '0x' - }, - deploymentCalldata: { - calldata: '0x', - value: 0n - }, - orderbookAddress: '0x123' as const, - chainId: 1 + args: { + approvals: { + approvalCalldata: '0x', + token: '0x', + spender: '0x' + }, + deploymentCalldata: { + calldata: '0x', + value: 0n + }, + orderbookAddress: '0x123' as const, + chainId: 1, + subgraphUrl: 'https://example.com', + network: 'mainnet' + } } as unknown as DeployModalProps; - beforeEach(() => { - vi.clearAllMocks(); - }); - it('renders and initiates transaction handling', () => { const config = get(mockWagmiConfigStore); const handleDeploymentTransactionSpy = vi.spyOn( - transactionStore, + mockTransactionStore, 'handleDeploymentTransaction' ); + render(DeployModal, { props: mockProps }); + expect(handleDeploymentTransactionSpy).toHaveBeenCalledWith({ - config: config, - approvals: mockProps.approvals, - deploymentCalldata: mockProps.deploymentCalldata, - orderbookAddress: mockProps.orderbookAddress, - chainId: mockProps.chainId + config, + ...mockProps.args }); }); }); diff --git a/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts b/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts index fa4ca41e0..4e6a4969b 100644 --- a/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts +++ b/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts @@ -6,151 +6,165 @@ import { signerAddress } from '$lib/stores/wagmi'; import { readContract, switchChain } from '@wagmi/core'; import type { ComponentProps } from 'svelte'; + export type ModalProps = ComponentProps; vi.mock('@rainlanguage/orderbook/js_api', () => ({ - getVaultDepositCalldata: vi.fn().mockResolvedValue({ to: '0x123', data: '0x456' }), - getVaultApprovalCalldata: vi.fn().mockResolvedValue({ to: '0x789', data: '0xabc' }), - getVaultWithdrawCalldata: vi.fn().mockResolvedValue({ to: '0xdef', data: '0xghi' }) + getVaultDepositCalldata: vi.fn().mockResolvedValue({ to: '0x123', data: '0x456' }), + getVaultApprovalCalldata: vi.fn().mockResolvedValue({ to: '0x789', data: '0xabc' }), + getVaultWithdrawCalldata: vi.fn().mockResolvedValue({ to: '0xdef', data: '0xghi' }) })); vi.mock('@wagmi/core', () => ({ - readContract: vi.fn(), - switchChain: vi.fn() + readContract: vi.fn(), + switchChain: vi.fn() })); describe('DepositOrWithdrawModal', () => { - const mockVault = { - token: { - address: '0x123', - symbol: 'TEST', - decimals: '18' - }, - vaultId: '1', - balance: BigInt(1) - }; - - const defaultProps = { - open: true, - action: 'deposit' as const, - vault: mockVault, - chainId: 1, - rpcUrl: 'https://example.com', - onDepositOrWithdraw: vi.fn() - } as unknown as ModalProps; - - beforeEach(() => { - vi.clearAllMocks(); - transactionStore.reset(); - signerAddress.set('0x123'); - }); - - it('renders deposit modal correctly', () => { - render(DepositOrWithdrawModal, defaultProps); - expect(screen.getByText('Enter Amount')).toBeInTheDocument(); - expect(screen.getByText('Deposit')).toBeInTheDocument(); - }); - - it('renders withdraw modal correctly', () => { - render(DepositOrWithdrawModal, { - ...defaultProps, - action: 'withdraw' - }); - expect(screen.getByText('Enter Amount')).toBeInTheDocument(); - expect(screen.getByText('Withdraw')).toBeInTheDocument(); - }); - - it('disables continue button when amount is 0', () => { - render(DepositOrWithdrawModal, defaultProps); - const continueButton = screen.getByText('Deposit'); - expect(continueButton).toBeDisabled(); - }); - - it('shows wallet connect button when not connected', () => { - signerAddress.set(''); - render(DepositOrWithdrawModal, defaultProps); - expect(screen.getByText('Connect Wallet')).toBeInTheDocument(); - }); - - it('handles deposit transaction correctly', async () => { - const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '1' } }); - - const depositButton = screen.getByText('Deposit'); - await fireEvent.click(depositButton); - - expect(handleTransactionSpy).toHaveBeenCalledWith( - expect.objectContaining({ - action: 'deposit', - chainId: 1, - vault: mockVault - }) - ); - }); - - it('Blocks deposit if not enough balance in wallet', async () => { - (readContract as Mock).mockReturnValue(BigInt(0)); - - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '1' } }); - - await waitFor(() => { - expect(screen.getByTestId('error')).toBeInTheDocument(); - }); - }); - - it('Blocks withdrawal if not enough balance in vault', async () => { - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '2' } }); - - await waitFor(() => { - expect(screen.getByTestId('error')).toBeInTheDocument(); - }); - }); - - it('handles withdraw transaction correctly', async () => { - const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); - render(DepositOrWithdrawModal, { - ...defaultProps, - action: 'withdraw' - }); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '1' } }); - - const withdrawButton = screen.getByText('Withdraw'); - await fireEvent.click(withdrawButton); - - expect(handleTransactionSpy).toHaveBeenCalledWith( - expect.objectContaining({ - action: 'withdraw', - chainId: 1, - vault: mockVault - }) - ); - }); - - it('closes modal and resets state', async () => { - render(DepositOrWithdrawModal, defaultProps); - - const cancelButton = screen.getByText('Cancel'); - await fireEvent.click(cancelButton); - - expect(screen.queryByText('Enter Amount')).not.toBeInTheDocument(); - }); - - it('shows an error if you fail to connect to the target chain', async () => { - (switchChain as Mock).mockRejectedValue(new Error('Failed to switch chain')); - render(DepositOrWithdrawModal, defaultProps); - await waitFor(() => { - expect(screen.getByTestId('chain-error')).toBeInTheDocument(); - }); - }); -}); + const mockVault = { + token: { + address: '0x123', + symbol: 'TEST', + decimals: '18' + }, + vaultId: '1', + balance: BigInt(1) + }; + + const defaultProps = { + open: true, + args: { + action: 'deposit' as const, + vault: mockVault, + chainId: 1, + rpcUrl: 'https://example.com', + onDepositOrWithdraw: vi.fn() + } + } as unknown as ModalProps; + + beforeEach(() => { + vi.clearAllMocks(); + transactionStore.reset(); + signerAddress.set('0x123'); + }); + + it('renders deposit modal correctly', () => { + render(DepositOrWithdrawModal, defaultProps); + expect(screen.getByText('Enter Amount')).toBeInTheDocument(); + expect(screen.getByText('Deposit')).toBeInTheDocument(); + }); + + it('renders withdraw modal correctly', () => { + render(DepositOrWithdrawModal, { + ...defaultProps, + args: { + ...defaultProps.args, + action: 'withdraw' + } + }); + expect(screen.getByText('Enter Amount')).toBeInTheDocument(); + expect(screen.getByText('Withdraw')).toBeInTheDocument(); + }); + + it('handles deposit transaction correctly', async () => { + const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '1' } }); + + const depositButton = screen.getByText('Deposit'); + await fireEvent.click(depositButton); + + expect(handleTransactionSpy).toHaveBeenCalledWith({ + action: 'deposit', + chainId: 1, + vault: mockVault, + config: undefined, + subgraphUrl: undefined, + approvalCalldata: { to: '0x789', data: '0xabc' }, + transactionCalldata: { to: '0x123', data: '0x456' } + }); + }); + + it('handles withdraw transaction correctly', async () => { + const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); + render(DepositOrWithdrawModal, { + ...defaultProps, + args: { + ...defaultProps.args, + action: 'withdraw' + } + }); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '1' } }); + + const withdrawButton = screen.getByText('Withdraw'); + await fireEvent.click(withdrawButton); + + expect(handleTransactionSpy).toHaveBeenCalledWith({ + config: undefined, + transactionCalldata: { to: '0xdef', data: '0xghi' }, + action: 'withdraw', + chainId: 1, + vault: mockVault, + subgraphUrl: undefined + }); + }); + + it('shows error when amount exceeds balance for deposit', async () => { + (readContract as Mock).mockResolvedValue(BigInt(0)); + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '2' } }); + + expect(screen.getByTestId('error')).toHaveTextContent('Amount cannot exceed available balance.'); + }); + + it('shows error when amount exceeds balance for withdraw', async () => { + render(DepositOrWithdrawModal, { + ...defaultProps, + args: { + ...defaultProps.args, + action: 'withdraw' + } + }); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '2' } }); + + expect(screen.getByTestId('error')).toHaveTextContent('Amount cannot exceed available balance.'); + }); + + it('shows chain switch error when switching fails', async () => { + (switchChain as Mock).mockRejectedValue(new Error('Failed to switch chain')); + render(DepositOrWithdrawModal, defaultProps); + + await waitFor(() => { + expect(screen.getByTestId('chain-error')).toHaveTextContent('Switch to Ethereum to check your balance.'); + }); + }); + + it('disables continue button when amount is 0', () => { + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + fireEvent.input(input, { target: { value: '0' } }); + + const continueButton = screen.getByText('Deposit'); + expect(continueButton).toBeDisabled(); + }); + + it('disables continue button when amount exceeds balance', async () => { + (readContract as Mock).mockResolvedValue(BigInt(0)); + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '1' } }); + + const continueButton = screen.getByText('Deposit'); + expect(continueButton).toBeDisabled(); + }); +}); \ No newline at end of file diff --git a/packages/webapp/src/__tests__/OrderRemoveModal.test.ts b/packages/webapp/src/__tests__/OrderRemoveModal.test.ts index 327c45d87..ca9c1ec05 100644 --- a/packages/webapp/src/__tests__/OrderRemoveModal.test.ts +++ b/packages/webapp/src/__tests__/OrderRemoveModal.test.ts @@ -2,9 +2,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import OrderRemoveModal from '$lib/components/OrderRemoveModal.svelte'; import { transactionStore } from '@rainlanguage/ui-components'; -import type { ComponentProps } from 'svelte'; - -export type ModalProps = ComponentProps; +import type { OrderRemoveModalProps } from '@rainlanguage/ui-components'; vi.mock('@rainlanguage/orderbook/js_api', () => ({ getRemoveOrderCalldata: vi.fn().mockResolvedValue('0x123') @@ -21,12 +19,14 @@ describe('OrderRemoveModal', () => { const defaultProps = { open: true, - order: mockOrder, - onRemove: vi.fn(), - wagmiConfig: {}, - chainId: 1, - orderbookAddress: '0x789' - } as unknown as ModalProps; + args: { + order: mockOrder, + chainId: 1, + orderbookAddress: '0x789', + config: {}, + onRemove: vi.fn() + } + } as unknown as OrderRemoveModalProps; beforeEach(() => { vi.clearAllMocks(); @@ -43,7 +43,8 @@ describe('OrderRemoveModal', () => { expect.objectContaining({ chainId: 1, orderbookAddress: '0x789', - config: {} + config: {}, + removeOrderCalldata: '0x123' }) ); }); @@ -64,6 +65,6 @@ describe('OrderRemoveModal', () => { transactionStore.transactionSuccess('0x123'); await vi.runAllTimersAsync(); - expect(defaultProps.onRemove).toHaveBeenCalled(); + expect(defaultProps.args.onRemove).toHaveBeenCalled(); }); }); diff --git a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte index 7296322cf..b9a15ff84 100644 --- a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte +++ b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte @@ -87,7 +87,10 @@ config: $wagmiConfig, transactionCalldata: depositCalldata, approvalCalldata, - ...args + action, + chainId, + vault, + subgraphUrl }); } else if (action === 'withdraw') { const withdrawCalldata: WithdrawCalldataResult = await getVaultWithdrawCalldata( @@ -98,7 +101,10 @@ transactionStore.handleDepositOrWithdrawTransaction({ config: $wagmiConfig, transactionCalldata: withdrawCalldata, - ...args + action, + chainId, + vault, + subgraphUrl }); } } diff --git a/packages/webapp/src/lib/components/OrderRemoveModal.svelte b/packages/webapp/src/lib/components/OrderRemoveModal.svelte index 4ecbef111..c457cf921 100644 --- a/packages/webapp/src/lib/components/OrderRemoveModal.svelte +++ b/packages/webapp/src/lib/components/OrderRemoveModal.svelte @@ -1,7 +1,6 @@ - + diff --git a/packages/webapp/src/lib/components/TransactionModal.svelte b/packages/webapp/src/lib/components/TransactionModal.svelte index e17e9eebc..04bd8731e 100644 --- a/packages/webapp/src/lib/components/TransactionModal.svelte +++ b/packages/webapp/src/lib/components/TransactionModal.svelte @@ -14,6 +14,11 @@ function handleClose() { open = false; } + const dispatch = createEventDispatcher(); + + $: if ($transactionStore.status === TransactionStatus.SUCCESS) { + dispatch('success'); + } $: if (!open) { transactionStore.reset(); From 160cadd8717e414ad09c078fe45807941080b3db Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 18:14:59 +0100 Subject: [PATCH 08/17] formatated --- packages/ui-components/src/lib/index.ts | 6 +- packages/ui-components/src/lib/types/modal.ts | 3 +- .../__tests__/DepositOrWithdrawModal.test.ts | 310 +++++++++--------- .../lib/components/TransactionModal.svelte | 2 +- packages/webapp/src/lib/services/modal.ts | 4 +- 5 files changed, 163 insertions(+), 162 deletions(-) diff --git a/packages/ui-components/src/lib/index.ts b/packages/ui-components/src/lib/index.ts index 42c317b45..f17c1d097 100644 --- a/packages/ui-components/src/lib/index.ts +++ b/packages/ui-components/src/lib/index.ts @@ -73,11 +73,7 @@ export { type TransactionState, type ExtendedApprovalCalldata } from './stores/transactionStore'; -export type { - DeploymentArgs, - DepositOrWithdrawArgs, - OrderRemoveArgs, -} from './types/transaction'; +export type { DeploymentArgs, DepositOrWithdrawArgs, OrderRemoveArgs } from './types/transaction'; export type { DepositOrWithdrawModalProps, OrderRemoveModalProps, diff --git a/packages/ui-components/src/lib/types/modal.ts b/packages/ui-components/src/lib/types/modal.ts index 10f2394cf..dbee2f872 100644 --- a/packages/ui-components/src/lib/types/modal.ts +++ b/packages/ui-components/src/lib/types/modal.ts @@ -8,7 +8,7 @@ export type DepositOrWithdrawModalProps = { export type OrderRemoveModalProps = { open: boolean; - args: OrderRemoveArgs + args: OrderRemoveArgs; }; export type DeployModalProps = { @@ -31,4 +31,3 @@ export type QuoteDebugModalHandler = ( ) => void; export type DebugTradeModalHandler = (hash: string, rpcUrl: string) => void; - diff --git a/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts b/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts index 4e6a4969b..171f82535 100644 --- a/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts +++ b/packages/webapp/src/__tests__/DepositOrWithdrawModal.test.ts @@ -10,161 +10,167 @@ import type { ComponentProps } from 'svelte'; export type ModalProps = ComponentProps; vi.mock('@rainlanguage/orderbook/js_api', () => ({ - getVaultDepositCalldata: vi.fn().mockResolvedValue({ to: '0x123', data: '0x456' }), - getVaultApprovalCalldata: vi.fn().mockResolvedValue({ to: '0x789', data: '0xabc' }), - getVaultWithdrawCalldata: vi.fn().mockResolvedValue({ to: '0xdef', data: '0xghi' }) + getVaultDepositCalldata: vi.fn().mockResolvedValue({ to: '0x123', data: '0x456' }), + getVaultApprovalCalldata: vi.fn().mockResolvedValue({ to: '0x789', data: '0xabc' }), + getVaultWithdrawCalldata: vi.fn().mockResolvedValue({ to: '0xdef', data: '0xghi' }) })); vi.mock('@wagmi/core', () => ({ - readContract: vi.fn(), - switchChain: vi.fn() + readContract: vi.fn(), + switchChain: vi.fn() })); describe('DepositOrWithdrawModal', () => { - const mockVault = { - token: { - address: '0x123', - symbol: 'TEST', - decimals: '18' - }, - vaultId: '1', - balance: BigInt(1) - }; - - const defaultProps = { - open: true, - args: { - action: 'deposit' as const, - vault: mockVault, - chainId: 1, - rpcUrl: 'https://example.com', - onDepositOrWithdraw: vi.fn() - } - } as unknown as ModalProps; - - beforeEach(() => { - vi.clearAllMocks(); - transactionStore.reset(); - signerAddress.set('0x123'); - }); - - it('renders deposit modal correctly', () => { - render(DepositOrWithdrawModal, defaultProps); - expect(screen.getByText('Enter Amount')).toBeInTheDocument(); - expect(screen.getByText('Deposit')).toBeInTheDocument(); - }); - - it('renders withdraw modal correctly', () => { - render(DepositOrWithdrawModal, { - ...defaultProps, - args: { - ...defaultProps.args, - action: 'withdraw' - } - }); - expect(screen.getByText('Enter Amount')).toBeInTheDocument(); - expect(screen.getByText('Withdraw')).toBeInTheDocument(); - }); - - it('handles deposit transaction correctly', async () => { - const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '1' } }); - - const depositButton = screen.getByText('Deposit'); - await fireEvent.click(depositButton); - - expect(handleTransactionSpy).toHaveBeenCalledWith({ - action: 'deposit', - chainId: 1, - vault: mockVault, - config: undefined, - subgraphUrl: undefined, - approvalCalldata: { to: '0x789', data: '0xabc' }, - transactionCalldata: { to: '0x123', data: '0x456' } - }); - }); - - it('handles withdraw transaction correctly', async () => { - const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); - render(DepositOrWithdrawModal, { - ...defaultProps, - args: { - ...defaultProps.args, - action: 'withdraw' - } - }); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '1' } }); - - const withdrawButton = screen.getByText('Withdraw'); - await fireEvent.click(withdrawButton); - - expect(handleTransactionSpy).toHaveBeenCalledWith({ - config: undefined, - transactionCalldata: { to: '0xdef', data: '0xghi' }, - action: 'withdraw', - chainId: 1, - vault: mockVault, - subgraphUrl: undefined - }); - }); - - it('shows error when amount exceeds balance for deposit', async () => { - (readContract as Mock).mockResolvedValue(BigInt(0)); - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '2' } }); - - expect(screen.getByTestId('error')).toHaveTextContent('Amount cannot exceed available balance.'); - }); - - it('shows error when amount exceeds balance for withdraw', async () => { - render(DepositOrWithdrawModal, { - ...defaultProps, - args: { - ...defaultProps.args, - action: 'withdraw' - } - }); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '2' } }); - - expect(screen.getByTestId('error')).toHaveTextContent('Amount cannot exceed available balance.'); - }); - - it('shows chain switch error when switching fails', async () => { - (switchChain as Mock).mockRejectedValue(new Error('Failed to switch chain')); - render(DepositOrWithdrawModal, defaultProps); - - await waitFor(() => { - expect(screen.getByTestId('chain-error')).toHaveTextContent('Switch to Ethereum to check your balance.'); - }); - }); - - it('disables continue button when amount is 0', () => { - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - fireEvent.input(input, { target: { value: '0' } }); - - const continueButton = screen.getByText('Deposit'); - expect(continueButton).toBeDisabled(); - }); - - it('disables continue button when amount exceeds balance', async () => { - (readContract as Mock).mockResolvedValue(BigInt(0)); - render(DepositOrWithdrawModal, defaultProps); - - const input = screen.getByRole('textbox'); - await fireEvent.input(input, { target: { value: '1' } }); - - const continueButton = screen.getByText('Deposit'); - expect(continueButton).toBeDisabled(); - }); -}); \ No newline at end of file + const mockVault = { + token: { + address: '0x123', + symbol: 'TEST', + decimals: '18' + }, + vaultId: '1', + balance: BigInt(1) + }; + + const defaultProps = { + open: true, + args: { + action: 'deposit' as const, + vault: mockVault, + chainId: 1, + rpcUrl: 'https://example.com', + onDepositOrWithdraw: vi.fn() + } + } as unknown as ModalProps; + + beforeEach(() => { + vi.clearAllMocks(); + transactionStore.reset(); + signerAddress.set('0x123'); + }); + + it('renders deposit modal correctly', () => { + render(DepositOrWithdrawModal, defaultProps); + expect(screen.getByText('Enter Amount')).toBeInTheDocument(); + expect(screen.getByText('Deposit')).toBeInTheDocument(); + }); + + it('renders withdraw modal correctly', () => { + render(DepositOrWithdrawModal, { + ...defaultProps, + args: { + ...defaultProps.args, + action: 'withdraw' + } + }); + expect(screen.getByText('Enter Amount')).toBeInTheDocument(); + expect(screen.getByText('Withdraw')).toBeInTheDocument(); + }); + + it('handles deposit transaction correctly', async () => { + const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '1' } }); + + const depositButton = screen.getByText('Deposit'); + await fireEvent.click(depositButton); + + expect(handleTransactionSpy).toHaveBeenCalledWith({ + action: 'deposit', + chainId: 1, + vault: mockVault, + config: undefined, + subgraphUrl: undefined, + approvalCalldata: { to: '0x789', data: '0xabc' }, + transactionCalldata: { to: '0x123', data: '0x456' } + }); + }); + + it('handles withdraw transaction correctly', async () => { + const handleTransactionSpy = vi.spyOn(transactionStore, 'handleDepositOrWithdrawTransaction'); + render(DepositOrWithdrawModal, { + ...defaultProps, + args: { + ...defaultProps.args, + action: 'withdraw' + } + }); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '1' } }); + + const withdrawButton = screen.getByText('Withdraw'); + await fireEvent.click(withdrawButton); + + expect(handleTransactionSpy).toHaveBeenCalledWith({ + config: undefined, + transactionCalldata: { to: '0xdef', data: '0xghi' }, + action: 'withdraw', + chainId: 1, + vault: mockVault, + subgraphUrl: undefined + }); + }); + + it('shows error when amount exceeds balance for deposit', async () => { + (readContract as Mock).mockResolvedValue(BigInt(0)); + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '2' } }); + + expect(screen.getByTestId('error')).toHaveTextContent( + 'Amount cannot exceed available balance.' + ); + }); + + it('shows error when amount exceeds balance for withdraw', async () => { + render(DepositOrWithdrawModal, { + ...defaultProps, + args: { + ...defaultProps.args, + action: 'withdraw' + } + }); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '2' } }); + + expect(screen.getByTestId('error')).toHaveTextContent( + 'Amount cannot exceed available balance.' + ); + }); + + it('shows chain switch error when switching fails', async () => { + (switchChain as Mock).mockRejectedValue(new Error('Failed to switch chain')); + render(DepositOrWithdrawModal, defaultProps); + + await waitFor(() => { + expect(screen.getByTestId('chain-error')).toHaveTextContent( + 'Switch to Ethereum to check your balance.' + ); + }); + }); + + it('disables continue button when amount is 0', () => { + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + fireEvent.input(input, { target: { value: '0' } }); + + const continueButton = screen.getByText('Deposit'); + expect(continueButton).toBeDisabled(); + }); + + it('disables continue button when amount exceeds balance', async () => { + (readContract as Mock).mockResolvedValue(BigInt(0)); + render(DepositOrWithdrawModal, defaultProps); + + const input = screen.getByRole('textbox'); + await fireEvent.input(input, { target: { value: '1' } }); + + const continueButton = screen.getByText('Deposit'); + expect(continueButton).toBeDisabled(); + }); +}); diff --git a/packages/webapp/src/lib/components/TransactionModal.svelte b/packages/webapp/src/lib/components/TransactionModal.svelte index 04bd8731e..182fe024a 100644 --- a/packages/webapp/src/lib/components/TransactionModal.svelte +++ b/packages/webapp/src/lib/components/TransactionModal.svelte @@ -79,7 +79,7 @@ {:else}
diff --git a/packages/webapp/src/lib/services/modal.ts b/packages/webapp/src/lib/services/modal.ts index 0c8240546..990e062bc 100644 --- a/packages/webapp/src/lib/services/modal.ts +++ b/packages/webapp/src/lib/services/modal.ts @@ -15,11 +15,11 @@ export const handleDeployModal = (props: DeployModalProps) => { }; export const handleDepositOrWithdrawModal = (props: DepositOrWithdrawModalProps) => { - new DepositOrWithdrawModal({ target: document.body, props}); + new DepositOrWithdrawModal({ target: document.body, props }); }; export const handleOrderRemoveModal = (props: OrderRemoveModalProps) => { - new OrderRemoveModal({ target: document.body, props}); + new OrderRemoveModal({ target: document.body, props }); }; export const handleDisclaimerModal = (props: DisclaimerModalProps) => { From b9190bc24a46f103caf2d66fd79750f961ce74dc Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 18:28:11 +0100 Subject: [PATCH 09/17] fix checks --- .../src/__tests__/DeploymentSteps.test.ts | 196 ++++++------------ .../src/__tests__/OrderDetail.test.ts | 4 - .../deployment/DeploymentSteps.svelte | 20 +- .../lib/components/detail/OrderDetail.svelte | 2 +- .../lib/components/detail/VaultDetail.svelte | 4 +- .../routes/orders/[network]-[id]/+page.svelte | 4 +- 6 files changed, 76 insertions(+), 154 deletions(-) diff --git a/packages/ui-components/src/__tests__/DeploymentSteps.test.ts b/packages/ui-components/src/__tests__/DeploymentSteps.test.ts index 993434cb9..525b8ef7d 100644 --- a/packages/ui-components/src/__tests__/DeploymentSteps.test.ts +++ b/packages/ui-components/src/__tests__/DeploymentSteps.test.ts @@ -6,8 +6,7 @@ import type { ComponentProps } from 'svelte'; import { writable } from 'svelte/store'; import type { AppKit } from '@reown/appkit'; import type { ConfigSource } from '../lib/typeshare/config'; -import type { DeploymentArgs } from '$lib/types/transaction'; -import type { DisclaimerModal } from '$lib'; +import type { DeployModalProps, DisclaimerModalProps } from '../lib/types/modal'; const { mockWagmiConfigStore, mockConnectedStore } = await vi.hoisted( () => import('../lib/__mocks__/stores') @@ -572,22 +571,31 @@ min-trade-amount: mul(min-amount 0.9), "Min trade amount."), :call<'set-cost-basis-io-ratio>();`; -describe('DeploymentSteps', () => { - const mockDeployment = { +const mockDeployment = { + key: 'flare-sflr-wflr', + name: 'SFLR<>WFLR on Flare', + description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', + deposits: [], + fields: [], + select_tokens: [], + deployment: { key: 'flare-sflr-wflr', - name: 'SFLR<>WFLR on Flare', - description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', - deposits: [], - fields: [], - select_tokens: [], - deployment: { + scenario: { + key: 'flare', + bindings: {} + } as Scenario, + order: { key: 'flare-sflr-wflr', - scenario: { + network: { + key: 'flare', + 'chain-id': 14, + 'network-id': 14, + rpc: 'https://rpc.ankr.com/flare', + label: 'Flare', + currency: 'FLR' + }, + deployer: { key: 'flare', - bindings: {} - } as Scenario, - order: { - key: 'flare-sflr-wflr', network: { key: 'flare', 'chain-id': 14, @@ -596,28 +604,36 @@ describe('DeploymentSteps', () => { label: 'Flare', currency: 'FLR' }, - deployer: { - key: 'flare', - network: { - key: 'flare', - 'chain-id': 14, - 'network-id': 14, - rpc: 'https://rpc.ankr.com/flare', - label: 'Flare', - currency: 'FLR' - }, - address: '0x0' - }, - orderbook: { - id: 'flare', - address: '0x0' - }, - inputs: [], - outputs: [] - } + address: '0x0' + }, + orderbook: { + id: 'flare', + address: '0x0' + }, + inputs: [], + outputs: [] } - }; + } +}; + +const defaultProps: DeploymentStepsProps = { + dotrain, + strategyDetail: { + name: 'SFLR<>WFLR on Flare', + description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', + short_description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.' + }, + deployment: mockDeployment, + wagmiConfig: mockWagmiConfigStore, + wagmiConnected: mockConnectedStore, + appKitModal: writable({} as AppKit), + handleDeployModal: vi.fn() as unknown as (args: DeployModalProps) => void, + handleDisclaimerModal: vi.fn() as unknown as (args: DisclaimerModalProps) => void, + settings: writable({} as ConfigSource), + handleUpdateGuiState: vi.fn() +}; +describe('DeploymentSteps', () => { beforeEach(() => { vi.clearAllMocks(); }); @@ -629,26 +645,7 @@ describe('DeploymentSteps', () => { getNetworkKey: vi.fn() }); - render(DeploymentSteps, { - props: { - dotrain, - strategyDetail: { - name: 'SFLR<>WFLR on Flare', - description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', - short_description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.' - }, - deployment: mockDeployment, - wagmiConfig: mockWagmiConfigStore, - wagmiConnected: mockConnectedStore, - appKitModal: writable({} as AppKit), - handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void, - handleDisclaimerModal: vi.fn() as unknown as ( - args: ComponentProps - ) => void, - settings: writable({} as ConfigSource), - handleUpdateGuiState: vi.fn() - } - }); + render(DeploymentSteps, { props: defaultProps }); await waitFor(() => { expect(screen.getByText('SFLR<>WFLR on Flare')).toBeInTheDocument(); @@ -663,32 +660,11 @@ describe('DeploymentSteps', () => { getNetworkKey: vi.fn() }); - render(DeploymentSteps, { - props: { - dotrain, - strategyDetail: { - name: 'SFLR<>WFLR on Flare', - description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', - short_description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.' - }, - deployment: mockDeployment, - wagmiConfig: mockWagmiConfigStore, - wagmiConnected: mockConnectedStore, - appKitModal: writable({} as AppKit), - handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void, - handleDisclaimerModal: vi.fn() as unknown as ( - args: ComponentProps - ) => void, - settings: writable({} as ConfigSource), - handleUpdateGuiState: vi.fn() - } - }); + render(DeploymentSteps, { props: defaultProps }); await waitFor(() => { expect(screen.getByText('Select Tokens')).toBeInTheDocument(); - expect( - screen.getByText('Select the tokens that you want to use in your order.') - ).toBeInTheDocument(); + expect(screen.getByText('Select the tokens that you want to use in your order.')).toBeInTheDocument(); }); }); @@ -697,26 +673,7 @@ describe('DeploymentSteps', () => { new Error('Failed to initialize GUI') ); - render(DeploymentSteps, { - props: { - dotrain, - strategyDetail: { - name: 'SFLR<>WFLR on Flare', - description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', - short_description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.' - }, - deployment: mockDeployment, - wagmiConfig: mockWagmiConfigStore, - wagmiConnected: mockConnectedStore, - appKitModal: writable({} as AppKit), - handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void, - handleDisclaimerModal: vi.fn() as unknown as ( - args: ComponentProps - ) => void, - settings: writable({} as ConfigSource), - handleUpdateGuiState: vi.fn() - } - }); + render(DeploymentSteps, { props: defaultProps }); await waitFor(() => { expect(screen.getByText('Error loading GUI')).toBeInTheDocument(); @@ -742,31 +699,13 @@ describe('DeploymentSteps', () => { getNetworkKey: vi.fn() }); - render(DeploymentSteps, { - props: { - dotrain, - strategyDetail: { - name: 'SFLR<>WFLR on Flare', - description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', - short_description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.' - }, - deployment: mockDeployment, - wagmiConfig: mockWagmiConfigStore, - wagmiConnected: mockConnectedStore, - appKitModal: writable({} as AppKit), - handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void, - handleDisclaimerModal: vi.fn() as unknown as ( - args: ComponentProps - ) => void, - settings: writable({} as ConfigSource), - handleUpdateGuiState: vi.fn() - } - }); + render(DeploymentSteps, { props: defaultProps }); await waitFor(() => { expect(screen.getByText('Deploy Strategy')).toBeInTheDocument(); }); }); + it('shows connect wallet button when not connected', async () => { mockConnectedStore.mockSetSubscribeValue(false); (DotrainOrderGui.chooseDeployment as Mock).mockResolvedValue({ @@ -785,26 +724,7 @@ describe('DeploymentSteps', () => { getNetworkKey: vi.fn() }); - render(DeploymentSteps, { - props: { - dotrain, - strategyDetail: { - name: 'SFLR<>WFLR on Flare', - description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.', - short_description: 'Rotate sFLR (Sceptre staked FLR) and WFLR on Flare.' - }, - deployment: mockDeployment, - wagmiConfig: mockWagmiConfigStore, - wagmiConnected: mockConnectedStore, - appKitModal: writable({} as AppKit), - handleDeployModal: vi.fn() as unknown as (args: DeploymentArgs) => void, - handleDisclaimerModal: vi.fn() as unknown as ( - args: ComponentProps - ) => void, - settings: writable({} as ConfigSource), - handleUpdateGuiState: vi.fn() - } - }); + render(DeploymentSteps, { props: defaultProps }); await waitFor(() => { expect(screen.getByText('Connect Wallet')).toBeInTheDocument(); diff --git a/packages/ui-components/src/__tests__/OrderDetail.test.ts b/packages/ui-components/src/__tests__/OrderDetail.test.ts index b87061ae4..b7dad943a 100644 --- a/packages/ui-components/src/__tests__/OrderDetail.test.ts +++ b/packages/ui-components/src/__tests__/OrderDetail.test.ts @@ -54,7 +54,6 @@ describe('OrderDetail Component', () => { id: 'mockId', subgraphUrl: 'https://example.com', walletAddressMatchesOrBlank: mockWalletAddressMatchesOrBlankStore, - wagmiConfig, chainId, orderbookAddress } @@ -87,7 +86,6 @@ describe('OrderDetail Component', () => { subgraphUrl: 'https://example.com', walletAddressMatchesOrBlank: mockWalletAddressMatchesOrBlankStore, handleOrderRemoveModal, - wagmiConfig, chainId, orderbookAddress } @@ -108,7 +106,6 @@ describe('OrderDetail Component', () => { subgraphUrl: 'https://example.com', walletAddressMatchesOrBlank: mockWalletAddressMatchesOrBlankStore, handleOrderRemoveModal: vi.fn(), - wagmiConfig, chainId, orderbookAddress } @@ -209,7 +206,6 @@ describe('OrderDetail Component', () => { id: mockOrderWithVaults.id, subgraphUrl: 'https://example.com', walletAddressMatchesOrBlank: mockWalletAddressMatchesOrBlankStore, - wagmiConfig, chainId, orderbookAddress } diff --git a/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte b/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte index c37866a41..32bc8fe56 100644 --- a/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte +++ b/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte @@ -25,7 +25,7 @@ import { onMount } from 'svelte'; import ShareChoicesButton from './ShareChoicesButton.svelte'; import { handleShareChoices } from '../../services/handleShareChoices'; - import type { DeploymentArgs, DisclaimerModalArgs } from '../../types/transaction'; + import type { DisclaimerModalProps, DeployModalProps } from '../../types/modal'; import { getDeploymentTransactionArgs } from './getDeploymentTransactionArgs'; import type { HandleAddOrderResult } from './getDeploymentTransactionArgs'; enum DeploymentStepErrors { @@ -47,8 +47,8 @@ export let deployment: GuiDeployment; export let strategyDetail: NameAndDescription; - export let handleDeployModal: (args: DeploymentArgs) => void; - export let handleDisclaimerModal: (args: DisclaimerModalArgs) => void; + export let handleDeployModal: (args: DeployModalProps) => void; + export let handleDisclaimerModal: (args: DisclaimerModalProps) => void; export let handleUpdateGuiState: (gui: DotrainOrderGui) => void; let selectTokens: SelectTokens | null = null; @@ -236,13 +236,19 @@ } handleDeployModal({ - ...result, - subgraphUrl: subgraphUrl, - network: networkKey + open: true, + args: { + ...result, + subgraphUrl: subgraphUrl, + network: networkKey + } }); }; - handleDisclaimerModal({ onAccept }); + handleDisclaimerModal({ + open: true, + onAccept + }); } const areAllTokensSelected = async () => { diff --git a/packages/ui-components/src/lib/components/detail/OrderDetail.svelte b/packages/ui-components/src/lib/components/detail/OrderDetail.svelte index f791ed203..3f17a275a 100644 --- a/packages/ui-components/src/lib/components/detail/OrderDetail.svelte +++ b/packages/ui-components/src/lib/components/detail/OrderDetail.svelte @@ -38,7 +38,7 @@ export let colorTheme; export let codeMirrorTheme; export let lightweightChartsTheme; - export let orderbookAddress: Hex | undefined = undefined; + export let orderbookAddress: Hex; export let id: string; export let rpcUrl: string; export let subgraphUrl: string; diff --git a/packages/ui-components/src/lib/components/detail/VaultDetail.svelte b/packages/ui-components/src/lib/components/detail/VaultDetail.svelte index d60fee8c8..6c7a0b953 100644 --- a/packages/ui-components/src/lib/components/detail/VaultDetail.svelte +++ b/packages/ui-components/src/lib/components/detail/VaultDetail.svelte @@ -22,10 +22,10 @@ import type { AppStoresInterface } from '../../types/appStores'; import type { Config } from 'wagmi'; import DepositOrWithdrawButtons from './DepositOrWithdrawButtons.svelte'; - import type { DepositOrWithdrawModalArgs } from '../../types/transaction'; + import type { DepositOrWithdrawModalProps } from '../../types/modal'; export let handleDepositOrWithdrawModal: - | ((args: DepositOrWithdrawModalArgs) => void) + | ((args: DepositOrWithdrawModalProps) => void) | undefined = undefined; export let id: string; export let network: string; diff --git a/tauri-app/src/routes/orders/[network]-[id]/+page.svelte b/tauri-app/src/routes/orders/[network]-[id]/+page.svelte index 49dc4f336..b7782d6e5 100644 --- a/tauri-app/src/routes/orders/[network]-[id]/+page.svelte +++ b/tauri-app/src/routes/orders/[network]-[id]/+page.svelte @@ -5,10 +5,10 @@ import { codeMirrorTheme, lightweightChartsTheme, colorTheme } from '$lib/stores/darkMode'; import { settings } from '$lib/stores/settings'; import { handleDebugTradeModal, handleQuoteDebugModal } from '$lib/services/modal'; - + import type { Hex } from 'viem'; const { id, network } = $page.params; - const orderbookAddress = $settings?.orderbooks?.[network]?.address; + const orderbookAddress = $settings?.orderbooks?.[network]?.address as Hex; const subgraphUrl = $settings?.subgraphs?.[network]; const rpcUrl = $settings?.networks?.[network]?.rpc; const chainId = $settings?.networks?.[network]?.['chain-id']; From 29f3e133f4ff80196c5b443fbb6de962e4602a09 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 18:32:01 +0100 Subject: [PATCH 10/17] tweak message --- .../webapp/src/lib/components/DepositOrWithdrawModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte index b9a15ff84..3066c256e 100644 --- a/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte +++ b/packages/webapp/src/lib/components/DepositOrWithdrawModal.svelte @@ -46,7 +46,7 @@ let switchChainError = ''; const messages = { - success: 'Your transaction was successful.', + success: 'Transaction successful.', pending: 'Processing your transaction...', error: 'Transaction failed.' }; From bb04d0a338595a12c0441c218220e8476030cda8 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 19:24:12 +0100 Subject: [PATCH 11/17] format --- packages/ui-components/src/__tests__/DeploymentSteps.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui-components/src/__tests__/DeploymentSteps.test.ts b/packages/ui-components/src/__tests__/DeploymentSteps.test.ts index 525b8ef7d..59638aca4 100644 --- a/packages/ui-components/src/__tests__/DeploymentSteps.test.ts +++ b/packages/ui-components/src/__tests__/DeploymentSteps.test.ts @@ -664,7 +664,9 @@ describe('DeploymentSteps', () => { await waitFor(() => { expect(screen.getByText('Select Tokens')).toBeInTheDocument(); - expect(screen.getByText('Select the tokens that you want to use in your order.')).toBeInTheDocument(); + expect( + screen.getByText('Select the tokens that you want to use in your order.') + ).toBeInTheDocument(); }); }); From 5affb033b2745ec231f3c30cdfca4b6afb2eeab1 Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Mon, 17 Feb 2025 19:42:52 +0100 Subject: [PATCH 12/17] rrmm --- packages/ui-components/src/__tests__/OrderDetail.test.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ui-components/src/__tests__/OrderDetail.test.svelte b/packages/ui-components/src/__tests__/OrderDetail.test.svelte index fe3655ac9..b779e0b72 100644 --- a/packages/ui-components/src/__tests__/OrderDetail.test.svelte +++ b/packages/ui-components/src/__tests__/OrderDetail.test.svelte @@ -10,9 +10,8 @@ import { Button } from 'flowbite-svelte'; import DepositOrWithdrawButtons from '../lib/components/detail/DepositOrWithdrawButtons.svelte'; import type { OrderRemoveModalProps } from '../lib/types/modal'; - - import type { Config } from 'wagmi'; import type { Hex } from 'viem'; + export let walletAddressMatchesOrBlank: Readable<(address: string) => boolean> | undefined = undefined; export let handleOrderRemoveModal: ((props: OrderRemoveModalProps) => void) | undefined = From 35190958ee31eebd0e314614f602cd8dfa419df3 Mon Sep 17 00:00:00 2001 From: findolor Date: Tue, 18 Feb 2025 10:53:23 +0300 Subject: [PATCH 13/17] create any functions for deposits and vault ids --- crates/js_api/src/gui/deposits.rs | 5 +++++ crates/js_api/src/gui/order_operations.rs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/crates/js_api/src/gui/deposits.rs b/crates/js_api/src/gui/deposits.rs index edcd741bf..b8b885e56 100644 --- a/crates/js_api/src/gui/deposits.rs +++ b/crates/js_api/src/gui/deposits.rs @@ -138,4 +138,9 @@ impl DotrainOrderGui { } Ok(missing_deposits) } + + #[wasm_bindgen(js_name = "hasAnyDeposit")] + pub fn has_any_deposit(&self) -> bool { + !self.deposits.is_empty() + } } diff --git a/crates/js_api/src/gui/order_operations.rs b/crates/js_api/src/gui/order_operations.rs index cf4c8572f..4b6438234 100644 --- a/crates/js_api/src/gui/order_operations.rs +++ b/crates/js_api/src/gui/order_operations.rs @@ -344,6 +344,12 @@ impl DotrainOrderGui { Ok(IOVaultIds(map)) } + #[wasm_bindgen(js_name = "hasAnyVaultId")] + pub fn has_any_vault_id(&self) -> Result { + let map = self.get_vault_ids()?; + Ok(map.0.values().any(|ids| ids.iter().any(|id| id.is_some()))) + } + #[wasm_bindgen(js_name = "updateScenarioBindings")] pub fn update_scenario_bindings(&mut self) -> Result<(), GuiError> { let deployment = self.get_current_deployment()?; From d8337de5732d97291f90f7e5110ec3efbbdacfd9 Mon Sep 17 00:00:00 2001 From: findolor Date: Tue, 18 Feb 2025 10:53:28 +0300 Subject: [PATCH 14/17] update tests --- packages/orderbook/test/js_api/gui.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/orderbook/test/js_api/gui.test.ts b/packages/orderbook/test/js_api/gui.test.ts index 2204bca30..d270a594a 100644 --- a/packages/orderbook/test/js_api/gui.test.ts +++ b/packages/orderbook/test/js_api/gui.test.ts @@ -5,11 +5,9 @@ import { AddOrderCalldataResult, AllFieldValuesResult, AllowancesResult, - ApprovalCalldataResult, DeploymentDetails, DeploymentKeys, DepositAndAddOrderCalldataResult, - DepositCalldataResult, Gui, GuiDeployment, IOVaultIds, @@ -447,9 +445,13 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Gui', async function () }); it('should add deposit', async () => { + assert.equal(gui.hasAnyDeposit(), false); + gui.saveDeposit('token1', '50.6'); const deposits: TokenDeposit[] = gui.getDeposits(); assert.equal(deposits.length, 1); + + assert.equal(gui.hasAnyDeposit(), true); }); it('should update deposit', async () => { @@ -1178,8 +1180,12 @@ ${dotrainWithoutVaultIds}`; assert.equal(currentDeployment.deployment.order.inputs[0].vaultId, undefined); assert.equal(currentDeployment.deployment.order.outputs[0].vaultId, undefined); + assert.equal(gui.hasAnyVaultId(), false); + gui.setVaultId(true, 0, '0x123'); + assert.equal(gui.hasAnyVaultId(), true); + assert.equal(gui.getVaultIds().get('input')?.[0], '0x123'); assert.equal(gui.getVaultIds().get('output')?.[0], undefined); From 45b760833353529c4fbb7914289776ac0a937769 Mon Sep 17 00:00:00 2001 From: findolor Date: Tue, 18 Feb 2025 10:54:29 +0300 Subject: [PATCH 15/17] update UI --- .../lib/components/deployment/DeploymentSteps.svelte | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte b/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte index 08d9354b5..bb025128c 100644 --- a/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte +++ b/packages/ui-components/src/lib/components/deployment/DeploymentSteps.svelte @@ -254,15 +254,9 @@ if (!allTokensSelected) return; // if we have deposits or vault ids set, show advanced options - const vaultIds = gui.getVaultIds(); - const inputVaultIds = vaultIds.get('input'); - const outputVaultIds = vaultIds.get('output'); - const deposits = gui.getDeposits(); - if ( - deposits.length > 0 || - (inputVaultIds && inputVaultIds.some((v) => v)) || - (outputVaultIds && outputVaultIds.some((v) => v)) - ) { + const hasDeposits = gui.hasAnyDeposit(); + const hasVaultIds = gui.hasAnyVaultId(); + if (hasDeposits || hasVaultIds) { showAdvancedOptions = true; } } catch (e) { From 6ddd551645e94a0ca35503f8809b1abbc8f45d6d Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Tue, 18 Feb 2025 11:25:51 +0100 Subject: [PATCH 16/17] remove all --- packages/webapp/src/__tests__/DeployModal.test.ts | 2 +- packages/webapp/src/lib/services/modal.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/webapp/src/__tests__/DeployModal.test.ts b/packages/webapp/src/__tests__/DeployModal.test.ts index c4d7d60c4..67341d6f2 100644 --- a/packages/webapp/src/__tests__/DeployModal.test.ts +++ b/packages/webapp/src/__tests__/DeployModal.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; import { render } from '@testing-library/svelte'; import DeployModal from '../lib/components/DeployModal.svelte'; import { get } from 'svelte/store'; diff --git a/packages/webapp/src/lib/services/modal.ts b/packages/webapp/src/lib/services/modal.ts index 990e062bc..082ac281b 100644 --- a/packages/webapp/src/lib/services/modal.ts +++ b/packages/webapp/src/lib/services/modal.ts @@ -3,7 +3,6 @@ import DepositOrWithdrawModal from '$lib/components/DepositOrWithdrawModal.svelt import OrderRemoveModal from '$lib/components/OrderRemoveModal.svelte'; import { DisclaimerModal, - type DeploymentArgs, type DepositOrWithdrawModalProps, type OrderRemoveModalProps, type DisclaimerModalProps, From 9c4f71b1dccee24ec6818eb716d6aaee4441caab Mon Sep 17 00:00:00 2001 From: Jamie Harding Date: Wed, 19 Feb 2025 11:36:36 +0100 Subject: [PATCH 17/17] re-run --- packages/ui-components/src/__tests__/transactionStore.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/__tests__/transactionStore.test.ts b/packages/ui-components/src/__tests__/transactionStore.test.ts index 2d3396849..2d9e7843e 100644 --- a/packages/ui-components/src/__tests__/transactionStore.test.ts +++ b/packages/ui-components/src/__tests__/transactionStore.test.ts @@ -1,5 +1,5 @@ -import { describe, it, expect, vi, beforeEach, afterAll, type Mock } from 'vitest'; import { get } from 'svelte/store'; +import { describe, it, expect, vi, beforeEach, afterAll, type Mock } from 'vitest'; import transactionStore, { TransactionStatus, TransactionErrorMessage