@polkadot/util#stringToU8a TypeScript Examples
The following examples show how to use
@polkadot/util#stringToU8a.
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: address.ts From subscan-multisig-react with Apache License 2.0 | 6 votes |
export function dvmAddressToAccountId(address: string | null | undefined): AccountId {
if (!address) {
return registry.createType('AccountId', '');
}
// eslint-disable-next-line no-magic-numbers
const data = new Uint8Array(32);
data.set(stringToU8a('dvm:'));
// eslint-disable-next-line no-magic-numbers
data.set(hexToU8a(address), 11);
// eslint-disable-next-line no-bitwise
const checksum = data.reduce((pre: number, current: number): number => pre ^ current);
// eslint-disable-next-line no-magic-numbers
data.set(numberToU8a(checksum), 31);
const accountId = registry.createType<AccountId>('AccountId', data);
return accountId;
}
Example #2
Source File: treasury.ts From commonwealth with GNU General Public License v3.0 | 6 votes |
public async init(ChainInfo: SubstrateChain, Accounts: SubstrateAccounts): Promise<void> {
this._disabled = !ChainInfo.api.query.treasury;
if (this._initializing || this._initialized || this.disabled) return;
this._initializing = true;
this._Chain = ChainInfo;
this._Accounts = Accounts;
// load server proposals
const entities = this.app.chain.chainEntities.store.getByType(SubstrateTypes.EntityKind.TreasuryProposal);
entities.forEach((e) => this._entityConstructor(e));
// save parameters
this._bondPct = +(ChainInfo.api.consts.treasury.proposalBond as Permill) / 1_000_000;
this._bondMinimum = this._Chain.coins(ChainInfo.api.consts.treasury.proposalBondMinimum as BalanceOf);
this._spendPeriod = +(ChainInfo.api.consts.treasury.spendPeriod as BlockNumber);
this._burnPct = +(ChainInfo.api.consts.treasury.burn as Permill) / 1_000_000;
const TREASURY_ACCOUNT = u8aToHex(stringToU8a('modlpy/trsry'.padEnd(32, '\0')));
const pot = await ChainInfo.api.derive.balances.account(TREASURY_ACCOUNT);
this._pot = this._Chain.coins(pot.freeBalance);
// register new chain-event handlers
this.app.chain.chainEntities.registerEntityHandler(
SubstrateTypes.EntityKind.TreasuryProposal, (entity, event) => {
this.updateProposal(entity, event);
}
);
// fetch proposals from chain
await this.app.chain.chainEntities.fetchEntities(
this.app.chain.id,
chainToEventNetwork(this.app.chain.meta),
() => this._Chain.fetcher.fetchTreasuryProposals(this.app.chain.block.height),
);
this._initialized = true;
this._initializing = false;
}
Example #3
Source File: account.ts From metamask-snap-polkadot with Apache License 2.0 | 6 votes |
/**
* Returns KeyringPair if one is saved in wallet state, creates new one otherwise
* @param wallet
*/
export async function getKeyPair(wallet: Wallet): Promise<KeyringPair> {
// get app key and wait for api to be ready
const [appKey] = await Promise.all([wallet.getAppKey(), cryptoWaitReady()]);
// generate keys
const seed = appKey.substr(0, 32);
const keyring = new Keyring({ss58Format: getConfiguration(wallet).addressPrefix});
return keyring.addFromSeed(stringToU8a(seed));
}
Example #4
Source File: BaseBytes.tsx From crust-apps with Apache License 2.0 | 6 votes |
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([])];
}
Example #5
Source File: encodeUrl.test.ts From parity-bridges-ui with GNU General Public License v3.0 | 6 votes |
describe('createPolkadotJsUrl', () => {
const jsonU8a = stringToU8a(JSON.stringify(types));
const compressed = zlibSync(jsonU8a, { level: 9 });
const encoded = base64Encode(compressed);
const providerUrl = 'wss://wss.rialto.brucke.link';
const expectedUrl = `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(providerUrl)}&types=${encodeURIComponent(
encoded
)}`;
describe('Test chain', () => {
it('should retrieve the expected URL for provided types', async () => {
const result = createPolkadotJsUrl(types, providerUrl);
expect(result).toEqual(expectedUrl);
});
});
});
Example #6
Source File: urlTypes.ts From subscan-multisig-react with Apache License 2.0 | 6 votes |
export function encodeUrlTypes(types: Record<string, any>): string {
const jsonU8a = stringToU8a(JSON.stringify(types));
const compressed = zlibSync(jsonU8a, { level: 9 });
const encoded = base64Encode(compressed);
return `${window.location.origin}${window.location.pathname}?rpc=${encodeURIComponent(
settings.apiUrl
)}&types=${encodeURIComponent(encoded)}`;
}
Example #7
Source File: util.ts From crust-apps with Apache License 2.0 | 6 votes |
// hash a message for use in signature recovery, adding the standard Ethereum header
export function hashMessage (message: string): Buffer {
const expanded = stringToU8a(`\x19Ethereum Signed Message:\n${message.length.toString()}${message}`);
const hashed = keccakAsU8a(expanded);
return u8aToBuffer(hashed);
}
Example #8
Source File: BaseBytes.tsx From subscan-multisig-react with Apache License 2.0 | 6 votes |
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([])];
}
Example #9
Source File: AccountName.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
KNOWN: [AccountId, string][] = [
[registry.createType('AccountId', stringToU8a('modlpy/socie'.padEnd(32, '\0'))), 'Society'],
[registry.createType('AccountId', stringToU8a('modlpy/trsry'.padEnd(32, '\0'))), 'Treasury'],
]
Example #10
Source File: Status.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
function filterEvents(
allAccounts: string[],
t: <T = string>(key: string, opts?: Record<string, unknown>) => T,
optionsAll?: KeyringOptions,
events?: EventRecord[]
): ActionStatus[] | null {
const eventHash = xxhashAsHex(stringToU8a(JSON.stringify(events)));
if (!optionsAll || !events || eventHash === prevEventHash) {
return null;
}
prevEventHash = eventHash;
return events
.map(({ event: { data, method, section } }): ActionStatus | null => {
if (section === 'balances' && method === 'Transfer') {
const account = data[1].toString();
if (allAccounts.includes(account)) {
return {
account,
action: `${section}.${method}`,
message: t<string>('transfer received'),
status: 'event',
};
}
} else if (section === 'democracy') {
const index = data[0].toString();
return {
action: `${section}.${method}`,
message: t<string>('update on #{{index}}', {
replace: {
index,
},
}),
status: 'event',
};
}
return null;
})
.filter((item): item is ActionStatus => !!item);
}
Example #11
Source File: index.ts From sdk with Apache License 2.0 | 5 votes |
async function signPayload(api: ApiPromise, { payload }, password: string) {
const { method, params } = payload;
const address = params[0];
const keyPair = ((window as any).keyring as Keyring).getPair(address);
try {
if (!keyPair.isLocked) {
keyPair.lock();
}
keyPair.decodePkcs8(password);
if (method == "signExtrinsic") {
const txInfo = params[1];
const { header, mortalLength, nonce } = (await api.derive.tx.signingInfo(address)) as any;
const tx = api.tx[txInfo.module][txInfo.call](...txInfo.params);
const signerPayload = api.registry.createType("SignerPayload", {
address,
blockHash: header.hash,
blockNumber: header ? header.number : 0,
era: api.registry.createType("ExtrinsicEra", {
current: header.number,
period: mortalLength,
}),
genesisHash: api.genesisHash,
method: tx.method,
nonce,
signedExtensions: ["CheckNonce"],
tip: txInfo.tip,
runtimeVersion: {
specVersion: api.runtimeVersion.specVersion,
transactionVersion: api.runtimeVersion.transactionVersion,
},
version: api.extrinsicVersion,
});
const payload = signerPayload.toPayload();
const txPayload = api.registry.createType("ExtrinsicPayload", payload, {
version: payload.version,
});
const signed = txPayload.sign(keyPair);
return signed;
}
if (method == "signBytes") {
const msg = params[1];
const isDataHex = isHex(msg);
return {
signature: u8aToHex(keyPair.sign(isDataHex ? hexToU8a(msg) : stringToU8a(msg))),
};
}
} catch (err) {
(window as any).send({ error: err.message });
}
return {};
}
Example #12
Source File: parachain.ts From sdk with Apache License 2.0 | 5 votes |
CROWD_PREFIX = stringToU8a('modlpy/cfund')
Example #13
Source File: gov.ts From sdk with Apache License 2.0 | 5 votes |
TREASURY_ACCOUNT = stringToU8a("modlpy/trsry".padEnd(32, "\0"))
Example #14
Source File: useTreasury.ts From subscan-multisig-react with Apache License 2.0 | 5 votes |
TREASURY_ACCOUNT = stringToU8a('modlpy/trsry'.padEnd(32, '\0'))
Example #15
Source File: getDeriveAccount.ts From parity-bridges-ui with GNU General Public License v3.0 | 5 votes |
export default function getDeriveAccount({ ss58Format = 42, bridgeId, address }: Data): string {
if (!address) {
return address;
}
const input = [...compactAddLength(stringToU8a(accountDerivation)), ...bridgeId, ...decodeAddress(address)];
return encodeAddress(blake2AsHex(Uint8Array.from(input)), ss58Format);
}
Example #16
Source File: createPolkadotJsUrl.ts From parity-bridges-ui with GNU General Public License v3.0 | 5 votes |
export function createPolkadotJsUrl(types: Record<string, any>, providerUrl: string): string {
const jsonU8a = stringToU8a(JSON.stringify(types));
const compressed = zlibSync(jsonU8a, { level: 9 });
const encoded = base64Encode(compressed);
return `https://polkadot.js.org/apps/?rpc=${encodeURIComponent(providerUrl)}&types=${encodeURIComponent(encoded)}`;
}
Example #17
Source File: modelUtils.ts From commonwealth with GNU General Public License v3.0 | 5 votes |
createAndVerifyAddress = async ({ chain }, mnemonic = 'Alice') => {
if (chain === 'ethereum' || chain === 'alex') {
const wallet_id = 'metamask';
const { keypair, address } = generateEthAddress();
let res = await chai.request
.agent(app)
.post('/api/createAddress')
.set('Accept', 'application/json')
.send({ address, chain, wallet_id });
const address_id = res.body.result.id;
const token = res.body.result.verification_token;
const chain_id = chain === 'alex' ? 3 : 1; // use ETH mainnet for testing except alex
const data = constructTypedMessage(chain_id, token);
const privateKey = keypair.getPrivateKey();
const signature = signTypedData({ privateKey, data, version: SignTypedDataVersion.V4 });
res = await chai.request
.agent(app)
.post('/api/verifyAddress')
.set('Accept', 'application/json')
.send({ address, chain, signature, wallet_id });
console.log(JSON.stringify(res.body));
const user_id = res.body.result.user.id;
const email = res.body.result.user.email;
return { address_id, address, user_id, email };
}
if (chain === 'edgeware') {
const wallet_id = 'polkadot';
const keyPair = new Keyring({
type: 'sr25519',
ss58Format: 7,
}).addFromMnemonic(mnemonic);
const address = keyPair.address;
let res = await chai.request
.agent(app)
.post('/api/createAddress')
.set('Accept', 'application/json')
.send({ address: keyPair.address, chain, wallet_id });
const address_id = res.body.result.id;
const token = res.body.result.verification_token;
const u8aSignature = keyPair.sign(stringToU8a(token));
const signature = u8aToHex(u8aSignature).slice(2);
res = await chai.request
.agent(app)
.post('/api/verifyAddress')
.set('Accept', 'application/json')
.send({ address, chain, signature, wallet_id });
const user_id = res.body.result.user.id;
const email = res.body.result.user.email;
return { address_id, address, user_id, email };
}
throw new Error('invalid chain');
}
Example #18
Source File: Events.tsx From subscan-multisig-react with Apache License 2.0 | 5 votes |
async function manageEvents(
api: ApiPromise,
prev: PrevHashes,
records: Vec<EventRecord>,
setState: React.Dispatch<React.SetStateAction<Events>>
): Promise<void> {
const newEvents: IndexedEvent[] = records
.map((record, index) => ({ indexes: [index], record }))
.filter(
({
record: {
event: { method, section },
},
}) =>
section !== 'system' &&
(!['balances', 'treasury'].includes(section) || !['Deposit'].includes(method)) &&
(!['parasInclusion', 'inclusion'].includes(section) ||
!['CandidateBacked', 'CandidateIncluded'].includes(method))
)
.reduce((combined: IndexedEvent[], e): IndexedEvent[] => {
const prev = combined.find(
({
record: {
event: { method, section },
},
}) => e.record.event.section === section && e.record.event.method === method
);
if (prev) {
prev.indexes.push(...e.indexes);
} else {
combined.push(e);
}
return combined;
}, [])
.reverse();
const newEventHash = xxhashAsHex(stringToU8a(stringify(newEvents)));
if (newEventHash !== prev.event && newEvents.length) {
prev.event = newEventHash;
// retrieve the last header, this will map to the current state
const header = await api.rpc.chain.getHeader(records.createdAtHash);
const blockNumber = header.number.unwrap();
const blockHash = header.hash.toHex();
if (blockHash !== prev.block) {
prev.block = blockHash;
setState(({ events }) => ({
eventCount: records.length,
events: [
...newEvents.map(
({ indexes, record }): KeyedEvent => ({
blockHash,
blockNumber,
indexes,
key: `${blockNumber.toNumber()}-${blockHash}-${indexes.join('.')}`,
record,
})
),
// remove all events for the previous same-height blockNumber
...events.filter((p) => !p.blockNumber?.eq(blockNumber)),
].slice(0, MAX_EVENTS),
}));
}
} else {
setState(({ events }) => ({
eventCount: records.length,
events,
}));
}
}
Example #19
Source File: Keyring.ts From gear-js with GNU General Public License v3.0 | 5 votes |
static sign(keyring: KeyringPair, message: string) {
return keyring.sign(stringToU8a(message));
}
Example #20
Source File: Events.tsx From crust-apps with Apache License 2.0 | 5 votes |
function EventsBase ({ children }: Props): React.ReactElement<Props> {
const { api } = useApi();
const [state, setState] = useState<Events>([]);
useEffect((): void => {
// No unsub, global context - destroyed on app close
api.isReady.then((): void => {
let prevBlockHash: string | null = null;
let prevEventHash: string | null = null;
api.query.system.events((records): void => {
const newEvents: IndexedEvent[] = records
.map((record, index) => ({ indexes: [index], record }))
.filter(({ record: { event: { method, section } } }) =>
section !== 'system' &&
(method !== 'Deposit' || !['balances', 'treasury'].includes(section)) &&
(section !== 'inclusion' || !['CandidateBacked', 'CandidateIncluded'].includes(method))
)
.reduce((combined: IndexedEvent[], e): IndexedEvent[] => {
const prev = combined.find(({ record: { event: { method, section } } }) => e.record.event.section === section && e.record.event.method === method);
if (prev) {
prev.indexes.push(...e.indexes);
} else {
combined.push(e);
}
return combined;
}, [])
.reverse();
const newEventHash = xxhashAsHex(stringToU8a(JSON.stringify(newEvents)));
if (newEventHash !== prevEventHash && newEvents.length) {
prevEventHash = newEventHash;
// retrieve the last header, this will map to the current state
api.rpc.chain.getHeader().then((header): void => {
const blockNumber = header.number.unwrap();
const blockHash = header.hash.toHex();
if (blockHash !== prevBlockHash) {
prevBlockHash = blockHash;
setState((events) => [
...newEvents.map(({ indexes, record }): KeyedEvent => ({
blockHash,
blockNumber,
indexes,
key: `${blockNumber.toNumber()}-${blockHash}-${indexes.join('.')}`,
record
})),
// remove all events for the previous same-height blockNumber
...events.filter((p) => !p.blockNumber?.eq(blockNumber))
].slice(0, MAX_EVENTS));
}
}).catch(console.error);
}
}).catch(console.error);
}).catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<EventsContext.Provider value={state}>
{children}
</EventsContext.Provider>
);
}
Example #21
Source File: useTreasury.ts From crust-apps with Apache License 2.0 | 5 votes |
TREASURY_ACCOUNT = stringToU8a('modlpy/trsry'.padEnd(32, '\0'))
Example #22
Source File: AccountName.tsx From crust-apps with Apache License 2.0 | 5 votes |
KNOWN: [AccountId, string][] = [
[registry.createType('AccountId', stringToU8a('modlpy/socie'.padEnd(32, '\0'))), 'Society'],
[registry.createType('AccountId', stringToU8a('modlpy/trsry'.padEnd(32, '\0'))), 'Treasury'],
//@ts-ignore
[registry.createType('AccountId', 'cTJp8A3DSBpyJth1HsyKtAqJ4KHqMUBqrJm9cXSqrDLTC9DHv'), 'Claims']
]
Example #23
Source File: urlTypes.ts From crust-apps with Apache License 2.0 | 5 votes |
export function encodeUrlTypes (types: Record<string, any>): string {
const jsonU8a = stringToU8a(JSON.stringify(types));
const compressed = zlibSync(jsonU8a, { level: 9 });
const encoded = base64Encode(compressed);
return `${window.location.origin}${window.location.pathname}?rpc=${encodeURIComponent(settings.apiUrl)}&types=${encodeURIComponent(encoded)}`;
}
Example #24
Source File: Hash.tsx From crust-apps with Apache License 2.0 | 5 votes |
function Hash (): React.ReactElement {
const { t } = useTranslation();
const [{ data, hash, isHexData }, setState] = useState<State>({
data: '',
hash: blake2AsHex(stringToU8a(''), 256),
isHexData: false
});
const _onChangeData = useCallback(
(data: string): void => {
const isHexData = isHex(data);
setState({
data,
hash: blake2AsHex(
isHexData
? hexToU8a(data)
: stringToU8a(data),
256
),
isHexData
});
},
[]
);
return (
<div className='toolbox--Hash'>
<div className='ui--row'>
<Input
autoFocus
className='full'
help={t<string>('The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.')}
label={t<string>('from the following data')}
onChange={_onChangeData}
value={data}
/>
</div>
<div className='ui--row'>
<Static
className='medium'
help={t<string>('Detection on the input string to determine if it is hex or non-hex.')}
label={t<string>('hex input data')}
value={
isHexData
? t<string>('Yes')
: t<string>('No')
}
/>
</div>
<div className='ui--row'>
<Output
className='full'
help={t<string>('The blake2b 256-bit hash of the actual input data.')}
isHidden={hash.length === 0}
isMonospace
label={t<string>('the resulting hash is')}
value={hash}
withCopy
/>
</div>
</div>
);
}
Example #25
Source File: Status.tsx From crust-apps with Apache License 2.0 | 5 votes |
function filterEvents (allAccounts: string[], t: <T = string> (key: string, opts?: Record<string, unknown>) => T, optionsAll?: KeyringOptions, events?: EventRecord[]): ActionStatus[] | null {
const eventHash = xxhashAsHex(stringToU8a(JSON.stringify(events)));
if (!optionsAll || !events || eventHash === prevEventHash) {
return null;
}
prevEventHash = eventHash;
return events
.map(({ event: { data, method, section } }): ActionStatus | null => {
if (section === 'balances' && method === 'Transfer') {
const account = data[1].toString();
if (allAccounts.includes(account)) {
return {
account,
action: `${section}.${method}`,
message: t<string>('transfer received'),
status: 'event'
};
}
} else if (section === 'democracy') {
const index = data[0].toString();
return {
action: `${section}.${method}`,
message: t<string>('update on #{{index}}', {
replace: {
index
}
}),
status: 'event'
};
}
return null;
})
.filter((item): item is ActionStatus => !!item);
}
Example #26
Source File: verifyAddress.ts From commonwealth with GNU General Public License v3.0 | 4 votes |
verifySignature = async (
models: DB,
chain: ChainInstance,
addressModel: AddressInstance,
user_id: number,
signatureString: string
): Promise<boolean> => {
if (!chain) {
log.error('no chain provided to verifySignature');
return false;
}
let isValid: boolean;
if (chain.base === ChainBase.Substrate) {
//
// substrate address handling
//
const address = decodeAddress(addressModel.address);
const keyringOptions: KeyringOptions = { type: 'sr25519' };
if (
!addressModel.keytype ||
addressModel.keytype === 'sr25519' ||
addressModel.keytype === 'ed25519'
) {
if (addressModel.keytype) {
keyringOptions.type = addressModel.keytype as KeypairType;
}
keyringOptions.ss58Format = chain.ss58_prefix ?? 42;
const signerKeyring = new Keyring(keyringOptions).addFromAddress(address);
const signedMessageNewline = stringToU8a(
`${addressModel.verification_token}\n`
);
const signedMessageNoNewline = stringToU8a(
addressModel.verification_token
);
const signatureU8a =
signatureString.slice(0, 2) === '0x'
? hexToU8a(signatureString)
: hexToU8a(`0x${signatureString}`);
isValid =
signerKeyring.verify(signedMessageNewline, signatureU8a, address) ||
signerKeyring.verify(signedMessageNoNewline, signatureU8a, address);
} else {
log.error('Invalid keytype.');
isValid = false;
}
} else if (
chain.base === ChainBase.CosmosSDK &&
addressModel.wallet_id === WalletId.CosmosEvmMetamask
) {
//
// ethereum address handling on cosmos chains
//
const msgBuffer = Buffer.from(addressModel.verification_token.trim());
// toBuffer() doesn't work if there is a newline
const msgHash = ethUtil.hashPersonalMessage(msgBuffer);
const ethSignatureParams = ethUtil.fromRpcSig(signatureString.trim());
const publicKey = ethUtil.ecrecover(
msgHash,
ethSignatureParams.v,
ethSignatureParams.r,
ethSignatureParams.s
);
const addressBuffer = ethUtil.publicToAddress(publicKey);
const lowercaseAddress = ethUtil.bufferToHex(addressBuffer);
try {
// const ethAddress = Web3.utils.toChecksumAddress(lowercaseAddress);
const injAddrBuf = ethUtil.Address.fromString(
lowercaseAddress.toString()
).toBuffer();
const injAddress = bech32.encode(
chain.bech32_prefix,
bech32.toWords(injAddrBuf)
);
if (addressModel.address === injAddress) isValid = true;
} catch (e) {
isValid = false;
}
} else if (chain.base === ChainBase.CosmosSDK && chain.bech32_prefix === 'terra') {
//
// cosmos-sdk address handling
//
// provided string should be serialized AminoSignResponse object
const { signature: stdSignature } =
JSON.parse(signatureString);
// we generate an address from the actual public key and verify that it matches,
// this prevents people from using a different key to sign the message than
// the account they registered with.
// TODO: ensure ion works
const bech32Prefix = chain.bech32_prefix;
if (!bech32Prefix) {
log.error('No bech32 prefix found.');
isValid = false;
} else {
const generatedAddress = pubkeyToAddress(
stdSignature.pub_key,
bech32Prefix
);
if (generatedAddress === addressModel.address) {
try {
// directly verify the generated signature, generated via SignBytes
const { pubkey, signature } = decodeSignature(stdSignature)
const secpSignature = Secp256k1Signature.fromFixedLength(signature);
const messageHash = new Sha256(
Buffer.from(addressModel.verification_token.trim())
).digest();
isValid = await Secp256k1.verifySignature(
secpSignature,
messageHash,
pubkey
);
} catch (e) {
isValid = false;
}
}
}
} else if (chain.base === ChainBase.CosmosSDK) {
//
// cosmos-sdk address handling
//
// provided string should be serialized AminoSignResponse object
const { signed, signature: stdSignature }: AminoSignResponse =
JSON.parse(signatureString);
// we generate an address from the actual public key and verify that it matches,
// this prevents people from using a different key to sign the message than
// the account they registered with.
// TODO: ensure ion works
const bech32Prefix = chain.bech32_prefix;
if (!bech32Prefix) {
log.error('No bech32 prefix found.');
isValid = false;
} else {
const generatedAddress = pubkeyToAddress(
stdSignature.pub_key,
bech32Prefix
);
const generatedAddressWithCosmosPrefix = pubkeyToAddress(
stdSignature.pub_key,
'cosmos'
);
if (
generatedAddress === addressModel.address ||
generatedAddressWithCosmosPrefix === addressModel.address
) {
let generatedSignDoc: StdSignDoc;
try {
// query chain ID from URL
const node = await chain.getChainNode();
const client = await StargateClient.connect(node.url);
const chainId = await client.getChainId();
client.disconnect();
generatedSignDoc = validationTokenToSignDoc(
chainId,
addressModel.verification_token.trim(),
signed.fee,
signed.memo,
<any>signed.msgs
);
} catch (e) {
log.info(e.message);
}
// ensure correct document was signed
if (
generatedSignDoc &&
serializeSignDoc(signed).toString() ===
serializeSignDoc(generatedSignDoc).toString()
) {
// ensure valid signature
const { pubkey, signature } = decodeSignature(stdSignature);
const secpSignature = Secp256k1Signature.fromFixedLength(signature);
const messageHash = new Sha256(
serializeSignDoc(generatedSignDoc)
).digest();
isValid = await Secp256k1.verifySignature(
secpSignature,
messageHash,
pubkey
);
if (!isValid) {
log.error('Signature verification failed.');
}
} else {
log.error(
`Sign doc not matched. Generated: ${JSON.stringify(
generatedSignDoc
)}, found: ${JSON.stringify(signed)}.`
);
isValid = false;
}
} else {
log.error(
`Address not matched. Generated ${generatedAddress}, found ${addressModel.address}.`
);
isValid = false;
}
}
} else if (chain.base === ChainBase.Ethereum) {
//
// ethereum address handling
//
try {
const node = await chain.getChainNode();
const typedMessage = constructTypedMessage(node.eth_chain_id || 1, addressModel.verification_token.trim());
const address = recoverTypedSignature({
data: typedMessage,
signature: signatureString.trim(),
version: SignTypedDataVersion.V4,
});
isValid = addressModel.address.toLowerCase() === address.toLowerCase();
if (!isValid) {
log.info(
`Eth verification failed for ${addressModel.address}: does not match recovered address ${address}`
);
}
} catch (e) {
log.info(
`Eth verification failed for ${addressModel.address}: ${e.message}`
);
isValid = false;
}
} else if (chain.base === ChainBase.NEAR) {
//
// near address handling
//
// both in base64 encoding
const { signature: sigObj, publicKey } = JSON.parse(signatureString);
isValid = nacl.sign.detached.verify(
Buffer.from(`${addressModel.verification_token}\n`),
Buffer.from(sigObj, 'base64'),
Buffer.from(publicKey, 'base64')
);
} else if (chain.base === ChainBase.Solana) {
//
// solana address handling
//
// ensure address is base58 string length 32, cf @solana/web3 impl
try {
const decodedAddress = bs58.decode(addressModel.address);
if (decodedAddress.length === 32) {
isValid = nacl.sign.detached.verify(
Buffer.from(`${addressModel.verification_token}`),
Buffer.from(signatureString, 'base64'),
decodedAddress
);
} else {
isValid = false;
}
} catch (e) {
isValid = false;
}
} else {
// invalid network
log.error(`invalid network: ${chain.network}`);
isValid = false;
}
addressModel.last_active = new Date();
if (isValid && user_id === null) {
// mark the address as verified, and if it doesn't have an associated user, create a new user
addressModel.verification_token_expires = null;
addressModel.verified = new Date();
if (!addressModel.user_id) {
const user = await models.User.createWithProfile(models, { email: null });
addressModel.profile_id = (user.Profiles[0] as ProfileAttributes).id;
await models.Subscription.create({
subscriber_id: user.id,
category_id: NotificationCategories.NewMention,
object_id: `user-${user.id}`,
is_active: true,
});
await models.Subscription.create({
subscriber_id: user.id,
category_id: NotificationCategories.NewCollaboration,
object_id: `user-${user.id}`,
is_active: true,
});
addressModel.user_id = user.id;
}
} else if (isValid) {
// mark the address as verified
addressModel.verification_token_expires = null;
addressModel.verified = new Date();
addressModel.user_id = user_id;
const profile = await models.Profile.findOne({ where: { user_id } });
addressModel.profile_id = profile.id;
}
await addressModel.save();
return isValid;
}
Example #27
Source File: Sign.tsx From crust-apps with Apache License 2.0 | 4 votes |
function Sign ({ className = '' }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [currentPair, setCurrentPair] = useState<KeyringPair | null>(() => keyring.getPairs()[0] || null);
const [{ data, isHexData }, setData] = useState<DataState>({ data: '', isHexData: false });
const [{ isInjected }, setAccountState] = useState<AccountState>({ isExternal: false, isHardware: false, isInjected: false });
const [isLocked, setIsLocked] = useState(false);
const [{ isUsable, signer }, setSigner] = useState<SignerState>({ isUsable: true, signer: null });
const [signature, setSignature] = useState('');
const [isUnlockVisible, toggleUnlock] = useToggle();
useEffect((): void => {
const meta = (currentPair && currentPair.meta) || {};
const isExternal = (meta.isExternal as boolean) || false;
const isHardware = (meta.isHardware as boolean) || false;
const isInjected = (meta.isInjected as boolean) || false;
const isUsable = !(isExternal || isHardware || isInjected);
setAccountState({ isExternal, isHardware, isInjected });
setIsLocked(
isInjected
? false
: (currentPair && currentPair.isLocked) || false
);
setSignature('');
setSigner({ isUsable, signer: null });
// for injected, retrieve the signer
if (meta.source && isInjected) {
web3FromSource(meta.source as string)
.catch((): null => null)
.then((injected) => setSigner({
isUsable: isFunction(injected?.signer?.signRaw),
signer: injected?.signer || null
}))
.catch(console.error);
}
}, [currentPair]);
const _onChangeAccount = useCallback(
(accountId: string | null) => accountId && setCurrentPair(keyring.getPair(accountId)),
[]
);
const _onChangeData = useCallback(
(data: string) => setData({ data, isHexData: isHex(data) }),
[]
);
const _onSign = useCallback(
(): void => {
if (isLocked || !isUsable || !currentPair) {
return;
}
if (signer && isFunction(signer.signRaw)) {
setSignature('');
signer
.signRaw({
address: currentPair.address,
data: isHexData
? data
: stringToHex(data),
type: 'bytes'
})
.then(({ signature }) => setSignature(signature))
.catch(console.error);
} else {
setSignature(u8aToHex(
currentPair.sign(
isHexData
? hexToU8a(data)
: stringToU8a(data)
)
));
}
},
[currentPair, data, isHexData, isLocked, isUsable, signer]
);
const _onUnlock = useCallback(
(): void => {
setIsLocked(false);
toggleUnlock();
},
[toggleUnlock]
);
return (
<div className={`toolbox--Sign ${className}`}>
<div className='ui--row'>
<InputAddress
className='full'
help={t<string>('select the account you wish to sign data with')}
isInput={false}
label={t<string>('account')}
onChange={_onChangeAccount}
type='account'
/>
</div>
<div className='toolbox--Sign-input'>
<div className='ui--row'>
<Input
autoFocus
className='full'
help={t<string>('The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.')}
label={t<string>('sign the following data')}
onChange={_onChangeData}
value={data}
/>
</div>
<div className='ui--row'>
<Static
className='medium'
help={t<string>('Detection on the input string to determine if it is hex or non-hex.')}
label={t<string>('hex input data')}
value={
isHexData
? t<string>('Yes')
: t<string>('No')
}
/>
</div>
<div className='ui--row'>
<Output
className='full'
help={t<string>('The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).')}
isHidden={signature.length === 0}
isMonospace
label={t<string>('signature of supplied data')}
value={signature}
withCopy
/>
</div>
<div
className='unlock-overlay'
hidden={!isUsable || !isLocked || isInjected}
>
{isLocked && (
<div className='unlock-overlay-warning'>
<div className='unlock-overlay-content'>
{t<string>('You need to unlock this account to be able to sign data.')}<br/>
<Button.Group>
<Button
icon='unlock'
label={t<string>('Unlock account')}
onClick={toggleUnlock}
/>
</Button.Group>
</div>
</div>
)}
</div>
<div
className='unlock-overlay'
hidden={isUsable}
>
<div className='unlock-overlay-warning'>
<div className='unlock-overlay-content'>
{isInjected
? t<string>('This injected account cannot be used to sign data since the extension does not support raw signing.')
: t<string>('This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.')}
</div>
</div>
</div>
{isUnlockVisible && (
<Unlock
onClose={toggleUnlock}
onUnlock={_onUnlock}
pair={currentPair}
/>
)}
</div>
<Button.Group>
<Button
icon='key'
isDisabled={!(isUsable && !isLocked)}
label={t<string>('Sign message')}
onClick={_onSign}
/>
</Button.Group>
</div>
);
}
Example #28
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 #29
Source File: hooks.ts From crust-apps with Apache License 2.0 | 4 votes |
export function useSign (account: LoginUser, metamask: Metamask, near: NearM, flow: FlowM, solana: SolanaM, elrond: ElrondM): UseSign {
const [state, setState] = useState<UseSign>({ isLocked: true });
useEffect(() => {
if (!account.account) return;
// eslint-disable-next-line
const nearKeyPair = near?.keyPair;
if (account.wallet === 'near' && near.wallet && nearKeyPair) {
setState((o) => ({ ...o, isLocked: false }));
const sign = function (data: string): Promise<string> {
const msg = Buffer.from(data);
// eslint-disable-next-line
const { signature } = nearKeyPair.sign(msg);
const hexSignature = Buffer.from(signature).toString('hex');
return Promise.resolve<string>(hexSignature);
};
setState((o) => ({ ...o, sign }));
return;
}
if (account.wallet === 'flow') {
setState((o) => ({ ...o, isLocked: false }));
const sign = function (data: string): Promise<string> {
const msg = Buffer.from(data);
// eslint-disable-next-line
return fcl.currentUser().signUserMessage(msg.toString('hex'))
.then((res: any) => {
if (!res) {
throw new Error('Signature failed');
}
if (_.includes(res, 'Declined: User rejected signature')) {
throw new Error('User rejected signature');
}
return window.btoa(JSON.stringify(res));
});
};
setState((o) => ({ ...o, sign }));
return;
}
if (account.wallet === 'solana') {
setState((o) => ({ ...o, isLocked: false }));
const sign = function (data: string): Promise<string> {
const encodedMessage = new TextEncoder().encode(data);
// eslint-disable-next-line
return window.solana.signMessage(encodedMessage, 'utf8')
// eslint-disable-next-line
.then((sig: any) => Buffer.from(sig.signature).toString('hex'));
};
setState((o) => ({ ...o, sign }));
return;
}
if (account.wallet === 'elrond') {
// waiting for sign
const provider = elrond.provider;
if (elrond.isInstalled && provider) {
const sign = function (data: string): Promise<string> {
const address = provider.account.address;
const signableMessage = new SignableMessage({
address: new Address(address),
message: Buffer.from('0x' + Buffer.from(address).toString('hex'), 'ascii')
});
return provider.signMessage(signableMessage).then((message) => {
return `elrond-${address}-${signableMessage.serializeForSigning().toString('hex')}:${message.signature.hex()}`;
})
.catch((err) => {
console.error('Elrond wallet signMessage error', err);
return '';
});
};
setState((o) => ({ ...o, sign, isLocked: false }));
}
return;
}
if (account.wallet === 'metamask') {
setState((o) => ({ ...o, isLocked: false }));
const ethReq = metamask?.ethereum?.request;
if (metamask.isInstalled && metamask.isAllowed && ethReq) {
const sign = function (data: string): Promise<string> {
const msg = Buffer.from(data, 'utf8').toString('hex');
// const msg = data;
console.info('msg::', msg);
return ethReq<string>({
from: account.account,
params: [msg, account.account],
method: 'personal_sign'
}).then((signature) => {
console.info('signData:', signature);
// return ethReq<string>({
// from: account.account,
// params: [msg, signature],
// method: 'personal_ecRecover'
// }).then((res) => {
// console.info('recover:', res);
//
// return signature;
// });
return signature;
});
};
setState((o) => ({ ...o, sign }));
}
} else {
const currentPair = keyring.getPair(account.account);
if (!currentPair) return;
const meta = currentPair.meta || {};
const isInjected = (meta.isInjected as boolean) || false;
const accountIsLocked = isInjected ? false : currentPair.isLocked || false;
setState((o) => ({ ...o, isLocked: accountIsLocked }));
// for injected, retrieve the signer
if (meta.source && isInjected) {
web3FromSource(meta.source as string)
.catch(() => null)
.then((injected) => {
const signRaw = injected?.signer?.signRaw;
if (signRaw && isFunction(signRaw)) {
const sign = function (data: string): Promise<string> {
return signRaw({
address: currentPair.address,
data: stringToHex(data),
type: 'bytes'
}).then((res) => res.signature);
};
setState((o) => ({ ...o, sign }));
}
return null;
})
.catch(console.error);
} else {
const sign = function (data: string, password?: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
try {
if (accountIsLocked) {
currentPair.unlock(password);
}
resolve(u8aToHex(currentPair.sign(stringToU8a(data))));
} catch (e) {
reject(e);
}
}, 100);
});
};
setState((o) => ({ ...o, sign }));
}
}
}, [account, metamask, near, flow, solana, elrond]);
return state;
}