diff --git a/packages/desktop/components/ProposalCard.svelte b/packages/desktop/components/ProposalCard.svelte index 23d4f244a2..cd4dc88c0c 100644 --- a/packages/desktop/components/ProposalCard.svelte +++ b/packages/desktop/components/ProposalCard.svelte @@ -3,11 +3,8 @@ import { EventStatus } from '@iota/sdk/out/types' import { ProposalStatusInfo } from '@views/governance' - import { Text } from '@ui' - import { FontWeight } from '@ui/enums' - import { TooltipIcon, IconName } from '@bloomwalletio/ui' + import { Text, TooltipIcon, IconName } from '@bloomwalletio/ui' - import { darkMode } from '@core/app/stores' import { localize } from '@core/i18n' import { GovernanceRoute, governanceRouter } from '@core/router' @@ -37,34 +34,27 @@ e.key === 'Enter' && onProposalClick()} - class:dark={$darkMode} - class:ended={proposal?.status === EventStatus.Ended} class="flex flex-col p-6 border border-solid border-gray-200 dark:border-transparent rounded-xl cursor-pointer h-fit shadow-elevation-1 focus:shadow-inner - {proposal?.status === EventStatus.Ended ? 'bg-transparent dark:bg-gray-900' : 'bg-white dark:bg-gray-800'}" + {proposal?.status === EventStatus.Ended + ? 'bg-surface-1 dark:bg-surface-1-dark' + : 'bg-surface-0 dark:bg-surface-0-dark'}" >
{#if proposal.organization} {/if} - {proposal.title} + {proposal.title}
{#if hasVoted} - {/if}
- - diff --git a/packages/desktop/components/index.ts b/packages/desktop/components/index.ts index 5872f5f63d..0d6715bbe2 100644 --- a/packages/desktop/components/index.ts +++ b/packages/desktop/components/index.ts @@ -2,7 +2,6 @@ export * from './drawers' export * from './filter' export * from './menus' export * from './modals' -export * from './panes' export * from './popup' export { default as AccountSwitcher } from './AccountSwitcher.svelte' @@ -16,11 +15,9 @@ export { default as FormattedBalance } from './FormattedBalance.svelte' export { default as NavbarContainer } from './NavbarContainer.svelte' export { default as NetworkCard } from './NetworkCard.svelte' export { default as NodeListTable } from './NodeListTable.svelte' -export { default as Proposals } from './Proposals.svelte' export { default as ProposalAnswer } from './ProposalAnswer.svelte' export { default as ProposalCard } from './ProposalCard.svelte' export { default as ProposalsDetails } from './ProposalsDetails.svelte' -export { default as ProposalQuestion } from './ProposalQuestion.svelte' export { default as SidebarTab } from './SidebarTab.svelte' export { default as StatusTile, type StatusTileProps } from './StatusTile.svelte' export { default as TitleBar } from './TitleBar.svelte' diff --git a/packages/desktop/components/menus/ProposalDetailsMenu.svelte b/packages/desktop/components/menus/ProposalDetailsMenu.svelte index a430051c2c..dc9a53d692 100644 --- a/packages/desktop/components/menus/ProposalDetailsMenu.svelte +++ b/packages/desktop/components/menus/ProposalDetailsMenu.svelte @@ -62,8 +62,8 @@ function setItems(proposal: IProposal, isVotingForProposal: boolean): void { items = [ { - icon: IconName.SettingsSliders, - title: localize('actions.changeNode'), + icon: IconName.LinkHorizontal, + title: localize('actions.changeNodeUrl'), onClick: onChangeNodeClick, }, { diff --git a/packages/desktop/components/panes/index.ts b/packages/desktop/components/panes/index.ts deleted file mode 100644 index e04d06635f..0000000000 --- a/packages/desktop/components/panes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ProposalInformationPane } from './ProposalInformationPane.svelte' diff --git a/packages/desktop/components/popup/Popup.svelte b/packages/desktop/components/popup/Popup.svelte index 107353d6ef..45c115c0ae 100644 --- a/packages/desktop/components/popup/Popup.svelte +++ b/packages/desktop/components/popup/Popup.svelte @@ -41,6 +41,8 @@ import ManageVotingPowerPopup from './popups/ManageVotingPowerPopup.svelte' import MintNativeTokenConfirmationPopup from './popups/MintNativeTokenConfirmationPopup.svelte' import MintNativeTokenFormPopup from './popups/MintNativeTokenFormPopup.svelte' + import MintNftCollectionConfirmationPopup from './popups/MintNftCollectionConfirmationPopup.svelte' + import MintNftCollectionFormPopup from './popups/MintNftCollectionFormPopup.svelte' import MintNftConfirmationPopup from './popups/MintNftConfirmationPopup.svelte' import MintNftFormPopup from './popups/MintNftFormPopup.svelte' import NodeAuthRequiredPopup from './popups/NodeAuthRequiredPopup.svelte' @@ -119,6 +121,8 @@ [PopupId.MintNativeTokenForm]: MintNativeTokenFormPopup, [PopupId.MintNftConfirmation]: MintNftConfirmationPopup, [PopupId.MintNftForm]: MintNftFormPopup, + [PopupId.MintNftCollectionForm]: MintNftCollectionFormPopup, + [PopupId.MintNftCollectionConfirmation]: MintNftCollectionConfirmationPopup, [PopupId.NodeAuthRequired]: NodeAuthRequiredPopup, [PopupId.NodeInfo]: NodeInfoPopup, [PopupId.ReceiveAddress]: ReceiveAddressPopup, diff --git a/packages/desktop/components/popup/popups/MintNftCollectionConfirmationPopup.svelte b/packages/desktop/components/popup/popups/MintNftCollectionConfirmationPopup.svelte new file mode 100644 index 0000000000..85179cca6e --- /dev/null +++ b/packages/desktop/components/popup/popups/MintNftCollectionConfirmationPopup.svelte @@ -0,0 +1,149 @@ + + + +
+ + + + + + + {#if selectedTab.key === PopupTab.Transaction} + + {:else if selectedTab.key === PopupTab.Nft} +
+ {:else if selectedTab.key === PopupTab.NftMetadata} +
+ {/if} + + + + diff --git a/packages/desktop/components/popup/popups/MintNftCollectionFormPopup.svelte b/packages/desktop/components/popup/popups/MintNftCollectionFormPopup.svelte new file mode 100644 index 0000000000..7a073721ee --- /dev/null +++ b/packages/desktop/components/popup/popups/MintNftCollectionFormPopup.svelte @@ -0,0 +1,205 @@ + + + + + + + + {#each Object.keys(optionalInputs) as key} + + {/each} + + {#if error} + + {/if} + + diff --git a/packages/desktop/components/popup/popups/MintNftConfirmationPopup.svelte b/packages/desktop/components/popup/popups/MintNftConfirmationPopup.svelte index d9249aa596..8ae4113ceb 100644 --- a/packages/desktop/components/popup/popups/MintNftConfirmationPopup.svelte +++ b/packages/desktop/components/popup/popups/MintNftConfirmationPopup.svelte @@ -19,8 +19,20 @@ let storageDeposit: number = 0 let totalStorageDeposit: number = 0 - const { standard, type, uri, name, collectionName, royalties, issuerName, description, attributes, quantity } = - $mintNftDetails || {} + const { + standard, + type, + uri, + name, + collectionName, + royalties, + issuerName, + description, + attributes, + quantity, + collectionId, + startIndex, + } = $mintNftDetails || {} $: irc27Metadata = { standard, @@ -53,6 +65,7 @@ openPopup({ id: PopupId.MintNftForm, overflow: true, + confirmClickOutside: true, }) } @@ -64,7 +77,7 @@ } try { - await mintNft(irc27Metadata, Number(quantity)) + await mintNft(irc27Metadata, startIndex, quantity, collectionId) closePopup() } catch (err) { handleError(err) diff --git a/packages/desktop/components/popup/popups/MintNftFormPopup.svelte b/packages/desktop/components/popup/popups/MintNftFormPopup.svelte index f7e9cad232..618973f5af 100644 --- a/packages/desktop/components/popup/popups/MintNftFormPopup.svelte +++ b/packages/desktop/components/popup/popups/MintNftFormPopup.svelte @@ -1,6 +1,5 @@ + diff --git a/packages/desktop/features/developer-tools.features.ts b/packages/desktop/features/developer-tools.features.ts index 50f71bea27..c066228d04 100644 --- a/packages/desktop/features/developer-tools.features.ts +++ b/packages/desktop/features/developer-tools.features.ts @@ -8,6 +8,9 @@ const developerToolsFeatures: IDeveloperFeatures = { mintNft: { enabled: true, }, + mintNftCollection: { + enabled: true, + }, mintNativeTokens: { enabled: true, }, diff --git a/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts b/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts index f03ddf4dc2..8dfd8ebe52 100644 --- a/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts +++ b/packages/desktop/lib/auxiliary/popup/enums/popup-id.enum.ts @@ -27,6 +27,8 @@ export enum PopupId { MintNativeTokenForm = 'mintNativeTokenForm', MintNftConfirmation = 'mintNftConfirmation', MintNftForm = 'mintNftForm', + MintNftCollectionForm = 'mintNftCollectionForm', + MintNftCollectionConfirmation = 'mintNftCollectionConfirmation', NodeAuthRequired = 'nodeAuthRequired', NodeInfo = 'nodeInfo', ReceiveAddress = 'receiveAddress', diff --git a/packages/desktop/views/dashboard/developer/Developer.svelte b/packages/desktop/views/dashboard/developer/Developer.svelte index 30af9695bf..dd885e7568 100644 --- a/packages/desktop/views/dashboard/developer/Developer.svelte +++ b/packages/desktop/views/dashboard/developer/Developer.svelte @@ -9,6 +9,7 @@ FaucetRequestButton, MintNativeTokenButton, MintNftButton, + MintNftCollectionButton, TestDeepLinkButton, } from './components' import { DashboardRoute, dashboardRouter } from '@core/router' @@ -30,6 +31,9 @@ {#if features.developerTools.mintNativeTokens.enabled} {/if} + {#if features.developerTools.mintNftCollection.enabled} + + {/if} {#if features.developerTools.mintNft.enabled} {/if} diff --git a/packages/desktop/views/dashboard/developer/components/MintNftCollectionButton.svelte b/packages/desktop/views/dashboard/developer/components/MintNftCollectionButton.svelte new file mode 100644 index 0000000000..c44e0cbbba --- /dev/null +++ b/packages/desktop/views/dashboard/developer/components/MintNftCollectionButton.svelte @@ -0,0 +1,22 @@ + + + diff --git a/packages/desktop/views/dashboard/developer/components/index.ts b/packages/desktop/views/dashboard/developer/components/index.ts index 70d196eb86..1cd1180fcb 100644 --- a/packages/desktop/views/dashboard/developer/components/index.ts +++ b/packages/desktop/views/dashboard/developer/components/index.ts @@ -2,4 +2,5 @@ export { default as CreateAliasButton } from './CreateAliasButton.svelte' export { default as FaucetRequestButton } from './FaucetRequestButton.svelte' export { default as MintNativeTokenButton } from './MintNativeTokenButton.svelte' export { default as MintNftButton } from './MintNftButton.svelte' +export { default as MintNftCollectionButton } from './MintNftCollectionButton.svelte' export { default as TestDeepLinkButton } from './TestDeepLinkButton.svelte' diff --git a/packages/desktop/views/dashboard/governance/views/GovernanceDashboardView.svelte b/packages/desktop/views/dashboard/governance/views/GovernanceDashboardView.svelte index 8854077cbc..43d3fb1706 100644 --- a/packages/desktop/views/dashboard/governance/views/GovernanceDashboardView.svelte +++ b/packages/desktop/views/dashboard/governance/views/GovernanceDashboardView.svelte @@ -1,13 +1,14 @@ - +
@@ -17,7 +18,7 @@
{#if Object.keys($registeredProposalsForSelectedAccount).length} - + {:else}
- +
@@ -242,7 +242,7 @@ ]} /> - +
import { EventStatus } from '@iota/sdk/out/types' - import { Table } from '@bloomwalletio/ui' - import { Pane, Text } from '@ui' + import { Table, Text } from '@bloomwalletio/ui' + import { Pane } from '@ui' import { formatDate, localize } from '@core/i18n' import { DATE_FORMAT, milestoneToDate, truncateString } from '@core/utils' import { networkStatus } from '@core/network/stores' import { selectedProposal } from '@contexts/governance/stores' - export let classes: string = '' - interface IProposalDateData { propertyKey: 'votingOpens' | 'countingStarts' | 'countingEnds' | 'countingEnded' milestone: number @@ -44,10 +42,8 @@ } - - - {localize('views.governance.details.proposalInformation.title')} - + + {localize('views.governance.details.proposalInformation.title')}
import { Filter, ProposalCard } from '@components' - import { Text, SearchInput } from '@ui' - import { FontWeight } from '@ui/enums' - + import { SearchInput } from '@ui' + import { Text } from '@bloomwalletio/ui' import { localize } from '@core/i18n' - import { proposalFilter, registeredProposalsForSelectedAccount } from '@contexts/governance/stores' import { isVisibleProposal, sortProposals } from '@contexts/governance/utils' @@ -31,15 +29,13 @@ - - {localize('views.governance.proposals.title')} - -
+ {localize('views.governance.proposals.title')} +
-
    +
      {#each sortedProposals as proposal} {/each} diff --git a/packages/desktop/components/ProposalQuestion.svelte b/packages/desktop/views/governance/components/ProposalQuestion.svelte similarity index 80% rename from packages/desktop/components/ProposalQuestion.svelte rename to packages/desktop/views/governance/components/ProposalQuestion.svelte index 4cb3db9b70..02abbbdf63 100644 --- a/packages/desktop/components/ProposalQuestion.svelte +++ b/packages/desktop/views/governance/components/ProposalQuestion.svelte @@ -2,16 +2,12 @@ import { AnswerStatus, EventStatus, Question } from '@iota/sdk/out/types' import { ProposalAnswer } from '@components' - import { Icon, Text } from '@ui' - import { FontWeight } from '@ui/enums' - import { TooltipIcon } from '@bloomwalletio/ui' + import { Icon, IconName, Text, TooltipIcon } from '@bloomwalletio/ui' import { ABSTAIN_VOTE_VALUE } from '@contexts/governance/constants' import { getPercentagesFromAnswerStatuses, IProposalAnswerPercentages } from '@contexts/governance' import { selectedProposal } from '@contexts/governance/stores' - import { Icon as IconEnum } from '@auxiliary/icon' - export let onQuestionClick: (questionIndex: number) => void export let onAnswerClick: (answerValue: number, questionIndex: number) => void @@ -49,23 +45,17 @@ {#each answers as answer, answerIndex} diff --git a/packages/desktop/views/governance/components/index.ts b/packages/desktop/views/governance/components/index.ts index 339e60d799..10cc878208 100644 --- a/packages/desktop/views/governance/components/index.ts +++ b/packages/desktop/views/governance/components/index.ts @@ -1,3 +1,6 @@ +export { default as ProposalInformationPane } from './ProposalInformationPane.svelte' +export { default as ProposalList } from './ProposalList.svelte' +export { default as ProposalQuestion } from './ProposalQuestion.svelte' export { default as ProposalStatusInfo } from './ProposalStatusInfo.svelte' export { default as ProposalStatusPill } from './ProposalStatusPill.svelte' export { default as ProposalStatusTimelineTooltip } from './ProposalStatusTimelineTooltip.svelte' diff --git a/packages/shared/package.json b/packages/shared/package.json index 621e0a97d4..03f42bfa0b 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -18,13 +18,11 @@ "@iota/unit-converter": "1.0.0-beta.30", "@iota/util.js": "2.0.0-rc.2", "@metamask/eth-sig-util": "7.0.1", - "@metamask/ethjs-provider-http": "0.3.0", "@spruceid/siwe-parser": "2.0.2", "@sveltejs/svelte-virtual-list": "3.0.1", "@walletconnect/jsonrpc-types": "1.0.3", "@walletconnect/types": "2.11.0", "@walletconnect/web3wallet": "1.9.5", - "eth-method-registry": "2.0.0", "http-status-codes": "2.3.0", "lottie-web": "5.12.2", "qrious": "4.0.2", diff --git a/packages/shared/src/lib/core/layer-2/abis/registry.abi.ts b/packages/shared/src/lib/core/layer-2/abis/registry.abi.ts new file mode 100644 index 0000000000..63fb12477b --- /dev/null +++ b/packages/shared/src/lib/core/layer-2/abis/registry.abi.ts @@ -0,0 +1,65 @@ +import { Abi } from '../types' + +export const REGISTRY_ABI: Abi = [ + { + constant: false, + inputs: [{ name: '_new', type: 'address' }], + name: 'setOwner', + outputs: [], + payable: false, + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'totalSignatures', + outputs: [{ name: '', type: 'uint256' }], + payable: false, + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'owner', + outputs: [{ name: '', type: 'address' }], + payable: false, + type: 'function', + }, + { constant: false, inputs: [], name: 'drain', outputs: [], payable: false, type: 'function' }, + { + constant: true, + inputs: [{ name: '', type: 'bytes4' }], + name: 'entries', + outputs: [{ name: '', type: 'string' }], + payable: false, + type: 'function', + }, + { + constant: false, + inputs: [{ name: '_method', type: 'string' }], + name: 'register', + outputs: [{ name: '', type: 'bool' }], + payable: false, + type: 'function', + }, + { inputs: [], type: 'constructor' }, + { + anonymous: false, + inputs: [ + { indexed: true, name: 'creator', type: 'address' }, + { indexed: true, name: 'signature', type: 'bytes4' }, + { indexed: false, name: 'method', type: 'string' }, + ], + name: 'Registered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, name: 'old', type: 'address' }, + { indexed: true, name: 'current', type: 'address' }, + ], + name: 'NewOwner', + type: 'event', + }, +] diff --git a/packages/shared/src/lib/core/layer-2/constants/registry-contract-address.constant.ts b/packages/shared/src/lib/core/layer-2/constants/registry-contract-address.constant.ts new file mode 100644 index 0000000000..9cf1edf894 --- /dev/null +++ b/packages/shared/src/lib/core/layer-2/constants/registry-contract-address.constant.ts @@ -0,0 +1 @@ +export const REGISTRY_CONTRACT_ADDRESS = '0x44691B39d1a75dC4E0A0346CBB15E310e6ED1E86' diff --git a/packages/shared/src/lib/core/layer-2/stores/method-registry.store.ts b/packages/shared/src/lib/core/layer-2/stores/method-registry.store.ts new file mode 100644 index 0000000000..224cd1757d --- /dev/null +++ b/packages/shared/src/lib/core/layer-2/stores/method-registry.store.ts @@ -0,0 +1,10 @@ +import { persistent } from '@core/utils/store' + +export const methodRegistry = persistent<{ [fourBytePrefix: string]: string }>('methodRegistry', {}) + +export function addMethodToRegistry(fourBytePrefix: string, signature: string): void { + methodRegistry.update((registry) => { + registry[fourBytePrefix] = signature + return registry + }) +} diff --git a/packages/shared/src/lib/core/layer-2/utils/getMethodNameForEvmTransaction.ts b/packages/shared/src/lib/core/layer-2/utils/getMethodNameForEvmTransaction.ts index 7d308896a5..d10d91fd40 100644 --- a/packages/shared/src/lib/core/layer-2/utils/getMethodNameForEvmTransaction.ts +++ b/packages/shared/src/lib/core/layer-2/utils/getMethodNameForEvmTransaction.ts @@ -1,9 +1,6 @@ -import { MethodRegistry } from 'eth-method-registry' -// @ts-ignore -import HttpProvider from '@metamask/ethjs-provider-http' -import { ETHEREUM_MAINNET_NODE } from '../constants' import { EvmTransactionData } from '../types' import featuresObject from '@features/features' +import { lookupMethodSignature } from './lookupMethodSignature' export async function getMethodNameForEvmTransaction(evmTransactionData: EvmTransactionData): Promise { const data = String(evmTransactionData.data ?? '') @@ -13,9 +10,7 @@ export async function getMethodNameForEvmTransaction(evmTransactionData: EvmTran return fourBytePrefix } - const provider = new HttpProvider(ETHEREUM_MAINNET_NODE) - const registry = new MethodRegistry({ provider, network: '1' }) - const result = await registry.lookup(fourBytePrefix) + const result = await lookupMethodSignature(fourBytePrefix) const name = result?.split('(')?.[0] if (!name) { 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 0b5670fe07..795568f6df 100644 --- a/packages/shared/src/lib/core/layer-2/utils/index.ts +++ b/packages/shared/src/lib/core/layer-2/utils/index.ts @@ -10,6 +10,7 @@ export * from './getEvmTokenMetadata' export * from './getErc20TransferSmartContractData' export * from './getErc721TransferSmartContractData' export * from './getMethodNameForEvmTransaction' +export * from './lookupMethodSignature' export * from './parseLayer2Metadata' export * from './parseLayer2MetadataForTransfer' export * from './parseSiweMessage' diff --git a/packages/shared/src/lib/core/layer-2/utils/lookupMethodSignature.ts b/packages/shared/src/lib/core/layer-2/utils/lookupMethodSignature.ts new file mode 100644 index 0000000000..5df61a337a --- /dev/null +++ b/packages/shared/src/lib/core/layer-2/utils/lookupMethodSignature.ts @@ -0,0 +1,28 @@ +import Web3 from 'web3' +import { REGISTRY_ABI } from '../abis/registry.abi' +import { ETHEREUM_MAINNET_NODE } from '../constants' +import { REGISTRY_CONTRACT_ADDRESS } from '../constants/registry-contract-address.constant' +import { get } from 'svelte/store' +import { addMethodToRegistry, methodRegistry } from '../stores/method-registry.store' + +const provider = new Web3(ETHEREUM_MAINNET_NODE) +const registry = new provider.eth.Contract(REGISTRY_ABI, REGISTRY_CONTRACT_ADDRESS) + +export async function lookupMethodSignature(fourBytePrefix: string): Promise { + const methodName = get(methodRegistry)[fourBytePrefix] + if (methodName) { + return methodName + } + + try { + const result = await registry.methods.entries(fourBytePrefix).call() + if (!result) { + return undefined + } + + addMethodToRegistry(fourBytePrefix, result) + return result + } catch (error) { + return undefined + } +} diff --git a/packages/shared/src/lib/core/wallet/actions/index.ts b/packages/shared/src/lib/core/wallet/actions/index.ts index ec51b33b01..b276c4d6ee 100644 --- a/packages/shared/src/lib/core/wallet/actions/index.ts +++ b/packages/shared/src/lib/core/wallet/actions/index.ts @@ -8,6 +8,7 @@ export * from './isTrackedNftAddress' export * from './isTrackedTokenAddress' export * from './mintNativeToken' export * from './mintNft' +export * from './mintNftCollection' export * from './rejectActivity' export * from './sendOutput' export * from './updateL2BalanceWithoutActivity' diff --git a/packages/shared/src/lib/core/wallet/actions/mintNft.ts b/packages/shared/src/lib/core/wallet/actions/mintNft.ts index 598fa77661..df37f4f117 100644 --- a/packages/shared/src/lib/core/wallet/actions/mintNft.ts +++ b/packages/shared/src/lib/core/wallet/actions/mintNft.ts @@ -15,18 +15,33 @@ import { getTransactionOptions } from '../utils' import { resetMintNftDetails } from '../stores' import { getActiveNetworkId } from '@core/network' -export async function mintNft(metadata: IIrc27Metadata, quantity: number): Promise { +export async function mintNft( + metadata: IIrc27Metadata, + startIndex: number, + quantity: number, + collectionId?: string +): Promise { try { const account = getSelectedAccount() const networkId = getActiveNetworkId() updateSelectedAccount({ isTransferring: true }) - const mintNftParams: MintNftParams = { - issuer: account.depositAddress, - immutableMetadata: Converter.utf8ToHex(JSON.stringify(metadata)), + const allNftParams: MintNftParams[] = [] + for (let i = startIndex; i < startIndex + quantity; i++) { + const updatedMetadata = { + ...metadata, + name: metadata.name.replace('{id}', i.toString()), + description: metadata.description?.replace('{id}', i.toString()), + uri: metadata.uri.replace('{id}', i.toString()), + } + const mintNftParams: MintNftParams = { + address: account.depositAddress, + issuer: collectionId || account.depositAddress, + immutableMetadata: Converter.utf8ToHex(JSON.stringify(updatedMetadata)), + } + allNftParams.push(mintNftParams) } - const allNftParams: MintNftParams[] = Array(quantity).fill(mintNftParams) // Mint NFT const preparedTransaction = await account.prepareMintNfts( diff --git a/packages/shared/src/lib/core/wallet/actions/mintNftCollection.ts b/packages/shared/src/lib/core/wallet/actions/mintNftCollection.ts new file mode 100644 index 0000000000..088262ef45 --- /dev/null +++ b/packages/shared/src/lib/core/wallet/actions/mintNftCollection.ts @@ -0,0 +1,35 @@ +import { showNotification } from '@auxiliary/notification' +import { getSelectedAccount, updateSelectedAccount } from '@core/account/stores' +import { sendPreparedTransaction } from '@core/wallet/utils' +import { localize } from '@core/i18n' +import { IIrc27Metadata } from '@core/nfts' +import { Converter } from '@core/utils' +import { AliasOutputParams } from '@iota/sdk/out/types' +import { getActiveNetworkId } from '@core/network/actions' +import { processAndAddToActivities } from '@core/activity' + +export async function mintNftCollection(metadata: IIrc27Metadata): Promise { + try { + const account = getSelectedAccount() + const networkId = getActiveNetworkId() + updateSelectedAccount({ isTransferring: true }) + + const mintNftCollectionParams: AliasOutputParams = { + // issuer: account.depositAddress, // TODO: uncomment when added to iota-sdk + immutableMetadata: Converter.utf8ToHex(JSON.stringify(metadata)), + } + + // Mint NFT + const preparedTransaction = await account.prepareCreateAliasOutput(mintNftCollectionParams) + const transaction = await sendPreparedTransaction(preparedTransaction) + showNotification({ + variant: 'success', + text: localize('notifications.mintNft.success'), + }) + await processAndAddToActivities(transaction, account, networkId) + } catch (err) { + return Promise.reject(err) + } finally { + updateSelectedAccount({ isTransferring: false }) + } +} diff --git a/packages/shared/src/lib/core/wallet/interfaces/index.ts b/packages/shared/src/lib/core/wallet/interfaces/index.ts index 8569b56f37..516e9f5e3c 100644 --- a/packages/shared/src/lib/core/wallet/interfaces/index.ts +++ b/packages/shared/src/lib/core/wallet/interfaces/index.ts @@ -2,6 +2,7 @@ export * from './account-subject.interface' export * from './address-subject.interface' export * from './contact-subject.interface' export * from './mint-nft-details.interface' +export * from './mint-nft-collection-details.interface' export * from './mint-token-details.interface' export * from './network-subject.interface' export * from './participation.interface' diff --git a/packages/shared/src/lib/core/wallet/interfaces/mint-nft-collection-details.interface.ts b/packages/shared/src/lib/core/wallet/interfaces/mint-nft-collection-details.interface.ts new file mode 100644 index 0000000000..a6fce4641a --- /dev/null +++ b/packages/shared/src/lib/core/wallet/interfaces/mint-nft-collection-details.interface.ts @@ -0,0 +1,3 @@ +import { IIrc27Metadata } from '@core/nfts' + +export interface IMintNftCollectionDetails extends Omit {} diff --git a/packages/shared/src/lib/core/wallet/interfaces/mint-nft-details.interface.ts b/packages/shared/src/lib/core/wallet/interfaces/mint-nft-details.interface.ts index b05ec8a577..8d66b288e2 100644 --- a/packages/shared/src/lib/core/wallet/interfaces/mint-nft-details.interface.ts +++ b/packages/shared/src/lib/core/wallet/interfaces/mint-nft-details.interface.ts @@ -1,5 +1,7 @@ import { IIrc27Metadata } from '@core/nfts' export interface IMintNftDetails extends IIrc27Metadata { + collectionId?: string quantity: number + startIndex: number } diff --git a/packages/shared/src/lib/core/wallet/stores/index.ts b/packages/shared/src/lib/core/wallet/stores/index.ts index 1d872c4aba..eb3dfd58c0 100644 --- a/packages/shared/src/lib/core/wallet/stores/index.ts +++ b/packages/shared/src/lib/core/wallet/stores/index.ts @@ -1,3 +1,4 @@ +export * from './mint-nft-collection-details.store' export * from './mint-nft-details.store' export * from './mint-token-details.store' export * from './send-flow-parameter.store' diff --git a/packages/shared/src/lib/core/wallet/stores/mint-nft-collection-details.store.ts b/packages/shared/src/lib/core/wallet/stores/mint-nft-collection-details.store.ts new file mode 100644 index 0000000000..6432c52b54 --- /dev/null +++ b/packages/shared/src/lib/core/wallet/stores/mint-nft-collection-details.store.ts @@ -0,0 +1,12 @@ +import { writable } from 'svelte/store' +import { IMintNftCollectionDetails } from '../interfaces' + +export const mintNftCollectionDetails = writable(undefined) + +export function setMintNftCollectionDetails(payload: IMintNftCollectionDetails): void { + mintNftCollectionDetails.set(payload) +} + +export function resetMintNftCollectionDetails(): void { + mintNftCollectionDetails.set(undefined) +} diff --git a/packages/shared/src/lib/features/interfaces/developer-features.interface.ts b/packages/shared/src/lib/features/interfaces/developer-features.interface.ts index a7953609f0..e370f4aac2 100644 --- a/packages/shared/src/lib/features/interfaces/developer-features.interface.ts +++ b/packages/shared/src/lib/features/interfaces/developer-features.interface.ts @@ -3,6 +3,7 @@ import { IFeatureFlag } from './feature-flag.interface' export interface IDeveloperFeatures extends IFeatureFlag { faucet: IFeatureFlag mintNft: IFeatureFlag + mintNftCollection: IFeatureFlag mintNativeTokens: IFeatureFlag alias: IFeatureFlag refreshTokens: IFeatureFlag diff --git a/packages/shared/src/locales/en.json b/packages/shared/src/locales/en.json index c7246fd4d7..f561d50236 100644 --- a/packages/shared/src/locales/en.json +++ b/packages/shared/src/locales/en.json @@ -1159,7 +1159,7 @@ "errors": { "invalidMimetype": "Invalid MimeType, check if the file type is supported", "quantityTooSmall": "Quantity needs to be greater than 0", - "quantityTooLarge": "Quantity needs to be smaller than 64", + "quantityTooLarge": "Quantity needs to be smaller than 127", "emptyName": "Name is a required field", "invalidURI": "Invalid URI, please provide a valid URI", "notReachable": "URI not reachable, unable to check NFT type", @@ -1357,6 +1357,7 @@ "addProposal": "Add proposal", "removeProposal": "Remove proposal", "changeNode": "Change node", + "changeNodeUrl": "Change node URL", "stopVoting": "Stop voting", "revote": "Revote", "skipAndKeep": "Skip and keep old password", @@ -1365,7 +1366,8 @@ "switchAccount": "Switch account", "syncAccounts": "Sync accounts", "importToken": "Import {type} token", - "syncTokens": "Force sync tokens" + "syncTokens": "Force sync tokens", + "mintNftCollection": "Mint NFT collection" }, "general": { "recipient": "Recipient", @@ -1586,7 +1588,9 @@ "raffle": "Raffle", "governance": "Governance", "baseCoin": "Base Coin", - "unverifiedContract": "Unverified Contract" + "unverifiedContract": "Unverified Contract", + "startIndex": "Start index", + "mintNftCollectionDescription": "Mint collection NFT in Alias Output" }, "filters":{ "title": "Filters", @@ -2070,7 +2074,8 @@ "description": "Optional parameter: A description of the NFT", "attributes": "Optional parameter: An array of traits and values that define attributes of the NFT", "uri": "To create a URI using custom media, first upload your file to IPFS via a storage service (e.g. https://nft.storage/)", - "quantity": "Optional parameter: The quantity of copies minted with this metadata." + "quantity": "Optional parameter: The quantity of copies minted with this metadata.", + "startIndex": "Optional parameter: The start index of the NFTs to mint." }, "governance": { "removeProposalWarning": "You must stop voting for this proposal before removing it.", diff --git a/yarn.lock b/yarn.lock index 51e2bd2299..5a8a3f8791 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1920,13 +1920,6 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" -"@metamask/ethjs-provider-http@0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@metamask/ethjs-provider-http/-/ethjs-provider-http-0.3.0.tgz#7b86ccc5220e8c0c29cceef4172450da880699aa" - integrity sha512-t4dHYDSXMJZc46SwTgbcXYWs4uzb311dzHe730ZKR0BiPbos8oc9+toMw43w+ziqymhAdIbYIXo3hXX3dw7tOw== - dependencies: - xhr2 "0.2.1" - "@metamask/utils@^8.0.0", "@metamask/utils@^8.1.0": version "8.3.0" resolved "https://registry.yarnpkg.com/@metamask/utils/-/utils-8.3.0.tgz#a20de447aeb9ffb75924d822a186a597033984b6" @@ -3838,14 +3831,6 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" -babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -4601,11 +4586,6 @@ copy-webpack-plugin@11.0.0: schema-utils "^4.0.0" serialize-javascript "^6.0.0" -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - core-js@^3.1.4: version "3.36.0" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.36.0.tgz#e752fa0b0b462a0787d56e9d73f80b0f7c0dde68" @@ -5734,13 +5714,6 @@ eth-lib@^0.1.26: ws "^3.0.0" xhr-request-promise "^0.1.2" -eth-method-registry@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eth-method-registry/-/eth-method-registry-2.0.0.tgz#96b643891d1c72853e709e3d74d89103a48a4460" - integrity sha512-uzwguuAdnd83SjogNo0xMd7pSuyK1dlyAeVGB++OaY7xaprT+31jXkBGYSIkmTz7sGIVf6A1Qy9Ir+/hXs4jWg== - dependencies: - ethjs "^0.4.0" - ethereum-bloom-filters@^1.0.6: version "1.0.10" resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" @@ -5826,81 +5799,6 @@ ethers@5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethjs-abi@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.2.0.tgz#d3e2c221011520fc499b71682036c14fcc2f5b25" - integrity sha512-ELExSTNV7rm9WA22pA3kxNmaQz1o/peM8X1pZdWypEhuNzFjOR+hIV09Mcfw69Q0kbdd7JNGAFb5dyoL/bnItA== - dependencies: - bn.js "4.11.6" - js-sha3 "0.5.5" - number-to-bn "1.7.0" - -ethjs-abi@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.2.1.tgz#e0a7a93a7e81163a94477bad56ede524ab6de533" - integrity sha512-g2AULSDYI6nEJyJaEVEXtTimRY2aPC2fi7ddSy0W+LXvEVL8Fe1y76o43ecbgdUKwZD+xsmEgX1yJr1Ia3r1IA== - dependencies: - bn.js "4.11.6" - js-sha3 "0.5.5" - number-to-bn "1.7.0" - -ethjs-contract@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ethjs-contract/-/ethjs-contract-0.2.3.tgz#f113ced8ed1c9c635b0b7ec71901340b64e8cded" - integrity sha512-fKsHm57wxwHrZhVlD8AHU2lC2G3c1fmvoEz15BpqIkuGWiTbjuvrQo2Avc+3EQpSsTFWNdyxC0h1WKRcn5kkyQ== - dependencies: - babel-runtime "^6.26.0" - ethjs-abi "0.2.0" - ethjs-filter "0.1.8" - ethjs-util "0.1.3" - js-sha3 "0.5.5" - -ethjs-filter@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/ethjs-filter/-/ethjs-filter-0.1.8.tgz#2b02726b820ed4dd3860614d185c0c0f7ed1747f" - integrity sha512-qTDPskDL2UadHwjvM8A+WG9HwM4/FoSY3p3rMJORkHltYcAuiQZd2otzOYKcL5w2Q3sbAkW/E3yt/FPFL/AVXA== - -ethjs-format@0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/ethjs-format/-/ethjs-format-0.2.7.tgz#20c92f31c259a381588d069830d838b489774b86" - integrity sha512-uNYAi+r3/mvR3xYu2AfSXx5teP4ovy9z2FrRsblU+h2logsaIKZPi9V3bn3V7wuRcnG0HZ3QydgZuVaRo06C4Q== - dependencies: - bn.js "4.11.6" - ethjs-schema "0.2.1" - ethjs-util "0.1.3" - is-hex-prefixed "1.0.0" - number-to-bn "1.7.0" - strip-hex-prefix "1.0.0" - -ethjs-provider-http@0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/ethjs-provider-http/-/ethjs-provider-http-0.1.6.tgz#1ec5d9b4be257ef1d56a500b22a741985e889420" - integrity sha512-y054N5xyyx43KTQjgdkAEj2uEa/flwpENU5ldx/rmA0Q2yy0vyB2lsOIn/7V0uADMc4iRSHZfnFc9b9YS5Qkdw== - dependencies: - xhr2 "0.1.3" - -ethjs-query@0.3.8: - version "0.3.8" - resolved "https://registry.yarnpkg.com/ethjs-query/-/ethjs-query-0.3.8.tgz#aa5af02887bdd5f3c78b3256d0f22ffd5d357490" - integrity sha512-/J5JydqrOzU8O7VBOwZKUWXxHDGr46VqNjBCJgBVNNda+tv7Xc8Y2uJc6aMHHVbeN3YOQ7YRElgIc0q1CI02lQ== - dependencies: - babel-runtime "^6.26.0" - ethjs-format "0.2.7" - ethjs-rpc "0.2.0" - promise-to-callback "^1.0.0" - -ethjs-rpc@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ethjs-rpc/-/ethjs-rpc-0.2.0.tgz#3d0011e32cfff156ed6147818c6fb8f801701b4c" - integrity sha512-RINulkNZTKnj4R/cjYYtYMnFFaBcVALzbtEJEONrrka8IeoarNB9Jbzn+2rT00Cv8y/CxAI+GgY1d0/i2iQeOg== - dependencies: - promise-to-callback "^1.0.0" - -ethjs-schema@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/ethjs-schema/-/ethjs-schema-0.2.1.tgz#47e138920421453617069034684642e26bb310f4" - integrity sha512-DXd8lwNrhT9sjsh/Vd2Z+4pfyGxhc0POVnLBUfwk5udtdoBzADyq+sK39dcb48+ZU+2VgtwHxtGWnLnCfmfW5g== - ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -5909,30 +5807,6 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.3.tgz#dfd5ea4a400dc5e421a889caf47e081ada78bb55" - integrity sha512-QqpX2dsEG2geSMG9dTMJVhfP1kGRdGMNjiHPiTjkju+X5cB0PQIwUzRr5k21pFkgF5zuLccqe83p7Gh5fFM5tQ== - dependencies: - is-hex-prefixed "1.0.0" - strip-hex-prefix "1.0.0" - -ethjs@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/ethjs/-/ethjs-0.4.0.tgz#53e6312b6fe050620bd6c5813cb683191cfd6e4a" - integrity sha512-UnQeRMpQ+JETN2FviexEskUwByid+eO8rybjPnk2DNUzjUn0VKNrUbiCAud7Es6otDFwjUeOS58vMZwkZxIIog== - dependencies: - bn.js "4.11.6" - ethjs-abi "0.2.1" - ethjs-contract "0.2.3" - ethjs-filter "0.1.8" - ethjs-provider-http "0.1.6" - ethjs-query "0.3.8" - ethjs-unit "0.1.6" - ethjs-util "0.1.3" - js-sha3 "0.5.5" - number-to-bn "1.7.0" - event-emitter@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -7213,11 +7087,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fn@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" - integrity sha512-XoFPJQmsAShb3jEQRfzf2rqXavq7fIqF/jOekp308JlThqrODnMpweVSGilKTCXELfLhltGP2AGgbQGVP8F1dg== - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -7903,11 +7772,6 @@ jiti@^1.18.2, jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== -js-sha3@0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a" - integrity sha512-yLLwn44IVeunwjpDVTDZmQeVbB0h+dZpY2eO68B/Zik8hu6dH+rKeLxwua79GGIvW6xr8NBAcrtiUbYrTjEFTA== - js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -9647,14 +9511,6 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -promise-to-callback@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" - integrity sha512-uhMIZmKM5ZteDMfLgJnoSq9GCwsNKrYau73Awf1jIy6/eUcuuZ3P+CD9zUv0kJsIUbU+x6uLNIhXhLHDs1pNPA== - dependencies: - is-fn "^1.0.0" - set-immediate-shim "^1.0.1" - prompts@2.4.2, prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -9932,11 +9788,6 @@ reflect-metadata@^0.1.13: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - regexp-tree@~0.1.1: version "0.1.27" resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" @@ -10402,11 +10253,6 @@ set-function-name@^2.0.1: functions-have-names "^1.2.3" has-property-descriptors "^1.0.0" -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - integrity sha512-Li5AOqrZWCVA2n5kryzEmqai6bKSIvpz5oUJHPVj6+dsbD3X1ixtsY5tEnsaNpH3pFAHmG8eIHUrtEtohrg+UQ== - setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -12467,16 +12313,6 @@ xhr-request@^1.0.1, xhr-request@^1.1.0: url-set-query "^1.0.0" xhr "^2.0.4" -xhr2@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.3.tgz#cbfc4759a69b4a888e78cf4f20b051038757bd11" - integrity sha512-6RmGK22QwC7yXB1CRwyLWuS2opPcKOlAu0ViAnyZjDlzrEmCKL4kLHkfvB8oMRWeztMsNoDGAjsMZY15w/4tTw== - -xhr2@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.2.1.tgz#4e73adc4f9cfec9cbd2157f73efdce3a5f108a93" - integrity sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw== - xhr@^2.0.4, xhr@^2.3.3: version "2.6.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d"