{profile.name}
{signedAccountId}
diff --git a/src/components/tools/FungibleToken/CreateTokenForm.tsx b/src/components/tools/FungibleToken/CreateTokenForm.tsx
index 4f1729e5c..238c2f9ed 100644
--- a/src/components/tools/FungibleToken/CreateTokenForm.tsx
+++ b/src/components/tools/FungibleToken/CreateTokenForm.tsx
@@ -1,9 +1,10 @@
import { Button, FileInput, Flex, Form, Grid, Input, openToast, Text } from '@near-pagoda/ui';
-import React, { useContext } from 'react';
-import type { SubmitHandler } from 'react-hook-form';
+import { formatNearAmount } from 'near-api-js/lib/utils/format';
+import React, { useCallback, useContext, useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { NearContext } from '@/components/wallet-selector/WalletSelector';
+import { network } from '@/config';
type FormData = {
total_supply: string;
@@ -13,123 +14,172 @@ type FormData = {
decimals: number;
};
-const FACTORY_CONTRACT = 'tkn.primitives.near';
+const FACTORY_CONTRACT = network.ftContract;
const MAX_FILE_SIZE = 10 * 1024;
const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'];
-const CreateTokenForm: React.FC = () => {
+const validateImage = (files: FileList) => {
+ if (files.length === 0) return 'Image is required';
+ const file = files[0];
+ if (file.size > MAX_FILE_SIZE) return 'Image size should be less than 10KB';
+ if (!ACCEPTED_IMAGE_TYPES.includes(file.type)) return 'Not a valid image format';
+ return true;
+};
+
+const convertToBase64 = (file: File): Promise
=> {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => resolve(reader.result as string);
+ reader.onerror = (error) => reject(error);
+ });
+};
+
+const CreateTokenForm = ({ reload }: { reload: (delay: number) => void }) => {
const {
control,
register,
handleSubmit,
+ watch,
formState: { errors, isSubmitting },
} = useForm();
const { wallet, signedAccountId } = useContext(NearContext);
+ const [requiredDeposit, setRequiredDeposit] = useState('0');
+
+ const symbolAvailable = useCallback(
+ async (symbol: string) => {
+ try {
+ await wallet?.getBalance(`${symbol}.${FACTORY_CONTRACT}`);
+ return `${symbol}.${FACTORY_CONTRACT} already exists`;
+ } catch {
+ return true;
+ }
+ },
+ [wallet],
+ );
- const validateImage = (files: FileList) => {
- if (files.length === 0) return 'Image is required';
- const file = files[0];
- if (file.size > MAX_FILE_SIZE) return 'Image size should be less than 10KB';
- if (!ACCEPTED_IMAGE_TYPES.includes(file.type)) return 'Not a valid image format';
- return true;
- };
-
- const convertToBase64 = (file: File): Promise => {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsDataURL(file);
- reader.onload = () => resolve(reader.result as string);
- reader.onerror = (error) => reject(error);
- });
- };
-
- const onSubmit: SubmitHandler = async (data) => {
- let base64Image = '';
- if (data.icon[0]) {
- base64Image = await convertToBase64(data.icon[0]);
- }
-
- const total_supply = BigInt(data.total_supply) * BigInt(Math.pow(10, Number(data.decimals)));
-
- const args = {
- args: {
- owner_id: signedAccountId,
- total_supply: total_supply.toString(),
- metadata: {
- spec: 'ft-1.0.0',
- name: data.name,
- symbol: data.symbol,
- icon: base64Image,
- decimals: data.decimals,
- },
- },
- account_id: signedAccountId,
- };
-
- const requiredDeposit = await wallet?.viewMethod({ contractId: FACTORY_CONTRACT, method: 'get_required', args });
-
- try {
- const result = await wallet?.signAndSendTransactions({
- transactions: [
- {
- receiverId: FACTORY_CONTRACT,
- actions: [
- {
- type: 'FunctionCall',
- params: {
- methodName: 'create_token',
- args,
- gas: '300000000000000',
- deposit: requiredDeposit,
- },
- },
- ],
+ // Watch all form fields
+ const formData = watch();
+ const onSubmit = useCallback(
+ async ({ total_supply, decimals, icon, name, symbol }: FormData, actuallySubmit: boolean) => {
+ if (!signedAccountId) return;
+
+ total_supply = total_supply || '0';
+ decimals = decimals || 0;
+ name = name || '';
+ symbol = symbol || '';
+ icon = icon || [false];
+
+ const base64Image = icon[0] ? await convertToBase64(icon[0]) : '';
+
+ const supply = BigInt(total_supply) * BigInt(Math.pow(10, Number(decimals)));
+
+ const args = {
+ args: {
+ owner_id: signedAccountId,
+ total_supply: supply.toString(),
+ metadata: {
+ spec: 'ft-1.0.0',
+ name,
+ symbol,
+ icon: base64Image,
+ decimals,
},
- ],
- });
+ },
+ account_id: signedAccountId,
+ };
+
+ const deposit = await wallet?.viewMethod({ contractId: FACTORY_CONTRACT, method: 'get_required', args });
+
+ setRequiredDeposit(formatNearAmount(deposit, 2));
+
+ if (!actuallySubmit) return;
+
+ let result = false;
+
+ try {
+ result = await wallet?.callMethod({
+ contractId: FACTORY_CONTRACT,
+ method: 'create_token',
+ args,
+ gas: '300000000000000',
+ deposit: deposit,
+ });
+ } catch (error) {}
if (result) {
- const transactionId = result[0].transaction_outcome.id;
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- window.open(`https://nearblocks.io/txns/${transactionId}`, '_blank')!.focus();
+ openToast({
+ type: 'success',
+ title: 'Token Created',
+ description: `Token ${name} (${symbol}) created successfully`,
+ duration: 5000,
+ });
+ reload(5000);
+ } else {
+ openToast({
+ type: 'error',
+ title: 'Error',
+ description: 'Failed to create token',
+ duration: 5000,
+ });
}
+ },
+ [wallet, signedAccountId, reload],
+ );
- openToast({
- type: 'success',
- title: 'Token Created',
- description: `Token ${data.name} (${data.symbol}) created successfully`,
- duration: 5000,
- });
- } catch (error) {
- openToast({
- type: 'error',
- title: 'Error',
- description: 'Failed to create token',
- duration: 5000,
- });
- }
- };
+ useEffect(() => {
+ onSubmit(formData, false);
+ }, [onSubmit, formData]);
return (
<>
-
- Mint a Fungible Token
+ Mint a Fungible Token
+
+ This tool allows you to deploy your own NEP-141 smart contract (Fungible Tokens)
-