Compare commits

...

3 Commits

Author SHA1 Message Date
Greg Shuflin 3f8d8f632d Add a bit of a design 2022-12-08 18:11:44 -08:00
Greg Shuflin 1d8e079e73 Run eslint on App.tsx 2022-12-08 17:38:38 -08:00
Greg Shuflin 13142d67cc Add eslint 2022-12-08 17:37:28 -08:00
4 changed files with 127 additions and 89 deletions

View File

@ -40,6 +40,7 @@
"@web3modal/react": "^2.0.0-beta.8", "@web3modal/react": "^2.0.0-beta.8",
"airdrop-artifact": "file:./artifacts/contracts/Airdrop.sol/", "airdrop-artifact": "file:./artifacts/contracts/Airdrop.sol/",
"create-react-app": "^5.0.1", "create-react-app": "^5.0.1",
"eslint": "^8.29.0",
"express": "^4.18.2", "express": "^4.18.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View File

@ -1,18 +1,54 @@
html {
height: 100%;
}
body {
height: 100%;
font-family: Lato, "Helvetica Neue", sans-serif;
}
div#root {
height: 100%;
color: white;
}
.App { .App {
text-align: center; display: grid;
grid-template-columns: 20% auto;
height: 100%;
} }
.App-logo { .sidebar {
height: 40vmin; grid-column: 1;
pointer-events: none; background-color: #5a6066;
text-align: center;
border-right: #8b8b8a;
} }
@media (prefers-reduced-motion: no-preference) { .connectorButton {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
} }
main.main {
background-color: black;
grid-column: 2;
padding: 10px;
}
.airdropPanel {
display: grid;
grid-template-columns: 30% auto;
}
.airdropSettings {
grid-column: 1;
}
.addressList {
grid-column: 2;
}
.App-header { .App-header {
background-color: #282c34; background-color: #282c34;
min-height: 100vh; min-height: 100vh;
@ -38,6 +74,5 @@
} }
div.addressError { div.addressError {
background-color: #ff9191; background-color: #c52121;
} }

View File

