@polkadot/types/interfaces#Call TypeScript Examples

The following examples show how to use @polkadot/types/interfaces#Call. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: useWeight.ts    From crust-apps with Apache License 2.0 6 votes vote down vote up
// for a given call, calculate the weight
export function useWeight (call?: Call | null): [BN, number] {
  const { api } = useApi();
  const mountedRef = useIsMountedRef();
  const [state, setState] = useState(EMPTY_STATE);

  useEffect((): void => {
    if (call) {
      api.tx(call)
        .paymentInfo(ZERO_ACCOUNT)
        .then(({ weight }) => mountedRef.current && setState([weight, call.encodedLength]))
        .catch(console.error);
    } else {
      setState(EMPTY_STATE);
    }
  }, [api, call, mountedRef]);

  return state;
}
Example #2
Source File: Address.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function findCall (tx: Call | SubmittableExtrinsic<'promise'>): { method: string; section: string } {
  try {
    const { method, section } = tx.registry.findMetaCall(tx.callIndex);

    return { method, section };
  } catch (error) {
    return { method: 'unknown', section: 'unknown' };
  }
}
Example #3
Source File: Address.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function filterProxies (allAccounts: string[], tx: Call | SubmittableExtrinsic<'promise'>, proxies: [string, ProxyType][]): string[] {
  // check an array of calls to all have proxies as the address
  const checkCalls = (address: string, txs: Call[]): boolean =>
    !txs.some((tx) => !filterProxies(allAccounts, tx, proxies).includes(address));

  // get the call info
  const { method, section } = findCall(tx);

  return proxies
    .filter(([address, proxy]): boolean => {
      if (!allAccounts.includes(address)) {
        return false;
      }

      switch (proxy.toString()) {
        case 'Any':
          return true;
        case 'Governance':
          return ['council', 'democracy', 'elections', 'electionsPhragmen', 'poll', 'society', 'technicalCommittee', 'tips', 'treasury'].includes(section);
        case 'IdentityJudgement':
          return section === 'identity' && method === 'provideJudgement';
        case 'NonTransfer':
          return !(section === 'balances' || (section === 'indices' && method === 'transfer') || (section === 'vesting' && method === 'vestedTransfer'));
        case 'Staking':
          return section === 'staking' ||
            (section === 'utility' && (
              (method === 'batch' && checkCalls(address, tx.args[0] as Vec<Call>)) ||
              (method === 'asLimitedSub' && checkCalls(address, [tx.args[0] as Call]))
            ));
        case 'SudoBalances':
          return (section === 'sudo' && (method === 'sudo' && findCall(tx.args[0] as Call).section === 'balances')) ||
            (section === 'utility' && (method === 'batch' && checkCalls(address, tx.args[0] as Vec<Call>)));
        default:
          return false;
      }
    })
    .map(([address]) => address);
}
Example #4
Source File: multisig.ts    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
txMethodDescription = (
  data: Call | undefined | null,
  api: ApiPromise | null
): { name: string; type: string; value: string }[] => {
  if (!data || !api) {
    return [];
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const call = data.toHuman() as any;

  if (call) {
    const meta = api.tx[call.section][call.method].meta.toJSON();
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const callJson = data.toJSON() as any;
    const params = meta.args as { name: string; type: string }[];

    return params.map(({ name, type }) => {
      const value = callJson.args[name];

      return {
        name,
        type,
        value: typeof value === 'object' ? Object.values(value).join(' ') : value,
      };
    });
  }

  return [];
}
Example #5
Source File: multisig.ts    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
txMethod = (data: Call | undefined | null, api: ApiPromise | null): string => {
  if (!data || !api) {
    return '-';
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const call = data?.toHuman() as any;

  if (call) {
    const meta = api.tx[call.section][call.method].meta.toJSON();

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return `${call.section}.${call.method}(${(meta.args as any[]).map((item) => item.name).join(',')})`;
  }

  return '-';
}
Example #6
Source File: collective.ts    From commonwealth with GNU General Public License v3.0 6 votes vote down vote up
public createTx(
    author: SubstrateAccount,
    threshold: number,
    action: Call,
    length?: number,
    fromTechnicalCommittee?: boolean,
  ) {
    // TODO: check council status
    const title = this._Chain.methodToTitle(action);

    // handle differing versions of substrate API
    const txFunc = fromTechnicalCommittee
      ? ((api: ApiPromise) => api.tx.technicalCommittee.propose.meta.args.length === 3
        ? api.tx.technicalCommittee.propose(threshold, action, length)
        : (api.tx.technicalCommittee.propose as any)(threshold, action))
      : ((api: ApiPromise) => api.tx.council.propose.meta.args.length === 3
        ? api.tx.council.propose(threshold, action, length)
        : (api.tx.council.propose as any)(threshold, action, null));
    return this._Chain.createTXModalData(
      author,
      txFunc,
      'createCouncilMotion',
      title
    );
  }
Example #7
Source File: Address.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function findCall(tx: Call | SubmittableExtrinsic<'promise'>): { method: string; section: string } {
  try {
    const { method, section } = tx.registry.findMetaCall(tx.callIndex);

    return { method, section };
  } catch (error) {
    return { method: 'unknown', section: 'unknown' };
  }
}
Example #8
Source File: democracy_proposals.ts    From commonwealth with GNU General Public License v3.0 6 votes vote down vote up
public notePreimage(author: SubstrateAccount, action: Call, encodedProposal: string) {
    const title = this._Chain.methodToTitle(action);
    return this._Chain.createTXModalData(
      author,
      (api: ApiPromise) => api.tx.democracy.notePreimage(encodedProposal),
      'notePreimage',
      title
    );
  }
Example #9
Source File: democracy_proposals.ts    From commonwealth with GNU General Public License v3.0 6 votes vote down vote up
public noteImminentPreimage(author: SubstrateAccount, action: Call, encodedProposal: string) {
    const title = this._Chain.methodToTitle(action);
    return this._Chain.createTXModalData(
      author,
      (api: ApiPromise) => api.tx.democracy.notePreimage(encodedProposal),
      'noteImminentPreimage',
      title,
    );
  }
Example #10
Source File: democracy_referendum.ts    From commonwealth with GNU General Public License v3.0 6 votes vote down vote up
// public async proxyVoteTx(vote: BinaryVote<SubstrateCoin>) {
  //   const proxyFor = await (vote.account as SubstrateAccount).proxyFor.pipe(first()).toPromise();
  //   if (!proxyFor) {
  //     throw new Error('not a proxy');
  //   }
  //   const srmlVote = this._Chain.createType('Vote', {
  //     aye: vote.choice,
  //     conviction: convictionToSubstrate(this._Chain, weightToConviction(vote.weight)),
  //   });
  //   return this._Chain.createTXModalData(
  //     vote.account as SubstrateAccount,
  //     (api: ApiPromise) => api.tx.democracy.proxyVote(this.data.index, srmlVote),
  //     'submitProxyDemocracyVote',
  //     this.title
  //   );
  // }

  public async notePreimage(author: SubstrateAccount, action: Call) {
    const txFunc = (api: ApiPromise) => api.tx.democracy.notePreimage(action.toHex());
    return this._Chain.createTXModalData(
      author,
      txFunc,
      'notePreimage',
      this._Chain.methodToTitle(action),
    );
  }
Example #11
Source File: useWeight.ts    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
// for a given call, calculate the weight
export function useWeight(call?: Call | null): [BN, number] {
  const { api } = useApi();
  const mountedRef = useIsMountedRef();
  const [state, setState] = useState(EMPTY_STATE);

  useEffect((): void => {
    if (call && isFunction(api.rpc.payment?.queryInfo)) {
      api
        .tx(call)
        .paymentInfo(ZERO_ACCOUNT)
        .then(({ weight }) => mountedRef.current && setState([weight, call.encodedLength]))
        .catch(console.error);
    } else {
      setState(EMPTY_STATE);
    }
  }, [api, call, mountedRef]);

  return state;
}
Example #12
Source File: types.ts    From commonwealth with GNU General Public License v3.0 6 votes vote down vote up
export function formatCall(c: Call | { section: string, method: string, args: string[] }): string {
  // build args string
  const args: (string | Codec)[] = c.args;
  const argsStr = args.map((v: Codec | string): string => {
    if (!v) return '[unknown]';
    const vStr = v.toString();
    if (vStr.length < 16) return vStr;
    return `${vStr.slice(0, 15)}…`;
  }).join(', ');

  // finish format
  return `${c.section}.${c.method}(${argsStr})`;
}
Example #13
Source File: multisig.ts    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
txDoc = (data: Call | undefined | null): string => {
  if (!data) {
    return '-';
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return (data?.meta as any).get('documentation').toHuman().join('');
}
Example #14
Source File: Address.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
function filterProxies(
  allAccounts: string[],
  tx: Call | SubmittableExtrinsic<'promise'>,
  proxies: [string, ProxyType][]
): string[] {
  // check an array of calls to all have proxies as the address
  const checkCalls = (address: string, txs: Call[]): boolean =>
    !txs.some((tx) => !filterProxies(allAccounts, tx, proxies).includes(address));

  // get the call info
  const { method, section } = findCall(tx);

  return proxies
    .filter(([address, proxy]): boolean => {
      if (!allAccounts.includes(address)) {
        return false;
      }

      switch (proxy.toString()) {
        case 'Any':
          return true;
        case 'Governance':
          return [
            'council',
            'democracy',
            'elections',
            'electionsPhragmen',
            'phragmenElection',
            'poll',
            'society',
            'technicalCommittee',
            'tips',
            'treasury',
          ].includes(section);
        case 'IdentityJudgement':
          return section === 'identity' && method === 'provideJudgement';
        case 'NonTransfer':
          return !(
            section === 'balances' ||
            (section === 'indices' && method === 'transfer') ||
            (section === 'vesting' && method === 'vestedTransfer')
          );
        case 'Staking':
          return (
            section === 'staking' ||
            (section === 'utility' &&
              ((method === 'batch' && checkCalls(address, tx.args[0] as Vec<Call>)) ||
                (method === 'asLimitedSub' && checkCalls(address, [tx.args[0] as Call]))))
          );
        case 'SudoBalances':
          return (
            (section === 'sudo' && method === 'sudo' && findCall(tx.args[0] as Call).section === 'balances') ||
            (section === 'utility' && method === 'batch' && checkCalls(address, tx.args[0] as Vec<Call>))
          );
        default:
          return false;
      }
    })
    .map(([address]) => address);
}
Example #15
Source File: shared.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public getTxMethod(mod: string, func: string, args: any[]): Call {
    const result = this.api.tx[mod][func];
    if (!result) {
      throw new Error(`unsupported transaction: ${mod}::${func}`);
    }
    return this.api.findCall(result.callIndex)(...args);
  }
Example #16
Source File: identities.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
// requires ForceOrigin or Root!
  public killIdentityMethod(target: SubstrateIdentity): Call {
    if (!target.exists) {
      throw new Error('target identity does not exist');
    }
    const func = this._Chain.getTxMethod('identity', 'killIdentity', [ target.account.address ]);
    return func;
  }
Example #17
Source File: identities.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
// requires RegistrarOrigin or Root!
  public addRegistrarMethod(account: SubstrateAccount): Call {
    const func = this._Chain.getTxMethod('identity', 'addRegistrar', [ account.address ]);
    return func;
  }
Example #18
Source File: democracy_referendum.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public noteImminentPreimage(author: SubstrateAccount, action: Call) {
    return this._Chain.createTXModalData(
      author,
      (api: ApiPromise) => api.tx.democracy.noteImminentPreimage(action.toHex()),
      'noteImminentPreimage',
      this._Chain.methodToTitle(action),
    );
  }
Example #19
Source File: democracy_proposals.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public async createTx(author: SubstrateAccount, action: Call, proposalHash: Hash, deposit: SubstrateCoin) {
    const txFunc = (api: ApiPromise) => api.tx.democracy.propose(proposalHash, deposit.asBN);
    const title = this._Chain.methodToTitle(action);
    return this._Chain.createTXModalData(author, txFunc, 'createDemocracyProposal', title);
  }
Example #20
Source File: collective.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public createExternalProposalDefault(author: SubstrateAccount, threshold: number, action: Call, length) {
    // only on kusama
    const func = this._Chain.getTxMethod('democracy', 'externalProposeDefault', [ action.hash ]);
    return this.createTx(author, threshold, func, length);
  }
Example #21
Source File: collective.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public createExternalProposalMajority(author: SubstrateAccount, threshold: number, action: Call, length) {
    const func = this._Chain.getTxMethod('democracy', 'externalProposeMajority', [ action.hash ]);
    return this.createTx(author, threshold, func, length);
  }
Example #22
Source File: collective.ts    From commonwealth with GNU General Public License v3.0 5 votes vote down vote up
public createExternalProposal(author: SubstrateAccount, threshold: number, action: Call, length: number) {
    const func = this._Chain.getTxMethod('democracy', 'externalPropose', [ action.hash ]);
    return this.createTx(author, threshold, func, length);
  }
Example #23
Source File: TxApprove.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
// eslint-disable-next-line complexity
export function TxApprove({ entry, txSpy, onOperation }: TxOperationComponentProps) {
  const { t } = useTranslation();
  const { accounts, api } = useApi();
  const [getApproveTx] = useMultiApprove();
  const { queueExtrinsic } = useContext(StatusContext);
  const [getUnapprovedInjectedList] = useUnapprovedAccounts();
  const { setIsPageLock, queryInProgress, refreshConfirmedAccount } = useMultisigContext();
  const unapprovedAddresses = getUnapprovedInjectedList(entry);
  const availableAccounts = (accounts ?? []).filter((extAddr) => unapprovedAddresses.includes(extAddr.address));
  const [inputCallDataModalVisible, setInputCallDataModalVisible] = useState(false);

  const handleApprove = useCallback(
    (accountId: string, target: Entry) => {
      setIsPageLock(true);
      setInputCallDataModalVisible(false);

      getApproveTx(target, accountId).then((tx) => {
        const queueTx: PartialQueueTxExtrinsic = {
          extrinsic: tx,
          accountId,
          txSuccessCb: () => {
            makeSure(txSpy)(null);
            queryInProgress();
            setTimeout(() => {
              refreshConfirmedAccount();
              // eslint-disable-next-line no-magic-numbers
            }, 6000);
          },
        };

        queueExtrinsic(queueTx);
        setIsPageLock(false);
        makeSure(txSpy)(queueTx);
      });

      makeSure(onOperation)({
        entry: target,
        type: 'approve',
        accounts: availableAccounts.map((account) => account.address),
      });
    },
    [
      availableAccounts,
      getApproveTx,
      onOperation,
      queryInProgress,
      queueExtrinsic,
      refreshConfirmedAccount,
      setIsPageLock,
      txSpy,
    ]
  );

  if (!entry.callHash || !entry.callData) {
    return (
      <>
        <Button onClick={() => setInputCallDataModalVisible(true)}>{t('approve')}</Button>

        <InputCallDataModal
          visible={inputCallDataModalVisible}
          onCancel={() => setInputCallDataModalVisible(false)}
          availableAccounts={availableAccounts}
          callHash={entry.callHash || ''}
          onConfirm={(selectedAddress, callData) => {
            try {
              const callDataObj = api?.registry.createType('Call', callData) as Call;
              handleApprove(selectedAddress, { ...entry, callHash: entry.callHash, callData: callDataObj });
            } catch {
              message.error(t('decode call data error'));
            }
          }}
        />
      </>
    );
  } else if (availableAccounts.length === 1) {
    return <Button onClick={() => handleApprove(availableAccounts[0].address, entry)}>{t('approve')}</Button>;
  } else {
    return (
      <Popover
        content={
          <Radio.Group
            onChange={(event) => {
              handleApprove(event.target.value, entry);
            }}
            value={null}
          >
            <Space direction="vertical">
              {availableAccounts.map((acc) => (
                <Radio.Button
                  value={acc.address}
                  key={acc.address}
                  className="max-w-xs md:max-w-full overflow-hidden overflow-ellipsis whitespace-nowrap"
                >
                  {acc.meta.name} - {acc.address}
                </Radio.Button>
              ))}
            </Space>
          </Radio.Group>
        }
        title={t('Select approve account')}
        trigger="focus"
      >
        <Button>{t('approve')}</Button>
      </Popover>
    );
  }
}
Example #24
Source File: MultisigApprove.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function MultisigApprove ({ className = '', onClose, ongoing, threshold, who }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const { api } = useApi();
  const { allAccounts } = useAccounts();
  const [callData, setCallData] = useState<Call | null>(null);
  const [callWeight] = useWeight(callData);
  const [hash, setHash] = useState<string | null>(ongoing[0][0].toHex());
  const [{ isMultiCall, multisig }, setMultisig] = useState<MultiInfo>({ isMultiCall: false, multisig: null });
  const [isCallOverride, setCallOverride] = useState(true);
  const [others, setOthers] = useState<AccountId[]>([]);
  const [signatory, setSignatory] = useState<string | null>(null);
  const [whoFilter, setWhoFilter] = useState<string[]>([]);
  const [type, setType] = useState<string | null>('aye');
  const [tx, setTx] = useState<SubmittableExtrinsic<'promise'> | null>(null);
  const calltypes = useMemo<Option[]>(
    () => [
      { text: t<string>('Approve this call hash'), value: 'aye' },
      { text: t<string>('Cancel this call hash'), value: 'nay' }
    ],
    [t]
  );
  const hashes = useMemo<Option[]>(
    () => ongoing.map(([h]) => ({ text: h.toHex(), value: h.toHex() })),
    [ongoing]
  );

  // filter the current multisig by supplied hash
  useEffect((): void => {
    const [, multisig] = ongoing.find(([h]) => h.eq(hash)) || [null, null];

    setMultisig({
      isMultiCall: !!multisig && (multisig.approvals.length + 1) >= threshold,
      multisig
    });
    setCallData(null);
  }, [hash, ongoing, threshold]);

  // the others are all the who elements, without the current signatory (re-encoded)
  useEffect((): void => {
    setOthers(
      who
        .map((w) => api.createType('AccountId', w))
        .filter((w) => !w.eq(signatory))
    );
  }, [api, signatory, who]);

  // Filter the who by those not approved yet that is an actual account we own. In the case of
  // rejections, we defer to the the first approver, since he is the only one to send the cancel
  // On reaching threshold, we include all possible signatories in the list
  useEffect((): void => {
    const hasThreshold = multisig && (multisig.approvals.length >= threshold);

    setWhoFilter(
      who
        .map((w) => api.createType('AccountId', w).toString())
        .filter((w) => allAccounts.some((a) => a === w) && multisig && (
          type === 'nay'
            ? multisig.approvals[0].eq(w)
            : hasThreshold || !multisig.approvals.some((a) => a.eq(w))
        ))
    );
  }, [api, allAccounts, multisig, threshold, type, who]);

  // based on the type, multisig, others create the tx. This can be either an approval or final call
  useEffect((): void => {
    const multiMod = api.tx.multisig || api.tx.utility;

    setTx(() =>
      hash && multisig
        ? type === 'aye'
          ? isMultiCall && isCallOverride
            ? callData
              ? multiMod.asMulti.meta.args.length === 6
                ? multiMod.asMulti(threshold, others, multisig.when, callData.toHex(), false, callWeight)
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore (We are doing toHex here since we have a Vec<u8> input)
                : multiMod.asMulti(threshold, others, multisig.when, callData)
              : null
            : multiMod.approveAsMulti.meta.args.length === 5
              ? multiMod.approveAsMulti(threshold, others, multisig.when, hash, callWeight)
              // eslint-disable-next-line @typescript-eslint/ban-ts-comment
              // @ts-ignore
              : multiMod.approveAsMulti(threshold, others, multisig.when, hash)
          : multiMod.cancelAsMulti(threshold, others, multisig.when, hash)
        : null
    );
  }, [api, callData, callWeight, hash, isCallOverride, isMultiCall, others, multisig, threshold, type]);

  // when the actual call input changes, create a call and set it
  const _setCallData = useCallback(
    (callHex: string): void => {
      try {
        assert(isHex(callHex), 'Hex call data required');

        const callData = api.createType('Call', callHex);

        setCallData(
          callData.hash.eq(hash)
            ? callData
            : null
        );
      } catch (error) {
        setCallData(null);
      }
    },
    [api, hash]
  );

  const isAye = type === 'aye';

  return (
    <Modal
      className={className}
      header={t<string>('Pending call hashes')}
      size='large'
    >
      <Modal.Content>
        <Modal.Columns hint={t('The call hash from the list of available and unapproved calls.')}>
          <Dropdown
            help={t<string>('The call hashes that have not been executed as of yet.')}
            label={t<string>('pending hashes')}
            onChange={setHash}
            options={hashes}
            value={hash}
          />
        </Modal.Columns>
        <Modal.Columns hint={t('The operation type to apply. For approvals both non-final and final approvals are supported.')}>
          <Dropdown
            help={t<string>('Either approve or reject this call.')}
            label={t<string>('approval type')}
            onChange={setType}
            options={calltypes}
            value={type}
          />
        </Modal.Columns>
        {whoFilter.length !== 0 && (
          <>
            <Modal.Columns hint={t('For approvals outstanding approvers will be shown, for hashes that should be cancelled the first approver is required.')}>
              <InputAddress
                filter={whoFilter}
                help={t<string>('The signatory to send the approval/cancel from')}
                label={t<string>('signatory')}
                onChange={setSignatory}
              />
            </Modal.Columns>
            {type === 'aye' && isMultiCall && (
              <>
                {isCallOverride && (
                  <Modal.Columns hint={t('The call data for this transaction matching the hash. Once sent, the multisig will be executed against this.')}>
                    <Input
                      autoFocus
                      help={t('For final approvals, the actual full call data is required to execute the transaction')}
                      isError={!callData}
                      label={t('call data for final approval')}
                      onChange={_setCallData}
                    />
                  </Modal.Columns>
                )}
                <Modal.Columns hint={t('Swap to a non-executing approval type, with subsequent calls providing the actual call data.')}>
                  <Toggle
                    className='tipToggle'
                    label={
                      isMultiCall
                        ? t<string>('Multisig message with call (for final approval)')
                        : t<string>('Multisig approval with hash (non-final approval)')
                    }
                    onChange={setCallOverride}
                    value={isCallOverride}
                  />
                </Modal.Columns>
              </>
            )}
          </>
        )}
      </Modal.Content>
      <Modal.Actions onCancel={onClose}>
        <TxButton
          accountId={signatory}
          extrinsic={tx}
          icon={isAye ? 'check' : 'times'}
          isDisabled={!tx || (isAye && !whoFilter.length)}
          label={isAye ? 'Approve' : 'Reject'}
          onStart={onClose}
        />
      </Modal.Actions>
    </Modal>
  );
}