@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 |
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 |
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 |
// 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 |
// 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 |
// 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 |
transformStatement = {
transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #7
Source File: index.tsx From crust-apps with Apache License 2.0 | 5 votes |
transformStatement = {
transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #8
Source File: index.tsx From crust-apps with Apache License 2.0 | 5 votes |
transformStatement = {
transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #9
Source File: index.tsx From crust-apps with Apache License 2.0 | 5 votes |
transformStatement = {
transform: (option: Option<StatementKind>) => option.unwrapOr(null)
}
Example #10
Source File: index.tsx From crust-apps with Apache License 2.0 | 4 votes |
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 |
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,')} </span>
<span>{t<string>('For the claiming tutorial, please refer to')} </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 |
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 |
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> ); }