Skip to content

Commit

Permalink
Merge tag 'desktop-shimmer-2.1.11' into release/desktop-iota-2.0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
begonaalvarezd committed Feb 6, 2024
2 parents 968a6c2 + 8a8deac commit 932a5dd
Show file tree
Hide file tree
Showing 105 changed files with 1,845 additions and 1,249 deletions.
27 changes: 18 additions & 9 deletions docs/specifications/deep-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@ confirmation on behalf of the user.

## Scheme

The Firefly deep link scheme can be broken down to the following (simple) syntax:
Our system incorporates two specific deeplink schemes—namely IOTA and SHIMMER. Breaking down the Firefly deep link scheme reveals the following simple syntax:


**IOTA**

```
iota[-<stage>]://<context>/<operation>[?param=<param>]
```

**Shimmer**

```
firefly[-<stage>]://<context>/<operation>[?param=<param>]
```

The parameters are as follows:

- `stage` - indicates a specific stage of the app to target, options are:
Expand All @@ -40,15 +49,14 @@ The parameters are as follows:
- `operation` - an operation within a specific context (see below for more detail)
- `param` - query parameter(s) relevant for the specified operation

If you wish to target the production version, simply omit this from the prefix:
To target the production version simply don't specify any stages, example for Shimmer:

```
firefly://
```

:::caution
This deep link scheme is **NOT** compatible with Firefly V1, as that version of the application is in maintenance mode.
:::
This prefix is specifically meant for the production version of Firefly. You don't need to add anything else after ``firefly://``


## Contexts

Expand Down Expand Up @@ -103,7 +111,7 @@ This operation brings the user to the send confirmation popup:
The deep link structure is as follows:

```
firefly://wallet/sendConfirmation?address=<address>&amount=<amount>[&unit=<unit>][&assetId=<assetId>][&metadata=<metadata>][&tag=<tag>][&giftStorageDeposit=<true|false>][&disableToggleGift=<true|false>][&disableChangeExpiration=<true|false>][&surplus=<surplus>]
firefly://wallet/sendConfirmation?address=<address>&amount=<amount>[&unit=<unit>][&assetId=<assetId>][&metadata=<metadata>][&tag=<tag>][&giftStorageDeposit=<true|false>][&disableToggleGift=<true|false>][&disableChangeExpiration=<true|false>][&surplus=<surplus>][&expiration=<expiration>]
```

The following parameters are **required**:
Expand All @@ -115,23 +123,24 @@ The following parameters are **required**:

The following parameters are **optional**:

- `unit` - a specified denomination of the token to use, if applicable (default for IOTA is `Mi`, SMR is `SMR`)
- `unit` - a specified denomination of the token to use, if applicable (default for IOTA is `micro`, SMR is `glow`)
- `assetId` - the identifier of the asset to send, e.g. `4218` (IOTA), `4219` (SMR), or a native token ID (default is base token of the network, i.e. IOTA or SMR)
- `metadata` - a string of text to embed as metadata in the transaction
- `tag` - a string to tag the transaction for indexing purposes
- `giftStorageDeposit` - gifts the tokens used in funding the storage deposit for a transaction
- `disableToggleGift` - prevents the user from being able to toggle the option to gift the storage deposit
- `disableChangeExpiration` - prevents the user from being able to change the expiration time of the transaction
- `surplus` - send additional amounts of the base token when transferring native tokens
- `expiration` - the expiration time of the transaction, e.g. `1w`, `2d`, `5h` or `10m`. Also accepts a UNIX timestamp in milliseconds.

Example:

