diff --git a/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte b/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte index 2212f985edb..6d275a2daf0 100644 --- a/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte +++ b/packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte @@ -2,7 +2,7 @@ import { isAddress } from 'ethereum-address'; import { createEventDispatcher } from 'svelte'; import { t } from 'svelte-i18n'; - + import type { Address } from 'viem'; import { Alert } from '$components/Alert'; import { uid } from '$libs/util/uid'; @@ -10,7 +10,7 @@ let inputId = `input-${uid()}`; let showAlert = true; - let ethereumAddress = ''; + export let ethereumAddress: Address | string = ''; let isValidEthereumAddress = false; let tooShort = true; @@ -23,6 +23,7 @@ if (ethereumAddress.length > 41) { tooShort = false; validateEthereumAddress(); + dispatch('input', ethereumAddress); } else { tooShort = true; } @@ -39,9 +40,6 @@ }; export const focus = () => input.focus(); - export const value = () => { - return input.value; - };
diff --git a/packages/bridge-ui-v2/src/components/Icon/ERC20.svelte b/packages/bridge-ui-v2/src/components/Icon/ERC20.svelte new file mode 100644 index 00000000000..752e06e83ad --- /dev/null +++ b/packages/bridge-ui-v2/src/components/Icon/ERC20.svelte @@ -0,0 +1,21 @@ + + + diff --git a/packages/bridge-ui-v2/src/components/Icon/Icon.svelte b/packages/bridge-ui-v2/src/components/Icon/Icon.svelte index c6b02fcd432..c3e410fd284 100644 --- a/packages/bridge-ui-v2/src/components/Icon/Icon.svelte +++ b/packages/bridge-ui-v2/src/components/Icon/Icon.svelte @@ -15,10 +15,12 @@ | 'up-down-circle' | 'check-circle' | 'info-circle' + | 'plus-circle' | 'circle' | 'arrow-right' | 'up-down' - | 'check'; + | 'check' + | 'trash'; - + {#if type === 'bridge'} + {:else if type === 'plus-circle'} + {:else if type === 'circle'} {:else if type === 'arrow-right'} @@ -145,5 +158,11 @@ clip-rule="evenodd" d="M16.7045 4.15347C17.034 4.4045 17.0976 4.87509 16.8466 5.20457L8.84657 15.7046C8.71541 15.8767 8.51627 15.9838 8.30033 15.9983C8.08439 16.0129 7.87271 15.9334 7.71967 15.7804L3.21967 11.2804C2.92678 10.9875 2.92678 10.5126 3.21967 10.2197C3.51256 9.92682 3.98744 9.92682 4.28033 10.2197L8.17351 14.1129L15.6534 4.29551C15.9045 3.96603 16.3751 3.90243 16.7045 4.15347Z" /> + {:else if type === 'trash'} + {/if} diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte new file mode 100644 index 00000000000..d352b59b0b6 --- /dev/null +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/AddCustomERC20.svelte @@ -0,0 +1,193 @@ + + + + + + diff --git a/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte b/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte index 626e4eedbb8..5deb6a4c603 100644 --- a/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte +++ b/packages/bridge-ui-v2/src/components/TokenDropdown/DropdownView.svelte @@ -1,15 +1,25 @@ @@ -44,6 +73,39 @@
{/each} + {#each customTokens as token (token.symbol)} +
  • selectToken(token)} + on:keydown={getTokenKeydownHandler(token)}> +
    + + + + {token.symbol} +
    +
  • + {/each} +
    +
  • + +
  • + diff --git a/packages/bridge-ui-v2/src/i18n/en.json b/packages/bridge-ui-v2/src/i18n/en.json index c5aabfd17db..c3a41e38a1e 100644 --- a/packages/bridge-ui-v2/src/i18n/en.json +++ b/packages/bridge-ui-v2/src/i18n/en.json @@ -32,7 +32,11 @@ "rejected": "Request to bridge rejected.", "no_allowance_required": "You already have enough allowance.", "insufficient_allowance": "You need to increase your allowance in order to be able to bridge.", - "unknown_error": "An error occured" + "unknown_error": "An error occured", + "custom_token": { + "not_found": "Token not found", + "description": "Are you on the right network?" + } }, "button": { "approve": "Approve", diff --git a/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts b/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts new file mode 100644 index 00000000000..3a00409f426 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/storage/CustomTokenService.ts @@ -0,0 +1,79 @@ +import type { Token, TokenService } from "$libs/token"; +import { jsonParseWithDefault } from "$libs/util/jsonParseWithDefault"; + +const STORAGE_PREFIX = 'custom-tokens'; + +export class CustomTokenService implements TokenService { + private readonly storage: Storage; + + private readonly storageChangeNotifier: EventTarget; + + + constructor(storage: Storage) { + this.storage = storage; + this.storageChangeNotifier = new EventTarget(); + } + + private _getTokensFromStorage(address: string): Token[] { + const existingCustomTokens = this.storage.getItem( + `${STORAGE_PREFIX}-${address.toLowerCase()}`, + ); + + return jsonParseWithDefault(existingCustomTokens, []); + } + + storeToken(token: Token, address: string): Token[] { + const tokens: Token[] = this._getTokensFromStorage(address); + + let doesTokenAlreadyExist = false; + if (tokens.length > 0) { + doesTokenAlreadyExist = + tokens.findIndex( + (tokenFromStorage) => tokenFromStorage.symbol === token.symbol, + ) >= 0; + } + if (!doesTokenAlreadyExist) { + tokens.push(token); + } + + this.storage.setItem( + `${STORAGE_PREFIX}-${address.toLowerCase()}`, + JSON.stringify(tokens), + ); + this.storageChangeNotifier.dispatchEvent(new CustomEvent('storageChange', { detail: tokens })); + + + return tokens; + } + + getTokens(address: string): Token[] { + if (address) { + return this._getTokensFromStorage(address); + + } return []; + } + + removeToken(token: Token, address: string): Token[] { + const tokens: Token[] = this._getTokensFromStorage(address); + + const updatedTokenList = tokens.filter( + (tokenFromStorage) => tokenFromStorage.symbol !== token.symbol, + ); + + this.storage.setItem( + `${STORAGE_PREFIX}-${address.toLowerCase()}`, + JSON.stringify(updatedTokenList), + ); + this.storageChangeNotifier.dispatchEvent(new CustomEvent('storageChange', { detail: updatedTokenList })); + + return updatedTokenList; + } + + subscribeToChanges(callback: (tokens: Token[]) => void): void { + this.storageChangeNotifier.addEventListener('storageChange', (event) => callback((event as CustomEvent).detail)); + } + + unsubscribeFromChanges(callback: (tokens: Token[]) => void): void { + this.storageChangeNotifier.removeEventListener('storageChange', (event) => callback((event as CustomEvent).detail)); + } +} \ No newline at end of file diff --git a/packages/bridge-ui-v2/src/libs/storage/services.ts b/packages/bridge-ui-v2/src/libs/storage/services.ts new file mode 100644 index 00000000000..084b3f1d274 --- /dev/null +++ b/packages/bridge-ui-v2/src/libs/storage/services.ts @@ -0,0 +1,4 @@ +import { CustomTokenService } from './CustomTokenService'; + + +export const tokenService = new CustomTokenService(globalThis.localStorage); \ No newline at end of file diff --git a/packages/bridge-ui-v2/src/libs/token/types.ts b/packages/bridge-ui-v2/src/libs/token/types.ts index 96e180efb64..68610d13176 100644 --- a/packages/bridge-ui-v2/src/libs/token/types.ts +++ b/packages/bridge-ui-v2/src/libs/token/types.ts @@ -25,4 +25,11 @@ export type TokenEnv = { name: string; address: Address; symbol: string; + balance: bigint; }; + +export interface TokenService { + storeToken(token: Token, address: string): Token[]; + getTokens(address: string): Token[]; + removeToken(token: Token, address: string): Token[]; +}