Support contract bundles (#4019)
* Bump API * Rebuild i18n * Fix InputAddress * Pass through registry where applicable * Support contract bundles
This commit is contained in:
parent
9fef98a300
commit
3000f5b7a4
@ -27,7 +27,7 @@ function Upload ({ onClose }: Props): React.ReactElement {
|
||||
const [uploadTx, setUploadTx] = useState<SubmittableExtrinsic<'promise'> | null>(null);
|
||||
const [[wasm, isWasmValid], setWasm] = useState<[Uint8Array | null, boolean]>([null, false]);
|
||||
const [name, isNameValid, setName] = useNonEmptyString();
|
||||
const { contractAbi, errorText, isAbiError, isAbiSupplied, isAbiValid, onChangeAbi, onRemoveAbi } = useAbi();
|
||||
const { abiName, contractAbi, errorText, isAbiError, isAbiSupplied, isAbiValid, onChangeAbi, onRemoveAbi } = useAbi();
|
||||
|
||||
const code = useMemo(
|
||||
() => isAbiValid && isWasmValid && wasm && contractAbi
|
||||
@ -40,6 +40,18 @@ function Upload ({ onClose }: Props): React.ReactElement {
|
||||
setUploadTx(() => code ? code.createBlueprint() : null);
|
||||
}, [code]);
|
||||
|
||||
useEffect((): void => {
|
||||
setWasm(
|
||||
contractAbi && isWasm(contractAbi.project.source.wasm)
|
||||
? [contractAbi.project.source.wasm, true]
|
||||
: [null, false]
|
||||
);
|
||||
}, [contractAbi]);
|
||||
|
||||
useEffect((): void => {
|
||||
abiName && setName(abiName);
|
||||
}, [abiName, setName]);
|
||||
|
||||
const _onAddWasm = useCallback(
|
||||
(wasm: Uint8Array, name: string): void => {
|
||||
setWasm([wasm, isWasm(wasm)]);
|
||||
@ -62,6 +74,7 @@ function Upload ({ onClose }: Props): React.ReactElement {
|
||||
);
|
||||
|
||||
const isSubmittable = !!accountId && (!isNull(name) && isNameValid) && isWasmValid && isAbiSupplied && isAbiValid && !!uploadTx;
|
||||
const invalidAbi = isAbiError || !isAbiSupplied;
|
||||
|
||||
return (
|
||||
<Modal header={t('Upload WASM')}>
|
||||
@ -80,31 +93,39 @@ function Upload ({ onClose }: Props): React.ReactElement {
|
||||
type='account'
|
||||
value={accountId}
|
||||
/>
|
||||
<InputFile
|
||||
help={t<string>('The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.')}
|
||||
isError={!isWasmValid}
|
||||
label={t<string>('compiled contract WASM')}
|
||||
onChange={_onAddWasm}
|
||||
placeholder={
|
||||
wasm && !isWasmValid
|
||||
? t<string>('The code is not recognized as being in valid WASM format')
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<InputName
|
||||
isError={!isNameValid}
|
||||
onChange={setName}
|
||||
value={name || undefined}
|
||||
/>
|
||||
<ABI
|
||||
contractAbi={contractAbi}
|
||||
errorText={errorText}
|
||||
isError={isAbiError || !isAbiSupplied}
|
||||
isError={invalidAbi}
|
||||
isSupplied={isAbiSupplied}
|
||||
isValid={isAbiValid}
|
||||
label={t<string>('json for either ABI or .contract bundle')}
|
||||
onChange={onChangeAbi}
|
||||
onRemove={onRemoveAbi}
|
||||
withWasm
|
||||
/>
|
||||
{!invalidAbi && contractAbi && (
|
||||
<>
|
||||
{!contractAbi.project.source.wasm.length && (
|
||||
<InputFile
|
||||
help={t<string>('The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.')}
|
||||
isError={!isWasmValid}
|
||||
label={t<string>('compiled contract WASM')}
|
||||
onChange={_onAddWasm}
|
||||
placeholder={
|
||||
wasm && !isWasmValid
|
||||
? t<string>('The code is not recognized as being in valid WASM format')
|
||||
: null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<InputName
|
||||
isError={!isNameValid}
|
||||
onChange={setName}
|
||||
value={name || undefined}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Modal.Content>
|
||||
<Modal.Actions onCancel={onClose}>
|
||||
<TxButton
|
||||
|
||||
@ -19,7 +19,7 @@ interface Props {
|
||||
isValid?: boolean;
|
||||
isSupplied?: boolean;
|
||||
label?: React.ReactNode;
|
||||
onChange: (u8a: Uint8Array) => void;
|
||||
onChange: (u8a: Uint8Array, name: string) => void;
|
||||
onRemove?: () => void;
|
||||
onRemoved?: () => void;
|
||||
onSelect?: () => void;
|
||||
@ -27,11 +27,12 @@ interface Props {
|
||||
withConstructors?: boolean;
|
||||
withLabel?: boolean;
|
||||
withMessages?: boolean;
|
||||
withWasm?: boolean;
|
||||
}
|
||||
|
||||
const NOOP = (): void => undefined;
|
||||
|
||||
function ABI ({ className, contractAbi, errorText, isDisabled, isError, isFull, isValid, onChange, onRemove = NOOP, onSelectConstructor, withConstructors = true, withLabel = true, withMessages = true }: Props): React.ReactElement<Props> {
|
||||
function ABI ({ className, contractAbi, errorText, isDisabled, isError, isFull, isValid, label, onChange, onRemove = NOOP, onSelectConstructor, withConstructors = true, withLabel = true, withMessages = true, withWasm }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (contractAbi && isValid)
|
||||
@ -39,7 +40,7 @@ function ABI ({ className, contractAbi, errorText, isDisabled, isError, isFull,
|
||||
<Labelled
|
||||
className={className}
|
||||
help={t<string>('This is the ABI as supplied. Any calls to the contract will use this information for encoding.')}
|
||||
label={t<string>('contract ABI')}
|
||||
label={label || t<string>('contract ABI')}
|
||||
labelExtra={onRemove && (
|
||||
<IconLink
|
||||
icon='trash'
|
||||
@ -55,6 +56,7 @@ function ABI ({ className, contractAbi, errorText, isDisabled, isError, isFull,
|
||||
onSelectConstructor={onSelectConstructor}
|
||||
withConstructors={withConstructors}
|
||||
withMessages={withMessages}
|
||||
withWasm={withWasm}
|
||||
/>
|
||||
</Labelled>
|
||||
)
|
||||
@ -65,9 +67,9 @@ function ABI ({ className, contractAbi, errorText, isDisabled, isError, isFull,
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
isFull={isFull}
|
||||
label={t<string>('contract ABI')}
|
||||
label={label || t<string>('contract ABI')}
|
||||
onChange={onChange}
|
||||
placeholder={errorText || t<string>('click to select or drag and drop a JSON ABI file')}
|
||||
placeholder={errorText || t<string>('click to select or drag and drop a JSON file')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -11,6 +11,7 @@ import { Abi, ContractPromise } from '@polkadot/api-contract';
|
||||
import { Expander } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import Message from './Message';
|
||||
import { useTranslation } from '../translate';
|
||||
@ -25,6 +26,7 @@ export interface Props {
|
||||
onSelectConstructor?: (constructorIndex: number) => void;
|
||||
withConstructors?: boolean;
|
||||
withMessages?: boolean;
|
||||
withWasm?: boolean;
|
||||
}
|
||||
|
||||
const READ_ADDR = '0x'.padEnd(66, '0');
|
||||
@ -41,7 +43,7 @@ function sortMessages (messages: AbiMessage[]): [AbiMessage, number][] {
|
||||
);
|
||||
}
|
||||
|
||||
function Messages ({ className = '', contract, contractAbi: { constructors, messages }, isLabelled, isWatching, onSelect, onSelectConstructor, withConstructors, withMessages } : Props): React.ReactElement<Props> {
|
||||
function Messages ({ className = '', contract, contractAbi: { constructors, messages, project: { source } }, isLabelled, isWatching, onSelect, onSelectConstructor, withConstructors, withMessages, withWasm } : Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const optInfo = useCall<Option<ContractInfo>>(contract && api.query.contracts.contractInfoOf, [contract?.address]);
|
||||
@ -110,6 +112,9 @@ function Messages ({ className = '', contract, contractAbi: { constructors, mess
|
||||
))}
|
||||
</Expander>
|
||||
)}
|
||||
{withWasm && source.wasm.length !== 0 && (
|
||||
<div>{t<string>('{{size}} WASM bytes', { replace: { size: formatNumber(source.wasm.length) } })}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,38 +12,39 @@ import store from './store';
|
||||
|
||||
interface UseAbi {
|
||||
abi: string | null;
|
||||
abiName: string | null;
|
||||
contractAbi: Abi | null;
|
||||
errorText: string | null;
|
||||
isAbiError: boolean;
|
||||
isAbiValid: boolean;
|
||||
isAbiSupplied: boolean;
|
||||
onChangeAbi: (u8a: Uint8Array) => void;
|
||||
onChangeAbi: (u8a: Uint8Array, name: string) => void;
|
||||
onRemoveAbi: () => void;
|
||||
}
|
||||
|
||||
export default function useAbi (initialValue: [string | null | undefined, Abi | null | undefined] = [null, null], codeHash: StringOrNull = null, isRequired = false): UseAbi {
|
||||
const [[abi, contractAbi, isAbiSupplied, isAbiValid], setAbi] = useState<[string | null | undefined, Abi | null | undefined, boolean, boolean]>([initialValue[0], initialValue[1], !!initialValue[1], !isRequired || !!initialValue[1]]);
|
||||
const [[abi, contractAbi, isAbiSupplied, isAbiValid, abiName], setAbi] = useState<[string | null | undefined, Abi | null | undefined, boolean, boolean, string | null]>([initialValue[0], initialValue[1], !!initialValue[1], !isRequired || !!initialValue[1], null]);
|
||||
const [[isAbiError, errorText], setError] = useState<[boolean, string | null]>([false, null]);
|
||||
|
||||
useEffect(
|
||||
(): void => {
|
||||
initialValue[0] && abi !== initialValue[0] && setAbi([initialValue[0], initialValue[1], !!initialValue[1], !isRequired || !!initialValue[1]]);
|
||||
initialValue[0] && abi !== initialValue[0] && setAbi([initialValue[0], initialValue[1], !!initialValue[1], !isRequired || !!initialValue[1], null]);
|
||||
},
|
||||
[abi, initialValue, isRequired]
|
||||
);
|
||||
|
||||
const onChangeAbi = useCallback(
|
||||
(u8a: Uint8Array): void => {
|
||||
(u8a: Uint8Array, name: string): void => {
|
||||
const json = u8aToString(u8a);
|
||||
|
||||
try {
|
||||
setAbi([json, new Abi(json, api.registry.getChainProperties()), true, true]);
|
||||
setAbi([json, new Abi(json, api.registry.getChainProperties()), true, true, name.replace('.contract', '').replace('.json', '').replace('_', ' ')]);
|
||||
|
||||
codeHash && store.saveCode(codeHash, { abi: json });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
setAbi([null, null, false, false]);
|
||||
setAbi([null, null, false, false, null]);
|
||||
setError([true, (error as Error).message]);
|
||||
}
|
||||
},
|
||||
@ -52,7 +53,7 @@ export default function useAbi (initialValue: [string | null | undefined, Abi |
|
||||
|
||||
const onRemoveAbi = useCallback(
|
||||
(): void => {
|
||||
setAbi([null, null, false, false]);
|
||||
setAbi([null, null, false, false, null]);
|
||||
setError([false, null]);
|
||||
|
||||
codeHash && store.saveCode(codeHash, { abi: null });
|
||||
@ -62,6 +63,7 @@ export default function useAbi (initialValue: [string | null | undefined, Abi |
|
||||
|
||||
return {
|
||||
abi: abi || null,
|
||||
abiName,
|
||||
contractAbi: contractAbi || null,
|
||||
errorText,
|
||||
isAbiError,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user