add PAD token
This commit is contained in:
parent
545bdf7bae
commit
fec688478b
|
|
@ -1,5 +1,6 @@
|
|||
rm "./frontend/src/evm-output/*"
|
||||
rm ./frontend/src/evm-output/*
|
||||
|
||||
cp "./evm/ignition/deployments/chain-31337/deployed_addresses.json" "./frontend/src/evm-output/deployed_addresses.json"
|
||||
|
||||
cp "./evm/ignition/deployments/chain-31337/artifacts/MessageBoxModule#MessageBox.json" "./frontend/src/evm-output/MessageBox.artifacts.json"
|
||||
cp "./evm/ignition/deployments/chain-31337/artifacts/MainModule#PADToken.json" "./frontend/src/evm-output/PADToken.artifacts.json"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
export const PAD_TOKEN_INITIAL_OWNER =
|
||||
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// Compatible with OpenZeppelin Contracts ^5.0.0
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
|
||||
|
||||
contract PADToken is ERC20, Ownable, ERC20Permit {
|
||||
constructor(address initialOwner)
|
||||
ERC20("PAD Token", "PAD")
|
||||
Ownable(initialOwner)
|
||||
ERC20Permit("PAD Token")
|
||||
{}
|
||||
|
||||
function mint(address to, uint256 amount) public onlyOwner {
|
||||
_mint(to, amount);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
|
||||
import { PAD_TOKEN_INITIAL_OWNER } from "../../config";
|
||||
|
||||
const MainModule = buildModule("MainModule", (m) => {
|
||||
const initialOwner = m.getParameter(
|
||||
"padTokenInitialOwner",
|
||||
PAD_TOKEN_INITIAL_OWNER
|
||||
);
|
||||
|
||||
const padToken = m.contract("PADToken", [initialOwner]);
|
||||
|
||||
return { padToken };
|
||||
});
|
||||
|
||||
export default MainModule;
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^5.0.2",
|
||||
"hardhat": "^2.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ settings:
|
|||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@openzeppelin/contracts':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
hardhat:
|
||||
specifier: ^2.22.2
|
||||
version: 2.22.2(ts-node@10.9.2)(typescript@5.4.5)
|
||||
|
|
@ -844,6 +847,10 @@ packages:
|
|||
'@nomicfoundation/solidity-analyzer-win32-ia32-msvc': 0.1.1
|
||||
'@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1
|
||||
|
||||
/@openzeppelin/contracts@5.0.2:
|
||||
resolution: {integrity: sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==}
|
||||
dev: false
|
||||
|
||||
/@scure/base@1.1.6:
|
||||
resolution: {integrity: sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
],
|
||||
ignorePatterns: ["dist", ".eslintrc.cjs"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["react-refresh"],
|
||||
rules: {
|
||||
"react-refresh/only-export-components": [
|
||||
"warn",
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
// My additions
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sonner": "^1.4.41",
|
||||
"web3": "^4.7.0"
|
||||
"web3": "^4.7.0",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tanstack/router-vite-plugin": "^1.28.2",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ dependencies:
|
|||
web3:
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0(typescript@5.4.5)
|
||||
zustand:
|
||||
specifier: ^4.5.2
|
||||
version: 4.5.2(@types/react@18.2.79)(react@18.2.0)
|
||||
|
||||
devDependencies:
|
||||
'@tanstack/router-vite-plugin':
|
||||
|
|
@ -1026,7 +1029,6 @@ packages:
|
|||
|
||||
/@types/prop-types@15.7.12:
|
||||
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
||||
dev: true
|
||||
|
||||
/@types/react-dom@18.2.25:
|
||||
resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==}
|
||||
|
|
@ -1039,7 +1041,6 @@ packages:
|
|||
dependencies:
|
||||
'@types/prop-types': 15.7.12
|
||||
csstype: 3.1.3
|
||||
dev: true
|
||||
|
||||
/@types/semver@7.5.8:
|
||||
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
|
||||
|
|
@ -1484,7 +1485,6 @@ packages:
|
|||
|
||||
/csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
dev: true
|
||||
|
||||
/culori@3.3.0:
|
||||
resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==}
|
||||
|
|
@ -3233,3 +3233,23 @@ packages:
|
|||
|
||||
/zod@3.22.4:
|
||||
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
|
||||
|
||||
/zustand@4.5.2(@types/react@18.2.79)(react@18.2.0):
|
||||
resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
immer: '>=9.0.6'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.2.79
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import { useStore } from "../store";
|
||||
import { shortenAddress, tryWithToast } from "../utils";
|
||||
import { web3 } from "../web3";
|
||||
|
||||
export function ConnectMetamaskButton() {
|
||||
const connectedAccount = useStore((state) => state.connectedAccount);
|
||||
const setConnectedAccount = useStore((state) => state.setConnectedAccount);
|
||||
const unsetConnectedAccount = useStore(
|
||||
(state) => state.unsetConnectedAccount
|
||||
);
|
||||
|
||||
// https://docs.web3js.org/guides/getting_started/metamask/#react-app
|
||||
async function connectMetamask() {
|
||||
await tryWithToast("Connect MetaMask", async () => {
|
||||
// Request user to connect accounts (MetaMask will prompt)
|
||||
await window.ethereum.request({ method: "eth_requestAccounts" });
|
||||
|
||||
// Get the connected accounts
|
||||
const accounts = await web3.eth.getAccounts();
|
||||
|
||||
// Show the first connected account in the page
|
||||
setConnectedAccount(accounts[0]);
|
||||
});
|
||||
}
|
||||
|
||||
function disconnectWallet() {
|
||||
unsetConnectedAccount();
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
onClick={() =>
|
||||
connectedAccount ? disconnectWallet() : connectMetamask()
|
||||
}
|
||||
>
|
||||
{connectedAccount ? shortenAddress(connectedAccount) : "Connect MetaMask"}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"MessageBoxModule#MessageBox": "0x5FbDB2315678afecb367f032d93F642f64180aa3"
|
||||
"MessageBoxModule#MessageBox": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
|
||||
"MainModule#PADToken": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,18 @@ import { Route as rootRoute } from './routes/__root'
|
|||
|
||||
// Create Virtual Routes
|
||||
|
||||
const PadTokenLazyImport = createFileRoute('/pad-token')()
|
||||
const MessageBoxLazyImport = createFileRoute('/message-box')()
|
||||
const AboutLazyImport = createFileRoute('/about')()
|
||||
const IndexLazyImport = createFileRoute('/')()
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const PadTokenLazyRoute = PadTokenLazyImport.update({
|
||||
path: '/pad-token',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any).lazy(() => import('./routes/pad-token.lazy').then((d) => d.Route))
|
||||
|
||||
const MessageBoxLazyRoute = MessageBoxLazyImport.update({
|
||||
path: '/message-box',
|
||||
getParentRoute: () => rootRoute,
|
||||
|
|
@ -53,6 +59,10 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof MessageBoxLazyImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/pad-token': {
|
||||
preLoaderRoute: typeof PadTokenLazyImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +72,7 @@ export const routeTree = rootRoute.addChildren([
|
|||
IndexLazyRoute,
|
||||
AboutLazyRoute,
|
||||
MessageBoxLazyRoute,
|
||||
PadTokenLazyRoute,
|
||||
])
|
||||
|
||||
/* prettier-ignore-end */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
|
||||
import { Toaster } from "sonner";
|
||||
import { ConnectMetamaskButton } from "../components/ConnectMetamaskButton";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootLayout,
|
||||
|
|
@ -8,20 +9,26 @@ export const Route = createRootRoute({
|
|||
function RootLayout() {
|
||||
return (
|
||||
<>
|
||||
<header className="bg-primary text-primary-content p-4">
|
||||
<span className="text-4xl font-bold">OsiPad™</span>
|
||||
<hr className="my-1" />
|
||||
<div className="flex gap-2">
|
||||
<Link to="/" className="[&.active]:font-bold">
|
||||
Home
|
||||
</Link>
|
||||
<Link to="/about" className="[&.active]:font-bold">
|
||||
About
|
||||
</Link>
|
||||
<Link to="/message-box" className="[&.active]:font-bold">
|
||||
Message Box
|
||||
</Link>
|
||||
<header className="bg-primary text-primary-content p-4 flex items-center">
|
||||
<div className="flex-grow">
|
||||
<span className="text-4xl font-bold">OsiPad™</span>
|
||||
<hr className="my-1" />
|
||||
<div className="flex gap-3">
|
||||
<Link to="/" className="[&.active]:font-bold">
|
||||
Home
|
||||
</Link>
|
||||
<Link to="/about" className="[&.active]:font-bold">
|
||||
About
|
||||
</Link>
|
||||
<Link to="/message-box" className="[&.active]:font-bold">
|
||||
Message Box
|
||||
</Link>
|
||||
<Link to="/pad-token" className="[&.active]:font-bold">
|
||||
PAD Token
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<ConnectMetamaskButton />
|
||||
</header>
|
||||
<main>
|
||||
<Outlet />
|
||||
|
|
|
|||
|
|
@ -1,31 +1,18 @@
|
|||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import { tryWithToast } from "../utils";
|
||||
import { messageBox, web3 } from "../web3";
|
||||
import { useStore } from "../store";
|
||||
import { toastError, tryWithToast } from "../utils";
|
||||
import { messageBox } from "../web3";
|
||||
|
||||
export const Route = createLazyFileRoute("/message-box")({
|
||||
component: MessageBoxPage,
|
||||
});
|
||||
|
||||
function MessageBoxPage() {
|
||||
const [connectedAccount, setConnectedAccount] = useState("(nothingness)");
|
||||
const connectedAccount = useStore((state) => state.connectedAccount);
|
||||
const [message, setMessage] = useState("(nothingness)");
|
||||
const [newMessage, setNewMessage] = useState("");
|
||||
|
||||
// https://docs.web3js.org/guides/getting_started/metamask/#react-app
|
||||
async function connectMetamask() {
|
||||
await tryWithToast("Connect MetaMask", async () => {
|
||||
// Request user to connect accounts (MetaMask will prompt)
|
||||
await window.ethereum.request({ method: "eth_requestAccounts" });
|
||||
|
||||
// Get the connected accounts
|
||||
const accounts = await web3.eth.getAccounts();
|
||||
|
||||
// Show the first connected account in the page
|
||||
setConnectedAccount(accounts[0]);
|
||||
});
|
||||
}
|
||||
|
||||
async function getMessage() {
|
||||
await tryWithToast("Get Message", async () => {
|
||||
const _message: string = await messageBox.methods.getMessage().call();
|
||||
|
|
@ -34,6 +21,11 @@ function MessageBoxPage() {
|
|||
}
|
||||
|
||||
async function _setMessage() {
|
||||
if (!connectedAccount) {
|
||||
toastError("Set Message", "Connect your wallet first.");
|
||||
return;
|
||||
}
|
||||
|
||||
await tryWithToast("Set Message", async () => {
|
||||
await messageBox.methods
|
||||
.setMessage(newMessage)
|
||||
|
|
@ -43,24 +35,6 @@ function MessageBoxPage() {
|
|||
|
||||
return (
|
||||
<div className="container mx-auto p-4 grid md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<div className="card bg-base-200">
|
||||
<div className="card-body items-start">
|
||||
<h2 className="text-2xl font-bold">✨Essentials✨</h2>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={() => connectMetamask()}
|
||||
>
|
||||
Connect to MetaMask
|
||||
</button>
|
||||
|
||||
<div>
|
||||
<span className="font-bold">Connected account address:</span>{" "}
|
||||
{connectedAccount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card bg-base-200">
|
||||
<div className="card-body items-start">
|
||||
<h2 className="text-2xl font-bold">Get Message</h2>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import type { Numbers } from "web3";
|
||||
import { useStore } from "../store";
|
||||
import { getFormInputValue, toastError, tryWithToast } from "../utils";
|
||||
import { padToken, web3 } from "../web3";
|
||||
|
||||
export const Route = createLazyFileRoute("/pad-token")({
|
||||
component: PadTokenPage,
|
||||
});
|
||||
|
||||
function PadTokenPage() {
|
||||
const connectedAccount = useStore((state) => state.connectedAccount);
|
||||
const [balance, setBalance] = useState("(nothingness)");
|
||||
|
||||
const onBalanceOfFormSubmit: React.FormEventHandler<HTMLFormElement> = async (
|
||||
event
|
||||
) => {
|
||||
event.preventDefault();
|
||||
|
||||
const address = getFormInputValue(
|
||||
event.target as HTMLFormElement,
|
||||
"address"
|
||||
).trim();
|
||||
if (!address) {
|
||||
toastError("Balance Of", "Enter an address.");
|
||||
return;
|
||||
}
|
||||
|
||||
await tryWithToast("Balance Of", async () => {
|
||||
const balanceBN: Numbers = await padToken.methods
|
||||
.balanceOf(address)
|
||||
.call();
|
||||
setBalance(web3.utils.fromWei(balanceBN, "ether"));
|
||||
});
|
||||
};
|
||||
|
||||
const onMintFormSubmit: React.FormEventHandler<HTMLFormElement> = async (
|
||||
event
|
||||
) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!connectedAccount) {
|
||||
toastError("Mint", "Connect your wallet first.");
|
||||
return;
|
||||
}
|
||||
|
||||
const to = getFormInputValue(event.target as HTMLFormElement, "to").trim();
|
||||
if (!to) {
|
||||
toastError("Mint", "Enter a target address.");
|
||||
return;
|
||||
}
|
||||
|
||||
const amount = getFormInputValue(
|
||||
event.target as HTMLFormElement,
|
||||
"amount"
|
||||
).trim();
|
||||
if (!amount) {
|
||||
toastError("Mint", "Enter an amount.");
|
||||
return;
|
||||
}
|
||||
|
||||
await tryWithToast("Mint", async () => {
|
||||
await padToken.methods
|
||||
.mint(to, web3.utils.toWei(amount, "ether"))
|
||||
.send({ from: connectedAccount });
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4 grid md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<div className="card bg-base-200">
|
||||
<div className="card-body items-start">
|
||||
<h2 className="text-2xl font-bold">Balance Of</h2>
|
||||
<form onSubmit={onBalanceOfFormSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
name="address"
|
||||
className="input"
|
||||
placeholder="Address"
|
||||
required
|
||||
minLength={42}
|
||||
maxLength={42}
|
||||
/>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Get
|
||||
</button>
|
||||
<div>
|
||||
<span className="font-bold">Balance:</span> {balance} PAD
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card bg-base-200">
|
||||
<div className="card-body items-start">
|
||||
<h2 className="text-2xl font-bold">Mint</h2>
|
||||
<form onSubmit={onMintFormSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
name="to"
|
||||
className="input"
|
||||
placeholder="To"
|
||||
required
|
||||
minLength={42}
|
||||
maxLength={42}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
name="amount"
|
||||
className="input"
|
||||
placeholder="Amount"
|
||||
required
|
||||
/>
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Mint
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { create } from "zustand";
|
||||
|
||||
interface State {
|
||||
connectedAccount: string | null;
|
||||
setConnectedAccount: (newAccount: string) => void;
|
||||
unsetConnectedAccount: () => void;
|
||||
}
|
||||
|
||||
export const useStore = create<State>()((set) => ({
|
||||
connectedAccount: null,
|
||||
setConnectedAccount: (newAccount) => set({ connectedAccount: newAccount }),
|
||||
unsetConnectedAccount: () => set({ connectedAccount: null }),
|
||||
}));
|
||||
|
|
@ -1,21 +1,41 @@
|
|||
import { toast } from "sonner";
|
||||
|
||||
export function toastSuccess(taskName: string) {
|
||||
toast.success(`${taskName}: Success`, {
|
||||
className: "border-none bg-green-100",
|
||||
});
|
||||
}
|
||||
|
||||
export function toastError(taskName: string, errorMessage: string) {
|
||||
toast.error(`${taskName}: Error`, {
|
||||
description: errorMessage,
|
||||
className: "border-none bg-red-100",
|
||||
});
|
||||
}
|
||||
|
||||
export async function tryWithToast(
|
||||
taskName: string,
|
||||
taskFn: () => Promise<void>
|
||||
) {
|
||||
try {
|
||||
await taskFn();
|
||||
toast.success(`${taskName}: Success`, {
|
||||
className: "border-none bg-green-100",
|
||||
});
|
||||
toastSuccess(taskName);
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
toast.error(`${taskName}: Error`, {
|
||||
description:
|
||||
error.message ||
|
||||
"No error message. Check the DevTools console for details.",
|
||||
className: "border-none bg-red-100",
|
||||
});
|
||||
toastError(
|
||||
taskName,
|
||||
error.message ||
|
||||
"No error message. Check the DevTools console for details."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function shortenAddress(address: string) {
|
||||
return address.slice(0, 6) + "..." + address.slice(-4);
|
||||
}
|
||||
|
||||
export function getFormInputValue(form: HTMLFormElement, name: string) {
|
||||
// @ts-expect-error
|
||||
const input = form.elements[name] as HTMLInputElement;
|
||||
return input.value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Web3 } from "web3";
|
||||
import messageBoxArtifacts from "./evm-output/MessageBox.artifacts.json";
|
||||
import padTokenArtifacts from "./evm-output/PADToken.artifacts.json";
|
||||
import deployedAddresses from "./evm-output/deployed_addresses.json";
|
||||
|
||||
export let web3: Web3;
|
||||
|
|
@ -17,3 +18,8 @@ export const messageBox = new web3!.eth.Contract(
|
|||
messageBoxArtifacts.abi,
|
||||
deployedAddresses["MessageBoxModule#MessageBox"]
|
||||
);
|
||||
|
||||
export const padToken = new web3!.eth.Contract(
|
||||
padTokenArtifacts.abi,
|
||||
deployedAddresses["MainModule#PADToken"]
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue