Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/ Adding Mix of Standars ERC1155 & ERC20 into the same UI for deploying airdrops #4

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
007f6a5
feat: form to create airdrop added
Jrosado16 Aug 29, 2024
bde36b1
feat: deployERC20Airdrop function integrated
Jrosado16 Aug 29, 2024
d49ab48
feat: formating date
Jrosado16 Aug 29, 2024
ba241e3
merge with master
scguaquetam Aug 30, 2024
62b50a5
fix: adding correct tx link to claim Modal
scguaquetam Sep 2, 2024
497672a
update typechain-types
IOVgomezdn Sep 2, 2024
7fda4d8
feat: Added a new tag and added a gasless switch when creating an air…
Jrosado16 Sep 2, 2024
6b984dc
Merge branch 'feat/deploy-airdrop' of github.com-iov:rsksmart/airdrop…
krlz16 Sep 2, 2024
8921d4e
feat: implementing gasless call to create airdrop
scguaquetam Sep 2, 2024
ee6c3d8
Merge branch 'feat/deploy-airdrop' of github.com-work:krlz16/airdrop-…
scguaquetam Sep 2, 2024
e88b9c4
feat: adding full support to create airdrop gasless
scguaquetam Sep 2, 2024
20dad26
style: fixes
Sep 3, 2024
5ab5d62
fix: create airdrop
Sep 3, 2024
4056208
feat: adding image on airdrop card
scguaquetam Sep 3, 2024
379ff0a
merging changes
scguaquetam Sep 3, 2024
d93b991
fix: filters
Sep 3, 2024
d9107af
Merge branch 'feat/deploy-airdrop' of https://github.com/ezequiel-rod…
Sep 3, 2024
7db8b1f
fix: image
Sep 3, 2024
fa58272
fix: styles
Sep 3, 2024
488e7b5
feat/updating typechain-types to new architecture contracts, and refa…
Oct 25, 2024
2060e44
feat/add normal and gasless airdrop creation ERC20/ERC1155
Oct 28, 2024
b1ea211
feat/ adding erc1155 deployment with wallet
Oct 28, 2024
44fbbde
feat/adding erc1155 airdrop creation with gasless
Oct 28, 2024
1a12069
feat/refactoring ui to show airdrop type, finished add airdrop for bo…
Oct 29, 2024
18397d2
merging master
Oct 29, 2024
005d080
feat/refactoring image management
Oct 29, 2024
bac6892
updating readme
Oct 29, 2024
d838639
fix/deploy issues
Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/airdrop-ui.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,24 @@ To clone and run this project locally, follow these steps:
Create a `.env` file in the root directory and add necessary environment variables. Example:

```sh
NEXT_PUBLIC_AIRDROP_MANAGER_ADDRESS=
NEXT_PUBLIC_RPC_URL=
NEXT_PUBLIC_EXPLORER=
NEXT_PUBLIC_AIRDROP_MANAGER_ADDRESS=
NEXT_PUBLIC_RPC_URL=
NEXT_PUBLIC_EXPLORER=
NEXT_PUBLIC_BUNDLER_API_KEY="eyJvcmciOiI2NTIzZjY5MzUwOTBmNzAwMDFiYjJkZWIiLCJpZCI6IjMxMDZiOGY2NTRhZTRhZTM4MGVjYjJiN2Q2NDMzMjM4IiwiaCI6Im11cm11cjEyOCJ9"
NEXT_PUBLIC_CUSTOM_BUNDLER_URL="https://rootstocktestnet-bundler.etherspot.io/"
NEXT_PUBLIC_CHAIN_ID="31"
NEXT_PUBLIC_ARKA_PUBLIC_KEY="arka_public_key"
NEXT_PUBLIC_PK=
NEXT_PUBLIC_PINATA_URL=
```

4. **Run the development server**:

```sh
npm run dev
```