@ -1,12 +1,11 @@
import React, {useState} from 'react'; import React, {useState} from "react";
import logo from './logo.svg'; import "./App.css";
import './App.css';
import Web3 from 'web3'; import Web3 from "web3";
import { WagmiConfig, createClient, configureChains, mainnet, useConnect, useAccount, useDisconnect } from 'wagmi' import { WagmiConfig, createClient, configureChains, mainnet, useConnect, useAccount, useDisconnect } from "wagmi"
import { MetaMaskConnector } from 'wagmi/connectors/metaMask' import { MetaMaskConnector } from "wagmi/connectors/metaMask"
import { publicProvider } from 'wagmi/providers/public' import { publicProvider } from "wagmi/providers/public"
const web3 = new Web3(Web3.givenProvider); const web3 = new Web3(Web3.givenProvider);
@ -23,17 +22,17 @@ const airdropContract = new web3.eth.Contract(Airdrop.abi as any, airdropAddress
const { chains, provider, webSocketProvider } = configureChains([mainnet], [publicProvider()]); const { chains, provider, webSocketProvider } = configureChains([mainnet], [publicProvider()]);
const wagmiClient = createClient({ const wagmiClient = createClient({
autoConnect: true, autoConnect: true,
connectors: [ connectors: [
new MetaMaskConnector({ chains }), new MetaMaskConnector({ chains }),
], ],
provider, provider,
webSocketProvider, webSocketProvider,
}) })
function Profile() { function Connector() {
const { connect, connectors, error, isLoading, pendingConnector } = const { connect, connectors, error, isLoading, pendingConnector } =
useConnect() useConnect()
const { address, connector, isConnected } = useAccount() const { address, connector, isConnected } = useAccount()
@ -41,39 +40,43 @@ function Profile() {
if (isConnected) { if (isConnected) {
const connectorName = connector?.name || "unknown connector"; const connectorName = connector?.name || "unknown connector";
return ( return (
<div> <div>
<div>Connected to {connectorName} address: {address}</div> <div>Connected to {connectorName}!</div>
<button onClick={(_evt) => disconnect() }>Disconnect</button> <div>Connected account: <b>{address}</b></div>
</div> <button onClick={(_evt) => disconnect() }>Disconnect</button>
) </div>
} )
}
return ( return (
<div> <div className="connector">
<div>Connect to Airdrop App</div> <div>Not currently connected to a wallet. Connect to...</div>
{connectors.map((connector) => ( {connectors.map((connector) => (
<button <button
disabled={!connector.ready} className="connectorButton"
key={connector.id} disabled={!connector.ready}
onClick={() => connect({ connector })} key={connector.id}
> onClick={() => connect({ connector })}
{connector.name} >
{!connector.ready && ' (unsupported)'} {connector.name}
{isLoading && {!connector.ready && " (unsupported)"}
{isLoading &&
connector.id === pendingConnector?.id && connector.id === pendingConnector?.id &&
' (connecting)'} " (connecting)"}
</button> </button>
))} ))}
{error && <div>{error.message}</div>} {error && <div>{error.message}</div>}
</div> </div>
) )
} }
interface AddressesProps { interface AddressesProps {
airdropButtonDisabled: boolean;
performAirdrop: any;
addressList: string[]; addressList: string[];
setAddressListFn: any; setAddressListFn: any;
numTokens: number; numTokens: number;
@ -83,7 +86,7 @@ interface AddressesProps {
function Addresses(props: AddressesProps) { function Addresses(props: AddressesProps) {
const [inputState, setInputState] = useState(""); const [inputState, setInputState] = useState("");
const [errorText, setErrorText] = useState(""); const [errorText, setErrorText] = useState("");
const { setAddressListFn, addressList, numTokens, setNumTokens } = props; const { airdropButtonDisabled, performAirdrop, setAddressListFn, addressList, numTokens, setNumTokens } = props;
const save = () => { const save = () => {
if (!web3.utils.isAddress(inputState)) { if (!web3.utils.isAddress(inputState)) {
@ -110,17 +113,21 @@ function Addresses(props: AddressesProps) {
} }
return ( return (
<div> <section className="airdropSettings">
<div> <p>Add an address to airdrop to:</p>
<input id="address" value={inputState} onChange={ (evt) => setInputState(evt.target.value) } ></input> <div>
<div className="addressError">{errorText}</div> <input id="address" value={inputState} onChange={ (evt) => setInputState(evt.target.value) } ></input>
<button onClick={save}>Add address</button> <div className="addressError">{errorText}</div>
</div> <button onClick={save}>Add address</button>
<div> </div>
Number of tokens: <div>
<input type="numeric" value={num} onChange={ (evt) => setNumTokens(parseInt(evt.target.value)) }></input> <div>Number of tokens:</div>
</div> <input type="numeric" value={num} onChange={ (evt) => setNumTokens(parseInt(evt.target.value)) }></input>
</div> </div>
<div>
<button disabled={airdropButtonDisabled} onClick={performAirdrop} >Perform Airdrop!</button>
</div>
</section>
); );
} }
@ -131,10 +138,10 @@ interface AddressListProps {
function AddressList({addressList}: AddressListProps) { function AddressList({addressList}: AddressListProps) {
const addresses = addressList.length == 0 ? const addresses = addressList.length == 0 ?
<p>No addresses specified yet</p> : <p>No addresses specified yet</p> :
addressList.map((addr: string) => <div key={addr} className="addressItem">{addr}</div>); addressList.map((addr: string) => <div key={addr} className="addressItem">{addr}</div>);
return ( return (
<div> <div className="addressList">
<h2>Addresses to airdrop to: </h2> <h2>Addresses to airdrop to: </h2>
{ addresses } { addresses }
</div> </div>
@ -175,21 +182,24 @@ function App() {
const airdropButtonDisabled = addressList.length == 0 || !isConnected; const airdropButtonDisabled = addressList.length == 0 || !isConnected;
return ( return (
<WagmiConfig client={wagmiClient}> <WagmiConfig client={wagmiClient}>
<div className="App"> <div className="App">
<Profile/> <div className="sidebar">
<h1>Airdrop App</h1>
<Connector/>
</div>
<main className="main">
<p>Enter your airdropping settings:</p>
<section className="airdropPanel">
<Addresses airdropButtonDisabled={airdropButtonDisabled} performAirdrop={() => performAirdrop()} addressList={addressList} setAddressListFn={setAddressList} numTokens={numTokens} setNumTokens={setNumTokens}/>
<AddressList addressList={addressList} />
</section>
</main>
<h1>Airdrop App</h1> </div>
</WagmiConfig>
<p>Add an address to airdrop to:</p> );
<Addresses addressList={addressList} setAddressListFn={setAddressList} numTokens={numTokens} setNumTokens={setNumTokens}/>
<button disabled={airdropButtonDisabled} onClick={performAirdrop} >Perform Airdrop!</button>
<AddressList addressList={addressList} />
</div>
</WagmiConfig>
);
} }
export default App; export default App;

View File

@ -6649,7 +6649,7 @@ eslint-webpack-plugin@^3.1.1:
normalize-path "^3.0.0" normalize-path "^3.0.0"
schema-utils "^4.0.0" schema-utils "^4.0.0"
eslint@^8.3.0: eslint@^8.29.0, eslint@^8.3.0:
version "8.29.0" version "8.29.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87"
integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg== integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==
@ -6923,15 +6923,7 @@ ethereum-cryptography@^1.0.3:
"@scure/bip32" "1.1.0" "@scure/bip32" "1.1.0"
"@scure/bip39" "1.1.0" "@scure/bip39" "1.1.0"
ethereumjs-abi@^0.6.8: ethereumjs-abi@^0.6.8, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git":
version "0.6.8"
resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae"
integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==
dependencies:
bn.js "^4.11.8"
ethereumjs-util "^6.0.0"
"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git":
version "0.6.8" version "0.6.8"
resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0" resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0"
dependencies: dependencies: