@polkadot/util#formatBalance TypeScript Examples

The following examples show how to use @polkadot/util#formatBalance. 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: InputCandyNumber.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function getSiPowers (si: SiDef | null): [BN, number, number] {
  if (!si) {
    return [BN_ZERO, 0, 0];
  }

  const basePower = formatBalance.getDefaults().decimals;

  return [new BN(basePower + si.power), basePower, si.power];
}
Example #2
Source File: substrate.ts    From subsocial-js with GNU General Public License v3.0 6 votes vote down vote up
getSubstrateApi = async (nodeUrl?: string) => {
  if (api) return api

  const rpcEndpoint = nodeUrl || 'ws://127.0.0.1:9944/';
  const provider = new WsProvider(rpcEndpoint);

  logger.info(`Connecting to Substrate node at ${rpcEndpoint}...`);
  api = new ApiPromise({ provider, typesBundle })
  await api.isReady

  const properties = await api.rpc.system.properties() as ChainProperties
  const tokenSymbol = properties.tokenSymbol.unwrapOr(undefined)?.map((x: Text) => x.toString());
  const tokenDecimals = properties.tokenDecimals.unwrapOr(undefined)?.map((x: U32) => x.toNumber());

  registry.setChainProperties(properties)

  formatBalance.setDefaults({
    decimals: tokenDecimals,
    unit: tokenSymbol
  });

  return api
}
Example #3
Source File: InputBalance.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function reformat (value: string | BN, isDisabled?: boolean): string {
  if (isBn(value)) {
    // format for 4 decimals (align with util)
    const valStr = value
      .mul(BN_TEN_THOUSAND)
      .div(BN_TEN.pow(new BN(formatBalance.getDefaults().decimals)))
      .toString()
      .padStart(5, '0'); // 4 after decimal, 1 before, min 5

    // dive using string format (the value may be too large for 2^53-1)
    let fmt = `${valStr.substr(0, valStr.length - 4)}.${valStr.slice(-4)}`;

    // remove all trailing 0's until the decimal
    while (fmt.length !== 1 && ['.', '0'].includes(fmt[fmt.length - 1])) {
      const isLast = fmt.endsWith('.');

      fmt = fmt.substr(0, fmt.length - 1);

      if (isLast) {
        break;
      }
    }

    return fmt;
  }

  return formatBalance(value, { forceUnit: '-', withSi: false }).replace(',', isDisabled ? ',' : '');
}
Example #4
Source File: useBalance.ts    From parity-bridges-ui with GNU General Public License v3.0 6 votes vote down vote up
useBalance = (api: ApiPromise, address: string, providedSi: boolean = false): State => {
  const [state, setState] = useMountedState<State>(initValues);
  const { areApiReady } = useLoadingApi();

  const getBalanceCall = useCallback(
    () =>
      api.query.system.account(address, ({ data }): void => {
        setState({
          chainTokens: data.registry.chainTokens[0],
          formattedBalance: formatBalance(data.free, {
            decimals: api.registry.chainDecimals[0],
            withUnit: api.registry.chainTokens[0],
            withSi: providedSi
          }),
          free: data.free
        });
      }),
    [address, api, providedSi, setState]
  );

  useApiSubscription(getBalanceCall, areApiReady && Boolean(address));

  return state as State;
}
Example #5
Source File: index.ts    From sdk with Apache License 2.0 6 votes vote down vote up
function _extractUnbondings(stakingInfo: any, progress: any) {
  if (!stakingInfo?.unlocking || !progress) {
    return { mapped: [], total: BN_ZERO };
  }

  const mapped = stakingInfo.unlocking
    .filter(
      ({ remainingEras, value }) =>
        value.gt(BN_ZERO) && remainingEras.gt(BN_ZERO)
    )
    .map((unlock: any) => [
      unlock,
      unlock.remainingEras
        .sub(BN_ONE)
        .imul(progress.eraLength)
        .iadd(progress.eraLength)
        .isub(progress.eraProgress)
        .toNumber(),
    ]);
  const total = mapped.reduce(
    (total: BN, [{ value }]) => total.iadd(value),
    new BN(0)
  );

  return {
    mapped: mapped.map((i: any) => [
      formatBalance(i[0].value, { forceUnit: "-", withSi: false }),
      i[1],
    ]),
    total,
  };
}
Example #6
Source File: InputCandyNumber.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function getValuesFromBn (valueBn: BN, si: SiDef | null): [string, BN, boolean] {
  const value = si
    ? valueBn.div(BN_TEN.pow(new BN(formatBalance.getDefaults().decimals + si.power))).toString()
    : valueBn.toString();

  return [
    value,
    valueBn,
    true
  ];
}
Example #7
Source File: InputBalance.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function reformat(value?: string | BN, isDisabled?: boolean, siDecimals?: number): [string?, SiDef?] {
  if (!value) {
    return [];
  }

  const decimals = isUndefined(siDecimals) ? formatBalance.getDefaults().decimals : siDecimals;
  const si = isDisabled ? formatBalance.calcSi(value.toString(), decimals) : formatBalance.findSi('-');

  return [
    formatBalance(value, { decimals, forceUnit: si.value, withSi: false }).replace(',', isDisabled ? ',' : ''),
    si,
  ];
}
Example #8
Source File: InputCsmBalance.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function reformat (value: string | BN, isDisabled?: boolean): string {
  if (isBn(value)) {
    let fmt = (value.mul(BN_THOUSAND).div(BN_TEN.pow(new BN(formatBalance.getDefaults().decimals))).toNumber() / 1000).toFixed(3);

    while (fmt.length !== 1 && ['.', '0'].includes(fmt[fmt.length - 1])) {
      const isLast = fmt.endsWith('.');

      fmt = fmt.substr(0, fmt.length - 1);

      if (isLast) {
        break;
      }
    }

    return fmt;
  }

  return formatBalance(value, { forceUnit: '-', withSi: false }).replace(',', isDisabled ? ',' : '');
}
Example #9
Source File: InputNumber.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function getSiPowers(si: SiDef | null, decimals?: number): [BN, number, number] {
  if (!si) {
    return [BN_ZERO, 0, 0];
  }

  const basePower = isUndefined(decimals) ? formatBalance.getDefaults().decimals : decimals;

  return [new BN(basePower + si.power), basePower, si.power];
}
Example #10
Source File: InputCsmNumber.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function getValuesFromBn (valueBn: BN, si: SiDef | null): [string, BN, boolean] {
  const value = si
    ? valueBn.div(BN_TEN.pow(new BN(formatBalance.getDefaults().decimals + si.power))).toString()
    : valueBn.toString();

  return [
    value,
    valueBn,
    true
  ];
}
Example #11
Source File: embeds.ts    From community-repo with GNU General Public License v3.0 6 votes vote down vote up
getWorkerRewardAmountUpdatedEmbed = (reward: RewardRelationship, member: Membership, 
    blockNumber: number, event: EventRecord): Discord.MessageEmbed => {

    return addCommonProperties(new Discord.MessageEmbed()
        .setTitle(`??? Salary of ${member.handle} updated`)
        .addFields(
            { name: 'Salary', value: formatBalance(reward.amount_per_payout, { withUnit: 'JOY' }), inline: true },
            { name: 'Payout Frequency', value: reward.payout_interval + "", inline: true },
        ), blockNumber, event );
}
Example #12
Source File: FormatBalance.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function applyFormat(
  value: Compact<any> | BN | string,
  [decimals, token]: [number, string],
  withCurrency = true,
  withSi?: boolean,
  _isShort?: boolean,
  labelPost?: string
): React.ReactNode {
  const [prefix, postfix] = formatBalance(value, { decimals, forceUnit: '-', withSi: false }).split('.');
  const isShort = _isShort || (withSi && prefix.length >= K_LENGTH);
  const unitPost = withCurrency ? token : '';

  if (prefix.length > M_LENGTH) {
    const [major, rest] = formatBalance(value, { decimals, withUnit: false }).split('.');
    const minor = rest.substr(0, 4);
    const unit = rest.substr(4);

    return (
      <>
        {major}.<span className="ui--FormatBalance-postfix">{minor}</span>
        <span className="ui--FormatBalance-unit">
          {unit}
          {unit ? unitPost : ` ${unitPost}`}
        </span>
        {labelPost || ''}
      </>
    );
  }

  return createElement(prefix, postfix, unitPost, labelPost, isShort);
}
Example #13
Source File: transferTokens.ts    From community-repo with GNU General Public License v3.0 6 votes vote down vote up
private async initApi(
    apiUri: string = DEFAULT_API_URI,
  ): Promise<ApiPromise> {
    const wsProvider: WsProvider = new WsProvider(apiUri);
    const api = new ApiPromise({
      provider: wsProvider,
      types,
    });
    await api.isReadyOrError;

    // Initializing some api params based on pioneer/packages/react-api/Api.tsx
    const [properties] = await Promise.all([api.rpc.system.properties()]);

    const tokenSymbol = properties.tokenSymbol.unwrap()[0].toString();
    const tokenDecimals = properties.tokenDecimals.unwrap()[0].toNumber();

    // formatBlanace config
    formatBalance.setDefaults({
      decimals: tokenDecimals,
      unit: tokenSymbol,
    });

    return api;
  }
Example #14
Source File: InputCsmNumber.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function getSiPowers (si: SiDef | null): [BN, number, number] {
  if (!si) {
    return [BN_ZERO, 0, 0];
  }

  const basePower = formatBalance.getDefaults().decimals;

  return [new BN(basePower + si.power), basePower, si.power];
}
Example #15
Source File: transferTokens.ts    From community-repo with GNU General Public License v3.0 5 votes vote down vote up
async start(recipient: string, amount: number) {
    console.log('start transfer', recipient, amount);

    await this.init();

    const selectedAccount: NamedKeyringPair =
      await this.getFaucetAccount();

    const amountBN: BN = new BN(amount);

    // Initial validation
    validateAddress(recipient, 'Invalid recipient address');

    const accBalance = await this.getAccountBalancesInfo(selectedAccount.address);

    checkBalance(accBalance, amountBN);

    await this.requestAccountDecoding(selectedAccount);

    console.log(chalk.magentaBright('Estimating fee...'));
    const tx = await this.createTransferTx(recipient, amountBN);
    let estimatedFee: BN = new BN(0);

    try {
      estimatedFee = await this.estimateFee(selectedAccount, tx);
    } catch (e) {
      throw new Error('Could not estimate the fee.');
    }

    const totalAmount: BN = amountBN.add(estimatedFee);
    console.log(
      chalk.magentaBright('Estimated fee:', formatBalance(estimatedFee))
    );
    console.log(
      chalk.magentaBright('Total transfer amount:', formatBalance(totalAmount))
    );

    checkBalance(accBalance, totalAmount);

    try {
      const txHash: Hash = await tx.signAndSend(selectedAccount);
      console.log(chalk.greenBright('Transaction successfully sent!'));
      console.log(chalk.magentaBright('Hash:', txHash.toString()));
    } catch (e) {
      console.error('Could not send the transaction.');
    }
  }
Example #16
Source File: balances.ts    From subsocial-js with GNU General Public License v3.0 5 votes vote down vote up
simpleFormatBalance = (balance: BN | string | number, decimals?: number, currency?: string, withUnit = true) => {
  const { unit, decimals: defaultDecimals } = formatBalance.getDefaults()

  return formatBalance(balance, { decimals: decimals || defaultDecimals, forceUnit: currency || unit, withUnit })
}
Example #17
Source File: DryRun.tsx    From contracts-ui with GNU General Public License v3.0 5 votes vote down vote up
export function DryRun() {
  const { api } = useApi();
  const { dryRunResult } = useInstantiate();

  const dryRunError =
    dryRunResult?.result.isErr && dryRunResult?.result.asErr.isModule
      ? api.registry.findMetaError(dryRunResult?.result.asErr.asModule)
      : null;

  return (
    <SidePanel className="instantiate-outcome" header="Predicted Outcome">
      <div className="body">
        <div className="row">
          <div>Gas Required:</div>
          <div>{dryRunResult?.gasRequired && <>{formatNumber(dryRunResult.gasRequired)}</>}</div>
        </div>
        <div className="row">
          <div>Gas Consumed:</div>
          <div>{dryRunResult?.gasConsumed && <>{formatNumber(dryRunResult.gasConsumed)}</>}</div>
        </div>
        <div className="row">
          <div>Storage Deposit:</div>
          <div>
            {dryRunResult?.storageDeposit &&
              (() => {
                if (dryRunResult.storageDeposit.isCharge) {
                  if (dryRunResult.storageDeposit.asCharge.eqn(0)) {
                    return 'None';
                  }

                  return formatBalance(dryRunResult.storageDeposit.asCharge, { decimals: 12 });
                }

                return 'None';
              })()}
          </div>
        </div>
        <div className="row h-8">
          <div>Contract Address:</div>
          <div>
            {dryRunResult?.result.isOk ? (
              <Account size={26} value={dryRunResult?.result.asOk.accountId.toString()} />
            ) : (
              'None'
            )}
          </div>
        </div>
        <div>
          {dryRunResult?.result.isOk && (
            <div className="validation success font-bold">
              <CheckCircleIcon className="mr-3" />
              The instantiation will be successful.
            </div>
          )}
          {dryRunError && dryRunResult && (
            <>
              <div className="validation error text-mono font-bold items-start">
                <ExclamationCircleIcon className="mr-3" style={{ marginTop: 1 }} />
                <div>
                  <p>{dryRunError.name}</p>
                  <p>{dryRunError.docs}</p>
                </div>
              </div>
              {dryRunResult.debugMessage.length > 0 && (
                <div className="validation error block text-mono break-words pl-4 mt-1">
                  {dryRunResult.debugMessage.toString()}
                </div>
              )}
            </>
          )}
        </div>
      </div>
    </SidePanel>
  );
}
Example #18
Source File: EstimatedFee.tsx    From parity-bridges-ui with GNU General Public License v3.0 5 votes vote down vote up
getFormattedAmount = (fee: string | null, chainDecimals: number, chainTokens: string) =>
  fee
    ? formatBalance(fee, {
        decimals: chainDecimals,
        withUnit: chainTokens,
        withSi: true
      })
    : null
