@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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
// 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
CROWD_PREFIX = stringToU8a('modlpy/cfund')
Example #13
Source File: gov.ts    From sdk with Apache License 2.0 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
export default function getDeriveAccount({ ss58Format = 42, bridgeId, address }: Data): string {
  if (!address) {
    return address;
  }
  const input = [...compactAddLength(stringToU8a(accountDerivation)), ...bridgeId, ...decodeAddress(address)];
  return encodeAddress(blake2AsHex(Uint8Array.from(input)), ss58Format);
}
Example #16
Source File: createPolkadotJsUrl.ts    From parity-bridges-ui with GNU General Public License v3.0 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
TREASURY_ACCOUNT = stringToU8a('modlpy/trsry'.padEnd(32, '\0'))
Example #22
Source File: AccountName.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 &amp; 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 vote down vote up
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;
}