From 9890e707bd421592e2ac65e9725af50d59b33cdf Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Mon, 20 Jun 2022 11:09:54 +0300 Subject: [PATCH 01/11] Amount validity check update --- utils/isValidAccountsFile.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/isValidAccountsFile.ts b/utils/isValidAccountsFile.ts index faaeb612..79c7d6a1 100644 --- a/utils/isValidAccountsFile.ts +++ b/utils/isValidAccountsFile.ts @@ -22,7 +22,7 @@ export const isValidAccountsFile = (file: AccountProps[]) => { return { address: false } } // Check if amount is valid - if (!Number(account.amount)) { + if (!Number.isInteger(Number(account.amount)) || !(Number(account.amount) > 0)) { return { amount: false } } return null @@ -33,7 +33,7 @@ export const isValidAccountsFile = (file: AccountProps[]) => { return false } if (checks.filter((check) => check?.amount === false).length > 0) { - toast.error('Invalid amount in file') + toast.error('Invalid amount in file. Amount must be a positive integer.') return false } From 0221c13b8b0e7e8e07da3e5d20f347d9326aaba9 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Tue, 21 Jun 2022 08:37:56 +0300 Subject: [PATCH 02/11] Airdrop fund and register subtitle fix (#219) * Fix subtitles for airdrop register & fund * Typo fix --- pages/airdrops/fund.tsx | 2 +- pages/airdrops/register.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/airdrops/fund.tsx b/pages/airdrops/fund.tsx index 7a6d88e5..76fbffee 100644 --- a/pages/airdrops/fund.tsx +++ b/pages/airdrops/fund.tsx @@ -168,7 +168,7 @@ const FundAirdropPage: NextPage = () => {
{
Date: Fri, 24 Jun 2022 09:04:31 +0300 Subject: [PATCH 03/11] Native token airdrop support (#220) * Add token selection UI to airdrop create page * Fix native token logic on airdrop create * Add new method for funding in merkle airdrop contract * Update airdrop props * Use correct funding method based on airdrop token * Implement native token logic on claim page * Fix client error on website load * Update instantiate message with native token * Fix balance error on website load --- contracts/cw20/merkleAirdrop/contract.ts | 50 ++++++++++++++ contracts/cw20/merkleAirdrop/useContract.ts | 4 +- pages/airdrops/[address]/claim.tsx | 23 +++++-- pages/airdrops/create.tsx | 74 ++++++++++++++++----- pages/airdrops/fund.tsx | 23 ++++--- utils/constants.ts | 1 + 6 files changed, 145 insertions(+), 30 deletions(-) diff --git a/contracts/cw20/merkleAirdrop/contract.ts b/contracts/cw20/merkleAirdrop/contract.ts index 8a3990e1..c5b27e15 100644 --- a/contracts/cw20/merkleAirdrop/contract.ts +++ b/contracts/cw20/merkleAirdrop/contract.ts @@ -64,6 +64,7 @@ export interface CW20MerkleAirdropInstance { stage: number, ) => Promise depositEscrow: () => Promise + fundWithSend: (amount: string) => Promise } export interface CW20MerkleAirdropMessages { @@ -77,6 +78,7 @@ export interface CW20MerkleAirdropMessages { ) => [RegisterMessage, ReleaseEscrowMessage] depositEscrow: (airdropAddress: string) => DepositEscrowMessage claim: (airdropAddress: string, stage: number, amount: string, proof: string[]) => ClaimMessage + fundWithSend: (recipient: string, amount: string) => FundWithSendMessage } export interface InstantiateMessage { @@ -137,6 +139,12 @@ export interface ClaimMessage { funds: Coin[] } +export interface FundWithSendMessage { + from_address: string + to_address: string + amount: Coin[] +} + export interface CW20MerkleAirdropContract { instantiate: ( senderAddress: string, @@ -333,6 +341,38 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin } } + const fundWithSend = async (amount: string): Promise => { + const config = getNetworkConfig(NETWORK) + const signed = await client.sign( + txSigner, + [ + { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + fromAddress: txSigner, + toAddress: contractAddress, + amount: [coin(amount, config.feeToken)], + }, + }, + ], + fee, + '', + ) + const result = await client.broadcastTx(TxRaw.encode(signed).finish()) + if (isDeliverTxFailure(result)) { + throw new Error( + [ + `Error when broadcasting tx ${result.transactionHash} at height ${result.height}.`, + `Code: ${result.code}; Raw log: ${result.rawLog ?? ''}`, + ].join(' '), + ) + } + return { + signed, + txHash: result.transactionHash, + } + } + return { contractAddress, getConfig, @@ -346,6 +386,7 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin burn, registerAndReleaseEscrow, depositEscrow, + fundWithSend, } } @@ -443,11 +484,20 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin } } + const fundWithSend = (recipient: string, amount: string): FundWithSendMessage => { + return { + from_address: txSigner, + to_address: recipient, + amount: [coin(amount, getNetworkConfig(NETWORK).feeToken)], + } + } + return { instantiate, registerAndReleaseEscrow, depositEscrow, claim, + fundWithSend, } } diff --git a/contracts/cw20/merkleAirdrop/useContract.ts b/contracts/cw20/merkleAirdrop/useContract.ts index a563aa58..ce12b3fc 100644 --- a/contracts/cw20/merkleAirdrop/useContract.ts +++ b/contracts/cw20/merkleAirdrop/useContract.ts @@ -32,7 +32,9 @@ export function useCW20MerkleAirdropContract(): UseCW20MerkleAirdropContractProp }, []) useEffect(() => { - const cw20MerkleAirdropContract = initContract(wallet.getClient(), wallet.address) + const client = wallet.getClient() + if (!client) return + const cw20MerkleAirdropContract = initContract(client, wallet.address) setCW20MerkleAirdrop(cw20MerkleAirdropContract) }, [wallet]) diff --git a/pages/airdrops/[address]/claim.tsx b/pages/airdrops/[address]/claim.tsx index a1bd0323..16d6b143 100644 --- a/pages/airdrops/[address]/claim.tsx +++ b/pages/airdrops/[address]/claim.tsx @@ -34,7 +34,12 @@ const ClaimAirdropPage: NextPage = () => { const [name, setName] = useState('') const [cw20TokenAddress, setCW20TokenAddress] = useState('') const [balance, setBalance] = useState(0) - const [cw20TokenInfo, setCW20TokenInfo] = useState(null) + const [cw20TokenInfo, setCW20TokenInfo] = useState({ + name: 'Juno Native Token', + decimals: 6, + symbol: getConfig(NETWORK).feeToken.slice(1).toUpperCase(), + total_supply: '', + }) const [stage, setStage] = useState(0) const [airdropState, setAirdropState] = useState('loading') @@ -82,6 +87,10 @@ const ClaimAirdropPage: NextPage = () => { void getAirdropInfo() }, [contractAddress, wallet.address, wallet.initialized]) + useEffect(() => { + setBalance(Number(wallet.balance[0]?.amount)) + }, [wallet.balance]) + useEffect(() => { if (!cw20BaseContract || !cw20TokenAddress) return @@ -190,7 +199,9 @@ const ClaimAirdropPage: NextPage = () => { {contractAddress} {cw20TokenInfo?.name} {cw20TokenInfo?.symbol} - {cw20TokenAddress} + + {cw20TokenAddress} + {convertDenomToReadable(amount)} {cw20TokenInfo?.symbol} @@ -210,9 +221,11 @@ const ClaimAirdropPage: NextPage = () => {
- + + + + + + + + +
+ {/* Warning: */} + + + +
+
+
+ )} + + + + + + +
+ + ) +} + +export default withMetadata(ManageAirdropPage, { center: false }) diff --git a/pages/airdrops/register.tsx b/pages/airdrops/register.tsx index 2244beb7..e15545d0 100644 --- a/pages/airdrops/register.tsx +++ b/pages/airdrops/register.tsx @@ -49,11 +49,13 @@ const RegisterAirdropPage: NextPage = () => { : { at_time: (airdrop.expiration * 1000000000).toString() } : null + const totalAmount = airdrop?.totalAmount ? airdrop.totalAmount : 0 + const contractAddressDebounce = useDebounce(contractAddress, 500) const transactionMessage = contract ?.messages() - ?.registerAndReleaseEscrow(contractAddress, airdrop?.merkleRoot || '', start, expiration, stage) + ?.registerAndReleaseEscrow(contractAddress, airdrop?.merkleRoot || '', start, expiration, totalAmount, stage) const showTransactionMessage = Boolean(airdrop && !airdrop.escrow && !airdrop.processing) From c94efb393cec1f6da4d855e796293ab884d752df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arda=20Nak=C4=B1=C5=9F=C3=A7=C4=B1?= Date: Mon, 4 Jul 2022 15:48:34 +0300 Subject: [PATCH 05/11] Feature: Terra airdrop support (#224) * Sync development to testnet (#205) * Remove comment * Update testnet chain id * Merge development to testnet (#214) * Remove comment * Update testnet chain id * Typo fixes (#208) * Changed "swiss knife" to "Swiss Army knife" * Removed unnecessary and unconventional "as" before "Smart Contract Dashboard" * Capitalized first letter of "Airdrops" and "Create Airdrop" page names, changed reference to Create Airdrop page to mention the page name * Sign and verify placeholder (#207) * add placeholders for sign and verify * Fix signature placeholder * fix placeholder * update testnet explorer * Add features to keplr config * The utility function isValidAccountsFile() now includes a check for duplicate addresses * Json formatted execute operation entry changed (#206) * Json formatted execute operation entry turned into form like formatted entry * Minor fixs * Minor fixes * Minor fixes * Walletless query (#212) * Wallet.tsx client turned into cosmwasmclient when not initialised * Minor fix * Minor fixes Co-authored-by: Lyte Co-authored-by: Highlander <68139321+Highlander-maker@users.noreply.github.com> Co-authored-by: Highlander-Maker Co-authored-by: Furkan Kaymak Co-authored-by: Serkan Reis Co-authored-by: name-user1 <101495985+name-user1@users.noreply.github.com> * Bump version * Add terra related logic (#2) * Add terra related logic to whitelist processing * Set terra airdrop flag on airdrop creation * Update airdrop props * Add terra related logic to merkle contract helpers * Add terra logic to claim page * Update logic * Show claim button for all terra airdrops * Include signature in Terra airdrop claim messages * Revert "Include signature in Terra airdrop claim messages" This reverts commit ecaed19eeb3237d51bade2a0704a101fbf8a8be4. * Include "sig_info" in Terra airdrop claim messages (#4) * Include signature in Terra airdrop claim messages * Remove txSigner from burn message on merkle airdrop * Remove unused code * Add comments for later on Co-authored-by: findolor * Add hrp field * Setup redirects and fix manage page error * Refactor claim page terra airdrop logic Co-authored-by: Lyte Co-authored-by: Highlander <68139321+Highlander-maker@users.noreply.github.com> Co-authored-by: Highlander-Maker Co-authored-by: Furkan Kaymak Co-authored-by: Serkan Reis Co-authored-by: name-user1 <101495985+name-user1@users.noreply.github.com> --- components/AirdropsTable.tsx | 3 +- contracts/cw20/merkleAirdrop/contract.ts | 58 +- next.config.js | 9 + package.json | 8 +- pages/_app.tsx | 52 +- pages/airdrops/[address]/claim.tsx | 121 +++- pages/airdrops/create.tsx | 6 +- pages/airdrops/manage.tsx | 5 +- pages/airdrops/register.tsx | 25 +- utils/constants.ts | 1 + utils/isValidAccountsFile.ts | 13 +- yarn.lock | 706 ++++++++++++++++++++++- 12 files changed, 945 insertions(+), 62 deletions(-) diff --git a/components/AirdropsTable.tsx b/components/AirdropsTable.tsx index 923f3c1a..c9c96183 100644 --- a/components/AirdropsTable.tsx +++ b/components/AirdropsTable.tsx @@ -19,6 +19,7 @@ export interface AirdropData { expiration: number expirationType: string logo: { url: string } | null + isTerraAirdrop?: boolean } export interface AirdropsTableProps extends ComponentProps<'table'> { @@ -80,7 +81,7 @@ export const AirdropsTable = (props: AirdropsTableProps) => {
Promise - claim: (txSigner: string, stage: number, amount: string, proof: string[]) => Promise + claim: ( + txSigner: string, + stage: number, + amount: string, + proof: string[], + signedMessage?: SignedMessage, + ) => Promise burn: (stage: number) => Promise withdraw: (stage: number, address: string) => Promise registerAndReleaseEscrow: ( @@ -63,6 +79,7 @@ export interface CW20MerkleAirdropInstance { expiration: Expiration, totalAmount: number, stage: number, + hrp?: string, ) => Promise depositEscrow: () => Promise fundWithSend: (amount: string) => Promise @@ -77,9 +94,16 @@ export interface CW20MerkleAirdropMessages { expiration: Expiration, totalAmount: number, stage: number, + hrp?: string, ) => [RegisterMessage, ReleaseEscrowMessage] depositEscrow: (airdropAddress: string) => DepositEscrowMessage - claim: (airdropAddress: string, stage: number, amount: string, proof: string[]) => ClaimMessage + claim: ( + airdropAddress: string, + stage: number, + amount: string, + proof: string[], + signedMessage?: SignedMessage, + ) => ClaimMessage fundWithSend: (recipient: string, amount: string) => FundWithSendMessage burn: (airdropAddress: string, stage: number) => BurnMessage withdraw: (airdropAddress: string, stage: number, address: string) => WithdrawMessage @@ -103,6 +127,7 @@ export interface RegisterMessage { start: Expiration expiration: Expiration total_amount: number + hrp?: string } } funds: Coin[] @@ -139,6 +164,7 @@ export interface ClaimMessage { stage: number amount: string proof: string[] + sig_info?: SignedMessage } } funds: Coin[] @@ -257,8 +283,19 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin return result.transactionHash } - const claim = async (_txSigner: string, stage: number, amount: string, proof: string[]): Promise => { - const result = await client.execute(_txSigner, contractAddress, { claim: { stage, amount, proof } }, fee) + const claim = async ( + _txSigner: string, + stage: number, + amount: string, + proof: string[], + signedMessage?: SignedMessage, + ): Promise => { + const result = await client.execute( + _txSigner, + contractAddress, + { claim: { stage, amount, proof, sig_info: signedMessage } }, + fee, + ) return result.transactionHash } @@ -278,6 +315,7 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin expiration: Expiration, totalAmount: number, stage: number, + hrp?: string, ): Promise => { const signed = await client.sign( txSigner, @@ -295,6 +333,7 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin start, expiration, total_amount: totalAmount.toString(), + hrp, }, }), ), @@ -462,6 +501,7 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin expiration: Expiration, totalAmount: number, stage: number, + hrp?: string, ): [RegisterMessage, ReleaseEscrowMessage] => { return [ { @@ -473,6 +513,7 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin start, expiration, total_amount: totalAmount, + hrp, }, }, funds: [], @@ -504,7 +545,13 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin } } - const claim = (airdropAddress: string, stage: number, amount: string, proof: string[]): ClaimMessage => { + const claim = ( + airdropAddress: string, + stage: number, + amount: string, + proof: string[], + signedMessage?: SignedMessage, + ): ClaimMessage => { return { sender: txSigner, contract: airdropAddress, @@ -513,6 +560,7 @@ export const CW20MerkleAirdrop = (client: SigningCosmWasmClient, txSigner: strin stage, amount, proof, + sig_info: signedMessage, }, }, funds: [], diff --git a/next.config.js b/next.config.js index 88967528..bc878b1f 100644 --- a/next.config.js +++ b/next.config.js @@ -32,6 +32,15 @@ const nextConfig = { ) return config }, + async redirects() { + return [ + { + source: '/airdrops/:address/manage', + destination: '/airdrops/manage', + permanent: true, + }, + ] + }, } module.exports = nextConfig diff --git a/package.json b/package.json index 6f215555..e9b3acbd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "juno-tools", - "version": "1.2.1", + "version": "1.3.0", "workspaces": [ "packages/*" ], @@ -26,6 +26,8 @@ "@svgr/webpack": "^6", "@tailwindcss/forms": "^0", "@tailwindcss/line-clamp": "^0", + "@terra-money/terra.js": "^3.1.3", + "@terra-money/wallet-provider": "^3.9.4", "axios": "^0", "clsx": "^1", "compare-versions": "^4", @@ -42,12 +44,14 @@ "react-query": "^3", "react-tracked": "^1", "scheduler": "^0", + "styled-components": "^5.3.3", "zustand": "^3" }, "devDependencies": { "@types/node": "^14", "@types/react": "^18", "@types/react-datetime-picker": "^3", + "@types/react-dom": "^17.0.11", "autoprefixer": "^10", "husky": "^7", "lint-staged": "^12", @@ -77,4 +81,4 @@ }, "prettier": "@juno-tools/prettier-config", "private": true -} +} \ No newline at end of file diff --git a/pages/_app.tsx b/pages/_app.tsx index 03a39835..d2451ab7 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -3,28 +3,52 @@ import '@fontsource/roboto/latin.css' import '../styles/globals.css' import '../styles/datepicker.css' +import type { WalletControllerChainOptions } from '@terra-money/wallet-provider' +import { StaticWalletProvider, WalletProvider } from '@terra-money/wallet-provider' import { Layout } from 'components/Layout' import { Modal } from 'components/Modal' import { queryClient } from 'config/react-query' import { ContractsProvider } from 'contexts/contracts' -import { WalletProvider } from 'contexts/wallet' +import { WalletProvider as KeplrWalletProvider } from 'contexts/wallet' import type { AppProps } from 'next/app' import { Toaster } from 'react-hot-toast' import { QueryClientProvider } from 'react-query' import { getComponentMetadata } from 'utils/layout' -export default function App({ Component, pageProps }: AppProps) { - return ( - - - - - - - - - - - +export default function App({ + Component, + pageProps, + defaultNetwork, + walletConnectChainIds, +}: AppProps & WalletControllerChainOptions) { + // TODO: Is this necessary? Look into it + return typeof window !== 'undefined' ? ( + + + + + + + + + + + + + + ) : ( + + + + + + + + + + + + + ) } diff --git a/pages/airdrops/[address]/claim.tsx b/pages/airdrops/[address]/claim.tsx index 16d6b143..50285464 100644 --- a/pages/airdrops/[address]/claim.tsx +++ b/pages/airdrops/[address]/claim.tsx @@ -1,3 +1,10 @@ +import type { SignBytesResult } from '@terra-money/wallet-provider' +import { + ConnectType, + useConnectedWallet, + useWallet as useTerraWallet, + WalletStatus, +} from '@terra-money/wallet-provider' import axios from 'axios' import clsx from 'clsx' import { Alert } from 'components/Alert' @@ -9,6 +16,7 @@ import { getConfig } from 'config' import { useContracts } from 'contexts/contracts' import { useWallet } from 'contexts/wallet' import type { TokenInfoResponse } from 'contracts/cw20/base' +import type { SignedMessage } from 'contracts/cw20/merkleAirdrop' import type { NextPage } from 'next' import { useRouter } from 'next/router' import { NextSeo } from 'next-seo' @@ -28,6 +36,13 @@ const ClaimAirdropPage: NextPage = () => { const cw20MerkleAirdropContract = useContracts().cw20MerkleAirdrop const cw20BaseContract = useContracts().cw20Base + // TODO: See if we can move these logic to a service + const { status, wallets, connect } = useTerraWallet() + + // TODO: See if we can move these logic to a service + const connectedWallet = useConnectedWallet() + const [signature, setSignature] = useState('') + const [amount, setAmount] = useState('') const [loading, setLoading] = useState(false) const [proofs, setProofs] = useState(['']) @@ -41,13 +56,68 @@ const ClaimAirdropPage: NextPage = () => { total_supply: '', }) const [stage, setStage] = useState(0) + const [signedMessage, setSignedMessage] = useState(undefined) + const [isTerraAirdrop, setIsTerraAirdrop] = useState(false) + // TODO: See if we can move these logic to a service + const [terraAddress, setTerraAddress] = useState('') const [airdropState, setAirdropState] = useState('loading') const contractAddress = String(router.query.address) const transactionMessage = - cw20MerkleAirdropContract?.messages()?.claim(contractAddress, stage, amount, proofs) || null + cw20MerkleAirdropContract?.messages()?.claim(contractAddress, stage, amount, proofs, signedMessage) || null + + const getAirdrop = async (address: string) => { + const { data } = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/airdrops/status/${address}`) + return data.airdrop + } + + useEffect(() => { + try { + if (status === WalletStatus.WALLET_NOT_CONNECTED) { + connect(ConnectType.EXTENSION) + } + } catch (err: any) { + toast.error(err.message, { + style: { maxWidth: 'none' }, + }) + } + }, [contractAddress, wallets]) + + useEffect(() => { + setTerraAddress(wallets[0]?.terraAddress) + }, [wallets[0]?.terraAddress]) + + useEffect(() => { + setSignedMessage({ claim_msg: { addr: wallet.address }, signature }) + }, [signature]) + + // TODO: Think about moving this to a service + const signTerraClaimSignature = async (): Promise => { + return new Promise((resolve, reject) => { + if (!connectedWallet) { + toast.error('Terra Station Wallet not connected!') + return + } + + const junoAddressMsgByteArray = Buffer.from(JSON.stringify({ addr: wallet.address })) + + connectedWallet + .signBytes(junoAddressMsgByteArray) + .then((nextSignBytesResult: SignBytesResult) => { + const signedJunoAddress = Buffer.from(nextSignBytesResult.result.signature).toString('base64') + const publickey = nextSignBytesResult.result.public_key?.toAmino().value + + const sig = Buffer.from(JSON.stringify({ pub_key: publickey, signature: signedJunoAddress })).toString( + 'base64', + ) + setSignature(sig) + resolve(sig) + }) + .catch(reject) + }) + } useEffect(() => { const getAirdropInfo = async () => { @@ -56,20 +126,28 @@ const ClaimAirdropPage: NextPage = () => { const merkleAirdropContractMessages = cw20MerkleAirdropContract?.use(contractAddress) + const airdrop = await getAirdrop(contractAddress) + + const address = airdrop.isTerraAirdrop ? wallets[0]?.terraAddress : wallet.address + const { data } = await axios.get( - `${process.env.NEXT_PUBLIC_API_URL}/proofs/contract/${contractAddress}/wallet/${wallet.address}`, + `${process.env.NEXT_PUBLIC_API_URL}/proofs/contract/${contractAddress}/wallet/${address}`, ) - - const { account, airdrop } = data + const { account } = data if (account) { // eslint-disable-next-line @typescript-eslint/no-shadow const stage = await merkleAirdropContractMessages?.getLatestStage() - const isClaimed = await merkleAirdropContractMessages?.isClaimed(wallet.address, stage || 0) + const isClaimed = await merkleAirdropContractMessages?.isClaimed(address, stage || 0) setProofs(account.proofs) setAmount((account.amount as number).toString()) setName(airdrop.name) setCW20TokenAddress(airdrop.cw20TokenAddress) + setIsTerraAirdrop(airdrop.isTerraAirdrop) + + if (airdrop.isTerraAirdrop) { + setSignedMessage({ claim_msg: { addr: wallet.address }, signature }) + } if (isClaimed) setAirdropState('claimed') else setAirdropState('not_claimed') @@ -85,7 +163,7 @@ const ClaimAirdropPage: NextPage = () => { } void getAirdropInfo() - }, [contractAddress, wallet.address, wallet.initialized]) + }, [contractAddress, wallet.address, wallet.initialized, wallets[0]?.terraAddress]) useEffect(() => { setBalance(Number(wallet.balance[0]?.amount)) @@ -132,7 +210,17 @@ const ClaimAirdropPage: NextPage = () => { const contractMessages = cw20MerkleAirdropContract.use(contractAddress) - await contractMessages?.claim(wallet.address, stage, amount, proofs) + let signedMsg + if (isTerraAirdrop) { + const sig = await signTerraClaimSignature() + signedMsg = { + claim_msg: { addr: wallet.address }, + signature: sig, + } + setSignedMessage(signedMessage) + } + + await contractMessages?.claim(wallet.address, stage, amount, proofs, signedMsg) setLoading(false) setAirdropState('claimed') @@ -174,7 +262,7 @@ const ClaimAirdropPage: NextPage = () => { No wallet detected. Please connect your wallet before claiming an airdrop. - + {airdropState === 'no_allocation' && ( No allocation @@ -184,7 +272,9 @@ const ClaimAirdropPage: NextPage = () => {
- +

{name}

@@ -211,15 +301,24 @@ const ClaimAirdropPage: NextPage = () => {
{JSON.stringify(proofs, null, 2)}
+ + {/* //TODO Fix the conditional */} + + + {status === WalletStatus.WALLET_CONNECTED ? terraAddress : 'Terra Station Wallet not connected'} + +
- + - +
-
- {fileContents && ( -
-
{JSON.stringify(fileContents, null, 2).trim()}
-
- )} + +
+ {accountsFile?.name} +
- )} + +
diff --git a/utils/constants.ts b/utils/constants.ts index 894198c3..3a9d259f 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -21,6 +21,8 @@ export const BLOCK_EXPLORER_URL = process.env.NEXT_PUBLIC_BLOCK_EXPLORER_URL export const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL +export const AIRDROP_ACCOUNT_LIMIT = parseInt(process.env.NEXT_PUBLIC_AIRDROP_ACCOUNT_LIMIT, 10) + export interface AirdropProps { name: string contractAddress: string diff --git a/utils/isValidAccountsFile.ts b/utils/isValidAccountsFile.ts index be9dd772..31203fbf 100644 --- a/utils/isValidAccountsFile.ts +++ b/utils/isValidAccountsFile.ts @@ -1,5 +1,6 @@ import { toast } from 'react-hot-toast' +import { AIRDROP_ACCOUNT_LIMIT } from './constants' import { isValidAddress } from './isValidAddress' export interface AccountProps { @@ -8,9 +9,14 @@ export interface AccountProps { } export const isValidAccountsFile = (file: AccountProps[]) => { - const duplicateCheck = file - .map((account) => account.address) - .filter((address, index, self) => self.indexOf(address) !== index) + // TODO: Think about duplicate values again + // const duplicateCheck = file + // .map((account) => account.address) + // .filter((address, index, self) => self.indexOf(address) !== index) + + if (file.length > AIRDROP_ACCOUNT_LIMIT) { + throw new Error(`Accounts file must have less than ${AIRDROP_ACCOUNT_LIMIT} accounts`) + } const checks = file.map((account) => { // Check if address is valid bech32 address @@ -44,10 +50,10 @@ export const isValidAccountsFile = (file: AccountProps[]) => { return false } - if (duplicateCheck.length > 0) { - toast.error('The file contains duplicate addresses.') - return false - } + // if (duplicateCheck.length > 0) { + // toast.error('The file contains duplicate addresses.') + // return false + // } return true }