[!button Click me!](firefly://wallet/sendForm?address=iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx&amount=10&unit=Gi&giftStorageDeposit=true&surplus=1&metadata=Take%20my%20money)
[!button Click me!](firefly://wallet/sendConfirmation?address=iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx&amount=10&unit=Gi&giftStorageDeposit=true&surplus=1&metadata=Take%20my%20money&expiration=1h)

Source:

```
firefly://wallet/sendConfirmation?address=iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx&amount=10&unit=Gi&giftStorageDeposit=true&disableToggleGift=true&surplus=1&metadata=Take%20my%20money
firefly://wallet/sendConfirmation?address=iota1qrhacyfwlcnzkvzteumekfkrrwks98mpdm37cj4xx3drvmjvnep6xqgyzyx&amount=10&unit=Gi&giftStorageDeposit=true&disableToggleGift=true&surplus=1&metadata=Take%20my%20money&expiration=1h
```

### Collectibles
Expand Down
Binary file modified docs/static/send-confirmation-popup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/send-form-popup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions packages/desktop/.sentrycliignore

This file was deleted.

10 changes: 9 additions & 1 deletion packages/desktop/components/modals/AccountActionsMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
modal?.close()
}
function onWithdrawFromL2Click(): void {
openPopup({ id: PopupId.WithdrawFromL2 })
modal?.close()
}
function onVerifyAddressClick(): void {
const ADDRESS_INDEX = 0
checkOrConnectLedger(() => {
Expand Down Expand Up @@ -72,13 +77,16 @@
<Modal bind:this={modal} {...$$restProps}>
<account-actions-menu class="flex flex-col">
<MenuItem icon={Icon.Doc} title={localize('actions.viewBalanceBreakdown')} onClick={onViewBalanceClick} />
{#if $activeProfile?.network?.id === NetworkId.Iota}
{#if $activeProfile?.network?.id === NetworkId.Iota || $activeProfile?.network?.id === NetworkId.IotaAlphanet}
<MenuItem
icon={Icon.Timer}
title={localize('actions.viewAddressHistory')}
onClick={onViewAddressHistoryClick}
/>
{/if}
{#if $activeProfile?.network?.id === NetworkId.Shimmer || $activeProfile?.network?.id === NetworkId.Testnet}
<MenuItem icon={Icon.Transfer} title={localize('actions.withdrawFromL2')} onClick={onWithdrawFromL2Click} />
{/if}
<MenuItem icon={Icon.Customize} title={localize('actions.customizeAcount')} onClick={onCustomiseAccountClick} />
{#if $isActiveLedgerProfile}
<MenuItem
Expand Down
161 changes: 144 additions & 17 deletions packages/desktop/components/popups/AddressHistoryPopup.svelte
Original file line number Diff line number Diff line change
@@ -1,36 +1,151 @@
<script lang="ts">
import { getSelectedAccount } from '@core/account'
import { selectedAccount } from '@core/account'
import { handleError } from '@core/error/handlers/handleError'
import { localize } from '@core/i18n'
import { truncateString } from '@core/utils'
import { CHRONICLE_ADDRESS_HISTORY_ROUTE, CHRONICLE_URLS } from '@core/network/constants/chronicle-urls.constant'
import { fetchWithTimeout } from '@core/nfts'
import { checkActiveProfileAuth, getActiveProfile, updateAccountPersistedDataOnActiveProfile } from '@core/profile'
import { getProfileManager } from '@core/profile-manager/stores'
import { setClipboard, truncateString } from '@core/utils'
import { AccountAddress } from '@iota/sdk/out/types'
import VirtualList from '@sveltejs/svelte-virtual-list'
import { FontWeight, KeyValueBox, Spinner, Text, TextType } from 'shared/components'
import { Button, FontWeight, KeyValueBox, Spinner, Text, TextType } from 'shared/components'
import { onMount } from 'svelte'
let addressList: AccountAddress[] | undefined = undefined
interface AddressHistory {
address: string
items: [
{
milestoneIndex: number
milestoneTimestamp: number
outputId: string
isSpent: boolean
}
]
}
const activeProfile = getActiveProfile()
const ADDRESS_GAP_LIMIT = 20
let knownAddresses: AccountAddress[] = []
$: accountIndex = $selectedAccount?.index
$: network = activeProfile?.network?.id
let searchURL: string
let searchAddressStartIndex = 0
let currentSearchGap = 0
let isBusy = false
function onCopyClick(): void {
const addresses = knownAddresses.map((address) => address.address).join(',')
setClipboard(addresses)
}
onMount(() => {
getSelectedAccount()
?.addresses()
.then((_addressList) => {
addressList = _addressList?.sort((a, b) => a.keyIndex - b.keyIndex) ?? []
})
.catch((err) => {
console.error(err)
addressList = []
})
knownAddresses = $selectedAccount?.knownAddresses
if (!knownAddresses?.length) {
isBusy = true
$selectedAccount
.addresses()
.then((_knownAddresses) => {
knownAddresses = sortAddresses(_knownAddresses)
updateAccountPersistedDataOnActiveProfile(accountIndex, { knownAddresses })
isBusy = false
})
.finally(() => {
isBusy = false
})
}
if (CHRONICLE_URLS[network] && CHRONICLE_URLS[network].length > 0) {
const chronicleRoot = CHRONICLE_URLS[network][0]
searchURL = `${chronicleRoot}${CHRONICLE_ADDRESS_HISTORY_ROUTE}`
} else {
throw new Error(localize('popups.addressHistory.errorNoChronicle'))
}
})
async function isAddressWithHistory(address: string): Promise<boolean> {
try {
const response = await fetchWithTimeout(`${searchURL}${address}`, 3, { method: 'GET' })
const addressHistory: AddressHistory = await response.json()
return addressHistory?.items?.length > 0
} catch (err) {
throw new Error(localize('popups.addressHistory.errorFailedFetch'))
}
}
async function generateNextUnknownAddress(): Promise<[string, number]> {
let nextUnknownAddress: string
try {
do {
nextUnknownAddress = await getProfileManager().generateEd25519Address(
accountIndex,
searchAddressStartIndex
)
searchAddressStartIndex++
} while (knownAddresses.map((accountAddress) => accountAddress.address).includes(nextUnknownAddress))
} catch (err) {
throw new Error(localize('popups.addressHistory.errorFailedGenerate'))
}
return [nextUnknownAddress, searchAddressStartIndex - 1]
}
async function search(): Promise<void> {
currentSearchGap = 0
const tmpKnownAddresses = [...knownAddresses]
while (currentSearchGap < ADDRESS_GAP_LIMIT) {
const [nextAddressToCheck, addressIndex] = await generateNextUnknownAddress()
if (!nextAddressToCheck) {
isBusy = false
break
}
const hasHistory = await isAddressWithHistory(nextAddressToCheck)
if (hasHistory) {
const accountAddress: AccountAddress = {
address: nextAddressToCheck,
keyIndex: addressIndex,
internal: false,
used: true,
}
tmpKnownAddresses.push(accountAddress)
} else {
currentSearchGap++
}
}
knownAddresses = sortAddresses(tmpKnownAddresses)
updateAccountPersistedDataOnActiveProfile(accountIndex, { knownAddresses })
}
async function handleSearchClick(): Promise<void> {
isBusy = true
try {
await checkActiveProfileAuth(search, { stronghold: true, ledger: true })
} catch (err) {
handleError(err)
} finally {
isBusy = false
}
}
function sortAddresses(addresses: AccountAddress[] = []): AccountAddress[] {
return addresses.sort((a, b) => a.keyIndex - b.keyIndex)
}
</script>

<div class="flex flex-col space-y-6">
<Text type={TextType.h3} fontWeight={FontWeight.semibold} lineHeight="6">
{localize('popups.addressHistory.title')}
</Text>
<Text fontSize="15" color="gray-700" classes="text-left">{localize('popups.addressHistory.disclaimer')}</Text>
{#if addressList}
{#if addressList.length > 0}
{#if knownAddresses}
{#if knownAddresses.length > 0}
<div class="w-full flex-col space-y-2 virtual-list-wrapper">
<VirtualList items={addressList} let:item>
<VirtualList items={knownAddresses} let:item>
<div class="mb-1">
<KeyValueBox
isCopyable
Expand Down Expand Up @@ -58,6 +173,18 @@
</div>
{/if}
</div>
<div class="flex flex-row flex-nowrap w-full space-x-4 mt-6">
<div class="flex w-full justify-center pt-8 space-x-4">
<Button outline classes="w-1/2" onClick={onCopyClick}>{localize('actions.copy')}</Button>
<Button
classes="w-1/2"
onClick={handleSearchClick}
disabled={isBusy}
{isBusy}
busyMessage={localize('actions.searching')}>{localize('actions.search')}</Button
>
</div>
</div>

<style lang="scss">
.virtual-list-wrapper :global(svelte-virtual-list-viewport) {
Expand Down
2 changes: 2 additions & 0 deletions packages/desktop/components/popups/Popup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import VestingCollectPopup from './VestingCollectPopup.svelte'
import PayoutDetailsPopup from './PayoutDetailsPopup.svelte'
import VestingRewardsFinderPopup from './VestingRewardsFinderPopup.svelte'
import WithdrawFromL2Popup from './WithdrawFromL2Popup.svelte'
export let id: PopupId
export let props: any
Expand Down Expand Up @@ -144,6 +145,7 @@
[PopupId.VestingCollect]: VestingCollectPopup,
[PopupId.PayoutDetails]: PayoutDetailsPopup,
[PopupId.VestingRewardsFinder]: VestingRewardsFinderPopup,
[PopupId.WithdrawFromL2]: WithdrawFromL2Popup,
}
function onKey(event: KeyboardEvent): void {
Expand Down
Loading

0 comments on commit 932a5dd

Please sign in to comment.