@polkadot/types/interfaces#StatementKind TypeScript Examples

The following examples show how to use @polkadot/types/interfaces#StatementKind. 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: util.ts    From crust-apps with Apache License 2.0 7 votes vote down vote up
function getPolkadot (kind?: StatementKind | null): Statement | undefined {
  if (!kind) {
    return undefined;
  }

  const url = kind.isRegular
    ? 'https://statement.polkadot.network/regular.html'
    : 'https://statement.polkadot.network/saft.html';
  const hash = kind.isRegular
    ? 'Qmc1XYqT6S39WNp2UeiRUrZichUWUPpGEThDE6dAb3f6Ny'
    : 'QmXEkMahfhHJPzT3RjkXiZVFi77ZeVeuxtAjhojGRNYckz';

  return {
    sentence: `I hereby agree to the terms of the statement whose SHA-256 multihash is ${hash}. (This may be found at the URL: ${url})`,
    url
  };
}
Example #2
Source File: util.ts    From crust-apps with Apache License 2.0 7 votes vote down vote up
export function getStatement (network: string, kind?: StatementKind | null): Statement | undefined {
  switch (network) {
    case 'Polkadot':
    case 'Polkadot CC1':
      return getPolkadot(kind);

    default:
      return undefined;
  }
}
Example #3
Source File: Claim.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
// Depending on isOldClaimProcess, construct the correct tx.
// FIXME We actually want to return the constructed extrinsic here (probably in useMemo)
function constructTx (api: ApiPromise, systemChain: string, accountId: string, ethereumSignature: string | null, kind: StatementKind | undefined, isOldClaimProcess: boolean, ethereumTxHash: string): ConstructTx {
  if (!ethereumSignature) {
    return {};
  }

  return isOldClaimProcess || !kind
    ? { params: [accountId, ethereumTxHash, ethereumSignature], tx: api.tx.claims.claim }
    : { params: [accountId, ethereumSignature, getStatement(systemChain, kind)?.sentence], tx: api.tx.claims.claimAttest };
}
Example #4
Source File: PreClaim.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
// Depending on isOldClaimProcess, construct the correct tx.
// FIXME We actually want to return the constructed extrinsic here (probably in useMemo)
function constructTx (api: ApiPromise, accountId: string, ethereumSignature: string | null, kind: StatementKind | undefined, isOldClaimProcess: boolean, ethereumTxHash: string): ConstructTx {
  if (!ethereumSignature) {
    return {};
  }

  return isOldClaimProcess || !kind
    ? { params: [accountId, ethereumSignature], tx: api.tx.claims.claimCru18 }
    : { params: [accountId, ethereumSignature], tx: api.tx.claims.claimCru18 };
}
Example #5
Source File: Claim.tsx    From crust-apps with Apache License 2.0 6 votes vote down vote up
// Depending on isOldClaimProcess, construct the correct tx.
// FIXME We actually want to return the constructed extrinsic here (probably in useMemo)
function constructTx (api: ApiPromise, systemChain: string, accountId: string, ethereumSignature: string | null, kind: StatementKind | undefined, isOldClaimProcess: boolean, ethereumTxHash: string): ConstructTx {
  if (!ethereumSignature) {
    return {};
  }

  return isOldClaimProcess || !kind
    ? { params: [accountId, ethereumTxHash, ethereumSignature], tx: api.tx.claims.claimCsm }
    : { params: [accountId, ethereumSignature, getStatement(systemChain, kind)?.sentence], tx: api.tx.claims.claimAttest };
}
Example #6
Source File: index.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
transformStatement = {
  transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #7
Source File: index.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
transformStatement = {
  transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #8
Source File: index.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
transformStatement = {
  transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #9
Source File: index.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
transformStatement = {
  transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #10
Source File: index.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function Claims(): React.ReactElement<Props> {
  const [didCopy, setDidCopy] = useState(false);
  const [ethereumAddress, setEthereumAddress] = useState<string | undefined | null>(null);
  const [signature, setSignature] = useState<EcdsaSignature | null>(null);
  const [step, setStep] = useState<Step>(Step.Account);
  const [accountId, setAccountId] = useState<string | null>(null);
  const { api, systemChain } = useApi();
  const { t } = useTranslation();
  const [statusOpen, setStatusOpen] = useState<boolean>(false);
  const [result, setResult] = useState<string>('');
  const [status, setStatus] = useState<string>('');
  const [ethereumTxHashValid, setEthereumTxHashValid] = useState<boolean>(false);
  const [isBusy, setIsBusy] = useState<boolean>(false);
  const [isValid, setIsValid] = useState(false);
  const [ethereumTxHash, setEthereumTxHash] = useState<string | undefined | null>(null);

  // This preclaimEthereumAddress holds the result of `api.query.claims.preclaims`:
  // - an `EthereumAddress` when there's a preclaim
  // - null if no preclaim
  // - `PRECLAIMS_LOADING` if we're fetching the results
  const [preclaimEthereumAddress, setPreclaimEthereumAddress] = useState<string | null | undefined | typeof PRECLAIMS_LOADING>(PRECLAIMS_LOADING);
  const isPreclaimed = !!preclaimEthereumAddress && preclaimEthereumAddress !== PRECLAIMS_LOADING;
  const claimLimit = useCall<BalanceOf>(api.query.claims.claimLimit);

  // Everytime we change account, reset everything, and check if the accountId
  // has a preclaim.
  useEffect(() => {
    if (!accountId) {
      return;
    }

    setStep(Step.Account);
    setEthereumAddress(null);
    setEthereumTxHash(null);
    setPreclaimEthereumAddress(PRECLAIMS_LOADING);

    if (!api.query.claims || !api.query.claims.preclaims) {
      return setPreclaimEthereumAddress(null);
    }

    api.query.claims
      .preclaims<Option<EthereumAddress>>(accountId)
      .then((preclaim): void => {
        const address = preclaim.unwrapOr(null)?.toString();

        setEthereumAddress(address);
        setPreclaimEthereumAddress(address);
      })
      .catch((): void => setPreclaimEthereumAddress(null));
  }, [accountId, api.query.claims, api.query.claims.preclaims]);

  // Old claim process used `api.tx.claims.claim`, and didn't have attest
  const isOldClaimProcess = !api.tx.claims.claimAttest;

  useEffect(() => {
    if (didCopy) {
      setTimeout((): void => {
        setDidCopy(false);
      }, 1000);
    }
  }, [didCopy]);

  const goToStepAccount = useCallback(() => {
    setStep(Step.Account);
    setEthereumTxHash("");
    setEthereumTxHashValid(false);
  }, []);

  const goToStepSign = useCallback(() => {
    setStep(Step.Sign);
  }, []);

  const goToStepClaim = useCallback(() => {
    setStep(Step.Claim);
  }, []);

  const handleAccountStep = useCallback(async () => {
    setIsBusy(true);

    api.query.claims
      .claimed<Option<BalanceOf>>(ethereumTxHash?.toString())
      .then(async (claim): Promise<void> => {
        const isClaimed = JSON.parse(JSON.stringify(claim));

        if (isClaimed) {
          setResult('AlreadyClaimed');
          setStatus('error');
          setStatusOpen(true);
        } else {
          const result = await httpPost("https://claim.crust.network/claim/" + ethereumTxHash);
          setIsBusy(false);
          setResult(result.statusText);
          setStatus(result.status);

          if (result.code == 200) {
            setEthereumTxHashValid(true);
            goToStepSign();
          } else {
            api.query.claims
              .claims<Option<BalanceOf>>(ethereumTxHash?.toString())
              .then(async (claim): Promise<void> => {
                const claimOpt = JSON.parse(JSON.stringify(claim));
                if (claimOpt) {
                  setResult('MintClaimSuccess');
                  setStatus('success');
                  setEthereumTxHashValid(true);
                  goToStepSign();
                } else {
                  setResult('MintError, Please try again');
                  setStatus('error');
                }
              })
              .catch((): void => setIsBusy(false));
          }
          setStatusOpen(true);
        }
      })
      .catch((): void => setIsBusy(false))
      .finally(() =>  setIsBusy(false));
  }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess, ethereumTxHash]);

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

    isValid = isValid && (
      length !== -1
        ? value.length === 32
        : value.length !== 0
    );
    setIsValid(isValid);
    setEthereumTxHash(hex.trim());
  }, [ethereumTxHash]);

  function convertInput(value: string): [boolean, Uint8Array] {
    if (value === '0x') {
      return [true, new Uint8Array([])];
    } else if (value.startsWith('0x')) {
      try {
        return [true, hexToU8a(value)];
      } catch (error) {
        return [false, new Uint8Array([])];
      }
    }

    // maybe it is an ss58?
    try {
      return [true, decodeAddress(value)];
    } catch (error) {
      // we continue
    }

    return isAscii(value)
      ? [true, stringToU8a(value)]
      : [value === '0x', new Uint8Array([])];
  }

  // Depending on the account, decide which step to show.
  // const handleAccountStep = useCallback(() => {
  //   if (isPreclaimed) {
  //     goToStepClaim();
  //   } else if (ethereumAddress || isOldClaimProcess) {
  //     goToStepSign();
  //   } else {
  //     setStep(Step.ETHAddress);
  //   }
  // }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess]);

  const onChangeSignature = useCallback((event: React.SyntheticEvent<Element>) => {
    const { value: signatureJson } = event.target as HTMLInputElement;

    const { ethereumAddress, signature } = recoverFromJSON(signatureJson);

    setEthereumAddress(ethereumAddress?.toString());
    setSignature(signature);
  }, []);

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

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

  const onCopy = useCallback(() => {
    setDidCopy(true);
  }, []);

  // If it's 1/ not preclaimed and 2/ not the old claiming process, fetch the
  // statement kind to sign.
  const statementKind = useCall<StatementKind | null>(!isPreclaimed && !isOldClaimProcess && !!ethereumAddress && api.query.claims.signing, [ethereumAddress], transformStatement);

  const statementSentence = getStatement(systemChain, statementKind)?.sentence || '';

  const prefix = u8aToString(api.consts.claims.prefix.toU8a(true));
  const payload = accountId
    ? `${prefix}${u8aToHex(decodeAddress(accountId), -1, false)}${statementSentence}${ethereumTxHash?.substring(2)}`
    : '';

  return (
    <main>
      {!isOldClaimProcess && <Warning />}
      <h2>
        {t<string>('Claim your {{token}} tokens', {
          replace: {
            token: TokenUnit.abbr
          }
        })}
      </h2>
      <Columar>
        <Columar.Column>
          <Card withBottomMargin>
            <h3>{t<string>('0. Burn your ')}<a href='https://etherscan.io/token/0x32a7C02e79c4ea1008dD6564b35F131428673c41'>{t('ERC20 CRU')}</a>{t<string>(', transfer to address ')} <a href='https://etherscan.io/address/0x0000000000000000000000000000000000000001' target="_blank">0x0000000000000000000000000000000000000001</a></h3>
            <Banner type='warning'>
              <p>{t<string>('Please make sure you have the authority to make signature with the private key of the wallet account, using an exchange account to sent a transfer (withdrawal) transaction will be invalidated and cause asset loss, you are responsible for the consequences')}</p>
            </Banner>
            <img style={{'marginLeft': 'auto', 'marginRight': 'auto', 'display': 'block', "width": "150px" }} src={burnPng as string} />
          </Card>
          {(<Card withBottomMargin>
            <h3>{t<string>(`1. Select your {{chain}} account and enter`, {
              replace: {
                chain: systemChain
              }
            })} <a href='https://etherscan.io/token/0x32a7C02e79c4ea1008dD6564b35F131428673c41'>{t('ERC20 CRU')}</a>
             {t<string>(' transfer tx hash')}<span>
               {t<string>(`, If your claim amount is greater than the claim limit `)}
               <span style={{ 'color': '#ff8812', 'textDecoration': 'underline', 'fontStyle': 'italic' }}>({formatBalance(claimLimit, { withUnit: 'CRU' })})</span>
               {t<string>(', please wait for the limit update')}
               </span>
            </h3>
            <InputAddress
              defaultValue={accountId}
              help={t<string>('The account you want to claim to.')}
              isDisabled={ethereumTxHashValid}
              label={t<string>('claim to account')}
              onChange={setAccountId}
              type='account'
            />
            <Input
              autoFocus
              className='full'
              help={t<string>('The Ethereum CRU transfer tx hash (starting by "0x")')}
              isDisabled={ethereumTxHashValid}
              isError={!isValid}
              label={t<string>('Ethereum tx hash')}
              onChange={onChangeEthereumTxHash}
              placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
              value={ethereumTxHash || ''}
            />
            {(step === Step.Account) && (<Button.Group>
              <Button
                icon='sign-in-alt'
                isBusy={isBusy}
                isDisabled={preclaimEthereumAddress === PRECLAIMS_LOADING || ethereumTxHash === null || ethereumTxHash === '' || !isValid}
                label={preclaimEthereumAddress === PRECLAIMS_LOADING
                  ? t<string>('Loading')
                  : t<string>('Continue')
                }
                onClick={handleAccountStep}
              />
            </Button.Group>)}
            <HttpStatus
              isStatusOpen={statusOpen}
              message={result}
              setStatusOpen={setStatusOpen}
              status={status}
            />
          </Card>)}
          {
            // We need to know the ethereuem address only for the new process
            // to be able to know the statement kind so that the users can sign it
            (step >= Step.ETHAddress && !isPreclaimed && !isOldClaimProcess) && (
              <Card withBottomMargin>
                <h3>{t<string>('2. Enter the ETH address from the sale.')}</h3>
                <Input
                  autoFocus
                  className='full'
                  help={t<string>('The the Ethereum address you burnt your ERC20 CRU in step 0 (starting by "0x")')}
                  label={t<string>('Pre-sale ethereum address')}
                  onChange={onChangeEthereumAddress}
                  value={ethereumAddress || ''}
                />
                {(step === Step.ETHAddress) && (
                  <Button.Group>
                    <Button
                      icon='sign-in-alt'
                      isDisabled={!ethereumAddress}
                      label={t<string>('Continue')}
                      onClick={goToStepSign}
                    />
                  </Button.Group>
                )}
              </Card>
            )}
            {(step >= Step.Sign && !isPreclaimed) && (
              <Card>
                <h3>{t<string>('{{step}}. Sign with your ETH address', { replace: { step: isOldClaimProcess ? '2' : '3' } })}</h3>
                {!isOldClaimProcess && (
                  <Statement
                    kind={statementKind}
                    systemChain={systemChain}
                  />
                )}
                <div>{t<string>('Copy the following string and sign it with the Ethereum account you burnt your ERC20 CRU in step 0, using the string as the payload, and then paste the transaction signature object below:')}</div>
                <CopyToClipboard
                  onCopy={onCopy}
                  text={payload}
                >
                  <Payload
                    data-for='tx-payload'
                    data-tip
                  >
                    {payload}
                  </Payload>
                </CopyToClipboard>
                <Tooltip
                  place='right'
                  text={didCopy ? t<string>('copied') : t<string>('click to copy')}
                  trigger='tx-payload'
                />
                <div>{t<string>('Paste the signed message into the field below. The placeholder text is there as a hint to what the message should look like:')}</div>
                <Signature
                  onChange={onChangeSignature}
                  placeholder={`{\n  "address": "0x ...",\n  "msg": "0x ...",\n  "sig": "...",\n  "version": "3",\n  "signer": "..."\n}`}
                  rows={10}
                />
                {(step === Step.Sign) && (
                  <Button.Group>
                    <Button
                      icon='sign-in-alt'
                      isDisabled={!accountId || !signature}
                      label={t<string>('Confirm claim')}
                      onClick={goToStepClaim}
                    />
                  </Button.Group>
                )}
              </Card>
            )}
        </Columar.Column>
        <Columar.Column>
            {(step >= Step.Claim) && (
              isPreclaimed
                ? <AttestDisplay
                  accountId={accountId}
                  ethereumAddress={ethereumAddress}
                  onSuccess={goToStepAccount}
                  statementKind={statementKind}
                  systemChain={systemChain}
                />
                : <ClaimDisplay
                  accountId={accountId}
                  ethereumAddress={ethereumAddress}
                  ethereumSignature={signature}
                  isOldClaimProcess={isOldClaimProcess}
                  onSuccess={goToStepAccount}
                  statementKind={statementKind}
                  ethereumTxHash={ethereumTxHash}
                />
            )}
        </Columar.Column>
      </Columar>
    </main>
  );
}
Example #11
Source File: index.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function ClaimsMainnet (): React.ReactElement<Props> {
  const [didCopy, setDidCopy] = useState(false);
  const [ethereumAddress, setEthereumAddress] = useState<string | undefined | null>(null);
  const [signature, setSignature] = useState<EcdsaSignature | null>(null);
  const [step, setStep] = useState<Step>(Step.Account);
  const [accountId, setAccountId] = useState<string | null>(null);
  const { api, systemChain } = useApi();
  const { t, i18n } = useTranslation();
  const [statusOpen, setStatusOpen] = useState<boolean>(false);
  const [result] = useState<string>('');
  const [status] = useState<string>('');
  const [ethereumTxHashValid] = useState<boolean>(false);
  const [isBusy] = useState<boolean>(false);
  const [ethereumTxHash, setEthereumTxHash] = useState<string | undefined | null>(null);

  // This preclaimEthereumAddress holds the result of `api.query.claims.preclaims`:
  // - an `EthereumAddress` when there's a preclaim
  // - null if no preclaim
  // - `PRECLAIMS_LOADING` if we're fetching the results
  const [preclaimEthereumAddress, setPreclaimEthereumAddress] = useState<string | null | undefined | typeof PRECLAIMS_LOADING>(PRECLAIMS_LOADING);
  const isPreclaimed = !!preclaimEthereumAddress && preclaimEthereumAddress !== PRECLAIMS_LOADING;

  // Everytime we change account, reset everything, and check if the accountId
  // has a preclaim.
  useEffect(() => {
    if (!accountId) {
      return;
    }

    setStep(Step.Account);
    setEthereumAddress(null);
    setEthereumTxHash(null);
    setPreclaimEthereumAddress(PRECLAIMS_LOADING);

    if (!api.query.claims || !api.query.claims.preclaims) {
      return setPreclaimEthereumAddress(null);
    }

    api.query.claims
      .preclaims<Option<EthereumAddress>>(accountId)
      .then((preclaim): void => {
        const address = preclaim.unwrapOr(null)?.toString();

        setEthereumAddress(address);
        setPreclaimEthereumAddress(address);
      })
      .catch((): void => setPreclaimEthereumAddress(null));
  }, [accountId, api.query.claims, api.query.claims.preclaims]);

  // Old claim process used `api.tx.claims.claim`, and didn't have attest
  const isOldClaimProcess = !api.tx.claims.claimAttest;

  useEffect(() => {
    if (didCopy) {
      setTimeout((): void => {
        setDidCopy(false);
      }, 1000);
    }
  }, [didCopy]);


  const goToStepSign = useCallback(() => {
    setStep(Step.Sign);
  }, []);

  const goToStepClaim = useCallback(() => {
    setStep(Step.Claim);
  }, []);

  const handleAccountStep = useCallback(async () => {
    goToStepSign();
  }, [goToStepSign, isPreclaimed, isOldClaimProcess, ethereumTxHash]);

  const onChangeSignature = useCallback((event: React.SyntheticEvent<Element>) => {
    const { value: signatureJson } = event.target as HTMLInputElement;

    const { ethereumAddress, signature } = recoverFromJSON(signatureJson);

    setEthereumAddress(ethereumAddress?.toString());
    setSignature(signature);
    goToStepSign();
  }, []);

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

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

  const onCopy = useCallback(() => {
    setDidCopy(true);
  }, []);

  // If it's 1/ not preclaimed and 2/ not the old claiming process, fetch the
  // statement kind to sign.
  const statementKind = useCall<StatementKind | null>(!isPreclaimed && !isOldClaimProcess && !!ethereumAddress && api.query.claims.signing, [ethereumAddress], transformStatement);

  const statementSentence = getStatement(systemChain, statementKind)?.sentence || '';

  const prefix = u8aToString(api.consts.claims.prefix.toU8a(true));
  const payload = accountId
    ? `${prefix}${u8aToHex(decodeAddress(accountId), -1, false)}${statementSentence}`
    : '';

  return (
    <main>
      {!isOldClaimProcess && <Warning />}
      
      
      <Columar>
        <Columar.Column>
          <Banner type='warning'>
            <p>
              <span style={{ "wordWrap": "break-word", "wordBreak": "break-all"  }}>
                <span>{t<string>('Since the mainnet democracy go live, users who have not yet claimed CRU18 can claim CRU18 ')}</span>
                <span style={{ 'fontWeight': 'bold' }}>{t<string>('before the block height #4,866,666 of mainnet,')}&nbsp;</span>
                <span>{t<string>('For the claiming tutorial, please refer to')}&nbsp;</span>
                <a href={ i18n.language == 'zh' ? "https://wiki-maxwell.crust.network/docs/zh-CN/claimCRU18" : 'https://wiki-maxwell.crust.network/docs/en/claimCRU18'} target="_blank">[Maxwell Wiki]</a> 
              </span>
            </p>
          </Banner>
          <Card withBottomMargin>
            {/* <h3>{t<string>(`1. Select your {{chain}} account and enter`, {
                replace: {
                  chain: systemChain
                }
              })} <a href='https://etherscan.io/token/0x32a7C02e79c4ea1008dD6564b35F131428673c41'>{t('ERC20 CRU')}</a> {t<string>('transfer tx hash')} </h3> */}
            <InputAddress
              defaultValue={accountId}
              help={t<string>('The account you want to claim to.')}
              isDisabled={ethereumTxHashValid}
              label={t<string>('claim to account')}
              onChange={setAccountId}
              type='account'
            />
            {/* <Dropdown
              defaultValue={accountId?.toString()}
              help={t<string>('The destination account for any payments as either a nominator or validator')}
              label={t<string>('token types')}
              onChange={setTokenType}
              options={options}
              value={tokenType}
            /> */}
            {(step === Step.Account) && (
              <Button.Group>
                <Button
                  icon='sign-in-alt'
                  isBusy={isBusy}
                  label={preclaimEthereumAddress === PRECLAIMS_LOADING
                    ? t<string>('Loading')
                    : t<string>('Continue')
                  }
                  onClick={handleAccountStep}
                />
              </Button.Group>
            )}
            <HttpStatus
              isStatusOpen={statusOpen}
              message={result}
              setStatusOpen={setStatusOpen}
              status={status}
            />
          </Card>
          {
            // We need to know the ethereuem address only for the new process
            // to be able to know the statement kind so that the users can sign it
            (step >= Step.ETHAddress && !isPreclaimed && !isOldClaimProcess) && (
              <Card withBottomMargin>
                <h3>{t<string>('2. Enter the ETH address from the sale.')}</h3>
                <Input
                  autoFocus
                  className='full'
                  help={t<string>('The the Ethereum address you used during the pre-sale (starting by "0x")')}
                  label={t<string>('Pre-sale ethereum address')}
                  onChange={onChangeEthereumAddress}
                  value={ethereumAddress || ''}
                />
                {(step === Step.ETHAddress) && (
                  <Button.Group>
                    <Button
                      icon='sign-in-alt'
                      isDisabled={!ethereumAddress}
                      label={t<string>('Continue')}
                      onClick={goToStepSign}
                    />
                  </Button.Group>
                )}
              </Card>
            )}
          {(step >= Step.Sign && !isPreclaimed) && (
            <Card>
              <h3>{t<string>('{{step}}. Sign with your ETH address', { replace: { step: isOldClaimProcess ? '2' : '3' } })}</h3>
              {!isOldClaimProcess && (
                <Statement
                  kind={statementKind}
                  systemChain={systemChain}
                />
              )}
              <div>{t<string>('Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:')}</div>
              <CopyToClipboard
                onCopy={onCopy}
                text={payload}
              >
                <Payload
                  data-for='tx-payload'
                  data-tip
                >
                  {payload}
                </Payload>
              </CopyToClipboard>
              <Tooltip
                place='right'
                text={didCopy ? t<string>('copied') : t<string>('click to copy')}
                trigger='tx-payload'
              />
              <div>{t<string>('Paste the signed message into the field below. The placeholder text is there as a hint to what the message should look like:')}</div>
              <Signature
                onChange={onChangeSignature}
                placeholder={`{\n  "address": "0x ...",\n  "msg": "${prefix}:...",\n  "sig": "0x ...",\n  "version": "3",\n  "signer": "..."\n}`}
                rows={10}
              />
              {(step === Step.Sign) && (
                <Button.Group>
                  <Button
                    icon='sign-in-alt'
                    isDisabled={!accountId || !signature}
                    label={t<string>('Confirm')}
                    onClick={goToStepClaim}
                  />
                </Button.Group>
              )}
            </Card>
          )}
        </Columar.Column>
        <Columar.Column>
          {(step >= Step.Claim) && (
            <PreClaimDisplay
              accountId={accountId}
              ethereumAddress={ethereumAddress}
              // tokenType={tokenType}
              ethereumSignature={signature}
              isOldClaimProcess={isOldClaimProcess}
              // onSuccess={goToStepAccount}
              statementKind={statementKind}
              ethereumTxHash={ethereumTxHash}
            />
          )}
        </Columar.Column>
      </Columar>
    </main>
  );
}
Example #12
Source File: index.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function MaxwellClaims (): React.ReactElement<Props> {
  const [didCopy, setDidCopy] = useState(false);
  const [ethereumAddress, setEthereumAddress] = useState<string | undefined | null>(null);
  const [signature, setSignature] = useState<EcdsaSignature | null>(null);
  const [step, setStep] = useState<Step>(Step.Account);
  const [accountId, setAccountId] = useState<string | null>(null);
  const { api, systemChain } = useApi();
  const { t } = useTranslation();
  const [statusOpen, setStatusOpen] = useState<boolean>(false);
  const [result, setResult] = useState<string>('');
  const [status, setStatus] = useState<string>('');
  const [ethereumTxHashValid, setEthereumTxHashValid] = useState<boolean>(false);
  const [isBusy, setIsBusy] = useState<boolean>(false);
  const [isValid, setIsValid] = useState(false);
  const [ethereumTxHash, setEthereumTxHash] = useState<string | undefined | null>(null);

  // This preclaimEthereumAddress holds the result of `api.query.claims.preclaims`:
  // - an `EthereumAddress` when there's a preclaim
  // - null if no preclaim
  // - `PRECLAIMS_LOADING` if we're fetching the results
  const [preclaimEthereumAddress, setPreclaimEthereumAddress] = useState<string | null | undefined | typeof PRECLAIMS_LOADING>(PRECLAIMS_LOADING);
  const isPreclaimed = !!preclaimEthereumAddress && preclaimEthereumAddress !== PRECLAIMS_LOADING;
  const claimLimit = useCall<BalanceOf>(api.query.claims.claimLimit);

  // Everytime we change account, reset everything, and check if the accountId
  // has a preclaim.
  useEffect(() => {
    if (!accountId) {
      return;
    }

    setStep(Step.Account);
    setEthereumAddress(null);
    setEthereumTxHash(null);
    setPreclaimEthereumAddress(PRECLAIMS_LOADING);

    if (!api.query.claims || !api.query.claims.preclaims) {
      return setPreclaimEthereumAddress(null);
    }

    api.query.claims
      .preclaims<Option<EthereumAddress>>(accountId)
      .then((preclaim): void => {
        const address = preclaim.unwrapOr(null)?.toString();

        setEthereumAddress(address);
        setPreclaimEthereumAddress(address);
      })
      .catch((): void => setPreclaimEthereumAddress(null));
  }, [accountId, api.query.claims, api.query.claims.preclaims]);

  // Old claim process used `api.tx.claims.claim`, and didn't have attest
  const isOldClaimProcess = !api.tx.claims.claimAttest;

  useEffect(() => {
    if (didCopy) {
      setTimeout((): void => {
        setDidCopy(false);
      }, 1000);
    }
  }, [didCopy]);

  const goToStepAccount = useCallback(() => {
    setStep(Step.Account);
    setEthereumTxHash("");
    setEthereumTxHashValid(false);
  }, []);

  const goToStepSign = useCallback(() => {
    setStep(Step.Sign);
  }, []);

  const goToStepClaim = useCallback(() => {
    setStep(Step.Claim);
  }, []);

  const handleAccountStep = useCallback(async () => {
    setIsBusy(true);
    const result = await httpPost("https://bridge-api.crust.network/claim/" + ethereumTxHash);

    setIsBusy(false);
    setResult(result.statusText);
    setStatus(result.status);

    if (result.code == 200) {
      setStatusOpen(true);
      setEthereumTxHashValid(true);
      goToStepSign();
    } else {
      api.query.claims
        .claims<Option<BalanceOf>>(ethereumTxHash?.toString())
        .then((claim): void => {
          const claimOpt = JSON.parse(JSON.stringify(claim));

          if (claimOpt) {
            api.query.claims
              .claimed<Option<BalanceOf>>(ethereumTxHash?.toString())
              .then((claimed): void => {
                const isClaimed = JSON.parse(JSON.stringify(claimed));

                if (isClaimed) {
                  setStatusOpen(true);
                } else {
                  setStatusOpen(true);
                  setResult('MintClaimSuccess');
                  setStatus('success');
                  setEthereumTxHashValid(true);
                  goToStepSign();
                }
              });
          } else {
            setStatusOpen(true);
          }
        })
        .catch((): void => setIsBusy(false));
    }
  }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess, ethereumTxHash]);

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

    isValid = isValid && (
      length !== -1
        ? value.length === 32
        : value.length !== 0
    );
    setIsValid(isValid);
    setEthereumTxHash(hex.trim());
  }, [ethereumTxHash]);

  function convertInput (value: string): [boolean, Uint8Array] {
    if (value === '0x') {
      return [true, new Uint8Array([])];
    } else if (value.startsWith('0x')) {
      try {
        return [true, hexToU8a(value)];
      } catch (error) {
        return [false, new Uint8Array([])];
      }
    }

    // maybe it is an ss58?
    try {
      return [true, decodeAddress(value)];
    } catch (error) {
      // we continue
    }

    return isAscii(value)
      ? [true, stringToU8a(value)]
      : [value === '0x', new Uint8Array([])];
  }

  // Depending on the account, decide which step to show.
  // const handleAccountStep = useCallback(() => {
  //   if (isPreclaimed) {
  //     goToStepClaim();
  //   } else if (ethereumAddress || isOldClaimProcess) {
  //     goToStepSign();
  //   } else {
  //     setStep(Step.ETHAddress);
  //   }
  // }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess]);

  const onChangeSignature = useCallback((event: React.SyntheticEvent<Element>) => {
    const { value: signatureJson } = event.target as HTMLInputElement;

    const { ethereumAddress, signature } = recoverFromJSON(signatureJson);

    setEthereumAddress(ethereumAddress?.toString());
    setSignature(signature);
  }, []);

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

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

  const onCopy = useCallback(() => {
    setDidCopy(true);
  }, []);

  // If it's 1/ not preclaimed and 2/ not the old claiming process, fetch the
  // statement kind to sign.
  const statementKind = useCall<StatementKind | null>(!isPreclaimed && !isOldClaimProcess && !!ethereumAddress && api.query.claims.signing, [ethereumAddress], transformStatement);

  const statementSentence = getStatement(systemChain, statementKind)?.sentence || '';

  const prefix = u8aToString(api.consts.claims.prefix.toU8a(true));
  const payload = accountId
    ? `${prefix}${u8aToHex(decodeAddress(accountId), -1, false)}${statementSentence}${ethereumTxHash?.substring(2)}`
    : '';

  return (
    <main>
      {!isOldClaimProcess && <Warning />}
      <h1>
        <Trans>Claim your <em>{TokenUnit.abbr}</em> tokens</Trans>
      </h1>
      <Columar>
        <Columar.Column>
          <Card withBottomMargin>
            <h3><span style={{"wordWrap": "break-word", "wordBreak": "break-all"}}>{t<string>(`0. Please make sure you have the authority to make signature with the private key of the wallet account `)}<span style={{ 'fontWeight': 'bold' }}>({t<string>('address: ')}<a href='https://etherscan.io/address/0x17a9037cdfb24ffcc13697d03c3bcd4dff34732b' target="_blank">0x17A9037cdFB24FfcC13697d03C3bcd4DFF34732b</a>)</span><span>{t<string>(', using an exchange account to sent a transfer (withdrawal) transaction will be invalidated and cause asset loss.')}</span> <span style={{ 'fontWeight': 'bold', 'color': 'red' }}>{t<string>(` This is Maxwell's claim. If you want to claim your tokens to the mainnet, please click on the upper left corner to switch to the mainnet, You are responsible for the consequences!`)}</span></span></h3>
            <img style={{'marginLeft': 'auto', 'marginRight': 'auto', 'display': 'block', "width": "150px" }} src={claimPng as string} />
          </Card>
          {(<Card withBottomMargin>
            <h3>{t<string>(`1. Select your {{chain}} account and enter`, {
                replace: {
                  chain: systemChain
                }
              })} <a href='https://etherscan.io/token/0x32a7C02e79c4ea1008dD6564b35F131428673c41'>{t('ERC20 CRU')}</a> {t<string>('transfer tx hash')}, <span>{t<string>(`The remaining claim limit is `)}<span style={{'color': '#ff8812', 'textDecoration': 'underline', 'fontStyle': 'italic'}}>{formatBalance(claimLimit, { withUnit: 'CRU' })}</span><span>{t<string>(`, If your claim amount is greater than the claim limit, please wait for the limit update`)}</span></span> </h3>
            <InputAddress
              defaultValue={accountId}
              help={t<string>('The account you want to claim to.')}
              isDisabled={ethereumTxHashValid}
              label={t<string>('claim to account')}
              onChange={setAccountId}
              type='account'
            />
            <Input
              autoFocus
              className='full'
              help={t<string>('The Ethereum CRU transfer tx hash (starting by "0x")')}
              isDisabled={ethereumTxHashValid}
              isError={!isValid}
              label={t<string>('Ethereum tx hash')}
              onChange={onChangeEthereumTxHash}
              placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
              value={ethereumTxHash || ''}
            />    
            {(step === Step.Account) && (<Button.Group>
              <Button
                icon='sign-in-alt'
                isBusy={isBusy}
                isDisabled={preclaimEthereumAddress === PRECLAIMS_LOADING || ethereumTxHash === null || ethereumTxHash === '' || !isValid}
                label={preclaimEthereumAddress === PRECLAIMS_LOADING
                  ? t<string>('Loading')
                  : t<string>('Continue')
                }
                onClick={handleAccountStep}
              />
            </Button.Group>)}   
            <HttpStatus
              isStatusOpen={statusOpen}
              message={result}
              setStatusOpen={setStatusOpen}
              status={status}
            />
          </Card>)}
          {
            // We need to know the ethereuem address only for the new process
            // to be able to know the statement kind so that the users can sign it
            (step >= Step.ETHAddress && !isPreclaimed && !isOldClaimProcess) && (
              <Card withBottomMargin>
                <h3>{t<string>('2. Enter the ETH address from the sale.')}</h3>
                <Input
                  autoFocus
                  className='full'
                  help={t<string>('The the Ethereum address you used during the pre-sale (starting by "0x")')}
                  label={t<string>('Pre-sale ethereum address')}
                  onChange={onChangeEthereumAddress}
                  value={ethereumAddress || ''}
                />
                {(step === Step.ETHAddress) && (
                  <Button.Group>
                    <Button
                      icon='sign-in-alt'
                      isDisabled={!ethereumAddress}
                      label={t<string>('Continue')}
                      onClick={goToStepSign}
                    />
                  </Button.Group>
                )}
              </Card>
            )}
          {(step >= Step.Sign && !isPreclaimed) && (
            <Card>
              <h3>{t<string>('{{step}}. Sign with your ETH address', { replace: { step: isOldClaimProcess ? '2' : '3' } })}</h3>
              {!isOldClaimProcess && (
                <Statement
                  kind={statementKind}
                  systemChain={systemChain}
                />
              )}
              <div>{t<string>('Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:')}</div>
              <CopyToClipboard
                onCopy={onCopy}
                text={payload}
              >
                <Payload
                  data-for='tx-payload'
                  data-tip
                >
                  {payload}
                </Payload>
              </CopyToClipboard>
              <Tooltip
                place='right'
                text={didCopy ? t<string>('copied') : t<string>('click to copy')}
                trigger='tx-payload'
              />
              <div>{t<string>('Paste the signed message into the field below. The placeholder text is there as a hint to what the message should look like:')}</div>
              <Signature
                onChange={onChangeSignature}
                placeholder={`{\n  "address": "0x ...",\n  "msg": "${prefix}...",\n  "sig": "0x ...",\n  "version": "3",\n  "signer": "..."\n}`}
                rows={10}
              />
              {(step === Step.Sign) && (
                <Button.Group>
                  <Button
                    icon='sign-in-alt'
                    isDisabled={!accountId || !signature}
                    label={t<string>('Confirm claim')}
                    onClick={goToStepClaim}
                  />
                </Button.Group>
              )}
            </Card>
          )}
        </Columar.Column>
        <Columar.Column>
          
          {(step >= Step.Claim) && (
            isPreclaimed
              ? <AttestDisplay
                accountId={accountId}
                ethereumAddress={ethereumAddress}
                onSuccess={goToStepAccount}
                statementKind={statementKind}
                systemChain={systemChain}
              />
              : <ClaimDisplay
                accountId={accountId}
                ethereumAddress={ethereumAddress}
                ethereumSignature={signature}
                isOldClaimProcess={isOldClaimProcess}
                onSuccess={goToStepAccount}
                statementKind={statementKind}
                ethereumTxHash={ethereumTxHash}
              />
          )}
        </Columar.Column>
      </Columar>
    </main>
  );
}
Example #13
Source File: index.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function CSMClaims (): React.ReactElement<Props> {
  const [didCopy, setDidCopy] = useState(false);
  const [ethereumAddress, setEthereumAddress] = useState<string | undefined | null>(null);
  const [signature, setSignature] = useState<EcdsaSignature | null>(null);
  const [step, setStep] = useState<Step>(Step.Account);
  const [accountId, setAccountId] = useState<string | null>(null);
  const { api, systemChain } = useApi();
  const { t } = useTranslation();
  const [statusOpen, setStatusOpen] = useState<boolean>(false);
  const [result, setResult] = useState<string>('');
  const [status, setStatus] = useState<string>('');
  const [ethereumTxHashValid, setEthereumTxHashValid] = useState<boolean>(false);
  const [isBusy, setIsBusy] = useState<boolean>(false);
  const [isValid, setIsValid] = useState(false);
  const [ethereumTxHash, setEthereumTxHash] = useState<string | undefined | null>(null);
  const csmClaimLimit = useCall<BalanceOf>(api.query.claims.csmClaimLimit);

  // This preclaimEthereumAddress holds the result of `api.query.claims.preclaims`:
  // - an `EthereumAddress` when there's a preclaim
  // - null if no preclaim
  // - `PRECLAIMS_LOADING` if we're fetching the results
  const [preclaimEthereumAddress, setPreclaimEthereumAddress] = useState<string | null | undefined | typeof PRECLAIMS_LOADING>(PRECLAIMS_LOADING);
  const isPreclaimed = !!preclaimEthereumAddress && preclaimEthereumAddress !== PRECLAIMS_LOADING;

  // Everytime we change account, reset everything, and check if the accountId
  // has a preclaim.
  useEffect(() => {
    if (!accountId) {
      return;
    }

    setStep(Step.Account);
    setEthereumAddress(null);
    setEthereumTxHash(null);
    setPreclaimEthereumAddress(PRECLAIMS_LOADING);

    if (!api.query.claims || !api.query.claims.preclaims) {
      return setPreclaimEthereumAddress(null);
    }

    api.query.claims
      .preclaims<Option<EthereumAddress>>(accountId)
      .then((preclaim): void => {
        const address = preclaim.unwrapOr(null)?.toString();

        setEthereumAddress(address);
        setPreclaimEthereumAddress(address);
      })
      .catch((): void => setPreclaimEthereumAddress(null));
  }, [accountId, api.query.claims, api.query.claims.preclaims]);

  // Old claim process used `api.tx.claims.claim`, and didn't have attest
  const isOldClaimProcess = !api.tx.claims.claimAttest;

  useEffect(() => {
    if (didCopy) {
      setTimeout((): void => {
        setDidCopy(false);
      }, 1000);
    }
  }, [didCopy]);

  const goToStepAccount = useCallback(() => {
    setStep(Step.Account);
    setEthereumTxHash("");
    setEthereumTxHashValid(false);
  }, []);

  const goToStepSign = useCallback(() => {
    setStep(Step.Sign);
  }, []);

  const goToStepClaim = useCallback(() => {
    setStep(Step.Claim);
  }, []);

  const handleAccountStep = useCallback(async () => {
    setIsBusy(true);
    const result = await httpPost("https://csm-bridge-api.crust.network/csmClaim/" + ethereumTxHash);

    setIsBusy(false);
    setResult(result.statusText);
    setStatus(result.status);

    if (result.code == 200) {
      setStatusOpen(true);
      setEthereumTxHashValid(true);
      goToStepSign();
    } else {
      api.query.claims
        .csmClaims<Option<BalanceOf>>(ethereumTxHash?.toString())
        .then((claim): void => {
          const claimOpt = JSON.parse(JSON.stringify(claim));

          if (claimOpt) {
            api.query.claims
              .csmClaimed<Option<BalanceOf>>(ethereumTxHash?.toString())
              .then((claimed): void => {
                const isClaimed = JSON.parse(JSON.stringify(claimed));

                if (isClaimed) {
                  setStatusOpen(true);
                } else {
                  setStatusOpen(true);
                  setResult('MintClaimSuccess');
                  setStatus('success');
                  setEthereumTxHashValid(true);
                  goToStepSign();
                }
              });
          } else {
            setStatusOpen(true);
          }
        })
        .catch((): void => setIsBusy(false));
    }
  }, [ethereumAddress, goToStepAccount, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess, ethereumTxHash]);

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

    isValid = isValid && (
      length !== -1
        ? value.length === 32
        : value.length !== 0
    );
    setIsValid(isValid);
    setEthereumTxHash(hex.trim());
  }, [ethereumTxHash]);

  function convertInput (value: string): [boolean, Uint8Array] {
    if (value === '0x') {
      return [true, new Uint8Array([])];
    } else if (value.startsWith('0x')) {
      try {
        return [true, hexToU8a(value)];
      } catch (error) {
        return [false, new Uint8Array([])];
      }
    }

    // maybe it is an ss58?
    try {
      return [true, decodeAddress(value)];
    } catch (error) {
      // we continue
    }

    return isAscii(value)
      ? [true, stringToU8a(value)]
      : [value === '0x', new Uint8Array([])];
  }

  // Depending on the account, decide which step to show.
  // const handleAccountStep = useCallback(() => {
  //   if (isPreclaimed) {
  //     goToStepClaim();
  //   } else if (ethereumAddress || isOldClaimProcess) {
  //     goToStepSign();
  //   } else {
  //     setStep(Step.ETHAddress);
  //   }
  // }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess]);

  const onChangeSignature = useCallback((event: React.SyntheticEvent<Element>) => {
    const { value: signatureJson } = event.target as HTMLInputElement;

    const { ethereumAddress, signature } = recoverFromJSON(signatureJson);

    setEthereumAddress(ethereumAddress?.toString());
    setSignature(signature);
  }, []);

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

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

  const onCopy = useCallback(() => {
    setDidCopy(true);
  }, []);

  // If it's 1/ not preclaimed and 2/ not the old claiming process, fetch the
  // statement kind to sign.
  const statementKind = useCall<StatementKind | null>(!isPreclaimed && !isOldClaimProcess && !!ethereumAddress && api.query.claims.signing, [ethereumAddress], transformStatement);

  const statementSentence = getStatement(systemChain, statementKind)?.sentence || '';

  // const prefix = u8aToString(api.consts.claims.CsmPrefix.toU8a(true));
  const prefix = 'Pay CSMs to the Crust account:'
  const payload = accountId
    ? `${prefix}${u8aToHex(decodeAddress(accountId), -1, false)}${statementSentence}${ethereumTxHash?.substring(2)}`
    : '';
  
  return (
    <main>
      {!isOldClaimProcess && <Warning />}
      <h1>
        <Trans>Claim your <em>CSM</em> tokens</Trans>
      </h1>
      <Columar>
        <Columar.Column>
          <Card withBottomMargin>
            <h3><span style={{"wordWrap": "break-word", "wordBreak": "break-all"}}>{t<string>(`0. Please make sure you have the authority to make signature with the private key of the wallet account `)}<span style={{ 'fontWeight': 'bold' }}>({t<string>('address: ')}<a href='https://etherscan.io/address/0x17a9037cdfb24ffcc13697d03c3bcd4dff34732b' target="_blank">0x17A9037cdFB24FfcC13697d03C3bcd4DFF34732b</a>)</span><span>{t<string>(', using an exchange account to sent a transfer (withdrawal) transaction will be invalidated and cause asset loss.')}</span> <span style={{ 'fontWeight': 'bold', 'color': 'red' }}>{t<string>(` You are responsible for the consequences!`)}</span></span></h3>
            <img style={{'marginLeft': 'auto', 'marginRight': 'auto', 'display': 'block', "width": "150px" }} src={claimPng as string} />
          </Card>
          {(<Card withBottomMargin>
            <h3>{t<string>(`1. Select your {{chain}} account and enter`, {
                replace: {
                  chain: systemChain
                }
              })} <a href='https://rinkeby.etherscan.io/token/0x7a1c61c526dae21c23b50a490c18d04f8077608f'>{t('ERC20 CSM')}</a> {t<string>('transfer tx hash')}, <span>{t<string>(`The remaining claim limit is `)}<span style={{'color': '#ff8812', 'textDecoration': 'underline', 'fontStyle': 'italic'}}>{formatBalance(csmClaimLimit, {  decimals: 12, withUnit: 'CSM' })}</span><span>{t<string>(`, If your claim amount is greater than the claim limit, please wait for the limit update`)}</span></span>
            </h3>

           <InputAddress
              defaultValue={accountId}
              help={t<string>('The account you want to claim to.')}
              isDisabled={ethereumTxHashValid}
              label={t<string>('claim to account')}
              onChange={setAccountId}
              type='account'
            />
            <Input
              autoFocus
              className='full'
              help={t<string>('The Ethereum CSM transfer tx hash (starting by "0x")')}
              isDisabled={ethereumTxHashValid}
              isError={!isValid}
              label={t<string>('Ethereum tx hash')}
              onChange={onChangeEthereumTxHash}
              placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
              value={ethereumTxHash || ''}
            />
            {(step === Step.Account) && (<Button.Group>
              <Button
                icon='sign-in-alt'
                isBusy={isBusy}
                isDisabled={preclaimEthereumAddress === PRECLAIMS_LOADING || ethereumTxHash === null || ethereumTxHash === '' || !isValid}
                label={preclaimEthereumAddress === PRECLAIMS_LOADING
                  ? t<string>('Loading')
                  : t<string>('Continue')
                }
                onClick={handleAccountStep}
              />
            </Button.Group>)}
            
            <HttpStatus
              isStatusOpen={statusOpen}
              message={result}
              setStatusOpen={setStatusOpen}
              status={status}
            />
          </Card>)}
          {
            // We need to know the ethereuem address only for the new process
            // to be able to know the statement kind so that the users can sign it
            (step >= Step.ETHAddress && !isPreclaimed && !isOldClaimProcess) && (
              <Card withBottomMargin>
                <h3>{t<string>('2. Enter the ETH address from the sale.')}</h3>
                <Input
                  autoFocus
                  className='full'
                  help={t<string>('The the Ethereum address you used during the pre-sale (starting by "0x")')}
                  label={t<string>('Pre-sale ethereum address')}
                  onChange={onChangeEthereumAddress}
                  value={ethereumAddress || ''}
                />
                {(step === Step.ETHAddress) && (
                  <Button.Group>
                    <Button
                      icon='sign-in-alt'
                      isDisabled={!ethereumAddress}
                      label={t<string>('Continue')}
                      onClick={goToStepSign}
                    />
                  </Button.Group>
                )}
              </Card>
            )}
          {(step >= Step.Sign && !isPreclaimed) && (
            <Card>
              <h3>{t<string>('{{step}}. Sign with your ETH address', { replace: { step: isOldClaimProcess ? '2' : '3' } })}</h3>
              {!isOldClaimProcess && (
                <Statement
                  kind={statementKind}
                  systemChain={systemChain}
                />
              )}
              <div>{t<string>('Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:')}</div>
              <CopyToClipboard
                onCopy={onCopy}
                text={payload}
              >
                <Payload
                  data-for='tx-payload'
                  data-tip
                >
                  {payload}
                </Payload>
              </CopyToClipboard>
              <Tooltip
                place='right'
                text={didCopy ? t<string>('copied') : t<string>('click to copy')}
                trigger='tx-payload'
              />
              <div>{t<string>('Paste the signed message into the field below. The placeholder text is there as a hint to what the message should look like:')}</div>
              <Signature
                onChange={onChangeSignature}
                placeholder={`{\n  "address": "0x ...",\n  "msg": "${prefix}...",\n  "sig": "0x ...",\n  "version": "3",\n  "signer": "..."\n}`}
                rows={10}
              />
              {(step === Step.Sign) && (
                <Button.Group>
                  <Button
                    icon='sign-in-alt'
                    isDisabled={!accountId || !signature}
                    label={t<string>('Confirm claim')}
                    onClick={goToStepClaim}
                  />
                </Button.Group>
              )}
            </Card>
          )}
        </Columar.Column>
        <Columar.Column>
          {(step >= Step.Claim) && (
            isPreclaimed
              ? <AttestDisplay
                accountId={accountId}
                ethereumAddress={ethereumAddress}
                onSuccess={goToStepAccount}
                statementKind={statementKind}
                systemChain={systemChain}
              />
              : <ClaimDisplay
                accountId={accountId}
                ethereumAddress={ethereumAddress}
                ethereumSignature={signature}
                isOldClaimProcess={isOldClaimProcess}
                onSuccess={goToStepAccount}
                statementKind={statementKind}
                ethereumTxHash={ethereumTxHash}
              />
          )}
        </Columar.Column>
      </Columar>
    </main>
  );
}