diff --git a/bun.lockb b/bun.lockb index 4159f1ecc..1656cbbb2 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 04ed151c6..e89aa25ab 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@coinbase/cbpay-js": "^2.4.0", "@fontsource/jetbrains-mono": "^5.1.0", "@tailwindcss/container-queries": "^0.1.1", - "@wharfkit/account": "^1.2.0", + "@wharfkit/account": "^1.3.0", "@wharfkit/account-creation-plugin-greymass": "^1.2.0", "@wharfkit/account-creation-plugin-metamask": "^1.1.1", "@wharfkit/antelope": "^1.0.11", @@ -63,6 +63,7 @@ "@wharfkit/session": "^1.4.0", "@wharfkit/transact-plugin-resource-provider": "^1.1.1", "@wharfkit/wallet-plugin-anchor": "^1.4.0", + "@wharfkit/wallet-plugin-cleos": "^1.2.0", "@wharfkit/wallet-plugin-metamask": "^1.1.1", "@wharfkit/wallet-plugin-privatekey": "^1.1.0", "@wharfkit/wallet-plugin-scatter": "^1.5.1", diff --git a/src/hooks.server.ts b/src/hooks.server.ts index ad2be39c6..857587df0 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -37,6 +37,22 @@ function isNetwork(value: string) { return isNetworkShortName(value); } +const redirects: Record = { + '/earn': '/staking', + '/resources/ram/buy': '/ram/buy', + '/resources/ram/sell': '/ram/sell' +}; + +function getManualRedirectPath(pathMore: string[]): string { + const pathname = '/' + pathMore.join('/'); + return redirects[pathname]; +} + +function isManualRedirectPath(pathMore: string[]): boolean { + const pathname = '/' + pathMore.join('/'); + return pathname in redirects; +} + export async function redirectHandle({ event, resolve }: HandleParams): Promise { const { pathname, search } = new URL(event.request.url); @@ -79,10 +95,15 @@ export async function redirectHandle({ event, resolve }: HandleParams): Promise< url += `/${network}`; } if (pathMore.length > 0) { - url += `/${pathMore.join('/')}`; + if (isManualRedirectPath(pathMore)) { + url += getManualRedirectPath(pathMore); + } else { + url += `/${pathMore.join('/')}`; + } } if (pathname !== url) { + console.log('redirecting to', url + search); return new Response(undefined, { headers: { Location: url + search }, status: 301 diff --git a/src/lib/components/button/button.svelte b/src/lib/components/button/button.svelte index 243588d90..c554fa4c6 100644 --- a/src/lib/components/button/button.svelte +++ b/src/lib/components/button/button.svelte @@ -1,4 +1,5 @@ - {@render props.children()} + {@render props.children()} - - diff --git a/src/lib/components/button/copy.svelte b/src/lib/components/button/copy.svelte index 579fe5f01..2cded7523 100644 --- a/src/lib/components/button/copy.svelte +++ b/src/lib/components/button/copy.svelte @@ -5,14 +5,16 @@ import { browser } from '$app/environment'; import type { UnicoveContext } from '$lib/state/client.svelte'; import { getContext } from 'svelte'; + import { cn } from '$lib/utils'; const context = getContext('state'); interface Props { data: string; + slop?: boolean; } - let props: Props = $props(); + let { slop = true, ...props }: Props = $props(); let hint = $state(false); @@ -26,6 +28,8 @@ if (context.settings.data.debugMode) console.error('Failed to copy text: ', err); } } + + let buttonSize = $derived(slop ? 'size-12' : 'size-4'); @@ -35,7 +39,10 @@ > {/if} +

Exchanges

+

+ EOS can be purchased through a number of platforms, depending on the users needs and location. + Below are some of the most popular options available. +

