Skip to content

Commit

Permalink
feat: set & persist explicit account created from the implicit flow a…
Browse files Browse the repository at this point in the history
…s the selected one (#7923)

* refactor(2.0): Dynamic password

* refactor(2.0): Remove Profile Manager (wip)

* clean up and fix

* clean up

* clean up more code

* feat: add account to routes

* feat: add to analytics featureflag

* feat: add account view and multistep

* style: refinements

* feat: rename  files and add locale

* feat: prepare UI step 1

* feat: add locales

* fix: update naming

* feat: update naming and improve code

* fix: move comment

* fix import

* feat: hook logic

* clean up

* adapt and remove for profile-manager related code

* clean up more code

* fmt

* fix: remove accountImplicit route and add to wallet tab

* fix: improve naming

* Update packages/desktop/views/dashboard/wallet/Wallet.svelte

* Update packages/desktop/views/dashboard/wallet/Wallet.svelte

* more cleanup

* fix: add router

* minor fixes

* more cleanup

* ix: add missing dots

* fix: colors

* fix: remove unnecessary function

* fix: rename files

* fix: move if/else and key block

* fix: reset after finish

* small tweaks

* fix: undo changes

* feat: use implicitAccountCreationAddress

* fix: rename and remove onmount

* more cleanup

* format

* update

* clean up

* feat: hook logic, first steps

* fix: add todo

* https://api.testnet.shimmer.network

* revert Wallet.svelte

* clean up

* feat: hook logic in implicit account creation step 3

* clean up

* clean up

* feat: update logic to go next route

* fix: update logic

* feat: remove getNftByIdFromAllAccountNfts debris

* feat: hook implicit account logic step3 done

* feat: create store and persist mainAccountId

* fix: use selectedWalletId

* fix: problem with the merge

* fix: avoid reactive error

* fix: update types, move function and fix store

* fix: improvements

* fix: update logic

* fix: update depositAddress

* fix: update updateMainAccountId function

* fix: add getBlockIssuerAccounts and hasBlockIssuerFeature utils

* fix: update store and conditions

* fix: rename stores

* fix: remove comment

* fix: add condition

* fix: update deposit address

* fix: add comments and move logic to login

* fix: remove code from login and update depositAddress

* feat: update wallet

* feat: remove debris

* feat: update handleNewOutputEvent

* fix: improve getDepositAddress

* feat: fix account output events related to mainAccount

* chore: cleanup

* chore: update build wallet to set first or unset if not owned

* fix: crashes

* fix: missing account output check

* fix: login removing mainAccount

---------

Co-authored-by: marc2332 <mespinsanz@gmail.com>
Co-authored-by: Begoña Álvarez de la Cruz <balvarez@boxfish.studio>
Co-authored-by: cpl121 <cpeon@boxfish.studio>
  • Loading branch information
4 people committed Feb 6, 2024
1 parent ec29877 commit fdff814
Show file tree
Hide file tree
Showing 18 changed files with 164 additions and 92 deletions.
13 changes: 8 additions & 5 deletions packages/desktop/views/dashboard/wallet/Wallet.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
import { selectedWallet } from '@core/wallet'
import { ImplicitAccountCreationView, WalletView } from './views'
$: hasAccount = $selectedWallet?.accountOutputs?.length !== 0
// TODO: replace 3 with 1 when the faucet is fixed
// https://github.com/iotaledger/inx-faucet/issues/122
$: showImplicitAccountFlow =
!$selectedWallet?.accountOutputs?.length && $selectedWallet?.implicitAccountOutputs?.length <= 3
</script>

{#if $selectedWallet}
{#key $selectedWallet?.index}
{#if hasAccount}
<WalletView />
{:else}
{#key $selectedWallet?.id}
{#if showImplicitAccountFlow}
<ImplicitAccountCreationView />
{:else}
<WalletView />
{/if}
{/key}
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
if ($selectedWallet?.implicitAccountOutputs?.length > 0) {
$implicitAccountCreationRoute = ImplicitAccountCreationRoute.FundConfirmation
}
if ($selectedWallet?.hasImplicitAccountCreationTransactionInProgress && $selectedWallet?.isTransferring) {
$implicitAccountCreationRoute = ImplicitAccountCreationRoute.AccountCreation
}
}
</script>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import { Button, FontWeight, PasswordInput, Text, TextType } from 'shared/components'
import { localize } from '@core/i18n'
import { selectedWallet } from '@core/wallet'
import { activeProfile, unlockStronghold } from '@core/profile'
import { selectedWallet, selectedWalletId } from '@core/wallet'
import { activeProfile, unlockStronghold, updateActiveWallet } from '@core/profile'
import { get } from 'svelte/store'
let error = ''
Expand All @@ -20,6 +20,11 @@
await unlockStronghold(strongholdPassword)
const outputId = $selectedWallet?.implicitAccountOutputs[0].outputId
updateActiveWallet($selectedWalletId, {
hasImplicitAccountCreationTransactionInProgress: true,
isTransferring: true,
})
await $selectedWallet?.implicitAccountTransition(outputId)
} catch (err) {
console.error('err', err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import { subscribeToWalletApiEventsForActiveProfile } from './subscribeToWalletA
import { checkAndUpdateActiveProfileNetwork } from './checkAndUpdateActiveProfileNetwork'
import { checkAndRemoveProfilePicture } from './checkAndRemoveProfilePicture'
import { setStrongholdPasswordClearInterval, startBackgroundSync } from '@core/wallet/actions'
import { selectedWallet } from 'shared/lib/core/wallet'

export async function login(loginOptions?: ILoginOptions): Promise<void> {
const loginRouter = get(routerManager).getRouterForAppContext(AppContext.Login)
Expand Down Expand Up @@ -95,11 +94,8 @@ export async function login(loginOptions?: ILoginOptions): Promise<void> {
// Step 7: start background sync
incrementLoginProgress()
subscribeToWalletApiEventsForActiveProfile()
if (get(selectedWallet)?.accountOutputs.length === 0) {
await startBackgroundSync({ syncIncomingTransactions: true, syncImplicitAccounts: true })
} else {
await startBackgroundSync({ syncIncomingTransactions: true })
}

await startBackgroundSync({ syncIncomingTransactions: true, syncImplicitAccounts: true })

// Step 8: finish login
incrementLoginProgress()
Expand Down
39 changes: 32 additions & 7 deletions packages/shared/lib/core/wallet/actions/buildWalletState.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Balance, OutputData } from '@iota/sdk/out/types'
import { IWallet } from '@core/profile/interfaces'
import { AccountAddress, AccountOutput, Balance, OutputData, OutputType } from '@iota/sdk/out/types'
import { updateWalletPersistedDataOnActiveProfile } from '../../profile'
import { IPersistedWalletData } from '../interfaces/persisted-wallet-data.interface'
import { IWalletState } from '../interfaces/wallet-state.interface'
import { IWallet } from '@core/profile/interfaces'
import { getDepositAddress } from '../utils/getDepositAddress'
import { getBech32AddressFromAddressTypes, getBlockIssuerAccounts } from '../utils'

export async function buildWalletState(
wallet: IWallet,
Expand All @@ -29,35 +30,59 @@ export async function buildWalletState(
delegations: [],
}

let depositAddress = ''
let votingPower = ''
let walletOutputs: OutputData[] = []
let accountOutputs: OutputData[] = []
let implicitAccountOutputs: OutputData[] = []
let depositAddress = ''

try {
balances = await wallet.getBalance()
depositAddress = await getDepositAddress(wallet)
votingPower = balances.baseCoin.votingPower
accountOutputs = await wallet.accounts()
// check if the mainAccountId is still valid
if (
walletPersistedData.mainAccountId &&
!accountOutputs.find(
(output) =>
output.output.type === OutputType.Account &&
(output as unknown as AccountOutput).accountId === walletPersistedData.mainAccountId
)
) {
updateWalletPersistedDataOnActiveProfile(wallet.id, { mainAccountId: undefined })
walletPersistedData.mainAccountId = undefined
}
// if there is no mainAccountId, try to set the first account from the block issuer accounts
if (!walletPersistedData.mainAccountId) {
const blockIssuerAccounts = await getBlockIssuerAccounts(wallet)
if (blockIssuerAccounts.length > 0) {
const mainAccountId = (blockIssuerAccounts[0]?.output as AccountOutput)?.accountId
updateWalletPersistedDataOnActiveProfile(wallet.id, { mainAccountId })
walletPersistedData.mainAccountId = mainAccountId
}
}
depositAddress = walletPersistedData.mainAccountId
? getBech32AddressFromAddressTypes(new AccountAddress(walletPersistedData.mainAccountId))
: ''
implicitAccountOutputs = await wallet.implicitAccounts()
walletOutputs = await wallet.outputs()
votingPower = balances.baseCoin.votingPower
} catch (err) {
console.error(err)
}

return {
...wallet,
...walletPersistedData,
depositAddress,
balances,
hasVotingPowerTransactionInProgress: false,
hasVotingTransactionInProgress: false,
hasConsolidatingOutputsTransactionInProgress: false,
hasImplicitAccountCreationTransactionInProgress: false,
isTransferring: false,
votingPower,
walletOutputs,
accountOutputs,
depositAddress,
implicitAccountOutputs,
} as IWalletState
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import {
WalletEvent,
NewOutputWalletEvent,
OutputType,
WalletEventType,
CommonOutput,
UnlockConditionType,
AddressType,
AddressUnlockCondition,
} from '@iota/sdk/out/types'
import { addNftsToDownloadQueue, addOrUpdateNftInAllWalletNfts, buildNftFromNftOutput } from '@core/nfts'
import { checkAndRemoveProfilePicture } from '@core/profile/actions'
import { activeWallets, updateActiveWallet } from '@core/profile'
import { checkAndRemoveProfilePicture, updateActiveWalletPersistedData } from '@core/profile/actions'
import {
ActivityType,
IWrappedOutput,
WalletApiEventHandler,
addActivitiesToWalletActivitiesInAllWalletActivities,
addPersistedAsset,
generateActivities,
getBech32AddressFromAddressTypes,
getOrRequestAssetFromPersistedAssets,
hasBlockIssuerFeature,
preprocessGroupedOutputs,
syncBalance,
validateWalletApiEvent,
getBech32AddressFromAddressTypes,
preprocessGroupedOutputs,
addActivitiesToWalletActivitiesInAllWalletActivities,
WalletApiEventHandler,
updateSelectedWallet,
getDepositAddress,
} from '@core/wallet'
import {
AccountAddress,
AccountOutput,
AddressType,
AddressUnlockCondition,
CommonOutput,
NewOutputWalletEvent,
OutputType,
UnlockConditionType,
WalletEvent,
WalletEventType,
} from '@iota/sdk/out/types'
import { get } from 'svelte/store'
import { activeWallets, updateActiveWallet } from '@core/profile'

export function handleNewOutputEvent(walletId: string): WalletApiEventHandler {
return (error: Error, rawEvent: WalletEvent) => {
Expand All @@ -44,7 +45,7 @@ export async function handleNewOutputEventInternal(walletId: string, payload: Ne
if (!wallet || !outputData) return

const output = outputData.output

const isAccountOutput = output.type === OutputType.Account
const isImplicitAccountOutput =
output.type === OutputType.Basic &&
(output as CommonOutput).unlockConditions.length === 1 &&
Expand All @@ -53,35 +54,15 @@ export async function handleNewOutputEventInternal(walletId: string, payload: Ne
(cmnOutput) => cmnOutput.type === UnlockConditionType.Address
) as AddressUnlockCondition
)?.address.type === AddressType.ImplicitAccountCreation

if (isImplicitAccountOutput) {
const implicitAccounts = await wallet.implicitAccounts()
updateSelectedWallet({
implicitAccountOutputs: implicitAccounts,
})
return
}

const isAccountOutput = output.type === OutputType.Account

if (isAccountOutput) {
const accounts = await wallet.accounts()
const depositAddress = await getDepositAddress(wallet)
updateSelectedWallet({
accountOutputs: accounts,
depositAddress,
})
return
}

const address = getBech32AddressFromAddressTypes(outputData.address)

const isNftOutput = output.type === OutputType.Nft

if (wallet?.depositAddress === address && !outputData?.remainder) {
const address = outputData.address ? getBech32AddressFromAddressTypes(outputData.address) : undefined

if ((address && wallet?.depositAddress === address && !outputData?.remainder) || isAccountOutput) {
await syncBalance(wallet.id)
const walletOutputs = await wallet.outputs()
updateActiveWallet(wallet.id, { walletOutputs })
const accountOutputs = await wallet.accounts()
updateActiveWallet(wallet.id, { walletOutputs, accountOutputs })

const processedOutput = preprocessGroupedOutputs([outputData], payload?.transactionInputs ?? [], wallet)

Expand All @@ -96,7 +77,33 @@ export async function handleNewOutputEventInternal(walletId: string, payload: Ne
}
addActivitiesToWalletActivitiesInAllWalletActivities(wallet.id, activities)
}
if (isImplicitAccountOutput) {
await syncBalance(wallet.id)
const implicitAccountOutputs = await wallet.implicitAccounts()
updateActiveWallet(wallet.id, { implicitAccountOutputs })
}
if (isAccountOutput) {
const accountOutput = output as AccountOutput
// TODO: move to packages/shared/lib/core/wallet/actions/events-handlers/handleTransactionInclusionEvent.ts
// when https://github.com/iotaledger/firefly/pull/7926 is merged and we can have ActivityType.Account

// if we receive the first account output, we set it as the mainAccountId of the wallet
if (
!wallet.mainAccountId &&
wallet?.hasImplicitAccountCreationTransactionInProgress &&
hasBlockIssuerFeature(accountOutput)
) {
const mainAccountId = accountOutput.accountId
updateActiveWalletPersistedData(walletId, {
mainAccountId: mainAccountId,
})
updateActiveWallet(walletId, {
hasImplicitAccountCreationTransactionInProgress: false,
isTransferring: false,
depositAddress: getBech32AddressFromAddressTypes(new AccountAddress(mainAccountId)),
})
}
}
if (isNftOutput) {
const wrappedOutput = outputData as unknown as IWrappedOutput
const nft = buildNftFromNftOutput(wrappedOutput, wallet.depositAddress)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { SpentOutputWalletEvent, WalletEvent, WalletEventType } from '@iota/sdk/out/types'

import { getNftByIdFromAllWalletNfts, updateNftInAllWalletNfts } from '@core/nfts'
import { activeWallets, updateActiveWallet } from '@core/profile'
import {
syncBalance,
ActivityAsyncStatus,
ActivityType,
validateWalletApiEvent,
updateAsyncDataByTransactionId,
allWalletActivities,
WalletApiEventHandler,
allWalletActivities,
syncBalance,
updateAsyncDataByTransactionId,
validateWalletApiEvent,
} from '@core/wallet'
import { get } from 'svelte/store'
import { activeWallets, updateActiveWallet } from '@core/profile'
import { AccountOutput, OutputType, SpentOutputWalletEvent, WalletEvent, WalletEventType } from '@iota/sdk/out/types'
import { nodeInfoProtocolParameters } from 'shared/lib/core/network'
import { getUnixTimestampFromNodeInfoAndSlotIndex } from 'shared/lib/core/network/helpers/getSlotInfoFromNodeProtocolParameters'
import { get } from 'svelte/store'

export function handleSpentOutputEvent(walletId: string): WalletApiEventHandler {
return async (error: Error, rawEvent: WalletEvent) => {
Expand All @@ -30,7 +29,19 @@ export async function handleSpentOutputEventInternal(walletId: string, payload:
await syncBalance(walletId)
if (wallet) {
const walletOutputs = await wallet.outputs()
updateActiveWallet(walletId, { walletOutputs })
const accountOutputs = await wallet.accounts()
const implicitAccountOutputs = await wallet.implicitAccounts()
updateActiveWallet(walletId, { walletOutputs, accountOutputs, implicitAccountOutputs })
if (
wallet.mainAccountId &&
!walletOutputs.find(
(output) =>
output.output.type === OutputType.Account &&
(output as unknown as AccountOutput).accountId === wallet.mainAccountId
)
) {
updateActiveWallet(walletId, { mainAccountId: undefined, depositAddress: '' })
}
}
const outputId = output?.outputId
const activity = get(allWalletActivities)?.[walletId]?.find((_activity) => _activity.outputId === outputId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export interface IPersistedWalletData {
shouldRevote: boolean
removedProposalIds?: ParticipationEventId[]
walletOptions: WalletOptions
mainAccountId?: string | undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface IWalletState extends IWallet, IPersistedWalletData {
hasVotingPowerTransactionInProgress: boolean
hasVotingTransactionInProgress: boolean
hasConsolidatingOutputsTransactionInProgress: boolean
hasImplicitAccountCreationTransactionInProgress: boolean
votingPower: string
walletOutputs: OutputData[]
accountOutputs: OutputData[]
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/lib/core/wallet/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export * from './claimed-activities.store'
export * from './hidden-activities.store'
export * from './mint-nft-details.store'
export * from './mint-token-details.store'
export * from './selected-wallet-main-account.store'
export * from './selected-wallet-main-account-id.store'
export * from './new-transaction-details.store'
export * from './persisted-assets.store'
export * from './selected-wallet-activities.store'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { derived } from 'svelte/store'
import { selectedWallet } from './selected-wallet.store'

export const selectedWalletMainAccountId = derived(
selectedWallet,
($selectedWallet) => $selectedWallet?.mainAccountId ?? null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { derived, Readable } from 'svelte/store'
import { selectedWalletMainAccountId } from './selected-wallet-main-account-id.store'
import { selectedWallet } from './selected-wallet.store'
import { AccountOutput, OutputData } from '@iota/sdk'

export const selectedWalletMainAccount: Readable<OutputData | undefined> = derived(
[selectedWallet, selectedWalletMainAccountId],
([$selectedWallet, $selectedWalletMainAccountId]) =>
$selectedWallet?.accountOutputs?.find(
(account) => (account?.output as AccountOutput)?.accountId === $selectedWalletMainAccountId
)
)
Loading

0 comments on commit fdff814

Please sign in to comment.