Skip to content

Commit

Permalink
feat: template ok
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Jan 12, 2024
1 parent 9a9d784 commit 9aa318e
Show file tree
Hide file tree
Showing 19 changed files with 322 additions and 2,138 deletions.
14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# ERC721
# ERC20

[![license-mit](https://img.shields.io/badge/License-MIT-teal.svg)](https://opensource.org/license/mit/)
[![build-test](https://github.com/veeso-dev/erc721-template/actions/workflows/build-test.yml/badge.svg)](https://github.com/veeso-dev/erc721-template/actions/workflows/build-test.yml)
[![build-test](https://github.com/veeso-dev/erc20-template/actions/workflows/build-test.yml/badge.svg)](https://github.com/veeso-dev/erc20-template/actions/workflows/build-test.yml)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org)

## Get started
Expand Down Expand Up @@ -34,7 +34,7 @@ yarn deploy:goerli
yarn deploy:ethereum
```

### Mint and administrate NFT
### Transfer and administrate the token

Enter directory `./app`

Expand All @@ -53,14 +53,8 @@ Now browse to <http://localhost:1234/>.

## How to setup code from template

## IPFS upload

This repo comes with a built-in IPFS upload tool.

Read more in this [README](./tools/ipfs-upload/README.md)

## License

ERC721 template is licensed under the MIT license.
ERC20 template is licensed under the MIT license.

See the full license [HERE](./LICENSE)
25 changes: 25 additions & 0 deletions app/src/js/components/Form/Balance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';
import { useConnectedMetaMask, useMetaMask } from 'metamask-react';
import Web3Client from '../../web3/Web3Client';
import Container from '../reusable/Container';
import Heading from '../reusable/Heading';

const Balance = () => {
const { account, ethereum } = useConnectedMetaMask();
const [balance, setBalance] = React.useState(0);

React.useEffect(() => {
const client = new Web3Client(account, ethereum);
client.balanceOf(account).then((balance) => {
setBalance(balance);
});
});

return (
<Container.Container>
<Heading.H2>Your current balance: {balance}</Heading.H2>
</Container.Container>
);
};

export default Balance;
10 changes: 8 additions & 2 deletions app/src/js/components/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@ import { useMetaMask } from 'metamask-react';

import Container from '../reusable/Container';
import Card from '../reusable/Card';
import MintForm from './Mint/MintForm';
import Alerts from '../reusable/Alerts';
import ChangeOwnerForm from './ChangeOwner/ChangeOwnerForm';
import Balance from './Balance';
import TransferForm from './Transfer/TransferForm';
import RenounceOwnershipForm from './RenounceOwnership/RenounceOwnershipForm';

const Form = () => {
const { status } = useMetaMask();

const content =
status === 'connected' ? (
<Container.FlexCols className="gap-8">
<Balance />
<Card>
<MintForm />
<TransferForm />
</Card>
<Card>
<ChangeOwnerForm />
</Card>
<Card>
<RenounceOwnershipForm />
</Card>
</Container.FlexCols>
) : (
<Container.Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react';
import { useConnectedMetaMask } from 'metamask-react';

import Container from '../../reusable/Container';
import Heading from '../../reusable/Heading';
import Button from '../../reusable/Button';
import Web3Client from '../../../web3/Web3Client';
import Alerts from '../../reusable/Alerts';

const RenounceOwnershipForm = () => {
const { account, ethereum } = useConnectedMetaMask();
const [pendingTx, setPendingTx] = React.useState<boolean>(false);
const [error, setError] = React.useState<string>();

const onTransfer = () => {
setPendingTx(true);
const client = new Web3Client(account, ethereum);
client
.renounceOwnership()
.then(() => {
setPendingTx(false);
setError(undefined);
})
.catch((e) => {
setError(e.message);
setPendingTx(false);
});
};

const btnDisabled = pendingTx;

return (
<Container.FlexCols className="items-center">
<Heading.H2>Renounce ownership</Heading.H2>
<Button.Danger
disabled={btnDisabled}
onClick={onTransfer}
className="!mt-4"
>
Renounce ownership
</Button.Danger>
{error && (
<Alerts.Danger>
<p>{error}</p>
</Alerts.Danger>
)}
</Container.FlexCols>
);
};

export default RenounceOwnershipForm;
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import Button from '../../reusable/Button';
import Web3Client from '../../../web3/Web3Client';
import Alerts from '../../reusable/Alerts';

const MintForm = () => {
const TransferForm = () => {
const { account, ethereum } = useConnectedMetaMask();
const [recipientAddress, setRecipientAddress] = React.useState('');
const [tokenURI, setTokenURI] = React.useState('');
const [amount, setAmount] = React.useState('');
const [pendingTx, setPendingTx] = React.useState<boolean>(false);
const [error, setError] = React.useState<string>();

Expand All @@ -21,15 +21,18 @@ const MintForm = () => {
setRecipientAddress(event.target.value);
};

const onTokenURIChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setTokenURI(event.target.value);
const onAmountChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setAmount(event.target.value);
};

const onMint = () => {
setPendingTx(true);
const client = new Web3Client(account, ethereum);

const amoutNum = Number(amount);

client
.safeMint(recipientAddress, tokenURI)
.transfer(recipientAddress, amoutNum)
.then(() => {
setPendingTx(false);
setError(undefined);
Expand All @@ -41,26 +44,26 @@ const MintForm = () => {
};

const btnDisabled =
tokenURI.length == 0 || recipientAddress.length !== 42 || pendingTx;
!isAmountNumber(amount) || recipientAddress.length !== 42 || pendingTx;

return (
<Container.FlexCols className="items-center">
<Heading.H2>Mint NFT</Heading.H2>
<Heading.H2>Transfer tokens</Heading.H2>
<Input.Input
id="mint-form-recipient-address"
label="Recipient address"
onChange={onRecipientAddressChange}
value={recipientAddress}
/>
<Input.Input
id="mint-form-token-uri"
label="Token URI"
onChange={onTokenURIChange}
value={tokenURI}
id="mint-form-token-amount"
label="Token amount"
onChange={onAmountChange}
value={amount}
/>
<Button.Primary disabled={btnDisabled} onClick={onMint} className="!mt-4">
Mint
</Button.Primary>
<Button.Danger disabled={btnDisabled} onClick={onMint} className="!mt-4">
Transfer
</Button.Danger>
{error && (
<Alerts.Danger>
<p>{error}</p>
Expand All @@ -70,4 +73,9 @@ const MintForm = () => {
);
};

export default MintForm;
const isAmountNumber = (amount: string) => {
const amountNum = Number(amount);
return !isNaN(amountNum);
};

export default TransferForm;
12 changes: 12 additions & 0 deletions app/src/js/components/reusable/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ const Primary = (props: React.HTMLProps<HTMLButtonElement>) => (
</button>
);

const Danger = (props: React.HTMLProps<HTMLButtonElement>) => (
<button
type={'button'}
className={`${props.className} text-white bg-red-500 hover:bg-red-900 focus:outline-none focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 800 :bg-gray-700 :ring-gray-700 disabled:cursor-not-allowed`}
disabled={props.disabled}
onClick={props.onClick}
>
{props.children}
</button>
);

const Alternative = (props: React.HTMLProps<HTMLButtonElement>) => (
<button
type={'button'}
Expand All @@ -35,6 +46,7 @@ const Tertiary = (props: React.HTMLProps<HTMLButtonElement>) => (

export default {
Primary,
Danger,
Alternative,
Tertiary,
};
18 changes: 15 additions & 3 deletions app/src/js/web3/Web3Client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Web3 from 'web3';

import { ABI, CONTRACT_ADDRESS } from './contracts/MyNft';
import { ABI, CONTRACT_ADDRESS } from './contracts/MyToken';

export default class Web3Client {
private address: string;
Expand All @@ -18,9 +18,21 @@ export default class Web3Client {
.send({ from: this.address });
}

async safeMint(address: string, uri: string) {
async renounceOwnership() {
const contract = this.getContract();
return contract.methods.safeMint(address, uri).send({ from: this.address });
return contract.methods.renounceOwnership().send({ from: this.address });
}

async transfer(recipient: string, amount: number) {
const contract = this.getContract();
return contract.methods
.transfer(recipient, amount)
.send({ from: this.address });
}

async balanceOf(address: string): Promise<number> {
const contract = this.getContract();
return contract.methods.balanceOf(address).call();
}

private getContract() {
Expand Down
Loading

0 comments on commit 9aa318e

Please sign in to comment.