Example #19
Source File: Api.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
// eslint-disable-next-line complexity
async function loadOnReady(
  api: ApiPromise,
  injectedPromise: Promise<InjectedExtension[]>,
  store: KeyringStore | undefined,
  types: Record<string, Record<string, string>>
): Promise<ApiState> {
  registry.register(types);
  const { injectedAccounts, properties, systemChain, systemChainType, systemName, systemVersion } = await retrieve(
    api,
    injectedPromise
  );
  const ss58Format = settings.prefix === -1 ? properties.ss58Format.unwrapOr(DEFAULT_SS58).toNumber() : settings.prefix;
  const tokenSymbol = properties.tokenSymbol.unwrapOr([formatBalance.getDefaults().unit, ...DEFAULT_AUX]);
  const tokenDecimals = properties.tokenDecimals.unwrapOr([DEFAULT_DECIMALS]);
  const isEthereum = ethereumChains.includes(api.runtimeVersion.specName.toString());
  const isDevelopment = systemChainType.isDevelopment || systemChainType.isLocal || isTestChain(systemChain);

  // explicitly override the ss58Format as specified
  registry.setChainProperties(
    registry.createType('ChainProperties', { ss58Format, tokenDecimals, tokenSymbol }) as ChainProperties
  );

  // first setup the UI helpers
  formatBalance.setDefaults({
    decimals: (tokenDecimals as BN[]).map((b) => b.toNumber()),
    unit: tokenSymbol[0].toString(),
  });
  TokenUnit.setAbbr(tokenSymbol[0].toString());

  // finally load the keyring
  const isLoaded = isKeyringLoaded();

  if (!isLoaded) {
    keyring.loadAll(
      {
        isDevelopment,
        ss58Format,
        store,
        type: isEthereum ? 'ethereum' : 'ed25519',
      },
      injectedAccounts
    );
  }

  const defaultSection = Object.keys(api.tx)[0];
  const defaultMethod = Object.keys(api.tx[defaultSection])[0];
  const apiDefaultTx = api.tx[defaultSection][defaultMethod];
  const apiDefaultTxSudo = (api.tx.system && api.tx.system.setCode) || apiDefaultTx;

  setDeriveCache(api.genesisHash.toHex(), deriveMapCache);

  return {
    apiDefaultTx,
    apiDefaultTxSudo,
    hasInjectedAccounts: injectedAccounts.length !== 0,
    isApiReady: true,
    isDevelopment: isEthereum ? false : isDevelopment,
    isEthereum,
    specName: api.runtimeVersion.specName.toString(),
    specVersion: api.runtimeVersion.specVersion.toString(),
    systemChain,
    systemName,
    systemVersion,
  };
}
Example #20
Source File: TransactionTable.tsx    From metamask-snap-polkadot with Apache License 2.0 5 votes vote down vote up
TransactionTable = (props: TransactionTableProps) => {
    return (
        <TableContainer className="transtaction-table" component={Paper}>
            <Table 
            aria-label="simple table">
                <TableHead>
                <TableRow>
                    <TableCell>Transaction id</TableCell>
                    <TableCell align="center">Block</TableCell>
                    <TableCell align="center">Sender</TableCell>
                    <TableCell align="center">Destination</TableCell>
                    <TableCell align="center">Amount</TableCell>
                    <TableCell align="center">Fee</TableCell>
                </TableRow>
                </TableHead>
                <TableBody>
                {props.txs.map(tx => (
                    <TableRow key={tx.hash}>
                    <TableCell  align="left" component="th" scope="row">
                        {tx.hash}
                    </TableCell>
                    <TableCell  align="center" component="th" scope="row">
                        {tx.block}
                    </TableCell>
                        <TableCell align="center">{shortAddress(tx.sender)}</TableCell>
                        <TableCell align="center">{shortAddress(tx.destination)}</TableCell>
                        <TableCell align="center">{formatBalance(tx.amount, {
                            decimals: 12,
                            withSi: true,
                            withUnit: "KSM"
                        })}</TableCell>
                        <TableCell align="center">{formatBalance(tx.fee, {
                            decimals: 12,
                            withSi: true,
                            withUnit: "KSM"
                        })}</TableCell>
                    </TableRow>
                ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
}
Example #21
Source File: InputCsmNumber.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
function getSiOptions (): { text: string; value: string }[] {
  return formatBalance.getOptions().map(({ power, text, value }): { text: string; value: string } => ({
    text: text === 'CRU'
      ? 'CSM'
      : text,
    value
  })).filter(e => e.text === 'CSM');
}
Example #22
Source File: embeds.ts    From community-repo with GNU General Public License v3.0 5 votes vote down vote up
getStakeUpdatedEmbed = (stake: Stake, member: Membership, action: string, blockNumber: number, event: EventRecord): Discord.MessageEmbed => {
    
    return addCommonProperties(new Discord.MessageEmbed()
        .setTitle(`??? ${member.handle}'s stake has been ${action}`)
        .addFields(
            { name: 'Stake', value: formatBalance(stake.value.toString(), { withUnit: 'JOY' }), inline: true }
        ), blockNumber, event );
}
Example #23
Source File: DemocracyLocks.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
// group by header & details
//   - all unlockable together
//   - all ongoing together
//   - unlocks are displayed individually
function groupLocks (t: TFunction, bestNumber: BN, locks: DeriveDemocracyLock[] = []): State {
  return {
    maxBalance: bnMax(...locks.map(({ balance }) => balance)),
    sorted: locks
      .map((info): [DeriveDemocracyLock, BN] => [info, info.unlockAt.gt(bestNumber) ? info.unlockAt.sub(bestNumber) : BN_ZERO])
      .sort((a, b) => a[0].referendumId.cmp(b[0].referendumId))
      .sort((a, b) => a[1].cmp(b[1]))
      .sort((a, b) => a[0].isFinished === b[0].isFinished ? 0 : (a[0].isFinished ? -1 : 1))
      .reduce((sorted: Entry[], [{ balance, isDelegated, isFinished, referendumId, vote }, blocks]): Entry[] => {
        const isCountdown = blocks.gt(BN_ZERO);
        const header = <div>#{referendumId.toString()} {formatBalance(balance, { forceUnit: '-' })} {vote.conviction.toString()}{isDelegated && '/d'}</div>;
        const prev = sorted.length ? sorted[sorted.length - 1] : null;

        if (!prev || (isCountdown || (isFinished !== prev.isFinished))) {
          sorted.push({
            details: (
              <div className='faded'>
                {isCountdown
                  ? (
                    <BlockToTime
                      label={`${t<string>('{{blocks}} blocks', { replace: { blocks: formatNumber(blocks) } })}, `}
                      value={blocks}
                    />
                  )
                  : isFinished
                    ? t<string>('lock expired')
                    : t<string>('ongoing referendum')
                }
              </div>
            ),
            headers: [header],
            isCountdown,
            isFinished
          });
        } else {
          prev.headers.push(header);
        }

        return sorted;
      }, [])
  };
}
Example #24
Source File: embeds.ts    From community-repo with GNU General Public License v3.0 5 votes vote down vote up
getMintCapacityChangedEmbed = (minted: number, mint: u128, blockNumber: number, event: EventRecord): Discord.MessageEmbed => {

    return addCommonProperties(new Discord.MessageEmbed()
        .setTitle(`? ? ? ? ? ${formatBalance(minted, { withUnit: 'JOY' })} minted to the Treasury ? ? ? ? ? `)
        .addFields(
            { name: 'Balance', value: formatBalance(mint, { withUnit: 'JOY' }), inline: true },
        ), blockNumber, event );
}
Example #25
Source File: InputNumber.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
function InputNumber({
  autoFocus,
  bitLength = DEFAULT_BITLENGTH,
  children,
  className = '',
  defaultValue,
  help,
  isDecimal,
  isFull,
  isSi,
  isDisabled,
  isError = false,
  isWarning,
  isZeroable = true,
  label,
  labelExtra,
  maxLength,
  maxValue,
  onChange,
  onEnter,
  onEscape,
  placeholder,
  siDecimals,
  siDefault,
  siSymbol,
  value: propsValue,
}: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const { api } = useApi();
  const [si, setSi] = useState<SiDef | null>(() => (isSi ? siDefault || formatBalance.findSi('-') : null));
  const [[value, valueBn, isValid], setValues] = useState<[string, BN, boolean]>(() =>
    getValues(api, propsValue || defaultValue, si, bitLength, isZeroable, maxValue, siDecimals)
  );
  const [isPreKeyDown, setIsPreKeyDown] = useState(false);

  const siOptions = useMemo(() => getSiOptions(siSymbol || TokenUnit.abbr, siDecimals), [siDecimals, siSymbol]);

  useEffect((): void => {
    // eslint-disable-next-line
    onChange && onChange(isValid ? valueBn : undefined);
  }, [isValid, onChange, valueBn]);

  const _onChangeWithSi = useCallback(
    (input: string, si: SiDef | null) =>
      setValues(getValuesFromString(api, input, si, bitLength, isZeroable, maxValue, siDecimals)),
    [api, bitLength, isZeroable, maxValue, siDecimals]
  );

  const _onChange = useCallback((input: string) => _onChangeWithSi(input, si), [_onChangeWithSi, si]);

  useEffect((): void => {
    // eslint-disable-next-line
    defaultValue && _onChange(defaultValue.toString());
  }, [_onChange, defaultValue]);

  const _onKeyDown = useCallback(
    (event: React.KeyboardEvent<Element>): void => {
      if (KEYS_PRE.includes(event.key)) {
        setIsPreKeyDown(true);

        return;
      }

      if (event.key.length === 1 && !isPreKeyDown) {
        const { selectionEnd: j, selectionStart: i, value } = event.target as HTMLInputElement;
        const newValue = `${value.substring(0, i || 0)}${event.key}${value.substring(j || 0)}`;

        if (!getRegex(isDecimal || !!si).test(newValue)) {
          event.preventDefault();
        }
      }
    },
    [isDecimal, isPreKeyDown, si]
  );

  const _onKeyUp = useCallback((event: React.KeyboardEvent<Element>): void => {
    if (KEYS_PRE.includes(event.key)) {
      setIsPreKeyDown(false);
    }
  }, []);

  const _onPaste = useCallback(
    (event: React.ClipboardEvent<Element>): void => {
      const { value: newValue } = event.target as HTMLInputElement;

      if (!getRegex(isDecimal || !!si).test(newValue)) {
        event.preventDefault();
      }
    },
    [isDecimal, si]
  );

  const _onSelectSiUnit = useCallback(
    (siUnit: string): void => {
      const si = formatBalance.findSi(siUnit);

      setSi(si);
      _onChangeWithSi(value, si);
    },
    [_onChangeWithSi, value]
  );

  // Same as the number of digits, which means it can still overflow, i.e.
  // for u8 we allow 3, which could be 999 (however 2 digits will limit to only 99,
  // so this is more-or-less the lesser of evils without a max-value check)
  const maxValueLength = getGlobalMaxValue(bitLength).toString().length;

  return (
    <Input
      autoFocus={autoFocus}
      className={`ui--InputNumber${isDisabled ? ' isDisabled' : ''} ${className}`}
      help={help}
      isAction={isSi}
      isDisabled={isDisabled}
      isError={!isValid || isError}
      isFull={isFull}
      isWarning={isWarning}
      label={label}
      labelExtra={labelExtra}
      maxLength={maxLength || maxValueLength}
      onChange={_onChange}
      onEnter={onEnter}
      onEscape={onEscape}
      onKeyDown={_onKeyDown}
      onKeyUp={_onKeyUp}
      onPaste={_onPaste}
      placeholder={placeholder || t<string>('Positive number')}
      type="text"
      value={value}
    >
      {!!si && (
        <Dropdown
          defaultValue={si.value}
          dropdownClassName="ui--SiDropdown"
          isButton
          onChange={_onSelectSiUnit}
          options={siOptions}
        />
      )}
      {children}
    </Input>
  );
}
Example #26
Source File: Account.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function Account ({ account: { address, meta }, className = '', delegation, filter, isFavorite, proxy, setBalance, toggleFavorite }: Props): React.ReactElement<Props> | null {
  const { t } = useTranslation();
  const { theme } = useContext<ThemeDef>(ThemeContext);
  const { queueExtrinsic } = useContext(StatusContext);
  const api = useApi();
  const { getLedger } = useLedger();
  const bestNumber = useBestNumber();
  const balancesAll = useCall<DeriveBalancesAll>(api.api.derive.balances.all, [address]);
  const democracyLocks = useCall<DeriveDemocracyLock[]>(api.api.derive.democracy?.locks, [address]);
  const recoveryInfo = useCall<RecoveryConfig | null>(api.api.query.recovery?.recoverable, [address], transformRecovery);
  const multiInfos = useMultisigApprovals(address);
  const proxyInfo = useProxies(address);
  const { flags: { isDevelopment, isEditable, isExternal, isHardware, isInjected, isMultisig, isProxied }, genesisHash, identity, name: accName, onSetGenesisHash, tags } = useAccountInfo(address);
  const [{ democracyUnlockTx }, setUnlockableIds] = useState<DemocracyUnlockable>({ democracyUnlockTx: null, ids: [] });
  const [vestingVestTx, setVestingTx] = useState<SubmittableExtrinsic<'promise'> | null>(null);
  const [isBackupOpen, toggleBackup] = useToggle();
  const [isDeriveOpen, toggleDerive] = useToggle();
  const [isForgetOpen, toggleForget] = useToggle();
  const [isIdentityMainOpen, toggleIdentityMain] = useToggle();
  const [isIdentitySubOpen, toggleIdentitySub] = useToggle();
  const [isMultisigOpen, toggleMultisig] = useToggle();
  const [isProxyOverviewOpen, toggleProxyOverview] = useToggle();
  const [isPasswordOpen, togglePassword] = useToggle();
  const [isRecoverAccountOpen, toggleRecoverAccount] = useToggle();
  const [isRecoverSetupOpen, toggleRecoverSetup] = useToggle();
  const [isSettingsOpen, toggleSettings] = useToggle();
  const [isTransferOpen, toggleTransfer] = useToggle();
  const [isDelegateOpen, toggleDelegate] = useToggle();
  const [isUndelegateOpen, toggleUndelegate] = useToggle();
  const candyAmount = useCall<Balance>(api.api.query.candy?.balances, [address]);
  const [isTransferCandyOpen, toggleTransferCandy] = useToggle();
  const [isTransferCsmOpen, toggleTransferCsm] = useToggle();

  useEffect((): void => {
    if (balancesAll) {
      setBalance(address, balancesAll.freeBalance.add(balancesAll.reservedBalance));

      api.api.tx.vesting?.vest && setVestingTx(() =>
        balancesAll.vestingLocked.isZero()
          ? null
          : api.api.tx.vesting.vest()
      );
    }
  }, [address, api, balancesAll, setBalance]);

  useEffect((): void => {
    bestNumber && democracyLocks && setUnlockableIds(
      (prev): DemocracyUnlockable => {
        const ids = democracyLocks
          .filter(({ isFinished, unlockAt }) => isFinished && bestNumber.gt(unlockAt))
          .map(({ referendumId }) => referendumId);

        if (JSON.stringify(prev.ids) === JSON.stringify(ids)) {
          return prev;
        }

        return {
          democracyUnlockTx: createClearDemocracyTx(api.api, address, ids),
          ids
        };
      }
    );
  }, [address, api, bestNumber, democracyLocks]);

  const isVisible = useMemo(
    () => calcVisible(filter, accName, tags),
    [accName, filter, tags]
  );

  const _onFavorite = useCallback(
    () => toggleFavorite(address),
    [address, toggleFavorite]
  );

  const _onForget = useCallback(
    (): void => {
      if (!address) {
        return;
      }

      const status: Partial<ActionStatus> = {
        account: address,
        action: 'forget'
      };

      try {
        keyring.forgetAccount(address);
        status.status = 'success';
        status.message = t<string>('account forgotten');
      } catch (error) {
        status.status = 'error';
        status.message = (error as Error).message;
      }
    },
    [address, t]
  );

  const _clearDemocracyLocks = useCallback(
    () => democracyUnlockTx && queueExtrinsic({
      accountId: address,
      extrinsic: democracyUnlockTx
    }),
    [address, democracyUnlockTx, queueExtrinsic]
  );

  const _vestingVest = useCallback(
    () => vestingVestTx && queueExtrinsic({
      accountId: address,
      extrinsic: vestingVestTx
    }),
    [address, queueExtrinsic, vestingVestTx]
  );

  const _showOnHardware = useCallback(
    // TODO: we should check the hardwareType from metadata here as well,
    // for now we are always assuming hardwareType === 'ledger'
    (): void => {
      showLedgerAddress(getLedger, meta).catch((error): void => {
        console.error(`ledger: ${(error as Error).message}`);
      });
    },
    [getLedger, meta]
  );

  const menuItems = useMemo(() => [
    createMenuGroup('identityGroup', [
      isFunction(api.api.tx.identity?.setIdentity) && !isHardware && (
        <Menu.Item
          key='identityMain'
          onClick={toggleIdentityMain}
        >
          {t('Set on-chain identity')}
        </Menu.Item>
      ),
      isFunction(api.api.tx.identity?.setSubs) && identity?.display && !isHardware && (
        <Menu.Item
          key='identitySub'
          onClick={toggleIdentitySub}
        >
          {t('Set on-chain sub-identities')}
        </Menu.Item>
      ),
      isFunction(api.api.tx.democracy?.unlock) && democracyUnlockTx && (
        <Menu.Item
          key='clearDemocracy'
          onClick={_clearDemocracyLocks}
        >
          {t('Clear expired democracy locks')}
        </Menu.Item>
      ),
      isFunction(api.api.tx.vesting?.vest) && vestingVestTx && (
        <Menu.Item
          key='vestingVest'
          onClick={_vestingVest}
        >
          {t('Unlock vested amount')}
        </Menu.Item>
      )
    ]),
    createMenuGroup('deriveGroup', [
      !(isExternal || isHardware || isInjected || isMultisig) && (
        <Menu.Item
          key='deriveAccount'
          onClick={toggleDerive}
        >
          {t('Derive account via derivation path')}
        </Menu.Item>
      ),
      isHardware && (
        <Menu.Item
          key='showHwAddress'
          onClick={_showOnHardware}
        >
          {t('Show address on hardware device')}
        </Menu.Item>
      )
    ]),
    createMenuGroup('backupGroup', [
      !(isExternal || isHardware || isInjected || isMultisig || isDevelopment) && (
        <Menu.Item
          key='backupJson'
          onClick={toggleBackup}
        >
          {t('Create a backup file for this account')}
        </Menu.Item>
      ),
      !(isExternal || isHardware || isInjected || isMultisig || isDevelopment) && (
        <Menu.Item
          key='changePassword'
          onClick={togglePassword}
        >
          {t("Change this account's password")}
        </Menu.Item>
      ),
      !(isInjected || isDevelopment) && (
        <Menu.Item
          key='forgetAccount'
          onClick={toggleForget}
        >
          {t('Forget this account')}
        </Menu.Item>
      )
    ]),
    isFunction(api.api.tx.recovery?.createRecovery) && createMenuGroup('reoveryGroup', [
      !recoveryInfo && (
        <Menu.Item
          key='makeRecoverable'
          onClick={toggleRecoverSetup}
        >
          {t('Make recoverable')}
        </Menu.Item>
      ),
      <Menu.Item
        key='initRecovery'
        onClick={toggleRecoverAccount}
      >
        {t('Initiate recovery for another')}
      </Menu.Item>
    ]),
    isFunction(api.api.tx.multisig?.asMulti) && isMultisig && createMenuGroup('multisigGroup', [
      <Menu.Item
        disabled={!multiInfos || !multiInfos.length}
        key='multisigApprovals'
        onClick={toggleMultisig}
      >
        {t('Multisig approvals')}
      </Menu.Item>
    ]),
    isFunction(api.api.query.democracy?.votingOf) && delegation?.accountDelegated && createMenuGroup('undelegateGroup', [
      <Menu.Item
        key='changeDelegate'
        onClick={toggleDelegate}
      >
        {t('Change democracy delegation')}
      </Menu.Item>,
      <Menu.Item
        key='undelegate'
        onClick={toggleUndelegate}
      >
        {t('Undelegate')}
      </Menu.Item>
    ]),
    isFunction(api.api.query.democracy?.votingOf) && !delegation?.accountDelegated && createMenuGroup('delegateGroup', [
      <Menu.Item
        key='delegate'
        onClick={toggleDelegate}
      >
        {t('Delegate democracy votes')}
      </Menu.Item>
    ]),
    isFunction(api.api.query.proxy?.proxies) && createMenuGroup('proxyGroup', [
      <Menu.Item
        key='proxy-overview'
        onClick={toggleProxyOverview}
      >
        {proxy?.[0].length
          ? t('Manage proxies')
          : t('Add proxy')
        }
      </Menu.Item>
    ]),
    isEditable && !api.isDevelopment && createMenuGroup('genesisGroup', [
      <ChainLock
        className='accounts--network-toggle'
        genesisHash={genesisHash}
        key='chainlock'
        onChange={onSetGenesisHash}
      />
    ])
  ].filter((i) => i),
  [_clearDemocracyLocks, _showOnHardware, _vestingVest, api, delegation, democracyUnlockTx, genesisHash, identity, isDevelopment, isEditable, isExternal, isHardware, isInjected, isMultisig, multiInfos, onSetGenesisHash, proxy, recoveryInfo, t, toggleBackup, toggleDelegate, toggleDerive, toggleForget, toggleIdentityMain, toggleIdentitySub, toggleMultisig, togglePassword, toggleProxyOverview, toggleRecoverAccount, toggleRecoverSetup, toggleUndelegate, vestingVestTx]);

  if (!isVisible) {
    return null;
  }

  return (
    <tr className={className}>
      <td className='favorite'>
        <Icon
          color={isFavorite ? 'orange' : 'gray'}
          icon='star'
          onClick={_onFavorite}
        />
      </td>
      <td className='together'>
        {meta.genesisHash
          ? <Badge color='transparent' />
          : isDevelopment
            ? (
              <Badge
                className='devBadge'
                color='orange'
                hover={t<string>('This is a development account derived from the known development seed. Do not use for any funds on a non-development network.')}
                icon='wrench'
              />
            )
            : (
              <Badge
                color='orange'
                hover={
                  <div>
                    <p>{t<string>('This account is available on all networks. It is recommended to link to a specific network via the account options ("only this network" option) to limit availability. For accounts from an extension, set the network on the extension.')}</p>
                    <p>{t<string>('This does not send any transaction, rather is only sets the genesis in the account JSON.')}</p>
                  </div>
                }
                icon='exclamation-triangle'
              />
            )
        }
        {recoveryInfo && (
          <Badge
            color='green'
            hover={
              <div>
                <p>{t<string>('This account is recoverable, with the following friends:')}</p>
                <div>
                  {recoveryInfo.friends.map((friend, index): React.ReactNode => (
                    <IdentityIcon
                      key={index}
                      value={friend}
                    />
                  ))}
                </div>
                <table>
                  <tbody>
                    <tr>
                      <td>{t<string>('threshold')}</td>
                      <td>{formatNumber(recoveryInfo.threshold)}</td>
                    </tr>
                    <tr>
                      <td>{t<string>('delay')}</td>
                      <td>{formatNumber(recoveryInfo.delayPeriod)}</td>
                    </tr>
                    <tr>
                      <td>{t<string>('deposit')}</td>
                      <td>{formatBalance(recoveryInfo.deposit)}</td>
                    </tr>
                  </tbody>
                </table>
              </div>
            }
            icon='shield'
          />
        )}
        {multiInfos && multiInfos.length !== 0 && (
          <Badge
            color='red'
            hover={t<string>('Multisig approvals pending')}
            info={multiInfos.length}
          />
        )}
        {isProxied && !proxyInfo.hasOwned && (
          <Badge
            color='red'
            hover={t<string>('Proxied account has no owned proxies')}
            info='0'
          />
        )}
        {delegation?.accountDelegated && (
          <Badge
            color='blue'
            hover={t<string>('This account has a governance delegation')}
            icon='calendar-check'
            onClick={toggleDelegate}
          />
        )}
        {!!proxy?.[0].length && api.api.tx.utility && (
          <Badge
            color='blue'
            hover={t<string>('This account has {{proxyNumber}} proxy set.', {
              replace: {
                proxyNumber: proxy[0].length
              }
            })}
            icon='arrow-right'
            onClick={toggleProxyOverview}
          />
        )}
      </td>
      <td className='address'>
        <AddressSmall value={address} />
        {isBackupOpen && (
          <Backup
            address={address}
            key='modal-backup-account'
            onClose={toggleBackup}
          />
        )}
        {isDelegateOpen && (
          <DelegateModal
            key='modal-delegate'
            onClose={toggleDelegate}
            previousAmount={delegation?.amount}
            previousConviction={delegation?.conviction}
            previousDelegatedAccount={delegation?.accountDelegated}
            previousDelegatingAccount={address}
          />
        )}
        {isDeriveOpen && (
          <Derive
            from={address}
            key='modal-derive-account'
            onClose={toggleDerive}
          />
        )}
        {isForgetOpen && (
          <Forget
            address={address}
            key='modal-forget-account'
            onClose={toggleForget}
            onForget={_onForget}
          />
        )}
        {isIdentityMainOpen && (
          <IdentityMain
            address={address}
            key='modal-identity-main'
            onClose={toggleIdentityMain}
          />
        )}
        {isIdentitySubOpen && (
          <IdentitySub
            address={address}
            key='modal-identity-sub'
            onClose={toggleIdentitySub}
          />
        )}
        {isPasswordOpen && (
          <ChangePass
            address={address}
            key='modal-change-pass'
            onClose={togglePassword}
          />
        )}
        {isTransferOpen && (
          <Transfer
            key='modal-transfer'
            onClose={toggleTransfer}
            senderId={address}
          />
        )}
        {isTransferCandyOpen && (
          <TransferCandy
            key='modal-transfer'
            onClose={toggleTransferCandy}
            senderId={address}
          />
        )}
        {isTransferCsmOpen && (
          <TransferCsm
            key='modal-transfer'
            onClose={toggleTransferCsm}
            senderId={address}
          />
        )}
        {isProxyOverviewOpen && (
          <ProxyOverview
            key='modal-proxy-overview'
            onClose={toggleProxyOverview}
            previousProxy={proxy}
            proxiedAccount={address}
          />
        )}
        {isMultisigOpen && multiInfos && (
          <MultisigApprove
            address={address}
            key='multisig-approve'
            onClose={toggleMultisig}
            ongoing={multiInfos}
            threshold={meta.threshold as number}
            who={meta.who as string[]}
          />
        )}
        {isRecoverAccountOpen && (
          <RecoverAccount
            address={address}
            key='recover-account'
            onClose={toggleRecoverAccount}
          />
        )}
        {isRecoverSetupOpen && (
          <RecoverSetup
            address={address}
            key='recover-setup'
            onClose={toggleRecoverSetup}
          />
        )}
        {isUndelegateOpen && (
          <UndelegateModal
            accountDelegating={address}
            key='modal-delegate'
            onClose={toggleUndelegate}
          />
        )}
      </td>
      {/* <td className='address media--1400'>
        {(meta.parentAddress as string) && (
          <AddressMini value={meta.parentAddress} />
        )}
      </td> */}
      <td className='address media--1400'>
        <CryptoType accountId={address} />
      </td>
      {/* <td className='all'>
        <div className='tags'>
          <Tags value={tags} />
        </div>
      </td> */}
      <td className='all'>
        {balancesAll?.accountNonce.gt(BN_ZERO) && formatNumber(balancesAll.accountNonce)}
      </td>
      <td className='number'>
        <AddressInfo
          address={address}
          withBalance
          withBalanceToggle
          withExtended={false}
        />
      </td>
      <td className='number'>
        <AddressCsmInfo address={address}
          withBalance
          withBalanceToggle
          withExtended={false} />
      </td>
      <td className='number'>
        <FormatCandy value={candyAmount} />
      </td>
      <td className='number'>
        <PreClaimCRU18 value={address} />
      </td>

      <td className='button'>
        {/* {isFunction(api.api.tx.balances?.transfer) && (
          <Button
            icon='paper-plane'
            label={t<string>('Send CRU')}
            onClick={toggleTransfer}
          />
        )} */}

        <Popup
          className='my-popup'
          on='click'
          trigger={<Button
            icon='paper-plane'
            label={t<string>('Send')}
          />}
        >

          {isFunction(api.api.tx.balances?.transfer) && (
            <Button
              icon='paper-plane'
              label={t<string>('Send CRU')}
              onClick={toggleTransfer}
            />
          )}
          {isFunction(api.api.tx.csm?.transfer) && (
            <Button
              icon='paper-plane'
              label={t<string>('Send CSM')}
              onClick={toggleTransferCsm}
            />
          )}
          {isFunction(api.api.tx.candy?.transfer) && (
            <Button
              icon='paper-plane'
              label={t<string>('Send Candy')}
              onClick={toggleTransferCandy}
            />
          )}

        </Popup>
        <Popup
          className={`theme--${theme}`}
          isOpen={isSettingsOpen}
          onClose={toggleSettings}
          trigger={
            <Button
              icon='ellipsis-v'
              isDisabled={!menuItems.length}
              onClick={toggleSettings}
            />
          }
        >
          <Menu
            onClick={toggleSettings}
            text
            vertical
          >
            {menuItems}
          </Menu>
        </Popup>
      </td>
      <td className='links media--1400'>
        <LinkExternal
          className='ui--AddressCard-exporer-link'
          data={address}
          isLogo
          type='address'
        />
      </td>
    </tr>
  );
}
Example #27
Source File: AddressInfo.tsx    From subscan-multisig-react with Apache License 2.0 4 votes vote down vote up
function createBalanceItems(
  formatIndex: number,
  lookup: Record<string, string>,
  t: TFunction,
  {
    address,
    balanceDisplay,
    balancesAll,
    bestNumber,
    democracyLocks,
    isAllLocked,
    otherBonded,
    ownBonded,
    stakingInfo,
    votingOf,
    withBalanceToggle,
  }: {
    address: string;
    balanceDisplay: BalanceActiveType;
    balancesAll?: DeriveBalancesAll | DeriveBalancesAccountData;
    bestNumber: BlockNumber;
    democracyLocks?: DeriveDemocracyLock[];
    isAllLocked: boolean;
    otherBonded: BN[];
    ownBonded: BN;
    stakingInfo?: DeriveStakingAccount;
    votingOf?: Voting;
    withBalanceToggle: boolean;
  }
): React.ReactNode {
  const allItems: React.ReactNode[] = [];
  const deriveBalances = balancesAll as DeriveBalancesAll;

  // eslint-disable-next-line
  !withBalanceToggle &&
    balancesAll &&
    balanceDisplay.total &&
    allItems.push(
      <React.Fragment key={0}>
        <Label label={t<string>('total')} />
        <FormatBalance
          className="result"
          formatIndex={formatIndex}
          value={balancesAll.freeBalance.add(balancesAll.reservedBalance)}
        />
      </React.Fragment>
    );
  // eslint-disable-next-line
  balancesAll &&
    balanceDisplay.available &&
    (balancesAll as DeriveBalancesAll).availableBalance &&
    allItems.push(
      <React.Fragment key={1}>
        <Label label={t<string>('transferrable')} />
        <FormatBalance
          className="result"
          formatIndex={formatIndex}
          value={(balancesAll as DeriveBalancesAll).availableBalance}
        />
      </React.Fragment>
    );
  if (balanceDisplay.vested && deriveBalances?.isVesting) {
    const allVesting = deriveBalances.vesting.filter(({ endBlock }) => bestNumber.lt(endBlock));

    allItems.push(
      <React.Fragment key={2}>
        <Label label={t<string>('vested')} />
        <FormatBalance
          className='result'
          formatIndex={formatIndex}
          labelPost={
            <Icon
              icon='info-circle'
              tooltip={`${address}-vested-trigger`}
            />
          }
          value={deriveBalances.vestedBalance}
        >
          <Tooltip
            text={
              <>
                <div>
                  {formatBalance(deriveBalances.vestedClaimable, { forceUnit: '-' })}
                  <div className='faded'>{t('available to be unlocked')}</div>
                </div>
                {allVesting.map(({ endBlock, locked, perBlock, vested }, index) => (
                  <div
                    className='inner'
                    key={`item:${index}`}
                  >
                    <div>
                      {formatBalance(vested, { forceUnit: '-' })}
                      <div className='faded'>{t('of {{locked}} vested', { replace: { locked: formatBalance(locked, { forceUnit: '-' }) } })}</div>
                    </div>
                    <div>
                      <BlockToTime value={endBlock.sub(bestNumber)} />
                      <div className='faded'>{t('until block')} {formatNumber(endBlock)}</div>
                    </div>
                    <div>
                      {formatBalance(perBlock)}
                      <div className='faded'>{t('per block')}</div>
                    </div>
                  </div>
                ))}
              </>
            }
            trigger={`${address}-vested-trigger`}
          />
        </FormatBalance>
      </React.Fragment>
    );
  }
  balanceDisplay.locked &&
    balancesAll &&
    (isAllLocked || (balancesAll as DeriveBalancesAll).lockedBalance?.gtn(0)) &&
    allItems.push(
      <React.Fragment key={3}>
        <Label label={t<string>('locked')} />
        <FormatBalance
          className="result"
          formatIndex={formatIndex}
          label={<Icon icon="info-circle" tooltip={`${address}-locks-trigger`} />}
          value={isAllLocked ? 'all' : (balancesAll as DeriveBalancesAll).lockedBalance}
        >
          <Tooltip
            text={(balancesAll as DeriveBalancesAll).lockedBreakdown.map(
              ({ amount, id, reasons }, index): React.ReactNode => (
                <div key={index}>
                  {amount?.isMax() ? t<string>('everything') : formatBalance(amount, { forceUnit: '-' })}
                  {id && <div className="faded">{lookupLock(lookup, id)}</div>}
                  <div className="faded">{reasons.toString()}</div>
                </div>
              )
            )}
            trigger={`${address}-locks-trigger`}
          />
        </FormatBalance>
      </React.Fragment>
    );
  balanceDisplay.reserved &&
    balancesAll?.reservedBalance?.gtn(0) &&
    allItems.push(
      <React.Fragment key={4}>
        <Label label={t<string>('reserved')} />
        <FormatBalance className="result" formatIndex={formatIndex} value={balancesAll.reservedBalance} />
      </React.Fragment>
    );
  balanceDisplay.bonded &&
    (ownBonded.gtn(0) || otherBonded.length !== 0) &&
    allItems.push(
      <React.Fragment key={5}>
        <Label label={t<string>('bonded')} />
        <FormatBalance className="result" formatIndex={formatIndex} value={ownBonded}>
          {otherBonded.length !== 0 && (
            <>
              &nbsp;(+
              {otherBonded.map(
                (bonded, index): React.ReactNode => (
                  <FormatBalance formatIndex={formatIndex} key={index} value={bonded} />
                )
              )}
              )
            </>
          )}
        </FormatBalance>
      </React.Fragment>
    );
  balanceDisplay.redeemable &&
    stakingInfo?.redeemable?.gtn(0) &&
    allItems.push(
      <React.Fragment key={6}>
        <Label label={t<string>('redeemable')} />
        <StakingRedeemable className="result" stakingInfo={stakingInfo} />
      </React.Fragment>
    );

  if (balanceDisplay.unlocking) {
    stakingInfo?.unlocking &&
      allItems.push(
        <React.Fragment key={7}>
          <Label label={t<string>('unbonding')} />
          <div className="result">
            <StakingUnbonding stakingInfo={stakingInfo} />
          </div>
        </React.Fragment>
      );

    if (democracyLocks && democracyLocks.length !== 0) {
      allItems.push(
        <React.Fragment key={8}>
          <Label label={t<string>('democracy')} />
          <div className="result">
            <DemocracyLocks value={democracyLocks} />
          </div>
        </React.Fragment>
      );
    } else if (votingOf && votingOf.isDirect) {
      const {
        prior: [unlockAt, balance],
      } = votingOf.asDirect;

      balance.gt(BN_ZERO) &&
        unlockAt.gt(BN_ZERO) &&
        allItems.push(
          <React.Fragment key={8}>
            <Label label={t<string>('democracy')} />
            <div className="result">
              <DemocracyLocks value={[{ balance, isFinished: bestNumber.gt(unlockAt), unlockAt }]} />
            </div>
          </React.Fragment>
        );
    }
  }

  if (withBalanceToggle) {
    return (
      <React.Fragment key={formatIndex}>
        <Expander
          summary={
            <FormatBalance
              formatIndex={formatIndex}
              value={balancesAll && balancesAll.freeBalance.add(balancesAll.reservedBalance)}
            />
          }
        >
          {allItems.length !== 0 && <div className="body column">{allItems}</div>}
        </Expander>
      </React.Fragment>
    );
  }

  return <React.Fragment key={formatIndex}>{allItems}</React.Fragment>;
}
Example #28
Source File: index.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function EthereumAssets ({ className = '', senderId: propSenderId }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const { api, systemChain } = useApi();
  const [amount, setAmount] = useState<BN | undefined>(BN_ZERO);
  const [hasAvailable] = useState(true);
  const [senderId, setSenderId] = useState<string | null>(propSenderId || null);
  const [ethereumAddress, setEthereumAddress] = useState<string | undefined | null>(null);
  const [isValid, setIsValid] = useState(false);
  const [bridgeFee, setBridgeFee] = useState<BN>(BN_ZERO);
  const isMaxwell = systemChain === 'Crust Maxwell';
  const bridgeTxStatusLink = isMaxwell ? 'https://etherscan.io/address/0x9d332427e6d1b91d9cf8d2fa3b41df2012887aab' : 'https://etherscan.io/address/0x18FCb27e4712AC11B8BecE851DAF96ba8ba34720';
  const whitePot = isMaxwell ? 0 : 2
  const [handlerAsset, setHandlerAsset] = useState<BN | undefined>(BN_ZERO);

  useEffect(() => {
    api.query.bridgeTransfer.bridgeFee(whitePot).then((bridgeFee) => {
      const fee = JSON.parse(JSON.stringify(bridgeFee));

      setBridgeFee(new BN(Number(fee[0]).toString()));
    });
  }, [api]);

  useEffect(() => {
    const provider = ethers.getDefaultProvider();

    const erc20Contract = new ethers.Contract(contractAddress, abi, provider);

    erc20Contract.getBalance(handler).then((res: any) => {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
      setHandlerAsset(new BN((Number(res) / 1000000.0).toString()))
    })
  }, [])

  const onChangeEthereumAddress = useCallback((hex: string) => {
    const isValidEthAddr = hex.startsWith('0x') && ethers.utils.isAddress(hex);

    if (isValidEthAddr) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }

    setEthereumAddress(hex.trim());
  }, []);

  //   const onChangeEthereumAddress = useCallback((value: string) => {
  //     // FIXME We surely need a better check than just a trim

  //     setEthereumAddress(value.trim());
  //   }, []);

  return (<div className={className}>
    <Columar>
      <Columar.Column>
        <Card withBottomMargin>
          <Banner type='warning'>
            <p>{t<string>('This function is an internal test stage, the assets will not be lost, but there may be a delay (max to 48 hours) in the arrival of the account.')}</p>
          </Banner>
          <h3><span style={{ fontWeight: 'bold' }}>{t<string>('From Crust')}</span></h3>
          <div style={{ display: 'flex' }}>
            <img src={logoCrust as string}
              style={{ width: '64px', height: '64px', padding: '1px', verticalAlign: 'middle' }} />
            <div style={{ flex: 1, verticalAlign: 'middle' }}>
              <InputAddress
                defaultValue={propSenderId}
                help={t<string>('The account you will sign tx.')}
                isDisabled={!!propSenderId}
                label={t<string>('account')}
                onChange={setSenderId}
                labelExtra={
                  <Available
                    label={t('transferrable')}
                    params={senderId}
                  />
                }
                type='account'
              />
            </div>
          </div>

          <h3><span style={{ fontWeight: 'bold' }}>{t<string>('To Ethereum')}</span></h3>
          <div style={{ display: 'flex', alignItems: 'middle' }}>
            <img src={ethereumLogo as string}
              style={{ width: '64px', height: '64px', padding: '3px', verticalAlign: 'middle' }} />
            <div style={{ flex: 1, verticalAlign: 'middle' }}>
              <Input
                autoFocus
                className='full'
                help={t<string>('The Ethereum address')}
                isError={!isValid}
                label={t<string>('Ethereum address')}
                onChange={onChangeEthereumAddress}
                placeholder={t<string>('0x prefixed hex')}
                value={ethereumAddress || ''}
              />
            </div>
          </div>

          <h3><span style={{ fontWeight: 'bold' }}>{t<string>('Amount')}</span></h3>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <div style={{ width: '64px', verticalAlign: 'middle' }}/>
            <div style={{ flex: 1, verticalAlign: 'middle' }}>
              <InputBalance
                autoFocus
                help={t<string>('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.')}
                isError={!hasAvailable}
                label={t<string>('amount')}
                onChange={setAmount}
                withMax
              />
              <MarkWarning content={t<string>('The transaction fee is ')}>
                <span style={{'color': '#ff8812', 'textDecoration': 'underline', 'fontStyle': 'italic'}}>{formatBalance(bridgeFee)}</span>&nbsp;<span>{t<string>('(The transaction fee will be dynamically adjusted according to the Gwei of Ethereum)')}</span>
              </MarkWarning>
            </div>
          </div>
          <Button.Group>
            <TxButton
              accountId={senderId}
              icon='paper-plane'
              isDisabled={!isValid || (handlerAsset && amount && handlerAsset.lte(amount))}
              label={t<string>('Transfer')}
              params={[amount, ethereumAddress, whitePot]}
              tx={api.tx.bridgeTransfer?.transferNative}
            />
          </Button.Group>
        </Card>
      </Columar.Column>
      <Columar.Column>
        <Card>
          <Banner type="warning">
            <p>{t<string>('Cross-chain transfers are automatically executed by smart contracts. after the execution of the contract is completed, the funds will arrive in the account. Please wait patiently.')}&nbsp;<a target="_blank" href={bridgeTxStatusLink}>{t<string>('You can check the transaction status here...')}</a></p>
          </Banner>
        </Card>
      </Columar.Column>
    </Columar>
  </div>);
}
Example #29
Source File: useApiCalls.ts    From parity-bridges-ui with GNU General Public License v3.0 4 votes vote down vote up
useApiCalls = (): ApiCallsContextType => {
  const { sourceChainDetails, targetChainDetails } = useSourceTarget();
  const {
    apiConnection: { api: sourceApi },
    chain: sourceChain
  } = sourceChainDetails;
  const { keyringPairs, keyringPairsReady } = useKeyringContext();
  const { getValuesByChain } = useChainGetters();

  const createType = useCallback(
    (chain, type, data) => {
      const { api } = getValuesByChain(chain);
      return api.registry.createType(type, data);
    },
    [getValuesByChain]
  );

  const stateCall = useCallback(
    (chain: string, methodName: string | Text, data: string | Uint8Array | Bytes, at) => {
      const { api } = getValuesByChain(chain);

      const params: [string | Text, string | Uint8Array | Bytes] = [methodName, data];
      if (at) {
        params.push(at);
      }

      return api.rpc.state.call<Codec>(...params);
    },
    [getValuesByChain]
  );

  const internalTransfer = useCallback(
    async (dispatchers, transfersData) => {
      const { dispatchTransaction, dispatchMessage } = dispatchers;
      const { receiverAddress, transferAmount, account } = transfersData;
      const type = TransactionTypes.INTERNAL_TRANSFER;

      const id = Date.now().toString();
      dispatchTransaction(TransactionActionCreators.setTransactionRunning(true));

      try {
        const transfer = sourceApi.tx.balances.transfer(receiverAddress, transferAmount);
        const options: Partial<SignerOptions> = {
          nonce: -1
        };
        let sourceAccount: string | KeyringPair = account;
        if (account.meta.isInjected) {
          const injector = await web3FromSource(account.meta.source as string);
          options.signer = injector.signer;
          sourceAccount = account.address;
        }

        const transactionDisplayPayload = {
          sourceAccount: account?.address || sourceAccount,
          transferAmount: transferAmount.toNumber(),
          receiverAddress
        };

        const unsub = await transfer.signAndSend(sourceAccount, { ...options }, async ({ status }) => {
          const steps = createEmptyInternalSteps(sourceChain);
          if (status.isReady) {
            dispatchTransaction(
              TransactionActionCreators.createTransactionStatus({
                block: null,
                blockHash: null,
                deliveryBlock: null,
                id,
                input: transferAmount,
                messageNonce: null,
                receiverAddress,
                sourceAccount: account.address,
                senderName: getName(account),
                sourceChain,
                status: TransactionStatusEnum.IN_PROGRESS,
                targetChain: '',
                type,
                transactionDisplayPayload,
                payloadHex: transfer.toHex(),
                steps
              })
            );
          }

          if (status.isBroadcast) {
            dispatchMessage(MessageActionsCreators.triggerInfoMessage({ message: 'Transaction was broadcasted' }));
            dispatchTransaction(TransactionActionCreators.reset());
          }

          if (status.isInBlock) {
            try {
              const res = (await sourceApi.rpc.chain.getBlock(status.asInBlock)) as SignedBlock;
              const block = res.block.header.number.toString();
              dispatchTransaction(
                TransactionActionCreators.updateTransactionStatus(
                  {
                    block,
                    blockHash: status.asInBlock.toString()
                  },
                  id
                )
              );
            } catch (e) {
              if (e instanceof Error) {
                logger.error(e.message);
                throw new Error('Issue reading block information.');
              }
            }
          }

          if (status.isFinalized) {
            dispatchTransaction(
              TransactionActionCreators.updateTransactionStatus(
                {
                  status: TransactionStatusEnum.FINALIZED
                },
                id
              )
            );
            logger.info(`Transaction finalized at blockHash ${status.asFinalized}`);
            unsub();
          }
        });
      } catch (e) {
        if (e instanceof Error) {
          dispatchMessage(MessageActionsCreators.triggerErrorMessage({ message: e.message }));
          logger.error(e.message);
        }
      } finally {
        dispatchTransaction(TransactionActionCreators.setTransactionRunning(false));
      }
    },
    [sourceApi.rpc.chain, sourceApi.tx.balances, sourceChain]
  );

  const updateSenderAccountsInformation = useCallback(
    async (dispatchAccount) => {
      const formatBalanceAddress = (data: any, api: ApiPromise): BalanceState => {
        return {
          chainTokens: data.registry.chainTokens[0],
          formattedBalance: formatBalance(data.free, {
            decimals: api.registry.chainDecimals[0],
            withUnit: api.registry.chainTokens[0],
            withSi: true
          }),
          free: data.free
        };
      };

      if (!keyringPairsReady || !keyringPairs.length) {
        return {};
      }

      const getAccountInformation = async (sourceRole: any, targetRole: any) => {
        const {
          apiConnection: { api: sourceApi },
          chain: sourceChain,
          configs: sourceConfigs
        } = sourceRole;
        const {
          apiConnection: { api: targetApi },
          configs: targetConfigs
        } = targetRole;

        const accounts = await Promise.all(
          keyringPairs.map(async ({ address, meta }) => {
            const sourceAddress = encodeAddress(address, sourceConfigs.ss58Format);
            const toDerive = {
              ss58Format: targetConfigs.ss58Format,
              address: sourceAddress || '',
              bridgeId: getBridgeId(targetApi, sourceChain)
            };
            const { data } = await sourceApi.query.system.account(sourceAddress);
            const sourceBalance = formatBalanceAddress(data, sourceApi);

            const companionAddress = getDeriveAccount(toDerive);
            const { data: dataCompanion } = await targetApi.query.system.account(companionAddress);
            const targetBalance = formatBalanceAddress(dataCompanion, targetApi);

            const name = (meta.name as string).toLocaleUpperCase();

            return {
              account: { address: sourceAddress, balance: sourceBalance, name },
              companionAccount: { address: companionAddress, balance: targetBalance, name }
            };
          })
        );

        return accounts;
      };

      const sourceAddresses = await getAccountInformation(sourceChainDetails, targetChainDetails);
      const targetAddresses = await getAccountInformation(targetChainDetails, sourceChainDetails);

      dispatchAccount(
        AccountActionCreators.setDisplaySenderAccounts({
          [sourceChainDetails.chain]: sourceAddresses,
          [targetChainDetails.chain]: targetAddresses
        })
      );
    },
    [keyringPairs, keyringPairsReady, sourceChainDetails, targetChainDetails]
  );

  return { createType, stateCall, internalTransfer, updateSenderAccountsInformation };
}