@polkadot/util#compactAddLength TypeScript Examples

The following examples show how to use @polkadot/util#compactAddLength. 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: InputWasm.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
function InputWasm ({ onChange, ...props }: Props): React.ReactElement<Props> {
  const _onChange = useCallback(
    (wasm: Uint8Array, name: string): void => {
      const isValid = isWasm(wasm);

      onChange(compactAddLength(wasm), isValid, name);
    },
    [onChange]
  );

  return (
    <InputFile
      {...props}
      accept='application/wasm'
      onChange={_onChange}
    />
  );
}
Example #2
Source File: KeyValue.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
// eslint-disable-next-line @typescript-eslint/ban-types
export function createParam (hex: string | String, length = -1): StateParam {
  let u8a;

  try {
    u8a = hexToU8a(hex.toString());
  } catch (error) {
    u8a = new Uint8Array([]);
  }

  const isValid = length !== -1
    ? u8a.length === length
    : u8a.length !== 0;

  return {
    isValid,
    u8a: compactAddLength(u8a)
  };
}
Example #3
Source File: InputWasm.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function InputWasm({ onChange, ...props }: Props): React.ReactElement<Props> {
  const _onChange = useCallback(
    (wasm: Uint8Array, name: string): void => {
      const isValid = isWasm(wasm);

      onChange(compactAddLength(wasm), isValid, name);
    },
    [onChange]
  );

  return <InputFile {...props} accept="application/wasm" onChange={_onChange} />;
}
Example #4
Source File: KeyValue.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
// eslint-disable-next-line @typescript-eslint/ban-types
export function createParam(hex: string | String, length = -1): StateParam {
  let u8a;

  try {
    u8a = hexToU8a(hex.toString());
  } catch (error) {
    u8a = new Uint8Array([]);
  }

  const isValid = length !== -1 ? u8a.length === length : u8a.length !== 0;

  return {
    isValid,
    u8a: compactAddLength(u8a),
  };
}
Example #5
Source File: RegisterThread.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
function RegisterThread ({ className, onClose }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const { api } = useApi();
  const [accountId, setAccountId] = useState<string | null>(null);
  const [paraId, setParaId] = useState<BN | undefined>();
  const [{ isWasmValid, wasm }, setWasm] = useState<CodeState>({ isWasmValid: false, wasm: null });
  const [genesisState, setGenesisState] = useState<Uint8Array | null>(null);

  const _setGenesisState = useCallback(
    (data: Uint8Array) => setGenesisState(compactAddLength(data)),
    []
  );

  const _setWasm = useCallback(
    (wasm: Uint8Array, isWasmValid: boolean) => setWasm({ isWasmValid, wasm }),
    []
  );

  return (
    <Modal
      className={className}
      header={t<string>('Register parathread')}
      size='large'
    >
      <Modal.Content>
        <Modal.Columns hint={t<string>('This account will be associated with the parachain and pay the deposit.')}>
          <InputAddress
            label={t<string>('register from')}
            onChange={setAccountId}
            type='account'
            value={accountId}
          />
        </Modal.Columns>
        <Modal.Columns hint={t<string>('The id of this parachain as known on the network')}>
          <InputNumber
            autoFocus
            isZeroable={false}
            label={t<string>('parachain id')}
            onChange={setParaId}
          />
        </Modal.Columns>
        <Modal.Columns hint={t<string>('The WASM validation function for this parachain.')}>
          <InputWasm
            help={t<string>('The compiled runtime WASM for the parachain you wish to register.')}
            isError={!isWasmValid}
            label={t<string>('code')}
            onChange={_setWasm}
            placeholder={wasm && !isWasmValid && t<string>('The code is not recognized as being in valid WASM format')}
          />
        </Modal.Columns>
        <Modal.Columns hint={t<string>('The genesis state for this parachain.')}>
          <InputFile
            help={t<string>('The genesis state for the parachain.')}
            isError={!genesisState}
            label={t<string>('initial state')}
            onChange={_setGenesisState}
          />
        </Modal.Columns>
      </Modal.Content>
      <Modal.Actions onCancel={onClose}>
        <TxButton
          accountId={accountId}
          icon='plus'
          isDisabled={!isWasmValid || !genesisState || !paraId?.gt(BN_ZERO)}
          onStart={onClose}
          params={[paraId, genesisState, wasm]}
          tx={api.tx.registrar.register}
        />
      </Modal.Actions>
    </Modal>
  );
}
Example #6
Source File: Raw.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
function Raw ({ onAdd }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const [{ isValid, key }, setValue] = useState<{ isValid: boolean; key: Uint8Array }>({ isValid: false, key: new Uint8Array([]) });

  const _onAdd = useCallback(
    (): void => {
      isValid && onAdd({ isConst: false, key });
    },
    [isValid, key, onAdd]
  );

  const _onChangeKey = useCallback(
    (key: string): void => {
      const u8a = u8aToU8a(key);

      setValue({
        isValid: u8a.length !== 0,
        key: compactAddLength(u8a)
      });
    },
    []
  );

  return (
    <section className='storage--actionrow'>
      <div className='storage--actionrow-value'>
        <Input
          autoFocus
          label={t<string>('hex-encoded storage key')}
          onChange={_onChangeKey}
          onEnter={_onAdd}
        />
      </div>
      <div className='storage--actionrow-buttons'>
        <Button
          icon='plus'
          isDisabled={!isValid}
          onClick={_onAdd}
        />
      </div>
    </section>
  );
}
Example #7
Source File: BaseBytes.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
function BaseBytes ({ asHex, children, className = '', defaultValue: { value }, isDisabled, isError, label, length = -1, onChange, onEnter, onEscape, size = 'full', validate = defaultValidate, withCopy, withLabel, withLength }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const [defaultValue] = useState(
    value
      ? isDisabled && isU8a(value) && isAscii(value)
        ? u8aToString(value)
        : isHex(value)
          ? value
          : u8aToHex(value as Uint8Array, isDisabled ? 256 : -1)
      : undefined
  );
  const [isValid, setIsValid] = useState(false);

  const _onChange = useCallback(
    (hex: string): void => {
      let [isValid, value] = convertInput(hex);

      isValid = isValid && validate(value) && (
        length !== -1
          ? value.length === length
          : value.length !== 0
      );

      if (withLength && isValid) {
        value = compactAddLength(value);
      }

      onChange && onChange({
        isValid,
        value: asHex
          ? u8aToHex(value)
          : value
      });

      setIsValid(isValid);
    },
    [asHex, length, onChange, validate, withLength]
  );

  return (
    <Bare className={className}>
      <Input
        className={size}
        defaultValue={defaultValue as string}
        isAction={!!children}
        isDisabled={isDisabled}
        isError={isError || !isValid}
        label={label}
        onChange={_onChange}
        onEnter={onEnter}
        onEscape={onEscape}
        placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
        type='text'
        withEllipsis
        withLabel={withLabel}
      >
        {children}
        {withCopy && (
          <CopyButton value={defaultValue} />
        )}
      </Input>
    </Bare>
  );
}
Example #8
Source File: Bytes.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
function Bytes ({ className = '', defaultValue, isDisabled, isError, label, name, onChange, onEnter, onEscape, type, withLabel }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const [isValid, setIsValid] = useState(false);
  const [isFileDrop, setFileInput] = useState(false);

  const _onChangeFile = useCallback(
    (value: Uint8Array): void => {
      const isValid = value.length !== 0;

      onChange && onChange({
        isValid,
        value: compactAddLength(value)
      });

      setIsValid(isValid);
    },
    [onChange]
  );

  return (
    <div className={className}>
      {!isDisabled && isFileDrop
        ? (
          <File
            isDisabled={isDisabled}
            isError={isError || !isValid}
            label={label}
            onChange={_onChangeFile}
            withLabel={withLabel}
          />
        )
        : (
          <BaseBytes
            defaultValue={defaultValue}
            isDisabled={isDisabled}
            isError={isError}
            label={label}
            length={-1}
            name={name}
            onChange={onChange}
            onEnter={onEnter}
            onEscape={onEscape}
            type={type}
            withLabel={withLabel}
            withLength
          />
        )
      }
      {!isDisabled && (
        <Toggle
          isOverlay
          label={t<string>('file upload')}
          onChange={setFileInput}
          value={isFileDrop}
        />
      )}
    </div>
  );
}
Example #9
Source File: index.ts    From contracts-ui with GNU General Public License v3.0 5 votes vote down vote up
export function encodeSalt(salt: Uint8Array | string | null = randomAsU8a()): Uint8Array {
  return salt instanceof Bytes
    ? salt
    : salt && salt.length
    ? compactAddLength(u8aToU8a(salt))
    : EMPTY_SALT;
}
Example #10
Source File: getDeriveAccount.ts    From parity-bridges-ui with GNU General Public License v3.0 5 votes vote down vote up
export default function getDeriveAccount({ ss58Format = 42, bridgeId, address }: Data): string {
  if (!address) {
    return address;
  }
  const input = [...compactAddLength(stringToU8a(accountDerivation)), ...bridgeId, ...decodeAddress(address)];
  return encodeAddress(blake2AsHex(Uint8Array.from(input)), ss58Format);
}
Example #11
Source File: BaseBytes.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
function BaseBytes({
  asHex,
  children,
  className = '',
  defaultValue: { value },
  isDisabled,
  isError,
  label,
  length = -1,
  onChange,
  onEnter,
  onEscape,
  size = 'full',
  validate = defaultValidate,
  withCopy,
  withLabel,
  withLength,
}: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const [defaultValue] = useState(
    value
      ? isDisabled && isU8a(value) && isAscii(value)
        ? u8aToString(value)
        : isHex(value)
        ? value
        : // eslint-disable-next-line no-magic-numbers
          u8aToHex(value as Uint8Array, isDisabled ? 256 : -1)
      : undefined
  );
  const [isValid, setIsValid] = useState(false);

  const _onChange = useCallback(
    (hex: string): void => {
      let [beValid, val] = convertInput(hex);

      beValid = beValid && validate(val) && (length !== -1 ? val.length === length : val.length !== 0);

      if (withLength && beValid) {
        val = compactAddLength(val);
      }

      // eslint-disable-next-line
      onChange &&
        onChange({
          isValid: beValid,
          value: asHex ? u8aToHex(val) : val,
        });

      setIsValid(beValid);
    },
    [asHex, length, onChange, validate, withLength]
  );

  return (
    <Bare className={className}>
      <Input
        className={size}
        defaultValue={defaultValue as string}
        isAction={!!children}
        isDisabled={isDisabled}
        isError={isError || !isValid}
        label={label}
        onChange={_onChange}
        onEnter={onEnter}
        onEscape={onEscape}
        placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
        type="text"
        withEllipsis
        withLabel={withLabel}
      >
        {children}
        {withCopy && <CopyButton value={defaultValue} />}
      </Input>
    </Bare>
  );
}
Example #12
Source File: Bytes.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
function Bytes({
  className = '',
  defaultValue,
  isDisabled,
  isError,
  label,
  name,
  onChange,
  onEnter,
  onEscape,
  type,
  withLabel,
}: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const [isValid, setIsValid] = useState(false);
  const [isFileDrop, setFileInput] = useState(false);

  const _onChangeFile = useCallback(
    (value: Uint8Array): void => {
      const beValid = value.length !== 0;

      // eslint-disable-next-line
      onChange &&
        onChange({
          isValid: beValid,
          value: compactAddLength(value),
        });

      setIsValid(beValid);
    },
    [onChange]
  );

  return (
    <div className={className}>
      {!isDisabled && isFileDrop ? (
        <File
          isDisabled={isDisabled}
          isError={isError || !isValid}
          label={label}
          onChange={_onChangeFile}
          withLabel={withLabel}
        />
      ) : (
        <BaseBytes
          defaultValue={defaultValue}
          isDisabled={isDisabled}
          isError={isError}
          label={label}
          length={-1}
          name={name}
          onChange={onChange}
          onEnter={onEnter}
          onEscape={onEscape}
          type={type}
          withLabel={withLabel}
          withLength
        />
      )}
      {!isDisabled && <Toggle isOverlay label={t<string>('file upload')} onChange={setFileInput} value={isFileDrop} />}
    </div>
  );
}
Example #13
Source File: Propose.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function Propose ({ className, onClose }: Props): React.ReactElement<Props> {
  const { t } = useTranslation();
  const { api } = useApi();
  const [accountId, setAccountId] = useState<string | null>(null);
  const [name, setName] = useState('');
  const [paraId, setParaId] = useState<BN | undefined>();
  const [balance, setBalance] = useState(() => BN_THOUSAND.mul(BN_TEN.pow(new BN(api.registry.chainDecimals[0]))));
  const [validators, setValidators] = useState<string[]>(['']);
  const [{ isWasmValid, wasm }, setWasm] = useState<CodeState>({ isWasmValid: false, wasm: null });
  const [genesisState, setGenesisState] = useState<Uint8Array | null>(null);

  const _setGenesisState = useCallback(
    (data: Uint8Array) => setGenesisState(compactAddLength(data)),
    []
  );

  const _setWasm = useCallback(
    (wasm: Uint8Array, isWasmValid: boolean) => setWasm({ isWasmValid, wasm }),
    []
  );

  const _setAddress = useCallback(
    (index: number, address: string) =>
      setValidators((v) => v.map((v, i) => i === index ? address : v)),
    []
  );

  const _addValidator = useCallback(
    () => setValidators((v) => [...v, '']),
    []
  );

  const _delValidator = useCallback(
    () => setValidators((v) => [...v.slice(0, v.length - 1)]),
    []
  );

  const isNameValid = name.length >= 3;
  const isValDuplicate = validators.some((a, ai) => validators.some((b, bi) => ai !== bi && a === b));

  return (
    <Modal
      className={className}
      header={t<string>('Propose parachain')}
      size='large'
    >
      <Modal.Content>
        <Modal.Columns hint={t<string>('This account will be associated with the parachain and pay the deposit.')}>
          <InputAddress
            label={t<string>('propose from')}
            onChange={setAccountId}
            type='account'
            value={accountId}
          />
        </Modal.Columns>
        <Modal.Columns hint={t<string>('The name for this parachain, the id and the allocated/requested balance.')}>
          <Input
            autoFocus
            isError={!isNameValid}
            label={t<string>('parachain name')}
            onChange={setName}
          />
          <InputNumber
            isZeroable={false}
            label={t<string>('requested id')}
            onChange={setParaId}
          />
          <InputBalance
            defaultValue={balance}
            label={t<string>('initial balance')}
            onChange={setBalance}
          />
        </Modal.Columns>
        <Modal.Columns hint={t<string>('The WASM validation function as well as the genesis state for this parachain.')}>
          <InputWasm
            help={t<string>('The compiled runtime WASM for the parachain you wish to register.')}
            isError={!isWasmValid}
            label={t<string>('validation code')}
            onChange={_setWasm}
            placeholder={wasm && !isWasmValid && t<string>('The code is not recognized as being in valid WASM format')}
          />
          <InputFile
            help={t<string>('The genesis state for the parachain.')}
            isError={!genesisState}
            label={t<string>('genesis state')}
            onChange={_setGenesisState}
          />
        </Modal.Columns>
        <Modal.Columns hint={t<string>('The validators for this parachain. At least one is required and where multiple is supplied, they need to be unique.')}>
          {validators.map((address, index) => (
            <Validator
              address={address}
              index={index}
              key={index}
              setAddress={_setAddress}
              t={t}
            />
          ))}
          {!validators.length && (
            <MarkWarning content={t<string>('You need to supply at last one running validator for your parachain alongside this request.')} />
          )}
          {isValDuplicate && (
            <MarkWarning content={t<string>('You have duplicated validator entries, ensure each is unique.')} />
          )}
          <Button.Group>
            <Button
              icon='plus'
              label={t<string>('Add validator')}
              onClick={_addValidator}
            />
            <Button
              icon='minus'
              isDisabled={validators.length === 0}
              label={t<string>('Remove validator')}
              onClick={_delValidator}
            />
          </Button.Group>
        </Modal.Columns>
      </Modal.Content>
      <Modal.Actions onCancel={onClose}>
        <TxButton
          accountId={accountId}
          icon='plus'
          isDisabled={!isWasmValid || !genesisState || !isNameValid || !validators.length || !paraId?.gt(BN_ZERO)}
          onStart={onClose}
          params={[paraId, name, wasm, genesisState, validators, balance]}
          tx={api.tx.proposeParachain?.proposeParachain}
        />
      </Modal.Actions>
    </Modal>
  );
}
Example #14
Source File: useEstimatedFeePayload.ts    From parity-bridges-ui with GNU General Public License v3.0 4 votes vote down vote up
useEstimatedFeePayload = (
  transactionState: TransactionState,
  dispatchTransaction: Dispatch<TransactionsActionType>
) => {
  const { createType, stateCall } = useApiCallsContext();

  const laneId = useLaneId();
  const sourceTargetDetails = useSourceTarget();
  const {
    sourceChainDetails: {
      chain: sourceChain,
      apiConnection: { api: sourceApi }
    },
    targetChainDetails: {
      apiConnection: { api: targetApi },
      chain: targetChain
    }
  } = sourceTargetDetails;
  const { account, senderAccountBalance, senderCompanionAccountBalance } = useAccountContext();
  const { action, isBridged } = useGUIContext();
  const { estimatedFeeMethodName } = getSubstrateDynamicNames(targetChain);
  const previousPayloadEstimatedFeeLoading = usePrevious(transactionState.payloadEstimatedFeeLoading);
  const { bridgedMessages } = getSubstrateDynamicNames(targetChain);

  const dispatch = useCallback(
    (error: string | null, data: PayloadEstimatedFee | null, loading: boolean) =>
      dispatchTransaction(
        TransactionActionCreators.setPayloadEstimatedFee({
          payloadEstimatedFeeError: error,
          payloadEstimatedFee: data,
          payloadEstimatedFeeLoading: loading,
          sourceTargetDetails,
          createType,
          isBridged,
          senderAccountBalance,
          senderCompanionAccountBalance,
          chainDecimals: targetApi.registry.chainDecimals[0]
        })
      ),
    [
      createType,
      dispatchTransaction,
      isBridged,
      senderAccountBalance,
      senderCompanionAccountBalance,
      sourceTargetDetails,
      targetApi.registry.chainDecimals
    ]
  );

  const calculateFeeAndPayload = useCallback(
    async (currentTransactionState: TransactionState) => {
      if (currentTransactionState.action === TransactionTypes.INTERNAL_TRANSFER) {
        const { estimatedFee, weight } = await getFeeAndWeightForInternals({
          api: sourceApi,
          transactionState: currentTransactionState
        });
        const payload = {
          sourceAccount: currentTransactionState.senderAccount,
          transferAmount: currentTransactionState.transferAmount!.toNumber(),
          receiverAddress: currentTransactionState.receiverAddress,
          weight
        };
        return { estimatedSourceFee: estimatedFee, payload };
      }
      const { call, weight } = await getTransactionCallWeight({
        action,
        account,
        targetApi,
        transactionState: currentTransactionState
      });

      if (!call || !weight) {
        return emptyData;
      }

      const callToCompact = currentTransactionState.action === TransactionTypes.CUSTOM ? call : call.slice(2);
      const payload = {
        call: compactAddLength(callToCompact),
        origin: {
          SourceAccount: account!.addressRaw
        },
        dispatch_fee_payment: currentTransactionState.payFee,
        spec_version: targetApi.consts.system.version.specVersion.toNumber(),
        weight
      };

      const payloadType = createType(sourceChain as keyof InterfaceTypes, 'OutboundPayload', payload);
      logger.info(`OutboundPayload: ${JSON.stringify(payload)}`);
      logger.info(`OutboundPayload.toHex(): ${payloadType.toHex()}`);
      const messageFeeType = createType(sourceChain as keyof InterfaceTypes, 'MessageFeeData', {
        lane_id: laneId,
        payload: payloadType.toHex()
      });

      // estimatedFeeMessageDelivery
      const estimatedFeeCall = await stateCall(sourceChain, estimatedFeeMethodName, messageFeeType.toHex());
      const estimatedFeeType = createType(sourceChain as keyof InterfaceTypes, 'Option<Balance>', estimatedFeeCall);
      const estimatedFeeMessageDelivery = estimatedFeeType.toString();

      // estimatedFeeBridgeCall
      const bridgeMessage = sourceApi.tx[bridgedMessages].sendMessage(laneId, payload, estimatedFeeCall);
      const submitMessageTransactionFee = await sourceApi.rpc.payment.queryFeeDetails(bridgeMessage.toHex());
      const estimatedFeeBridgeCallBalance = (submitMessageTransactionFee as FeeDetails).inclusionFee.unwrap()
        .adjustedWeightFee;
      const estimatedFeeBridgeCall = estimatedFeeBridgeCallBalance.toString();

      // estimatedSourceFee calculation based on the sum of estimatedFeeMessageDelivery + estimatedFeeBridgeCallBalance
      const estimatedSourceFeeBN = new BN(estimatedFeeMessageDelivery).add(estimatedFeeBridgeCallBalance.toBn());
      const estimatedSourceFee = estimatedSourceFeeBN.toString();

      // estimatedTargetFee
      const targetFeeDetails = await targetApi.rpc.payment.queryFeeDetails(u8aToHex(call));
      const estimatedTargetFee = (targetFeeDetails as FeeDetails).inclusionFee.unwrap().adjustedWeightFee.toString();

      return {
        estimatedSourceFee,
        estimatedFeeMessageDelivery,
        estimatedFeeBridgeCall,
        estimatedTargetFee,
        payload
      };
    },
    [
      account,
      action,
      bridgedMessages,
      createType,
      estimatedFeeMethodName,
      laneId,
      sourceApi,
      sourceChain,
      stateCall,
      targetApi
    ]
  );

  useEffect(() => {
    const { shouldEvaluatePayloadEstimatedFee, payloadEstimatedFeeLoading } = transactionState;

    if (shouldEvaluatePayloadEstimatedFee && payloadEstimatedFeeLoading) {
      logger.info(
        'Transaction information changed while estimated fee is being calculating. Batching the new calculation.'
      );
      dispatchTransaction(TransactionActionCreators.setBatchedEvaluationPayloadEstimatedFee(transactionState));
    }
    if (shouldEvaluatePayloadEstimatedFee && !payloadEstimatedFeeLoading) {
      genericCall({
        //@ts-ignore
        call: () => calculateFeeAndPayload(transactionState),
        dispatch,
        emptyData
      });
    }
  }, [calculateFeeAndPayload, dispatch, dispatchTransaction, transactionState]);

  useEffect(() => {
    const { batchedTransactionState, payloadEstimatedFeeLoading } = transactionState;

    if (
      previousPayloadEstimatedFeeLoading &&
      !payloadEstimatedFeeLoading &&
      batchedTransactionState &&
      senderAccountBalance &&
      senderCompanionAccountBalance
    ) {
      genericCall({
        //@ts-ignore
        call: () => calculateFeeAndPayload(batchedTransactionState),
        dispatch,
        emptyData
      });
      dispatchTransaction(TransactionActionCreators.setBatchedEvaluationPayloadEstimatedFee(null));
    }
  }, [
    account,
    calculateFeeAndPayload,
    dispatch,
    dispatchTransaction,
    previousPayloadEstimatedFeeLoading,
    senderAccountBalance,
    senderCompanionAccountBalance,
    transactionState
  ]);
}
Example #15
Source File: transactionReducer.test.ts    From parity-bridges-ui with GNU General Public License v3.0 4 votes vote down vote up
describe('transactionReducer', () => {
  describe('SET_RECEIVER', () => {
    const payload = {
      unformattedReceiverAddress: '',
      sourceChainDetails,
      targetChainDetails,
      isBridged: true
    };
    describe('Bridge', () => {
      it('should return the according transaction state for a companion address and its corresponding companion account.', () => {
        payload.unformattedReceiverAddress = '5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2';
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          derivedReceiverAccount: '714dr3fW9PAKWMRn9Zcr6vtqn8gUEoKF7E2bDu5BniTMS4bo',
          receiverAddress: '5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2',
          unformattedReceiverAddress: '5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2',
          showBalance: true,
          formatFound: 'chain1',
          transactionReadyToExecute: false,
          payloadEstimatedFeeLoading: false,
          shouldEvaluatePayloadEstimatedFee: false,
          estimatedSourceFee: null,
          estimatedFeeMessageDelivery: null,
          estimatedFeeBridgeCall: null,
          estimatedTargetFee: null
        });
      });

      it('should return the according transaction state for a target native address.', () => {
        payload.unformattedReceiverAddress = '74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5';
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          receiverAddress: '74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5',
          unformattedReceiverAddress: '74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5',
          showBalance: true,
          formatFound: 'chain2',
          transactionReadyToExecute: false,
          shouldEvaluatePayloadEstimatedFee: false
        });
      });

      it('should return the according transaction state for a generic address.', () => {
        payload.unformattedReceiverAddress = '5H3ZryLmpNwrochemdVFTq9WMJW39NCo5HWFEwRtjbVtrThD';
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          genericReceiverAccount: '5H3ZryLmpNwrochemdVFTq9WMJW39NCo5HWFEwRtjbVtrThD',
          unformattedReceiverAddress: '5H3ZryLmpNwrochemdVFTq9WMJW39NCo5HWFEwRtjbVtrThD',
          formatFound: 'GENERIC',
          transactionReadyToExecute: false,
          shouldEvaluatePayloadEstimatedFee: false,
          estimatedSourceFee: null,
          estimatedFeeMessageDelivery: null,
          estimatedFeeBridgeCall: null,
          estimatedTargetFee: null
        });
      });

      it('should return the according transaction state for an invalid address.', () => {
        payload.unformattedReceiverAddress = 'invalid';
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          unformattedReceiverAddress: 'invalid',
          addressValidationError: 'Invalid address',
          formatFound: 'INCORRECT_FORMAT',
          transactionReadyToExecute: false,
          shouldEvaluatePayloadEstimatedFee: false
        });
      });

      it('should return the according transaction state for unsupported prefix provided.', () => {
        payload.unformattedReceiverAddress = 'tH95Ew4kVD9VcwsyXaSdC74Noe3H8o6fJfnKhZezXHKHEcs';
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          unformattedReceiverAddress: 'tH95Ew4kVD9VcwsyXaSdC74Noe3H8o6fJfnKhZezXHKHEcs',
          addressValidationError: 'Unsupported address SS58 prefix: 8',
          formatFound: '8',
          transactionReadyToExecute: false,
          shouldEvaluatePayloadEstimatedFee: false
        });
      });
    });
    describe('Local', () => {
      it('should return the according transaction state for native address.', () => {
        payload.unformattedReceiverAddress = '5smXQvbC88CDjy8SCpmPjEk4EDMoUTyvCvq2w3HhWj4pYQqz';
        payload.isBridged = false;
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          unformattedReceiverAddress: payload.unformattedReceiverAddress,
          receiverAddress: payload.unformattedReceiverAddress,
          genericReceiverAccount: null,
          addressValidationError: null,
          showBalance: true,
          formatFound: payload.sourceChainDetails.chain
        });
      });

      it('should return the according transaction state for generic address.', () => {
        payload.unformattedReceiverAddress = '5H3ZryLmpNwrochemdVFTq9WMJW39NCo5HWFEwRtjbVtrThD';
        payload.isBridged = false;
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          unformattedReceiverAddress: payload.unformattedReceiverAddress,
          receiverAddress: '5smXQvbC88CDjy8SCpmPjEk4EDMoUTyvCvq2w3HhWj4pYQqz',
          genericReceiverAccount: null,
          addressValidationError: null,
          showBalance: true,
          formatFound: GENERIC
        });
      });

      it('should return invalid account.', () => {
        payload.unformattedReceiverAddress = '74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5';
        payload.isBridged = false;
        const action = TransactionActionCreators.setReceiver(payload);
        const result = transactionReducer(state, action);
        expect(result).toEqual({
          ...state,
          unformattedReceiverAddress: payload.unformattedReceiverAddress,
          addressValidationError: 'Invalid Address',
          receiverAddress: null,
          genericReceiverAccount: null,
          showBalance: false,
          formatFound: null
        });
      });
    });
  });

  describe('SET_PAYLOAD_ESTIMATED_FEE', () => {
    type PayloadEstimatedFee = {
      payload: TransactionPayload | null;
      estimatedSourceFee: string | null;
      estimatedFeeMessageDelivery: string | null;
      estimatedFeeBridgeCall: string | null;
      estimatedTargetFee: string | null;
    };

    let payloadEstimatedFeeError: string | null;
    let payloadEstimatedFee: PayloadEstimatedFee;
    let payloadEstimatedFeeLoading: boolean;

    beforeEach(() => {
      payloadEstimatedFeeError = null;
      payloadEstimatedFee = {
        estimatedSourceFee: null,
        estimatedFeeMessageDelivery: null,
        estimatedFeeBridgeCall: null,
        estimatedTargetFee: null,
        payload: null
      };
      payloadEstimatedFeeLoading = false;
      (getTransactionDisplayPayload as jest.Mock).mockReturnValue({
        payloadHex: null,
        transactionDisplayPayload: null
      });
    });

    it('should return initial state regarding estimated fee', () => {
      const action = TransactionActionCreators.setPayloadEstimatedFee({
        payloadEstimatedFeeError,
        payloadEstimatedFee,
        payloadEstimatedFeeLoading,
        sourceTargetDetails: {} as SourceTargetState,
        //@ts-ignore
        createType: () => 'type',
        isBridged: true,
        senderAccountBalance: null,
        senderCompanionAccountBalance: null,
        chainDecimals: 9
      });
      const result = transactionReducer(state, action);

      expect(result).toEqual({
        ...state,
        estimatedSourceFee: null,
        estimatedFeeMessageDelivery: null,
        estimatedFeeBridgeCall: null,
        estimatedTargetFee: null,
        payloadEstimatedFeeError: null,
        payloadEstimatedFeeLoading: false,
        payload: null,
        transactionReadyToExecute: false
      });
    });
    it('should return loading estimated fee', () => {
      const payloadEstimatedFeeLoading = true;
      const action = TransactionActionCreators.setPayloadEstimatedFee({
        payloadEstimatedFeeError,
        payloadEstimatedFee,
        payloadEstimatedFeeLoading,
        sourceTargetDetails: {} as SourceTargetState,
        //@ts-ignore
        createType: () => 'type',
        isBridged: true,
        senderAccountBalance: null,
        senderCompanionAccountBalance: null,
        chainDecimals: 9
      });
      const result = transactionReducer(state, action);

      expect(result).toEqual({
        ...state,
        estimatedSourceFee: null,
        estimatedFeeMessageDelivery: null,
        estimatedFeeBridgeCall: null,
        estimatedTargetFee: null,
        payloadEstimatedFeeError: null,
        payloadEstimatedFeeLoading: true,
        payload: null,
        transactionReadyToExecute: false,
        payloadHex: null,
        transactionDisplayPayload: null
      });
    });

    it('should return corresponding error state for estimated fee', () => {
      payloadEstimatedFeeError = 'Error';
      const action = TransactionActionCreators.setPayloadEstimatedFee({
        payloadEstimatedFeeError,
        payloadEstimatedFee,
        payloadEstimatedFeeLoading,
        sourceTargetDetails: {} as SourceTargetState,
        //@ts-ignore
        createType: () => 'type',
        isBridged: true,
        senderAccountBalance: null,
        senderCompanionAccountBalance: null,
        chainDecimals: 9
      });
      const result = transactionReducer(state, action);

      expect(result).toEqual({
        ...state,
        estimatedSourceFee: null,
        estimatedFeeMessageDelivery: null,
        estimatedFeeBridgeCall: null,
        estimatedTargetFee: null,
        payloadEstimatedFeeError,
        payloadEstimatedFeeLoading: false,
        payload: null,
        transactionReadyToExecute: false,
        payloadHex: null,
        transactionDisplayPayload: null
      });
    });

    it('should return corresponding error state for estimated fee', () => {
      payloadEstimatedFeeError = 'Error';
      const action = TransactionActionCreators.setPayloadEstimatedFee({
        payloadEstimatedFeeError,
        payloadEstimatedFee,
        payloadEstimatedFeeLoading,
        sourceTargetDetails: {} as SourceTargetState,
        //@ts-ignore
        createType: () => 'type',
        isBridged: true,
        senderAccountBalance: null,
        senderCompanionAccountBalance: null,
        chainDecimals: 9
      });
      const result = transactionReducer(state, action);

      expect(result).toEqual({
        ...state,
        estimatedSourceFee: null,
        estimatedFeeMessageDelivery: null,
        estimatedFeeBridgeCall: null,
        estimatedTargetFee: null,
        payloadEstimatedFeeError,
        payloadEstimatedFeeLoading: false,
        payload: null,
        transactionReadyToExecute: false,
        payloadHex: null,
        transactionDisplayPayload: null
      });
    });

    it('should return corresponding estimated fee, payload and all the necessary conditions to execute the transaction', () => {
      const payloadHex = 'payloadHexMode';

      const createType = jest.fn();
      const payload = {
        call: compactAddLength(new Uint8Array([0, 0, 0, 0])),
        origin: {
          SourceAccount: new Uint8Array([0, 0, 0, 0])
        },
        spec_version: 1,
        weight: 1234
      };
      const transactionDisplayPayload = { payload };
      (getTransactionDisplayPayload as jest.Mock).mockReturnValue({
        payloadHex,
        transactionDisplayPayload
      });
      const estimatedSourceFee = '1234';
      const estimatedFeeMessageDelivery = '1234';
      const estimatedFeeBridgeCall = '1234';
      const estimatedTargetFee = '1234';
      payloadEstimatedFee = {
        estimatedSourceFee,
        estimatedFeeMessageDelivery,
        estimatedFeeBridgeCall,
        estimatedTargetFee,
        payload
      };
      const sourceTargetDetails = {} as SourceTargetState;
      const action = TransactionActionCreators.setPayloadEstimatedFee({
        payloadEstimatedFeeError,
        payloadEstimatedFee,
        payloadEstimatedFeeLoading,
        sourceTargetDetails,
        //@ts-ignore
        createType: () => 'type',
        isBridged: true,
        senderAccountBalance: null,
        senderCompanionAccountBalance: null,
        chainDecimals: 9
      });

      const newState = { ...state };
      newState.action = TransactionTypes.TRANSFER;
      newState.transferAmount = new BN(1);
      newState.receiverAddress = '74GNQjmkcfstRftSQPJgMREchqHM56EvAUXRc266cZ1NYVW5';
      newState.senderAccount = '5rERgaT1Z8nM3et2epA5i1VtEBfp5wkhwHtVE8HK7BRbjAH2';
      const result = transactionReducer(newState, action);

      expect(result).toEqual({
        ...newState,
        estimatedSourceFee,
        estimatedFeeMessageDelivery,
        estimatedFeeBridgeCall,
        estimatedTargetFee,
        payloadEstimatedFeeError: null,
        payloadEstimatedFeeLoading: false,
        payload,
        transactionReadyToExecute: true,
        payloadHex,
        transactionDisplayPayload
      });
    });
  });
});