Skip to content

Commit

Permalink
feat(bridge-ui): configurable destOwner (#17721)
Browse files Browse the repository at this point in the history
  • Loading branch information
KorbinianK authored Jul 4, 2024
1 parent e505392 commit 3220a22
Show file tree
Hide file tree
Showing 39 changed files with 341 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { importDone, processingFeeMethod, recipientAddress } from '$components/Bridge/state';
import { destOwnerAddress, importDone, processingFeeMethod, recipientAddress } from '$components/Bridge/state';
import { ChainSelector, ChainSelectorType } from '$components/ChainSelectors';
import { ProcessingFeeMethod } from '$libs/fee';
Expand All @@ -14,6 +14,7 @@
const reset = () => {
$recipientAddress = null;
$destOwnerAddress = null;
$processingFeeMethod = ProcessingFeeMethod.RECOMMENDED;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import { debounce } from '$libs/util/debounce';
import { getLogger } from '$libs/util/logger';
import { truncateDecimal } from '$libs/util/truncateDecimal';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';
import { ethBalance } from '$stores/balance';
import { connectedSourceChain } from '$stores/network';
Expand All @@ -46,7 +45,7 @@
export let hasEnoughEth: boolean = false;
export let exceedsQuota: boolean = false;
let inputId = `input-${uid()}`;
let inputId = `input-${crypto.randomUUID()}`;
let inputBox: InputBox;
let value = '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
import { chainConfig } from '$chainConfig';
import { Alert } from '$components/Alert';
import { ProcessingFee, Recipient } from '$components/Bridge/SharedBridgeComponents';
import { destNetwork as destChain, enteredAmount, processingFee, selectedToken } from '$components/Bridge/state';
import DestOwner from '$components/Bridge/SharedBridgeComponents/RecipientStep/DestOwner.svelte';
import {
destNetwork as destChain,
destOwnerAddress,
enteredAmount,
processingFee,
selectedToken,
} from '$components/Bridge/state';
import { PUBLIC_SLOW_L1_BRIDGING_WARNING } from '$env/static/public';
import { LayerType } from '$libs/chain';
import { isStablecoin, isSupported, isWrapped, type Token, TokenType } from '$libs/token';
import { isToken } from '$libs/token/isToken';
import { account } from '$stores/account';
import { ethBalance } from '$stores/balance';
import { connectedSourceChain } from '$stores/network';
Expand All @@ -19,6 +27,7 @@
export let hasEnoughFundsToContinue: boolean = true;
let recipientComponent: Recipient;
let destOwnerComponent: DestOwner;
let processingFeeComponent: ProcessingFee;
let slowL1Warning = PUBLIC_SLOW_L1_BRIDGING_WARNING || false;
Expand Down Expand Up @@ -106,6 +115,9 @@ Recipient & Processing Fee
<button class="flex justify-start link" on:click={editTransactionDetails}> {$t('common.edit')} </button>
</div>
<Recipient bind:this={recipientComponent} small />
{#if $destOwnerAddress !== $account?.address && $destOwnerAddress}
<DestOwner bind:this={destOwnerComponent} small />
{/if}
<ProcessingFee bind:this={processingFeeComponent} small bind:hasEnoughEth />
</div>

Expand Down
2 changes: 2 additions & 0 deletions packages/bridge-ui/src/components/Bridge/NFTBridge.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import {
activeBridge,
destNetwork as destinationChain,
destOwnerAddress,
importDone,
recipientAddress,
selectedNFTs,
Expand Down Expand Up @@ -101,6 +102,7 @@
if (nftIdInputComponent) nftIdInputComponent.clearIds();
$recipientAddress = $account?.address || null;
$destOwnerAddress = $account?.address || null;
bridgingStatus = BridgingStatus.PENDING;
$selectedToken = ETHToken;
importMethod === null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { Icon } from '$components/Icon';
import InputBox from '$components/InputBox/InputBox.svelte';
import { uid } from '$libs/util/uid';
import { IDInputState as State } from './state';
Expand All @@ -24,7 +23,7 @@
const dispatch = createEventDispatcher();
let inputId = `input-${uid()}`;
let inputId = `input-${crypto.randomUUID()}`;
function validateInput(idInput: EventTarget | number[] | null = null) {
state = State.VALIDATING;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import { ETHToken, fetchBalance, fetchBalance as getTokenBalance, TokenType } from '$libs/token';
import { debounce } from '$libs/util/debounce';
import { getLogger } from '$libs/util/logger';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';
import { ethBalance } from '$stores/balance';
import { connectedSourceChain } from '$stores/network';
Expand All @@ -30,7 +29,7 @@
const log = getLogger('component:Amount');
let inputId = `input-${uid()}`;
let inputId = `input-${crypto.randomUUID()}`;
let inputBox: InputBox;
let computingMaxAmount = false;
let invalidInput = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
import { Icon } from '$components/Icon';
import { withHoverAndFocusListener } from '$libs/customActions';
import { classNames } from '$libs/util/classNames';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';
import { AddressInputState as State } from './state';
let inputElement: HTMLInputElement;
let inputId = `input-${uid()}`;
let inputId = `input-${crypto.randomUUID()}`;
let isElementFocused = false;
let isElementHovered = false;
Expand All @@ -26,6 +26,7 @@
export let isDisabled = false;
export let quiet = false;
export let state: State = State.DEFAULT;
export let resettable = false;
export let onDialog = false;
Expand Down Expand Up @@ -54,6 +55,12 @@
state = State.DEFAULT;
};
const setToCurrentAddress = (): void => {
clearAddress();
ethereumAddress = $account?.address || '';
validateAddress();
};
export const focus = (): void => inputElement.focus();
$: defaultBorder = (() => {
Expand All @@ -78,6 +85,9 @@
<!-- Input field and label -->
<div class="f-between-center text-secondary-content">
<label class="body-regular" for={inputId}>{labelText}</label>
{#if resettable}
<button class="link" on:click={setToCurrentAddress}>{$t('common.reset_to_wallet')}</button>
{/if}
</div>
<div class="relative f-items-center">
<input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import { closeOnEscapeOrOutsideClick } from '$libs/customActions';
import { ProcessingFeeMethod } from '$libs/fee';
import { parseToWei } from '$libs/util/parseToWei';
import { uid } from '$libs/util/uid';
import NoneOption from './NoneOption.svelte';
import RecommendedFee from './RecommendedFee.svelte';
Expand All @@ -23,7 +22,7 @@
export let hasEnoughEth: boolean = false;
export let disabled = false;
let dialogId = `dialog-${uid()}`;
let dialogId = `dialog-${crypto.randomUUID()}`;
let recommendedAmount = BigInt(0);
let errorCalculatingRecommendedAmount = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<script lang="ts">
import { t } from 'svelte-i18n';
import type { Address } from 'viem';
import { destNetwork, destOwnerAddress } from '$components/Bridge/state';
import { ActionButton, CloseButton } from '$components/Button';
import { Tooltip } from '$components/Tooltip';
import { isSmartContract } from '$libs/util/isSmartContract';
import { shortenAddress } from '$libs/util/shortenAddress';
import { account } from '$stores/account';
import AddressInput from '../AddressInput/AddressInput.svelte';
// Public API
export const clearRecipient = () => {
if (addressInput) addressInput.clearAddress(); // update UI
$destOwnerAddress = null; // update state
};
export let small = false;
export let disabled = false;
let dialogId = `dialog-${crypto.randomUUID()}`;
let addressInput: AddressInput;
let modalOpen = false;
let invalidAddress = false;
let prevDestOwnerAddress: Maybe<Address> = null;
let destOwnerIsSmartContract = false;
function closeModal() {
modalOpen = false;
}
function openModal() {
modalOpen = true;
addressInput.focus();
addEscKeyListener();
}
function cancelModal() {
// Revert change of destOwner address
$destOwnerAddress = prevDestOwnerAddress;
removeEscKeyListener();
closeModal();
}
function modalOpenChange(open: boolean) {
if (open) {
// Save it in case we want to cancel
prevDestOwnerAddress = $destOwnerAddress;
}
}
async function onAddressValidation(event: CustomEvent<{ isValidEthereumAddress: boolean; addr: Address }>) {
const { isValidEthereumAddress, addr } = event.detail;
if (isValidEthereumAddress) {
invalidAddress = false;
if ($destNetwork?.id && (await isSmartContract(addr, $destNetwork.id))) {
destOwnerIsSmartContract = true;
} else {
destOwnerIsSmartContract = false;
$destOwnerAddress = addr;
}
} else {
invalidAddress = true;
}
}
const resetAddress = () => {
$destOwnerAddress = $account?.address;
ethereumAddressBinding = undefined;
destOwnerIsSmartContract = false;
};
let escKeyListener: (event: KeyboardEvent) => void;
const addEscKeyListener = () => {
escKeyListener = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
closeModal();
}
};
window.addEventListener('keydown', escKeyListener);
};
const removeEscKeyListener = () => {
window.removeEventListener('keydown', escKeyListener);
};
$: modalOpenChange(modalOpen);
$: ethereumAddressBinding = undefined;
$: displayedDestOwner = $destOwnerAddress || $account?.address;
</script>

<div class="Recipient f-col">
{#if small}
<div class="f-between-center">
<span class="text-secondary-content">{$t('destOwner.title')}</span>
{#if displayedDestOwner}
{shortenAddress(displayedDestOwner, 8, 10)}
{#if displayedDestOwner !== $account?.address}
<span class="text-primary-link">| {$t('common.customized')}</span>
{/if}
{:else}
{$t('destOwner.placeholder')}
{/if}
</div>
{:else}
<div class="f-between-center">
<div class="flex space-x-2">
<span class="body-small-bold text-primary-content">{$t('destOwner.title')}</span>
<Tooltip>
<h2>{$t('destOwner.tooltip_title')}</h2>
{$t('destOwner.tooltip')}
</Tooltip>
</div>
{#if !disabled}
<button class="link" on:click={openModal} on:focus={openModal}>{$t('common.edit')}</button>
{/if}
</div>

<span class="body-small-regular text-secondary-content mt-[4px]">
{#if displayedDestOwner}
{shortenAddress(displayedDestOwner, 15, 13)}
{#if displayedDestOwner !== $account?.address}
<span class="text-primary-link">| {$t('common.customized')}</span>
{/if}
{:else}
{$t('recipient.placeholder')}
{/if}
</span>

<dialog id={dialogId} class="modal" class:modal-open={modalOpen}>
<div class="modal-box relative px-6 md:rounded-[20px] bg-neutral-background">
<CloseButton onClick={cancelModal} />

<div class="w-full">
<h3 class="title-body-bold mb-7">{$t('destOwner.title')}</h3>

<p class="body-regular text-secondary-content mb-3">{$t('destOwner.description')}</p>

<div class="relative my-[20px]">
<AddressInput
bind:this={addressInput}
bind:ethereumAddress={ethereumAddressBinding}
on:addressvalidation={onAddressValidation}
on:clearInput={resetAddress}
onDialog
resettable />
</div>

{#if destOwnerIsSmartContract}
<p class="body-regular text-secondary-content mb-3">
You cannot set a smart contract as destination owner.
</p>
{/if}

<div class="grid grid-cols-2 gap-[20px]">
<ActionButton on:click={cancelModal} priority="secondary" onPopup>
<span class="body-bold">{$t('common.cancel')}</span>
</ActionButton>
<ActionButton
priority="primary"
disabled={invalidAddress || !ethereumAddressBinding || destOwnerIsSmartContract}
on:click={closeModal}
onPopup>
<span class="body-bold">{$t('common.confirm')}</span>
</ActionButton>
</div>
</div>
</div>
</dialog>
{/if}
</div>
Loading

0 comments on commit 3220a22

Please sign in to comment.