diff --git a/packages/page-contracts/src/Codes/Upload.tsx b/packages/page-contracts/src/Codes/Upload.tsx index 65bca87a88f3..4c822cba521d 100644 --- a/packages/page-contracts/src/Codes/Upload.tsx +++ b/packages/page-contracts/src/Codes/Upload.tsx @@ -8,12 +8,11 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { CodePromise } from '@polkadot/api-contract'; import { Button, Dropdown, InputAddress, InputBalance, InputFile, MarkError, Modal, TxButton } from '@polkadot/react-components'; -import { useAccountId, useApi, useNonEmptyString, useNonZeroBn, useStepper } from '@polkadot/react-hooks'; +import { useAccountId, useApi, useFormField, useNonEmptyString, useStepper } from '@polkadot/react-hooks'; import { Available } from '@polkadot/react-query'; import { keyring } from '@polkadot/ui-keyring'; -import { isNull, isWasm, stringify } from '@polkadot/util'; +import { BN, BN_ZERO, isNull, isWasm, stringify } from '@polkadot/util'; -import { ENDOWMENT } from '../constants'; import { ABI, InputMegaGas, InputName, MessageSignature, Params } from '../shared'; import store from '../store'; import { useTranslation } from '../translate'; @@ -31,13 +30,15 @@ function Upload ({ onClose }: Props): React.ReactElement { const [step, nextStep, prevStep] = useStepper(); const [[uploadTx, error], setUploadTx] = useState<[SubmittableExtrinsic<'promise'> | null, string | null]>([null, null]); const [constructorIndex, setConstructorIndex] = useState(0); - const [endowment, isEndowmentValid, setEndowment] = useNonZeroBn(ENDOWMENT); + 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 hasStorageDeposit = api.tx.contracts.instantiate.meta.args.length === 6; + const code = useMemo( () => isAbiValid && isWasmValid && wasm && contractAbi ? new CodePromise(api, contractAbi, wasm) @@ -87,10 +88,11 @@ function Upload ({ onClose }: Props): React.ReactElement { let error: string | null = null; try { - contract = code && contractAbi?.constructors[constructorIndex]?.method && endowment + contract = code && contractAbi?.constructors[constructorIndex]?.method && value ? code.tx[contractAbi.constructors[constructorIndex].method]({ gasLimit: weight.weight, - value: endowment + storageDepositLimit: null, + value }, ...params) : null; } catch (e) { @@ -98,7 +100,7 @@ function Upload ({ onClose }: Props): React.ReactElement { } setUploadTx(() => [contract, error]); - }, [code, contractAbi, constructorIndex, endowment, params, weight]); + }, [code, contractAbi, constructorIndex, value, params, weight]); const _onAddWasm = useCallback( (wasm: Uint8Array, name: string): void => { @@ -199,14 +201,15 @@ function Upload ({ onClose }: Props): React.ReactElement { registry={contractAbi.registry} /> ('The allotted endowment for the deployed contract, i.e. the amount transferred to the contract upon instantiation.')} - isError={!isEndowmentValid} - label={t('endowment')} - onChange={setEndowment} - value={endowment} + help={t('The balance to transfer from the `origin` to the newly created contract.')} + isError={!isValueValid} + isZeroable={hasStorageDeposit} + label={t('value')} + onChange={setValue} + value={value} /> ('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.')} + help={t('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail')} weight={weight} /> {error && ( diff --git a/packages/page-contracts/src/Contracts/Call.tsx b/packages/page-contracts/src/Contracts/Call.tsx index 3c0b7ce4f82e..e8aa07a90782 100644 --- a/packages/page-contracts/src/Contracts/Call.tsx +++ b/packages/page-contracts/src/Contracts/Call.tsx @@ -11,7 +11,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; import { Button, Dropdown, Expander, InputAddress, InputBalance, Modal, Toggle, TxButton } from '@polkadot/react-components'; -import { useAccountId, useDebounce, useFormField, useToggle } from '@polkadot/react-hooks'; +import { useAccountId, useApi, useDebounce, useFormField, useToggle } from '@polkadot/react-hooks'; import { Available } from '@polkadot/react-query'; import { BN_ONE, BN_ZERO } from '@polkadot/util'; @@ -34,10 +34,11 @@ const MAX_CALL_WEIGHT = new BN(5_000_000_000_000).isub(BN_ONE); function Call ({ className = '', contract, messageIndex, onCallResult, onChangeMessage, onClose }: Props): React.ReactElement | null { const { t } = useTranslation(); + const { api } = useApi(); const message = contract.abi.messages[messageIndex]; const [accountId, setAccountId] = useAccountId(); const [estimatedWeight, setEstimatedWeight] = useState(null); - const [value, isValueValid, setEndowment] = useFormField(BN_ZERO); + const [value, isValueValid, setValue] = useFormField(BN_ZERO); const [outcomes, setOutcomes] = useState([]); const [execTx, setExecTx] = useState | null>(null); const [params, setParams] = useState([]); @@ -45,6 +46,7 @@ function Call ({ className = '', contract, messageIndex, onCallResult, onChangeM const weight = useWeight(); const dbValue = useDebounce(value); const dbParams = useDebounce(params); + const hasStorageDeposit = api.tx.contracts.call.meta.args.length === 5; useEffect((): void => { setEstimatedWeight(null); @@ -54,65 +56,53 @@ function Call ({ className = '', contract, messageIndex, onCallResult, onChangeM useEffect((): void => { value && message.isMutating && setExecTx((): SubmittableExtrinsic<'promise'> | null => { try { - return contract.tx[message.method]({ - gasLimit: weight.weight, - value: message.isPayable - ? value - : 0 - }, ...params); + return hasStorageDeposit + ? contract.tx[message.method]({ gasLimit: weight.weight, storageDepositLimit: null, value: message.isPayable ? value : 0 }, ...params) + : contract.tx[message.method]({ gasLimit: weight.weight, value: message.isPayable ? value : 0 }, ...params); } catch (error) { return null; } }); - }, [accountId, contract, message, value, weight, params]); + }, [accountId, contract, message, value, weight, params, hasStorageDeposit]); useEffect((): void => { if (!accountId || !message || !dbParams || !dbValue) return; + const query = hasStorageDeposit + ? contract.query[message.method](accountId, { gasLimit: -1, storageDepositLimit: null, value: message.isPayable ? dbValue : 0 }, ...dbParams) + : contract.query[message.method](accountId, { gasLimit: -1, value: message.isPayable ? dbValue : 0 }, ...dbParams); - contract - .query[message.method](accountId, { - gasLimit: -1, - value: message.isPayable - ? dbValue - : 0 - }, ...dbParams) + query .then(({ gasRequired, result }) => setEstimatedWeight( result.isOk ? gasRequired : null )) .catch(() => setEstimatedWeight(null)); - }, [accountId, contract, message, dbParams, dbValue]); + }, [accountId, contract, message, dbParams, dbValue, hasStorageDeposit]); const _onSubmitRpc = useCallback( (): void => { if (!accountId || !message || !value || !weight) return; - - contract - .query[message.method](accountId, { - gasLimit: weight.isEmpty - ? -1 - : weight.weight, - value: message.isPayable - ? value - : 0 - }, ...params) - .then((result): void => { - setOutcomes([{ - ...result, - from: accountId, - message, - params, - when: new Date() - }, ...outcomes]); - onCallResult && onCallResult(messageIndex, result); - }) + const query = hasStorageDeposit + ? contract.query[message.method](accountId, { gasLimit: weight.isEmpty ? -1 : weight.weight, storageDepositLimit: null, value: message.isPayable ? value : 0 }, ...params) + : contract.query[message.method](accountId, { gasLimit: weight.isEmpty ? -1 : weight.weight, value: message.isPayable ? value : 0 }, ...params); + + query.then((result): void => { + setOutcomes([{ + ...result, + from: accountId, + message, + params, + when: new Date() + }, ...outcomes]); + onCallResult && onCallResult(messageIndex, result); + }) .catch((error): void => { console.error(error); onCallResult && onCallResult(messageIndex); }); }, - [accountId, contract, message, messageIndex, onCallResult, outcomes, params, value, weight] + [accountId, contract.query, hasStorageDeposit, message, messageIndex, onCallResult, outcomes, params, value, weight] ); const _onClearOutcome = useCallback( @@ -180,7 +170,7 @@ function Call ({ className = '', contract, messageIndex, onCallResult, onChangeM isError={!isValueValid} isZeroable label={t('value')} - onChange={setEndowment} + onChange={setValue} value={value} /> )} diff --git a/packages/page-contracts/src/Contracts/Deploy.tsx b/packages/page-contracts/src/Contracts/Deploy.tsx index ad7a94136c05..8f675b36f132 100644 --- a/packages/page-contracts/src/Contracts/Deploy.tsx +++ b/packages/page-contracts/src/Contracts/Deploy.tsx @@ -8,13 +8,12 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { BlueprintPromise } from '@polkadot/api-contract'; import { Dropdown, Input, InputAddress, InputBalance, Modal, Toggle, TxButton } from '@polkadot/react-components'; -import { useApi, useFormField, useNonEmptyString, useNonZeroBn } from '@polkadot/react-hooks'; +import { useApi, useFormField, useNonEmptyString } from '@polkadot/react-hooks'; import { Available } from '@polkadot/react-query'; import { keyring } from '@polkadot/ui-keyring'; -import { isHex, stringify } from '@polkadot/util'; +import { BN, BN_ZERO, isHex, stringify } from '@polkadot/util'; import { randomAsHex } from '@polkadot/util-crypto'; -import { ENDOWMENT } from '../constants'; import { ABI, InputMegaGas, InputName, MessageSignature, Params } from '../shared'; import store from '../store'; import { useTranslation } from '../translate'; @@ -31,13 +30,15 @@ interface Props { function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); - const weight = useWeight(); const [initTx, setInitTx] = useState | null>(null); - const [params, setParams] = useState([]); const [accountId, isAccountIdValid, setAccountId] = useFormField(null); - const [endowment, isEndowmentValid, setEndowment] = useNonZeroBn(ENDOWMENT); + const [value, isValueValid, setValue] = useFormField(BN_ZERO); + const [params, setParams] = useState([]); const [salt, setSalt] = useState(() => randomAsHex()); const [withSalt, setWithSalt] = useState(false); + const weight = useWeight(); + + const hasStorageDeposit = api.tx.contracts.instantiate.meta.args.length === 6; useEffect((): void => { setParams([]); @@ -76,7 +77,7 @@ function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex ); useEffect((): void => { - endowment && setInitTx((): SubmittableExtrinsic<'promise'> | null => { + value && setInitTx((): SubmittableExtrinsic<'promise'> | null => { if (blueprint && contractAbi?.constructors[constructorIndex]?.method) { try { return blueprint.tx[contractAbi.constructors[constructorIndex].method]({ @@ -84,7 +85,8 @@ function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex salt: withSalt ? salt : null, - value: endowment + storageDepositLimit: null, + value }, ...params); } catch (error) { return null; @@ -93,7 +95,7 @@ function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex return null; }); - }, [blueprint, contractAbi, constructorIndex, endowment, params, salt, weight, withSalt]); + }, [blueprint, contractAbi, constructorIndex, value, params, salt, weight, withSalt]); const _onSuccess = useCallback( (result: BlueprintSubmittableResult): void => { @@ -114,7 +116,7 @@ function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex ); const isSaltValid = !withSalt || (salt && (!salt.startsWith('0x') || isHex(salt))); - const isValid = isNameValid && isEndowmentValid && weight.isValid && isAccountIdValid && isSaltValid; + const isValid = isNameValid && isValueValid && weight.isValid && isAccountIdValid && isSaltValid; return ( )} ('The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.')} - isError={!isEndowmentValid} - label={t('endowment')} - onChange={setEndowment} - value={endowment} + help={t('The balance to transfer from the `origin` to the newly created contract.')} + isError={!isValueValid} + isZeroable={hasStorageDeposit} + label={t('value')} + onChange={setValue} + value={value} /> ('A hex or string value that acts as a salt for this deployment.')}