Multiple token airdrop to a single address (with shuffle)

This commit is contained in:
Serkan Reis
2022-10-10 12:37:20 +03:00
parent 0541c3f046
commit de18a319b8
4 changed files with 173 additions and 6 deletions
+57
View File
@@ -0,0 +1,57 @@
import clsx from 'clsx'
import React from 'react'
import { toast } from 'react-hot-toast'
import { csvToArray } from 'utils/csvToArray'
import type { AirdropAllocation } from 'utils/isValidAccountsFile'
import { isValidAccountsFile } from 'utils/isValidAccountsFile'
interface AirdropUploadProps {
onChange: (data: AirdropAllocation[]) => void
}
export const AirdropUpload = ({ onChange }: AirdropUploadProps) => {
const onFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (!event.target.files) return toast.error('Error opening file')
if (!event.target.files[0].name.endsWith('.csv')) {
toast.error('Please select a .csv file!')
return onChange([])
}
const reader = new FileReader()
reader.onload = (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())
if (!isValidAccountsFile(accountsData)) {
event.target.value = ''
return onChange([])
}
return onChange(accountsData)
} catch (error: any) {
toast.error(error.message)
}
}
reader.readAsText(event.target.files[0])
}
return (
<div
className={clsx(
'flex relative justify-center items-center mt-2 space-y-4 w-full h-32',
'rounded border-2 border-white/20 border-dashed',
)}
>
<input
accept=".csv"
className={clsx(
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
)}
id="airdrop-file"
multiple
onChange={onFileChange}
type="file"
/>
</div>
)
}
+28 -6
View File
@@ -1,3 +1,4 @@
import { AirdropUpload } from 'components/AirdropUpload'
import { Button } from 'components/Button'
import type { DispatchExecuteArgs } from 'components/collections/actions/actions'
import { dispatchExecute, isEitherType, previewExecutePayload } from 'components/collections/actions/actions'
@@ -11,15 +12,15 @@ import { useInputState, useNumberInputState } from 'components/forms/FormInput.h
import { InputDateTime } from 'components/InputDateTime'
import { JsonPreview } from 'components/JsonPreview'
import { TransactionHash } from 'components/TransactionHash'
import { WhitelistUpload } from 'components/WhitelistUpload'
import { useWallet } from 'contexts/wallet'
import type { MinterInstance } from 'contracts/minter'
import type { SG721Instance } from 'contracts/sg721'
import type { FormEvent } from 'react'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { toast } from 'react-hot-toast'
import { FaArrowRight } from 'react-icons/fa'
import { useMutation } from 'react-query'
import type { AirdropAllocation } from 'utils/isValidAccountsFile'
import { TextInput } from '../../forms/FormInput'
@@ -40,6 +41,7 @@ export const CollectionActions = ({
const [lastTx, setLastTx] = useState('')
const [timestamp, setTimestamp] = useState<Date | undefined>(undefined)
const [airdropAllocationArray, setAirdropAllocationArray] = useState<AirdropAllocation[]>([])
const [airdropArray, setAirdropArray] = useState<string[]>([])
const actionComboboxState = useActionsComboboxState()
@@ -113,6 +115,22 @@ export const CollectionActions = ({
txSigner: wallet.address,
type,
}
useEffect(() => {
const addresses: string[] = []
airdropAllocationArray.forEach((allocation) => {
for (let i = 0; i < Number(allocation.amount); i++) {
addresses.push(allocation.address)
}
})
//shuffle the addresses array
for (let i = addresses.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[addresses[i], addresses[j]] = [addresses[j], addresses[i]]
}
setAirdropArray(addresses)
}, [airdropAllocationArray])
const { isLoading, mutate } = useMutation(
async (event: FormEvent) => {
event.preventDefault()
@@ -138,8 +156,9 @@ export const CollectionActions = ({
},
)
const airdropFileOnChange = (data: string[]) => {
setAirdropArray(data)
const airdropFileOnChange = (data: AirdropAllocation[]) => {
setAirdropAllocationArray(data)
console.log(data)
}
return (
@@ -154,8 +173,11 @@ export const CollectionActions = ({
{showTokenIdListField && <TextInput {...tokenIdListState} />}
{showNumberOfTokensField && <NumberInput {...batchNumberState} />}
{showAirdropFileField && (
<FormGroup subtitle="TXT file that contains the airdrop addresses" title="Airdrop File">
<WhitelistUpload onChange={airdropFileOnChange} />
<FormGroup
subtitle="CSV file that contains the airdrop addresses and the amount of tokens allocated for each address."
title="Airdrop File"
>
<AirdropUpload onChange={airdropFileOnChange} />
</FormGroup>
)}
<Conditional test={showDateField}>