Names support for Collection Actions > Airdrop Tokens
This commit is contained in:
parent
f1a2c153e0
commit
468bf95cc2
@ -1,16 +1,60 @@
|
||||
import { toUtf8 } from '@cosmjs/encoding'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import { useWallet } from 'contexts/wallet'
|
||||
import React, { useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import { SG721_NAME_ADDRESS } from 'utils/constants'
|
||||
import { csvToArray } from 'utils/csvToArray'
|
||||
import type { AirdropAllocation } from 'utils/isValidAccountsFile'
|
||||
import { isValidAccountsFile } from 'utils/isValidAccountsFile'
|
||||
import { isValidAddress } from 'utils/isValidAddress'
|
||||
|
||||
interface AirdropUploadProps {
|
||||
onChange: (data: AirdropAllocation[]) => void
|
||||
}
|
||||
|
||||
export const AirdropUpload = ({ onChange }: AirdropUploadProps) => {
|
||||
const wallet = useWallet()
|
||||
const [resolvedAllocationData, setResolvedAllocationData] = useState<AirdropAllocation[]>([])
|
||||
|
||||
const resolveAllocationData = async (allocationData: AirdropAllocation[]) => {
|
||||
if (!allocationData.length) return []
|
||||
await new Promise((resolve) => {
|
||||
let i = 0
|
||||
allocationData.map(async (data) => {
|
||||
if (!wallet.client) throw new Error('Wallet not connected')
|
||||
await wallet.client
|
||||
.queryContractRaw(
|
||||
SG721_NAME_ADDRESS,
|
||||
toUtf8(
|
||||
Buffer.from(
|
||||
`0006${Buffer.from('tokens').toString('hex')}${Buffer.from(
|
||||
data.address.trim().substring(0, data.address.lastIndexOf('.stars')),
|
||||
).toString('hex')}`,
|
||||
'hex',
|
||||
).toString(),
|
||||
),
|
||||
)
|
||||
.then((res) => {
|
||||
const tokenUri = JSON.parse(new TextDecoder().decode(res as Uint8Array)).token_uri
|
||||
if (tokenUri && isValidAddress(tokenUri))
|
||||
resolvedAllocationData.push({ address: tokenUri, amount: data.amount })
|
||||
else toast.error(`Resolved address is empty or invalid for the name: ${data.address}`)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e)
|
||||
toast.error(`Error resolving address for the name: ${data.address}`)
|
||||
})
|
||||
|
||||
i++
|
||||
if (i === allocationData.length) resolve(resolvedAllocationData)
|
||||
})
|
||||
})
|
||||
return resolvedAllocationData
|
||||
}
|
||||
|
||||
const onFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setResolvedAllocationData([])
|
||||
if (!event.target.files) return toast.error('Error opening file')
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!event.target.files[0]?.name.endsWith('.csv')) {
|
||||
@ -18,16 +62,29 @@ export const AirdropUpload = ({ onChange }: AirdropUploadProps) => {
|
||||
return onChange([])
|
||||
}
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e: ProgressEvent<FileReader>) => {
|
||||
reader.onload = async (e: ProgressEvent<FileReader>) => {
|
||||
try {
|
||||
if (!e.target?.result) return toast.error('Error parsing file.')
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
const accountsData = csvToArray(e.target.result.toString())
|
||||
console.log(accountsData)
|
||||
if (!isValidAccountsFile(accountsData)) {
|
||||
event.target.value = ''
|
||||
return onChange([])
|
||||
}
|
||||
return onChange(accountsData)
|
||||
await resolveAllocationData(accountsData.filter((data) => data.address.trim().endsWith('.stars'))).finally(
|
||||
() => {
|
||||
return onChange(
|
||||
accountsData
|
||||
.filter((data) => data.address.startsWith('stars') && !data.address.endsWith('.stars'))
|
||||
.map((data) => ({
|
||||
address: data.address.trim(),
|
||||
amount: data.amount,
|
||||
}))
|
||||
.concat(resolvedAllocationData),
|
||||
)
|
||||
},
|
||||
)
|
||||
} catch (error: any) {
|
||||
toast.error(error.message, { style: { maxWidth: 'none' } })
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ export const CollectionActions = ({
|
||||
}
|
||||
const resolveRecipientAddress = async () => {
|
||||
await resolveAddress(recipientState.value.trim(), wallet).then((resolvedAddress) => {
|
||||
setResolvedRecipientAddress(resolvedAddress as string)
|
||||
setResolvedRecipientAddress(resolvedAddress)
|
||||
})
|
||||
}
|
||||
useEffect(() => {
|
||||
@ -216,7 +216,7 @@ export const CollectionActions = ({
|
||||
royalty_info:
|
||||
royaltyPaymentAddressState.value && royaltyShareState.value
|
||||
? {
|
||||
payment_address: resolvedAddress as string,
|
||||
payment_address: resolvedAddress,
|
||||
share: (Number(royaltyShareState.value) / 100).toString(),
|
||||
}
|
||||
: undefined,
|
||||
@ -344,6 +344,7 @@ export const CollectionActions = ({
|
||||
|
||||
const airdropFileOnChange = (data: AirdropAllocation[]) => {
|
||||
setAirdropAllocationArray(data)
|
||||
console.log('airdrop allocation array: ', data)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -13,17 +13,19 @@ export const isValidAccountsFile = (file: AirdropAllocation[]) => {
|
||||
sumOfAmounts += Number(allocation.amount)
|
||||
})
|
||||
if (sumOfAmounts > 10000) {
|
||||
toast.error(`Accounts file must have less than 10000 accounts`)
|
||||
toast.error(`Accounts file must cover less than 10000 tokens`)
|
||||
return false
|
||||
}
|
||||
|
||||
const checks = file.map((account) => {
|
||||
// Check if address is valid bech32 address
|
||||
if (!isValidAddress(account.address)) {
|
||||
return { address: false }
|
||||
if (account.address.trim().startsWith('stars')) {
|
||||
if (!isValidAddress(account.address.trim())) {
|
||||
return { address: false }
|
||||
}
|
||||
}
|
||||
// Check if address start with stars
|
||||
if (!account.address.startsWith('stars')) {
|
||||
if (!account.address.trim().startsWith('stars') && !account.address.trim().endsWith('.stars')) {
|
||||
return { address: false }
|
||||
}
|
||||
// Check if amount is valid
|
||||
@ -33,7 +35,9 @@ export const isValidAccountsFile = (file: AirdropAllocation[]) => {
|
||||
return null
|
||||
})
|
||||
|
||||
const isStargazeAddresses = file.every((account) => account.address.startsWith('stars'))
|
||||
const isStargazeAddresses = file.every(
|
||||
(account) => account.address.trim().startsWith('stars') || account.address.trim().endsWith('.stars'),
|
||||
)
|
||||
if (!isStargazeAddresses) {
|
||||
toast.error('All accounts must be on the same network')
|
||||
return false
|
||||
|
@ -5,7 +5,7 @@ import type { KeplrWalletStore } from '../contexts/wallet'
|
||||
import { SG721_NAME_ADDRESS } from './constants'
|
||||
import { isValidAddress } from './isValidAddress'
|
||||
|
||||
export const resolveAddress = async (name: string, wallet: KeplrWalletStore): Promise<string | undefined> => {
|
||||
export const resolveAddress = async (name: string, wallet: KeplrWalletStore): Promise<string> => {
|
||||
if (!name.trim().endsWith('.stars')) return name
|
||||
|
||||
if (wallet.client) {
|
||||
|
Loading…
Reference in New Issue
Block a user