diff --git a/packages/desktop/components/popup/popups/EvmTransactionFromDappPopup.svelte b/packages/desktop/components/popup/popups/EvmTransactionFromDappPopup.svelte index a49222b68b..f210d84640 100644 --- a/packages/desktop/components/popup/popups/EvmTransactionFromDappPopup.svelte +++ b/packages/desktop/components/popup/popups/EvmTransactionFromDappPopup.svelte @@ -3,7 +3,7 @@ import { handleError } from '@core/error/handlers' import { IConnectedDapp } from '@auxiliary/wallet-connect/interface' import { CallbackParameters } from '@auxiliary/wallet-connect/types' - import { signAndSendTransactionFromEvm } from '@core/wallet/actions' + import { sendAndPersistTransactionFromEvm, signEvmTransaction } from '@core/wallet/actions' import { selectedAccount } from '@core/account/stores' import { ExplorerEndpoint, IChain, getDefaultExplorerUrl } from '@core/network' import { DappInfo, TransactionAssetSection } from '@ui' @@ -13,6 +13,7 @@ import { calculateEstimatedGasFeeFromTransactionData, calculateMaxGasFeeFromTransactionData, + getHexEncodedTransaction, getMethodNameForEvmTransaction, } from '@core/layer-2' import { getTokenFromSelectedAccountTokens } from '@core/token/stores' @@ -28,17 +29,23 @@ import { BASE_TOKEN_ID } from '@core/token/constants' import { checkActiveProfileAuthAsync } from '@core/profile/actions' import { LedgerAppName } from '@core/ledger' - import { DappVerification } from '@auxiliary/wallet-connect/enums' + import { DappVerification, RpcMethod } from '@auxiliary/wallet-connect/enums' + import { LegacyTransaction } from '@ethereumjs/tx' export let preparedTransaction: EvmTransactionData export let chain: IChain export let dapp: IConnectedDapp - export let signAndSend: boolean export let verifiedState: DappVerification + export let method: RpcMethod.EthSendTransaction | RpcMethod.EthSignTransaction | RpcMethod.EthSendRawTransaction export let callback: (params: CallbackParameters) => void const { id } = chain.getConfiguration() - $: localeKey = signAndSend ? (isSmartContractCall ? 'smartContractCall' : 'sendTransaction') : 'signTransaction' + $: localeKey = + method === RpcMethod.EthSignTransaction + ? 'signTransaction' + : isSmartContractCall + ? 'smartContractCall' + : 'sendTransaction' let nft: Nft | undefined let tokenTransfer: TokenTransferData | undefined @@ -79,6 +86,30 @@ } } + async function getSignedTransaction(): Promise { + if (preparedTransaction?.v && preparedTransaction?.s && preparedTransaction?.r) { + const transaction = LegacyTransaction.fromTxData(preparedTransaction) + return getHexEncodedTransaction(transaction) + } else { + return await signEvmTransaction(preparedTransaction, chain, $selectedAccount) + } + } + + async function signOrSend(): Promise { + const signedTransaction = await getSignedTransaction() + if (method === RpcMethod.EthSignTransaction) { + callback({ result: signedTransaction }) + return + } + const transactionHash = await sendAndPersistTransactionFromEvm( + preparedTransaction, + signedTransaction, + chain, + $selectedAccount + ) + callback({ result: transactionHash }) + } + async function onConfirmClick(): Promise { try { await checkActiveProfileAuthAsync(LedgerAppName.Ethereum) @@ -89,15 +120,11 @@ try { busy = true modifyPopupState({ preventClose: true }) - const response = await signAndSendTransactionFromEvm( - preparedTransaction, - chain, - $selectedAccount, - signAndSend - ) + + await signOrSend() + modifyPopupState({ preventClose: false }, true) busy = false - callback({ result: response }) openPopup({ id: PopupId.SuccessfulDappInteraction, props: { @@ -112,7 +139,7 @@ } } - $: setMethodName(preparedTransaction) + $: void setMethodName(preparedTransaction) async function setMethodName(preparedTransaction: EvmTransactionData): Promise { const result = await getMethodNameForEvmTransaction(preparedTransaction) methodName = result?.startsWith('0x') ? undefined : result diff --git a/packages/desktop/components/popup/popups/SyncAccountsPopup.svelte b/packages/desktop/components/popup/popups/SyncAccountsPopup.svelte index cfc389888a..c06f02376c 100644 --- a/packages/desktop/components/popup/popups/SyncAccountsPopup.svelte +++ b/packages/desktop/components/popup/popups/SyncAccountsPopup.svelte @@ -9,7 +9,7 @@ import { DEFAULT_ACCOUNT_RECOVERY_CONFIGURATION } from '@core/profile' import { RecoverAccountsPayload, recoverAccounts } from '@core/profile-manager' import { checkActiveProfileAuth, getBaseToken, loadAccounts } from '@core/profile/actions' - import { activeAccounts, activeProfile, visibleActiveAccounts } from '@core/profile/stores' + import { activeAccounts, activeProfile, activeProfileId, visibleActiveAccounts } from '@core/profile/stores' import { formatTokenAmountBestMatch } from '@core/token' import { refreshAccountTokensForActiveProfile } from '@core/token/actions' import { closePopup } from '@desktop/auxiliary/popup' @@ -141,7 +141,7 @@ onDestroy(async () => { if (hasUsedWalletFinder) { await refreshAccountTokensForActiveProfile() - await generateAndStoreActivitiesForAllAccounts() + await generateAndStoreActivitiesForAllAccounts($activeProfileId) loadNftsForActiveProfile() } }) diff --git a/packages/desktop/views/dashboard/drawers/dapp-config/components/NetworkSelection.svelte b/packages/desktop/views/dashboard/drawers/dapp-config/components/NetworkSelection.svelte index 7b8b1c2904..4f25837a45 100644 --- a/packages/desktop/views/dashboard/drawers/dapp-config/components/NetworkSelection.svelte +++ b/packages/desktop/views/dashboard/drawers/dapp-config/components/NetworkSelection.svelte @@ -6,6 +6,7 @@ import { localize } from '@core/i18n' import { SupportedNamespaces } from '@auxiliary/wallet-connect/types' import { NetworkId, getChainConfiguration } from '@core/network' + import { SelectionOption } from '@core/utils/interfaces' export let checkedNetworks: string[] export let requiredNamespaces: ProposalTypes.RequiredNamespaces @@ -14,9 +15,10 @@ const localeKey = 'views.dashboard.drawers.dapps.confirmConnection.networks' - let networkSelections: { label: string; value: string; checked: boolean; required: boolean }[] = [] + let requiredNetworks: SelectionOption[] = [] + let optionalNetworks: SelectionOption[] = [] function setNetworkSelections(): void { - const networks = {} + const networks: Record = {} for (const namespace of Object.values(requiredNamespaces)) { for (const chain of namespace.chains) { const chainName = getChainConfiguration(chain as NetworkId)?.name ?? chain @@ -34,18 +36,32 @@ } } } - networkSelections = Object.values(networks) + requiredNetworks = Object.values(networks).filter((network) => network.required) + optionalNetworks = Object.values(networks).filter((network) => !network.required) } - $: checkedNetworks = networkSelections.filter((selection) => selection.checked).map((selection) => selection.value) + $: checkedNetworks = [...requiredNetworks, ...optionalNetworks] + .filter((selection) => selection.checked) + .map((selection) => selection.value) onMount(() => { setNetworkSelections() }) - +
+ {#if requiredNetworks.length} + + {/if} + {#if optionalNetworks.length} + + {/if} +
diff --git a/packages/desktop/views/dashboard/drawers/dapp-config/components/PermissionSelection.svelte b/packages/desktop/views/dashboard/drawers/dapp-config/components/PermissionSelection.svelte index f1060f250c..87e15070c1 100644 --- a/packages/desktop/views/dashboard/drawers/dapp-config/components/PermissionSelection.svelte +++ b/packages/desktop/views/dashboard/drawers/dapp-config/components/PermissionSelection.svelte @@ -7,18 +7,19 @@ import { SupportedNamespaces } from '@auxiliary/wallet-connect/types' import { Text } from '@bloomwalletio/ui' import { getPermissionForMethod } from '@auxiliary/wallet-connect/utils' + import { RpcMethod } from '@auxiliary/wallet-connect/enums' + import { SelectionOption } from '@core/utils/interfaces' export let checkedMethods: string[] export let requiredNamespaces: ProposalTypes.RequiredNamespaces export let optionalNamespaces: ProposalTypes.RequiredNamespaces export let persistedNamespaces: SupportedNamespaces | undefined = undefined - export let permissionSelections: { label: string; value: string; checked: boolean; required: boolean }[] = [] const localeKey = 'views.dashboard.drawers.dapps.confirmConnection.permissions' + let requiredPermissions: SelectionOption[] = [] + let optionalPermissions: SelectionOption[] = [] function setPermissionSelections(): void { - const permissions: { label: string; value: string; checked: boolean; required: boolean }[] = [] - const checkedMethods: { [method: string]: boolean } = {} const addedPermission: { [permission: string]: boolean } = {} @@ -37,7 +38,7 @@ } checkedMethods[method.method] = true - const permission = getPermissionForMethod(method.method) + const permission = getPermissionForMethod(method.method as RpcMethod) if (!permission || addedPermission[permission]) { continue } @@ -47,21 +48,24 @@ ? Object.values(persistedNamespaces).some((namespace) => namespace.methods.includes(method.method)) : true - permissions.push({ + const option = { label: localize(`views.dashboard.drawers.dapps.confirmConnection.permissions.${String(permission)}`), value: permission, checked: isChecked, required: method.required, - }) + } + if (method.required) { + requiredPermissions = [...requiredPermissions, option] + } else { + optionalPermissions = [...optionalPermissions, option] + } } - - permissionSelections = permissions } - $: permissionSelections, (checkedMethods = getMethodsFromCheckedPermissions()) + $: requiredPermissions, optionalPermissions, (checkedMethods = getMethodsFromCheckedPermissions()) function getMethodsFromCheckedPermissions(): string[] { - return permissionSelections + return [...requiredPermissions, ...optionalPermissions] .filter((selection) => selection.checked) .flatMap((selection) => METHODS_FOR_PERMISSION[selection.value]) } @@ -71,12 +75,23 @@ }) -{#if permissionSelections.length} - +{#if requiredPermissions.length || optionalPermissions.length} +
+ {#if requiredPermissions.length} + + {/if} + {#if optionalPermissions.length} + + {/if} +
{:else} {localize(`${localeKey}.title`)} diff --git a/packages/desktop/views/dashboard/drawers/dapp-config/components/Selection.svelte b/packages/desktop/views/dashboard/drawers/dapp-config/components/Selection.svelte index 9519843067..1533e3b390 100644 --- a/packages/desktop/views/dashboard/drawers/dapp-config/components/Selection.svelte +++ b/packages/desktop/views/dashboard/drawers/dapp-config/components/Selection.svelte @@ -10,6 +10,7 @@ }[] export let showPrimary: boolean = false export let title: string + export let disableSelectAll: boolean = false export let error: string | undefined = undefined $: indexOfPrimary = selectionOptions.findIndex((option) => option.checked) @@ -29,10 +30,12 @@
{title} -
- {localize('general.all')} - -
+ {#if !disableSelectAll} +
+ {localize('general.all')} + +
+ {/if}
{#each selectionOptions as option, index} diff --git a/packages/desktop/views/dashboard/drawers/dapp-config/views/ConfirmConnectionDrawer.svelte b/packages/desktop/views/dashboard/drawers/dapp-config/views/ConfirmConnectionDrawer.svelte index c5ed33213a..1fb918bda7 100644 --- a/packages/desktop/views/dashboard/drawers/dapp-config/views/ConfirmConnectionDrawer.svelte +++ b/packages/desktop/views/dashboard/drawers/dapp-config/views/ConfirmConnectionDrawer.svelte @@ -35,7 +35,6 @@ localize(`${localeKey}.accounts.step`), ] - let permissionSelections: { label: string; value: string; checked: boolean; required: boolean }[] = [] let checkedAccounts: IAccountState[] = [] let checkedNetworks: string[] = [] let checkedMethods: string[] = [] @@ -48,7 +47,7 @@ $: isButtonDisabled = loading || - (!persistedNamespaces && currentStep === 0 && permissionSelections.length && checkedMethods.length === 0) || + (!persistedNamespaces && currentStep === 0 && checkedMethods.length === 0) || (currentStep === 1 && checkedNetworks.length === 0) || (currentStep === 2 && checkedAccounts.length === 0) @@ -129,7 +128,6 @@
diff --git a/packages/desktop/views/dashboard/drawers/dapp-config/views/ConnectionRequestDrawer.svelte b/packages/desktop/views/dashboard/drawers/dapp-config/views/ConnectionRequestDrawer.svelte index d02631035e..2572e0122d 100644 --- a/packages/desktop/views/dashboard/drawers/dapp-config/views/ConnectionRequestDrawer.svelte +++ b/packages/desktop/views/dashboard/drawers/dapp-config/views/ConnectionRequestDrawer.svelte @@ -24,6 +24,7 @@ const localeKey = 'views.dashboard.drawers.dapps.connectionRequest' let acceptedInsecureConnection = false + let flash = false $: isVerified = $sessionProposal?.verifyContext.verified.validation === SessionVerification.Valid $: alreadyConnected = !!getPersistedDappNamespacesForDapp($sessionProposal?.params.proposer.metadata.url) $: unsupportedMethods = getUnsupportedMethods($sessionProposal) @@ -109,6 +110,14 @@ } function onContinueClick(): void { + if (!isVerified && !acceptedInsecureConnection) { + flash = true + setTimeout(() => { + flash = false + }, 1500) + return + } + drawerRouter.next() } @@ -181,10 +190,12 @@ {:else if !isVerified}
- + + +
{/if} {:else} @@ -201,12 +212,29 @@ text={localize(`actions.${fulfillsRequirements ? 'reject' : 'cancel'}`)} /> {#if fulfillsRequirements} -
+ + diff --git a/packages/desktop/views/dashboard/governance/components/ProposalAnswer.svelte b/packages/desktop/views/dashboard/governance/components/ProposalAnswer.svelte index b89128bcd9..e58a956e06 100644 --- a/packages/desktop/views/dashboard/governance/components/ProposalAnswer.svelte +++ b/packages/desktop/views/dashboard/governance/components/ProposalAnswer.svelte @@ -1,6 +1,6 @@ {#if status} - +
{#if error} diff --git a/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalAccountVotingPane.svelte b/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalAccountVotingPane.svelte index a79ed2c8f7..dd3fdda96c 100644 --- a/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalAccountVotingPane.svelte +++ b/packages/desktop/views/dashboard/governance/components/proposal-details/ProposalAccountVotingPane.svelte @@ -18,14 +18,13 @@ const { metadata } = $visibleSelectedAccountTokens?.[$activeProfile?.network?.id]?.baseCoin ?? {} let totalVotes = BigInt(0) - const hasMounted = false $: selectedProposalOverview = $participationOverviewForSelectedAccount?.participations?.[$selectedProposal?.id] $: trackedParticipations = Object.values(selectedProposalOverview ?? {}) $: currentMilestone = $networkStatus.currentMilestone // Reactively start updating votes once component has mounted and participation overview is available. - $: hasMounted && $selectedParticipationEventStatus && trackedParticipations && currentMilestone && setTotalVotes() + $: $selectedParticipationEventStatus && trackedParticipations && currentMilestone && setTotalVotes() function setTotalVotes(): void { switch ($selectedParticipationEventStatus?.status) { diff --git a/packages/desktop/views/dashboard/send-flow/views/TransactionSummaryView.svelte b/packages/desktop/views/dashboard/send-flow/views/TransactionSummaryView.svelte index d98bd92b6f..316802fc32 100644 --- a/packages/desktop/views/dashboard/send-flow/views/TransactionSummaryView.svelte +++ b/packages/desktop/views/dashboard/send-flow/views/TransactionSummaryView.svelte @@ -10,8 +10,9 @@ import { createEvmTransactionFromSendFlowParameters, createStardustOutputFromSendFlowParameters, - signAndSendTransactionFromEvm, + sendAndPersistTransactionFromEvm, signAndSendStardustTransaction, + signEvmTransaction, } from '@core/wallet/actions' import { sendFlowParameters } from '@core/wallet/stores' import { getNetworkIdFromSendFlowParameters, validateSendConfirmation } from '@core/wallet/utils' @@ -104,7 +105,9 @@ busy = true modifyPopupState({ preventClose: true }) if (isSourceNetworkLayer2) { - await signAndSendTransactionFromEvm(preparedTransaction, chain, $selectedAccount, true) + const signedTransaction = await signEvmTransaction(preparedTransaction, chain, $selectedAccount) + + await sendAndPersistTransactionFromEvm(preparedTransaction, signedTransaction, chain, $selectedAccount) } else { await signAndSendStardustTransaction(preparedOutput, $selectedAccount) } diff --git a/packages/shared/src/lib/auxiliary/blockscout/api/blockscout.api.ts b/packages/shared/src/lib/auxiliary/blockscout/api/blockscout.api.ts new file mode 100644 index 0000000000..b408a9f773 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/blockscout/api/blockscout.api.ts @@ -0,0 +1,99 @@ +import { NftStandard } from '@core/nfts/enums' +import { TokenStandard } from '@core/token/enums' +import { QueryParameters } from '@core/utils' +import { BaseApi } from '@core/utils/api' +import { DEFAULT_EXPLORER_URLS } from '@core/network/constants' +import { SupportedNetworkId } from '@core/network/enums' +import { IBlockscoutApi, IBlockscoutAsset, IBlockscoutAssetMetadata, IBlockscoutTransaction } from '../interfaces' +import { NetworkId } from '@core/network/types' + +interface INextPageParams { + block_number: number + index: number + items_count: number +} +interface IPaginationResponse { + items: T[] + next_page_params: INextPageParams | null +} + +export type BlockscoutExitFunction = (items: T[]) => boolean + +export class BlockscoutApi extends BaseApi implements IBlockscoutApi { + constructor(networkId: NetworkId) { + const explorerUrl = DEFAULT_EXPLORER_URLS[networkId as SupportedNetworkId] + super(`${explorerUrl}/api/v2`) + } + + private async makePaginatedGetRequest( + path: string, + queryParameters?: QueryParameters, + items: T[] = [], + nextPageParameters?: INextPageParams | null, + exitFunction?: BlockscoutExitFunction + ): Promise { + if (nextPageParameters === null) { + return Promise.resolve(items) + } + if (exitFunction && exitFunction(items)) { + return Promise.resolve(items) + } + return this.get>(path, { ...queryParameters, ...nextPageParameters }).then( + (response) => { + if (!response?.items) { + return Promise.resolve(items) + } + return this.makePaginatedGetRequest( + path, + queryParameters, + items.concat(response.items), + response.next_page_params, + exitFunction + ) + } + ) + } + + async getAssetMetadata(assetAddress: string): Promise { + const response = await this.get(`tokens/${assetAddress}`) + if (response) { + response.type = response.type.replace('-', '') as TokenStandard.Erc20 | NftStandard.Erc721 + return response + } + } + + async getAssetsForAddress( + address: string, + standard: TokenStandard.Erc20 | NftStandard.Erc721 = TokenStandard.Erc20 + ): Promise { + const tokenType = standard.replace('ERC', 'ERC-') + const path = `addresses/${address}/tokens` + const response = await this.get>(path, { type: tokenType }) + if (response) { + return (response?.items ?? []).map((asset) => ({ + ...asset, + token: { + ...asset.token, + type: asset.token.type.replace('-', ''), + }, + })) + } else { + return [] + } + } + + async getTransactionsForAddress( + address: string, + exitFunction?: BlockscoutExitFunction + ): Promise { + const path = `addresses/${address}/transactions` + const items = await this.makePaginatedGetRequest( + path, + undefined, + [], + undefined, + exitFunction + ) + return items + } +} diff --git a/packages/shared/src/lib/auxiliary/blockscout/api/index.ts b/packages/shared/src/lib/auxiliary/blockscout/api/index.ts new file mode 100644 index 0000000000..9c3b613282 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/blockscout/api/index.ts @@ -0,0 +1 @@ +export * from './blockscout.api' diff --git a/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-api.interface.ts b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-api.interface.ts new file mode 100644 index 0000000000..46a5610528 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-api.interface.ts @@ -0,0 +1,14 @@ +import { NftStandard } from '@core/nfts/enums' +import { TokenStandard } from '@core/token/enums' +import { IBlockscoutAsset } from './blockscout-asset.interface' +import { IBlockscoutAssetMetadata } from './blockscout-asset-metadata.interface' +import { IBlockscoutTransaction } from './blockscout-transaction.interface' + +export interface IBlockscoutApi { + getAssetMetadata(assetAddress: string): Promise + getAssetsForAddress( + address: string, + tokenStandard?: TokenStandard.Erc20 | NftStandard.Erc721 + ): Promise + getTransactionsForAddress(address: string): Promise +} diff --git a/packages/shared/src/lib/core/network/interfaces/explorer-asset-metadata.interface.ts b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-asset-metadata.interface.ts similarity index 85% rename from packages/shared/src/lib/core/network/interfaces/explorer-asset-metadata.interface.ts rename to packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-asset-metadata.interface.ts index 1952408e7b..6c67bc5929 100644 --- a/packages/shared/src/lib/core/network/interfaces/explorer-asset-metadata.interface.ts +++ b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-asset-metadata.interface.ts @@ -1,5 +1,5 @@ // snake_case returned by the API -export interface IExplorerAssetMetadata { +export interface IBlockscoutAssetMetadata { address: string circulating_market_cap: string decimals: number diff --git a/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-asset.interface.ts b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-asset.interface.ts new file mode 100644 index 0000000000..5ebb40c5fe --- /dev/null +++ b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-asset.interface.ts @@ -0,0 +1,9 @@ +import { IBlockscoutAssetMetadata } from './blockscout-asset-metadata.interface' + +// snake_case returned by the API +export interface IBlockscoutAsset { + token: IBlockscoutAssetMetadata + token_id: string + token_instance: unknown + value: string +} diff --git a/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-transaction.interface.ts b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-transaction.interface.ts new file mode 100644 index 0000000000..8bcd877195 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/blockscout/interfaces/blockscout-transaction.interface.ts @@ -0,0 +1,94 @@ +import { IBlockscoutAssetMetadata } from './blockscout-asset-metadata.interface' + +interface IFee { + type: 'maximum' | 'actual' + value: string +} + +interface IAddressTag { + address_hash: string + display_name: string + label: string +} + +interface IWatchlistName { + display_name: string + label: string +} + +interface IAddressParam { + hash: string + implementation_name: string + name: string + is_contract: boolean + private_tags: IAddressTag[] + watchlist_names: IWatchlistName[] + public_tags: IAddressTag[] + is_verified: boolean +} + +interface IDecodedInput { + method_call: string + method_id: string + parameters: Record // IDecodedInputParameters +} + +interface ITokenTransfer { + block_hash: string + from: IAddressParam + log_index: string + method: string + timestamp: string + to: IAddressParam + token: IBlockscoutAssetMetadata +} + +enum BlockscoutTransactionType { + TokenTransfer = 'token_transfer', + ContractCreation = 'contract_creation', + ContractCall = 'contract_call', + TokenCreation = 'token_creation', + CoinTransfer = 'coin_transfer', +} + +enum BlockscoutTransactionStatus { + Ok = 'ok', + Error = 'error', +} + +export interface IBlockscoutTransaction { + timestamp: string + fee: IFee + gas_limit: number + block: number + status: BlockscoutTransactionStatus + method: string // e.g transferFrom + confirmations: number + type: number + exchange_rate: string + to: IAddressParam + tx_burnt_fee: string + max_fee_per_gas: string + result: string + hash: string + gas_price: string + priority_fee: string + base_fee_per_gas: string + from: IAddressParam + token_transfers: ITokenTransfer[] + tx_types: BlockscoutTransactionType[] + gas_used: string + created_contract: IAddressParam + position: number + nonce: number + has_error_in_internal_txs: boolean + actions: unknown // TransactionAction + decoded_input: IDecodedInput + token_transfers_overflow: boolean + raw_input: string + value: string + max_priority_fee_per_gas: string + revert_reason: string + confirmation_duration: string + tx_tag: string +} diff --git a/packages/shared/src/lib/auxiliary/blockscout/interfaces/index.ts b/packages/shared/src/lib/auxiliary/blockscout/interfaces/index.ts new file mode 100644 index 0000000000..3a8f009cd6 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/blockscout/interfaces/index.ts @@ -0,0 +1,4 @@ +export * from './blockscout-api.interface' +export * from './blockscout-asset.interface' +export * from './blockscout-asset-metadata.interface' +export * from './blockscout-transaction.interface' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/constants/general-supported-methods.constant.ts b/packages/shared/src/lib/auxiliary/wallet-connect/constants/general-supported-methods.constant.ts index 13f7647d7c..f3d7ba4605 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/constants/general-supported-methods.constant.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/constants/general-supported-methods.constant.ts @@ -1 +1,3 @@ -export const GENERAL_SUPPORTED_METHODS = ['wallet_watchAsset'] +import { RpcMethod } from '../enums' + +export const GENERAL_SUPPORTED_METHODS = [RpcMethod.WalletWatchAsset] diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/constants/methods-for-permissions.constant.ts b/packages/shared/src/lib/auxiliary/wallet-connect/constants/methods-for-permissions.constant.ts index 493583b769..421ef75737 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/constants/methods-for-permissions.constant.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/constants/methods-for-permissions.constant.ts @@ -1,13 +1,14 @@ import { DappPermission } from '../enums/dapp-permission.enum' +import { RpcMethod } from '../enums/rpc-method.enum' export const METHODS_FOR_PERMISSION = { [DappPermission.SignData]: [ - 'eth_sign', - 'personal_sign', - 'eth_signTypedData', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', + RpcMethod.EthSign, + RpcMethod.PersonalSign, + RpcMethod.EthSignTypedData, + RpcMethod.EthSignTypedDataV3, + RpcMethod.EthSignTypedDataV4, ], - [DappPermission.SignTransaction]: ['eth_signTransaction'], - [DappPermission.SendTransaction]: ['eth_sendTransaction', 'eth_sendRawTransaction'], + [DappPermission.SignTransaction]: [RpcMethod.EthSignTransaction], + [DappPermission.SendTransaction]: [RpcMethod.EthSendTransaction, RpcMethod.EthSendRawTransaction], } diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/enums/index.ts b/packages/shared/src/lib/auxiliary/wallet-connect/enums/index.ts index 562d4c75be..a392eab438 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/enums/index.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/enums/index.ts @@ -1,3 +1,4 @@ export * from './dapp-permission.enum' export * from './dapp-verification.enum' +export * from './rpc-method.enum' export * from './wallet-connect-events.enum' diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/enums/rpc-method.enum.ts b/packages/shared/src/lib/auxiliary/wallet-connect/enums/rpc-method.enum.ts new file mode 100644 index 0000000000..e35509b6c4 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/wallet-connect/enums/rpc-method.enum.ts @@ -0,0 +1,11 @@ +export enum RpcMethod { + EthSign = 'eth_sign', + PersonalSign = 'personal_sign', + EthSignTypedData = 'eth_signTypedData', + EthSignTypedDataV3 = 'eth_signTypedData_v3', + EthSignTypedDataV4 = 'eth_signTypedData_v4', + EthSignTransaction = 'eth_signTransaction', + EthSendTransaction = 'eth_sendTransaction', + EthSendRawTransaction = 'eth_sendRawTransaction', + WalletWatchAsset = 'wallet_watchAsset', +} diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/handlers/eth_transaction.handler.ts b/packages/shared/src/lib/auxiliary/wallet-connect/handlers/eth_transaction.handler.ts index ca6141fad8..8150a32607 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/handlers/eth_transaction.handler.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/handlers/eth_transaction.handler.ts @@ -7,13 +7,13 @@ import { EvmTransactionData } from '@core/layer-2' import { switchToRequiredAccount } from '@auxiliary/wallet-connect/utils' import { getSdkError } from '@walletconnect/utils' import { Platform } from '@core/app' -import { DappVerification } from '../enums' +import { DappVerification, RpcMethod } from '../enums' export async function handleEthTransaction( evmTransactionData: EvmTransactionData & { from: string }, dapp: IConnectedDapp, chain: IChain, - signAndSend: boolean, + method: RpcMethod.EthSendTransaction | RpcMethod.EthSignTransaction | RpcMethod.EthSendRawTransaction, responseCallback: (params: CallbackParameters) => void, verifiedState: DappVerification ): Promise { @@ -28,7 +28,7 @@ export async function handleEthTransaction( return } - if (!nonce || !gasPrice || !gasLimit) { + if (nonce === undefined || !gasPrice || !gasLimit) { try { const { nonce, gasPrice, gasLimit } = await buildEvmTransactionData( chain, @@ -61,7 +61,7 @@ export async function handleEthTransaction( chain, dapp, preparedTransaction: evmTransactionData, - signAndSend, + method, verifiedState, callback: responseCallback, onCancel: () => responseCallback({ error: getSdkError('USER_REJECTED') }), diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/handlers/onSessionRequest.handler.ts b/packages/shared/src/lib/auxiliary/wallet-connect/handlers/onSessionRequest.handler.ts index 0f0e5fff51..2c74ea4ce2 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/handlers/onSessionRequest.handler.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/handlers/onSessionRequest.handler.ts @@ -9,14 +9,15 @@ import { handleEthSignTypedData } from './eth_signTypedData.handler' import { handleEthTransaction } from './eth_transaction.handler' import { handleSignMessage } from './sign_message.handler' import { handleWatchAsset } from '@auxiliary/wallet-connect/handlers' -import { DappVerification } from '../enums' +import { DappVerification, RpcMethod } from '../enums' +import { EvmTransactionData, getEvmTransactionFromHexString } from '@core/layer-2' export function onSessionRequest(event: Web3WalletTypes.SessionRequest): void { // We need to call this here, because if the dapp requests too fast after approval, we won't have the dapp in the store yet setConnectedDapps() const { topic, params, id, verifyContext } = event const { request, chainId } = params - const method = request.method + const method = request.method as RpcMethod const dapp = getConnectedDappByOrigin(verifyContext.verified.origin) const verifiedState: DappVerification = verifyContext.verified.isScam @@ -58,23 +59,28 @@ export function onSessionRequest(event: Web3WalletTypes.SessionRequest): void { return } - const signAndSend = method === 'eth_sendTransaction' - switch (method) { - case 'eth_sendTransaction': - case 'eth_signTransaction': - void handleEthTransaction(request.params[0], dapp, chain, signAndSend, returnResponse, verifiedState) + case RpcMethod.EthSendTransaction: + case RpcMethod.EthSignTransaction: + case RpcMethod.EthSendRawTransaction: { + const evmTransactionData: EvmTransactionData & { from: string } = + method === RpcMethod.EthSendRawTransaction + ? getEvmTransactionFromHexString(request.params[0]) + : request.params[0] + + void handleEthTransaction(evmTransactionData, dapp, chain, method, returnResponse, verifiedState) break - case 'eth_sign': - case 'personal_sign': + } + case RpcMethod.EthSign: + case RpcMethod.PersonalSign: void handleSignMessage(request.params, dapp, method, chain, returnResponse, verifiedState) break - case 'eth_signTypedData': - case 'eth_signTypedData_v3': - case 'eth_signTypedData_v4': + case RpcMethod.EthSignTypedData: + case RpcMethod.EthSignTypedDataV3: + case RpcMethod.EthSignTypedDataV4: void handleEthSignTypedData(request.params, method, dapp, chain, returnResponse, verifiedState) break - case 'wallet_watchAsset': + case RpcMethod.WalletWatchAsset: void handleWatchAsset(request.params, dapp, chain, returnResponse) break default: diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/handlers/sign_message.handler.ts b/packages/shared/src/lib/auxiliary/wallet-connect/handlers/sign_message.handler.ts index 95b0c032e9..a4865dd4bf 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/handlers/sign_message.handler.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/handlers/sign_message.handler.ts @@ -6,7 +6,7 @@ import { CallbackParameters } from '../types' import { switchToRequiredAccount } from '../utils' import { getSdkError } from '@walletconnect/utils' import { Platform } from '@core/app' -import { DappVerification } from '../enums' +import { DappVerification, RpcMethod } from '../enums' import { parseSiweMessage, validateSiwe } from '@core/layer-2' import { showNotification } from '@auxiliary/notification' import { localize } from '@core/i18n' @@ -14,7 +14,7 @@ import { localize } from '@core/i18n' export async function handleSignMessage( params: unknown, dapp: IConnectedDapp, - method: 'personal_sign' | 'eth_sign', + method: RpcMethod.PersonalSign | RpcMethod.EthSign, chain: IChain, responseCallback: (params: CallbackParameters) => void, verifiedState: DappVerification @@ -26,8 +26,8 @@ export async function handleSignMessage( // Type for `eth_sign` params: [ address, hexMessage ] // Type for `personal_sign` params: [ hexMessage, address ] - const hexMessage = method === 'personal_sign' ? params[0] : params[1] - const accountAddress = method === 'personal_sign' ? params[1] : params[0] + const hexMessage = method === RpcMethod.PersonalSign ? params[0] : params[1] + const accountAddress = method === RpcMethod.PersonalSign ? params[1] : params[0] if (typeof hexMessage !== 'string') { responseCallback({ error: getSdkError('INVALID_METHOD') }) diff --git a/packages/shared/src/lib/auxiliary/wallet-connect/utils/getPermissionForMethod.ts b/packages/shared/src/lib/auxiliary/wallet-connect/utils/getPermissionForMethod.ts index feaefedc07..bc3aca4bf0 100644 --- a/packages/shared/src/lib/auxiliary/wallet-connect/utils/getPermissionForMethod.ts +++ b/packages/shared/src/lib/auxiliary/wallet-connect/utils/getPermissionForMethod.ts @@ -1,7 +1,7 @@ import { METHODS_FOR_PERMISSION } from '../constants' -import { DappPermission } from '../enums' +import { DappPermission, RpcMethod } from '../enums' -export function getPermissionForMethod(method: string): DappPermission | undefined { +export function getPermissionForMethod(method: RpcMethod): DappPermission | undefined { for (const permission of Object.values(DappPermission)) { const supportedMethods = METHODS_FOR_PERMISSION[permission] ?? [] diff --git a/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAccount.ts b/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAccount.ts index ced967a1c4..73672c13c3 100644 --- a/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAccount.ts +++ b/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAccount.ts @@ -13,6 +13,7 @@ import { NetworkId } from '@core/network' import { setOutgoingAsyncActivitiesToClaimed } from './setOutgoingAsyncActivitiesToClaimed' export async function generateAndStoreActivitiesForAccount( + profileId: string, account: IAccountState, networkId: NetworkId ): Promise { @@ -34,7 +35,7 @@ export async function generateAndStoreActivitiesForAccount( const balanceChangeActivities = await generateActivitiesFromBalanceChanges(account) activities.push(...balanceChangeActivities) - const chainActivities = await generateActivitiesFromChains(account) + const chainActivities = await generateActivitiesFromChains(profileId, account) activities.push(...chainActivities) // Step 4: set account activities with generated activities diff --git a/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAllAccounts.ts b/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAllAccounts.ts index 864599c0b8..52805d78e2 100644 --- a/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAllAccounts.ts +++ b/packages/shared/src/lib/core/activity/actions/generateAndStoreActivitiesForAllAccounts.ts @@ -4,13 +4,13 @@ import { activeAccounts } from '@core/profile/stores' import { generateAndStoreActivitiesForAccount } from './generateAndStoreActivitiesForAccount' import { getActiveNetworkId } from '@core/network' -export async function generateAndStoreActivitiesForAllAccounts(): Promise { +export async function generateAndStoreActivitiesForAllAccounts(profileId: string): Promise { try { const networkId = getActiveNetworkId() const accounts = get(activeAccounts) await Promise.all( - accounts.map((activeAccount) => generateAndStoreActivitiesForAccount(activeAccount, networkId)) + accounts.map((activeAccount) => generateAndStoreActivitiesForAccount(profileId, activeAccount, networkId)) ) } catch (err) { console.error(err) diff --git a/packages/shared/src/lib/core/activity/stores/persisted-transactions.store.ts b/packages/shared/src/lib/core/activity/stores/persisted-transactions.store.ts index fc7d640c41..2a9f66e91d 100644 --- a/packages/shared/src/lib/core/activity/stores/persisted-transactions.store.ts +++ b/packages/shared/src/lib/core/activity/stores/persisted-transactions.store.ts @@ -1,9 +1,6 @@ -import { activeProfileId } from '@core/profile/stores' +import { NetworkId } from '@core/network/types' import { persistent } from '@core/utils/store' -import { get } from 'svelte/store' import { PersistedEvmTransaction } from '../types' -import { NetworkId } from '@core/network/types' -import { IChain } from '@core/network/interfaces' type PersistedEvmTransactions = { [profileId: string]: { @@ -14,40 +11,3 @@ type PersistedEvmTransactions = { } export const persistedEvmTransactions = persistent('evmTransactions', {}) - -export function getPersistedEvmTransactions(accountIndex: number, chain: IChain): PersistedEvmTransaction[] { - const networkId = chain.getConfiguration().id - return get(persistedEvmTransactions)?.[get(activeProfileId)]?.[accountIndex]?.[networkId] ?? [] -} - -export function addPersistedTransaction( - accountIndex: number, - chain: IChain, - ...newTransactions: PersistedEvmTransaction[] -): void { - const networkId = chain.getConfiguration().id - const profileId = get(activeProfileId) - persistedEvmTransactions.update((state) => { - if (!state[profileId]) { - state[profileId] = {} - } - if (!state[profileId][accountIndex]) { - state[profileId][accountIndex] = { - [networkId]: [], - } - } - if (!state[profileId][accountIndex][networkId]) { - state[profileId][accountIndex][networkId] = [] - } - - state[get(activeProfileId)][accountIndex][networkId]?.push(...newTransactions) - return state - }) -} - -export function removePersistedEvmTransactionsForProfile(profileId: string): void { - persistedEvmTransactions.update((state) => { - delete state[profileId] - return state - }) -} diff --git a/packages/shared/src/lib/core/activity/utils/generateActivitiesFromChains.ts b/packages/shared/src/lib/core/activity/utils/generateActivitiesFromChains.ts index 7f485d1b12..b814d9ddd4 100644 --- a/packages/shared/src/lib/core/activity/utils/generateActivitiesFromChains.ts +++ b/packages/shared/src/lib/core/activity/utils/generateActivitiesFromChains.ts @@ -1,21 +1,24 @@ import { IAccountState } from '@core/account' +import { network } from '@core/network' +import { getPersistedTransactionsForChain } from '@core/transactions/stores' +import { get } from 'svelte/store' import { Activity } from '../types' -import { getPersistedEvmTransactions } from '../stores' import { generateActivityFromEvmTransaction } from './generateActivityFromEvmTransaction' -import { get } from 'svelte/store' -import { network } from '@core/network' -export async function generateActivitiesFromChains(account: IAccountState): Promise { +export async function generateActivitiesFromChains(profileId: string, account: IAccountState): Promise { const activities: Activity[] = [] const chains = get(network)?.getChains() ?? [] for (const chain of chains) { - const transactions = getPersistedEvmTransactions(account.index, chain) - for (const transaction of transactions) { + const persistedTransactions = getPersistedTransactionsForChain(profileId, account.index, chain) + for (const transaction of persistedTransactions) { try { - const activity = await generateActivityFromEvmTransaction(transaction, chain, account) - if (activity) { - activities.push(activity) + if (transaction.local) { + // TODO: build activities from persisted transactions + const activity = await generateActivityFromEvmTransaction(transaction.local, chain, account) + if (activity) { + activities.push(activity) + } } } catch (error) { console.error(error) diff --git a/packages/shared/src/lib/core/activity/utils/getActivityTileAction.ts b/packages/shared/src/lib/core/activity/utils/getActivityTileAction.ts index 0c6de07927..0fc3b7213b 100644 --- a/packages/shared/src/lib/core/activity/utils/getActivityTileAction.ts +++ b/packages/shared/src/lib/core/activity/utils/getActivityTileAction.ts @@ -17,7 +17,7 @@ export function getActivityTileAction(activity: Activity): string | undefined { } else if (activity.governanceAction === GovernanceAction.DecreaseVotingPower) { return isConfirmed ? 'general.decreased' : 'general.decreasing' } else if (activity.governanceAction === GovernanceAction.StartVoting) { - return isConfirmed ? 'general.voteStarted' : 'general.voteStarting' + return isConfirmed ? 'general.votedOn' : 'general.votingOn' } else if (activity.governanceAction === GovernanceAction.StopVoting) { return isConfirmed ? 'general.unvoted' : 'general.unvoting' } else if (activity.governanceAction === GovernanceAction.ChangedVote) { diff --git a/packages/shared/src/lib/core/layer-2/actions/checkForUntrackedTokens.ts b/packages/shared/src/lib/core/layer-2/actions/checkForUntrackedTokens.ts index 20106ade3f..a0b7ef96de 100644 --- a/packages/shared/src/lib/core/layer-2/actions/checkForUntrackedTokens.ts +++ b/packages/shared/src/lib/core/layer-2/actions/checkForUntrackedTokens.ts @@ -1,9 +1,10 @@ import { IAccountState } from '@core/account/interfaces' -import { EvmExplorerApi, EvmNetworkId } from '@core/network' +import { EvmNetworkId } from '@core/network' import { getNetwork } from '@core/network/stores' import { TokenStandard, TokenTrackingStatus } from '@core/token' import { addNewTrackedTokenToActiveProfile, hasTokenBeenUntracked } from '@core/wallet/actions' import { BASE_TOKEN_CONTRACT_ADDRESS } from '../constants' +import { BlockscoutApi } from '@auxiliary/blockscout/api' export function checkForUntrackedTokens(account: IAccountState, addPreviouslyUntracked?: boolean): void { const chains = getNetwork()?.getChains() @@ -14,9 +15,9 @@ export function checkForUntrackedTokens(account: IAccountState, addPreviouslyUnt return } const networkId = chain.getConfiguration().id - const explorerApi = new EvmExplorerApi(networkId) + const blockscoutApi = new BlockscoutApi(networkId) - const tokens = await explorerApi.getAssetsForAddress(evmAddress) + const tokens = await blockscoutApi.getAssetsForAddress(evmAddress) const untrackedTokensToTrack = tokens.filter( ({ token }) => addPreviouslyUntracked || !hasTokenBeenUntracked(token.address.toLowerCase(), networkId) ) diff --git a/packages/shared/src/lib/core/layer-2/utils/getEvmTransactionFromHexString.ts b/packages/shared/src/lib/core/layer-2/utils/getEvmTransactionFromHexString.ts new file mode 100644 index 0000000000..6de2b2568f --- /dev/null +++ b/packages/shared/src/lib/core/layer-2/utils/getEvmTransactionFromHexString.ts @@ -0,0 +1,24 @@ +import { Converter } from '@core/utils' +import { LegacyTransaction, TransactionFactory } from '@ethereumjs/tx' +import { EvmTransactionData } from '../types' + +export function getEvmTransactionFromHexString(transactionHex: string): EvmTransactionData & { from: string } { + const transaction = TransactionFactory.fromSerializedData(Converter.hexToBytes(transactionHex)) as LegacyTransaction + const sender = transaction.getSenderAddress().toString() + + const { nonce, gasPrice, gasLimit, to, value, data, v, r, s, type } = transaction + + return { + nonce: Converter.bigIntToHex(nonce), + gasPrice: Converter.bigIntToHex(gasPrice), + gasLimit: Converter.bigIntToHex(gasLimit), + to, + value: Converter.bigIntToHex(value), + data: Converter.bytesToHex(data), + r: Converter.bigIntToHex(r), + v: Converter.bigIntToHex(v), + s: Converter.bigIntToHex(s), + type, + from: sender, + } +} diff --git a/packages/shared/src/lib/core/layer-2/utils/getHexEncodedTransaction.ts b/packages/shared/src/lib/core/layer-2/utils/getHexEncodedTransaction.ts new file mode 100644 index 0000000000..1cff95d72b --- /dev/null +++ b/packages/shared/src/lib/core/layer-2/utils/getHexEncodedTransaction.ts @@ -0,0 +1,8 @@ +import { HEX_PREFIX } from '@core/utils' +import { LegacyTransaction } from '@ethereumjs/tx' + +export function getHexEncodedTransaction(transaction: LegacyTransaction): string { + const serializedTransaction = transaction.serialize() + const hexEncodedTransaction = HEX_PREFIX + Buffer.from(serializedTransaction).toString('hex') + return hexEncodedTransaction +} diff --git a/packages/shared/src/lib/core/layer-2/utils/index.ts b/packages/shared/src/lib/core/layer-2/utils/index.ts index 795568f6df..90f2b1da1c 100644 --- a/packages/shared/src/lib/core/layer-2/utils/index.ts +++ b/packages/shared/src/lib/core/layer-2/utils/index.ts @@ -9,6 +9,8 @@ export * from './getAmountFromEvmTransactionValue' export * from './getEvmTokenMetadata' export * from './getErc20TransferSmartContractData' export * from './getErc721TransferSmartContractData' +export * from './getEvmTransactionFromHexString' +export * from './getHexEncodedTransaction' export * from './getMethodNameForEvmTransaction' export * from './lookupMethodSignature' export * from './parseLayer2Metadata' diff --git a/packages/shared/src/lib/core/network/classes/evm-explorer-api.class.ts b/packages/shared/src/lib/core/network/classes/evm-explorer-api.class.ts deleted file mode 100644 index 9b06c043f5..0000000000 --- a/packages/shared/src/lib/core/network/classes/evm-explorer-api.class.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { NftStandard } from '@core/nfts/enums' -import { TokenStandard } from '@core/token/enums' -import { BaseApi } from '@core/utils/api' - -import { DEFAULT_EXPLORER_URLS } from '../constants' -import { IExplorerApi, IExplorerAsset, IExplorerAssetMetadata } from '../interfaces' -import { NetworkId } from '../types' - -export class EvmExplorerApi extends BaseApi implements IExplorerApi { - constructor(networkId: NetworkId) { - const explorerUrl = DEFAULT_EXPLORER_URLS[networkId] - super(`${explorerUrl}/api/v2`) - } - - async getAssetMetadata(assetAddress: string): Promise { - const response = await this.get(`tokens/${assetAddress}`) - if (response) { - response.type = response.type.replace('-', '') as TokenStandard.Erc20 | NftStandard.Erc721 - return response - } - } - - async getAssetsForAddress( - address: string, - standard: TokenStandard.Erc20 | NftStandard.Erc721 = TokenStandard.Erc20 - ): Promise { - const tokenType = standard.replace('ERC', 'ERC-') - const response = await this.get<{ items: IExplorerAsset[]; next_page_params: unknown }>( - `addresses/${address}/tokens?type=${tokenType}` - ) - if (response) { - return (response?.items ?? []).map((asset) => ({ - ...asset, - token: { - ...asset.token, - type: asset.token.type.replace('-', ''), - }, - })) - } else { - return [] - } - } -} diff --git a/packages/shared/src/lib/core/network/classes/index.ts b/packages/shared/src/lib/core/network/classes/index.ts index bbca6f250e..0710e6657d 100644 --- a/packages/shared/src/lib/core/network/classes/index.ts +++ b/packages/shared/src/lib/core/network/classes/index.ts @@ -1,3 +1,2 @@ -export * from './evm-explorer-api.class' export * from './iscp-chain.class' export * from './stardust-network.class' diff --git a/packages/shared/src/lib/core/network/interfaces/explorer-api.interface.ts b/packages/shared/src/lib/core/network/interfaces/explorer-api.interface.ts deleted file mode 100644 index b219a55a0a..0000000000 --- a/packages/shared/src/lib/core/network/interfaces/explorer-api.interface.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NftStandard } from '@core/nfts/enums' -import { TokenStandard } from '@core/token/enums' -import { IExplorerAsset } from './explorer-asset.interface' -import { IExplorerAssetMetadata } from './explorer-asset-metadata.interface' - -export interface IExplorerApi { - getAssetMetadata(assetAddress: string): Promise - getAssetsForAddress( - address: string, - tokenStandard?: TokenStandard.Erc20 | NftStandard.Erc721 - ): Promise -} diff --git a/packages/shared/src/lib/core/network/interfaces/explorer-asset.interface.ts b/packages/shared/src/lib/core/network/interfaces/explorer-asset.interface.ts deleted file mode 100644 index 9a7c8e10c9..0000000000 --- a/packages/shared/src/lib/core/network/interfaces/explorer-asset.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IExplorerAssetMetadata } from './explorer-asset-metadata.interface' - -// snake_case returned by the API -export interface IExplorerAsset { - token: IExplorerAssetMetadata - token_id: string - token_instance: unknown - value: string -} diff --git a/packages/shared/src/lib/core/network/interfaces/index.ts b/packages/shared/src/lib/core/network/interfaces/index.ts index 71e0401773..6ff8524eba 100644 --- a/packages/shared/src/lib/core/network/interfaces/index.ts +++ b/packages/shared/src/lib/core/network/interfaces/index.ts @@ -6,9 +6,6 @@ export * from './chain.interface' export * from './client-options.interface' export * from './connected-chain.interface' export * from './evm-addresses.interface' -export * from './explorer-api.interface' -export * from './explorer-asset-metadata.interface' -export * from './explorer-asset.interface' export * from './gas-fee-policy.interface' export * from './gas-limits.interface' export * from './network-status.interface' diff --git a/packages/shared/src/lib/core/nfts/actions/checkForUntrackedNfts.ts b/packages/shared/src/lib/core/nfts/actions/checkForUntrackedNfts.ts index 9471b0399b..19fe0c48d5 100644 --- a/packages/shared/src/lib/core/nfts/actions/checkForUntrackedNfts.ts +++ b/packages/shared/src/lib/core/nfts/actions/checkForUntrackedNfts.ts @@ -1,8 +1,7 @@ import { IAccountState } from '@core/account/interfaces' import { ContractType } from '@core/layer-2/enums' -import { EvmExplorerApi } from '@core/network/classes' import { getNetwork } from '@core/network/stores' -import { IChain, IExplorerAsset } from '@core/network/interfaces' +import { IChain } from '@core/network/interfaces' import features from '@features/features' import { NftStandard } from '../enums' @@ -13,6 +12,8 @@ import { addNftsToDownloadQueue } from './addNftsToDownloadQueue' import { Nft } from '../interfaces' import { addNewTrackedNftToActiveProfile } from './addNewTrackedNftToActiveProfile' import { TokenTrackingStatus } from '@core/token' +import { IBlockscoutAsset } from '@auxiliary/blockscout/interfaces' +import { BlockscoutApi } from '@auxiliary/blockscout/api' export async function checkForUntrackedNfts(account: IAccountState): Promise { if (!features?.collectibles?.erc721?.enabled) { @@ -28,9 +29,9 @@ export async function checkForUntrackedNfts(account: IAccountState): Promise { const { token, value } = asset diff --git a/packages/shared/src/lib/core/nfts/actions/loadNftsForActiveProfile.ts b/packages/shared/src/lib/core/nfts/actions/loadNftsForActiveProfile.ts index 12c7f89985..8df381d0c2 100644 --- a/packages/shared/src/lib/core/nfts/actions/loadNftsForActiveProfile.ts +++ b/packages/shared/src/lib/core/nfts/actions/loadNftsForActiveProfile.ts @@ -1,25 +1,27 @@ -import { NftOutput, OutputType } from '@iota/sdk/out/types' +import { getAddressFromAccountForNetwork } from '@core/account' import { IAccountState } from '@core/account/interfaces' -import { activeAccounts } from '@core/profile/stores' +import { ActivityType } from '@core/activity' import { getNftId } from '@core/activity/utils/outputs' +import { getTransferInfoFromTransactionData } from '@core/layer-2/utils/getTransferInfoFromTransactionData' +import { getActiveNetworkId, getNetwork } from '@core/network' +import { activeAccounts, activeProfileId } from '@core/profile/stores' +import { getPersistedTransactionsForChain } from '@core/transactions/stores' import { IWrappedOutput } from '@core/wallet/interfaces' +import { NftOutput, OutputType } from '@iota/sdk/out/types' import { get } from 'svelte/store' import { Nft } from '../interfaces' -import { buildNftFromNftOutput } from './buildNftFromNftOutput' -import { setAccountNftsInAllAccountNfts } from './setAccountNftsInAllAccountNfts' -import { getActiveNetworkId, getNetwork } from '@core/network' -import { ActivityType, getPersistedEvmTransactions } from '@core/activity' -import { getTransferInfoFromTransactionData } from '@core/layer-2/utils/getTransferInfoFromTransactionData' import { buildNftFromPersistedErc721Nft, getNftsFromNftIds } from '../utils' import { addNftsToDownloadQueue } from './addNftsToDownloadQueue' +import { buildNftFromNftOutput } from './buildNftFromNftOutput' import { getPersistedErc721Nfts } from './getPersistedErc721Nfts' -import { getAddressFromAccountForNetwork } from '@core/account' +import { setAccountNftsInAllAccountNfts } from './setAccountNftsInAllAccountNfts' export async function loadNftsForActiveProfile(): Promise { let nftsToDownload: Nft[] = [] + const profileId = get(activeProfileId) const allAccounts = get(activeAccounts) for (const account of allAccounts) { - const accountNfts = await loadNftsForAccount(account) + const accountNfts = await loadNftsForAccount(profileId, account) nftsToDownload = [...nftsToDownload, ...accountNfts] } @@ -27,7 +29,7 @@ export async function loadNftsForActiveProfile(): Promise { void addNftsToDownloadQueue(nftsToDownload) } -export async function loadNftsForAccount(account: IAccountState): Promise { +export async function loadNftsForAccount(profileId: string, account: IAccountState): Promise { const accountNfts: Nft[] = [] const unspentOutputs = await account.unspentOutputs() const networkId = getActiveNetworkId() @@ -40,10 +42,13 @@ export async function loadNftsForAccount(account: IAccountState): Promise for (const chain of getNetwork()?.getChains() ?? []) { // Wrapped L1 NFTs - const transactionsOnChain = getPersistedEvmTransactions(account.index, chain) + const transactionsOnChain = getPersistedTransactionsForChain(profileId, account.index, chain) const nftIdsOnChain = [] for (const transaction of transactionsOnChain) { - const transferInfo = getTransferInfoFromTransactionData(transaction, chain) + if (!transaction.local) { + continue + } + const transferInfo = getTransferInfoFromTransactionData(transaction.local, chain) if (transferInfo?.type !== ActivityType.Nft) { continue } diff --git a/packages/shared/src/lib/core/profile/actions/active-profile/login.ts b/packages/shared/src/lib/core/profile/actions/active-profile/login.ts index 447fd6430e..be09aaa201 100644 --- a/packages/shared/src/lib/core/profile/actions/active-profile/login.ts +++ b/packages/shared/src/lib/core/profile/actions/active-profile/login.ts @@ -25,6 +25,7 @@ import { get } from 'svelte/store' import { ProfileType } from '../../enums' import { ILoginOptions } from '../../interfaces' import { + activeAccounts, activeProfile, getLastLoggedInProfileId, incrementLoginProgress, @@ -42,6 +43,7 @@ import { subscribeToWalletApiEventsForActiveProfile } from './subscribeToWalletA import { disconnectAllDapps } from '@auxiliary/wallet-connect/utils' import { initializeWalletConnect } from '@auxiliary/wallet-connect/actions' import { cleanupOnboarding } from '@contexts/onboarding' +import { fetchAndPersistTransactionsForAccounts } from '@core/transactions/actions' export async function login(loginOptions?: ILoginOptions): Promise { const loginRouter = get(routerManager).getRouterForAppContext(AppContext.Login) @@ -83,7 +85,7 @@ export async function login(loginOptions?: ILoginOptions): Promise { // Step 5: generate and store activities for all accounts incrementLoginProgress() - await generateAndStoreActivitiesForAllAccounts() + await generateAndStoreActivitiesForAllAccounts(_activeProfile.id) if (type === ProfileType.Software) { // Step 6: set initial stronghold status @@ -105,6 +107,7 @@ export async function login(loginOptions?: ILoginOptions): Promise { subscribeToWalletApiEventsForActiveProfile() await startBackgroundSync({ syncIncomingTransactions: true }) fetchL2BalanceForAllAccounts() + void fetchAndPersistTransactionsForAccounts(_activeProfile.id, get(activeAccounts)) // Step 8: finish login incrementLoginProgress() diff --git a/packages/shared/src/lib/core/profile/actions/profiles/removeAllProfileData.ts b/packages/shared/src/lib/core/profile/actions/profiles/removeAllProfileData.ts index 5871946a87..47016e7d06 100644 --- a/packages/shared/src/lib/core/profile/actions/profiles/removeAllProfileData.ts +++ b/packages/shared/src/lib/core/profile/actions/profiles/removeAllProfileData.ts @@ -3,15 +3,15 @@ import { removeClaimedActivitiesForProfile, removeHiddenActivitiesForProfile, removePersistedBalanceChangesForProfile, - removePersistedEvmTransactionsForProfile, } from '@core/activity/stores' import { removePersistedNftsForProfile } from '@core/nfts/stores' import { removePersistedProfile } from '@core/profile/stores' import { removePersistedTokensForProfile } from '@core/token/stores' +import { removePersistedTransactionsForProfile } from '@core/transactions/stores' export function removeAllProfileData(profileId: string): void { removePersistedProfile(profileId) - removePersistedEvmTransactionsForProfile(profileId) + removePersistedTransactionsForProfile(profileId) removePersistedBalanceChangesForProfile(profileId) removeClaimedActivitiesForProfile(profileId) removePersistedNftsForProfile(profileId) diff --git a/packages/shared/src/lib/core/stronghold/utils/signEvmTransactionWithStronghold.ts b/packages/shared/src/lib/core/stronghold/utils/signEvmTransactionWithStronghold.ts index f38d7bf469..c511c32ee7 100644 --- a/packages/shared/src/lib/core/stronghold/utils/signEvmTransactionWithStronghold.ts +++ b/packages/shared/src/lib/core/stronghold/utils/signEvmTransactionWithStronghold.ts @@ -1,5 +1,5 @@ import { LegacyTransaction, TransactionFactory, TypedTxData } from '@ethereumjs/tx' -import { prepareEvmTransaction } from '@core/layer-2/utils' +import { getHexEncodedTransaction, prepareEvmTransaction } from '@core/layer-2/utils' import { EvmChainId, getEvmTransactionOptions } from '@core/network' import { removeLeadingZeros } from '@core/utils/array' import { ECDSASignature } from '@ethereumjs/util' @@ -36,12 +36,6 @@ function createSignedTransaction( return signedTransaction } -function getHexEncodedTransaction(transaction: LegacyTransaction): string { - const serializedTransaction = transaction.serialize() - const hexEncodedTransaction = HEX_PREFIX + Buffer.from(serializedTransaction).toString('hex') - return hexEncodedTransaction -} - function padHexString(str: string): string { return str.length % 2 !== 0 ? '0' + str : str } diff --git a/packages/shared/src/lib/core/tide/apis/tide.api.ts b/packages/shared/src/lib/core/tide/apis/tide.api.ts index 780a88bee4..0312139097 100644 --- a/packages/shared/src/lib/core/tide/apis/tide.api.ts +++ b/packages/shared/src/lib/core/tide/apis/tide.api.ts @@ -1,5 +1,5 @@ import { INftAttribute } from '@core/nfts' -import { BaseApi, buildQueryParametersFromObject } from '@core/utils' +import { BaseApi } from '@core/utils' import { TIDE_API_BASE_URL } from '../constants' import { TideApiEndpoint } from '../enums' import { ITideLeaderboardItem, ITideUserPosition } from '../interfaces' @@ -82,10 +82,8 @@ export class TideApi extends BaseApi { projectId: number, queryParams?: ProjectLeaderboardQueryParams ): Promise { - const path = `${TideApiEndpoint.Project}/${projectId}/leaderboard?${ - queryParams ? buildQueryParametersFromObject(queryParams) : '' - }` - const response = await this.get(path) + const path = `${TideApiEndpoint.Project}/${projectId}/leaderboard` + const response = await this.get(path, queryParams) return response } diff --git a/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts b/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts new file mode 100644 index 0000000000..a3adfb9976 --- /dev/null +++ b/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts @@ -0,0 +1,59 @@ +import { IBlockscoutTransaction } from '@auxiliary/blockscout/interfaces' +import { IAccountState, getAddressFromAccountForNetwork } from '@core/account' +import { addBlockscoutTransactionToPersistedTransactions, isBlockscoutTransactionPersisted } from '../stores' +import { BlockscoutApi } from '@auxiliary/blockscout/api' +import { EvmNetworkId, getNetwork } from '@core/network' + +export async function fetchAndPersistTransactionsForAccounts( + profileId: string, + accounts: IAccountState[] +): Promise { + const chains = getNetwork()?.getChains() ?? [] + for (const chain of chains) { + const networkId = chain.getConfiguration().id as EvmNetworkId + for (const account of accounts) { + try { + const blockscoutTransactions = await fetchBlockscoutTransactionsForAccount( + profileId, + account, + networkId + ) + blockscoutTransactions && + addBlockscoutTransactionToPersistedTransactions( + profileId, + account.index, + networkId, + blockscoutTransactions + ) + } catch (err) { + console.error(err) + } + } + } +} + +function getTransactionsExitFunction( + items: IBlockscoutTransaction[], + profileId: string, + accountIndex: number, + networkId: EvmNetworkId +): boolean { + const lastItem = items[items.length - 1] + return lastItem ? isBlockscoutTransactionPersisted(profileId, accountIndex, networkId, lastItem.hash) : false +} + +async function fetchBlockscoutTransactionsForAccount( + profileId: string, + account: IAccountState, + networkId: EvmNetworkId +): Promise { + const address = getAddressFromAccountForNetwork(account, networkId) + if (!address) { + return undefined + } + const blockscoutApi = new BlockscoutApi(networkId) + const transactions = await blockscoutApi.getTransactionsForAddress(address, (items: IBlockscoutTransaction[]) => + getTransactionsExitFunction(items, profileId, account.index, networkId) + ) + return transactions +} diff --git a/packages/shared/src/lib/core/transactions/actions/index.ts b/packages/shared/src/lib/core/transactions/actions/index.ts new file mode 100644 index 0000000000..aa79746517 --- /dev/null +++ b/packages/shared/src/lib/core/transactions/actions/index.ts @@ -0,0 +1 @@ +export * from './fetchAndPersistTransactionsForAccounts' diff --git a/packages/shared/src/lib/core/transactions/stores/index.ts b/packages/shared/src/lib/core/transactions/stores/index.ts new file mode 100644 index 0000000000..07282a2660 --- /dev/null +++ b/packages/shared/src/lib/core/transactions/stores/index.ts @@ -0,0 +1 @@ +export * from './transactions.store' diff --git a/packages/shared/src/lib/core/transactions/stores/transactions.store.ts b/packages/shared/src/lib/core/transactions/stores/transactions.store.ts new file mode 100644 index 0000000000..9e93583963 --- /dev/null +++ b/packages/shared/src/lib/core/transactions/stores/transactions.store.ts @@ -0,0 +1,126 @@ +import { IBlockscoutTransaction } from '@auxiliary/blockscout/interfaces' +import { PersistedEvmTransaction } from '@core/activity' +import { EvmNetworkId } from '@core/network' +import { IChain } from '@core/network/interfaces' +import { activeProfileId } from '@core/profile/stores' +import { persistent } from '@core/utils/store' +import { get } from 'svelte/store' + +type PersistedTransaction = + | { + blockscout: IBlockscoutTransaction + local: PersistedEvmTransaction + } + | { + blockscout?: IBlockscoutTransaction + local: PersistedEvmTransaction + } + | { + blockscout: IBlockscoutTransaction + local?: PersistedEvmTransaction + } + +type PersistedTransactions = { + [profileId: string]: { + [accountId: string]: { + [networkId in EvmNetworkId]?: { + [transactionHash: string]: PersistedTransaction + } + } + } +} + +export const persistedTransactions = persistent('transactions', {}) + +export function getPersistedTransactionsForChain( + profileId: string, + accountIndex: number, + chain: IChain +): PersistedTransaction[] { + const networkId = chain.getConfiguration().id as EvmNetworkId + return Object.values(get(persistedTransactions)?.[profileId]?.[accountIndex]?.[networkId] ?? {}) ?? [] +} + +export function addLocalTransactionToPersistedTransaction( + profileId: string, + accountIndex: number, + networkId: EvmNetworkId, + newTransactions: PersistedEvmTransaction[] +): void { + persistedTransactions.update((state) => { + if (!state[profileId]) { + state[profileId] = {} + } + if (!state[profileId][accountIndex]) { + state[profileId][accountIndex] = { + [networkId]: {}, + } + } + if (!state[profileId][accountIndex][networkId]) { + state[profileId][accountIndex][networkId] = {} + } + + const _transactions = state[get(activeProfileId)][accountIndex][networkId] ?? {} + for (const transaction of newTransactions) { + const existingTransaction = _transactions?.[transaction.transactionHash.toLowerCase()] + const updatedTransaction: PersistedTransaction = { + ...existingTransaction, + local: transaction, + } + _transactions[transaction.transactionHash.toLowerCase()] = updatedTransaction + } + state[get(activeProfileId)][accountIndex][networkId] = _transactions + + return state + }) +} + +export function addBlockscoutTransactionToPersistedTransactions( + profileId: string, + accountIndex: number, + networkId: EvmNetworkId, + newTransactions: IBlockscoutTransaction[] +): void { + persistedTransactions.update((state) => { + if (!state[profileId]) { + state[profileId] = {} + } + if (!state[profileId][accountIndex]) { + state[profileId][accountIndex] = { + [networkId]: {}, + } + } + if (!state[profileId][accountIndex][networkId]) { + state[profileId][accountIndex][networkId] = {} + } + + const _transactions = state[get(activeProfileId)][accountIndex][networkId] ?? {} + for (const transaction of newTransactions) { + const existingTransaction = _transactions?.[transaction.hash.toLowerCase()] + const updatedTransaction: PersistedTransaction = { + ...existingTransaction, + blockscout: transaction, + } + _transactions[transaction.hash.toLowerCase()] = updatedTransaction + } + state[get(activeProfileId)][accountIndex][networkId] = _transactions + + return state + }) +} + +export function removePersistedTransactionsForProfile(profileId: string): void { + persistedTransactions.update((state) => { + delete state[profileId] + return state + }) +} + +export function isBlockscoutTransactionPersisted( + profileId: string, + accountIndex: number, + networkId: EvmNetworkId, + transactionHash: string +): boolean { + return !!get(persistedTransactions)?.[profileId]?.[accountIndex]?.[networkId]?.[transactionHash]?.blockscout +} diff --git a/packages/shared/src/lib/core/utils/api.ts b/packages/shared/src/lib/core/utils/api.ts index bfffcaf1a8..823c0afca7 100644 --- a/packages/shared/src/lib/core/utils/api.ts +++ b/packages/shared/src/lib/core/utils/api.ts @@ -1,3 +1,6 @@ +import { QueryParameters } from './types' +import { buildQueryParametersFromObject } from './url' + interface IApiRequestOptions { disableCors?: boolean } @@ -9,15 +12,29 @@ export class BaseApi { this._baseUrl = baseUrl } - protected get(path: string, options?: IApiRequestOptions): Promise { - return this.makeRequest(path, '', options) + protected get( + path: string, + queryParameters?: QueryParameters, + options?: IApiRequestOptions + ): Promise { + return this.makeRequest(path, queryParameters, undefined, options) } - protected post(path: string, body: string, options?: IApiRequestOptions): Promise { - return this.makeRequest(path, body, options) + protected post( + path: string, + queryParameters?: QueryParameters, + body?: string, + options?: IApiRequestOptions + ): Promise { + return this.makeRequest(path, queryParameters, body, options) } - private async makeRequest(path: string, body?: string, options?: IApiRequestOptions): Promise { + private async makeRequest( + path: string, + queryParameters?: QueryParameters, + body?: string, + options?: IApiRequestOptions + ): Promise { try { const requestInit: RequestInit = { method: body ? 'POST' : 'GET', @@ -28,6 +45,10 @@ export class BaseApi { ...(body && { body }), ...(options?.disableCors && { mode: 'no-cors' }), } + if (queryParameters && Object.keys(queryParameters).length) { + const queryParametersString = buildQueryParametersFromObject(queryParameters) + path = `${path}?${queryParametersString}` + } const response = await fetch(`${this._baseUrl}/${path}`, requestInit) return (await response.json()) as T } catch (err) { diff --git a/packages/shared/src/lib/core/utils/convert.ts b/packages/shared/src/lib/core/utils/convert.ts index 0193c14580..77f338453f 100644 --- a/packages/shared/src/lib/core/utils/convert.ts +++ b/packages/shared/src/lib/core/utils/convert.ts @@ -233,8 +233,8 @@ export class Converter { return prefix ? HEX_PREFIX + number.toString(16) : number.toString(16) } - public static bigIntToHex(bigInt: bigint, prefix = true): string { - bigInt = BigInt(bigInt) + public static bigIntToHex(bigInt: bigint | undefined, prefix = true): string { + bigInt = BigInt(bigInt ?? 0) return prefix ? HEX_PREFIX + bigInt.toString(16) : bigInt.toString(16) } diff --git a/packages/shared/src/lib/core/utils/interfaces/index.ts b/packages/shared/src/lib/core/utils/interfaces/index.ts index 87db7cb7fa..1b86c04430 100644 --- a/packages/shared/src/lib/core/utils/interfaces/index.ts +++ b/packages/shared/src/lib/core/utils/interfaces/index.ts @@ -1,3 +1,4 @@ export * from './date-difference.interface' export * from './dropdown-item.interface' +export * from './selection-option.interface' export * from './validation-options.interface' diff --git a/packages/shared/src/lib/core/utils/interfaces/selection-option.interface.ts b/packages/shared/src/lib/core/utils/interfaces/selection-option.interface.ts new file mode 100644 index 0000000000..d30557ed48 --- /dev/null +++ b/packages/shared/src/lib/core/utils/interfaces/selection-option.interface.ts @@ -0,0 +1,6 @@ +export interface SelectionOption { + label: string + value: string + checked: boolean + required: boolean +} diff --git a/packages/shared/src/lib/core/utils/types/api.types.ts b/packages/shared/src/lib/core/utils/types/api.types.ts new file mode 100644 index 0000000000..7fb05ab28b --- /dev/null +++ b/packages/shared/src/lib/core/utils/types/api.types.ts @@ -0,0 +1 @@ +export type QueryParameters = Record diff --git a/packages/shared/src/lib/core/utils/types/index.ts b/packages/shared/src/lib/core/utils/types/index.ts index ccaa2d5c5f..deda6f81da 100644 --- a/packages/shared/src/lib/core/utils/types/index.ts +++ b/packages/shared/src/lib/core/utils/types/index.ts @@ -1,3 +1,4 @@ +export * from './api.types' export * from './currencies.type' export * from './duration.type' export * from './exchange-rates.type' diff --git a/packages/shared/src/lib/core/utils/url.ts b/packages/shared/src/lib/core/utils/url.ts index 10dd8a7455..a0a4cf8ee7 100644 --- a/packages/shared/src/lib/core/utils/url.ts +++ b/packages/shared/src/lib/core/utils/url.ts @@ -1,4 +1,5 @@ import { stripSpaces, stripTrailingSlash } from './string' +import { QueryParameters } from './types' export function cleanUrl( url: string, @@ -23,7 +24,7 @@ export function cleanUrl( return cleanedUrl } -export function buildQueryParametersFromObject(obj: Record): string { +export function buildQueryParametersFromObject(obj: QueryParameters): string { return Object.keys(obj) .map( (key) => diff --git a/packages/shared/src/lib/core/wallet/actions/send/index.ts b/packages/shared/src/lib/core/wallet/actions/send/index.ts index 19bd373c0f..895e123b2d 100644 --- a/packages/shared/src/lib/core/wallet/actions/send/index.ts +++ b/packages/shared/src/lib/core/wallet/actions/send/index.ts @@ -2,5 +2,5 @@ export * from './createEvmChainToEvmChainTransaction' export * from './createEvmChainToStardustNetworkTransaction' export * from './createEvmTransactionFromSendFlowParameters' export * from './createStardustOutputFromSendFlowParameters' -export * from './signAndSendTransactionFromEvm' +export * from './sendAndPersistTransactionFromEvm' export * from './signAndSendStardustTransaction' diff --git a/packages/shared/src/lib/core/wallet/actions/send/signAndSendTransactionFromEvm.ts b/packages/shared/src/lib/core/wallet/actions/send/sendAndPersistTransactionFromEvm.ts similarity index 78% rename from packages/shared/src/lib/core/wallet/actions/send/signAndSendTransactionFromEvm.ts rename to packages/shared/src/lib/core/wallet/actions/send/sendAndPersistTransactionFromEvm.ts index 328f40da0a..4c7d5d23a4 100644 --- a/packages/shared/src/lib/core/wallet/actions/send/signAndSendTransactionFromEvm.ts +++ b/packages/shared/src/lib/core/wallet/actions/send/sendAndPersistTransactionFromEvm.ts @@ -1,8 +1,3 @@ -import { addAccountActivity, addPersistedTransaction } from '@core/activity/stores' -import { EvmTransactionData } from '@core/layer-2' -import { IChain } from '@core/network' -import { signEvmTransaction } from '../signEvmTransaction' -import { generateActivityFromEvmTransaction } from '@core/activity/utils/generateActivityFromEvmTransaction' import { Activity, ActivityDirection, @@ -10,24 +5,22 @@ import { PersistedEvmTransaction, calculateAndAddPersistedNftBalanceChange, } from '@core/activity' -import { IAccountState } from '@core/account' -import { updateL2BalanceWithoutActivity } from '../updateL2BalanceWithoutActivity' +import { addAccountActivity } from '@core/activity/stores' +import { generateActivityFromEvmTransaction } from '@core/activity/utils/generateActivityFromEvmTransaction' +import { EvmTransactionData } from '@core/layer-2' +import { EvmNetworkId, IChain } from '@core/network' +import { addLocalTransactionToPersistedTransaction } from '@core/transactions/stores' import { sendSignedEvmTransaction } from '@core/wallet/actions/sendSignedEvmTransaction' +import { updateL2BalanceWithoutActivity } from '../updateL2BalanceWithoutActivity' +import { IAccountState } from '@core/account' -export async function signAndSendTransactionFromEvm( +export async function sendAndPersistTransactionFromEvm( preparedTransaction: EvmTransactionData, + signedTransaction: string, chain: IChain, account: IAccountState, - signAndSend: boolean -): Promise { - const signedTransaction = await signEvmTransaction(preparedTransaction, chain, account) - if (!signedTransaction) { - throw Error('No signed transaction!') - } - if (!signAndSend) { - return signedTransaction - } - + profileId: string +): Promise { const transactionReceipt = await sendSignedEvmTransaction(chain, signedTransaction) if (!transactionReceipt) { throw Error('No transaction receipt!') @@ -40,16 +33,18 @@ export async function signAndSendTransactionFromEvm( ...transactionReceipt, timestamp: Date.now(), } - await persistEvmTransaction(evmTransaction, chain, account) + await persistEvmTransaction(profileId, account, chain, evmTransaction) return transactionReceipt.transactionHash } async function persistEvmTransaction( - evmTransaction: PersistedEvmTransaction, + profileId: string, + account: IAccountState, chain: IChain, - account: IAccountState + evmTransaction: PersistedEvmTransaction ): Promise { - addPersistedTransaction(account.index, chain, evmTransaction) + const networkId = chain.getConfiguration().id as EvmNetworkId + addLocalTransactionToPersistedTransaction(profileId, account.index, networkId, [evmTransaction]) const activity = await generateActivityFromEvmTransaction(evmTransaction, chain, account) if (!activity) { @@ -62,7 +57,7 @@ async function persistEvmTransaction( if (activity.recipient?.type === 'account') { const recipientAccount = activity.recipient.account - addPersistedTransaction(recipientAccount.index, chain, evmTransaction) + addLocalTransactionToPersistedTransaction(profileId, recipientAccount.index, networkId, [evmTransaction]) const receiveActivity = await generateActivityFromEvmTransaction(evmTransaction, chain, recipientAccount) if (!receiveActivity) { return diff --git a/packages/shared/src/lib/core/wallet/actions/sendSignedEvmTransaction.ts b/packages/shared/src/lib/core/wallet/actions/sendSignedEvmTransaction.ts index c76e27685d..2def0eaf84 100644 --- a/packages/shared/src/lib/core/wallet/actions/sendSignedEvmTransaction.ts +++ b/packages/shared/src/lib/core/wallet/actions/sendSignedEvmTransaction.ts @@ -1,6 +1,5 @@ import { TransactionReceipt } from 'web3-core' import { updateSelectedAccount } from '@core/account/stores' -import { handleError } from '@core/error/handlers' import { closePopup } from '../../../../../../desktop/lib/auxiliary/popup' import { IChain } from '@core/network' import { getIsActiveLedgerProfile } from '@core/profile/stores' @@ -17,7 +16,7 @@ export async function sendSignedEvmTransaction( if (getIsActiveLedgerProfile()) { closePopup({ forceClose: true }) } - handleError(err) + throw err } finally { updateSelectedAccount({ isTransferring: false }) } diff --git a/packages/shared/src/lib/core/wallet/actions/signEvmTransaction.ts b/packages/shared/src/lib/core/wallet/actions/signEvmTransaction.ts index 5c3843b7d4..a9712c3bd5 100644 --- a/packages/shared/src/lib/core/wallet/actions/signEvmTransaction.ts +++ b/packages/shared/src/lib/core/wallet/actions/signEvmTransaction.ts @@ -10,7 +10,7 @@ export async function signEvmTransaction( transaction: EvmTransactionData, chain: IChain, account: IAccountState -): Promise { +): Promise { const { chainId, coinType } = chain.getConfiguration() ?? {} const bip44Path = { coinType, @@ -21,14 +21,20 @@ export async function signEvmTransaction( const { index } = account const transactionCopy = structuredClone(transaction) + let signature: string | undefined if (get(isSoftwareProfile)) { // Follow MetaMask's convention around incrementing address indices instead of account indices bip44Path.addressIndex = index - return await signEvmTransactionWithStronghold(transactionCopy, chainId, bip44Path) + signature = await signEvmTransactionWithStronghold(transactionCopy, chainId, bip44Path) } else if (get(isActiveLedgerProfile)) { bip44Path.account = index delete transactionCopy?.estimatedGas - return (await Ledger.signEvmTransaction(transactionCopy, chainId, bip44Path)) as string + signature = (await Ledger.signEvmTransaction(transactionCopy, chainId, bip44Path)) as string } + + if (!signature) { + throw new Error('Failed to sign EVM transaction') + } + return signature } diff --git a/packages/shared/src/locales/de.json b/packages/shared/src/locales/de.json index dd4f20a2c0..7707744f51 100644 --- a/packages/shared/src/locales/de.json +++ b/packages/shared/src/locales/de.json @@ -522,7 +522,8 @@ "permissions": { "step": "Berechtigung", "tip": "Dies sind begrenzte Berechtigungen für die dApp, eine Interaktionsanfrage für Ihre Genehmigung zu senden. Bestätige nur dApps die du kennst und denen du vertraust.", - "title": "Berechtigungen auswählen", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Anfrage zur Signierung von Daten", "signTransaction": "Anfrage zur Signierung einer Transaktion", "sendTransaction": "Anfrage zum Senden einer Transaktion", @@ -531,7 +532,8 @@ }, "networks": { "step": "Netzwerke", - "title": "Netzwerke auswählen", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "Die benötigten Netzwerke werden von der dApp vorausgewählt und können später auch neu konfiguriert werden.", "empty": "Es muss mindestens ein Netzwerk ausgewählt werden" }, @@ -650,24 +652,24 @@ }, "statusTimeline": { "upcoming": { - "past": "Announced", - "present": "Announcement", - "future": "Announcement" + "past": "Angekündigt", + "present": "Ankündigung", + "future": "Ankündigung" }, "commencing": { - "past": "Voting opened", - "present": "Voting open", - "future": "Voting opens" + "past": "Abstimmung begonnen", + "present": "Abstimmung läuft", + "future": "Abstimmung beginnt" }, "holding": { - "past": "Counting started", - "present": "Counting started", - "future": "Counting starts" + "past": "Zählung gestartet", + "present": "Zählung gestartet", + "future": "Zählung beginnt" }, "ended": { - "past": "Counting stopped", - "present": "Counting stopped", - "future": "Counting stops" + "past": "Zählung beendet", + "present": "Zählung beendet", + "future": "Zählung endet" } }, "details": { @@ -686,7 +688,11 @@ "nodeUrl": "Node-URL" }, "fetching": "Abrufen der Abstimmungsdaten", - "hintVote": "Du kannst nicht über eine Abstimmung abstimmen, die sich noch in der Ankündigungsphase befindet. Die Abstimmung wird in {time} geöffnet." + "hintVote": "Du kannst nicht über eine Abstimmung abstimmen, die sich noch in der Ankündigungsphase befindet. Die Abstimmung wird in {time} geöffnet.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -863,7 +869,7 @@ "loadingNodeInfo": "Lade Node-Informationen", "nodeAddress": "Node-Adresse", "removeConfirmation": "Willst du diesen Node wirklich löschen?", - "requiresAuthentication": "Node requires authentication", + "requiresAuthentication": "Node erfordert Authentifizierung", "info": { "general": { "tab": "Allgemein", @@ -899,8 +905,8 @@ "useMetricPrefix": "Metrisches Präfix verwenden" } }, - "jwt": "JSON web token", - "credentials": "Sign-in credentials" + "jwt": "JSON-Web-Token", + "credentials": "Anmeldeinformationen" }, "errorLog": { "title": "Fehlerprotokoll", @@ -973,10 +979,18 @@ }, "siwe": { "title": "Anmelden mit Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", - "resources": "Resources", - "action": "Sign in" + "resources": "Ressourcen", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", + "action": "Einloggen" }, "signTransaction": { "title": "Transaktion signieren", @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Aufruf des Smart Contracts an {contractAddress}", "unableToVerify": "Bloom kann den Smart Contract nicht verifizieren. Fahre nur mit Apps fort, denen du vertraust.", + "viewSmartContract": "View Smart Contract", "action": "Bestätigen", "success": "Aufruf des Smart Contracts an {recipient}" }, @@ -1157,7 +1172,7 @@ "errors": { "invalidMimetype": "Ungültiger MimeTyp, überprüfen Sie, ob der Dateityp unterstützt wird", "quantityTooSmall": "Menge muss größer als Null sein", - "quantityTooLarge": "Quantity needs to be smaller than 127", + "quantityTooLarge": "Menge muss kleiner als 127 sein", "emptyName": "Name ist ein Pflichtfeld", "invalidURI": "Ungültige URL, bitte gib eine gültige URL ein", "notReachable": "URI nicht erreichbar, NFT Typ kann nicht überprüft werden", @@ -1186,7 +1201,7 @@ "title": "Vorschlag hinzufügen", "body": "Bitte gib die unten aufgeführten Informationen an, um einen Vorschlag hinzuzufügen.", "addToAllAccounts": "Vorschlag zu allen Konten hinzufügen", - "addAllProposalsOnNode": "Add all proposals on this node" + "addAllProposalsOnNode": "Alle Vorschläge zu diesem Node hinzufügen" }, "editProposal": { "title": "Vorschlag bearbeiten", @@ -1355,7 +1370,7 @@ "addProposal": "Vorschlag hinzufügen", "removeProposal": "Abstimmung entfernen", "changeNode": "Node wechseln", - "changeNodeUrl": "Change node URL", + "changeNodeUrl": "Node-URL ändern", "stopVoting": "Abstimmen beenden", "revote": "Wiederabstimmen", "skipAndKeep": "Überspringen und altes Passwort behalten", @@ -1365,11 +1380,11 @@ "syncAccounts": "Konten synchronisieren", "importToken": "{type} Token importiert", "syncTokens": "Token-Synchronisation erzwingen", - "mintNftCollection": "Mint NFT collection" + "mintNftCollection": "Minte NFT-Sammlung" }, "general": { "recipient": "Empfänger", - "username": "Username", + "username": "Benutzername", "password": "Passwort", "confirmPassword": "Passwort bestätigen", "currentPassword": "Aktuelles Passwort", @@ -1381,18 +1396,18 @@ "receiving": "Empfange", "newVotingPower": "Neues Stimmrecht", "votingPower": "Stimmrecht", - "increased": "Stimmrecht erhöht", - "increasing": "Erhöhe Stimmrecht", - "decreased": "Stimmrecht verringert", - "decreasing": "Verringere Stimmrecht", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Abgestimmt", "voting": "Abstimmung", "changedVote": "Stimme geändert", "changingVote": "Stimme ändern", "revoted": "Erneut abgestimmt", "revoting": "Erneut abstimmen", - "unvoted": "Nicht abgestimmt", - "unvoting": "Stimme zurücknehmen", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transferiert", "transferring": "Transferiere", "shimmerClaimed": "Beansprucht", @@ -1531,6 +1546,7 @@ "contractFunction": "Vertragsfunktion", "standard": "Standard", "uri": "URI", + "image": "Image", "issuer": "Aussteller", "issuerAddress": "Ausstelleradresse", "issuerName": "Name des Ausstellers", @@ -1587,8 +1603,8 @@ "governance": "Governance", "baseCoin": "Basis-Token", "unverifiedContract": "Unverifizierter Vertrag", - "startIndex": "Start index", - "mintNftCollectionDescription": "Mint collection NFT in Alias Output" + "startIndex": "Start-Index", + "mintNftCollectionDescription": "Minte Sammlungs-NFT im Alias-Output" }, "filters": { "title": "Filter", @@ -1797,7 +1813,7 @@ "success": "Kontaktadressen erfolgreich aktualisiert" }, "siwe": { - "rejected": "Invalid SIWE request" + "rejected": "Ungültige SIWE-Anfrage" }, "syncTokens": { "success": "Token erfolgreich synchronisiert" @@ -2073,7 +2089,7 @@ "attributes": "Optionaler Parameter: Eine Reihe von Merkmalen und Werten, die Attribute der NFT definieren", "uri": "Um eine URI mittels benutzerdefinierter Medien zu erstellen, laden Sie zuerst Ihre Datei über einen Speicherdienst auf IPFS hoch (z.B. https://nft.storage/)", "quantity": "Optionaler Parameter: Die Anzahl der mit diesen Metadaten gemintete Kopien.", - "startIndex": "Optional parameter: The start index of the NFTs to mint." + "startIndex": "Optionaler Parameter: Der Start-Index der zu mintenden NFTs." }, "governance": { "removeProposalWarning": "Du musst das Abstimmen für diesen Vorschlag stoppen, bevor du sie entfernen kannst.", @@ -2099,7 +2115,7 @@ "collectibles": "Sammlerstücke", "governance": "Governance", "campaigns": "Kampagnen", - "buySell": "Buy IOTA", + "buySell": "Kaufe IOTA", "developer": "Entwickler", "tokens": "Token", "settings": "Einstellungen" @@ -2134,10 +2150,10 @@ "appNotOpen": "App nicht geöffnet" }, "dappVerification": { - "valid": "Verified", - "invalid": "Domain Mismatch", - "unknown": "Cannot verify", - "scam": "Security Risk" + "valid": "Verifiziert", + "invalid": "Domain stimmt nicht überein", + "unknown": "Verifizierung nicht möglich", + "scam": "Sicherheitsrisiko" } }, "menus": { diff --git a/packages/shared/src/locales/en.json b/packages/shared/src/locales/en.json index ec696f6156..b3f1c6ef70 100644 --- a/packages/shared/src/locales/en.json +++ b/packages/shared/src/locales/en.json @@ -524,7 +524,8 @@ "permissions": { "step": "Permission", "tip": "These are limited permissions for the dApp to send an interaction request for your approval. Only approve apps you know and trust.", - "title": "Select permissions", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Request to sign data", "signTransaction": "Request to sign a transaction", "sendTransaction": "Request to send a transaction", @@ -533,7 +534,8 @@ }, "networks": { "step": "Networks", - "title": "Select networks", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "The required networks are pre-selected for you by the dApp and can also be reconfigured later.", "empty": "You have to select at least one network" }, @@ -1083,19 +1085,19 @@ "Conflicting": "Failed to decrease voting power" }, "startVoting": { - "Pending": "Voting for {proposalName}", - "Confirmed": "Voted for {proposalName}", - "Conflicting": "Failed to vote for" + "Pending": "Voting on {proposalName}", + "Confirmed": "Voted on {proposalName}", + "Conflicting": "Failed to vote on" }, "stopVoting": { - "Pending": "Stopping voting for {proposalName}", - "Confirmed": "Stopped voting for {proposalName}", - "Conflicting": "Failed to stop voting for" + "Pending": "Stopping voting on {proposalName}", + "Confirmed": "Stopped voting on {proposalName}", + "Conflicting": "Failed to stop voting on" }, "changedVote": { - "Pending": "Changing vote for {proposalName}", - "Confirmed": "Changed vote for {proposalName}", - "Conflicting": "Failed to change vote for" + "Pending": "Changing vote on {proposalName}", + "Confirmed": "Changed vote on {proposalName}", + "Conflicting": "Failed to change vote on" }, "revote": { "Pending": "Revoting", @@ -1217,7 +1219,7 @@ "revote": { "title": "Revote on proposals", "body": "Changing your voting power temporarily stops your votes for all proposals from being counted. Simply click \"Revote\" to resume voting.", - "hint": "Bloom remembers all of the proposals and answer options you previously voted for." + "hint": "Bloom remembers all the proposals and answer options you previously voted for." }, "removeProposal": { "title": "Remove proposal", @@ -1402,6 +1404,8 @@ "decreasing": "Decreasing voting", "voted": "Voted", "voting": "Voting", + "votingOn": "Voting on", + "votedOn": "Voted on", "changedVote": "Changed vote", "changingVote": "Changing vote", "revoted": "Revoted", diff --git a/packages/shared/src/locales/es-ES.json b/packages/shared/src/locales/es-ES.json index 4b28084b20..87c7dbe40f 100644 --- a/packages/shared/src/locales/es-ES.json +++ b/packages/shared/src/locales/es-ES.json @@ -522,7 +522,8 @@ "permissions": { "step": "Permission", "tip": "These are limited permissions for the dApp to send an interaction request for your approval. Only approve apps you know and trust.", - "title": "Select permissions", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Request to sign data", "signTransaction": "Request to sign a transaction", "sendTransaction": "Request to send a transaction", @@ -531,7 +532,8 @@ }, "networks": { "step": "Networks", - "title": "Select networks", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "The required networks are pre-selected for you by the dApp and can also be reconfigured later.", "empty": "You have to select at least one network" }, @@ -686,7 +688,11 @@ "nodeUrl": "URL del nodo" }, "fetching": "Obteniendo datos de la propuesta", - "hintVote": "No puede votar sobre una propuesta que está en la fase de anuncio, la votación se abrirá en {time}." + "hintVote": "No puede votar sobre una propuesta que está en la fase de anuncio, la votación se abrirá en {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -973,9 +979,17 @@ }, "siwe": { "title": "Sign-In with Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", "resources": "Resources", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", "action": "Sign in" }, "signTransaction": { @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Smart contract call to {contractAddress}", "unableToVerify": "Bloom is unable to verify the smart contract call. Only proceed with apps you trust.", + "viewSmartContract": "View Smart Contract", "action": "Confirm", "success": "Smart contract call to {recipient}" }, @@ -1381,18 +1396,18 @@ "receiving": "Recibiendo", "newVotingPower": "Nuevo peso de voto", "votingPower": "Peso de voto", - "increased": "Peso de voto incrementado", - "increasing": "Incrementando peso de voto", - "decreased": "Peso de voto disminuido", - "decreasing": "Disminuyendo peso de voto", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Votado", "voting": "Votación", "changedVote": "Voto cambiado", "changingVote": "Cambiando voto", "revoted": "Votado de nuevo", "revoting": "Votando de nuevo", - "unvoted": "Sin votar", - "unvoting": "No votar", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transferred", "transferring": "Transferring", "shimmerClaimed": "Reclamado", @@ -1531,6 +1546,7 @@ "contractFunction": "Función de contrato", "standard": "Estándar", "uri": "URI", + "image": "Image", "issuer": "Emisor", "issuerAddress": "Dirección del emisor", "issuerName": "Nombre del emisor", diff --git a/packages/shared/src/locales/fr.json b/packages/shared/src/locales/fr.json index ffab834bd8..2e9ef3dc0f 100644 --- a/packages/shared/src/locales/fr.json +++ b/packages/shared/src/locales/fr.json @@ -522,7 +522,8 @@ "permissions": { "step": "Permission", "tip": "These are limited permissions for the dApp to send an interaction request for your approval. Only approve apps you know and trust.", - "title": "Select permissions", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Request to sign data", "signTransaction": "Request to sign a transaction", "sendTransaction": "Request to send a transaction", @@ -531,7 +532,8 @@ }, "networks": { "step": "Networks", - "title": "Select networks", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "The required networks are pre-selected for you by the dApp and can also be reconfigured later.", "empty": "You have to select at least one network" }, @@ -686,7 +688,11 @@ "nodeUrl": "Node URL" }, "fetching": "Récupération des données de la proposition", - "hintVote": "Vous ne pouvez pas voter sur une proposition qui est en phase d'annonce, le vote s'ouvrira dans {time}." + "hintVote": "Vous ne pouvez pas voter sur une proposition qui est en phase d'annonce, le vote s'ouvrira dans {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -973,9 +979,17 @@ }, "siwe": { "title": "Sign-In with Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", "resources": "Resources", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", "action": "Sign in" }, "signTransaction": { @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Smart contract call to {contractAddress}", "unableToVerify": "Bloom is unable to verify the smart contract call. Only proceed with apps you trust.", + "viewSmartContract": "View Smart Contract", "action": "Confirm", "success": "Smart contract call to {recipient}" }, @@ -1381,18 +1396,18 @@ "receiving": "Réception en cours", "newVotingPower": "Nouveau pouvoir de vote", "votingPower": "Pouvoir de vote", - "increased": "Augmentation du pouvoir de vote", - "increasing": "Augmentation du pouvoir de vote", - "decreased": "Diminution du pouvoir de vote", - "decreasing": "Diminuer le pouvoir de vote", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "A voté", "voting": "Vote", "changedVote": "Vote modifié", "changingVote": "Changement du vote", "revoted": "Voté à nouveau", "revoting": "Revoting", - "unvoted": "Arrêt du vote", - "unvoting": "Arrêt du vote", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transferred", "transferring": "Transferring", "shimmerClaimed": "Claimed", @@ -1531,6 +1546,7 @@ "contractFunction": "Contract function", "standard": "Standard", "uri": "URI", + "image": "Image", "issuer": "Issuer", "issuerAddress": "Issuer Address", "issuerName": "Issuer Name", diff --git a/packages/shared/src/locales/it.json b/packages/shared/src/locales/it.json index d70a96145f..51591f0dca 100644 --- a/packages/shared/src/locales/it.json +++ b/packages/shared/src/locales/it.json @@ -522,7 +522,8 @@ "permissions": { "step": "Permission", "tip": "These are limited permissions for the dApp to send an interaction request for your approval. Only approve apps you know and trust.", - "title": "Select permissions", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Request to sign data", "signTransaction": "Request to sign a transaction", "sendTransaction": "Request to send a transaction", @@ -531,7 +532,8 @@ }, "networks": { "step": "Networks", - "title": "Select networks", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "The required networks are pre-selected for you by the dApp and can also be reconfigured later.", "empty": "You have to select at least one network" }, @@ -686,7 +688,11 @@ "nodeUrl": "URL del nodo" }, "fetching": "Recupero dati proposta", - "hintVote": "Non puoi votare una proposta nella fase di annuncio, il voto sarà aperto tra {time}." + "hintVote": "Non puoi votare una proposta nella fase di annuncio, il voto sarà aperto tra {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -973,9 +979,17 @@ }, "siwe": { "title": "Sign-In with Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", "resources": "Resources", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", "action": "Sign in" }, "signTransaction": { @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Smart contract call to {contractAddress}", "unableToVerify": "Bloom is unable to verify the smart contract call. Only proceed with apps you trust.", + "viewSmartContract": "View Smart Contract", "action": "Confirm", "success": "Smart contract call to {recipient}" }, @@ -1381,18 +1396,18 @@ "receiving": "Ricezione", "newVotingPower": "Nuovo potere di voto", "votingPower": "Potere di voto", - "increased": "Potere di voto aumentato", - "increasing": "Potere di voto in aumento", - "decreased": "Potere di voto ridotto", - "decreasing": "Potere di voto in riduzione", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Votato", "voting": "Votazione", "changedVote": "Voto modificato", "changingVote": "Modifica del voto", "revoted": "Revocato", "revoting": "In fase di revocazione", - "unvoted": "Non Votato", - "unvoting": "Annulla Voto", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transferred", "transferring": "Transferring", "shimmerClaimed": "Rivendicato", @@ -1531,6 +1546,7 @@ "contractFunction": "Funzione del contratto", "standard": "Standard", "uri": "URI", + "image": "Image", "issuer": "Emittente", "issuerAddress": "Indirizzo Emittente", "issuerName": "Nome Emittente", diff --git a/packages/shared/src/locales/nl.json b/packages/shared/src/locales/nl.json index 0eed1a7c98..8a7e31fd9b 100644 --- a/packages/shared/src/locales/nl.json +++ b/packages/shared/src/locales/nl.json @@ -522,7 +522,8 @@ "permissions": { "step": "Permissie", "tip": "Dit zijn beperkte machtigingen voor de dApp om een interactieverzoek te sturen voor jouw goedkeuring. Keur alleen apps goed die je kent en vertrouwt.", - "title": "Selecteer machtigingen", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Verzoek om data te ondertekenen", "signTransaction": "Verzoek om een transactie te ondertekenen", "sendTransaction": "Verzoek om een transactie te ondertekenen", @@ -531,7 +532,8 @@ }, "networks": { "step": "Netwerken", - "title": "Selecteer netwerken", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "De vereiste netwerken zijn vooraf geselecteerd door de dApp en kunnen later ook opnieuw worden geconfigureerd.", "empty": "Je moet tenminste één netwerk selecteren" }, @@ -650,24 +652,24 @@ }, "statusTimeline": { "upcoming": { - "past": "Announced", - "present": "Announcement", - "future": "Announcement" + "past": "Aangekondigd", + "present": "Aankondiging", + "future": "Aankondiging" }, "commencing": { - "past": "Voting opened", - "present": "Voting open", - "future": "Voting opens" + "past": "Stemmen geopend", + "present": "Stemmen open", + "future": "Stemming opent" }, "holding": { - "past": "Counting started", - "present": "Counting started", - "future": "Counting starts" + "past": "Tellen gestart", + "present": "Tellen gestart", + "future": "Tellen begint" }, "ended": { - "past": "Counting stopped", - "present": "Counting stopped", - "future": "Counting stops" + "past": "Tellen gestopt", + "present": "Tellen gestopt", + "future": "Tellen stopt" } }, "details": { @@ -686,7 +688,11 @@ "nodeUrl": "Knooppunt URL" }, "fetching": "Ophalen voorstel gegevens", - "hintVote": "Je kunt niet stemmen over een voorstel dat in de aankondigingsfase verkeert, de stemming wordt geopend in {time}." + "hintVote": "Je kunt niet stemmen over een voorstel dat in de aankondigingsfase verkeert, de stemming wordt geopend in {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -863,7 +869,7 @@ "loadingNodeInfo": "Laden van knooppunt info", "nodeAddress": "Knooppunt adres", "removeConfirmation": "Weet u zeker dat u dit knooppunt wilt verwijderen?", - "requiresAuthentication": "Node requires authentication", + "requiresAuthentication": "Node vereist authenticatie", "info": { "general": { "tab": "Algemeen", @@ -899,8 +905,8 @@ "useMetricPrefix": "Metrische voorvoegsel gebruiken" } }, - "jwt": "JSON web token", - "credentials": "Sign-in credentials" + "jwt": "JSON webtoken", + "credentials": "Inloggegevens" }, "errorLog": { "title": "Foutenlogboek", @@ -973,10 +979,18 @@ }, "siwe": { "title": "Inloggen met Ethereum", - "domain": "Domain", - "statement": "Statement", - "resources": "Resources", - "action": "Sign in" + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", + "domain": "Domein", + "statement": "Verklaring", + "resources": "Bronnen", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", + "action": "Inloggen" }, "signTransaction": { "title": "Transactie ondertekenen", @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Smart Contract-oproep naar {contractAddress}", "unableToVerify": "Bloom is niet in staat om de smart Contract-oproep te verifiëren. Ga alleen verder met apps die je vertrouwt.", + "viewSmartContract": "View Smart Contract", "action": "Bevestig", "success": "Smart contract oproep naar {recipient}" }, @@ -1157,7 +1172,7 @@ "errors": { "invalidMimetype": "Ongeldige MimeType, controleer of het bestandstype wordt ondersteund", "quantityTooSmall": "Hoeveelheid moet groter zijn dan 0", - "quantityTooLarge": "Quantity needs to be smaller than 127", + "quantityTooLarge": "Hoeveelheid moet kleiner zijn dan 127", "emptyName": "Naam is een verplicht veld", "invalidURI": "Ongeldige URI, geef een geldige URI op", "notReachable": "URI niet bereikbaar, niet mogelijk het NFT type te controleren", @@ -1186,7 +1201,7 @@ "title": "Voorstel toevoegen", "body": "Lever onderstaande informatie aan om een voorstel toe te voegen.", "addToAllAccounts": "Voeg het voorstel toe aan alle accounts", - "addAllProposalsOnNode": "Add all proposals on this node" + "addAllProposalsOnNode": "Voeg alle voorstellen toe aan deze node" }, "editProposal": { "title": "Voorstel bewerken", @@ -1355,7 +1370,7 @@ "addProposal": "Voorstel toevoegen", "removeProposal": "Voorstel verwijderen", "changeNode": "Wijzig knooppunt", - "changeNodeUrl": "Change node URL", + "changeNodeUrl": "Verander node URL", "stopVoting": "Stoppen met stemmen", "revote": "Stem opnieuw", "skipAndKeep": "Overslaan en behoud oude wachtwoord", @@ -1365,11 +1380,11 @@ "syncAccounts": "Synchroniseer accounts", "importToken": "Importeer {type} token", "syncTokens": "Forceer synchronisatie tokens", - "mintNftCollection": "Mint NFT collection" + "mintNftCollection": "Mint NFT collectie" }, "general": { "recipient": "Ontvanger", - "username": "Username", + "username": "Gebruikersnaam", "password": "Wachtwoord", "confirmPassword": "Bevestig wachtwoord", "currentPassword": "Huidig wachtwoord", @@ -1381,18 +1396,18 @@ "receiving": "Ontvangen", "newVotingPower": "Nieuwe stemkracht", "votingPower": "Stemkracht", - "increased": "Stemkracht vergroot", - "increasing": "Stemkracht vergroten", - "decreased": "Stemkracht verkleind", - "decreasing": "Stemkracht verkleinen", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Gestemd", "voting": "Stemmen", "changedVote": "Stem gewijzigd", "changingVote": "Stem wijzigen", "revoted": "Herstemd", "revoting": "Herstemmen", - "unvoted": "Niet gestemd", - "unvoting": "Niet stemmen", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Verzonden", "transferring": "Aan het verzenden", "shimmerClaimed": "Geclaimed", @@ -1531,6 +1546,7 @@ "contractFunction": "Contract functie", "standard": "Standaard", "uri": "URI", + "image": "Image", "issuer": "Uitgever", "issuerAddress": "Uitgever adres", "issuerName": "Naam uitgever", @@ -1588,7 +1604,7 @@ "baseCoin": "Basis token", "unverifiedContract": "Ongeverifieerd contract", "startIndex": "Start index", - "mintNftCollectionDescription": "Mint collection NFT in Alias Output" + "mintNftCollectionDescription": "Mint collectie NFT in Alias Output" }, "filters": { "title": "Filters", @@ -1797,7 +1813,7 @@ "success": "Contactadressen succesvol bijgewerkt" }, "siwe": { - "rejected": "Invalid SIWE request" + "rejected": "Ongeldig SIWE verzoek" }, "syncTokens": { "success": "Tokens succesvol gesynchroniseerd" @@ -2073,7 +2089,7 @@ "attributes": "Optionele parameter: Een reeks van eigenschappen en waarden die kenmerken van de NFT definiëren", "uri": "Om een URI met eigen media te maken, upload eerst je bestand naar IPFS via een opslagservice (bijv. https://nft.storage/)", "quantity": "Optionele parameter: Het aantal geminte kopieën met deze metadata.", - "startIndex": "Optional parameter: The start index of the NFTs to mint." + "startIndex": "Optionele parameter: De startindex van de NFT's om te minten." }, "governance": { "removeProposalWarning": "Je moet eerst stoppen met stemmen voor dit voorstel voordat je het verwijdert.", @@ -2099,7 +2115,7 @@ "collectibles": "Verzamelingen", "governance": "Governance", "campaigns": "Campagnes", - "buySell": "Buy IOTA", + "buySell": "IOTA kopen", "developer": "Ontwikkelaar", "tokens": "Tokens", "settings": "Instellingen" @@ -2134,10 +2150,10 @@ "appNotOpen": "App niet geopend" }, "dappVerification": { - "valid": "Verified", - "invalid": "Domain Mismatch", - "unknown": "Cannot verify", - "scam": "Security Risk" + "valid": "Geverifiëerd", + "invalid": "Domein komt niet overeen", + "unknown": "Kan niet verifiëren", + "scam": "Beveiligingsrisico" } }, "menus": { diff --git a/packages/shared/src/locales/pl.json b/packages/shared/src/locales/pl.json index 8a9b69a36b..b4ea6134c9 100644 --- a/packages/shared/src/locales/pl.json +++ b/packages/shared/src/locales/pl.json @@ -522,7 +522,8 @@ "permissions": { "step": "Uprawnienie", "tip": "Są to ograniczone uprawnienia dla aplikacji dApp w celu wysyłania żądania interakcji do Twojego zatwierdzenia. Zatwierdzaj tylko aplikacje, które znasz i którym ufasz.", - "title": "Wybierz uprawnienia", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Prośba o podpisanie danych", "signTransaction": "Prośba o podpisanie transakcji", "sendTransaction": "Poproś o wysłanie transakcji", @@ -531,7 +532,8 @@ }, "networks": { "step": "Sieci", - "title": "Wybierz sieci", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "Wymagane sieci są wstępnie wybierane przez aplikację dApp. Można to później skonfigurować w ustawieniach.", "empty": "Musisz wybrać przynajmniej jedną sieć" }, @@ -650,24 +652,24 @@ }, "statusTimeline": { "upcoming": { - "past": "Announced", - "present": "Announcement", - "future": "Announcement" + "past": "Ogłoszono głosowanie", + "present": "Ogłoszenie głosowania", + "future": "Nadchodzące głosowanie" }, "commencing": { - "past": "Voting opened", - "present": "Voting open", - "future": "Voting opens" + "past": "Głosowanie otwarto", + "present": "Otwarcie głosowania", + "future": "Głosowanie rozpocznie się" }, "holding": { - "past": "Counting started", - "present": "Counting started", - "future": "Counting starts" + "past": "Zliczanie rozpoczęto", + "present": "Zliczanie rozpoczęte", + "future": "Zliczanie rozpocznie się" }, "ended": { - "past": "Counting stopped", - "present": "Counting stopped", - "future": "Counting stops" + "past": "Zliczanie zakończono", + "present": "Zliczanie zakończone", + "future": "Zliczanie zakończy się" } }, "details": { @@ -686,7 +688,11 @@ "nodeUrl": "URL serwera" }, "fetching": "Pobieranie danych propozycji", - "hintVote": "Nie możesz głosować nad propozycją, która dopiero została zapowiedziana. Głosowanie zostanie otwarte za {time}." + "hintVote": "Nie możesz głosować nad propozycją, która dopiero została zapowiedziana. Głosowanie zostanie otwarte za {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -863,7 +869,7 @@ "loadingNodeInfo": "Wczytywanie informacji o serwerze", "nodeAddress": "Adres serwera", "removeConfirmation": "Czy na pewno chcesz usunąć ten serwer?", - "requiresAuthentication": "Node requires authentication", + "requiresAuthentication": "Serwer wymaga uwierzytelniania", "info": { "general": { "tab": "Ogólne", @@ -900,7 +906,7 @@ } }, "jwt": "JSON web token", - "credentials": "Sign-in credentials" + "credentials": "Dane logowania" }, "errorLog": { "title": "Dziennik błędów", @@ -973,10 +979,18 @@ }, "siwe": { "title": "Zaloguj się z Ethereum", - "domain": "Domain", - "statement": "Statement", - "resources": "Resources", - "action": "Sign in" + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", + "domain": "Domena", + "statement": "Oświadczenie", + "resources": "Dokumentacja", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", + "action": "Zaloguj się" }, "signTransaction": { "title": "Podpisz transakcję", @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Interakcja smart kontraktu z {contractAddress}", "unableToVerify": "Bloom nie może zweryfikować interakcji z smart kontraktem. Kontynuuj tylko z aplikacjami, którym ufasz.", + "viewSmartContract": "View Smart Contract", "action": "Potwierdź", "success": "Interakcja smart kontraktu z {recipient}" }, @@ -1157,7 +1172,7 @@ "errors": { "invalidMimetype": "Nieprawidłowy MimeType, sprawdź czy typ pliku jest obsługiwany", "quantityTooSmall": "Ilość musi być większa niż 0", - "quantityTooLarge": "Quantity needs to be smaller than 127", + "quantityTooLarge": "Ilość musi być mniejsza niż 127", "emptyName": "Nazwa jest wymaganym polem", "invalidURI": "Nieprawidłowy URI, proszę podać poprawny URI", "notReachable": "URI nieosiągalny, nie można sprawdzić typu NFT", @@ -1186,7 +1201,7 @@ "title": "Dodaj propozycję", "body": "Wypełnij poniższe pola, aby dodać propozycję.", "addToAllAccounts": "Dodaj propozycję do wszystkich kont", - "addAllProposalsOnNode": "Add all proposals on this node" + "addAllProposalsOnNode": "Dodaj wszystkie propozycje z tego serwera" }, "editProposal": { "title": "Edytuj propozycję", @@ -1355,7 +1370,7 @@ "addProposal": "Dodaj propozycję", "removeProposal": "Usuń propozycję", "changeNode": "Zmień serwer", - "changeNodeUrl": "Change node URL", + "changeNodeUrl": "Zmień adres serwera", "stopVoting": "Zatrzymaj głosowanie", "revote": "Odnów głosy", "skipAndKeep": "Pomiń i zachowaj stare hasło", @@ -1365,11 +1380,11 @@ "syncAccounts": "Synchronizuj konta", "importToken": "Importowanie tokena {type}", "syncTokens": "Wymuś synchronizację tokenów", - "mintNftCollection": "Mint NFT collection" + "mintNftCollection": "Mintuj kolekcję NFT" }, "general": { "recipient": "Odbiorca", - "username": "Username", + "username": "Nazwa użytkownika", "password": "Hasło", "confirmPassword": "Potwierdź hasło", "currentPassword": "Aktualne hasło", @@ -1381,18 +1396,18 @@ "receiving": "Odbieranie", "newVotingPower": "Nowa siła głosu", "votingPower": "Siła głosu", - "increased": "Zwiększono siłę głosu", - "increasing": "Zwiększanie siły głosu", - "decreased": "Zmniejszono siłę głosu", - "decreasing": "Zmniejszanie siły głosu", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Zagłosowano", "voting": "Głosowanie", "changedVote": "Zmieniono głos", "changingVote": "Zmiana głosu", "revoted": "Odnowiono głosy", "revoting": "Odnawianie głosów", - "unvoted": "Zatrzymano głos", - "unvoting": "Wycofywanie głosu", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Przesłano", "transferring": "Przesyłanie", "shimmerClaimed": "Odebrano", @@ -1531,6 +1546,7 @@ "contractFunction": "Funkcja kontraktu", "standard": "Standard", "uri": "URI", + "image": "Image", "issuer": "Wydawca", "issuerAddress": "Adres wydawcy", "issuerName": "Nazwa wydawcy", @@ -1587,8 +1603,8 @@ "governance": "Zarządzanie", "baseCoin": "Token podstawowy", "unverifiedContract": "Niezweryfikowany kontrakt", - "startIndex": "Start index", - "mintNftCollectionDescription": "Mint collection NFT in Alias Output" + "startIndex": "Indeks startowy", + "mintNftCollectionDescription": "Mintuj kolekcję NFT w wyjściu aliasu" }, "filters": { "title": "Filtry", @@ -1797,7 +1813,7 @@ "success": "Adresy kontaktu zostały pomyślnie zaktualizowane" }, "siwe": { - "rejected": "Invalid SIWE request" + "rejected": "Nieprawidłowe żądanie SIWE" }, "syncTokens": { "success": "Tokeny pomyślnie zsynchronizowane" @@ -2073,7 +2089,7 @@ "attributes": "Opcjonalny parametr: tablica cech i wartości, które definiują atrybuty NFT", "uri": "Aby utworzyć URI za pomocą niestandardowych mediów, najpierw prześlij plik do IPFS za pośrednictwem usługi przechowywania danych (np. https://nft.storage/)", "quantity": "Opcjonalny parametr: Ilość kopii mintowanych za pomocą tych metadanych.", - "startIndex": "Optional parameter: The start index of the NFTs to mint." + "startIndex": "Opcjonalny parametr: Indeks startowy NFT do mintowania." }, "governance": { "removeProposalWarning": "Musisz przestać głosować na tę propozycję, zanim ją usuniesz.", @@ -2099,7 +2115,7 @@ "collectibles": "Kolekcje", "governance": "Zarządzanie", "campaigns": "Kampanie", - "buySell": "Buy IOTA", + "buySell": "Kup IOTA", "developer": "Deweloper", "tokens": "Tokeny", "settings": "Ustawienia" @@ -2134,10 +2150,10 @@ "appNotOpen": "Aplikacja nie jest otwarta" }, "dappVerification": { - "valid": "Verified", - "invalid": "Domain Mismatch", - "unknown": "Cannot verify", - "scam": "Security Risk" + "valid": "Zweryfikowano", + "invalid": "Niezgodność domeny", + "unknown": "Nie można zweryfikować", + "scam": "Zagrożenie bezpieczeństwa" } }, "menus": { diff --git a/packages/shared/src/locales/pt-BR.json b/packages/shared/src/locales/pt-BR.json index 11047a1b20..3dae0eac3c 100644 --- a/packages/shared/src/locales/pt-BR.json +++ b/packages/shared/src/locales/pt-BR.json @@ -522,7 +522,8 @@ "permissions": { "step": "Permissão", "tip": "Estas são permissões limitadas para o dApp enviar uma solicitação de interação para sua aprovação. Aprove somente apps que você conhece e confia.", - "title": "Selecionar permissões", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Request to sign data", "signTransaction": "Request to sign a transaction", "sendTransaction": "Request to send a transaction", @@ -531,7 +532,8 @@ }, "networks": { "step": "Redes", - "title": "Selecionar redes", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "As redes necessárias são pré-selecionadas para você pelo dApp e também podem ser reconfiguradas mais tarde.", "empty": "You have to select at least one network" }, @@ -686,7 +688,11 @@ "nodeUrl": "URL do nó" }, "fetching": "Obtendo dados da proposta", - "hintVote": "Você não pode votar em uma proposta que está na fase de anúncio, a votação abrirá em {time}." + "hintVote": "Você não pode votar em uma proposta que está na fase de anúncio, a votação abrirá em {time}.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -973,9 +979,17 @@ }, "siwe": { "title": "Sign-In with Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", "resources": "Resources", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", "action": "Sign in" }, "signTransaction": { @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Smart contract call to {contractAddress}", "unableToVerify": "Bloom is unable to verify the smart contract call. Only proceed with apps you trust.", + "viewSmartContract": "View Smart Contract", "action": "Confirm", "success": "Smart contract call to {recipient}" }, @@ -1381,18 +1396,18 @@ "receiving": "Recebendo", "newVotingPower": "New voting power", "votingPower": "Voting power", - "increased": "Voting power increased", - "increasing": "Increasing voting power", - "decreased": "Voting power decreased", - "decreasing": "Decreasing voting power", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Votado", "voting": "Votando", "changedVote": "Changed vote", "changingVote": "Changing vote", "revoted": "Revotado", "revoting": "Revotando", - "unvoted": "Unvoted", - "unvoting": "Unvoting", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transferred", "transferring": "Transferring", "shimmerClaimed": "Resgatado", @@ -1531,6 +1546,7 @@ "contractFunction": "Contract function", "standard": "Padrão", "uri": "URI", + "image": "Image", "issuer": "Emissor", "issuerAddress": "Endereço do Emissor", "issuerName": "Nome do Emissor", diff --git a/packages/shared/src/locales/tr.json b/packages/shared/src/locales/tr.json index 8b82e1878b..e0b5cda78a 100644 --- a/packages/shared/src/locales/tr.json +++ b/packages/shared/src/locales/tr.json @@ -522,7 +522,8 @@ "permissions": { "step": "İzin", "tip": "Bunlar dApp'in onayınıza etkileşim isteği göndermesine yönelik sınırlı izinlerdir. Yalnızca bildiğiniz ve güvendiğiniz uygulamaları onaylayın.", - "title": "İzinleri seçin", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Veri imzalama isteği", "signTransaction": "Bir işlemi imzalama isteği", "sendTransaction": "İşlem gönderme isteği", @@ -531,7 +532,8 @@ }, "networks": { "step": "Ağlar", - "title": "Ağ seç", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "Gerekli ağlar sizin için dApp tarafından önceden seçilir ve daha sonra yeniden yapılandırılabilir.", "empty": "En az bir ağ seçmeniz gerekiyor" }, @@ -686,7 +688,11 @@ "nodeUrl": "Node URL'si" }, "fetching": "Teklif verilerini alma", - "hintVote": "Duyuru aşamasında olan bir teklife oy veremezsiniz, oylama {zaman} içinde açılacaktır." + "hintVote": "Duyuru aşamasında olan bir teklife oy veremezsiniz, oylama {zaman} içinde açılacaktır.", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -973,9 +979,17 @@ }, "siwe": { "title": "Sign-In with Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", "resources": "Resources", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", "action": "Sign in" }, "signTransaction": { @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "{contractAddress} adresine akıllı sözleşme çağrısı", "unableToVerify": "Bloom akıllı sözleşme çağrısını doğrulayamıyor. Yalnızca güvendiğiniz uygulamalarla devam edin.", + "viewSmartContract": "View Smart Contract", "action": "Onayla", "success": "{recipient} adlı kişiye akıllı sözleşme çağrısı" }, @@ -1381,18 +1396,18 @@ "receiving": "Alınıyor", "newVotingPower": "Yeni oy gücü", "votingPower": "Oy gücü", - "increased": "Oy kullanma gücü arttı", - "increasing": "Oy kullanma gücünün artırılması", - "decreased": "Oy kullanma gücü azaldı", - "decreasing": "Oy kullanma gücünün azaltılması", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "Oylandı", "voting": "Oylama", "changedVote": "Oy değiştirildi", "changingVote": "Oy değiştiriliyor", "revoted": "İptal edildi", "revoting": "İptal ediliyor", - "unvoted": "Oy geri alındı", - "unvoting": "Oylama iptali", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transfer edildi", "transferring": "Transfer ediliyor", "shimmerClaimed": "Talep edildi", @@ -1531,6 +1546,7 @@ "contractFunction": "Sözleşme işlevi", "standard": "Standart", "uri": "URl", + "image": "Image", "issuer": "İhraççı", "issuerAddress": "İhraççı Adresi", "issuerName": "İhraççı Adı", diff --git a/packages/shared/src/locales/zh-CN.json b/packages/shared/src/locales/zh-CN.json index 2a46c9e23f..c5e8929ba1 100644 --- a/packages/shared/src/locales/zh-CN.json +++ b/packages/shared/src/locales/zh-CN.json @@ -522,7 +522,8 @@ "permissions": { "step": "Permission", "tip": "These are limited permissions for the dApp to send an interaction request for your approval. Only approve apps you know and trust.", - "title": "Select permissions", + "requiredTitle": "Required permissions", + "optionalTitle": "Optional permissions", "signData": "Request to sign data", "signTransaction": "Request to sign a transaction", "sendTransaction": "Request to send a transaction", @@ -531,7 +532,8 @@ }, "networks": { "step": "Networks", - "title": "Select networks", + "requiredTitle": "Required networks", + "optionalTitle": "Optional networks", "tip": "The required networks are pre-selected for you by the dApp and can also be reconfigured later.", "empty": "You have to select at least one network" }, @@ -686,7 +688,11 @@ "nodeUrl": "节点 URL" }, "fetching": "正在获取提案数据", - "hintVote": "您不能对处于宣布阶段的提案进行投票,投票将在 {time} 进行。" + "hintVote": "您不能对处于宣布阶段的提案进行投票,投票将在 {time} 进行。", + "projection": { + "label": "Projected votes", + "tooltip": "The projection is based on current voting weight and remaining milestones." + } } }, "updateStronghold": { @@ -973,9 +979,17 @@ }, "siwe": { "title": "Sign-In with Ethereum", + "details": "Details", + "raw": "Raw", + "rawMessage": "Raw message", "domain": "Domain", "statement": "Statement", "resources": "Resources", + "chainId": "Chain Id", + "nonce": "Nonce", + "issuedAt": "Issued at", + "notBefore": "Not before", + "requestId": "Request Id", "action": "Sign in" }, "signTransaction": { @@ -991,6 +1005,7 @@ "smartContractCall": { "title": "Smart contract call to {contractAddress}", "unableToVerify": "Bloom is unable to verify the smart contract call. Only proceed with apps you trust.", + "viewSmartContract": "View Smart Contract", "action": "Confirm", "success": "Smart contract call to {recipient}" }, @@ -1381,18 +1396,18 @@ "receiving": "接收", "newVotingPower": "新的投票权重", "votingPower": "投票权重", - "increased": "投票权已增加", - "increasing": "增加投票权", - "decreased": "投票权重降低", - "decreasing": "降低投票权重", + "increased": "Voting increased", + "increasing": "Increasing voting", + "decreased": "Voting decreased", + "decreasing": "Decreasing voting", "voted": "已投票", "voting": "投票中", "changedVote": "变更投票", "changingVote": "投票变更中...", "revoted": "已重新投票", "revoting": "重新投票", - "unvoted": "已取消投票", - "unvoting": "取消投票中...", + "unvoted": "Stopped voting", + "unvoting": "Stopping voting", "transferred": "Transferred", "transferring": "Transferring", "shimmerClaimed": "已认领", @@ -1531,6 +1546,7 @@ "contractFunction": "合约功能", "standard": "标准", "uri": "URI", + "image": "Image", "issuer": "发行者", "issuerAddress": "发行者地址", "issuerName": "发行者名称",