## Smart Contracts
You can follow the [Airdrop Template Repository](https://github.com/rsksmart/airdrop-template) which contains the smart contracts for the Airdrop Manager and the Airdrop Token. You can deploy them by yourself or use the deployed contracts on the RSK Testnet.

## Usage

Expand Down
Binary file added public/img/RSKLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 0 additions & 9 deletions src/components/Hero/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ function Hero() {
<div className='flex-1'>
<Title />
<div className='flex justify-between mt-14 items-center'>
<Button
onClick={undefined}
outline
rounded
variant='secondary'
width={190}
>
Airdrop Section
</Button>
<div className='w-[345px] font-medium'>
<span className='bg-custom-orange w-[60px] h-[14px] rounded-full px-1 text-black'>WHAT IS?</span>
<p className='mb-3 mt-4'>Welcome to the Runes Giveaway Machine! Airdrop Section, claim unique airdrops on RSK Network</p>
Expand Down
146 changes: 101 additions & 45 deletions src/components/airdrop/AirdropCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Button from '../common/Button'
import XIcon from '../icons/XIcon'
import ProgressBar from './ProgressBar'
import AirdropIcon from '../icons/AirdropIcon'
import ArrowRightIcon from '../icons/ArrowRightIcon'
import { useAuth } from '@/context/AuthContext'
import Badge from '../common/Badge'
Expand All @@ -10,7 +9,11 @@ import Connect from '../navigation/Connect'
import MerkleData from '@/utils/merkleData.json'
import { useEffect, useState } from 'react'
import { ethers } from 'ethers'

import CopyIcon from '../icons/CopyIcon'
import { formatDate } from '@/utils/formatDate'
import useAirdrop from '@/hooks/useAirdrop'
import ConnectWalletButton from '../navigation/ConnectWalletButton'
import { PINATA_URL } from '@/constants'
type props = {
background?: string
dialog?: boolean
Expand All @@ -19,20 +22,51 @@ type props = {
airdrop: IAirdrop
}

function formatAddress(address: string) {
return `${address.slice(0, 4)}...${address.slice(-4)}`
}

function AirdropCard({ background = 'bg-custom-orange', onClick, dialog = false, airdrop, onCloseDialog }: props) {
const { isAdmin, address, domain, gasless, setGasless } = useAuth();
const { isAdmin, address, gasless, setGasless } = useAuth();
const [amount, setAmount] = useState<string>('0');
const [copied, setCopied] = useState<boolean>(false);
const [imgLink, setImgLink] = useState<string | null>(null);

let disabled = false;
if (address) disabled = !isAdmin ? (!airdrop.isAllowed || airdrop.isClaimed! || airdrop?.isExpired! || airdrop.balance === 0) : false;
useEffect(() => {
if (airdrop.airdropType !== 'merkle') return;
if (airdrop.airdropType !== '1') return;
const claim = MerkleData.claims.find(claim => claim.address.toLowerCase() === address.toLowerCase());
setAmount(claim?.amount ? ethers.formatUnits(claim?.amount, 18).toString() : '0');
}, [address])
}, [address, airdrop.airdropType]);
const copyAddress = (address: string) => {
setCopied(true);
navigator.clipboard.writeText(address)
setTimeout(() => {
setCopied(false);
}, 1000);
}

const trim1 = (text: string) => {
return text.substring(0, 4);
}
const trim2 = (text: string) => {
return text.substring(text.length - 4);
}
console.log(imgLink);

return (
<>
<article className={`${(disabled && !dialog) ? 'cursor-not-allowed bg-zinc-950 border-zinc-700' : 'border-white'} rounded-[20px] justify-between gap-2 relative ${dialog ? 'w-full' : 'border p-7 w-[400px]'}`}>
<div className={`!absolute -top-3 right-0 px-2 flex gap-2 ${!dialog ? '' : 'hidden'}`}>
<article className={`${(disabled && !dialog) ? 'cursor-not-allowed bg-zinc-950 border-zinc-700' : 'border-white'} rounded-[20px] justify-between gap-2 relative ${dialog ? 'w-full h-full' : 'border p-6 w-[400px]'}`}>
<div className={`!absolute w-full justify-end -top-4 right-0 px-2 flex gap-2 ${!dialog ? '' : 'hidden'}`}>
{
(airdrop.new) &&
<Badge
color='new'
title='New'
isNew
/>
}
{
(!airdrop.isAllowed && address) &&
<Badge
Expand All @@ -58,7 +92,7 @@ function AirdropCard({ background = 'bg-custom-orange', onClick, dialog = false,
(airdrop.balance === 0 && address) &&
<Badge
color='cyan'
title='No Balance'
title='Insufficient balance'
/>
}
{
Expand All @@ -78,52 +112,78 @@ function AirdropCard({ background = 'bg-custom-orange', onClick, dialog = false,
)
}
</div>
<section className='flex w-full'>
<section className='flex w-full h-full'>
<div className='w-2/3'>
<h3 className={`${background} w-max font-semibold text-xl px-1 text-black`}>{airdrop.name}</h3>
<ProgressBar value={airdrop.progress!} background={background} />
<section className='w-full mt-2'>
<div className='flex justify-between'>
<h6>Amount to receive</h6>
<p>{airdrop.airdropType === 'merkle' ? amount : airdrop.claimAmount}</p>
</div>
<div className='flex justify-between mt-2'>
<h6>Total available</h6>
<p>{airdrop.airdropAmountLeft}</p>
</div>
<div className='text-zinc-500 font-semibold text-xs flex justify-between mt-2'>
<h6>Expiration date</h6>
<p>{airdrop.expirationDate.toDateString()}</p>
<p>{airdrop.airdropType === '1' ? 'Variable' : airdrop.claimAmount}</p>
</div>
{
airdrop.balance !== 0 &&
<div className='flex justify-between mt-2'>
<h6>Available to claim</h6>
<p>{airdrop.airdropAmountLeft}</p>
</div>
}
<div className='text-zinc-500 font-semibold text-xs flex justify-between mt-1'>
<h6>Type</h6>
<p>{airdrop.airdropType}</p>
<p>{airdrop.airdropType === '0' ? 'ERC 1155 Custom' : airdrop.airdropType === '1' ? 'ERC 1155 Merkle' : 'ERC 20'}</p>
</div>
<div className='text-zinc-500 font-semibold text-xs flex justify-between mt-2'>
<h6>Expiration</h6>
<p>{formatDate(airdrop.expirationDate)}</p>
</div>
</section>
</div>
<div className='w-1/3 flex justify-between flex-col items-end'>
<div className="gap-4 flex flex-col">
<AirdropIcon />
<Button
show={(!dialog || !!address)}
onClick={onClick}
className='self-end !px-0 group'
width={dialog ? 76 : 45}
outline
variant={dialog ? 'secondary' : 'primary'}
disabled={disabled}
>
{
dialog
? 'claim'
: <ArrowRightIcon className={`${disabled ? '' : 'group-hover:fill-black'} fill-white`} />
}
</Button>
<div className='mt-1 flex flex-col items-end gap2'>
<div className='text-zinc-300 mb-2 flex justify-between items-center gap-2 '>
<div>{formatAddress(airdrop.address)}</div>
<button className="relative w-4 h-4" onClick={() => copyAddress(airdrop.address)}>
{
copied && <span className='absolute -left-4 -top-6 transition-all duration-500'>copied</span>
}
<CopyIcon className='hover:fill-zinc-200 fill-zinc-400 relative w-4 h-4' />
</button>
</div>
{airdrop.uri && airdrop.uri != '' ?
(
<img
src={`${PINATA_URL}${airdrop.uri}`}
alt={`airdrop token logo`}
className="w-20 h-20 rounded-full"
/>
) : (
<img
src={'/img/RSKLogo.png'}
alt={`airdrop token logo`}
className="w-20 h-20 rounded-full"
/>
)
}
</div>
<Button
show={(!dialog || !!address)}
onClick={onClick}
className='self-end !px-0 group'
width={dialog ? 76 : 45}
outline
variant={dialog ? 'secondary' : 'primary'}
disabled={disabled}
>
{
dialog
? 'claim'
: <ArrowRightIcon className={`${disabled ? '' : 'group-hover:fill-black'} fill-white`} />
}
</Button>
{
(dialog && address && !domain) &&
<div className="flex gap-2 items-center">
<label htmlFor="">gasless</label>
(dialog && address) &&
<div className="flex gap-2 items-center mt-2">
<label htmlFor="" className='text-zinc-400'>gasless</label>
<label className="flex relative items-center cursor-pointer">
<input
checked={gasless}
Expand All @@ -137,12 +197,8 @@ function AirdropCard({ background = 'bg-custom-orange', onClick, dialog = false,
}
</div>
</section>
<section className={`mt-8 ${!(!address && dialog) ? 'hidden' : ''}`}>
<Connect className='flex gap-4 flex-col w-full items-center justify-center' connectWalletTitle='Connect wallet to claim' connectRNSTitle="Use RNS to claim" />
</section>
<section className={`mt-7 ${dialog ? '' : 'hidden'}`}>
<h4 className='font-semibold text-sm'>Description</h4>
<p className='text-xs text-zinc-400'>nisi porta lorem mollis aliquam ut porttitor leo a diam sollicitudin tempor id eu nisl nunc mi ipsum faucibus vitae aliquet nec ullamcorper sit amet risus nullam eget felis eget nunc lobortis mattis aliquam faucibus purus in massa tempor nec</p>
<section className={`flex justify-center mt-8 ${!(!address && dialog) ? 'hidden' : ''}`}>
<ConnectWalletButton title='Connect wallet to claim' width={230} />
</section>
</article>
</>
Expand Down
27 changes: 21 additions & 6 deletions src/components/airdrop/AirdropContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,46 @@ import useAirdrop from '@/hooks/useAirdrop'
import Filters from './Filters';
import CardLoader from '../loader/CardLoader';
import { useAuth } from '@/context/AuthContext';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { IAirdrop } from '@/interface/IAirdrop';

function AirdropContainer() {
const colors = ['bg-custom-orange', 'bg-custom-green', 'bg-custom-pink'];

const { getAllAirdrops } = useAirdrop();
const [filters, setFilters] = useState("")
const { provider, airdropLoading, airdrops } = useAuth();
const { getAllAirdrops } = useAirdrop();
const [currrentAirdrops, setCurrentAirdrops] = useState(airdrops)

useEffect(() => {
if (airdrops && !filters) {
setCurrentAirdrops(airdrops)
}

if (airdrops && filters) {
const s: IAirdrop[] = airdrops!.filter((airdrop: IAirdrop) => airdrop.name.toLocaleLowerCase().includes(filters.toLocaleLowerCase()))
setCurrentAirdrops(s)
}
}, [airdrops, filters]);

useEffect(() => {
getAllAirdrops();
}, [provider, getAllAirdrops]);

return (
<div className="lg:w-[90%] xl:w-[1300px] m-auto mt-[90px]">
<Filters />
<Filters filters={filters} setFilters={setFilters} />
<div className='mt-16 flex flex-wrap gap-6'>
{airdropLoading ? (
<CardLoader />
) :
airdrops?.map((air, i) =>
) : (
currrentAirdrops?.map((air, i) =>
<AirdropItem
airdrop={air}
key={i}
background={`${colors[i % colors.length]}`}
/>
)
)
}
{
(airdrops?.length === 0 && !airdropLoading) && (
Expand Down
3 changes: 1 addition & 2 deletions src/components/airdrop/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import AirdropContainer from './AirdropContainer'
function Content() {
return (
<section id='airdrop-content' className="m-auto min-h-screen">
<div className="w-full border-y border-y-white flex justify-center gap-3 py-8 mb-10 items-center">
<span className="w-max bg-gray-700 text-white text-xs rounded-full px-2 font-semibold leading-none py-1 h-max flex justify-center items-center">01</span>
<div className="w-full border-y border-y-white flex justify-center gap-3 py-6 items-center">
<h4 className="text-xl font-bold">Airdrops available for claimiming</h4>
</div>
<AirdropContainer />
Expand Down
Loading