@polkadot/util#u8aToString TypeScript Examples
The following examples show how to use
@polkadot/util#u8aToString.
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: InputFile.tsx From crust-apps with Apache License 2.0 | 6 votes |
function convertResult (result: ArrayBuffer): Uint8Array {
const data = new Uint8Array(result);
// this converts the input (if detected as hex), via the hex conversion route
if (data[0] === BYTE_STR_0 && data[1] === BYTE_STR_X) {
let hex = u8aToString(data);
while (hex[hex.length - 1] === STR_NL) {
hex = hex.substr(0, hex.length - 1);
}
if (isHex(hex)) {
return hexToU8a(hex);
}
}
return data;
}
Example #2
Source File: KeyValueArray.tsx From subscan-multisig-react with Apache License 2.0 | 6 votes |
function parseFile(raw: Uint8Array): Parsed {
const json = JSON.parse(u8aToString(raw)) as Record<string, string>;
const keys = Object.keys(json);
let isValid = keys.length !== 0;
const value = keys.map((key): [Uint8Array, Uint8Array] => {
const val = json[key];
assert(isHex(key) && isHex(val), `Non-hex key/value pair found in ${key.toString()} => ${val.toString()}`);
const encKey = createParam(key);
const encValue = createParam(val);
isValid = isValid && encKey.isValid && encValue.isValid;
return [encKey.u8a, encValue.u8a];
});
return {
isValid,
value,
};
}
Example #3
Source File: InputFile.tsx From subscan-multisig-react with Apache License 2.0 | 6 votes |
function convertResult(result: ArrayBuffer): Uint8Array {
const data = new Uint8Array(result);
// this converts the input (if detected as hex), via the hex conversion route
if (data[0] === BYTE_STR_0 && data[1] === BYTE_STR_X) {
let hex = u8aToString(data);
while (hex[hex.length - 1] === STR_NL) {
hex = hex.substr(0, hex.length - 1);
}
if (isHex(hex)) {
return hexToU8a(hex);
}
}
return data;
}
Example #4
Source File: urlTypes.ts From subscan-multisig-react with Apache License 2.0 | 6 votes |
export function decodeUrlTypes(): Record<string, any> | null {
const urlOptions = queryString.parse(location.href.split('?')[1]);
if (urlOptions.types) {
try {
assert(!Array.isArray(urlOptions.types), 'Expected a single type specification');
const parts = urlOptions.types.split('#');
const compressed = base64Decode(decodeURIComponent(parts[0]));
const uncompressed = unzlibSync(compressed);
return JSON.parse(u8aToString(uncompressed)) as Record<string, any>;
} catch (error) {
console.error(error);
}
}
return null;
}
Example #5
Source File: InputFile.tsx From contracts-ui with GNU General Public License v3.0 | 6 votes |
function convertResult(result: ArrayBuffer): Uint8Array {
const data = new Uint8Array(result);
// this converts the input (if detected as hex), via the hex conversion route
if (data[0] === BYTE_STR_0 && data[1] === BYTE_STR_X) {
let hex = u8aToString(data);
while (hex[hex.length - 1] === STR_NL) {
hex = hex.substr(0, hex.length - 1);
}
if (isHex(hex)) {
return hexToU8a(hex);
}
}
return data;
}
Example #6
Source File: identity.ts From commonwealth with GNU General Public License v3.0 | 6 votes |
// keeps track of changing registration info
public async update() {
const rOpt = await this._Chain.api.query.identity.identityOf(this.account.address);
if (rOpt.isSome) {
const { judgements, deposit, info } = rOpt.unwrap();
this._judgements = judgements;
this._info = info;
this._deposit = this._Chain.coins(deposit);
// update username
const d2s = (d: Data) => u8aToString(d.toU8a()).replace(/[^\x20-\x7E]/g, '');
this.username = d2s(this._info.display);
const quality = getIdentityQuality(this._judgements.map((j) => j[1].toString()));
if (!this._quality || quality !== IdentityQuality.Unknown) {
this._quality = quality;
}
if (this._Identities.store.getById(this.id)) {
this._Identities.store.update(this);
} else {
this._Identities.store.add(this);
}
this._exists = true;
} else {
this._exists = false;
this._judgements = [];
this._quality = IdentityQuality.Unknown;
this._deposit = this._Chain.coins(0);
}
}
Example #7
Source File: blocks.errors.ts From gear-js with GNU General Public License v3.0 | 6 votes |
constructor(message: string, hash?: string | Uint8Array) {
super();
const splittedMessage = message.split(':');
if (isU8a(hash)) {
hash = u8aToString(hash);
}
const errorCode = splittedMessage.length > 0 ? parseInt(splittedMessage[0]) : NaN;
switch (errorCode) {
case -32603:
this.message = `State already discarded for block ${hash}`;
break;
default:
this.message = 'Unknow error occurred';
break;
}
}
Example #8
Source File: KeyValueArray.tsx From crust-apps with Apache License 2.0 | 6 votes |
function parseFile (raw: Uint8Array): Parsed {
const json = JSON.parse(u8aToString(raw)) as Record<string, string>;
const keys = Object.keys(json);
let isValid = keys.length !== 0;
const value = keys.map((key): [Uint8Array, Uint8Array] => {
const value = json[key];
assert(isHex(key) && isHex(value), `Non-hex key/value pair found in ${key.toString()} => ${value.toString()}`);
const encKey = createParam(key);
const encValue = createParam(value);
isValid = isValid && encKey.isValid && encValue.isValid;
return [encKey.u8a, encValue.u8a];
});
return {
isValid,
value
};
}
Example #9
Source File: urlTypes.ts From crust-apps with Apache License 2.0 | 6 votes |
export function decodeUrlTypes (): Record<string, any> | null {
const urlOptions = queryString.parse(location.href.split('?')[1]);
if (urlOptions.types) {
try {
assert(!Array.isArray(urlOptions.types), 'Expected a single type specification');
const parts = urlOptions.types.split('#');
const compressed = base64Decode(decodeURIComponent(parts[0]));
const uncompressed = unzlibSync(compressed);
return JSON.parse(u8aToString(uncompressed)) as Record<string, any>;
} catch (error) {
console.error(error);
}
}
return null;
}
Example #10
Source File: Query.tsx From crust-apps with Apache License 2.0 | 6 votes |
function keyToName (isConst: boolean, _key: Uint8Array | QueryableStorageEntry<'promise'> | ConstValue): string {
if (isConst) {
const key = _key as ConstValue;
return `const ${key.section}.${key.method}`;
}
const key = _key as Uint8Array | QueryableStorageEntry<'promise'>;
if (isU8a(key)) {
const [, u8a] = compactStripLength(key);
// If the string starts with `:`, handle it as a pure string
return u8a[0] === 0x3a
? u8aToString(u8a)
: u8aToHex(u8a);
}
return `${key.creator.section}.${key.creator.method}`;
}
Example #11
Source File: Import.tsx From crust-apps with Apache License 2.0 | 6 votes |
function parseFile (file: Uint8Array, genesisHash?: string | null): KeyringPair | null {
try {
return keyring.createFromJson(JSON.parse(u8aToString(file)) as KeyringPair$Json, { genesisHash });
} catch (error) {
console.error(error);
}
return null;
}
Example #12
Source File: IdentitySub.tsx From crust-apps with Apache License 2.0 | 6 votes |
function extractInfo ([[ids], opts]: [[string[]], Option<ITuple<[AccountId, Data]>>[]]): [string, string][] {
return ids.reduce((result: [string, string][], id, index): [string, string][] => {
const opt = opts[index];
if (opt.isSome) {
const [, data] = opt.unwrap();
if (data.isRaw) {
result.push([id, u8aToString(data.asRaw)]);
}
}
return result;
}, []);
}
Example #13
Source File: useAbi.ts From crust-apps with Apache License 2.0 | 5 votes |
export default function useAbi (initialValue: [string | null | undefined, Abi | null | undefined] = [null, null], codeHash: string | null = null, isRequired = false): UseAbi {
const [state, setAbi] = useState<AbiState>(() => fromInitial(initialValue, isRequired));
useEffect(
() => setAbi((state) =>
initialValue[0] && state.abi !== initialValue[0]
? fromInitial(initialValue, isRequired)
: state
),
[initialValue, isRequired]
);
const onChangeAbi = useCallback(
(u8a: Uint8Array, name: string): void => {
const json = u8aToString(u8a);
try {
setAbi({
abi: json,
abiName: name.replace('.contract', '').replace('.json', '').replace('_', ' '),
contractAbi: new Abi(json, api.registry.getChainProperties()),
errorText: null,
isAbiError: false,
isAbiSupplied: true,
isAbiValid: true
});
codeHash && store.saveCode(codeHash, { abi: json });
} catch (error) {
console.error(error);
setAbi({ ...EMPTY, errorText: (error as Error).message });
}
},
[codeHash]
);
const onRemoveAbi = useCallback(
(): void => {
setAbi(EMPTY);
codeHash && store.saveCode(codeHash, { abi: null });
},
[codeHash]
);
return {
...state,
onChangeAbi,
onRemoveAbi
};
}
Example #14
Source File: test-precompile-local-assets-erc20.ts From moonbeam with GNU General Public License v3.0 | 5 votes |
describeDevMoonbeamAllEthTxTypes(
"Precompiles - Assets-ERC20 Wasm",
(context) => {
let sudoAccount, baltatharAccount, assetId, iFace, assetAddress;
before("Setup contract and mock balance", async () => {
const keyring = new Keyring({ type: "ethereum" });
sudoAccount = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
baltatharAccount = await keyring.addFromUri(BALTATHAR_PRIV_KEY, null, "ethereum");
// registerAsset
const { events: eventsRegister } = await createBlockWithExtrinsic(
context,
sudoAccount,
context.polkadotApi.tx.sudo.sudo(
context.polkadotApi.tx.assetManager.registerLocalAsset(
baltatharAccount.address,
baltatharAccount.address,
true,
new BN(1)
)
)
);
// Look for assetId in events
eventsRegister.forEach((e) => {
if (e.section.toString() === "assetManager") {
assetId = e.data[0].toHex();
}
});
assetId = assetId.replace(/,/g, "");
assetAddress = u8aToHex(new Uint8Array([...hexToU8a("0xFFFFFFFE"), ...hexToU8a(assetId)]));
const contractData = await getCompiled("LocalAssetExtendedErc20Instance");
iFace = new ethers.utils.Interface(contractData.contract.abi);
const { contract, rawTx } = await createContract(context, "LocalAssetExtendedErc20Instance");
const address = contract.options.address;
await context.createBlock({ transactions: [rawTx] });
});
it("allows to set metadata", async function () {
let data = iFace.encodeFunctionData(
// action
"set_metadata",
["Local", "LOC", 12]
);
const tx = await createTransaction(context, {
from: BALTATHAR,
privateKey: BALTATHAR_PRIV_KEY,
value: "0x0",
gas: "0x200000",
gasPrice: GAS_PRICE,
to: assetAddress,
data: data,
});
const block = await context.createBlock({
transactions: [tx],
});
const receipt = await context.web3.eth.getTransactionReceipt(block.txResults[0].result);
expect(receipt.status).to.equal(true);
const metadata = (await context.polkadotApi.query.localAssets.metadata(assetId)) as any;
expect(u8aToString(metadata.name)).to.eq("Local");
expect(u8aToString(metadata.symbol)).to.eq("LOC");
expect(metadata.decimals.toString()).to.eq("12");
});
},
true
);
Example #15
Source File: BaseBytes.tsx From crust-apps with Apache License 2.0 | 5 votes |
function BaseBytes ({ asHex, children, className = '', defaultValue: { value }, isDisabled, isError, label, length = -1, onChange, onEnter, onEscape, size = 'full', validate = defaultValidate, withCopy, withLabel, withLength }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [defaultValue] = useState(
value
? isDisabled && isU8a(value) && isAscii(value)
? u8aToString(value)
: isHex(value)
? value
: u8aToHex(value as Uint8Array, isDisabled ? 256 : -1)
: undefined
);
const [isValid, setIsValid] = useState(false);
const _onChange = useCallback(
(hex: string): void => {
let [isValid, value] = convertInput(hex);
isValid = isValid && validate(value) && (
length !== -1
? value.length === length
: value.length !== 0
);
if (withLength && isValid) {
value = compactAddLength(value);
}
onChange && onChange({
isValid,
value: asHex
? u8aToHex(value)
: value
});
setIsValid(isValid);
},
[asHex, length, onChange, validate, withLength]
);
return (
<Bare className={className}>
<Input
className={size}
defaultValue={defaultValue as string}
isAction={!!children}
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
type='text'
withEllipsis
withLabel={withLabel}
>
{children}
{withCopy && (
<CopyButton value={defaultValue} />
)}
</Input>
</Bare>
);
}
Example #16
Source File: useMetadata.ts From contracts-ui with GNU General Public License v3.0 | 5 votes |
export function useMetadata(
initialValue?: Record<string, unknown>,
options: Options & Callbacks = {}
): UseMetadata {
const { api } = useApi();
const { isWasmRequired = false, ...callbacks } = options;
const [state, setState] = useState<MetadataState>(() =>
deriveFromJson({ isWasmRequired }, initialValue, api)
);
function onChange(file: FileState): void {
try {
const json = JSON.parse(u8aToString(file.data)) as Record<string, unknown>;
const name = file.name.replace('.contract', '').replace('.json', '').replace('_', ' ');
const newState = deriveFromJson({ isWasmRequired, name }, json, api);
setState(newState);
callbacks.onChange && callbacks.onChange(file, json);
} catch (error) {
console.error(error);
setState({
...EMPTY,
message: 'This contract file is not in a valid format.',
isError: true,
isSupplied: true,
isValid: false,
});
}
}
function onRemove(): void {
setState(EMPTY);
callbacks.onChange && callbacks.onChange(undefined);
callbacks.onRemove && callbacks.onRemove();
}
useEffect((): void => {
setState(deriveFromJson({ isWasmRequired }, initialValue, api));
}, [api, initialValue, isWasmRequired]);
return {
...state,
onChange,
onRemove,
};
}
Example #17
Source File: IdentityMain.tsx From crust-apps with Apache License 2.0 | 5 votes |
function setData (data: Data, setActive: null | ((isActive: boolean) => void), setVal: (val: string) => void): void {
if (data.isRaw) {
setActive && setActive(true);
setVal(u8aToString(data.asRaw.toU8a(true)));
}
}
Example #18
Source File: BaseBytes.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
function BaseBytes({
asHex,
children,
className = '',
defaultValue: { value },
isDisabled,
isError,
label,
length = -1,
onChange,
onEnter,
onEscape,
size = 'full',
validate = defaultValidate,
withCopy,
withLabel,
withLength,
}: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [defaultValue] = useState(
value
? isDisabled && isU8a(value) && isAscii(value)
? u8aToString(value)
: isHex(value)
? value
: // eslint-disable-next-line no-magic-numbers
u8aToHex(value as Uint8Array, isDisabled ? 256 : -1)
: undefined
);
const [isValid, setIsValid] = useState(false);
const _onChange = useCallback(
(hex: string): void => {
let [beValid, val] = convertInput(hex);
beValid = beValid && validate(val) && (length !== -1 ? val.length === length : val.length !== 0);
if (withLength && beValid) {
val = compactAddLength(val);
}
// eslint-disable-next-line
onChange &&
onChange({
isValid: beValid,
value: asHex ? u8aToHex(val) : val,
});
setIsValid(beValid);
},
[asHex, length, onChange, validate, withLength]
);
return (
<Bare className={className}>
<Input
className={size}
defaultValue={defaultValue as string}
isAction={!!children}
isDisabled={isDisabled}
isError={isError || !isValid}
label={label}
onChange={_onChange}
onEnter={onEnter}
onEscape={onEscape}
placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
type="text"
withEllipsis
withLabel={withLabel}
>
{children}
{withCopy && <CopyButton value={defaultValue} />}
</Input>
</Bare>
);
}
Example #19
Source File: test-precompile-local-assets-erc20.ts From moonbeam with GNU General Public License v3.0 | 5 votes |
describeDevMoonbeamAllEthTxTypes(
"Precompiles - Assets-ERC20 Wasm",
(context) => {
let sudoAccount, baltatharAccount, assetId, iFace, assetAddress;
before("Setup contract and mock balance", async () => {
const keyring = new Keyring({ type: "ethereum" });
sudoAccount = await keyring.addFromUri(ALITH_PRIV_KEY, null, "ethereum");
baltatharAccount = await keyring.addFromUri(BALTATHAR_PRIV_KEY, null, "ethereum");
// registerAsset
const { events: eventsRegister } = await createBlockWithExtrinsic(
context,
sudoAccount,
context.polkadotApi.tx.sudo.sudo(
context.polkadotApi.tx.assetManager.registerLocalAsset(
baltatharAccount.address,
baltatharAccount.address,
true,
new BN(1)
)
)
);
// Look for assetId in events
eventsRegister.forEach((e) => {
if (e.section.toString() === "assetManager") {
assetId = e.data[0].toHex();
}
});
assetId = assetId.replace(/,/g, "");
assetAddress = u8aToHex(new Uint8Array([...hexToU8a("0xFFFFFFFE"), ...hexToU8a(assetId)]));
// Set metadata
await createBlockWithExtrinsic(
context,
baltatharAccount,
context.polkadotApi.tx.localAssets.setMetadata(assetId, "Local", "Local", new BN(12))
);
const contractData = await getCompiled("LocalAssetExtendedErc20Instance");
iFace = new ethers.utils.Interface(contractData.contract.abi);
const { contract, rawTx } = await createContract(context, "LocalAssetExtendedErc20Instance");
const address = contract.options.address;
await context.createBlock({ transactions: [rawTx] });
});
it("allows to clear metadata", async function () {
let data = iFace.encodeFunctionData(
// action
"clear_metadata",
[]
);
const tx = await createTransaction(context, {
from: BALTATHAR,
privateKey: BALTATHAR_PRIV_KEY,
value: "0x0",
gas: "0x200000",
gasPrice: GAS_PRICE,
to: assetAddress,
data: data,
});
const block = await context.createBlock({
transactions: [tx],
});
const receipt = await context.web3.eth.getTransactionReceipt(block.txResults[0].result);
expect(receipt.status).to.equal(true);
const metadata = (await context.polkadotApi.query.localAssets.metadata(assetId)) as any;
expect(u8aToString(metadata.name)).to.eq("");
expect(u8aToString(metadata.symbol)).to.eq("");
expect(metadata.decimals.toString()).to.eq("0");
});
},
true
);
Example #20
Source File: Developer.tsx From crust-apps with Apache License 2.0 | 4 votes |
function Developer ({ className = '', onStatusChange }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [code, setCode] = useState(EMPTY_CODE);
const [isJsonValid, setIsJsonValid] = useState(true);
const [isTypesValid, setIsTypesValid] = useState(true);
const [types, setTypes] = useState<Record<string, any>>(EMPTY_TYPES);
const [typesPlaceholder, setTypesPlaceholder] = useState<string | null>(null);
const [sharedUrl, setSharedUrl] = useState<string | null>(null);
useEffect((): void => {
const types = decodeUrlTypes() || store.get('types') as Record<string, unknown> || {};
if (Object.keys(types).length) {
setCode(JSON.stringify(types, null, 2));
setTypes({});
setTypesPlaceholder(Object.keys(types).join(', '));
setSharedUrl(encodeUrlTypes(types));
}
}, []);
const _setState = useCallback(
({ code, isJsonValid, isTypesValid, types, typesPlaceholder }: AllState): void => {
setCode(code);
setIsJsonValid(isJsonValid);
setIsTypesValid(isTypesValid);
setTypes(types);
setTypesPlaceholder(typesPlaceholder);
},
[]
);
const _clearTypes = useCallback(
(): void => {
store.remove('types');
_setState({
code: EMPTY_CODE,
isJsonValid: true,
isTypesValid: true,
types: EMPTY_TYPES,
typesPlaceholder: null
});
},
[_setState]
);
const _onChangeTypes = useCallback(
(data: Uint8Array): void => {
const code = u8aToString(data);
try {
const types = JSON.parse(code) as Record<string, unknown>;
const typesPlaceholder = Object.keys(types).join(', ');
console.log('Detected types:', typesPlaceholder);
_setState({
code,
isJsonValid: true,
isTypesValid: true,
types: Object.keys(types).length === 0 ? {} : types,
typesPlaceholder
});
} catch (error) {
console.error('Error registering types:', error);
_setState({
code,
isJsonValid: false,
isTypesValid: false,
types: {},
typesPlaceholder: (error as Error).message
});
}
},
[_setState]
);
const _onEditTypes = useCallback(
(code: string): void => {
try {
if (!isJsonObject(code)) {
throw Error(t('This is not a valid JSON object.'));
}
_onChangeTypes(stringToU8a(code));
} catch (error) {
setCode(code);
setIsJsonValid(false);
setTypesPlaceholder((error as Error).message);
}
},
[_onChangeTypes, t]
);
const _saveDeveloper = useCallback(
(): void => {
let url = null;
try {
api.registerTypes(types);
store.set('types', types);
setIsTypesValid(true);
onStatusChange({
action: t<string>('Your custom types have been added'),
status: 'success'
});
if (Object.keys(types).length) {
url = encodeUrlTypes(types);
console.log(url);
}
} catch (error) {
console.error(error);
setIsTypesValid(false);
onStatusChange({
action: t(`Error saving your custom types. ${(error as Error).message}`),
status: 'error'
});
}
setSharedUrl(url);
},
[api, onStatusChange, t, types]
);
const typesHasNoEntries = Object.keys(types).length === 0;
// Trans component
/* eslint-disable react/jsx-max-props-per-line */
return (
<div className={className}>
<div className='ui--row'>
<div className='full'>
<InputFile
clearContent={typesHasNoEntries && isTypesValid}
help={t<string>('Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.')}
isError={!isTypesValid}
label={t<string>('Additional types as a JSON file (or edit below)')}
onChange={_onChangeTypes}
placeholder={typesPlaceholder}
/>
</div>
</div>
<div className='ui--row'>
<div className='full'>
<Editor
className='editor'
code={code}
isValid={isJsonValid}
onEdit={_onEditTypes}
/>
</div>
</div>
<div className='ui--row'>
<div className='full'>
<Trans i18nKey='devConfig'><div className='help'>If you are a development team with at least a test network available, consider adding the types directly <a href='https://github.com/polkadot-js/apps/tree/master/packages/apps-config' rel='noopener noreferrer' target='_blank'>to the apps-config</a>, allowing out of the box operation for your spec & chains, both for you and anybody trying to connect to it. This is not a replacement for your chain-specific UI, however doing so does help in allowing users to easily discover and use with zero-config.</div></Trans>
</div>
</div>
<Button.Group>
<CopyButton
label={t<string>('Share')}
type={t<string>('url')}
value={sharedUrl}
/>
<Button
icon='sync'
label={t<string>('Reset')}
onClick={_clearTypes}
/>
<Button
icon='save'
isDisabled={!isTypesValid || !isJsonValid}
label={t<string>('Save')}
onClick={_saveDeveloper}
/>
</Button.Group>
</div>
);
}
Example #21
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 #22
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 #23
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 #24
Source File: edit_identity_modal.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
EditIdentityModal: m.Component<IAttrs, IState> = {
oninit: (vnode) => {
app.runWhenReady(async () => {
vnode.state.identity = await (app.chain as Substrate).identities.load(vnode.attrs.account);
m.redraw();
});
},
oncreate: (vnode: m.VnodeDOM<IAttrs, IState>) => {
if (vnode.state.identity?.info) {
const {
additional,
display,
legal,
web,
riot,
email,
// pgpFingerprint,
image,
twitter
} = vnode.state.identity?.info;
// do not display SHA values, only raw strings
const d2s = (d: Data) => u8aToString(d.toU8a()).replace(/[^\x20-\x7E]/g, '');
$(vnode.dom).find('input[name=display]').val(d2s(display));
$(vnode.dom).find('input[name=legal]').val(d2s(legal));
$(vnode.dom).find('input[name=web]').val(d2s(web));
$(vnode.dom).find('input[name=riot]').val(d2s(riot));
$(vnode.dom).find('input[name=email]').val(d2s(email));
// if (pgpFingerprint.isSome) {
// $(vnode.dom).find('input[name=pgp]').val(pgpFingerprint.unwrap().toString());
// }
$(vnode.dom).find('input[name=twitter]').val(d2s(twitter));
}
},
view: (vnode: m.VnodeDOM<IAttrs, IState>) => {
const updateIdentity = async () => {
const data = {
display: `${$(vnode.dom).find('input[name=display]').val()}`.trim(),
legal: `${$(vnode.dom).find('input[name=legal]').val()}`.trim(),
web: `${$(vnode.dom).find('input[name=web]').val()}`.trim(),
riot: `${$(vnode.dom).find('input[name=riot]').val()}`.trim(),
email: `${$(vnode.dom).find('input[name=email]').val()}`.trim(),
// pgpFingerprint: `${$(vnode.dom).find('input[name=pgp]').val()}`.trim(),
twitter: `${$(vnode.dom).find('input[name=twitter]').val()}`.trim(),
image: null,
};
vnode.state.saving = true;
const idData: IdentityInfoProps = {
display: { [data.display ? 'raw' : 'none']: data.display ? data.display : null },
email: { [data.email ? 'raw' : 'none']: data.email ? data.email : null },
image: { [data.image ? 'sha256' : 'none']: data.image ? data.image : null },
legal: { [data.legal ? 'raw' : 'none']: data.legal ? data.legal : null },
riot: { [data.riot ? 'raw' : 'none']: data.riot ? data.riot : null },
web: { [data.web ? 'raw' : 'none']: data.web ? data.web : null },
twitter: { [data.twitter ? 'raw' : 'none']: data.twitter ? data.twitter : null },
// pgpFingerprint: data.pgpFingerprint ? data.pgpFingerprint : null,
additional: [],
};
try {
await createTXModal(
(app.chain as Substrate).identities.setIdentityTx(app.user.activeAccount as SubstrateAccount, idData)
);
} catch (error) {
if (typeof error === 'string') {
notifyError(error);
} else if (error.txType === 'setIdentity') {
notifyError('Sending transaction failed');
} else {
notifyError('Unknown error');
}
}
// force creation and update of the user's on-chain identity, guaranteeing that the identity
// component has immediate access to the new identity.
await (app.chain as Substrate).identities.load(app.user.activeAccount as SubstrateAccount);
// temporarily mark the user's profile as invalid, since they've potentially updated their
// display name. this ensures that any identity display will fall back to the loaded identity.
const profile = app.profiles.getProfile(app.chain.id, app.user.activeAccount.address);
if (profile) {
profile.invalidateName();
}
vnode.state.saving = false;
m.redraw();
};
const getInput = (inputLabel, inputName, description, prefixAt = false) => {
return m(FormGroup, [
m(FormLabel, {
for: inputName,
}, inputLabel),
m(Input, {
name: inputName,
id: inputName,
placeholder: description,
autocomplete: 'off',
contentLeft: prefixAt ? m(Icon, { name: Icons.AT_SIGN }) : null,
}),
]);
};
return m('.EditIdentityModal', [
m('.compact-modal-title', [
m('h3', 'Set on-chain identity')
]),
m(Form, { class: 'form' }, [
getInput('Display Name', 'display', 'A reasonable display name for the controller of the account'),
getInput('Legal Name', 'legal', 'Full legal name in the local jurisdiction of the entity'),
getInput('Website', 'web', 'Website for the controller of the account, https:// automatically prepended'),
getInput('Riot/Matrix', 'riot', 'Riot/Matrix handle held by the controller of the account'),
getInput('Email', 'email', 'Email address of the controller of the account'),
// getInput('PGP', 'pgp', 'PGP/GPG public key of the controller of the account'),
getInput('Twitter', 'twitter', 'Twitter identity of the controller of the account', true),
m('.form-bottom', [
m('.buttons', [
m(Button, {
intent: 'primary',
disabled: vnode.state.saving || !app.chain?.loaded,
rounded: true,
onclick: (e) => {
e.preventDefault();
updateIdentity().then(() => $(vnode.dom).trigger('modalexit'));
},
label: 'Set identity'
}),
m(Button, {
rounded: true,
onclick: (e) => {
e.preventDefault();
$(vnode.dom).trigger('modalexit');
},
label: 'Cancel'
}),
]),
m('.clear'),
])
])
]);
}
}