// Copyright 2017-2025 @pezkuwi/app-contracts authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { SubmittableExtrinsic } from '@pezkuwi/api/types'; import type { CodeSubmittableResult } from '@pezkuwi/api-contract/promise/types'; import type { BN } from '@pezkuwi/util'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { CodePromise } from '@pezkuwi/api-contract'; import { Button, Dropdown, InputAddress, InputBalance, InputFile, MarkError, Modal, TxButton } from '@pezkuwi/react-components'; import { useAccountId, useApi, useFormField, useNonEmptyString, useStepper } from '@pezkuwi/react-hooks'; import { Available } from '@pezkuwi/react-query'; import { keyring } from '@pezkuwi/ui-keyring'; import { BN_ZERO, isNull, isWasm, stringify } from '@pezkuwi/util'; import { ABI, InputMegaGas, InputName, MessageSignature, Params } from '../shared/index.js'; import store from '../store.js'; import { useTranslation } from '../translate.js'; import useAbi from '../useAbi.js'; import useWeight from '../useWeight.js'; interface Props { onClose: () => void; } function Upload ({ onClose }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const [accountId, setAccountId] = useAccountId(); const [step, nextStep, prevStep] = useStepper(); const [[uploadTx, error], setUploadTx] = useState<[SubmittableExtrinsic<'promise'> | null, string | null]>([null, null]); const [constructorIndex, setConstructorIndex] = useState(0); const [value, isValueValid, setValue] = useFormField(BN_ZERO); const [params, setParams] = useState([]); const [[wasm, isWasmValid], setWasm] = useState<[Uint8Array | null, boolean]>([null, false]); const [name, isNameValid, setName] = useNonEmptyString(); const { abiName, contractAbi, errorText, isAbiError, isAbiSupplied, isAbiValid, onChangeAbi, onRemoveAbi } = useAbi(); const weight = useWeight(); const code = useMemo( () => isAbiValid && isWasmValid && wasm && contractAbi ? new CodePromise(api, contractAbi, wasm) : null, [api, contractAbi, isAbiValid, isWasmValid, wasm] ); const constructOptions = useMemo( () => contractAbi ? contractAbi.constructors.map((c, index) => ({ info: c.identifier, key: c.identifier, text: ( ), value: index })) : [], [contractAbi] ); useEffect((): void => { setConstructorIndex(0); }, [constructOptions]); useEffect((): void => { setParams([]); }, [contractAbi, constructorIndex]); useEffect((): void => { setWasm( contractAbi && isWasm(contractAbi.info.source.wasm) ? [contractAbi.info.source.wasm, true] : [null, false] ); }, [contractAbi]); useEffect((): void => { abiName && setName(abiName); }, [abiName, setName]); useEffect((): void => { async function dryRun () { let contract: SubmittableExtrinsic<'promise'> | null = null; let error: string | null = null; try { if (code && contractAbi?.constructors[constructorIndex]?.method && value && accountId) { const dryRunParams: Parameters = [ accountId, contractAbi?.constructors[constructorIndex].isPayable ? api.registry.createType('Balance', value) : api.registry.createType('Balance', BN_ZERO), weight.weightV2, null, { Upload: api.registry.createType('Raw', wasm) }, contractAbi?.constructors[constructorIndex]?.toU8a(params), '' ]; const dryRunResult = await api.call.contractsApi.instantiate(...dryRunParams); contract = code.tx[contractAbi.constructors[constructorIndex].method]({ gasLimit: dryRunResult.gasRequired, storageDepositLimit: dryRunResult.storageDeposit.isCharge ? dryRunResult.storageDeposit.asCharge : null, value: contractAbi?.constructors[constructorIndex].isPayable ? value : undefined }, ...params); } } catch (e) { error = (e as Error).message; } setUploadTx(() => [contract, error]); } dryRun().catch((e) => console.error(e)); }, [accountId, wasm, api, code, contractAbi, constructorIndex, value, params, weight]); const _onAddWasm = useCallback( (wasm: Uint8Array, name: string): void => { setWasm([wasm, isWasm(wasm)]); setName(name.replace('.wasm', '').replace('_', ' ')); }, [setName] ); const _onSuccess = useCallback( (result: CodeSubmittableResult): void => { result.blueprint && store.saveCode(result.blueprint.codeHash, { abi: stringify(result.blueprint.abi.json), name: name || '<>', tags: [] }); result.contract && keyring.saveContract(result.contract.address.toString(), { contract: { abi: stringify(result.contract.abi.json), genesisHash: api.genesisHash.toHex() }, name: name || '<>', tags: [] }); }, [api, name] ); const isSubmittable = !!accountId && (!isNull(name) && isNameValid) && isWasmValid && isAbiSupplied && isAbiValid && !!uploadTx && step === 2; const invalidAbi = isAbiError || !isAbiSupplied; return ( {step === 1 && ( <> } onChange={setAccountId} type='account' value={accountId} /> {!invalidAbi && contractAbi && ( <> {!contractAbi.info.source.wasm.length && ( )} )} )} {step === 2 && contractAbi && ( <> {contractAbi.constructors[constructorIndex].isPayable && ( ) } {error && ( )} )} {step === 1 ? (