Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions packages/page-contracts/src/Codes/Upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<number>(0);
const [endowment, isEndowmentValid, setEndowment] = useNonZeroBn(ENDOWMENT);
const [value, isValueValid, setValue] = useFormField<BN>(BN_ZERO);
const [params, setParams] = useState<unknown[]>([]);
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)
Expand Down Expand Up @@ -87,18 +88,19 @@ 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) {
error = (e as Error).message;
}

setUploadTx(() => [contract, error]);
}, [code, contractAbi, constructorIndex, endowment, params, weight]);
}, [code, contractAbi, constructorIndex, value, params, weight]);

const _onAddWasm = useCallback(
(wasm: Uint8Array, name: string): void => {
Expand Down Expand Up @@ -199,14 +201,15 @@ function Upload ({ onClose }: Props): React.ReactElement {
registry={contractAbi.registry}
/>
<InputBalance
help={t<string>('The allotted endowment for the deployed contract, i.e. the amount transferred to the contract upon instantiation.')}
isError={!isEndowmentValid}
label={t<string>('endowment')}
onChange={setEndowment}
value={endowment}
help={t<string>('The balance to transfer from the `origin` to the newly created contract.')}
isError={!isValueValid}
isZeroable={hasStorageDeposit}
label={t<string>('value')}
onChange={setValue}
value={value}
/>
<InputMegaGas
help={t<string>('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.')}
help={t<string>('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail')}
weight={weight}
/>
{error && (
Expand Down
68 changes: 29 additions & 39 deletions packages/page-contracts/src/Contracts/Call.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -34,17 +34,19 @@ 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<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const message = contract.abi.messages[messageIndex];
const [accountId, setAccountId] = useAccountId();
const [estimatedWeight, setEstimatedWeight] = useState<BN | null>(null);
const [value, isValueValid, setEndowment] = useFormField<BN>(BN_ZERO);
const [value, isValueValid, setValue] = useFormField<BN>(BN_ZERO);
const [outcomes, setOutcomes] = useState<CallResult[]>([]);
const [execTx, setExecTx] = useState<SubmittableExtrinsic<'promise'> | null>(null);
const [params, setParams] = useState<unknown[]>([]);
const [isViaCall, toggleViaCall] = useToggle();
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);
Expand All @@ -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(
Expand Down Expand Up @@ -180,7 +170,7 @@ function Call ({ className = '', contract, messageIndex, onCallResult, onChangeM
isError={!isValueValid}
isZeroable
label={t<string>('value')}
onChange={setEndowment}
onChange={setValue}
value={value}
/>
)}
Expand Down
33 changes: 18 additions & 15 deletions packages/page-contracts/src/Contracts/Deploy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -31,13 +30,15 @@ interface Props {
function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const weight = useWeight();
const [initTx, setInitTx] = useState<SubmittableExtrinsic<'promise'> | null>(null);
const [params, setParams] = useState<unknown[]>([]);
const [accountId, isAccountIdValid, setAccountId] = useFormField<string | null>(null);
const [endowment, isEndowmentValid, setEndowment] = useNonZeroBn(ENDOWMENT);
const [value, isValueValid, setValue] = useFormField<BN>(BN_ZERO);
const [params, setParams] = useState<unknown[]>([]);
const [salt, setSalt] = useState<string>(() => randomAsHex());
const [withSalt, setWithSalt] = useState(false);
const weight = useWeight();

const hasStorageDeposit = api.tx.contracts.instantiate.meta.args.length === 6;

useEffect((): void => {
setParams([]);
Expand Down Expand Up @@ -76,15 +77,16 @@ 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]({
gasLimit: weight.weight,
salt: withSalt
? salt
: null,
value: endowment
storageDepositLimit: null,
value
}, ...params);
} catch (error) {
return null;
Expand All @@ -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 => {
Expand All @@ -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 (
<Modal
Expand Down Expand Up @@ -171,11 +173,12 @@ function Deploy ({ codeHash, constructorIndex = 0, onClose, setConstructorIndex
</>
)}
<InputBalance
help={t<string>('The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.')}
isError={!isEndowmentValid}
label={t<string>('endowment')}
onChange={setEndowment}
value={endowment}
help={t<string>('The balance to transfer from the `origin` to the newly created contract.')}
isError={!isValueValid}
isZeroable={hasStorageDeposit}
label={t<string>('value')}
onChange={setValue}
value={value}
/>
<Input
help={t<string>('A hex or string value that acts as a salt for this deployment.')}
Expand Down