From b9e7de0c8ddb2ce905fd24c38c389a2a268474ee Mon Sep 17 00:00:00 2001 From: Nikita Yutanov Date: Thu, 4 Jul 2024 17:50:26 +0300 Subject: [PATCH] refactor(idea/frontend): upload program/code `signAndSend` (#1585) --- idea/frontend/src/api/program/requests.ts | 4 +- .../src/hooks/use-contract-api-with-file.ts | 2 +- idea/frontend/src/hooks/use-sign-and-send.tsx | 18 +- .../frontend/src/hooks/useCodeUpload/types.ts | 14 +- .../src/hooks/useCodeUpload/useCodeUpload.tsx | 137 +++------ .../src/hooks/useProgramActions/consts.ts | 7 - .../src/hooks/useProgramActions/types.ts | 25 +- .../useProgramActions/useProgramActions.tsx | 291 +++++------------- .../ui/InitializeProgram.tsx | 30 +- .../pages/uploadProgram/ui/UploadProgram.tsx | 13 +- .../ui/UploadMetadataModal.tsx | 2 +- 11 files changed, 165 insertions(+), 378 deletions(-) delete mode 100644 idea/frontend/src/hooks/useProgramActions/consts.ts diff --git a/idea/frontend/src/api/program/requests.ts b/idea/frontend/src/api/program/requests.ts index 2f4131a30b..fc798bb781 100644 --- a/idea/frontend/src/api/program/requests.ts +++ b/idea/frontend/src/api/program/requests.ts @@ -8,8 +8,8 @@ import { FetchProgramsParams, ProgramPaginationModel } from './types'; const fetchProgram = (id: string) => rpcService.callRPC(RpcMethods.GetProgram, { id }); -const addProgramName = (params: { id: HexString; name: string }, isDevChain: boolean | undefined) => - isDevChain ? Promise.resolve() : rpcService.callRPC(RpcMethods.AddProgramName, params); +const addProgramName = (params: { id: HexString; name: string }) => + rpcService.callRPC(RpcMethods.AddProgramName, params); const fetchPrograms = (params: FetchProgramsParams) => rpcService.callRPC(RpcMethods.GetAllPrograms, params); diff --git a/idea/frontend/src/hooks/use-contract-api-with-file.ts b/idea/frontend/src/hooks/use-contract-api-with-file.ts index 05747572cd..2d30130d55 100644 --- a/idea/frontend/src/hooks/use-contract-api-with-file.ts +++ b/idea/frontend/src/hooks/use-contract-api-with-file.ts @@ -52,7 +52,7 @@ function useContractApiWithFile(codeIdOrBuffer: HexString | Buffer | undefined) if (extension === FILE_EXTENSION.IDL) { metadata.reset(); - await sails.set(text); + sails.set(text); } }; diff --git a/idea/frontend/src/hooks/use-sign-and-send.tsx b/idea/frontend/src/hooks/use-sign-and-send.tsx index 0373da452f..4106c190f1 100644 --- a/idea/frontend/src/hooks/use-sign-and-send.tsx +++ b/idea/frontend/src/hooks/use-sign-and-send.tsx @@ -3,21 +3,26 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'; import { Event } from '@polkadot/types/interfaces'; import { ISubmittableResult } from '@polkadot/types/types'; import { web3FromSource } from '@polkadot/extension-dapp'; +import { ReactNode } from 'react'; import { useExtrinsicFailedMessage } from './use-extrinsic-failed-message'; type Extrinsic = SubmittableExtrinsic<'promise', ISubmittableResult>; type Options = { + successAlert: ReactNode; onSuccess: () => void; onError: () => void; onFinally: () => void; + onFinalized: (value: ISubmittableResult) => void; }; -const DEFAULT_OPTIONS: Options = { +const DEFAULT_OPTIONS = { + successAlert: 'Success', onSuccess: () => {}, onError: () => {}, onFinally: () => {}, + onFinalized: () => {}, } as const; function useSignAndSend() { @@ -26,7 +31,7 @@ function useSignAndSend() { const getExtrinsicFailedMessage = useExtrinsicFailedMessage(); const handleEvent = (event: Event, method: string, options: Options) => { - const { onSuccess, onError, onFinally } = options; + const { successAlert, onSuccess, onError, onFinally } = options; const alertOptions = { title: `${event.section}.${event.method}` }; if (event.method === 'ExtrinsicFailed') { @@ -39,16 +44,17 @@ function useSignAndSend() { } if (event.method === method) { - alert.success('Success', alertOptions); + alert.success(successAlert, alertOptions); onSuccess(); onFinally(); } }; - const handleStatus = ({ events, status }: ISubmittableResult, method: string, options: Options, alertId: string) => { + const handleStatus = (result: ISubmittableResult, method: string, options: Options, alertId: string) => { + const { events, status } = result; const { isInvalid, isReady, isInBlock, isFinalized } = status; - const { onError, onFinally } = options; + const { onError, onFinally, onFinalized } = options; if (isInvalid) { alert.update(alertId, 'Transaction error. Status: isInvalid', DEFAULT_ERROR_OPTIONS); @@ -64,6 +70,8 @@ function useSignAndSend() { if (isFinalized) { alert.update(alertId, 'Finalized', DEFAULT_SUCCESS_OPTIONS); + onFinalized(result); + events.forEach(({ event }) => handleEvent(event, method, options)); } }; diff --git a/idea/frontend/src/hooks/useCodeUpload/types.ts b/idea/frontend/src/hooks/useCodeUpload/types.ts index 17f2c0fe44..264768779a 100644 --- a/idea/frontend/src/hooks/useCodeUpload/types.ts +++ b/idea/frontend/src/hooks/useCodeUpload/types.ts @@ -1,8 +1,4 @@ import { HexString } from '@polkadot/util/types'; -import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { ISubmittableResult } from '@polkadot/types/types'; - -import { ParamsToSignAndSend as CommonParamsToSignAndSend } from '@/entities/hooks'; type ParamsToUploadCode = { optBuffer: Buffer; @@ -13,12 +9,4 @@ type ParamsToUploadCode = { resolve: () => void; }; -type ParamsToSignAndSend = Omit & { - extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>; - name: string; - codeId: HexString; - metaHex: HexString | undefined; - idl: string | undefined; -}; - -export type { ParamsToUploadCode, ParamsToSignAndSend }; +export type { ParamsToUploadCode }; diff --git a/idea/frontend/src/hooks/useCodeUpload/useCodeUpload.tsx b/idea/frontend/src/hooks/useCodeUpload/useCodeUpload.tsx index 5b13bde510..835b2642ad 100644 --- a/idea/frontend/src/hooks/useCodeUpload/useCodeUpload.tsx +++ b/idea/frontend/src/hooks/useCodeUpload/useCodeUpload.tsx @@ -1,118 +1,77 @@ -import { useCallback } from 'react'; import { web3FromSource } from '@polkadot/extension-dapp'; -import { EventRecord } from '@polkadot/types/interfaces'; import { HexString } from '@polkadot/util/types'; -import { useApi, useAlert, useAccount, DEFAULT_ERROR_OPTIONS, DEFAULT_SUCCESS_OPTIONS } from '@gear-js/react-hooks'; +import { useApi, useAccount } from '@gear-js/react-hooks'; -import { useChain, useModal } from '@/hooks'; -import { Method } from '@/features/explorer'; -import { checkWallet, getExtrinsicFailedMessage } from '@/shared/helpers'; -import { PROGRAM_ERRORS, TransactionName, TransactionStatus, UPLOAD_METADATA_TIMEOUT } from '@/shared/config'; +import { useChain, useModal, useSignAndSend } from '@/hooks'; +import { UPLOAD_METADATA_TIMEOUT } from '@/shared/config'; import { CopiedInfo } from '@/shared/ui/copiedInfo'; import { addMetadata, addCodeName } from '@/api'; import { addIdl } from '@/features/sails'; -import { ParamsToUploadCode, ParamsToSignAndSend } from './types'; +import { ParamsToUploadCode } from './types'; const useCodeUpload = () => { const { api, isApiReady } = useApi(); - const alert = useAlert(); const { account } = useAccount(); const { showModal } = useModal(); const { isDevChain } = useChain(); - - const handleEventsStatus = (events: EventRecord[], codeHash: HexString, resolve?: () => void) => { + const signAndSend = useSignAndSend(); + + // will be refactored in the upcoming local indexer refactoring + const handleMetadataUpload = ( + codeId: HexString, + codeName: string, + metaHex: HexString | undefined, + idl: string | undefined, + ) => { if (!isApiReady) throw new Error('API is not initialized'); + if (isDevChain) return; - events.forEach(({ event }) => { - const { method, section } = event; - const alertOptions = { title: `${section}.${method}` }; - - if (method === Method.ExtrinsicFailed) { - alert.error(getExtrinsicFailedMessage(api, event), alertOptions); - } else if (method === Method.CodeChanged) { - alert.success(, alertOptions); + // timeout cuz wanna be sure that block data is ready + setTimeout(async () => { + const id = codeId; + const name = codeName || id; - if (resolve) resolve(); - } - }); - }; + await addCodeName({ id, name }); + if (idl) addIdl(codeId, idl); - const signAndSend = async ({ extrinsic, signer, codeId, metaHex, idl, name, resolve }: ParamsToSignAndSend) => { - const alertId = alert.loading('SignIn', { title: TransactionName.SubmitCode }); - - try { - if (!isApiReady) throw new Error('API is not initialized'); - - await extrinsic.signAndSend(account!.address, { signer }, ({ events, status }) => { - if (status.isReady) { - alert.update(alertId, TransactionStatus.Ready); - } else if (status.isInBlock) { - alert.update(alertId, TransactionStatus.InBlock); - handleEventsStatus(events, codeId, resolve); - } else if (status.isFinalized) { - alert.update(alertId, TransactionStatus.Finalized, DEFAULT_SUCCESS_OPTIONS); - - if (isDevChain) return; - - // timeout cuz wanna be sure that block data is ready - setTimeout(() => { - const id = codeId; - - addCodeName({ id, name: name || id }) - .then(async () => { - // TODO: no need to upload if meta/idl is from storage - if (metaHex) addMetadata(await api.code.metaHash(id), metaHex); - if (idl) addIdl(id, idl); - }) - .catch(({ message }: Error) => alert.error(message)); - }, UPLOAD_METADATA_TIMEOUT); - } else if (status.isInvalid) { - alert.update(alertId, PROGRAM_ERRORS.INVALID_TRANSACTION, DEFAULT_ERROR_OPTIONS); - } - }); - } catch (error) { - const message = (error as Error).message; + if (!metaHex) return; + const hash = await api.code.metaHash(id); - alert.update(alertId, message, DEFAULT_ERROR_OPTIONS); - } + addMetadata(hash, metaHex); + }, UPLOAD_METADATA_TIMEOUT); }; - const uploadCode = useCallback( - async ({ optBuffer, name, voucherId, metaHex, idl, resolve }: ParamsToUploadCode) => { - try { - if (!isApiReady) throw new Error('API is not initialized'); - checkWallet(account); - - const { address, meta } = account!; - - const [code, { signer }] = await Promise.all([api.code.upload(optBuffer), web3FromSource(meta.source)]); + return async ({ optBuffer, name, voucherId, metaHex, idl, resolve }: ParamsToUploadCode) => { + if (!isApiReady) throw new Error('API is not initialized'); + if (!account) throw new Error('Account not found'); - const codeExtrinsic = code.extrinsic; - const codeId = code.codeHash; - const extrinsic = voucherId ? api.voucher.call(voucherId, { UploadCode: codeExtrinsic }) : codeExtrinsic; + const { address, meta } = account; - const { partialFee } = await api.code.paymentInfo(address, { signer }); + const [{ codeHash, extrinsic: codeExtrinsic }, { signer }] = await Promise.all([ + api.code.upload(optBuffer), + web3FromSource(meta.source), + ]); - const handleConfirm = () => signAndSend({ extrinsic, signer, name, codeId, metaHex, idl, resolve }); + const extrinsic = voucherId ? api.voucher.call(voucherId, { UploadCode: codeExtrinsic }) : codeExtrinsic; + const { partialFee } = await api.code.paymentInfo(address, { signer }); - showModal('transaction', { - fee: partialFee.toHuman(), - name: TransactionName.SubmitCode, - addressFrom: address, - onConfirm: handleConfirm, - }); - } catch (error) { - const message = (error as Error).message; + const onFinalized = () => handleMetadataUpload(codeHash, name, metaHex, idl); - alert.error(message); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [api, account], - ); + const onConfirm = () => + signAndSend(extrinsic, 'CodeChanged', { + successAlert: , + onSuccess: resolve, + onFinalized, + }); - return uploadCode; + showModal('transaction', { + fee: partialFee.toHuman(), + name: `${extrinsic.method.section}.${extrinsic.method.method}`, + addressFrom: address, + onConfirm, + }); + }; }; export { useCodeUpload }; diff --git a/idea/frontend/src/hooks/useProgramActions/consts.ts b/idea/frontend/src/hooks/useProgramActions/consts.ts deleted file mode 100644 index 745aa1dc1f..0000000000 --- a/idea/frontend/src/hooks/useProgramActions/consts.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { AlertOptions } from '@gear-js/react-hooks'; - -const ALERT_OPTIONS: AlertOptions = { - title: 'Program initialization', -}; - -export { ALERT_OPTIONS }; diff --git a/idea/frontend/src/hooks/useProgramActions/types.ts b/idea/frontend/src/hooks/useProgramActions/types.ts index 3a9c998ba8..261d0bc385 100644 --- a/idea/frontend/src/hooks/useProgramActions/types.ts +++ b/idea/frontend/src/hooks/useProgramActions/types.ts @@ -1,8 +1,6 @@ import { ProgramMetadata } from '@gear-js/api'; import { HexString } from '@polkadot/util/types'; -import { OperationCallbacks, ParamsToSignAndSend } from '@/entities/hooks'; - type Payload = { value: string; gasLimit: string; @@ -15,25 +13,4 @@ type Payload = { idl?: string; }; -type DataToUpload = { - optBuffer: Buffer; - payload: Payload; -}; - -type DataToCreate = { - codeId: HexString; - payload: Payload; -}; - -type ParamsToUpload = OperationCallbacks & DataToUpload; - -type ParamsToCreate = OperationCallbacks & DataToCreate; - -type ParamsToSignAndUpload = ParamsToSignAndSend & { - method: string; - payload: Payload; - programId: HexString; - codeId: HexString; -}; - -export type { Payload, ParamsToUpload, ParamsToCreate, ParamsToSignAndUpload }; +export type { Payload }; diff --git a/idea/frontend/src/hooks/useProgramActions/useProgramActions.tsx b/idea/frontend/src/hooks/useProgramActions/useProgramActions.tsx index f2d4a19803..655f5a4223 100644 --- a/idea/frontend/src/hooks/useProgramActions/useProgramActions.tsx +++ b/idea/frontend/src/hooks/useProgramActions/useProgramActions.tsx @@ -1,257 +1,106 @@ -import { useCallback } from 'react'; import { generatePath } from 'react-router-dom'; -import { EventRecord } from '@polkadot/types/interfaces'; +import { useApi, useAccount } from '@gear-js/react-hooks'; +import { HexString, IProgramCreateResult, IProgramUploadResult, ProgramMetadata } from '@gear-js/api'; import { web3FromSource } from '@polkadot/extension-dapp'; -import { useApi, useAccount, useAlert, DEFAULT_ERROR_OPTIONS, DEFAULT_SUCCESS_OPTIONS } from '@gear-js/react-hooks'; -import { HexString } from '@polkadot/util/types'; -import { ProgramMetadata } from '@gear-js/api'; +import { ISubmittableResult } from '@polkadot/types/types'; -import { useChain, useModal } from '@/hooks'; +import { useChain, useModal, useSignAndSend } from '@/hooks'; import { uploadLocalProgram } from '@/api/LocalDB'; -import { Method } from '@/features/explorer'; -import { OperationCallbacks } from '@/entities/hooks'; -import { - PROGRAM_ERRORS, - TransactionName, - TransactionStatus, - absoluteRoutes, - UPLOAD_METADATA_TIMEOUT, -} from '@/shared/config'; -import { checkWallet, getExtrinsicFailedMessage, isNullOrUndefined } from '@/shared/helpers'; +import { absoluteRoutes, UPLOAD_METADATA_TIMEOUT } from '@/shared/config'; +import { isNullOrUndefined } from '@/shared/helpers'; import { CustomLink } from '@/shared/ui/customLink'; -import { ProgramStatus, useProgramStatus } from '@/features/program'; +import { useProgramStatus } from '@/features/program'; import { isHumanTypesRepr } from '@/features/metadata'; import { addProgramName } from '@/api'; import { addIdl } from '@/features/sails'; import { useMetadataUpload } from '../useMetadataUpload'; -import { ALERT_OPTIONS } from './consts'; -import { Payload, ParamsToCreate, ParamsToUpload, ParamsToSignAndUpload } from './types'; +import { Payload } from './types'; const useProgramActions = () => { - const alert = useAlert(); const { api, isApiReady } = useApi(); const { account } = useAccount(); const { isDevChain } = useChain(); + const signAndSend = useSignAndSend(); const { showModal } = useModal(); const { getProgramStatus } = useProgramStatus(); const uploadMetadata = useMetadataUpload(); - const getProgramMessage = (programId: string) => ( + const getSuccessAlert = (programId: string) => (

ID:

); - const createProgram = (codeId: HexString, payload: Payload) => { - if (!isApiReady) throw new Error('API is not initialized'); - - const { gasLimit, value, initPayload, metadata, payloadType, keepAlive } = payload; - - const program = { value, codeId, gasLimit, initPayload, keepAlive }; - - const result = api.program.create(program, metadata, payloadType); + // will be refactored in the upcoming local indexer refactoring + const handleMetadataUpload = async ( + programId: HexString, + codeId: HexString, + payload: Payload, + result: ISubmittableResult, + ) => { + const { programName, metaHex, idl } = payload; + const name = programName || programId; - return result.programId; - }; + // timeout cuz wanna be sure that block data is ready + setTimeout(async () => { + if (!isDevChain) await addProgramName({ id: programId, name }); + if (metaHex) uploadMetadata({ codeHash: codeId, metaHex, programId }); + if (idl) addIdl(codeId, idl); + }, UPLOAD_METADATA_TIMEOUT); - const uploadProgram = (optBuffer: Buffer, payload: Payload) => { + if (!isDevChain) return; if (!isApiReady) throw new Error('API is not initialized'); - - const { gasLimit, value, initPayload, metadata, payloadType, keepAlive } = payload; - - const program = { code: optBuffer, value, gasLimit, initPayload, keepAlive }; - - return api.program.upload(program, metadata, payloadType); + if (!account) throw new Error('Account not found'); + + const programStatus = await getProgramStatus(programId); + const metahash = await api.code.metaHash(codeId); + const meta = metaHex ? ProgramMetadata.from(metaHex) : undefined; + + const hasState = + !!meta && + (typeof meta.types.state === 'number' || + (isHumanTypesRepr(meta.types.state) && !isNullOrUndefined(meta.types.state.output))); + + uploadLocalProgram({ + id: programId, + owner: account.decodedAddress, + code: { id: codeId }, + status: programStatus, + blockHash: result.status.asFinalized.toHex(), + hasState, + metahash, + name, + }); }; - const handleEventsStatus = (events: EventRecord[], { reject }: OperationCallbacks) => { + return async ( + { programId, extrinsic }: IProgramCreateResult | IProgramUploadResult, + codeId: HexString, + payload: Payload, + onSuccess?: () => void, + onError?: () => void, + ) => { if (!isApiReady) throw new Error('API is not initialized'); - - events.forEach(({ event }) => { - const { method, section } = event; - const alertOptions = { title: `${section}.${method}` }; - - if (method === Method.ExtrinsicFailed) { - alert.error(getExtrinsicFailedMessage(api, event), alertOptions); - - if (reject) reject(); - } + if (!account) throw new Error('Account not found'); + + const { meta, address } = account; + const { signer } = await web3FromSource(meta.source); + const { partialFee } = await api.program.paymentInfo(address, { signer }); + + const successAlert = getSuccessAlert(programId); + const onFinalized = (result: ISubmittableResult) => handleMetadataUpload(programId, codeId, payload, result); + const onConfirm = () => signAndSend(extrinsic, 'ProgramChanged', { successAlert, onSuccess, onError, onFinalized }); + + showModal('transaction', { + fee: partialFee.toHuman(), + name: `${extrinsic.method.section}.${extrinsic.method.method}`, + addressFrom: address, + onAbort: onError, + onConfirm, }); }; - - const signAndUpload = async ({ - signer, - payload, - programId, - codeId, - reject, - resolve, - method, - }: ParamsToSignAndUpload) => { - const { metaHex, programName, idl } = payload; - const alertId = alert.loading('SignIn', { title: method }); - const programMessage = getProgramMessage(programId); - const name = programName || programId; - - try { - if (!isApiReady) throw new Error('API is not initialized'); - - await api.program.signAndSend(account!.address, { signer }, ({ status, events }) => { - if (status.isReady) { - alert.update(alertId, TransactionStatus.Ready); - } else if (status.isInBlock) { - alert.update(alertId, TransactionStatus.InBlock); - } else if (status.isFinalized) { - alert.update(alertId, TransactionStatus.Finalized, DEFAULT_SUCCESS_OPTIONS); - handleEventsStatus(events, { reject, resolve }); - - // timeout cuz wanna be sure that block data is ready - setTimeout(() => { - addProgramName({ id: programId, name }, isDevChain).then(() => { - // TODO: no need to upload if meta/idl is from storage - if (metaHex) uploadMetadata({ codeHash: codeId, metaHex, programId }); - if (idl) addIdl(codeId, idl); - }); - }, UPLOAD_METADATA_TIMEOUT); - - getProgramStatus(programId).then(async (programStatus) => { - if ([ProgramStatus.Terminated, ProgramStatus.Exited].includes(programStatus)) { - alert.error(programMessage, ALERT_OPTIONS); - - if (reject) reject(); - } else { - alert.success(programMessage, ALERT_OPTIONS); - - if (resolve) resolve(); - } - - if (isDevChain) { - const metahash = await api.code.metaHash(codeId); - const meta = metaHex ? ProgramMetadata.from(metaHex) : undefined; - - const hasState = - !!meta && - (typeof meta.types.state === 'number' || - (isHumanTypesRepr(meta.types.state) && !isNullOrUndefined(meta.types.state.output))); - - await uploadLocalProgram({ - id: programId, - owner: account!.decodedAddress, - code: { id: codeId }, - status: programStatus, - blockHash: status.asFinalized.toHex(), - hasState, - metahash, - name, - }); - } - }); - } else if (status.isInvalid) { - alert.update(alertId, PROGRAM_ERRORS.INVALID_TRANSACTION, DEFAULT_ERROR_OPTIONS); - - if (reject) reject(); - } - }); - } catch (error) { - const message = (error as Error).message; - - alert.update(alertId, message, DEFAULT_ERROR_OPTIONS); - - if (reject) reject(); - } - }; - - const create = useCallback( - async ({ codeId, payload, reject, resolve }: ParamsToCreate) => { - try { - if (!isApiReady) throw new Error('API is not initialized'); - checkWallet(account); - - const { meta, address } = account!; - - const [{ signer }, programId] = await Promise.all([ - web3FromSource(meta.source), - createProgram(codeId, payload), - ]); - - const { partialFee } = await api.program.paymentInfo(address, { signer }); - - const handleConfirm = () => - signAndUpload({ - method: TransactionName.CreateProgram, - signer, - payload, - programId, - codeId, - reject, - resolve, - }); - - showModal('transaction', { - fee: partialFee.toHuman(), - name: TransactionName.CreateProgram, - addressFrom: address, - onAbort: reject, - onConfirm: handleConfirm, - }); - } catch (error) { - const message = (error as Error).message; - - alert.error(message); - if (reject) reject(); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [api, account, uploadMetadata], - ); - - const upload = useCallback( - async ({ optBuffer, payload, reject, resolve }: ParamsToUpload) => { - try { - if (!isApiReady) throw new Error('API is not initialized'); - checkWallet(account); - - const { meta, address } = account!; - - const [{ signer }, { programId, codeId }] = await Promise.all([ - web3FromSource(meta.source), - uploadProgram(optBuffer, payload), - ]); - - const { partialFee } = await api.program.paymentInfo(address, { signer }); - - const handleConfirm = () => - signAndUpload({ - method: TransactionName.UploadProgram, - signer, - payload, - programId, - codeId, - reject, - resolve, - }); - - showModal('transaction', { - fee: partialFee.toHuman(), - name: TransactionName.UploadProgram, - addressFrom: address, - onAbort: reject, - onConfirm: handleConfirm, - }); - } catch (error) { - const message = (error as Error).message; - - alert.error(message); - if (reject) reject(); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [api, account, uploadMetadata], - ); - - return { uploadProgram: upload, createProgram: create }; }; export { useProgramActions }; diff --git a/idea/frontend/src/pages/initializeProgram/ui/InitializeProgram.tsx b/idea/frontend/src/pages/initializeProgram/ui/InitializeProgram.tsx index 225a5d4794..3731ee0988 100644 --- a/idea/frontend/src/pages/initializeProgram/ui/InitializeProgram.tsx +++ b/idea/frontend/src/pages/initializeProgram/ui/InitializeProgram.tsx @@ -1,3 +1,4 @@ +import { useApi } from '@gear-js/react-hooks'; import { Button, Input } from '@gear-js/ui'; import { useParams } from 'react-router-dom'; @@ -14,21 +15,26 @@ import { PageParams } from '../model'; import styles from './InitializeProgram.module.scss'; const InitializeProgram = () => { + const { api, isApiReady } = useApi(); const { codeId } = useParams() as PageParams; const { metadata, sails, isLoading, ...contractApi } = useContractApiWithFile(codeId); - const { createProgram } = useProgramActions(); + const createProgram = useProgramActions(); - const handleSubmit = (payload: Payload, helpers: SubmitHelpers) => - createProgram({ - payload, - codeId: codeId, - resolve: () => { - helpers.resetForm(); - helpers.enableButtons(); - metadata.reset(); - }, - reject: helpers.enableButtons, - }); + const handleSubmit = (payload: Payload, helpers: SubmitHelpers) => { + if (!isApiReady) throw new Error('API is not initialized'); + + const { gasLimit, value, initPayload, payloadType, keepAlive } = payload; + const program = { value, codeId, gasLimit, initPayload, keepAlive }; + const result = api.program.create(program, payload.metadata, payloadType); + + const onSuccess = () => { + helpers.resetForm(); + helpers.enableButtons(); + metadata.reset(); + }; + + createProgram(result, codeId, payload, onSuccess, helpers.enableButtons); + }; const renderButtons = ({ isDisabled }: RenderButtonsProps) => ( <> diff --git a/idea/frontend/src/pages/uploadProgram/ui/UploadProgram.tsx b/idea/frontend/src/pages/uploadProgram/ui/UploadProgram.tsx index db9b4c39ad..d445698e0a 100644 --- a/idea/frontend/src/pages/uploadProgram/ui/UploadProgram.tsx +++ b/idea/frontend/src/pages/uploadProgram/ui/UploadProgram.tsx @@ -1,3 +1,4 @@ +import { useApi } from '@gear-js/react-hooks'; import { Button, FileInput } from '@gear-js/ui'; import cx from 'clsx'; @@ -15,9 +16,10 @@ import { UploadMetadata } from '@/features/uploadMetadata'; import styles from './UploadProgram.module.scss'; const UploadProgram = () => { + const { api, isApiReady } = useApi(); const wasmFile = useWasmFile(); const { metadata, sails, isLoading, ...contractApi } = useContractApiWithFile(wasmFile.buffer); - const { uploadProgram } = useProgramActions(); + const uploadProgram = useProgramActions(); const reset = () => { wasmFile.reset(); @@ -37,9 +39,14 @@ const UploadProgram = () => { ); const handleSubmit = (payload: Payload, { enableButtons }: SubmitHelpers) => { - if (!wasmFile.buffer) return; + if (!isApiReady) throw new Error('API is not initialized'); + if (!wasmFile.buffer) throw new Error('File is not found'); - uploadProgram({ optBuffer: wasmFile.buffer, payload, resolve: reset, reject: enableButtons }); + const { gasLimit, value, initPayload, payloadType, keepAlive } = payload; + const program = { code: wasmFile.buffer, value, gasLimit, initPayload, keepAlive }; + const result = api.program.upload(program, payload.metadata, payloadType); + + uploadProgram(result, result.codeId, payload, reset, enableButtons); }; return ( diff --git a/idea/frontend/src/widgets/uploadMetadataModal/ui/UploadMetadataModal.tsx b/idea/frontend/src/widgets/uploadMetadataModal/ui/UploadMetadataModal.tsx index c4ced076a2..88ace3f3ec 100644 --- a/idea/frontend/src/widgets/uploadMetadataModal/ui/UploadMetadataModal.tsx +++ b/idea/frontend/src/widgets/uploadMetadataModal/ui/UploadMetadataModal.tsx @@ -50,7 +50,7 @@ const UploadMetadataModal = ({ codeId, programId, onClose, onSuccess }: Props) = try { if (programId) { - await addProgramName({ name, id: programId }, isDevChain); + if (!isDevChain) await addProgramName({ name, id: programId }); } else { await addCodeName({ name, id: codeId }); }