UI support for v2 ABI (#1602)
* ui support for v2 ABI * drop v1 support * update ABI display * changes * buttons * update ABI display * ABI v1 error message * fix key warning * Bump deps * Bump deps * Updates for merge * default gas and endowment
This commit is contained in:
parent
161eed0e60
commit
81c925f3d7
@ -1,4 +1,4 @@
|
||||
name: CI
|
||||
name: PR
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
24
.github/workflows/push-master.yml
vendored
Normal file
24
.github/workflows/push-master.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: Master
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
merge:
|
||||
name: Merge
|
||||
if: "! contains(github.event.head_commit.message, '[CI Skip]')"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: build
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn build
|
||||
12
package.json
12
package.json
@ -10,10 +10,10 @@
|
||||
"packages/*"
|
||||
],
|
||||
"resolutions": {
|
||||
"@polkadot/api": "^0.95.0-beta.0",
|
||||
"@polkadot/api-contract": "^0.95.0-beta.0",
|
||||
"@polkadot/api": "^0.95.0-beta.2",
|
||||
"@polkadot/api-contract": "^0.95.0-beta.2",
|
||||
"@polkadot/keyring": "^1.5.1",
|
||||
"@polkadot/types": "^0.95.0-beta.0",
|
||||
"@polkadot/types": "^0.95.0-beta.2",
|
||||
"@polkadot/util": "^1.5.1",
|
||||
"@polkadot/util-crypto": "^1.5.1",
|
||||
"@types/styled-components": "4.1.8",
|
||||
@ -34,9 +34,9 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.2",
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/dev-react": "^0.32.0-beta.3",
|
||||
"@polkadot/ts": "^0.1.77",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"@polkadot/dev-react": "^0.32.0-beta.5",
|
||||
"@polkadot/ts": "^0.1.79",
|
||||
"autoprefixer": "^9.6.4",
|
||||
"empty": "^0.10.1",
|
||||
"gh-pages": "^2.1.1",
|
||||
"html-loader": "^0.5.5",
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/react-components": "^0.36.0-beta.68",
|
||||
"@polkadot/react-qr": "^0.46.0-beta.5",
|
||||
"@polkadot/react-qr": "^0.46.0-beta.6",
|
||||
"@types/file-saver": "^2.0.0",
|
||||
"@types/yargs": "^13.0.2",
|
||||
"babel-plugin-module-resolver": "^3.1.1",
|
||||
"detect-browser": "^4.7.0",
|
||||
"file-saver": "^2.0.0",
|
||||
"yargs": "^14.0.0"
|
||||
"yargs": "^14.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/api-contract": "^0.95.0-beta.0",
|
||||
"@polkadot/api-contract": "^0.95.0-beta.2",
|
||||
"@polkadot/react-components": "^0.36.0-beta.68"
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import translate from './translate';
|
||||
interface Props extends I18nProps {
|
||||
className?: string;
|
||||
contractAbi?: Abi | null;
|
||||
errorText?: string | null;
|
||||
help?: React.ReactNode;
|
||||
isError?: boolean;
|
||||
isDisabled?: boolean;
|
||||
@ -24,10 +25,12 @@ interface Props extends I18nProps {
|
||||
onRemove?: () => void;
|
||||
onRemoved?: () => void;
|
||||
onSelect?: () => void;
|
||||
onSelectConstructor?: (constructorIndex?: number) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
contractAbi: Abi | null;
|
||||
errorText: string | null;
|
||||
isAbiValid: boolean;
|
||||
isEmpty: boolean;
|
||||
isError: boolean;
|
||||
@ -36,6 +39,7 @@ interface State {
|
||||
class ABI extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
contractAbi: null,
|
||||
errorText: null,
|
||||
isAbiValid: false,
|
||||
isEmpty: true,
|
||||
isError: false
|
||||
@ -49,6 +53,7 @@ class ABI extends React.PureComponent<Props, State> {
|
||||
|
||||
this.state = {
|
||||
contractAbi: contractAbi || null,
|
||||
errorText: null,
|
||||
isAbiValid,
|
||||
isEmpty: !isAbiValid,
|
||||
isError: isError || (isRequired && !isAbiValid) || false
|
||||
@ -83,7 +88,7 @@ class ABI extends React.PureComponent<Props, State> {
|
||||
|
||||
private renderInputFile (): React.ReactNode {
|
||||
const { className, help, isDisabled, isRequired, label, t } = this.props;
|
||||
const { isAbiValid, isEmpty, isError } = this.state;
|
||||
const { isAbiValid, isEmpty, isError, errorText } = this.state;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
@ -95,7 +100,17 @@ class ABI extends React.PureComponent<Props, State> {
|
||||
onChange={this.onChange}
|
||||
placeholder={
|
||||
!isEmpty && !isAbiValid
|
||||
? t('invalid ABI file selected')
|
||||
? (
|
||||
<>
|
||||
{t('invalid ABI file selected')}
|
||||
{!!errorText && (
|
||||
<>
|
||||
{' — '}
|
||||
{t(errorText)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
: t('click to select or drag and drop a JSON ABI file')
|
||||
}
|
||||
/>
|
||||
@ -104,7 +119,7 @@ class ABI extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
private renderMessages (): React.ReactNode {
|
||||
const { help, isDisabled, label, onRemove } = this.props;
|
||||
const { help, isDisabled, label, onRemove, onSelectConstructor } = this.props;
|
||||
const { contractAbi } = this.state;
|
||||
|
||||
if (!contractAbi) {
|
||||
@ -122,17 +137,24 @@ class ABI extends React.PureComponent<Props, State> {
|
||||
onRemove={onRemove || this.onRemove}
|
||||
isLabelled={!!label}
|
||||
isRemovable={!isDisabled}
|
||||
onSelectConstructor={onSelectConstructor}
|
||||
withConstructors
|
||||
/>
|
||||
</Labelled>
|
||||
);
|
||||
}
|
||||
|
||||
private onChange = (u8a: Uint8Array): void => {
|
||||
const { onChange } = this.props;
|
||||
const { onChange, t } = this.props;
|
||||
const json = u8aToString(u8a);
|
||||
|
||||
try {
|
||||
const contractAbi = new Abi(JSON.parse(json));
|
||||
const abi = JSON.parse(json);
|
||||
|
||||
if (abi.deploy || abi.messages) {
|
||||
throw new Error(t('You are using an ABI with an outdated format. Please generate a new one.'));
|
||||
}
|
||||
|
||||
const contractAbi = new Abi(abi);
|
||||
|
||||
this.setState({
|
||||
contractAbi,
|
||||
@ -146,7 +168,8 @@ class ABI extends React.PureComponent<Props, State> {
|
||||
this.setState({
|
||||
isAbiValid: false,
|
||||
isEmpty: false,
|
||||
isError: true
|
||||
isError: true,
|
||||
errorText: error
|
||||
}, (): void => onChange(null, null));
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,28 +19,41 @@ import translate from '../translate';
|
||||
|
||||
interface Props extends I18nProps, RouteComponentProps<{}> {
|
||||
code: CodeStored;
|
||||
showDeploy: (codeHash?: string) => () => void;
|
||||
showDeploy: (codeHash?: string, constructorIndex?: number) => () => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
isAbiOpen: boolean;
|
||||
isForgetOpen: boolean;
|
||||
isRemoveABIOpen: boolean;
|
||||
}
|
||||
|
||||
const CodeCard = styled(Card)`
|
||||
&& {
|
||||
min-height: 13rem;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
class Contract extends React.PureComponent<Props, State> {
|
||||
class Code extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isAbiOpen: false,
|
||||
isForgetOpen: false,
|
||||
isRemoveABIOpen: false
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { code, code: { contractAbi } } = this.props;
|
||||
const { code, code: { contractAbi }, t } = this.props;
|
||||
const { isAbiOpen } = this.state;
|
||||
|
||||
const abi = (
|
||||
<ABI
|
||||
contractAbi={contractAbi}
|
||||
onChange={this.onChangeABI}
|
||||
onRemove={this.toggleRemoveABI}
|
||||
onSelectConstructor={this.onDeployConstructor}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<CodeCard>
|
||||
@ -51,11 +64,18 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
isEditable
|
||||
withTags
|
||||
>
|
||||
<ABI
|
||||
contractAbi={contractAbi}
|
||||
onChange={this.onChangeABI}
|
||||
onRemove={this.toggleRemoveABI}
|
||||
/>
|
||||
{contractAbi
|
||||
? (
|
||||
<details
|
||||
onClick={this.toggleAbi}
|
||||
open={isAbiOpen}
|
||||
>
|
||||
<summary>{t('ABI')}</summary>
|
||||
{abi}
|
||||
</details>
|
||||
)
|
||||
: abi
|
||||
}
|
||||
</CodeRow>
|
||||
</CodeCard>
|
||||
);
|
||||
@ -121,6 +141,17 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
return modals;
|
||||
}
|
||||
|
||||
private toggleAbi = (event: React.MouseEvent): () => void => {
|
||||
return (): void => {
|
||||
event.preventDefault();
|
||||
const { isAbiOpen } = this.state;
|
||||
|
||||
this.setState({
|
||||
isAbiOpen: !isAbiOpen
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private toggleForget = (): void => {
|
||||
const { isForgetOpen } = this.state;
|
||||
|
||||
@ -137,6 +168,12 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
});
|
||||
}
|
||||
|
||||
private onDeployConstructor = (constructorIndex = 0): void => {
|
||||
const { code: { json: { codeHash } }, showDeploy } = this.props;
|
||||
|
||||
codeHash && showDeploy && showDeploy(codeHash, constructorIndex)();
|
||||
}
|
||||
|
||||
private onForget = (): void => {
|
||||
const { code: { json: { codeHash } } } = this.props;
|
||||
|
||||
@ -153,14 +190,19 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
private onChangeABI = async (abi: string | null = null): Promise<void> => {
|
||||
private onChangeABI = (abi: string | null = null): void => {
|
||||
const { code: { json: { codeHash } } } = this.props;
|
||||
|
||||
await contracts.saveCode(
|
||||
codeHash,
|
||||
{ abi }
|
||||
this.setState(
|
||||
{ isAbiOpen: true },
|
||||
(): void => {
|
||||
contracts.saveCode(
|
||||
codeHash,
|
||||
{ abi }
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(withRouter(Contract));
|
||||
export default translate(withRouter(Code));
|
||||
|
||||
@ -15,6 +15,7 @@ import { compactAddLength } from '@polkadot/util';
|
||||
import ContractModal, { ContractModalProps, ContractModalState } from '../Modal';
|
||||
import store from '../store';
|
||||
import translate from '../translate';
|
||||
import { GAS_LIMIT } from '../constants';
|
||||
|
||||
interface Props extends ContractModalProps, ApiProps {}
|
||||
|
||||
@ -30,6 +31,7 @@ class Upload extends ContractModal<Props, State> {
|
||||
|
||||
this.defaultState = {
|
||||
...this.defaultState,
|
||||
gasLimit: new BN(GAS_LIMIT),
|
||||
isWasmValid: false,
|
||||
wasm: null
|
||||
};
|
||||
|
||||
@ -13,9 +13,11 @@ import { Abi } from '@polkadot/api-contract';
|
||||
import { Button, Dropdown, InputAddress, InputBalance, InputNumber, Modal, TxButton, TxComponent } from '@polkadot/react-components';
|
||||
import { getContractAbi } from '@polkadot/react-components/util';
|
||||
import { withApi, withMulti } from '@polkadot/react-api';
|
||||
import { displayType } from '@polkadot/types';
|
||||
|
||||
import translate from '../translate';
|
||||
import Params from '../Params';
|
||||
import { GAS_LIMIT } from '../constants';
|
||||
|
||||
interface Props extends BareProps, I18nProps, ApiProps, RouteComponentProps<{}> {
|
||||
address: string | null;
|
||||
@ -41,7 +43,7 @@ class Call extends TxComponent<Props, State> {
|
||||
address: null,
|
||||
accountId: null,
|
||||
endowment: new BN(0),
|
||||
gasLimit: new BN(0),
|
||||
gasLimit: new BN(GAS_LIMIT),
|
||||
method: null,
|
||||
isAddressValid: false,
|
||||
isBusy: false,
|
||||
@ -107,7 +109,7 @@ class Call extends TxComponent<Props, State> {
|
||||
|
||||
const [address, contractAbi, method] = this.getCallProps();
|
||||
const isEndowValid = true;
|
||||
const isGasValid = !gasLimit.isZero();
|
||||
const isGasValid = gasLimit.gtn(0);
|
||||
|
||||
if (!address || !contractAbi) {
|
||||
return null;
|
||||
@ -116,8 +118,8 @@ class Call extends TxComponent<Props, State> {
|
||||
const methodOptions = contractAbi
|
||||
? Object.keys(contractAbi.messages).map((key): { key: string; text: string; value: string } => {
|
||||
const fn = contractAbi.messages[key];
|
||||
const type = fn.type ? `: ${fn.type}` : '';
|
||||
const args = fn.args.map(({ name, type }): string => `${name}: ${type}`);
|
||||
const type = fn.type ? `: ${displayType(fn.type)}` : '';
|
||||
const args = fn.args.map(({ name, type }): string => `${name}: ${displayType(type)}`);
|
||||
const text = `${key}(${args.join(', ')})${type}`;
|
||||
|
||||
return {
|
||||
@ -169,10 +171,12 @@ class Call extends TxComponent<Props, State> {
|
||||
onChange={this.onChangeEndowment}
|
||||
/>
|
||||
<InputNumber
|
||||
defaultValue={gasLimit}
|
||||
help={t('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.')}
|
||||
isError={!isGasValid}
|
||||
label={t('maximum gas allowed')}
|
||||
onChange={this.onChangeGas}
|
||||
value={gasLimit}
|
||||
onEnter={this.sendTx}
|
||||
/>
|
||||
</div>
|
||||
@ -267,7 +271,7 @@ class Call extends TxComponent<Props, State> {
|
||||
}
|
||||
|
||||
private onChangeGas = (gasLimit: BN | undefined): void => {
|
||||
this.setState({ gasLimit: gasLimit || new BN(0) });
|
||||
this.setState({ gasLimit: gasLimit || new BN(GAS_LIMIT) });
|
||||
}
|
||||
|
||||
private onChangeMethod = (method: string | null): void => {
|
||||
|
||||
@ -6,6 +6,7 @@ import { ActionStatus } from '@polkadot/react-components/Status/types';
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
@ -26,6 +27,13 @@ interface State {
|
||||
isPasswordOpen: boolean;
|
||||
}
|
||||
|
||||
const ContractCard = styled(Card)`
|
||||
&& {
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
class Contract extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
isBackupOpen: false,
|
||||
@ -34,7 +42,7 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { address, onCall } = this.props;
|
||||
const { address, onCall, t } = this.props;
|
||||
|
||||
const contractAbi = getContractAbi(address);
|
||||
|
||||
@ -43,7 +51,7 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ContractCard>
|
||||
{this.renderModals()}
|
||||
<AddressRow
|
||||
buttons={this.renderButtons()}
|
||||
@ -55,14 +63,17 @@ class Contract extends React.PureComponent<Props, State> {
|
||||
withNonce={false}
|
||||
withTags
|
||||
>
|
||||
<Messages
|
||||
address={address}
|
||||
contractAbi={contractAbi}
|
||||
isRemovable={false}
|
||||
onSelect={onCall}
|
||||
/>
|
||||
<details>
|
||||
<summary>{t('Messages')}</summary>
|
||||
<Messages
|
||||
address={address}
|
||||
contractAbi={contractAbi}
|
||||
isRemovable={false}
|
||||
onSelect={onCall}
|
||||
/>
|
||||
</details>
|
||||
</AddressRow>
|
||||
</Card>
|
||||
</ContractCard>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { Button, CardGrid } from '@polkadot/react-components';
|
||||
import { stringCamelCase } from '@polkadot/util';
|
||||
|
||||
import translate from '../translate';
|
||||
import Add from './Add';
|
||||
@ -104,7 +105,7 @@ class Contracts extends React.PureComponent<Props, State> {
|
||||
this.setState({
|
||||
isCallOpen: true,
|
||||
callAddress: callAddress || null,
|
||||
callMethod: callMethod || null
|
||||
callMethod: callMethod ? stringCamelCase(callMethod) : null
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId } from '@polkadot/types/interfaces';
|
||||
import { TypeDef } from '@polkadot/types/types';
|
||||
import { ApiProps } from '@polkadot/react-api/types';
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
|
||||
@ -16,22 +15,25 @@ import { Abi } from '@polkadot/api-contract';
|
||||
import { withApi, withMulti } from '@polkadot/react-api';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import { Button, Dropdown, InputBalance, TxButton } from '@polkadot/react-components';
|
||||
import { getTypeDef } from '@polkadot/types';
|
||||
import createValues from '@polkadot/react-params/values';
|
||||
import { displayType } from '@polkadot/types';
|
||||
|
||||
import ContractModal, { ContractModalProps, ContractModalState } from './Modal';
|
||||
import Params from './Params';
|
||||
import store from './store';
|
||||
import translate from './translate';
|
||||
import { GAS_LIMIT, ENDOWMENT } from './constants';
|
||||
|
||||
type ConstructOptions = { key: string; text: string; value: string }[];
|
||||
|
||||
interface Props extends ContractModalProps, ApiProps, I18nProps, RouteComponentProps {
|
||||
codeHash?: string;
|
||||
constructorIndex?: number;
|
||||
}
|
||||
|
||||
interface State extends ContractModalState {
|
||||
codeHash?: string;
|
||||
constructorIndex: number;
|
||||
constructOptions: ConstructOptions;
|
||||
endowment: BN;
|
||||
isHashValid: boolean;
|
||||
@ -48,8 +50,10 @@ class Deploy extends ContractModal<Props, State> {
|
||||
|
||||
this.defaultState = {
|
||||
...this.defaultState,
|
||||
constructorIndex: -1,
|
||||
constructOptions: [],
|
||||
endowment: new BN(0),
|
||||
endowment: new BN(ENDOWMENT),
|
||||
gasLimit: new BN(GAS_LIMIT),
|
||||
isHashValid: false,
|
||||
params: [],
|
||||
...Deploy.getCodeState(props.codeHash)
|
||||
@ -59,44 +63,35 @@ class Deploy extends ContractModal<Props, State> {
|
||||
|
||||
public static getDerivedStateFromProps (props: Props, state: State): Pick<State, never> {
|
||||
if (props.codeHash && (!state.codeHash || state.codeHash !== props.codeHash)) {
|
||||
return Deploy.getCodeState(props.codeHash);
|
||||
return Deploy.getCodeState(props.codeHash, Math.max(props.constructorIndex || 0));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private static getContractAbiState = (abi: string | null | undefined, contractAbi: Abi | null = null): Partial<State> => {
|
||||
private static getContractAbiState = (abi: string | null | undefined, contractAbi: Abi | null = null, constructorIndex = 0): Partial<State> => {
|
||||
if (contractAbi) {
|
||||
const args = contractAbi.deploy.args.map(({ name, type }): string => `${name}: ${type}`);
|
||||
const text = `deploy(${args.join(', ')})`;
|
||||
|
||||
return {
|
||||
abi,
|
||||
constructOptions: [{
|
||||
key: 'deploy',
|
||||
text,
|
||||
value: 'deploy'
|
||||
}],
|
||||
constructorIndex,
|
||||
contractAbi,
|
||||
isAbiValid: !!contractAbi,
|
||||
params: createValues(
|
||||
contractAbi.deploy.args.map(({ name, type }): { type: TypeDef } => ({
|
||||
type: getTypeDef(type, name)
|
||||
}))
|
||||
)
|
||||
...Deploy.getConstructorState(contractAbi, Math.max(constructorIndex, 0))
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
constructorIndex: -1,
|
||||
constructOptions: [] as ConstructOptions,
|
||||
abi: null,
|
||||
contractAbi: null,
|
||||
isAbiSupplied: false,
|
||||
isAbiValid: false,
|
||||
params: [] as unknown[]
|
||||
...Deploy.getConstructorState()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static getCodeState = (codeHash: string | null = null): Pick<State, never> => {
|
||||
private static getCodeState = (codeHash: string | null = null, constructorIndex = 0): Pick<State, never> => {
|
||||
if (codeHash) {
|
||||
const code = store.getCode(codeHash);
|
||||
|
||||
@ -109,7 +104,7 @@ class Deploy extends ContractModal<Props, State> {
|
||||
name: `${json.name} (instance)`,
|
||||
isHashValid: true,
|
||||
isNameValid: true,
|
||||
...Deploy.getContractAbiState(json.abi, contractAbi)
|
||||
...Deploy.getContractAbiState(json.abi, contractAbi, Math.max(constructorIndex, 0))
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -117,9 +112,40 @@ class Deploy extends ContractModal<Props, State> {
|
||||
return {};
|
||||
}
|
||||
|
||||
private static getConstructorState = (contractAbi: Abi | null = null, ci = 0): Pick<State, never> => {
|
||||
const constructorIndex = Math.max(ci, 0);
|
||||
if (!contractAbi || constructorIndex < 0 || constructorIndex >= contractAbi.constructors.length) {
|
||||
return {
|
||||
constructorIndex: -1,
|
||||
constructOptions: [],
|
||||
params: []
|
||||
};
|
||||
}
|
||||
|
||||
const { abi: { contract: { constructors } } } = contractAbi;
|
||||
const constructor = constructors[constructorIndex];
|
||||
const constructOptions: ConstructOptions = constructors.map(
|
||||
(constr) => {
|
||||
const { name, args } = constr;
|
||||
const textArgs = args.map(({ name, type }): string => `${name}: ${displayType(type)}`);
|
||||
|
||||
return {
|
||||
key: `${constructorIndex}`,
|
||||
text: `${name}(${textArgs.join(', ')})`,
|
||||
value: `${constructorIndex}`
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
constructorIndex,
|
||||
constructOptions,
|
||||
params: createValues(constructor.args)
|
||||
};
|
||||
}
|
||||
|
||||
protected renderContent = (): React.ReactNode => {
|
||||
const { t } = this.props;
|
||||
const { codeHash, constructOptions, contractAbi, endowment, isAbiSupplied, isBusy, isHashValid } = this.state;
|
||||
const { codeHash, constructorIndex, constructOptions, contractAbi, endowment, isAbiSupplied, isBusy, isHashValid } = this.state;
|
||||
|
||||
const isEndowValid = !endowment.isZero();
|
||||
const codeOptions = store.getAllCode().map(({ json: { codeHash, name } }): { text: string; value: string } => ({
|
||||
@ -154,13 +180,14 @@ class Deploy extends ContractModal<Props, State> {
|
||||
contractAbi
|
||||
? (
|
||||
<Dropdown
|
||||
defaultValue='deploy'
|
||||
defaultValue={`${constructorIndex}`}
|
||||
help={t('The deployment constructor information for this contract, as provided by the ABI.')}
|
||||
isDisabled
|
||||
isDisabled={contractAbi.abi.contract.constructors.length <= 1}
|
||||
label={t('constructor')}
|
||||
onChange={this.onChangeConstructorIndex}
|
||||
options={constructOptions}
|
||||
style={{ fontFamily: 'monospace' }}
|
||||
value='deploy'
|
||||
value={`${constructorIndex}`}
|
||||
/>
|
||||
)
|
||||
: null
|
||||
@ -170,8 +197,8 @@ class Deploy extends ContractModal<Props, State> {
|
||||
onChange={this.onChangeParams}
|
||||
onEnter={this.sendTx}
|
||||
params={
|
||||
contractAbi
|
||||
? contractAbi.deploy.args
|
||||
contractAbi && constructorIndex >= 0
|
||||
? contractAbi.abi.contract.constructors[constructorIndex].args
|
||||
: []
|
||||
}
|
||||
/>
|
||||
@ -224,13 +251,13 @@ class Deploy extends ContractModal<Props, State> {
|
||||
}
|
||||
|
||||
private constructCall = (): any[] => {
|
||||
const { codeHash, contractAbi, endowment, gasLimit, params } = this.state;
|
||||
const { codeHash, constructorIndex, contractAbi, endowment, gasLimit, params } = this.state;
|
||||
|
||||
if (!contractAbi) {
|
||||
if (!contractAbi || constructorIndex < 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [endowment, gasLimit, codeHash, contractAbi.deploy(...params)];
|
||||
return [endowment, gasLimit, codeHash, contractAbi.constructors[constructorIndex](...params)];
|
||||
}
|
||||
|
||||
protected onAddAbi = (abi: string | null | undefined, contractAbi?: Abi | null): void => {
|
||||
@ -245,6 +272,15 @@ class Deploy extends ContractModal<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private onChangeConstructorIndex = (constructorIndexString: string): void => {
|
||||
const { contractAbi } = this.state;
|
||||
const constructorIndex = Math.max(0, parseInt(constructorIndexString, 10) || 0);
|
||||
|
||||
this.setState(
|
||||
Deploy.getConstructorState(contractAbi, constructorIndex)
|
||||
);
|
||||
};
|
||||
|
||||
private onChangeEndowment = (endowment?: BN | null): void => {
|
||||
this.setState({ endowment: endowment || new BN(0) });
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import { RawParams } from '@polkadot/react-params/types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import UIParams from '@polkadot/react-params';
|
||||
import { getTypeDef } from '@polkadot/types';
|
||||
|
||||
interface Props {
|
||||
isDisabled?: boolean;
|
||||
@ -27,12 +26,7 @@ export default function Params ({ isDisabled, onChange, onEnter, params: propPar
|
||||
|
||||
useEffect((): void => {
|
||||
if (propParams) {
|
||||
setParams(
|
||||
propParams.map(({ name, type }): ParamDef => ({
|
||||
name,
|
||||
type: getTypeDef(type, name)
|
||||
}))
|
||||
);
|
||||
setParams(propParams);
|
||||
}
|
||||
}, [propParams]);
|
||||
|
||||
|
||||
6
packages/app-contracts/src/constants.ts
Normal file
6
packages/app-contracts/src/constants.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2019 @polkadot/app-staking authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export const ENDOWMENT = 1000;
|
||||
export const GAS_LIMIT = 500000;
|
||||
@ -28,6 +28,7 @@ interface Props extends AppProps, I18nProps, RouteComponentProps {
|
||||
|
||||
interface State {
|
||||
codeHash?: string;
|
||||
constructorIndex: number;
|
||||
hasContracts: boolean;
|
||||
isDeployOpen: boolean;
|
||||
updated: number;
|
||||
@ -35,6 +36,7 @@ interface State {
|
||||
|
||||
class App extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
constructorIndex: 0,
|
||||
hasContracts: false,
|
||||
isDeployOpen: false,
|
||||
updated: 0
|
||||
@ -62,7 +64,7 @@ class App extends React.PureComponent<Props, State> {
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { basePath, t } = this.props;
|
||||
const { codeHash, isDeployOpen } = this.state;
|
||||
const { codeHash, constructorIndex, isDeployOpen } = this.state;
|
||||
const hidden: string[] = [];
|
||||
|
||||
return (
|
||||
@ -93,6 +95,7 @@ class App extends React.PureComponent<Props, State> {
|
||||
<Deploy
|
||||
basePath={basePath}
|
||||
codeHash={codeHash}
|
||||
constructorIndex={constructorIndex}
|
||||
isOpen={isDeployOpen}
|
||||
onClose={this.hideDeploy}
|
||||
/>
|
||||
@ -103,6 +106,7 @@ class App extends React.PureComponent<Props, State> {
|
||||
private renderComponent (Component: React.ComponentType<ComponentProps>): () => React.ReactNode {
|
||||
return (): React.ReactNode => {
|
||||
const { accounts, basePath, contracts, onStatusChange } = this.props;
|
||||
const { updated } = this.state;
|
||||
|
||||
if (!contracts) {
|
||||
return null;
|
||||
@ -116,15 +120,17 @@ class App extends React.PureComponent<Props, State> {
|
||||
hasCode={store.hasCode}
|
||||
onStatusChange={onStatusChange}
|
||||
showDeploy={this.showDeploy}
|
||||
updated={updated}
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
private showDeploy = (codeHash?: string): () => void =>
|
||||
private showDeploy = (codeHash?: string, constructorIndex = 0): () => void =>
|
||||
(): void => {
|
||||
this.setState({
|
||||
codeHash: codeHash || undefined,
|
||||
constructorIndex,
|
||||
isDeployOpen: true
|
||||
});
|
||||
}
|
||||
|
||||
@ -74,10 +74,11 @@ class Store extends EventEmitter {
|
||||
|
||||
private addCode (json: CodeJson): void {
|
||||
try {
|
||||
const abi = json.abi ? JSON.parse(json.abi) : null;
|
||||
this.allCode[json.codeHash] = {
|
||||
json,
|
||||
contractAbi: json.abi
|
||||
? new Abi(JSON.parse(json.abi))
|
||||
contractAbi: abi
|
||||
? new Abi(abi)
|
||||
: undefined
|
||||
};
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@ export interface ComponentProps extends AppProps {
|
||||
accounts: SubjectInfo[];
|
||||
contracts: SubjectInfo[];
|
||||
hasCode: boolean;
|
||||
showDeploy: (codeHash?: string) => () => void;
|
||||
showDeploy: (codeHash?: string, constructorIndex?: number) => () => void;
|
||||
updated: number;
|
||||
}
|
||||
|
||||
export interface CodeJson {
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/react-components": "^0.36.0-beta.68",
|
||||
"@polkadot/react-signer": "^0.36.0-beta.68",
|
||||
"@polkadot/ui-assets": "^0.46.0-beta.5",
|
||||
"@polkadot/ui-assets": "^0.46.0-beta.6",
|
||||
"query-string": "^6.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,8 +31,8 @@
|
||||
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-reactive#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/api": "^0.95.0-beta.0",
|
||||
"@polkadot/extension-dapp": "^0.13.0-beta.6",
|
||||
"@polkadot/api": "^0.95.0-beta.2",
|
||||
"@polkadot/extension-dapp": "^0.13.0-beta.7",
|
||||
"edgeware-node-types": "^1.0.10",
|
||||
"rxjs-compat": "^6.5.3"
|
||||
}
|
||||
|
||||
@ -13,31 +13,29 @@
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/keyring": "^1.5.1",
|
||||
"@polkadot/react-api": "^0.36.0-beta.68",
|
||||
"@polkadot/react-identicon": "^0.46.0-beta.5",
|
||||
"@polkadot/react-identicon": "^0.46.0-beta.6",
|
||||
"@polkadot/react-query": "^0.36.0-beta.68",
|
||||
"@polkadot/ui-keyring": "^0.46.0-beta.5",
|
||||
"@polkadot/ui-settings": "^0.46.0-beta.5",
|
||||
"@types/chart.js": "^2.8.5",
|
||||
"@polkadot/ui-keyring": "^0.46.0-beta.6",
|
||||
"@polkadot/ui-settings": "^0.46.0-beta.6",
|
||||
"@types/chart.js": "^2.8.7",
|
||||
"@types/i18next": "^13.0.0",
|
||||
"@types/react-copy-to-clipboard": "^4.2.6",
|
||||
"@types/react-copy-to-clipboard": "^4.3.0",
|
||||
"@types/react-router-dom": "^5.1.0",
|
||||
"@types/react-tooltip": "^3.9.3",
|
||||
"@types/styled-components": "4.1.8",
|
||||
"@types/styled-theming": "2.2.1",
|
||||
"chart.js": "^2.8.0",
|
||||
"codeflask": "^1.4.0",
|
||||
"i18next": "^17.0.16",
|
||||
"i18next-browser-languagedetector": "^3.0.3",
|
||||
"i18next": "^17.0.18",
|
||||
"i18next-browser-languagedetector": "^3.1.1",
|
||||
"i18next-xhr-backend": "^3.2.0",
|
||||
"react": "^16.8.2",
|
||||
"react-chartjs-2": "^2.8.0",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
"react-dom": "^16.8.2",
|
||||
"react-dropzone": "^10.1.9",
|
||||
"react-i18next": "^10.13.0",
|
||||
"react-dropzone": "^10.1.10",
|
||||
"react-i18next": "^10.13.1",
|
||||
"react-markdown": "^4.2.2",
|
||||
"react-router": "^5.1.0",
|
||||
"react-router-dom": "^5.1.0",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-tooltip": "^3.11.1",
|
||||
"semantic-ui-css": "^2.3.1",
|
||||
"semantic-ui-react": "^0.88.1",
|
||||
|
||||
@ -231,7 +231,7 @@ function renderBalances (props: Props): React.ReactNode {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -50,9 +50,9 @@ export default function BondedDisplay (props: Props): React.ReactElement<Props>
|
||||
|
||||
return bonded
|
||||
? (
|
||||
<>
|
||||
{renderProvided(props)}
|
||||
</>
|
||||
<>
|
||||
{renderProvided(props)}
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<Bonded
|
||||
|
||||
@ -115,6 +115,10 @@ const Wrapper = styled.div`
|
||||
padding-left: 1.45rem;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.ui--Messages {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -2,13 +2,17 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ContractABIMethod } from '@polkadot/api-contract/types';
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { Button } from '@polkadot/react-components';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Abi } from '@polkadot/api-contract';
|
||||
import { displayType } from '@polkadot/types';
|
||||
|
||||
import Icon from './Icon';
|
||||
import Tooltip from './Tooltip';
|
||||
import translate from './translate';
|
||||
import { classes } from './util';
|
||||
|
||||
@ -19,65 +23,158 @@ export interface Props extends I18nProps {
|
||||
isRemovable: boolean;
|
||||
onRemove?: () => void;
|
||||
onSelect?: (callAddress?: string, callMethod?: string) => void;
|
||||
onSelectConstructor?: (index: number) => void;
|
||||
withConstructors?: boolean;
|
||||
}
|
||||
|
||||
const NOOP = (): void => {};
|
||||
|
||||
function onSelect (props: Props, index: number): () => void {
|
||||
return function (): void {
|
||||
const { address: callAddress, contractAbi: { abi: { messages } }, onSelect } = props;
|
||||
const { address: callAddress, contractAbi: { abi: { contract: { messages } } }, onSelect } = props;
|
||||
|
||||
if (!callAddress || !messages || !messages[index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { name: callMethod } = messages[index];
|
||||
const { name } = messages[index];
|
||||
|
||||
onSelect && onSelect(callAddress, callMethod);
|
||||
onSelect && onSelect(callAddress, name);
|
||||
};
|
||||
}
|
||||
|
||||
function onSelectConstructor (props: Props, index: number): () => void {
|
||||
return function (): void {
|
||||
const { contractAbi: { abi: { contract: { constructors } } }, onSelectConstructor } = props;
|
||||
|
||||
if (!constructors || !constructors[index]) {
|
||||
return;
|
||||
}
|
||||
console.log(onSelectConstructor);
|
||||
onSelectConstructor && onSelectConstructor(index);
|
||||
};
|
||||
}
|
||||
|
||||
function renderItem (props: Props, { args, docs = [], mutates, name, returnType }: ContractABIMethod, index: number, asConstructor = false): React.ReactNode {
|
||||
return (
|
||||
<div
|
||||
key={name}
|
||||
className={classes('message', !onSelect && 'exempt-hover', asConstructor && 'constructor')}
|
||||
>
|
||||
<div className="info">
|
||||
<div className="signature">
|
||||
<span className="name">
|
||||
{name}
|
||||
</span>
|
||||
(
|
||||
{args.map(({ name, type }, index): React.ReactNode => {
|
||||
return (
|
||||
<React.Fragment key={`${name}-args-${index}`}>
|
||||
{name}:
|
||||
{' '}
|
||||
<span className='type'>
|
||||
{displayType(type)}
|
||||
</span>
|
||||
{index < args.length - 1 && ', '}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
)
|
||||
{(!asConstructor && returnType) && (
|
||||
<>
|
||||
:
|
||||
{' '}
|
||||
<span className="return-type">
|
||||
{displayType(returnType)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{mutates && (
|
||||
<>
|
||||
<Icon
|
||||
data-tip
|
||||
data-for={`mutates-${name}`}
|
||||
name="database"
|
||||
className="mutates"
|
||||
/>
|
||||
<Tooltip
|
||||
trigger={`mutates-${name}`}
|
||||
text={props.t('Mutates contract state')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<details className="docs">
|
||||
<summary>
|
||||
{
|
||||
docs
|
||||
.filter((line) => line !== '')
|
||||
.map((line, index) => ((
|
||||
<React.Fragment key={`${name}-docs-${index}`}>
|
||||
<span>{line}</span>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
)))
|
||||
}
|
||||
</summary>
|
||||
</details>
|
||||
</div>
|
||||
{!asConstructor && props.onSelect && (
|
||||
<div className="accessory">
|
||||
<Button
|
||||
className="execute"
|
||||
icon="play"
|
||||
onClick={onSelect(props, index)}
|
||||
tooltip={props.t('Call this message')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{asConstructor && props.onSelectConstructor && (
|
||||
<div className="accessory">
|
||||
<Button
|
||||
className="execute"
|
||||
icon="cloud upload"
|
||||
onClick={onSelectConstructor(props, index)}
|
||||
tooltip={props.t('Deploy with this constructor')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderConstructor (props: Props, index: number): React.ReactNode {
|
||||
const { contractAbi: { abi: { contract: { constructors } } } } = props;
|
||||
if (!constructors[index]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return renderItem(props, constructors[index], index, true);
|
||||
}
|
||||
|
||||
function renderMessage (props: Props, index: number): React.ReactNode {
|
||||
const { contractAbi: { abi: { messages } }, onSelect: onSelectProp } = props;
|
||||
const { contractAbi: { abi: { contract: { messages } } } } = props;
|
||||
if (!messages[index]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { args, name, return_type: returnType } = messages[index];
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={name}
|
||||
className={classes('message', !onSelect && 'exempt-hover')}
|
||||
isDisabled={!onSelectProp}
|
||||
onClick={onSelect(props, index)}
|
||||
isPrimary={!!onSelectProp}
|
||||
icon='info'
|
||||
>
|
||||
{name}
|
||||
(
|
||||
{args.map(({ name, type }): string => `${name}: ${type}`).join(', ')}
|
||||
)
|
||||
{returnType && `: ${returnType}`}
|
||||
</Button>
|
||||
);
|
||||
return renderItem(props, messages[index], index);
|
||||
}
|
||||
|
||||
function Messages (props: Props): React.ReactElement<Props> {
|
||||
const { className, contractAbi: { abi: { messages } }, isLabelled, isRemovable, onRemove = NOOP, onSelect, t } = props;
|
||||
|
||||
const { className, contractAbi: { abi: { contract: { constructors, messages } } }, isLabelled, isRemovable, onRemove = NOOP, withConstructors } = props;
|
||||
return (
|
||||
<div className={classes(className, 'ui--Messages', isLabelled && 'labelled', onSelect && 'select')}>
|
||||
<div className={classes(className, 'ui--Messages', isLabelled && 'labelled')}>
|
||||
{withConstructors && constructors.map((_, index): React.ReactNode => renderConstructor(props, index))}
|
||||
{messages.map((_, index): React.ReactNode => renderMessage(props, index))}
|
||||
{isRemovable && (
|
||||
<Button
|
||||
className='iconButton'
|
||||
icon='remove'
|
||||
<a
|
||||
className='remove-abi'
|
||||
onClick={onRemove}
|
||||
size='tiny'
|
||||
isNegative
|
||||
tooltip={t('Remove ABI')}
|
||||
/>
|
||||
>
|
||||
<Icon name='remove' />
|
||||
Remove ABI
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@ -85,13 +182,16 @@ function Messages (props: Props): React.ReactElement<Props> {
|
||||
|
||||
export default translate(styled(Messages)`
|
||||
font-size: 0.9rem;
|
||||
min-height: 3.5rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.remove-abi {
|
||||
float: right;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.labelled {
|
||||
background: white;
|
||||
@ -102,22 +202,82 @@ export default translate(styled(Messages)`
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& > .message {
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
& .message {
|
||||
width: calc(100% - 1rem);
|
||||
background: #f8f8f8;
|
||||
display: inline-flex;
|
||||
margin-bottom: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
margin: 0;
|
||||
border-radius: 0.7rem;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.constructor {
|
||||
background: #e8f4ff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.accessory .execute {
|
||||
display: block;
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
|
||||
&:hover {
|
||||
color: #2e86ab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1 1;
|
||||
|
||||
.signature {
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
|
||||
.name {
|
||||
color: #2f8ddb;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.type {
|
||||
color: #21a2b2;
|
||||
}
|
||||
|
||||
.return-type {
|
||||
color: #ff8600;
|
||||
}
|
||||
}
|
||||
|
||||
.docs {
|
||||
font-size: 0.8rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.mutates {
|
||||
color: #ff8600;
|
||||
margin-left: 0.5rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.accessory {
|
||||
width: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.execute {
|
||||
display: none;
|
||||
background: transparent;
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 1 !important;
|
||||
background: #eee !important;
|
||||
color: #555 !important;
|
||||
}
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@ -99,6 +99,10 @@ export default createGlobalStyle`
|
||||
|
||||
&[open] > summary {
|
||||
white-space: normal;
|
||||
|
||||
br, br + * {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
> summary {
|
||||
@ -106,6 +110,10 @@ export default createGlobalStyle`
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
outline: none;
|
||||
|
||||
br, br + * {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,8 @@ export default function getContractAbi (address: string | null): Abi | null {
|
||||
const meta = getAddressMeta(address, 'contract');
|
||||
|
||||
try {
|
||||
abi = meta.contract && new Abi(JSON.parse(meta.contract.abi));
|
||||
const data = meta.contract && JSON.parse(meta.contract.abi);
|
||||
abi = new Abi(data);
|
||||
} catch (error) {
|
||||
// invalid address, maybe
|
||||
}
|
||||
|
||||
@ -85,6 +85,9 @@ export default function findComponent (def: TypeDef, overrides: ComponentMap = {
|
||||
return 'Struct';
|
||||
|
||||
case TypeDefInfo.Tuple:
|
||||
if (components[type] === Account) {
|
||||
return type;
|
||||
}
|
||||
return 'Tuple';
|
||||
|
||||
case TypeDefInfo.Vec:
|
||||
|
||||
@ -12,6 +12,6 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.6.2",
|
||||
"@polkadot/react-components": "^0.36.0-beta.68",
|
||||
"@polkadot/react-qr": "^0.46.0-beta.5"
|
||||
"@polkadot/react-qr": "^0.46.0-beta.6"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user