Skip to content

Commit

Permalink
Merge branch 'develop' into feat/allow-connecting-http-nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
evavirseda authored Jan 4, 2024
2 parents 9623388 + b3f64f7 commit 3dae98c
Show file tree
Hide file tree
Showing 52 changed files with 360 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
<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')}
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ export async function buildAccountStateAndPersistedData(
color?: string
): Promise<[IAccountState, IPersistedAccountData]> {
const { index } = account.getMetadata()
const knownAddresses = await account.addresses()
const persistedAccountData: IPersistedAccountData = {
name: name || `${localize('general.account')} ${index + 1}`,
color: color || getRandomAccountColor(),
hidden: false,
shouldRevote: false,
knownAddresses,
}
const accountState = await buildAccountState(account, persistedAccountData)
return [accountState, persistedAccountData]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ParticipationEventId } from '@iota/sdk/out/types'
import { AccountAddress, ParticipationEventId } from '@iota/sdk/out/types'

export interface IPersistedAccountData {
name: string
color: string
hidden: boolean
shouldRevote: boolean
removedProposalIds?: ParticipationEventId[]
knownAddresses: AccountAddress[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NetworkId } from '../enums'

export const CHRONICLE_URLS: Readonly<{ [key in NetworkId]?: string[] }> = {
[NetworkId.Iota]: ['https://chronicle.stardust-mainnet.iotaledger.net/'],
[NetworkId.IotaAlphanet]: ['https://chronicle.alphanet.iotaledger.net/'],
[NetworkId.Shimmer]: ['https://chronicle.shimmer.network/'],
[NetworkId.Testnet]: ['https://chronicle.testnet.shimmer.network/'],
}

export const CHRONICLE_ADDRESS_HISTORY_ROUTE = 'api/explorer/v2/ledger/updates/by-address/'
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function checkAndMigrateChrysalisProfiles(): boolean {
color: account.color,
hidden: chrysalisProfile.hiddenAccounts?.includes(account.id) ?? false,
shouldRevote: false,
knownAddresses: [],
}

if (chrysalisProfile.lastUsedAccountId === account.id) {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/af.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Historial de l'adreça",
"disclaimer": "Llista d'adreces amb fons o reconegudes per aquest perfil",
"indexAndType": "{internal, select, true {Adreça interna} other {Adreça de dipòsit}}{index}",
"internal": "Interna"
"internal": "Interna",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Adressehistorik",
"disclaimer": "Liste over adresser med midler eller kendt af denne profil",
"indexAndType": "{internal, select, true {Intern adresse} other {Indbetalingsadresse}} {index}",
"internal": "Intern"
"internal": "Intern",
"errorNoChronicle": "Historik ikke opsat",
"errorFailedFetch": "Kunne ikke hente adressehistorik",
"errorFailedGenerate": "Kunne ikke generere en ny adresse"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Adressverlauf",
"disclaimer": "Liste der Adressen mit Guthaben bzw. die diesem Profil bekannt sind",
"indexAndType": "{internal, select, true {Interne Adresse} other {Einzahlungsadresse}} {index}",
"internal": "Intern"
"internal": "Intern",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/eo.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/shared/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,11 @@
},
"addressHistory": {
"title": "Address history",
"disclaimer": "List of addresses with funds or known by this profile",
"indexAndType": "{internal, select, true {Internal address} other {Deposit address}} {index}",
"internal": "Internal"
"internal": "Internal",
"errorNoChronicle": "Chronicle not configured",
"errorFailedFetch": "Couldn't fetch address history",
"errorFailedGenerate": "Couldn't generate a new address"
}
},
"charts": {
Expand Down
Loading

0 comments on commit 3dae98c

Please sign in to comment.