From faa49f1ec23bb25ccbb28a1238ed754abd9e2541 Mon Sep 17 00:00:00 2001 From: Matias Benary Date: Thu, 19 Sep 2024 12:07:45 -0300 Subject: [PATCH 1/3] wip --- .../tools/FungibleToken/CreateTokenForm.tsx | 195 +++ src/components/tools/FungibleToken/index.tsx | 52 + src/hooks/useFungibleTokens.ts | 61 + src/pages/tools.tsx | 5 +- src/utils/white-list.json | 1213 +++++++++++++++++ 5 files changed, 1525 insertions(+), 1 deletion(-) create mode 100644 src/components/tools/FungibleToken/CreateTokenForm.tsx create mode 100644 src/components/tools/FungibleToken/index.tsx create mode 100644 src/hooks/useFungibleTokens.ts create mode 100644 src/utils/white-list.json diff --git a/src/components/tools/FungibleToken/CreateTokenForm.tsx b/src/components/tools/FungibleToken/CreateTokenForm.tsx new file mode 100644 index 000000000..377ae82c6 --- /dev/null +++ b/src/components/tools/FungibleToken/CreateTokenForm.tsx @@ -0,0 +1,195 @@ +import { NearContext } from '@/components/WalletSelector'; +import { Button, FileInput, Flex, Form, Input, openToast } from '@near-pagoda/ui'; +import React, { useContext, useState } from 'react'; +import type { SubmitHandler} from 'react-hook-form'; +import { Controller, useForm } from 'react-hook-form'; + +type FormData = { + owner_id: string; + total_supply: string; + name: string; + symbol: string; + icon: FileList; + decimals: number; +}; + +const FACTORY_CONTRACT = 'tkn.primitives.near'; + +const MAX_FILE_SIZE = 10 * 1024 ; +const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml']; + +const CreateTokenForm: React.FC = () => { + const { control, register, handleSubmit, formState: { errors, isSubmitting } } = useForm(); + + const { wallet, signedAccountId } = useContext(NearContext); + + const [imagePreview, setImagePreview] = useState(null); + + 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 onImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setImagePreview(reader.result as string); + }; + reader.readAsDataURL(file); + } + }; + + 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) => { + try { + let base64Image = ''; + if (data.icon[0]) { + base64Image = await convertToBase64(data.icon[0]); + } + const args = { + args: { + owner_id: data.owner_id, + total_supply: data.total_supply, + metadata: { + spec: "ft-1.0.0", + name: data.name, + symbol: data.symbol, + icon: base64Image, + decimals: data.decimals, + }, + }, + account_id: data.owner_id, + }; + + const requiredDeposit = await wallet?.viewMethod({ contractId: FACTORY_CONTRACT, method: 'get_required', args }); + + const result = await wallet?.signAndSendTransactions({ + transactions: [{ + receiverId: FACTORY_CONTRACT, + actions: [ + { + type: 'FunctionCall', + params: { + methodName: 'create_token', + args, + gas: "300000000000000", + deposit: requiredDeposit + }, + }, + ], + }] + }); + + 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 ${data.name} (${data.symbol}) created successfully`, + duration: 5000, + }); + } catch (error) { + openToast({ + type: 'error', + title: 'Error', + description: 'Failed to create token', + duration: 5000, + }); + } + }; + + return ( +
+ + + + + +
+ ( + { + const files = value; + field.onChange(files); + }} + /> + )} + /> + + Accepted Formats: PNG, JPEG, GIF, SVG | Ideal dimension: 1:1 | Max size: 10kb + +
+ +