+ + {#if context.settings.data.debugMode}

{m.common_debugging()}

diff --git a/src/routes/[network]/(account)/ram/(forms)/buy/+page.svelte b/src/routes/[network]/(account)/ram/(forms)/buy/+page.svelte index 6fd898ce7..e8b835d1a 100644 --- a/src/routes/[network]/(account)/ram/(forms)/buy/+page.svelte +++ b/src/routes/[network]/(account)/ram/(forms)/buy/+page.svelte @@ -20,6 +20,7 @@ import { BuyRAMState } from './state.svelte'; import { calAvailableSize, preventDefault } from '$lib/utils'; + import { DD, DL, DLRow } from '$lib/components/descriptionlist'; let bytesInput: BytesInput | undefined = $state(); let assetInput: AssetInput | undefined = $state(); @@ -139,34 +140,39 @@ -
-

- {m.common_labeled_unit_price({ - unit: `${context.network.chain.systemToken?.symbol.name}/RAM` - })} (KB) -

- - -
- -

{m.ram_to_purchase()}

- - -
- -

{m.ram_purchase_value()}

- - -
- -

{m.common_network_fees()} (0.5%)

- - -
- -

{m.common_total_cost()}

- -
+
+ +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+
{#if buyRamState.valid} {#if buyRamState.format === 'asset'} diff --git a/src/routes/[network]/(account)/ram/(forms)/sell/+page.svelte b/src/routes/[network]/(account)/ram/(forms)/sell/+page.svelte index f6d7ab8a5..bb9583580 100644 --- a/src/routes/[network]/(account)/ram/(forms)/sell/+page.svelte +++ b/src/routes/[network]/(account)/ram/(forms)/sell/+page.svelte @@ -19,6 +19,7 @@ import { SellRAMState } from './state.svelte'; import { calAvailableSize, preventDefault } from '$lib/utils'; + import { DD, DL, DLRow } from '$lib/components/descriptionlist'; let bytesInput: BytesInput | undefined = $state(); let assetInput: AssetInput | undefined = $state(); @@ -125,36 +126,40 @@ {m.common_unit_sell({ unit: 'RAM' })} - -
-

- {m.common_labeled_unit_price({ - unit: `${context.network.chain.systemToken?.symbol.name}/RAM` - })} (KB) -

- - -
- -

{m.ram_to_sell()}

- - -
- -

{m.total_proceeds()}

- - -
- -

{m.common_network_fees()} (0.5%)

- - -
- -

{m.common_expected_receive()}

- -
+
+ +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+
{#if sellRamState.valid}
- +
{/if} diff --git a/src/routes/[network]/(account)/staking/(staking)/unstake/+page.svelte b/src/routes/[network]/(account)/staking/(staking)/unstake/+page.svelte index 924cf90d8..c8837028e 100644 --- a/src/routes/[network]/(account)/staking/(staking)/unstake/+page.svelte +++ b/src/routes/[network]/(account)/staking/(staking)/unstake/+page.svelte @@ -6,7 +6,7 @@ import Button from '$lib/components/button/button.svelte'; import Label from '$lib/components/input/label.svelte'; import TransactionSummary from '$lib/components/transactionSummary.svelte'; - import Descriptionlist from '$lib/components/descriptionlist.svelte'; + import { DL } from '$lib/components/descriptionlist'; import type { UnicoveContext } from '$lib/state/client.svelte'; import { getContext } from 'svelte'; import { UnstakeManager } from './manager.svelte'; @@ -18,7 +18,7 @@ let manager: UnstakeManager = $state(new UnstakeManager(data.network)); let hints = $derived([ - { key: m.staking_withdraw_timeframe(), value: manager.assetValue.toString() } + { title: m.staking_withdraw_timeframe(), description: manager.assetValue.toString() } ]); $effect(() => { @@ -78,6 +78,6 @@ - +
{/if} diff --git a/src/routes/[network]/(dev)/debug/components/sections/tables.svelte b/src/routes/[network]/(dev)/debug/components/sections/tables.svelte index a5f021078..51d297d0b 100644 --- a/src/routes/[network]/(dev)/debug/components/sections/tables.svelte +++ b/src/routes/[network]/(dev)/debug/components/sections/tables.svelte @@ -1,6 +1,7 @@ @@ -50,8 +51,39 @@ row striping here.

+ +
+ + +

+ For displaying a component in the description, or multiple values under a single key, you can + use the components declaratively. +

+ +

+ Note: You need to wrap the child component correctly or you'll get a warning. There are CSS + rules that'll visually yell at you to fix it :) +

+
- +
+ +
+ +
+
+ +
+ +
+
+ +
+
+ + + +
diff --git a/src/routes/[network]/(explorer)/account/[name]/+layout.svelte b/src/routes/[network]/(explorer)/account/[name]/+layout.svelte index 0d007e55d..9fa18876c 100644 --- a/src/routes/[network]/(explorer)/account/[name]/+layout.svelte +++ b/src/routes/[network]/(explorer)/account/[name]/+layout.svelte @@ -14,13 +14,17 @@ { href: `/${network}/account/${account}`, text: 'Overview' }, { href: `/${network}/account/${account}/activity`, text: 'Activity' }, { href: `/${network}/account/${account}/balances`, text: 'Balances' } - // { href: `/${network}/account/${account}/permissions`, text: 'Permissions' }, - // { href: `/${network}/account/${account}/ram`, text: 'RAM' }, - // { href: `/${network}/account/${account}/resources`, text: 'Resources' }, - // { href: `/${network}/account/${account}/staked`, text: 'Staked' }, ]; + if (context.settings.data.advancedMode) { + items.push({ href: `/${network}/account/${account}/votes`, text: 'Votes' }); + } + if (context.settings.data.debugMode) { + items.push({ href: `/${network}/account/${account}/permissions`, text: 'Permissions' }); + items.push({ href: `/${network}/account/${account}/ram`, text: 'RAM' }); + items.push({ href: `/${network}/account/${account}/resources`, text: 'Resources' }); + items.push({ href: `/${network}/account/${account}/staked`, text: 'Staked' }); items.push({ href: `/${network}/account/${account}/chaindata`, text: 'Data' }); } diff --git a/src/routes/[network]/(explorer)/account/[name]/balances/+page.svelte b/src/routes/[network]/(explorer)/account/[name]/balances/+page.svelte index e15a14b32..e51e8c7a8 100644 --- a/src/routes/[network]/(explorer)/account/[name]/balances/+page.svelte +++ b/src/routes/[network]/(explorer)/account/[name]/balances/+page.svelte @@ -32,7 +32,8 @@ Token - Amount + Amount + Value {#if isCurrentUser} {/if} @@ -48,12 +49,17 @@ LOGO {/if} - {balance.asset.symbol.name} + + {balance.asset.symbol.name} + - + + {#if isCurrentUser} {@render tableAction(balance.asset)} {/if} diff --git a/src/routes/[network]/(explorer)/account/[name]/permissions/+page.svelte b/src/routes/[network]/(explorer)/account/[name]/permissions/+page.svelte index 98a6d8e09..161f1b60b 100644 --- a/src/routes/[network]/(explorer)/account/[name]/permissions/+page.svelte +++ b/src/routes/[network]/(explorer)/account/[name]/permissions/+page.svelte @@ -1,12 +1,9 @@ {#if data.account} -
-

Permissions

- {JSON.stringify(data.account.permissions, null, 2)} -
+ {/if} diff --git a/src/routes/[network]/(explorer)/account/[name]/permissions/+page.ts b/src/routes/[network]/(explorer)/account/[name]/permissions/+page.ts new file mode 100644 index 000000000..b3524481a --- /dev/null +++ b/src/routes/[network]/(explorer)/account/[name]/permissions/+page.ts @@ -0,0 +1,45 @@ +import type { Permission } from '@wharfkit/account'; +import type { PageLoad } from './$types'; +import { Name } from '@wharfkit/antelope'; + +export interface TreePermission { + permission: Permission; + children?: TreePermission[]; +} + +function buildTree(data: TreePermission[], parentId = Name.from('')): TreePermission[] { + const tree: TreePermission[] = []; + data.forEach((item) => { + // Check if the item belongs to the current parent + if (item.permission.parent.equals(parentId)) { + // Recursively build the children of the current item + const children = buildTree(data, item.permission.perm_name); + // If children exist, assign them to the current item + if (children.length) { + item.children = children; + } + // Add the current item to the tree + tree.push(item); + } + }); + return tree; +} + +export const load: PageLoad = async ({ params, parent }) => { + const { network, account } = await parent(); + + let tree: TreePermission[] = []; + if (account.permissions) { + const permissionTree = account.permissions.map((p) => ({ permission: p })); + tree = buildTree(permissionTree); + } + + return { + subtitle: `Permissions on the ${network.chain.name} Network.`, + tree: tree, + pageMetaTags: { + title: `Permissions | ${params.name} | ${network.chain.name} Network`, + description: `Permissions for ${params.name} on the ${network.chain.name} network.` + } + }; +}; diff --git a/src/routes/[network]/(explorer)/account/[name]/permissions/permission.svelte b/src/routes/[network]/(explorer)/account/[name]/permissions/permission.svelte new file mode 100644 index 000000000..3ab698494 --- /dev/null +++ b/src/routes/[network]/(explorer)/account/[name]/permissions/permission.svelte @@ -0,0 +1,151 @@ + + +
  • +
    +
    +
    Permission Name
    +
    {permission.perm_name}
    +
    +
    +
    Threshold Required
    +
    {permission.required_auth.threshold}
    +
    + {#if permission.linked_actions} +
    +
    Actions
    + {#each permission.linked_actions as { action, account }} +
    + + + {#if action} + ::{action} + {/if} + +
    + {/each} +
    + {/if} +
    + +
    + {#if anyPermissions} + + + + + + + + + {#if permission.required_auth.keys} + {#each permission.required_auth.keys as { weight, key }} + + + + + + {/each} + {/if} + {#if permission.required_auth.accounts} + {#each permission.required_auth.accounts as { weight, permission: account }} + + + + + + {/each} + {/if} + {#if permission.required_auth.waits} + {#each permission.required_auth.waits as { weight, wait_sec }} + + + + + {/each} + {/if} + +
    WeightAuthorization
    + +{weight.toString()} + + + + +
    + +{weight.toString()} + + + {account} + + + +
    + +{weight.toString()} + + + {wait_sec.toString()}s ({dayjs + .duration(wait_sec.toNumber(), 'seconds') + .humanize()}) +
    + {/if} +
    + + {#if level > 0} +
    + {/if} +
  • + +{#if children} +
  • + +
      0} + class:ml-4={level === 0} + > + + {#each children as child} + + {/each} +
    +
  • +{/if} diff --git a/src/routes/[network]/(explorer)/account/[name]/permissions/permissiontree.svelte b/src/routes/[network]/(explorer)/account/[name]/permissions/permissiontree.svelte new file mode 100644 index 000000000..b92407a3d --- /dev/null +++ b/src/routes/[network]/(explorer)/account/[name]/permissions/permissiontree.svelte @@ -0,0 +1,11 @@ + + +
      + {#each props.permissions as permission} + + {/each} +
    diff --git a/src/routes/[network]/(explorer)/account/[name]/votes/+page.svelte b/src/routes/[network]/(explorer)/account/[name]/votes/+page.svelte new file mode 100644 index 000000000..7dc35b31b --- /dev/null +++ b/src/routes/[network]/(explorer)/account/[name]/votes/+page.svelte @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
    Proxied To + +
    Is Proxy? + {#if data.account.voter.isProxy} + Yes + {:else} + No + {/if} +
    Total Vote Weight + +
    Self Vote Weight + +
    Proxied Weight + +
    + +{#if data.account.voter.votes.length} + + + + + + + + {#each data.account.voter.votes as vote} + + + + {/each} + +
    Block Producer
    + +
    +{/if} diff --git a/src/routes/[network]/(explorer)/block/[number]/+page.svelte b/src/routes/[network]/(explorer)/block/[number]/+page.svelte index 1443c0e8a..1091aeb7a 100644 --- a/src/routes/[network]/(explorer)/block/[number]/+page.svelte +++ b/src/routes/[network]/(explorer)/block/[number]/+page.svelte @@ -4,7 +4,7 @@ import AccountText from '$lib/components/elements/account.svelte'; import Button from '$lib/components/button/button.svelte'; import { ArrowLeftRight, ArrowRight, ArrowLeft } from 'lucide-svelte'; - import { DL, DLRow } from '$lib/components/descriptionlist/index.js'; + import { DD, DL, DLRow } from '$lib/components/descriptionlist/index.js'; import { goto } from '$lib/utils'; let { data } = $props(); @@ -78,22 +78,34 @@
    - {data.details.blockNumber} +
    + {data.details.blockNumber} +
    - +
    + +
    - {data.details.totalCpu} μs +
    + {data.details.totalCpu} μs +
    - {data.details.totalNet * 8} Bytes +
    + {data.details.totalNet * 8} Bytes +
    - {data.details.totalActions} +
    + {data.details.totalActions} +
    - {data.details.blockId} +
    + {data.details.blockId} +
    diff --git a/src/routes/[network]/(explorer)/key/[publicKey]/+page.svelte b/src/routes/[network]/(explorer)/key/[publicKey]/+page.svelte index c1a43677d..88bbf2c95 100644 --- a/src/routes/[network]/(explorer)/key/[publicKey]/+page.svelte +++ b/src/routes/[network]/(explorer)/key/[publicKey]/+page.svelte @@ -1,5 +1,5 @@ - + - - +
    + -
    +

    {pubKey}

    {m.legacy_key()}{': '}{legacyPubKey}

    - +
    {#if data.accounts && data.accounts.length > 0} @@ -33,7 +33,7 @@
  • {/each} @@ -42,4 +42,4 @@

    {m.no_accounts_found()}

    {/if}
    - + diff --git a/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+layout.ts b/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+layout.ts index fbb84cd96..76c59092f 100644 --- a/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+layout.ts +++ b/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+layout.ts @@ -1,4 +1,4 @@ -import { Name, PackedTransaction, PermissionLevel, Serializer } from '@wharfkit/antelope'; +import { Name, PackedTransaction, PermissionLevel } from '@wharfkit/antelope'; import type { LayoutLoad } from './$types'; import { error } from '@sveltejs/kit'; @@ -32,18 +32,6 @@ export const load: LayoutLoad = async ({ fetch, params, parent }) => { ({ level }: { level?: PermissionLevel }) => (level ? PermissionLevel.from(level) : undefined) ); - // TODO: Use ABICache here to prevent duplicate calls - const actions = await Promise.all( - transaction.actions.map(async (a) => { - const { abi } = await network.client.v1.chain.get_abi(String(a.account)); - if (abi) { - const decoded = a.decodeData(abi); - return Serializer.objectify(decoded) as Record; - } - return {}; - }) - ); - return { title: `${params.proposal}`, subtitle: `An MSIG proposed by ${params.proposer} on the ${network.chain.name} Network`, @@ -53,8 +41,8 @@ export const load: LayoutLoad = async ({ fetch, params, parent }) => { name: params.proposal, hash: transaction.id, packed, - transaction, - actions - } + transaction + }, + producers: json.producers }; }; diff --git a/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+page.svelte b/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+page.svelte index cfaa99b14..dfefae6c8 100644 --- a/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+page.svelte +++ b/src/routes/[network]/(explorer)/msig/[proposer]/[proposal]/+page.svelte @@ -1,165 +1,169 @@ - - -

    Requested Approvals

    - -
    -
    -
    - - - - {totalApproved} - - Approved -
    - -
    - - - - {totalRequested} - - Requested + + + +

    Requested Approvals

    + +
    +
    +
    + + + + {manager.totalApproved} + + Approved +
    + +
    + + + + {manager.totalRequested} + + Requested +
    -
    - - - - - - - - - - {#each total_approvals as requested} - - - - +
    ActorPermissionStatus
    {requested.permission} - {#if accountHasApproved(requested)} - Approved - {:else} - Requested - {/if} -
    + + + + + + - {/each} - -
    ActorPermissionRoleStatus
    - + + + {#each manager.participants as participant} + {@const isProducer = data.producers.includes(String(participant.actor))} + {@const isTop21 = top21.includes(String(participant.actor))} + + + {participant.permission} + + {#if isTop21} + Top 21 + {:else if isProducer} + Standby + {:else} + Signer + {/if} + + + {#if manager.accountHasApproved(participant)} + Approved + {:else} + Requested + {/if} + + + {/each} + + + + + +

    Multisig Details

    + +
    + +
    + +
    +
    + +
    + {manager.proposal.name} +
    +
    + +
    + {manager.proposal.transaction.expiration} ({manager.expiresIn}) +
    +
    + +
    + {manager.proposal.hash} +
    +
    +
    + + {#if manager.userIsApprover} + {#if manager.userHasApproved} + + {:else} + + {/if} + {/if} - -

    Multisig Details

    - -
    - - - - - {proposal.name} - - - {proposal.transaction.expiration} ({relativeTimeToExpiry}) - - - {proposal.hash} - -
    - - {#if userIsApprover} - {#if userHasApproved} - manager.cancel()}>Cancel MSIG - {:else} - {/if} - {/if} - - {#if userIsProposer} - - {/if} - -
    + +
    + - +

    Proposed Actions

    - {#each proposal.actions as action} - + {#each manager.actions as action} + {/each}
    - +
    + +{#if context.settings.data.debugMode} +
    {JSON.stringify(manager.actions, null, 2)}
    +{/if}