Contract.call RPC interface with return type (#1780)

* contracts api + rpc

* finalized

* tweak

* api bump

* linting
This commit is contained in:
Keith Ingram 2019-10-31 20:45:31 +02:00 committed by GitHub
parent 9a69849b3d
commit c2543ae9b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 458 additions and 284 deletions

View File

@ -10,10 +10,10 @@
"packages/*"
],
"resolutions": {
"@polkadot/api": "^0.96.0-beta.16",
"@polkadot/api-contract": "^0.96.0-beta.16",
"@polkadot/api": "^0.96.0-beta.17",
"@polkadot/api-contract": "^0.96.0-beta.17",
"@polkadot/keyring": "^1.6.1",
"@polkadot/types": "^0.96.0-beta.16",
"@polkadot/types": "^0.96.0-beta.17",
"@polkadot/util": "^1.6.1",
"@polkadot/util-crypto": "^1.6.1",
"babel-core": "^7.0.0-bridge.0",

View File

@ -11,7 +11,7 @@
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.6.3",
"@polkadot/api-contract": "^0.96.0-beta.16",
"@polkadot/api-contract": "^0.96.0-beta.17",
"@polkadot/react-components": "^0.37.0-beta.28"
}
}

View File

@ -2,63 +2,76 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ContractCallOutcome } from '@polkadot/api-contract/types';
import { ApiProps } from '@polkadot/react-api/types';
import { BareProps, CallContract, I18nProps, StringOrNull } from '@polkadot/react-components/types';
import { QueueProps } from '@polkadot/react-components/Status/types';
import { BareProps, I18nProps, StringOrNull } from '@polkadot/react-components/types';
import { ContractExecResult } from '@polkadot/types/interfaces/contracts';
import BN from 'bn.js';
import React, { useState } from 'react';
import rpc from '@polkadot/jsonrpc';
import { Button, Dropdown, InputAddress, InputBalance, InputNumber, Modal, Output, TxButton } from '@polkadot/react-components';
import { QueueConsumer } from '@polkadot/react-components/Status/Context';
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { Button, Dropdown, IconLink, InputAddress, InputBalance, InputNumber, Modal, Toggle, TxButton } from '@polkadot/react-components';
import { PromiseContract as ApiContract } from '@polkadot/api-contract';
import { withApi, withMulti } from '@polkadot/react-api';
import { isNull, isUndefined } from '@polkadot/util';
import { createValue } from '@polkadot/react-params/values';
import { isNull } from '@polkadot/util';
import Params from '../Params';
import Outcome from './Outcome';
import translate from '../translate';
import { GAS_LIMIT } from '../constants';
import { findCallMethod, getContractForAddress, getCallMethodOptions, getContractMethodFn } from './util';
import { getCallMessageOptions } from './util';
interface Props extends BareProps, I18nProps, ApiProps {
callContract: CallContract | null;
callMethodIndex: number | null;
callContract: ApiContract | null;
callMessageIndex: number | null;
callResults: ContractExecResult[];
isOpen: boolean;
onChangeCallContract: (callContract: CallContract) => void;
onChangeCallMethodIndex: (callMethodIndex: number) => void;
onChangeCallContractAddress: (callContractAddress: StringOrNull) => void;
onChangeCallMessageIndex: (callMessageIndex: number) => void;
onClose: () => void;
}
function Call (props: Props): React.ReactElement<Props> | null {
const { isOpen, callContract, callMethodIndex, onChangeCallContract, onChangeCallMethodIndex, onClose, api, t } = props;
const { className, isOpen, callContract, callMessageIndex, onChangeCallContractAddress, onChangeCallMessageIndex, onClose, api, t } = props;
if (isNull(callContract) || isNull(callMethodIndex)) {
if (isNull(callContract) || isNull(callMessageIndex)) {
return null;
}
const hasRpc = api.rpc.contracts && api.rpc.contracts.call;
const callMethod = findCallMethod(callContract, callMethodIndex);
const useRpc = hasRpc && callMethod && !callMethod.mutates;
// const isRpc = false;
let callMessage = callContract.getMessage(callMessageIndex);
const [accountId, setAccountId] = useState<StringOrNull>(null);
const [endowment, setEndowment] = useState<BN>(new BN(0));
const [gasLimit, setGasLimit] = useState<BN>(new BN(GAS_LIMIT));
const [isBusy, setIsBusy] = useState(false);
const [params, setParams] = useState<any[]>([]);
const [outcomes, setOutcomes] = useState<ContractCallOutcome[]>([]);
const [params, setParams] = useState<any[]>(callMessage ? callMessage.def.args.map(({ type }): any => createValue({ type })) : []);
const [useRpc, setUseRpc] = useState(callMessage && !callMessage.def.mutates);
useEffect((): void => {
callMessage = callContract.getMessage(callMessageIndex);
setParams(callMessage ? callMessage.def.args.map(({ type }): any => createValue({ type })) : []);
if (!callMessage || callMessage.def.mutates) {
setUseRpc(false);
} else {
setUseRpc(true);
}
}, [callContract, callMessageIndex]);
useEffect((): void => {
setOutcomes([]);
}, [callContract]);
const _onChangeAccountId = (accountId: StringOrNull): void => setAccountId(accountId);
const _onChangeCallAddress = (callAddress: StringOrNull): void => {
const callContract = getContractForAddress(callAddress);
onChangeCallContract && callContract.abi && onChangeCallContract(callContract);
};
const _onChangeCallMethodString = (callMethodString: string): void => {
setParams([]);
onChangeCallMethodIndex && onChangeCallMethodIndex(parseInt(callMethodString, 10) || 0);
const _onChangeCallMessageIndexString = (callMessageIndexString: string): void => {
onChangeCallMessageIndex && onChangeCallMessageIndex(
parseInt(callMessageIndexString, 10) || 0
);
};
const _onChangeEndowment = (endowment?: BN): void => endowment && setEndowment(endowment);
@ -68,28 +81,29 @@ function Call (props: Props): React.ReactElement<Props> | null {
const _toggleBusy = (): void => setIsBusy(!isBusy);
const _constructTx = (): any[] => {
const fn = getContractMethodFn(callContract, callMethod);
if (!fn || !callContract || !callContract.address) {
if (!accountId || !callMessage || !callMessage.fn || !callContract || !callContract.address) {
return [];
}
return [callContract.address, endowment, gasLimit, fn(...params)];
return [callContract.address.toString(), endowment, gasLimit, callMessage.fn(...params)];
};
const _constructRpc = (): [any] | null => {
const fn = getContractMethodFn(callContract, callMethod);
if (!fn || !accountId || !callContract || !callContract.address || !callContract.abi || !callMethod) {
return null;
}
return [
{
origin: accountId,
dest: callContract.address,
value: endowment,
gasLimit,
inputData: fn(...params)
}
];
const _onSubmitRpc = (): void => {
if (!accountId) return;
callContract
.call('rpc', callMessage.def.name, endowment, gasLimit, ...params)
.send(accountId)
.then(
(outcome: ContractCallOutcome): void => {
setOutcomes([outcome, ...outcomes]);
}
);
};
const _onClearOutcomes = (): void => setOutcomes([]);
const _onClearOutcome = (outcomeIndex: number) => (): void => {
setOutcomes(outcomes.slice(0, outcomeIndex).concat(outcomes.slice(outcomeIndex + 1)));
};
const isEndowmentValid = true;
@ -98,7 +112,7 @@ function Call (props: Props): React.ReactElement<Props> | null {
return (
<Modal
className='app--contracts-Modal'
className={[className || '', 'app--contracts-Modal'].join(' ')}
dimmer='inverted'
onClose={onClose}
open={isOpen}
@ -122,27 +136,28 @@ function Call (props: Props): React.ReactElement<Props> | null {
help={t('A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.')}
isDisabled={isBusy}
label={t('contract to use')}
onChange={_onChangeCallAddress}
onChange={onChangeCallContractAddress}
type='contract'
value={callContract.address}
value={callContract.address.toString()}
/>
{callMethodIndex !== null && (
{callMessageIndex !== null && (
<>
<Dropdown
defaultValue={`${callMessage.index}`}
help={t('The message to send to this contract. Parameters are adjusted based on the ABI provided.')}
isDisabled={isBusy}
isError={callMethod === null}
isError={callMessage === null}
label={t('message to send')}
onChange={_onChangeCallMethodString}
options={getCallMethodOptions(callContract)}
value={`${callMethodIndex}`}
onChange={_onChangeCallMessageIndexString}
options={getCallMessageOptions(callContract)}
value={`${callMessage.index}`}
/>
<Params
isDisabled={isBusy}
onChange={_onChangeParams}
params={
callMethod
? callMethod.args
callMessage
? callMessage.def.args
: undefined
}
/>
@ -167,109 +182,98 @@ function Call (props: Props): React.ReactElement<Props> | null {
/>
</div>
)}
<QueueConsumer>
{
({ queueRpc, txqueue }: QueueProps): React.ReactNode => {
const _onSubmitRpc = (): void => {
const values = _constructRpc();
if (values) {
queueRpc({
accountId,
rpc: rpc.contracts.methods.call,
values
});
}
};
const results = txqueue
.filter(({ error, result, rpc, values }): boolean =>
((!isUndefined(error) || !isUndefined(result)) &&
rpc.section === 'contracts' && rpc.method === 'call' && !!values && values[0].dest === callContract.address)
)
.reverse();
return (
<>
<Button.Group>
<Button
icon='cancel'
isNegative
onClick={onClose}
label={t('Cancel')}
/>
<Button.Or />
{useRpc
? (
<Button
icon='sign-in'
isDisabled={!isValid}
isPrimary
label={t('Call')}
onClick={_onSubmitRpc}
/>
)
: (
<TxButton
accountId={accountId}
icon='sign-in'
isDisabled={!isValid}
isPrimary
label={t('Call')}
onClick={_toggleBusy}
onFailed={_toggleBusy}
onSuccess={_toggleBusy}
params={_constructTx}
tx={api.tx.contracts ? 'contracts.call' : 'contract.call'}
/>
)
}
</Button.Group>
{results.length > 0 && (
<>
<h3>{t('Call results')}</h3>
<div>
{
results.map(
(tx, index): React.ReactNode => {
let output: string;
const contractExecResult = tx.result as ContractExecResult;
if (contractExecResult.isSuccess) {
const { data } = contractExecResult.asSuccess;
output = data.toHex();
} else {
output = 'Error';
}
return (
<Output
isError={contractExecResult.isError}
key={`result-${tx.id}`}
label={t(`#${results.length - 1 - index}`)}
style={{ fontFamily: 'monospace' }}
value={output}
withCopy
withLabel
/>
);
}
)
}
</div>
</>
)}
</>
);
{hasRpc && (
<Toggle
className='rpc-toggle'
isDisabled={!!callMessage && callMessage.def.mutates}
label={
useRpc
? t('send as RPC call')
: t('send as transaction')
}
onChange={setUseRpc}
value={useRpc || false}
/>
)}
<Button.Group>
<Button
icon='cancel'
isNegative
onClick={onClose}
label={t('Cancel')}
/>
<Button.Or />
{useRpc
? (
<Button
icon='sign-in'
isDisabled={!isValid}
isPrimary
label={t('Call')}
onClick={_onSubmitRpc}
/>
)
: (
<TxButton
accountId={accountId}
icon='sign-in'
isDisabled={!isValid}
isPrimary
label={t('Call')}
onClick={_toggleBusy}
onFailed={_toggleBusy}
onSuccess={_toggleBusy}
params={_constructTx}
tx={api.tx.contracts ? 'contracts.call' : 'contract.call'}
/>
)
}
</QueueConsumer>
</Button.Group>
{outcomes.length > 0 && (
<>
<h3>
{t('Call results')}
<IconLink
className='clear-all'
icon='close'
label={t('Clear all')}
onClick={_onClearOutcomes}
/>
</h3>
<div>
{
outcomes.map(
(outcome, index): React.ReactNode => {
return (
<Outcome
key={`outcome-${index}`}
onClear={_onClearOutcome(index)}
outcome={outcome}
/>
);
}
)
}
</div>
</>
)}
</Modal.Content>
</Modal>
);
}
export default withMulti(
Call,
styled(Call)`
.rpc-toggle {
margin-top: 1rem;
display: flex;
justify-content: flex-end;
}
.clear-all {
float: right;
}
`,
translate,
withApi
);

View File

@ -3,21 +3,22 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { ActionStatus } from '@polkadot/react-components/Status/types';
import { CallContract, I18nProps } from '@polkadot/react-components/types';
import { I18nProps } from '@polkadot/react-components/types';
import React, { useState } from 'react';
import styled from 'styled-components';
import { RouteComponentProps } from 'react-router';
import { withRouter } from 'react-router-dom';
import keyring from '@polkadot/ui-keyring';
import { PromiseContract as ApiContract } from '@polkadot/api-contract';
import { AddressRow, Button, Card, Forget, Messages } from '@polkadot/react-components';
import translate from '../translate';
interface Props extends I18nProps, RouteComponentProps {
basePath: string;
contract: CallContract;
onCall: (_: CallContract) => (_?: number) => () => void;
contract: ApiContract;
onCall: (_?: number) => () => void;
}
const ContractCard = styled(Card)`
@ -28,7 +29,7 @@ const ContractCard = styled(Card)`
`;
function Contract (props: Props): React.ReactElement<Props> | null {
const { contract, contract: { abi, address }, onCall, t } = props;
const { contract: { abi, address }, onCall, t } = props;
if (!address || !abi) {
return null;
@ -48,7 +49,7 @@ function Contract (props: Props): React.ReactElement<Props> | null {
};
try {
keyring.forgetContract(address);
keyring.forgetContract(address.toString());
status.status = 'success';
status.message = t('address forgotten');
} catch (error) {
@ -58,15 +59,12 @@ function Contract (props: Props): React.ReactElement<Props> | null {
_toggleForget();
};
const _onCallMessage = onCall(contract);
const _onCall = _onCallMessage();
return (
<ContractCard>
{
isForgetOpen && (
<Forget
address={address}
address={address.toString()}
mode='contract'
onForget={_onForget}
key='modal-forget-contract'
@ -88,7 +86,7 @@ function Contract (props: Props): React.ReactElement<Props> | null {
icon='play'
isPrimary
label={t('execute')}
onClick={_onCall}
onClick={onCall()}
size='small'
tooltip={t('Call a method on this contract')}
/>
@ -105,10 +103,10 @@ function Contract (props: Props): React.ReactElement<Props> | null {
<details>
<summary>{t('Messages')}</summary>
<Messages
address={address}
address={address.toString()}
contractAbi={abi}
isRemovable={false}
onSelect={_onCallMessage}
onSelect={onCall}
/>
</details>
</AddressRow>

View File

@ -0,0 +1,103 @@
// Copyright 2017-2019 @polkadot/app-contracts authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ContractCallOutcome } from '@polkadot/api-contract/types';
import React from 'react';
import styled from 'styled-components';
import { AddressMini, Button, MessageSignature, Output } from '@polkadot/react-components';
interface Props {
className?: string;
onClear?: () => void;
outcome: ContractCallOutcome;
}
function Outcome (props: Props): React.ReactElement<Props> | null {
const { className, onClear, outcome: { message, origin, output, params, success, time } } = props;
const dateTime = new Date(time);
return (
<div className={className}>
<div className='info'>
<AddressMini
className='origin'
value={origin}
withAddress={false}
isPadded={false}
/>
<MessageSignature
message={message}
params={params}
/>
<span className='date-time'>
{dateTime.toLocaleDateString()}
{' '}
{dateTime.toLocaleTimeString()}
</span>
<Button
className='icon-button clear-btn'
icon='close'
size='mini'
isPrimary
onClick={onClear}
/>
</div>
<Output
isError={!success}
className='output'
value={output || '()'}
withCopy
withLabel={false}
/>
</div>
);
}
export default styled(Outcome)`
& {
.info {
display: inline-flex;
justify-content: center;
align-items: center;
padding: 0.5rem;
& > *:not(:first-child) {
padding-left: 1.5rem !important;
}
}
.clear-btn {
display: none;
}
.date-time {
color: #aaa;
}
.origin {
padding-left: 0 !important;
* {
margin-left: 0 !important;
}
}
.output {
font-family: monospace;
margin-left: 3.5rem;
.ui--output {
border-color: #aaa;
margin: 0;
}
}
&:hover {
.clear-btn {
display: block;
}
}
}
`;

View File

@ -2,48 +2,70 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { CallContract, NullContract, I18nProps } from '@polkadot/react-components/types';
import { ApiProps } from '@polkadot/react-api/types';
import { I18nProps, StringOrNull } from '@polkadot/react-components/types';
import { ComponentProps } from '../types';
import React, { useState } from 'react';
import { RouteComponentProps } from 'react-router';
import { withRouter } from 'react-router-dom';
import React, { useState, useEffect } from 'react';
import { PromiseContract as ApiContract } from '@polkadot/api-contract';
import { withApi, withMulti } from '@polkadot/react-api';
import { Button, CardGrid } from '@polkadot/react-components';
import translate from '../translate';
import Add from './Add';
import Contract from './Contract';
import ContractCard from './Contract';
import Call from './Call';
import { getContractForAddress } from './util';
interface Props extends ComponentProps, I18nProps, RouteComponentProps {}
interface Props extends ComponentProps, I18nProps, ApiProps {}
function filterContracts ({ api, accounts, contracts: keyringContracts }: Props): ApiContract[] {
return accounts && keyringContracts && Object.keys(keyringContracts)
.map((address): ApiContract | null => getContractForAddress(api, address))
.filter((contract: ApiContract | null): boolean => !!contract) as ApiContract[];
}
function Contracts (props: Props): React.ReactElement<Props> {
const { accounts, basePath, contracts, hasCode, showDeploy, t } = props;
// const { callAddress, callMethod, isAddOpen, isCallOpen } = this.state;
const { accounts, basePath, contracts: keyringContracts, hasCode, showDeploy, t } = props;
// const { callAddress, callMessage, isAddOpen, isCallOpen } = this.state;
const [callContract, setCallContract] = useState<CallContract | null>(null);
const [callMethodIndex, setCallMethodIndex] = useState<number | null>(null);
const [contracts, setContracts] = useState<ApiContract[]>(filterContracts(props));
const [callContractIndex, setCallContractIndex] = useState<number>(0);
const [callMessageIndex, setCallMessageIndex] = useState<number>(0);
const [isAddOpen, setIsAddOpen] = useState(false);
const [isCallOpen, setIsCallOpen] = useState(false);
useEffect((): void => {
setContracts(filterContracts(props));
}, [accounts, keyringContracts]);
let callContract = contracts[callContractIndex] || null;
useEffect((): void => {
callContract = contracts[callContractIndex];
}, [callContractIndex]);
const _toggleAdd = (): void => setIsAddOpen(!isAddOpen);
const _toggleCall = (): void => setIsCallOpen(!isCallOpen);
const _onChangeCallContract = (newCallContract: CallContract): void => {
if (callContract && newCallContract.address !== callContract.address) {
setCallMethodIndex(0);
const _onChangeCallContractAddress = (newCallContractAddress: StringOrNull): void => {
const index = contracts.findIndex(({ address }: ApiContract): boolean => newCallContractAddress === address.toString());
if (index > -1) {
index !== callContractIndex && setCallMessageIndex(0);
setCallContractIndex(index);
}
setCallContract(callContract);
};
const _onChangeCallMethodIndex = (callMethodIndex: number): void => {
!!callContract && setCallMethodIndex(callMethodIndex);
const _onChangeCallMessageIndex = (callMessageIndex: number): void => {
!!callContract && setCallMessageIndex(callMessageIndex);
};
const _onCall = (callContract: CallContract): (_?: number) => () => void => {
return function (callMethodIndex?: number): () => void {
const _onCall = (callContractIndex: number): (_?: number) => () => void => {
return function (callMessageIndex?: number): () => void {
return function (): void {
setCallContract(callContract);
setCallMethodIndex(callMethodIndex || 0);
setCallContractIndex(callContractIndex);
setCallMessageIndex(callMessageIndex || 0);
setIsCallOpen(true);
};
};
@ -75,16 +97,14 @@ function Contracts (props: Props): React.ReactElement<Props> {
</Button.Group>
}
>
{(accounts && contracts && Object.keys(contracts)
.map((address): CallContract | NullContract => getContractForAddress(address))
.filter(({ abi, address }: CallContract | NullContract): boolean => !!address && !!abi) as CallContract[])
.map((contract: CallContract): React.ReactNode => {
{contracts
.map((contract: ApiContract, index): React.ReactNode => {
return (
<Contract
<ContractCard
basePath={basePath}
contract={contract}
key={contract.address}
onCall={_onCall}
key={contract.address.toString()}
onCall={_onCall(index)}
/>
);
})}
@ -96,14 +116,18 @@ function Contracts (props: Props): React.ReactElement<Props> {
/>
<Call
callContract={callContract}
callMethodIndex={callMethodIndex}
callMessageIndex={callMessageIndex}
isOpen={isCallOpen}
onChangeCallContract={_onChangeCallContract}
onChangeCallMethodIndex={_onChangeCallMethodIndex}
onChangeCallContractAddress={_onChangeCallContractAddress}
onChangeCallMessageIndex={_onChangeCallMessageIndex}
onClose={_toggleCall}
/>
</>
);
}
export default translate(withRouter(Contracts));
export default withMulti(
Contracts,
translate,
withApi
);

View File

@ -2,52 +2,47 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ContractABIFn, ContractABIMethod } from '@polkadot/api-contract/types';
import { CallContract, NullContract, StringOrNull } from '@polkadot/react-components/types';
import { CONTRACT_NULL } from '../constants';
import { ContractABIFn, ContractABIMessage } from '@polkadot/api-contract/types';
import { StringOrNull } from '@polkadot/react-components/types';
import React from 'react';
import { ApiPromise } from '@polkadot/api';
import { PromiseContract as Contract } from '@polkadot/api-contract';
import { MessageSignature } from '@polkadot/react-components';
import { getContractAbi } from '@polkadot/react-components/util';
import { stringCamelCase } from '@polkadot/util';
export function findCallMethod (callContract: CallContract | null, callMethodIndex = 0): ContractABIMethod | null {
export function findCallMethod (callContract: Contract | null, callMethodIndex = 0): ContractABIMessage | null {
const message = callContract && callContract.abi.abi.contract.messages[callMethodIndex];
return message || null;
}
export function getContractMethodFn (callContract: CallContract | null, callMethod: ContractABIMethod | null): ContractABIFn | null {
const fn = callContract && callContract.abi && callMethod && callContract.abi.messages[stringCamelCase(callMethod.name)];
export function getContractMethodFn (callContract: Contract | null, callMethodIndex: number | null): ContractABIFn | null {
const fn = callContract && callContract.abi && callMethodIndex !== null && callContract.abi.messages[callMethodIndex];
return fn || null;
}
export function getContractForAddress (address: StringOrNull): CallContract | NullContract {
export function getContractForAddress (api: ApiPromise, address: StringOrNull): Contract | null {
if (!address) {
return CONTRACT_NULL;
return null;
} else {
const abi = getContractAbi(address);
return abi
? {
address,
abi
}
: CONTRACT_NULL;
? new Contract(api, abi, address)
: null;
}
}
export function getCallMethodOptions (callContract: CallContract | null): any[] {
return callContract && callContract.abi
? callContract.abi.abi.contract.messages.map((message, messageIndex): { key: string; text: React.ReactNode; value: string } => {
const key = message.name;
export function getCallMessageOptions (callContract: Contract | null): any[] {
return callContract
? callContract.messages.map(({ def: message, def: { name }, index }): { key: string; text: React.ReactNode; value: string } => {
return {
key,
key: name,
text: (
<MessageSignature message={message} />
),
value: `${messageIndex}`
value: `${index}`
};
})
: [];

View File

@ -31,7 +31,7 @@
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-reactive#readme",
"dependencies": {
"@babel/runtime": "^7.6.3",
"@polkadot/api": "^0.96.0-beta.16",
"@polkadot/api": "^0.96.0-beta.17",
"@polkadot/extension-dapp": "^0.14.0-beta.1",
"edgeware-node-types": "^1.0.10",
"rxjs-compat": "^6.5.3"

View File

@ -274,7 +274,7 @@ function renderBalances (props: Props): React.ReactNode {
{stakingInfo.controllerId && (
<TxButton
accountId={stakingInfo.controllerId.toString()}
className='iconButton'
className='icon-button'
icon='lock'
size='small'
isPrimary
@ -375,7 +375,7 @@ export default withMulti(
padding-right: 0 !important;
}
button.ui.icon.primary.button.iconButton {
button.ui.icon.primary.button.icon-button {
background: white !important;
}
}

View File

@ -41,7 +41,7 @@ function CopyButton ({ children, className, icon = 'copy', isAddress = false, t,
{children}
<span className='copySpan'>
<Button
className='iconButton'
className='icon-button'
icon={icon}
size='mini'
isPrimary
@ -57,7 +57,7 @@ export default translate(
styled(CopyButton)`
cursor: copy;
button.ui.mini.icon.primary.button.iconButton {
button.ui.mini.icon.primary.button.icon-button {
cursor: copy;
}

View File

@ -0,0 +1,40 @@
// Copyright 2017-2019 @polkadot/react-components authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { BareProps } from './types';
import React from 'react';
import styled from 'styled-components';
import Icon from './Icon';
interface Props extends BareProps {
icon?: string;
label?: React.ReactNode;
onClick: () => void;
}
function IconLink ({ className, icon, label, onClick }: Props): React.ReactElement<Props> {
return (
<a
className={className}
onClick={onClick}
>
{icon && <Icon className={icon} />}
{label}
</a>
);
}
export default styled(IconLink)`
font-size: 0.9rem !important;
&:hover {
text-decoration: underline;
i {
text-decoration: none;
}
}
`;

View File

@ -2,7 +2,7 @@
// 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 { ContractABIMessage } from '@polkadot/api-contract/types';
import { I18nProps } from '@polkadot/react-components/types';
import React from 'react';
@ -15,7 +15,8 @@ import translate from './translate';
export interface Props extends I18nProps {
asConstructor?: boolean;
message: ContractABIMethod;
message: ContractABIMessage;
params?: any[];
withTooltip?: boolean;
}
@ -43,7 +44,7 @@ const ReturnType = styled.span`
color: #ff8600;
`;
function MessageSignature ({ message: { args, mutates, name, returnType }, asConstructor = false, withTooltip = false, t }: Props): React.ReactElement<Props> {
function MessageSignature ({ message: { args, mutates, name, returnType }, params = [], asConstructor = false, withTooltip = false, t }: Props): React.ReactElement<Props> {
return (
<Signature>
<Name>
@ -56,7 +57,13 @@ function MessageSignature ({ message: { args, mutates, name, returnType }, asCon
{name}:
{' '}
<Type>
{displayType(type)}
{params && params[index]
? (
<b>
{params[index].toString()}
</b>
)
: displayType(type)}
</Type>
{index < args.length - 1 && ', '}
</React.Fragment>

View File

@ -2,7 +2,7 @@
// 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 { ContractABIMessage } from '@polkadot/api-contract/types';
import { I18nProps } from '@polkadot/react-components/types';
import { Button } from '@polkadot/react-components';
@ -10,7 +10,7 @@ import React from 'react';
import styled from 'styled-components';
import { Abi } from '@polkadot/api-contract';
import Icon from './Icon';
import IconLink from './IconLink';
import MessageSignature from './MessageSignature';
import translate from './translate';
import { classes } from './util';
@ -52,7 +52,7 @@ function onSelectConstructor (props: Props, index: number): () => void {
};
}
function renderItem (props: Props, message: ContractABIMethod, index: number, asConstructor = false): React.ReactNode {
function renderItem (props: Props, message: ContractABIMessage, index: number, asConstructor = false): React.ReactNode {
const { docs = [], name } = message;
return (
@ -124,19 +124,18 @@ function renderMessage (props: Props, index: number): React.ReactNode {
}
function Messages (props: Props): React.ReactElement<Props> {
const { className, contractAbi: { abi: { contract: { constructors, messages } } }, isLabelled, isRemovable, onRemove = NOOP, withConstructors } = props;
const { className, contractAbi: { abi: { contract: { constructors, messages } } }, isLabelled, isRemovable, onRemove = NOOP, withConstructors, t } = props;
return (
<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 && (
<a
<IconLink
label={t('Remove ABI')}
icon='remove'
className='remove-abi'
onClick={onRemove}
>
<Icon name='remove' />
Remove ABI
</a>
/>
)}
</div>
);
@ -150,7 +149,7 @@ export default translate(styled(Messages)`
.remove-abi {
float: right;
&:hover {
&:hover, &:hover :not(i) {
text-decoration: underline;
}
}

View File

@ -264,7 +264,7 @@ export default class Row<P extends RowProps, S extends RowState> extends React.P
return (
<span className='editSpan'>
<Button
className='iconButton'
className='icon-button'
icon='edit'
size='mini'
isPrimary

View File

@ -33,6 +33,7 @@ export { default as FilterOverlay } from './FilterOverlay';
export { default as Forget } from './Forget';
export { default as HelpOverlay } from './HelpOverlay';
export { default as Icon } from './Icon';
export { default as IconLink } from './IconLink';
export { default as IdentityIcon } from './IdentityIcon';
export { default as InfoForInput } from './InfoForInput';
export { default as Input } from './Input';

View File

@ -44,8 +44,11 @@ export default css`
margin: 0;
}
button.ui.icon.iconButton {
padding: 0em 0em .3em .3em !important;
button.ui.icon.icon-button {
padding-top: 0;
padding-right: 0;
padding-bottom: 0.3em;
padding-left: 0.3em;
color: #2e86ab !important;
background: none !important;
}

View File

@ -2016,45 +2016,45 @@
once "^1.4.0"
universal-user-agent "^4.0.0"
"@polkadot/api-contract@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-0.96.0-beta.16.tgz#0c46e6b69d4dd8b5f3327b1d0ef61db6ec27ceba"
integrity sha512-kfaypjOd/YjpUyzxZH6WGW8rtj6rbevpCIDmWQaqYg5c4RpC1JO0iFzx6Q4vRLRwnRGZorygKdY7rTrI/Yh3lw==
"@polkadot/api-contract@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-0.96.0-beta.17.tgz#1f3465ff8fe4741833bb4489dd9ba68e63e81fa2"
integrity sha512-Y17MlyjmTP1OF42v96kj7ZUS+E9E2Op0z80u8M108Nss8YR28V7AHMh3X2X3PPEW91vk9vjRFxVquRRuVlNt7g==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/types" "^0.96.0-beta.16"
"@polkadot/types" "^0.96.0-beta.17"
"@polkadot/api-derive@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-0.96.0-beta.16.tgz#2e8e22a18c5ea78d013ac3ddf6b6b598b35bdbcf"
integrity sha512-HdgynIq6EeggVpNWndZe9S+3UIKZGFUyQf+NDjsP0On9XI1E3Q+3KQRduRVjsgMCo4bQSTp9CTUT9Fdc0EbBjQ==
"@polkadot/api-derive@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-0.96.0-beta.17.tgz#1ffcf15534c91adcadca88cb68f3e8ce6ac94535"
integrity sha512-QzO+26/Z4cSYwBYHsd7/Xq9b//XXR083sa7S9fOmBVeMOYj4rtXmmzzcl4k28byqFvUIT0Izt73m9aw8fSADKQ==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/api" "^0.96.0-beta.16"
"@polkadot/types" "^0.96.0-beta.16"
"@polkadot/api" "^0.96.0-beta.17"
"@polkadot/types" "^0.96.0-beta.17"
"@polkadot/api-metadata@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/api-metadata/-/api-metadata-0.96.0-beta.16.tgz#6722617e0a26ce18d70533f26a72f3d7bc8d87d3"
integrity sha512-xNGoO0IgaNv4NkFWIsXHsbi5Hks55Alz6GcPH1rgeFAq8odGm+Dosbpg/N1ftPHV0v/hyy/e9bqJDpGYwUD8+A==
"@polkadot/api-metadata@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/api-metadata/-/api-metadata-0.96.0-beta.17.tgz#4039943c18dbf7b143e48c2ee95365396e137b05"
integrity sha512-j9JKQuH114RcCSsE2X6NWfjPZgyLRn2plFnc6ZKB6c3rFbfDYwVtazAqaRi/IpOLnFRj+h1Rfapbu1ihz6q8nw==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/types" "^0.96.0-beta.16"
"@polkadot/types" "^0.96.0-beta.17"
"@polkadot/util" "^1.6.1"
"@polkadot/util-crypto" "^1.6.1"
"@polkadot/api@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-0.96.0-beta.16.tgz#cf6a5b20a097743246fc098b25b9a4195a6e1982"
integrity sha512-qIEbARUbWEoSPnLJmLvDNbSctE9ilYKtn3QzBD4rnidvcLi+xFG9YvDvc0S8KojK5DtkQK6FaqW/Tt2E8gCUsg==
"@polkadot/api@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-0.96.0-beta.17.tgz#ecff2ccd549d16f297683c69ab23fe0f84d1b318"
integrity sha512-E+0wSfUMuQrEotxfqeveqJaWoX+zXBMhHN/HesCdMFTEHcm5wlD7N+tfAY3yg3nzWQkDKlao728Y6fqsnFyKDQ==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/api-derive" "^0.96.0-beta.16"
"@polkadot/api-metadata" "^0.96.0-beta.16"
"@polkadot/api-derive" "^0.96.0-beta.17"
"@polkadot/api-metadata" "^0.96.0-beta.17"
"@polkadot/keyring" "^1.6.1"
"@polkadot/rpc-core" "^0.96.0-beta.16"
"@polkadot/rpc-provider" "^0.96.0-beta.16"
"@polkadot/types" "^0.96.0-beta.16"
"@polkadot/rpc-core" "^0.96.0-beta.17"
"@polkadot/rpc-provider" "^0.96.0-beta.17"
"@polkadot/types" "^0.96.0-beta.17"
"@polkadot/util-crypto" "^1.6.1"
"@polkadot/dev-react@^0.32.0-beta.12":
@ -2156,10 +2156,10 @@
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/jsonrpc@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-0.96.0-beta.16.tgz#b6593613aab8921f4b282d4dd2bd9b15be44b1f0"
integrity sha512-2L0wfsZfIJm8iSMT107d71uUmbHepkIK08PKmAXxZ36cak7Iy7nkq+oPA1I4vxghgthqdSypoikN/l8S4Cu3dw==
"@polkadot/jsonrpc@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-0.96.0-beta.17.tgz#888249f017db65ddda294ef1a7277c67875771a0"
integrity sha512-cPqSjZrMEGYAwoxSyqth/P9EX7l8frWO1psH2AcK8zQs2/7PzGVJHoqMXDfBz541d5mm6egGkJJ1AvHc37Wxvg==
dependencies:
"@babel/runtime" "^7.6.3"
@ -2197,25 +2197,25 @@
qrcode-generator "^1.4.4"
react-qr-reader "^2.2.1"
"@polkadot/rpc-core@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-0.96.0-beta.16.tgz#3106dc3c3ed541f81096012221f8cf35a4d09603"
integrity sha512-El/XwN7hVoYDD3tJcuFEWJYYLKZGtBxGDbGvbSy7LmYFScsT5L96DuDIMepLrjy1XK1T7hAyvFR70Ed+PEx7Gg==
"@polkadot/rpc-core@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-0.96.0-beta.17.tgz#474718a9f77661f0fecd46042666cefa6251f8cd"
integrity sha512-d50fmHsz62TB87k6pTUQKyNUFHp+k5Mts7VsmI0AxCWTyfwfeWVJM7TBqSIeHNcs6nW9BB1wwWvgoHUXLOOT6Q==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/jsonrpc" "^0.96.0-beta.16"
"@polkadot/rpc-provider" "^0.96.0-beta.16"
"@polkadot/types" "^0.96.0-beta.16"
"@polkadot/jsonrpc" "^0.96.0-beta.17"
"@polkadot/rpc-provider" "^0.96.0-beta.17"
"@polkadot/types" "^0.96.0-beta.17"
"@polkadot/util" "^1.6.1"
rxjs "^6.5.3"
"@polkadot/rpc-provider@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-0.96.0-beta.16.tgz#884225d5b05bdd18551bba2fcc12224412b4f391"
integrity sha512-3B3aepAh5Nyb9bATqqKgyy4Y6Ujj8uRF4rmRlSAaijuMHJAEgrHx2yoHVPNQIyGZlRdPnG2Nr4zs/J+ewbO2zA==
"@polkadot/rpc-provider@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-0.96.0-beta.17.tgz#3c3bf2b94f22993971e26b8c361c30b4deaf010e"
integrity sha512-zrIk4bJUbec9WCFu4Ry/qXoR9wUe2ggEcYSMDuvbCZZaKSpNzk2/qBBcAzPriLMUy0Kykb23xjnxoQehj+Wvag==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/api-metadata" "^0.96.0-beta.16"
"@polkadot/api-metadata" "^0.96.0-beta.17"
"@polkadot/util" "^1.6.1"
"@polkadot/util-crypto" "^1.6.1"
"@types/nock" "^11.1.0"
@ -2230,10 +2230,10 @@
dependencies:
"@types/chrome" "^0.0.91"
"@polkadot/types@^0.96.0-beta.16":
version "0.96.0-beta.16"
resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-0.96.0-beta.16.tgz#eb843128c720ab284dc79c88c9f22b042b2c7d58"
integrity sha512-SahHunUp9q2HeYs0PIIGRUF/7I0KtG1CWp9Z4nj0kwEqs440oU+Dc/BqHc4lMR5OIU5plPdrzZEiEV6trI79Xw==
"@polkadot/types@^0.96.0-beta.17":
version "0.96.0-beta.17"
resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-0.96.0-beta.17.tgz#5b5804d803d48f0774e89a9b14bb30226c045aca"
integrity sha512-9iV8H1bJbFbIyxDZLLbZ4buVG1weHksuvM8AzEWu9BxPgNGigUrxoRLaUC02qjmPKPfhwCQ4Q2XS8NfaoPiCcA==
dependencies:
"@babel/runtime" "^7.6.3"
"@polkadot/util" "^1.6.1"