Skip to content

Commit

Permalink
app consistent address (#3636)
Browse files Browse the repository at this point in the history
- **feat(app): introduce Address component**
- **feat(app): show raw and bech addresses**
- **feat(app): improve partial intent feedback**
- **feat(app): bech32 decode receiver**
- **feat(app): bech32 decode/encode cosmos receive address**
  • Loading branch information
cor authored Jan 24, 2025
2 parents 60a0283 + 67894a2 commit 85e8cdb
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Token from "$lib/components/token.svelte"
import type { Chain, Ucs03Channel } from "$lib/types"
import ArrowRightIcon from "virtual:icons/lucide/arrow-right"
import { toDisplayName } from "$lib/utilities/chains"
import Address from "$lib/components/address.svelte"
interface Props {
stores: {
Expand Down Expand Up @@ -98,24 +99,28 @@ let { rawIntents, intents, validation } = stores
{#if !$channel}
<div>No recommended UCS03 channel to go from {toDisplayName($rawIntents.source, chains)} to {toDisplayName($rawIntents.destination, chains)}</div>
{:else}
{#if !$rawIntents.asset}
Select an asset
{:else}
<div class="flex flex-col gap-1">
{#if !transferArgs}
<LoadingDots/>
<div class="flex flex-col gap-1 justify-end items-center">
<div class="flex gap-4 text-muted-foreground text-xs">{$channel?.source_connection_id} | {$channel?.source_channel_id} <ArrowRightIcon />{$channel?.destination_connection_id} | {$channel?.destination_channel_id}</div>
{#if !$rawIntents.asset}
Select an asset
{:else}
<div class="flex-1 flex flex-col items-center text-xs">
<div class="flex gap-4 text-muted-foreground">{$channel?.source_connection_id} | {$channel?.source_channel_id} <ArrowRightIcon />{$channel?.destination_connection_id} | {$channel?.destination_channel_id}</div>
<Token amount={$rawIntents.amount} chainId={$rawIntents.destination} denom={transferArgs.quoteToken} {chains}/>
</div>
<Button
disabled={!$validation.isValid}
on:click={() => rotateTo("verifyFace")}>Transfer
</Button>
{/if}
{#if !transferArgs}
<LoadingDots/>
{:else}
<div class="flex-1 flex flex-col items-center text-xs">
<Token amount={$rawIntents.amount} chainId={$rawIntents.destination} denom={transferArgs.quoteToken} {chains}/>
</div>
{#if $validation.isValid}
<Address address={transferArgs.receiver} {chains} chainId={$channel.destination_chain_id}/>
{/if}
<Button
class="w-full mt-2"
disabled={!$validation.isValid}
on:click={() => rotateTo("verifyFace")}>Transfer
</Button>
{/if}
{/if}
</div>
{/if}
{/if}
</div>
</div>
17 changes: 15 additions & 2 deletions app/src/lib/components/TransferFrom/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import { userBalancesQuery } from "$lib/queries/balance"
import { userAddress, balanceStore } from "$lib/components/TransferFrom/transfer/balances.ts"
import { createRawIntentsStore } from "./transfer/raw-intents.ts"
import { derived, writable, type Writable } from "svelte/store"
import { getChannelInfo, getQuoteToken } from "@unionlabs/client"
import {
bech32AddressToHex,
getChannelInfo,
getQuoteToken,
isValidBech32Address
} from "@unionlabs/client"
import { fromHex, isHex, toHex } from "viem"
import { subscribe } from "graphql"
Expand Down Expand Up @@ -44,6 +49,9 @@ rawIntents.subscribe(async () => {
const chain = chains.find(c => c.chain_id === $rawIntents.source)
if (!chain) return null
const destChain = chains.find(c => c.chain_id === $rawIntents.destination)
if (!destChain) return null
// decode from hex if cosmos to assert proper quote token prediction.
let baseToken =
chain.rpc_type === "cosmos" && isHex($rawIntents.asset)
Expand All @@ -58,6 +66,11 @@ rawIntents.subscribe(async () => {
return null
}
const receiver =
destChain.rpc_type === "cosmos" && isValidBech32Address($rawIntents.receiver)
? bech32AddressToHex({ address: $rawIntents.receiver })
: $rawIntents.receiver
let ucs03address =
chain.rpc_type === "cosmos"
? fromHex(`0x${$channel.source_port_id}`, "string")
Expand All @@ -70,7 +83,7 @@ rawIntents.subscribe(async () => {
baseAmount: BigInt($rawIntents.amount),
quoteToken: quoteToken.value.quote_token,
quoteAmount: BigInt($rawIntents.amount),
receiver: $rawIntents.receiver,
receiver,
sourceChannelId: $channel.source_channel_id,
ucs03address
})
Expand Down
44 changes: 44 additions & 0 deletions app/src/lib/components/address.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts">
import type { Chain } from "$lib/types"
import { toDisplayName } from "$lib/utilities/chains"
import { hexAddressToBech32 } from "@unionlabs/client"
import { isHex } from "viem"
import ArrowLeftIcon from "virtual:icons/lucide/arrow-left"
export let chains: Array<Chain>
export let chainId: string
export let address: string | null
export let showChain = false
export let showRaw = false
const chain = chains.find(c => c.chain_id === chainId) ?? null
const parsedAddress =
chain?.rpc_type === "cosmos" && isHex(address)
? hexAddressToBech32({ address, bech32Prefix: chain.addr_prefix })
: address
const explorer = chain?.explorers?.at(0)?.address_url ?? null
</script>

<div class="flex flex-col text-xs">
<div class="flex gap-1 items-center">
{#if parsedAddress}
{#if !chain}
invalid chain {chainId}
{:else}
{#if !explorer}
{parsedAddress}
{:else}
<a class="underline" href={`${explorer}/${parsedAddress}`}>{parsedAddress}</a>
{/if}{#if showChain}<ArrowLeftIcon />{toDisplayName(
chainId,
chains,
)}{/if}
{/if}
{/if}
</div>
{#if address && showRaw}
<div class="text-muted-foreground">
RAW: {address}
</div>
{/if}
</div>
10 changes: 2 additions & 8 deletions app/src/lib/components/table-cells/cell-origin-transfer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { cn } from "$lib/utilities/shadcn.ts"
import { truncate } from "$lib/utilities/format"
import * as Tooltip from "$lib/components/ui/tooltip"
import type { Chain } from "$lib/types"
import Address from "../address.svelte"
export let chains: Array<Chain>
export let value: {
Expand All @@ -20,12 +21,5 @@ const chainDisplayName =
<div class="font-bold">
{chainDisplayName}
</div>
<Tooltip.Root>
<Tooltip.Trigger>
<div>{truncate(value.address, 8)}</div>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{value.address}</p>
</Tooltip.Content>
</Tooltip.Root>
<Address address={value.address} chainId={value.chainId} {chains}/>
</div>
45 changes: 3 additions & 42 deletions app/src/lib/components/transfer-details.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Truncate from "$lib/components/truncate.svelte"
import { formatUnits } from "viem"
import PacketPath from "./packet-path.svelte"
import Token from "./token.svelte"
import Address from "./address.svelte"
// prefix a source with 0x if not there for cosmos tx hashes
const source = $page.params.source.startsWith("0x")
Expand Down Expand Up @@ -122,51 +123,11 @@ let processedTransfers = derived(
<section class="flex flex-col lg:flex-row gap-8">
<div class="flex-col text-muted-foreground">
<DetailsHeading>Sender</DetailsHeading>
{#if sourceExplorer !== undefined}
<a
href={`/explorer/address/${transfer.sender}`}
class="block text-sm underline break-words"
><Truncate
class="underline"
value={transfer.sender}
type="address"
/>
</a>{:else}<p class="text-sm break-words">
<Truncate value={transfer.sender} type="address" />
</p>{/if}
<p
class={cn(
"text-[10px] break-words",
"normalized_sender" in transfer &&
transfer.normalized_sender
? "text-black dark:text-muted-foreground"
: "text-transparent",
)}
></p>
<Address address={transfer.sender} {chains} chainId={transfer.source_chain_id} />
</div>
<div class="flex-1 lg:text-right flex-col text-muted-foreground">
<DetailsHeading>Receiver</DetailsHeading>
{#if destinationExplorer !== undefined}
<a
href={`/explorer/address/${transfer.receiver}`}
class="block text-sm underline break-words"
><Truncate
class="underline"
value={transfer.receiver}
type="address"
/>
</a>{:else}<p class="text-sm break-words">
<Truncate value={transfer.receiver} type="address" />
</p>{/if}
<p
class={cn(
"text-[10px] break-words",
"normalized_receiver" in transfer &&
transfer.normalized_receiver
? "text-black dark:text-muted-foreground"
: "text-transparent",
)}
></p>
<Address address={transfer.receiver} {chains} chainId={transfer.destination_chain_id} />
</div>
</section>
</Card.Content>
Expand Down
2 changes: 2 additions & 0 deletions app/src/lib/graphql/fragments/transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { graphql } from "../index.ts"
export const transferListDataFragment = graphql(`
fragment TransferListData on v1_ibc_union_fungible_asset_orders {
sender
sender_normalized
source_chain_id
packet_send_timestamp
packet_send_transaction_hash
receiver
receiver_normalized
destination_chain_id
packet_recv_timestamp
packet_recv_transaction_hash
Expand Down
4 changes: 2 additions & 2 deletions app/src/lib/queries/transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ const transferTransform = (tx: FragmentOf<typeof transferListDataFragment>) => {
source: {
hash: transfer.packet_send_transaction_hash || "unknown",
chainId: transfer.source_chain_id ?? raise("source_chain_id is null"),
address: transfer.sender || "unknown"
address: transfer.sender_normalized || "unknown"
},
destination: {
hash: transfer.packet_recv_transaction_hash || "unknown",
chainId: transfer.destination_chain_id ?? raise("destination_chain_id is null"),
address: transfer.receiver || "unknown"
address: transfer.receiver_normalized || "unknown"
},
baseToken: transfer.base_token,
baseAmount: transfer.base_amount,
Expand Down

0 comments on commit 85e8cdb

Please sign in to comment.