From c2bbebe06b927bf9caca2bf8e7019e8bc17f945e Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Wed, 3 Feb 2021 02:06:17 +0900 Subject: [PATCH 01/31] commit for basic polkadotChainState --- package.json | 1 + src/chainFactory.ts | 3 + src/chains/polkadot_1/ChainPolkadotV1.ts | 202 ++++++++++++++++++ src/chains/polkadot_1/models/generalModels.ts | 37 ++++ src/chains/polkadot_1/models/index.ts | 1 + src/chains/polkadot_1/polkadotChainState.ts | 113 ++++++++++ src/index.ts | 2 + src/models/generalModels.ts | 1 + 8 files changed, 360 insertions(+) create mode 100644 src/chains/polkadot_1/ChainPolkadotV1.ts create mode 100644 src/chains/polkadot_1/models/generalModels.ts create mode 100644 src/chains/polkadot_1/models/index.ts create mode 100644 src/chains/polkadot_1/polkadotChainState.ts diff --git a/package.json b/package.json index cf59ee06..41d9f5c0 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "dependencies": { "@aikon/sjcl": "^0.1.8", + "@polkadot/api": "^3.6.4", "@types/bignumber.js": "^5.0.0", "@types/bn.js": "^4.11.6", "@types/bs58": "^4.0.1", diff --git a/src/chainFactory.ts b/src/chainFactory.ts index e0850a57..e5a315ed 100644 --- a/src/chainFactory.ts +++ b/src/chainFactory.ts @@ -1,6 +1,7 @@ import { ChainEosV2 } from './chains/eos_2/ChainEosV2' import { ChainEthereumV1 } from './chains/ethereum_1/ChainEthereumV1' import { ChainAlgorandV1 } from './chains/algorand_1/ChainAlgorandV1' +import { ChainPolkadotV1 } from './chains/polkadot_1/ChainPolkadotV1' import { Chain } from './interfaces' import { ChainType, ChainEndpoint } from './models' import { throwNewError } from './errors' @@ -16,6 +17,8 @@ export class ChainFactory { return new ChainEthereumV1(endpoints, settings) case ChainType.AlgorandV1: return new ChainAlgorandV1(endpoints, settings) + case ChainType.PolkadotV1: + return new ChainPolkadotV1(endpoints, settings) default: throwNewError(`Chain type ${chainType} is not supported`) } diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts new file mode 100644 index 00000000..2a7fc90a --- /dev/null +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -0,0 +1,202 @@ +import { + ChainActionType, + ChainInfo, + ChainType, + CryptoCurve, + ChainEntityName, + ChainDate, +} from '../../models' +import { + ChainError, +} from '../../errors' +import { Chain } from '../../interfaces' +import { + PolkadotChainEndpoint, + PolkadotChainSettings, + PolkadotNewKeysOptions, + PolkadotSymbol +} from './models' +import { PolkadotChainState } from './polkadotChainState' +import { notImplemented } from '../../helpers' +import { PolkadotChainActionType } from './models/chainActionType' +import { + PolkadotAddress, + PolkadotTransactionAction, +} from './models/transactionModels' +import { PolkadotDecomposeReturn } from './models/PolkadotStructures' +import { PolkadotAccount } from './polkadotAccount' +import { PolkadotTransaction } from './polkadotTransaction' + +import * as ethcrypto from '../ethereum_1/ethCrypto' +import { Asymmetric } from '../../crypto' +import { + isValidEthereumPrivateKey, + isValidEthereumPublicKey, + isValidEthereumDateString, + toEthereumEntityName, + toEthereumDate, + toEthereumPublicKey, + toEthereumPrivateKey, + toEthereumSignature, +} from '../ethereum_1/helpers' +import { PolkadotPublicKey } from './models/cryptoModels' + +class ChainPolkadotV1 implements Chain { + private _endpoints: PolkadotChainEndpoint[] + + private _settings: PolkadotChainSettings + + private _chainState: PolkadotChainState + + constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { + this._endpoints = endpoints + this._settings = settings + this._chainState = new PolkadotChainState(endpoints, settings) + } + + public get isConnected(): boolean { + return this._chainState?.isConnected + } + + public get chainType(): ChainType { + return ChainType.PolkadotV1 + } + + public connect(): Promise { + return this._chainState.connect() + } + + public get chainId(): string { + notImplemented() + return null + } + + public get chainInfo(): ChainInfo { + notImplemented() + return null + } + + public composeAction = async ( + actionType: ChainActionType | PolkadotChainActionType, + args: any, + ): Promise => { + notImplemented() + return null + } + + public decomposeAction = async ( + action: PolkadotTransactionAction + ): Promise => { + notImplemented() + return null + } + + public get description(): string { + return 'Polkadot Chain' + } + + public get nativeToken(): { defaultUnit: string; symbol: PolkadotSymbol; tokenAddress: any } { + notImplemented() + return null + } + + public async fetchBalance( + account: PolkadotAddress, + symbol: PolkadotSymbol, + tokenAddress?: PolkadotAddress, + ): Promise<{ balance: string }> { + notImplemented() + return null + } + + public fetchContractData = ( + contract: string, + table: string, + owner: string, + indexNumber: number, + lowerRow: number, + upperRow: number, + limit: number, + reverseOrder: boolean, + showPayer: boolean, + keyType: string, + ): Promise => { + return null + } + + private newAccount = async (address?: PolkadotAddress): Promise => { + notImplemented() + return null + } + + private newCreateAccount = (options?: PolkadotNewKeysOptions): any => { + notImplemented() + return null + } + + private newTransaction = (options?: any): PolkadotTransaction => { + notImplemented() + return null + } + + public new = { + Account: this.newAccount, + CreateAccount: this.newCreateAccount, + Transaction: this.newTransaction, + } + + public isValidEntityName = (value: string): boolean => { + notImplemented() + return false + } + + public isValidDate = (value: string): boolean => { + notImplemented() + return false + } + + public toEntityName = (value: string): ChainEntityName => { + return toEthereumEntityName(value) as ChainEntityName + } + + public toDate = (value: string | Date ): ChainDate => { + return toEthereumDate(value) as ChainDate + } + + public setPublicKey = (publicKey: PolkadotPublicKey) => { + notImplemented() + return '' + } + + public mapChainError = (error: Error): ChainError => { + notImplemented() + return new ChainError(null, null, null, error) + } + + cryptoCurve: CryptoCurve.Secp256k1 + + decryptWithPassword = ethcrypto.decryptWithPassword + encryptWithPassword = ethcrypto.encryptWithPassword + decryptWithPrivateKey = ethcrypto.decryptWithPrivateKey + encryptWithPublicKey = ethcrypto.encryptWithPublicKey + decryptWithPrivateKeys = ethcrypto.decryptWithPrivateKeys + encryptWithPublicKeys = ethcrypto.encryptWithPublicKeys + getPublicKeyFromSignature = ethcrypto.getEthereumPublicKeyFromSignature + generateKeyPair = ethcrypto.generateKeyPair + isSymEncryptedDataString = ethcrypto.isSymEncryptedDataString + isAsymEncryptedDataString = Asymmetric.isAsymEncryptedDataString + toAsymEncryptedDataString = Asymmetric.toAsymEncryptedDataString + toSymEncryptedDataString = ethcrypto.toSymEncryptedDataString + toPublicKey = toEthereumPublicKey + toPrivateKey = toEthereumPrivateKey + toSignature = toEthereumSignature + + sign = ethcrypto.sign + verifySignedWithPublicKey = ethcrypto.verifySignedWithPublicKey + + isValidPrivateKey = isValidEthereumPrivateKey + isValidPublicKey = isValidEthereumPublicKey + isValidEthereumDate = isValidEthereumDateString +} + +export { ChainPolkadotV1 } \ No newline at end of file diff --git a/src/chains/polkadot_1/models/generalModels.ts b/src/chains/polkadot_1/models/generalModels.ts new file mode 100644 index 00000000..a22d6a54 --- /dev/null +++ b/src/chains/polkadot_1/models/generalModels.ts @@ -0,0 +1,37 @@ +import { ChainSymbolBrand } from '../../../models' +import { + ModelsCryptoAes, +} from '../../../models' + +export type PolkadotChainEndpoint = { + url: string +} + +export type PolkadotChainSettings = {} + +export type PolkadotChainInfo = { + headBlockNumber: number + headBlockHash: string + nativeInfo: PolkadotNativeInfo +} + +export type PolkadotNativeInfo = { + chain: string + existentialDeposit: number + transactionByteFee: number +} + +export type PolkadotSymbol = string & ChainSymbolBrand + +export type PolkadotNewKeysOptions = { + password: string + encryptionOptions?: ModelsCryptoAes.AesEncryptionOptions +} + +export type PolkadotChainSettingsCommunicationSettings = { + blocksToCheck: number + checkInterval: number + getBlockAttempts: number +} + +export type PolkadotBlockHash = string | Uint8Array \ No newline at end of file diff --git a/src/chains/polkadot_1/models/index.ts b/src/chains/polkadot_1/models/index.ts new file mode 100644 index 00000000..0b9274de --- /dev/null +++ b/src/chains/polkadot_1/models/index.ts @@ -0,0 +1 @@ +export * from './generalModels' \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts new file mode 100644 index 00000000..aa8d65ad --- /dev/null +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -0,0 +1,113 @@ +import { throwAndLogError, throwNewError } from '../../errors' +import { PolkadotBlockHash, PolkadotChainEndpoint, PolkadotChainInfo, PolkadotChainSettings } from './models' +import { ApiPromise, WsProvider } from '@polkadot/api' +import { isNullOrEmpty, trimTrailingChars } from '../../helpers' + +export class PolkadotChainState { + private _chainInfo: PolkadotChainInfo + + private _chainSettings: PolkadotChainSettings + + private _endpoints: PolkadotChainEndpoint[] + + private _activeEndpoint: PolkadotChainEndpoint + + private _isConnected: boolean = false + + private _api: ApiPromise + + constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { + this._endpoints = endpoints + this._chainSettings = settings + } + + public get activeEndpoint(): PolkadotChainEndpoint { + return this._activeEndpoint + } + + public get chain(): string { + this.assertIsConnected() + return this._chainInfo?.nativeInfo?.chain.toString() + } + + public get chainInfo(): PolkadotChainInfo { + this.assertIsConnected() + return this._chainInfo + } + + public get isConnected(): boolean { + return this._isConnected + } + + public get endpoints(): PolkadotChainEndpoint[] { + return this._endpoints + } + + public async connect(): Promise { + try { + if (!this._api) { + const { url, endpoint } = this.selectEndpoint() + this._activeEndpoint = endpoint + this._api = new ApiPromise({ provider: new WsProvider(url) }) + await this._api.isReady + } + await this.getChainInfo() + this._isConnected = true + } catch (error) { + throwAndLogError('Problem connection to api', 'apiConnectFailed', error) + } + } + + private async createConnection(): Promise { + try { + if (!this._api.isConnected) { + await this._api.connect() + } + } catch (error) { + throwAndLogError('Problem connection to chain', 'chainConnectFailed', error) + } + } + + private async purgeConnection(): Promise { + try { + this._api?.disconnect() + } catch (error) { + throwAndLogError('Problem disconnection from chain', 'chainDisconnectFailed', error) + } + } + + public async getChainInfo(): Promise { + try { + await this.createConnection() + const [chain, latestHeader] = await Promise.all([ + this._api.rpc.system.chain(), + this._api.rpc.chain.getHeader(), + ]) + + this._chainInfo = { + headBlockNumber: latestHeader.number.toNumber(), + headBlockHash: latestHeader.hash.toString(), + nativeInfo: { + chain: chain.toString(), + existentialDeposit: this._api.consts.balances.existentialDeposit.toNumber(), + transactionByteFee: this._api.consts.transactionPayment.transactionByteFee.toNumber() + } + } + await this.purgeConnection() + return this._chainInfo + } catch (error) { + throwAndLogError('error', 'error', error) + } + } + + private selectEndpoint() : { url: string; endpoint: PolkadotChainEndpoint } { + const selectedEndpoint = this.endpoints[0] + const endpointUrl = new URL(selectedEndpoint.url) + return { url: trimTrailingChars(endpointUrl?.href, '/'), endpoint: selectedEndpoint } + } + public assertIsConnected(): void { + if (!this._isConnected) { + throwNewError('Not connected to chain') + } + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 570b0abb..99cda229 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import * as Models from './models' import * as ModelsAlgorand from './chains/algorand_1/models' import * as ModelsEos from './chains/eos_2/models' import * as ModelsEthereum from './chains/ethereum_1/models' +import * as ModelsPolkadot from './chains/polkadot_1/models' import * as HelpersAlgorand from './chains/algorand_1/helpers' import * as HelpersEos from './chains/eos_2/helpers' import * as HelpersEthereum from './chains/ethereum_1/helpers' @@ -37,5 +38,6 @@ export { ModelsAlgorand, ModelsEos, ModelsEthereum, + ModelsPolkadot, Transaction, } diff --git a/src/models/generalModels.ts b/src/models/generalModels.ts index a3323a83..8fcab223 100644 --- a/src/models/generalModels.ts +++ b/src/models/generalModels.ts @@ -39,6 +39,7 @@ export enum ChainType { EosV2 = 'eos', EthereumV1 = 'ethereum', AlgorandV1 = 'algorand', + PolkadotV1 = 'polkadot', } /** Chain urls and related details used to connect to chain */ From 4513bfc9a2743446215839d4b1df42f5c653209e Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Tue, 9 Feb 2021 00:21:01 +0900 Subject: [PATCH 02/31] commit for basic model of polkadot plugin --- src/chains/polkadot_1/ChainPolkadotV1.ts | 22 +- src/chains/polkadot_1/examples/misc.ts | 35 ++ src/chains/polkadot_1/helpers/index.ts | 37 ++ src/chains/polkadot_1/index.ts | 0 src/chains/polkadot_1/models/accountModels.ts | 15 + .../polkadot_1/models/chainActionType.ts | 3 + src/chains/polkadot_1/models/cryptoModels.ts | 11 + src/chains/polkadot_1/models/generalModels.ts | 65 ++- .../polkadot_1/models/polkadotStructures.ts | 8 + .../polkadot_1/models/transactionModels.ts | 17 + src/chains/polkadot_1/polkadotAccount.ts | 83 ++++ src/chains/polkadot_1/polkadotAction.ts | 9 + src/chains/polkadot_1/polkadotChainState.ts | 126 ++++-- .../polkadot_1/polkadotChainState_http.ts | 157 +++++++ .../polkadot_1/polkadotCreateAccount.ts | 130 ++++++ src/chains/polkadot_1/polkadotCrypto.ts | 38 ++ src/chains/polkadot_1/polkadotTransaction.ts | 423 ++++++++++++++++++ 17 files changed, 1126 insertions(+), 53 deletions(-) create mode 100644 src/chains/polkadot_1/examples/misc.ts create mode 100644 src/chains/polkadot_1/helpers/index.ts create mode 100644 src/chains/polkadot_1/index.ts create mode 100644 src/chains/polkadot_1/models/accountModels.ts create mode 100644 src/chains/polkadot_1/models/chainActionType.ts create mode 100644 src/chains/polkadot_1/models/cryptoModels.ts create mode 100644 src/chains/polkadot_1/models/polkadotStructures.ts create mode 100644 src/chains/polkadot_1/models/transactionModels.ts create mode 100644 src/chains/polkadot_1/polkadotAccount.ts create mode 100644 src/chains/polkadot_1/polkadotAction.ts create mode 100644 src/chains/polkadot_1/polkadotChainState_http.ts create mode 100644 src/chains/polkadot_1/polkadotCreateAccount.ts create mode 100644 src/chains/polkadot_1/polkadotCrypto.ts create mode 100644 src/chains/polkadot_1/polkadotTransaction.ts diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts index 2a7fc90a..0ce612f0 100644 --- a/src/chains/polkadot_1/ChainPolkadotV1.ts +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -14,13 +14,14 @@ import { PolkadotChainEndpoint, PolkadotChainSettings, PolkadotNewKeysOptions, - PolkadotSymbol + PolkadotSymbol, + PolkadotAddress, } from './models' import { PolkadotChainState } from './polkadotChainState' import { notImplemented } from '../../helpers' import { PolkadotChainActionType } from './models/chainActionType' import { - PolkadotAddress, + PolkadotTransactionAction, } from './models/transactionModels' import { PolkadotDecomposeReturn } from './models/PolkadotStructures' @@ -40,6 +41,8 @@ import { toEthereumSignature, } from '../ethereum_1/helpers' import { PolkadotPublicKey } from './models/cryptoModels' +import { SignedBlock } from '@polkadot/types/interfaces/runtime' +import { PolkadotCreateAccount } from './polkadotCreateAccount' class ChainPolkadotV1 implements Chain { private _endpoints: PolkadotChainEndpoint[] @@ -72,8 +75,7 @@ class ChainPolkadotV1 implements Chain { } public get chainInfo(): ChainInfo { - notImplemented() - return null + return this._chainState.chainInfo } public composeAction = async ( @@ -130,8 +132,8 @@ class ChainPolkadotV1 implements Chain { } private newCreateAccount = (options?: PolkadotNewKeysOptions): any => { - notImplemented() - return null + this.assertIsConnected() + return new PolkadotCreateAccount(this._chainState) } private newTransaction = (options?: any): PolkadotTransaction => { @@ -173,7 +175,13 @@ class ChainPolkadotV1 implements Chain { return new ChainError(null, null, null, error) } - cryptoCurve: CryptoCurve.Secp256k1 + public assertIsConnected(): void { + if (!this._chainState?.isConnected) { + throw new Error('Not connected to chain') + } + } + + cryptoCurve: CryptoCurve.Ed25519 decryptWithPassword = ethcrypto.decryptWithPassword encryptWithPassword = ethcrypto.encryptWithPassword diff --git a/src/chains/polkadot_1/examples/misc.ts b/src/chains/polkadot_1/examples/misc.ts new file mode 100644 index 00000000..0aa6fe2d --- /dev/null +++ b/src/chains/polkadot_1/examples/misc.ts @@ -0,0 +1,35 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable max-len */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { ChainFactory, ChainType } from '../../../index' +import { PolkadotChainEndpoint } from '../models' + +/* eslint-disable no-console */ +require('dotenv').config() + +export const { env } = process + +export const serverEndpoints: PolkadotChainEndpoint[] = [ + { + // url: 'https://rpc.polkadot.io', + // options: { + // headers: { + // 'Content-Type': 'application/json', + // } + // } + url: 'wss://rpc.polkadot.io' + }, +]; + +(async () => { + try { + const polka = new ChainFactory().create(ChainType.PolkadotV1, serverEndpoints) + await polka.connect() + // console.debug(polka.chainInfo) + } catch (error) { + console.log(error) + } +})() diff --git a/src/chains/polkadot_1/helpers/index.ts b/src/chains/polkadot_1/helpers/index.ts new file mode 100644 index 00000000..3f07f9b1 --- /dev/null +++ b/src/chains/polkadot_1/helpers/index.ts @@ -0,0 +1,37 @@ +import { PolkadotPublicKey } from "../models/cryptoModels"; +import { isNullOrEmpty } from "../../../helpers"; +import { PolkadotAddress } from "../models"; +import { decodeAddress, encodeAddress } from '@polkadot/keyring' +import { hexToU8a, isHex } from '@polkadot/util' + +export function isValidPolkadotPublicKey(value: PolkadotPublicKey): boolean { + return true +} + +export function isValidPolkadotAddress(address: PolkadotAddress): boolean { + try { + encodeAddress( + isHex(address) + ? hexToU8a(address) + : decodeAddress(address) + ) + + return true + } catch (error) { + return false + } +} + +export function toPolkadotEntityName(name: string): PolkadotAddress { + if (isValidPolkadotAddress(name)) { + return name + } + + if (isNullOrEmpty(name)) { + return null + } + + throw new Error( + `Not a valid Ethereum entity :${name}. Ethereum entity can valid address, public key, private key or transaction data.`, + ) +} \ No newline at end of file diff --git a/src/chains/polkadot_1/index.ts b/src/chains/polkadot_1/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/chains/polkadot_1/models/accountModels.ts b/src/chains/polkadot_1/models/accountModels.ts new file mode 100644 index 00000000..1def1d43 --- /dev/null +++ b/src/chains/polkadot_1/models/accountModels.ts @@ -0,0 +1,15 @@ +import { + PolkadotPublicKey, + PolkadotNewKeysOptions +} from './' + +export type PolkadotCreateAccountOptions = { + publicKey?: PolkadotPublicKey + newKeysOptions: PolkadotNewKeysOptions +} + +/** Type of account to create */ +export enum EthereumNewAccountType { + /** Native account for chain type (Ethereum, etc.) */ + Native = 'Native', +} diff --git a/src/chains/polkadot_1/models/chainActionType.ts b/src/chains/polkadot_1/models/chainActionType.ts new file mode 100644 index 00000000..fb5efa4b --- /dev/null +++ b/src/chains/polkadot_1/models/chainActionType.ts @@ -0,0 +1,3 @@ +export enum PolkadotChainActionType { + +} \ No newline at end of file diff --git a/src/chains/polkadot_1/models/cryptoModels.ts b/src/chains/polkadot_1/models/cryptoModels.ts new file mode 100644 index 00000000..f510025b --- /dev/null +++ b/src/chains/polkadot_1/models/cryptoModels.ts @@ -0,0 +1,11 @@ +import { + SignatureBrand, +} from '../../../models' + +export interface ECDSASignature { + v: number + r: Buffer + s: Buffer +} + +export type PolkadotSignature = ECDSASignature & SignatureBrand diff --git a/src/chains/polkadot_1/models/generalModels.ts b/src/chains/polkadot_1/models/generalModels.ts index a22d6a54..12abee51 100644 --- a/src/chains/polkadot_1/models/generalModels.ts +++ b/src/chains/polkadot_1/models/generalModels.ts @@ -1,37 +1,74 @@ -import { ChainSymbolBrand } from '../../../models' -import { - ModelsCryptoAes, -} from '../../../models' +import BN from 'bn.js' +import { AnyJson, AnyNumber, } from '@polkadot/types/types' +import { AccountData } from '@polkadot/types/interfaces/balances' +import { Compact } from '@polkadot/types' +import { KeyringPair } from '@polkadot/keyring/types' +import { BlockNumber, Balance, Weight, Hash, } from '@polkadot/types/interfaces' +import { ChainSymbolBrand, PublicKeyBrand, PrivateKeyBrand, } from '../../../models' export type PolkadotChainEndpoint = { url: string + options?: { + headers?: { [key: string]: string } + } } export type PolkadotChainSettings = {} -export type PolkadotChainInfo = { +export type PolkadotChainInfo = { headBlockNumber: number - headBlockHash: string + headBlockTime: Date + version: string nativeInfo: PolkadotNativeInfo } export type PolkadotNativeInfo = { chain: string - existentialDeposit: number - transactionByteFee: number + transacitonByteFee: Balance + decimals: number + SS58: number } export type PolkadotSymbol = string & ChainSymbolBrand -export type PolkadotNewKeysOptions = { - password: string - encryptionOptions?: ModelsCryptoAes.AesEncryptionOptions -} - export type PolkadotChainSettingsCommunicationSettings = { blocksToCheck: number checkInterval: number getBlockAttempts: number } -export type PolkadotBlockHash = string | Uint8Array \ No newline at end of file +export type PolkadotBlockNumber = BlockNumber | BN | BigInt | Uint8Array | number | string + +export type PolkadotBlock = Record + +export type PolkadotAddress = string | Uint8Array + +export type PolkadotKeyringPair = KeyringPair + +export type DotAmount = Compact | AnyNumber | Uint8Array + +export type PolkadotPaymentInfo = { + weight: Weight + partialFee: Balance +} + +export type PolkadotAccountBalance = AccountData + +export type PolkadotHash = Hash + +export type PolkadotKeypairType = 'ed25519' | 'sr25519' | 'ecdsa' + +export type PolkadotNewKeysOptions = { + phrase?: string + type?: PolkadotKeypairType + derivationPath?: string +} + +export type PolkadotKeypair = { + publicKey: PolkadotPublicKey + secretKey: PolkadotPrivateKey +} + +export type PolkadotPublicKey = Uint8Array & PublicKeyBrand + +export type PolkadotPrivateKey = Uint8Array & PrivateKeyBrand \ No newline at end of file diff --git a/src/chains/polkadot_1/models/polkadotStructures.ts b/src/chains/polkadot_1/models/polkadotStructures.ts new file mode 100644 index 00000000..cc190675 --- /dev/null +++ b/src/chains/polkadot_1/models/polkadotStructures.ts @@ -0,0 +1,8 @@ +import { ChainActionType } from '../../../models' +import { PolkadotChainActionType } from './chainActionType' + +export type PolkadotDecomposeReturn = { + chainActionType: ChainActionType | PolkadotChainActionType + args: any + partial: boolean +} \ No newline at end of file diff --git a/src/chains/polkadot_1/models/transactionModels.ts b/src/chains/polkadot_1/models/transactionModels.ts new file mode 100644 index 00000000..701da5a3 --- /dev/null +++ b/src/chains/polkadot_1/models/transactionModels.ts @@ -0,0 +1,17 @@ +export type PolkadotTransactionAction = {} + +export type PolkadotActionHelperInput = {} + +export type PolkadotTransactionOptions = {} + +export type PolkadotRawTransaction = {} + +export type PolkadotTransactionHeader = {} + +export type PolkadotAddressBuffer = Buffer + +export type PolkadotTransactionCost = {} + +export type PolkadotSetDesiredFeeOptions = {} + +export type PolkadotTransactionResources = {} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotAccount.ts b/src/chains/polkadot_1/polkadotAccount.ts new file mode 100644 index 00000000..777cee8d --- /dev/null +++ b/src/chains/polkadot_1/polkadotAccount.ts @@ -0,0 +1,83 @@ +import { notSupported } from '../../helpers' +import { Account } from '../../interfaces' +import { PolkadotAddress, PolkadotPublicKey } from './models' +import { PolkadotChainState } from './polkadotChainState' +import { throwNewError } from '../../errors' +import { isValidPolkadotAddress, isValidPolkadotPublicKey } from './helpers' +import { getPolkadotAddressFromPublicKey } from './polkadotCrypto' + +export class PolkadotAccount implements Account { + private _address: PolkadotAddress + + private _publicKey: PolkadotPublicKey + + private _chainState: PolkadotChainState + + constructor(chainState: PolkadotChainState) { + this._chainState = chainState + } + + get canBeRecycled(): boolean { + return notSupported('PolkadotAccount.canBeRecycled') + } + + get name(): any { + this.assertHasAddress() + return this._address + } + + get publicKeys(): any { + this.assertHasAddress() + return this._publicKey + } + + isValidNewAccountName = async (accountName: PolkadotAddress): Promise => { + return isValidPolkadotAddress(accountName) + } + + load = async (address?: PolkadotAddress): Promise => { + this.assertValidPolkadotAddress(address) + this._address = address + } + + setPublicKey = async (publicKey: PolkadotPublicKey) => { + this.assertValidPolkadotPublickey(publicKey) + this._publicKey = publicKey + this._address = getPolkadotAddressFromPublicKey(publicKey) + } + + get supportsOnChainAccountRegistry(): boolean { + return false + } + + get supportsRecycling(): boolean { + return false + } + + toJson() { + this.assertHasAddress() + return { address: this._address } + } + + get value(): PolkadotAddress { + return this._address + } + + private assertHasAddress(): void { + if (!this._address) { + throwNewError('Polkadot address or Public key not provided') + } + } + + private assertValidPolkadotAddress(address: PolkadotAddress): void { + if (!isValidPolkadotAddress(address)) { + throwNewError('Not a valid polkadot address') + } + } + + private assertValidPolkadotPublickey(publicKey: PolkadotPublicKey): void { + if (!isValidPolkadotPublicKey(publicKey)) { + throwNewError('Not a valid polkadot public key') + } + } +} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotAction.ts b/src/chains/polkadot_1/polkadotAction.ts new file mode 100644 index 00000000..07e5e2d4 --- /dev/null +++ b/src/chains/polkadot_1/polkadotAction.ts @@ -0,0 +1,9 @@ +export type ActionChainOptions = { + chain: string + hardfork: string +} + +export class PolkadotActionHelper { + constructor(actionInput: PolkadotActionHelper, chainOptions: ActionChainOptions) { + } +} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts index aa8d65ad..aed9ae48 100644 --- a/src/chains/polkadot_1/polkadotChainState.ts +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -1,7 +1,20 @@ +import { + PolkadotBlock, + PolkadotBlockNumber, + PolkadotChainEndpoint, + PolkadotChainInfo, + PolkadotChainSettings, + PolkadotAddress, + PolkadotPaymentInfo, + PolkadotAccountBalance, + PolkadotHash, + PolkadotKeyringPair, + DotAmount, +} from './models' +import { trimTrailingChars } from '../../helpers' import { throwAndLogError, throwNewError } from '../../errors' -import { PolkadotBlockHash, PolkadotChainEndpoint, PolkadotChainInfo, PolkadotChainSettings } from './models' +import BigNumber from 'bignumber.js' import { ApiPromise, WsProvider } from '@polkadot/api' -import { isNullOrEmpty, trimTrailingChars } from '../../helpers' export class PolkadotChainState { private _chainInfo: PolkadotChainInfo @@ -16,6 +29,8 @@ export class PolkadotChainState { private _api: ApiPromise + private _provider: WsProvider + constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { this._endpoints = endpoints this._chainSettings = settings @@ -45,12 +60,15 @@ export class PolkadotChainState { public async connect(): Promise { try { - if (!this._api) { + if (!this._provider) { const { url, endpoint } = this.selectEndpoint() this._activeEndpoint = endpoint - this._api = new ApiPromise({ provider: new WsProvider(url) }) - await this._api.isReady + this._provider = new WsProvider(url) + } + if (!this._api) { + this._api = await ApiPromise.create({ provider: this._provider }) } + await this.getChainInfo() this._isConnected = true } catch (error) { @@ -58,42 +76,26 @@ export class PolkadotChainState { } } - private async createConnection(): Promise { - try { - if (!this._api.isConnected) { - await this._api.connect() - } - } catch (error) { - throwAndLogError('Problem connection to chain', 'chainConnectFailed', error) - } - } - - private async purgeConnection(): Promise { - try { - this._api?.disconnect() - } catch (error) { - throwAndLogError('Problem disconnection from chain', 'chainDisconnectFailed', error) - } - } - public async getChainInfo(): Promise { try { - await this.createConnection() - const [chain, latestHeader] = await Promise.all([ + const [headBlockTime, chain, version, lastBlockHead] = await Promise.all([ + this._api.query.timestamp.now(), this._api.rpc.system.chain(), + this._api.rpc.system.version(), this._api.rpc.chain.getHeader(), ]) this._chainInfo = { - headBlockNumber: latestHeader.number.toNumber(), - headBlockHash: latestHeader.hash.toString(), - nativeInfo: { + headBlockNumber: lastBlockHead.number.toNumber(), + headBlockTime: new Date(headBlockTime.toNumber()), + version: version.toString(), + nativeInfo: { chain: chain.toString(), - existentialDeposit: this._api.consts.balances.existentialDeposit.toNumber(), - transactionByteFee: this._api.consts.transactionPayment.transactionByteFee.toNumber() + transacitonByteFee: this._api.consts.transactionPayment.transactionByteFee, + decimals: this._api.registry.chainDecimals[0], + SS58: this._api.registry.chainSS58, } } - await this.purgeConnection() return this._chainInfo } catch (error) { throwAndLogError('error', 'error', error) @@ -105,9 +107,69 @@ export class PolkadotChainState { const endpointUrl = new URL(selectedEndpoint.url) return { url: trimTrailingChars(endpointUrl?.href, '/'), endpoint: selectedEndpoint } } + + public async getBlock(blockNumber: PolkadotBlockNumber): Promise { + try { + const blockHash = await this._api.rpc.chain.getBlockHash(blockNumber) + const block = await this._api.rpc.chain.getBlock(blockHash) + return block.toHuman() + } catch (error) { + throw error + } + } + + public async getBalance(address: string): Promise { + try { + const { data } = await this._api.query.system.account(address) + + return data + } catch (error) { + throw error + } + } + + public async estimateTxFee(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + try { + const info = await this._api.tx.balances.transfer(receiver, amount) + .paymentInfo(sender) + + const paymentInfo: PolkadotPaymentInfo = { + weight: info.weight, + partialFee: info.partialFee + } + + return paymentInfo + } catch (error) { + throw error + } + } + + public async sendTransaction(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + try{ + const tx = await this._api.tx.balances.transfer(receiver, amount) + const paymentInfo = await tx.paymentInfo(sender) + const fee = paymentInfo.partialFee.toString() + const balanceInfo = await this.getBalance(sender.address) + const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) + + const bnFee = new BigNumber(fee, this._chainInfo.nativeInfo.decimals) + const bnAmount = new BigNumber(amount.toString(), this._chainInfo.nativeInfo.decimals) + const bnBalance = new BigNumber(balance.toString(), this._chainInfo.nativeInfo.decimals) + + if (bnFee.plus(bnAmount).isGreaterThan(bnBalance) ) { + throw new Error('Insufficient balance') + } + + const txHash = await tx.signAndSend(sender) + return txHash + } catch (error) { + throw error + } + } + public assertIsConnected(): void { if (!this._isConnected) { - throwNewError('Not connected to chain') + throwNewError('Not connected to chain') } } } \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotChainState_http.ts b/src/chains/polkadot_1/polkadotChainState_http.ts new file mode 100644 index 00000000..ec120743 --- /dev/null +++ b/src/chains/polkadot_1/polkadotChainState_http.ts @@ -0,0 +1,157 @@ +import { throwAndLogError, throwNewError } from '../../errors' +import { PolkadotBlock, PolkadotBlockNumber, PolkadotChainEndpoint, PolkadotChainInfo, PolkadotChainSettings } from './models' +import { ApiPromise } from '@polkadot/api' +import { HttpProvider } from '@polkadot/rpc-provider' +import { trimTrailingChars } from '../../helpers' +import { AnyTuple } from '@polkadot/types/types' +import { GenericExtrinsic } from '@polkadot/types' + +export class PolkadotChainState { + private _chainInfo: PolkadotChainInfo + + private _chainSettings: PolkadotChainSettings + + private _endpoints: PolkadotChainEndpoint[] + + private _activeEndpoint: PolkadotChainEndpoint + + private _isConnected: boolean = false + + private _isReady: boolean = false + + private _provider: HttpProvider + + constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { + this._endpoints = endpoints + this._chainSettings = settings + } + + public get activeEndpoint(): PolkadotChainEndpoint { + return this._activeEndpoint + } + + public get chain(): string { + this.assertIsConnected() + return this._chainInfo?.nativeInfo?.chain.toString() + } + + public get chainInfo(): PolkadotChainInfo { + this.assertIsConnected() + return this._chainInfo + } + + public get chainSettings(): PolkadotChainSettings { + return this._chainSettings + } + + public get endpoints(): PolkadotChainEndpoint[] { + return this._endpoints + } + + public get isConnected(): boolean { + return this._isConnected + } + + public async connect(): Promise { + try { + if (!this._provider) { + const { url, endpoint } = this.selectEndpoint() + this._activeEndpoint = endpoint + const httpProviderOptions = this.mapOptionsToHttpProviderOption(endpoint) + this._provider = new HttpProvider(url, httpProviderOptions) + } + await this.getChainInfo() + this._isConnected = true + } catch (error) { + throwAndLogError('Problem connecting to chain provider', 'chainProviderConnectFailed', error) + } + } + + public mapOptionsToHttpProviderOption(endpoint: PolkadotChainEndpoint): Record { + const headers : Record = {} + const headerObj = Object(endpoint.options?.headers) + Object.keys(headerObj).forEach(key => { + headers[key] = headerObj[key] + }) + return headers; + } + + public async getChainInfo(): Promise { + try { + const [chain, version, latestBlockHead, latestBlockHash] = await Promise.all([ + this._provider.send('system_chain', []), + this._provider.send('system_version', []), + this._provider.send('chain_getHeader', []), + this._provider.send('chain_getBlockHash', []), + ]) + const res = await this._provider.send('chain_getBlock', [latestBlockHash]) + let api = await this.createApi() + const block = api.createType('SignedBlock', res) + const extrinsics: GenericExtrinsic[] = block.block.extrinsics.toArray() + const timestamp: string = extrinsics.map(extrinsic => { + const typedExt = extrinsic.toHuman() + const jsonExt = JSON.parse(JSON.stringify(typedExt)) + if (jsonExt?.method?.section == 'timestamp') { + return jsonExt.method.args[0] + } + return '' + }).reduce((pV, cV) => pV != '' ? pV : cV, '') + this._chainInfo = { + headBlockNumber: parseInt((latestBlockHead?.number | 0).toString()), + headBlockTime: new Date( parseInt(timestamp.split(',').join('')) ), + version, + nativeInfo: { + chain: chain.toString(), + } + } + this.removeApi(api) + + return this._chainInfo + } catch (error) { + throwAndLogError('error', 'error', error) + } + } + + private createApi(): Promise { + return ApiPromise.create({provider: new HttpProvider('https://rpc.polkadot.io')}) + } + + private async removeApi(api: ApiPromise): Promise { + api?.disconnect() + } + + private selectEndpoint() : { url: string; endpoint: PolkadotChainEndpoint } { + const selectedEndpoint = this.endpoints[0] + const endpointUrl = new URL(selectedEndpoint.url) + return { url: trimTrailingChars(endpointUrl?.href, '/'), endpoint: selectedEndpoint } + } + + public async getBlock(blockNumber: PolkadotBlockNumber): Promise { + try { + this.assertIsConnected() + const blockHash = await this._provider.send('chain_getBlockHash', [blockNumber]) + const block = await this._provider.send('chain_getBlock', [blockHash]) + let api = await this.createApi() + const enBlock = api.createType('SignedBlock', block) + this.removeApi(api) + + return enBlock.toHuman() + } catch (error) { + throw error + } + } + + public async getTransactionCount(): Promise { + + } + + public async fetchBalance(): Promise { + + } + + public assertIsConnected(): void { + if (!this._isConnected) { + throwNewError('Not connected to chain') + } + } +} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotCreateAccount.ts b/src/chains/polkadot_1/polkadotCreateAccount.ts new file mode 100644 index 00000000..357214d8 --- /dev/null +++ b/src/chains/polkadot_1/polkadotCreateAccount.ts @@ -0,0 +1,130 @@ +import { Keyring } from '@polkadot/api' +import { PolkadotCreateAccountOptions } from './models/accountModels' +import { PolkadotChainState } from './polkadotChainState' +import { generateNewAccountPhrase } from './polkadotCrypto' +import { isValidPolkadotPublicKey, toPolkadotEntityName } from './helpers' +import { isNullOrEmpty, notSupported } from '../../helpers' +import { CreateAccount } from '../../interfaces' +import { throwNewError } from '../../errors' +import { + PolkadotAddress, + PolkadotPublicKey, + PolkadotKeypairType, + PolkadotKeyringPair +} from './models' + +export class PolkadotCreateAccount implements CreateAccount { + private _accountName: string + + private _accountType: PolkadotKeypairType + + private _chainState: PolkadotChainState + + private _options: PolkadotCreateAccountOptions + + private _generatedKeyringPair: PolkadotKeyringPair + + constructor(chainState: PolkadotChainState, options?: PolkadotCreateAccountOptions) { + this._chainState = chainState + this._options = options + } + + public get accountName(): any { + return this._accountName + } + + public get accountType(): PolkadotKeypairType { + return this._accountType + } + + public getSS58Format(): number { + return this._chainState.chainInfo.nativeInfo.SS58 + } + + get didRecycleAccount() { + return false + } + + get generatedKeys() { + if (this._generatedKeyringPair) { + return this._generatedKeyringPair + } + return null + } + + get options() { + return this._options + } + + get transaction(): any { + throwNewError( + 'Polkadot account creation does not require any on chain transactions. You should always first check the supportsTransactionToCreateAccount property - if false, transaction is not supported/required for this chain type', + ) + return null + } + + get supportsTransactionToCreateAccount(): boolean { + return false + } + + async composeTransaction(): Promise { + notSupported('CreateAccount.composeTransaction') + } + + async determineNewAccountName(accountName: PolkadotAddress): Promise { + return { alreadyExists: false, newAccountName: accountName, canRecycle: false } + } + + async generateAccountName(): Promise { + const accountName = await this.generateAccountNameString() + return toPolkadotEntityName(accountName) + } + + async generateAccountNameString(): Promise { + await this.generateKeysIfNeeded() + return this.accountName as string + } + + async generateKeysIfNeeded() { + let publicKey: PolkadotPublicKey + this.assertValidOptionPublicKeys() + this.assertValidOptionNewKeys() + + publicKey = this?._options?.publicKey + if (!publicKey) { + await this.generateAccountKeys() + } + this._accountName = this._generatedKeyringPair.address + this._accountType = this._generatedKeyringPair.type as PolkadotKeypairType + } + + private async generateAccountKeys() { + const { newKeysOptions } = this._options + const { derivationPath } = newKeysOptions || {} + const overrideType = 'ed25519' + const overridePhrase = generateNewAccountPhrase() + const keyring = new Keyring({ ss58Format: this.getSS58Format(), type: overrideType }) + const suri = derivationPath ? overridePhrase + '//' + derivationPath : overridePhrase + + const generatedKeyPair = keyring.createFromUri(`${suri}`) + this._generatedKeyringPair = generatedKeyPair + this._options.publicKey = this._generatedKeyringPair.publicKey as PolkadotPublicKey + this._options.newKeysOptions.phrase = overridePhrase + this._options.newKeysOptions.type = overrideType + } + + private assertValidOptionPublicKeys() { + const { publicKey } = this._options + if (publicKey && isValidPolkadotPublicKey(publicKey)) { + throwNewError('Invalid option - provided publicKey isnt valid') + } + } + + private assertValidOptionNewKeys() { + const { newKeysOptions } = this._options + const { phrase } = newKeysOptions || {} + if (isNullOrEmpty(phrase)) { + throwNewError('Invalid option - you must provide a phrase to generate new keys') + } + } +} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts new file mode 100644 index 00000000..25a66e7d --- /dev/null +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -0,0 +1,38 @@ +import { + PolkadotAddress, + PolkadotPublicKey, + PolkadotKeypair, + PolkadotKeypairType +} from "./models" +import { + mnemonicGenerate, + mnemonicToMiniSecret, +} from '@polkadot/util-crypto' +import { + encodeAddress, + naclKeypairFromSeed as naclFromSeed, + schnorrkelKeypairFromSeed as schnorrkelFromSeed, + secp256k1KeypairFromSeed as secp256k1FromSeed, +} from '@polkadot/util-crypto' + +export const keypairFromSeed = { + ecdsa: (seed: Uint8Array): PolkadotKeypair => secp256k1FromSeed(seed) as PolkadotKeypair, + ed25519: (seed: Uint8Array): PolkadotKeypair => naclFromSeed(seed) as PolkadotKeypair, + sr25519: (seed: Uint8Array): PolkadotKeypair => schnorrkelFromSeed(seed) as PolkadotKeypair +} + +export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { + return encodeAddress(publicKey) +} + +export function generateNewAccountPhrase(): string { + const mnemonic = mnemonicGenerate() + return mnemonic +} + +export function generateNewKeyPair(type: PolkadotKeypairType): PolkadotKeypair { + const mnemonic = generateNewAccountPhrase() + const seed = mnemonicToMiniSecret(mnemonic) + const keyPair = keypairFromSeed[type](seed) + return keyPair +} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotTransaction.ts b/src/chains/polkadot_1/polkadotTransaction.ts new file mode 100644 index 00000000..c80500e2 --- /dev/null +++ b/src/chains/polkadot_1/polkadotTransaction.ts @@ -0,0 +1,423 @@ +import { ConfirmType, TxExecutionPriority } from '../../models' +import { notImplemented } from '../../helpers' +import { Transaction } from '../../interfaces' +import { PolkadotSignature, } from './models/cryptoModels' +import { + PolkadotAddress, + PolkadotPublicKey, + PolkadotPrivateKey, + PolkadotChainSettingsCommunicationSettings, +} from './models' +import { + PolkadotAddressBuffer, + PolkadotTransactionOptions, + PolkadotRawTransaction, + PolkadotTransactionHeader, + PolkadotActionHelperInput, + PolkadotTransactionAction, + PolkadotTransactionCost, + PolkadotSetDesiredFeeOptions, + PolkadotTransactionResources, +} from './models/transactionModels' +import { PolkadotActionHelper } from './polkadotAction' +import { PolkadotChainState } from './polkadotChainState' + +export class PolkadotTransaction implements Transaction { + private _actionHelper: PolkadotActionHelper + + private _chainState: PolkadotChainState + + private _actualCost: string + + private _desiredFee: string + + /** estimated gas for transacton - encoded as string to handle big numbers */ + private _estimatedGas: string + + private _maxFeeIncreasePercentage: number + + private _isValidated: boolean + + private _options: PolkadotTransactionOptions + + private _signBuffer: Buffer + + constructor(chainState: PolkadotChainState, options?: PolkadotTransactionOptions) { + this._chainState = chainState + this._options = options + this.applyDefaultOptions() + } + + private applyDefaultOptions() { + notImplemented() + } + + /** Multisig transactions are not supported by ethereum */ + public get isMultiSig(): boolean { + return false + } + + /** Whether transaction has been validated - via validate() */ + get isValidated() { + return this._isValidated + } + + /** Address from which transaction is being sent- from action.from (if provided) or derived from attached signature */ + get senderAddress() { + notImplemented() + return '' + } + + /** Address retrieved from attached signature - Returns null if no signature attached */ + get signedByAddress(): PolkadotAddress { + notImplemented() + return null + } + + /** Public Key retrieved from attached signature - Returns null if no from value or signature attached */ + get signedByPublicKey(): PolkadotPublicKey { + notImplemented() + return null + } + + /** Header includes values included in transaction when sent to the chain + * These values are set by setRawProperties() is called since it includes gasPrice, gasLimit, etc. + */ + get header(): PolkadotTransactionHeader { + notImplemented() + return null + } + + /** Options provided when the transaction class was created */ + get options() { + return this._options + } + + /** Raw transaction body - all values are Buffer types */ + get raw(): PolkadotRawTransaction { + notImplemented() + return null + } + + /** Whether the raw transaction body has been set (via setting action or setFromRaw()) */ + get hasRaw(): boolean { + notImplemented() + return false + } + + /** Ethereum doesn't have any native multi-sig functionality */ + get supportsMultisigTransaction(): boolean { + return false + } + + /** + * Updates 'raw' transaction properties using the actions attached + * Creates and sets private _ethereumJsTx (web3 EthereumJsTx object) + * Also adds header values (nonce, gasPrice, gasLimit) if not already set in action + */ + private setRawProperties(): void { + notImplemented() + } + + /** update locally cached EthereumJsTx from action helper data */ + updateEthTxFromAction() { + notImplemented() + } + + /** + * Updates nonce and gas fees (if necessary) - these values must be present + */ + public async prepareToBeSigned(): Promise { + notImplemented() + return null + } + + /** Set the body of the transaction using Hex raw transaction data */ + async setFromRaw(raw: PolkadotActionHelperInput): Promise { + notImplemented() + return null + } + + /** Creates a sign buffer using raw transaction body */ + private setSignBuffer() { + notImplemented() + } + + /** calculates a unique nonce value for the tx (if not already set) by using the chain transaction count for a given address */ + async setNonceIfEmpty(fromAddress: PolkadotAddress | PolkadotAddressBuffer) { + notImplemented() + } + + /** Ethereum transaction action (transfer & contract functions) + * Returns null or an array with exactly one action + */ + public get actions(): PolkadotTransactionAction[] { + notImplemented() + return null + } + + /** Private property for the Ethereum contract action - uses _actionHelper */ + private get action(): PolkadotTransactionAction { + notImplemented() + return null + } + + /** Sets actions array + * Array length has to be exactly 1 because ethereum doesn't support multiple actions + */ + public set actions(actions: PolkadotTransactionAction[]) { + notImplemented() + } + + /** Add action to the transaction body + * throws if transaction.actions already has a value + * Ignores asFirstAction parameter since only one action is supported in ethereum */ + public addAction(action: PolkadotTransactionAction, asFirstAction?: boolean): void { + notImplemented() + } + + // validation + + /** Verifies that raw trx exists, sets nonce (using sender's address) if not already set + * Throws if any problems */ + public async validate(): Promise { + notImplemented() + } + + // signatures + + /** Get signature attached to transaction - returns null if no signature */ + get signatures(): PolkadotSignature[] { + notImplemented() + return null + } + + /** Sets the Set of signatures */ + set signatures(signatures: PolkadotSignature[]) { + notImplemented() + } + + /** Add signature to raw transaction - Accepts array with exactly one signature */ + addSignatures = (signatures: PolkadotSignature[]): void => { + notImplemented() + } + + /** Throws if signatures isn't properly formatted */ + private assertValidSignature = (signature: PolkadotSignature) => { + notImplemented() + } + + /** Whether there is an attached signature */ + get hasAnySignatures(): boolean { + notImplemented() + return false + } + + /** Throws if any signatures are attached */ + private assertNoSignatures() { + notImplemented() + } + + /** Throws if transaction is missing any signatures */ + private assertHasSignature(): void { + notImplemented() + } + + /** Whether there is an attached signature for the provided publicKey */ + public hasSignatureForPublicKey = (publicKey: PolkadotPublicKey): boolean => { + notImplemented() + return false + } + + /** Whether there is an attached signature for the publicKey of the address */ + public async hasSignatureForAuthorization(authorization: PolkadotAddress): Promise { + notImplemented() + return false + } + + /** Whether signature is attached to transaction (and/or whether the signature is correct) + * If a specific action.from is specifed, ensure that attached signature matches its address/public key */ + public get hasAllRequiredSignatures(): boolean { + notImplemented() + return false + } + + /** Throws if transaction is missing any signatures */ + private assertHasAllRequiredSignature(): void { + notImplemented() + } + + /** Returns address, for which, a matching signature must be attached to transaction + * ... always an array of length 1 because ethereum only supports one signature + * If no action.from is set, and no signature attached, throws an error since from addr cant be determined + * Throws if action.from is not a valid address */ + public get missingSignatures(): PolkadotAddress[] { + notImplemented() + return null + } + + // Fees + + /** Ethereum has a fee for transactions */ + public get supportsFee(): boolean { + return true + } + + /** Gets estimated cost in units of gas to execute this transaction (at current chain rates) */ + public async resourcesRequired(): Promise { + notImplemented() + return null + } + + /** Gets the estimated cost for this transaction + * if refresh = true, get updated cost from chain */ + async getEstimatedGas(refresh: boolean = false) { + notImplemented() + return 0 + } + + /** Get the suggested Eth fee (in Ether) for this transaction */ + public async getSuggestedFee(priority: TxExecutionPriority = TxExecutionPriority.Average): Promise { + notImplemented() + return null + } + + /** get the desired fee (in Ether) to spend on sending the transaction */ + public async getDesiredFee(): Promise { + notImplemented() + return null + } + + /** set the fee that you would like to pay (in Ether) - this will set the gasPrice and gasLimit (based on maxFeeIncreasePercentage) + * If gasLimitOverride is provided, gasPrice will be calculated and gasLimit will be set to gasLimitOverride + * */ + public async setDesiredFee(desiredFee: string, options?: PolkadotSetDesiredFeeOptions) { + } + + /** Hash of transaction - signature must be present to determine transactionId */ + public get transactionId(): string { + notImplemented() + return null + } + + /** get the actual cost (in Ether) for executing the transaction */ + public async getActualCost(): Promise { + notImplemented() + return null + } + + /** get the estimated cost for sending the transaction */ + public async getEstimatedCost(refresh: boolean = false): Promise { + notImplemented() + return null + } + + public get maxFeeIncreasePercentage(): number { + return this._maxFeeIncreasePercentage || 0 + } + + /** The maximum percentage increase over the desiredGas */ + public set maxFeeIncreasePercentage(percentage: number) { + notImplemented() + } + + /** throws if required fee properties aren't set */ + private assertHasFeeSetting(): void { + notImplemented() + } + + /** Get the execution priority for the transaction - higher value attaches more fees */ + public get executionPriority(): TxExecutionPriority { + notImplemented() + return null + } + + public set executionPriority(value: TxExecutionPriority) { + notImplemented() + } + + // Authorizations + + /** Returns address specified by actions[].from property + * throws if actions[].from is not a valid address - needed to determine the required signature */ + public get requiredAuthorizations(): PolkadotAddress[] { + notImplemented() + return null + } + + /** Return the one signature address required */ + private get requiredAuthorization(): PolkadotAddress { + notImplemented() + return null + } + + /** set transaction hash to sign */ + public get signBuffer(): Buffer { + notImplemented() + return null + } + + /** Sign the transaction body with private key and add to attached signatures */ + public async sign(privateKeys: PolkadotPrivateKey[]): Promise { + notImplemented() + return null + } + + // send + + /** Broadcast a signed transaction to the chain + * waitForConfirm specifies whether to wait for a transaction to appear in a block before returning */ + public async send( + waitForConfirm: ConfirmType = ConfirmType.None, + communicationSettings?: PolkadotChainSettingsCommunicationSettings, + ): Promise { + } + + // helpers + + /** Throws if not yet connected to chain - via chain.connect() */ + private assertIsConnected(): void { + notImplemented() + } + + /** Throws if not validated */ + private assertIsValidated(): void { + notImplemented() + } + + /** Whether action.from (if present) is a valid ethereum address - also checks that from is provided if data was */ + private assertFromIsValid(): void { + notImplemented() + } + + /** Throws if an action isn't attached to this transaction */ + private assertHasAction() { + notImplemented() + } + + /** Throws if no raw transaction body */ + private assertHasRaw(): void { + notImplemented() + } + + private isFromAValidAddressOrEmpty(): boolean { + notImplemented() + return false + } + + /** Whether the from address is null or empty */ + private isFromEmptyOrNullAddress(): boolean { + notImplemented() + return false + } + + getOptionsForEthereumJsTx() { + notImplemented() + return '' + } + + /** JSON representation of transaction data */ + public toJson(): any { + return { header: this.header, actions: this.actions, raw: this.raw, signatures: this.signatures } + } +} From b79ff037c2d3b6e5a096005cb096247c395637a9 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Tue, 9 Feb 2021 00:26:45 +0900 Subject: [PATCH 03/31] verify signature --- src/chains/polkadot_1/polkadotCrypto.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index 25a66e7d..31a3211c 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -4,12 +4,15 @@ import { PolkadotKeypair, PolkadotKeypairType } from "./models" +import { u8aToHex } from '@polkadot/util' import { mnemonicGenerate, mnemonicToMiniSecret, + signatureVerify, } from '@polkadot/util-crypto' import { encodeAddress, + decodeAddress, naclKeypairFromSeed as naclFromSeed, schnorrkelKeypairFromSeed as schnorrkelFromSeed, secp256k1KeypairFromSeed as secp256k1FromSeed, @@ -35,4 +38,11 @@ export function generateNewKeyPair(type: PolkadotKeypairType): PolkadotKeypair { const seed = mnemonicToMiniSecret(mnemonic) const keyPair = keypairFromSeed[type](seed) return keyPair +} + +export function verifySignatureWithAddress(signedMessage: string, signature: string, address: PolkadotPublicKey): boolean { + const publicKey = decodeAddress(address); + const hexPublicKey = u8aToHex(publicKey); + + return signatureVerify(signedMessage, signature, hexPublicKey).isValid; } \ No newline at end of file From ed166768d784c2f668f35a842bc6de1028167a48 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Wed, 10 Feb 2021 02:43:46 +0900 Subject: [PATCH 04/31] add fetchbalance to the chainpolkadotv1 --- src/chains/polkadot_1/ChainPolkadotV1.ts | 5 ++--- src/chains/polkadot_1/polkadotChainState.ts | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts index 0ce612f0..7850d3c3 100644 --- a/src/chains/polkadot_1/ChainPolkadotV1.ts +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -40,7 +40,7 @@ import { toEthereumPrivateKey, toEthereumSignature, } from '../ethereum_1/helpers' -import { PolkadotPublicKey } from './models/cryptoModels' +import { PolkadotPublicKey } from './models' import { SignedBlock } from '@polkadot/types/interfaces/runtime' import { PolkadotCreateAccount } from './polkadotCreateAccount' @@ -107,8 +107,7 @@ class ChainPolkadotV1 implements Chain { symbol: PolkadotSymbol, tokenAddress?: PolkadotAddress, ): Promise<{ balance: string }> { - notImplemented() - return null + return this._chainState.fetchBalance(account) } public fetchContractData = ( diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts index aed9ae48..dd0bf815 100644 --- a/src/chains/polkadot_1/polkadotChainState.ts +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -15,6 +15,7 @@ import { trimTrailingChars } from '../../helpers' import { throwAndLogError, throwNewError } from '../../errors' import BigNumber from 'bignumber.js' import { ApiPromise, WsProvider } from '@polkadot/api' +import { isU8a, u8aToString } from '@polkadot/util'; export class PolkadotChainState { private _chainInfo: PolkadotChainInfo @@ -118,6 +119,12 @@ export class PolkadotChainState { } } + public async fetchBalance(address: PolkadotAddress): Promise<{ balance: string}> { + const balanceInfo = await this.getBalance(isU8a(address) ? u8aToString(address) : address) + const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) + return { balance: balance.toString() } + } + public async getBalance(address: string): Promise { try { const { data } = await this._api.query.system.account(address) From a248b029e9a0d41ad0c53e0e0c04f08916a27860 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Wed, 10 Feb 2021 02:54:50 +0900 Subject: [PATCH 05/31] add some functions to chainpolkadotv1 --- src/chains/polkadot_1/ChainPolkadotV1.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts index 7850d3c3..38bd8848 100644 --- a/src/chains/polkadot_1/ChainPolkadotV1.ts +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -43,6 +43,7 @@ import { import { PolkadotPublicKey } from './models' import { SignedBlock } from '@polkadot/types/interfaces/runtime' import { PolkadotCreateAccount } from './polkadotCreateAccount' +import { PolkadotCreateAccountOptions } from './models/accountModels' class ChainPolkadotV1 implements Chain { private _endpoints: PolkadotChainEndpoint[] @@ -70,8 +71,7 @@ class ChainPolkadotV1 implements Chain { } public get chainId(): string { - notImplemented() - return null + return this._chainState.chain } public get chainInfo(): ChainInfo { @@ -98,7 +98,6 @@ class ChainPolkadotV1 implements Chain { } public get nativeToken(): { defaultUnit: string; symbol: PolkadotSymbol; tokenAddress: any } { - notImplemented() return null } @@ -126,18 +125,22 @@ class ChainPolkadotV1 implements Chain { } private newAccount = async (address?: PolkadotAddress): Promise => { - notImplemented() - return null + this.assertIsConnected() + const account = new PolkadotAccount(this._chainState) + if (address) { + await account.load(address) + } + return account } - private newCreateAccount = (options?: PolkadotNewKeysOptions): any => { + private newCreateAccount = (options?: PolkadotCreateAccountOptions): any => { this.assertIsConnected() - return new PolkadotCreateAccount(this._chainState) + return new PolkadotCreateAccount(this._chainState, options) } private newTransaction = (options?: any): PolkadotTransaction => { - notImplemented() - return null + this.assertIsConnected() + return new PolkadotTransaction(this._chainState, options) } public new = { From aefe80a6a12c75347d5ddf4142efd9805cb62bc5 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Mon, 15 Feb 2021 22:26:22 +0900 Subject: [PATCH 06/31] complete polkadotAccount, polkadotCreateAccount class and example --- src/chains/polkadot_1/ChainPolkadotV1.ts | 10 +- src/chains/polkadot_1/examples/account.ts | 33 +++ src/chains/polkadot_1/examples/transaction.ts | 191 ++++++++++++++++++ src/chains/polkadot_1/helpers/index.ts | 2 +- src/chains/polkadot_1/models/accountModels.ts | 2 +- src/chains/polkadot_1/models/index.ts | 3 +- src/chains/polkadot_1/polkadotAccount.ts | 19 +- .../polkadot_1/polkadotCreateAccount.ts | 80 +++++--- src/chains/polkadot_1/polkadotCrypto.ts | 3 +- src/examples/multiplechains.ts | 39 ++-- 10 files changed, 324 insertions(+), 58 deletions(-) create mode 100644 src/chains/polkadot_1/examples/account.ts create mode 100644 src/chains/polkadot_1/examples/transaction.ts diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts index 38bd8848..74b0acbf 100644 --- a/src/chains/polkadot_1/ChainPolkadotV1.ts +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -62,14 +62,16 @@ class ChainPolkadotV1 implements Chain { return this._chainState?.isConnected } - public get chainType(): ChainType { - return ChainType.PolkadotV1 - } - + /** Connect to chain endpoint to verify that it is operational and to get latest block info */ public connect(): Promise { return this._chainState.connect() } + /** Returns chain type enum - resolves to chain family as a string e.g. 'polkadot' */ + public get chainType(): ChainType { + return ChainType.PolkadotV1 + } + public get chainId(): string { return this._chainState.chain } diff --git a/src/chains/polkadot_1/examples/account.ts b/src/chains/polkadot_1/examples/account.ts new file mode 100644 index 00000000..07211be3 --- /dev/null +++ b/src/chains/polkadot_1/examples/account.ts @@ -0,0 +1,33 @@ +import { ChainFactory, ChainType } from '../../../index' +import { PolkadotChainEndpoint } from '../models' + +require('dotenv').config() + +const { env } = process + +const westendEndpoints: PolkadotChainEndpoint[] = [ + { + url: 'wss://westend-rpc.polkadot.io' + } +] + +const createAccountOptions = { +} +;(async () => { + try { + const westend = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints) + await westend.connect() + const createAccount = westend.new.CreateAccount(createAccountOptions) + await createAccount.generateKeysIfNeeded() + console.log('generatedKeys:', createAccount.generatedKeys) + console.log('address:', createAccount.accountName) + const account = await westend.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') + console.log('account', account) + // const { password, salt } = createAccountOptions.newKeysOptions + // const decryptedPrivateKey = ropsten.decryptWithPassword(createAccount.generatedKeys.privateKey, password, { salt }) + // console.log('decrypted privateKey: ', decryptedPrivateKey) + } catch (error) { + console.log(error) + } +})() +process.exit() \ No newline at end of file diff --git a/src/chains/polkadot_1/examples/transaction.ts b/src/chains/polkadot_1/examples/transaction.ts new file mode 100644 index 00000000..cbfbbcee --- /dev/null +++ b/src/chains/polkadot_1/examples/transaction.ts @@ -0,0 +1,191 @@ +/* eslint-disable max-len */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-console */ +import { ChainFactory, ChainType, } from '../../../index' +import { + PolkadotChainEndpoint, PolkadotChainSettings, +} from '../models' +require('dotenv').config() + +const { env } = process +;(async () => { + try { + const westendEndpoints: PolkadotChainEndpoint[] = [ + { + url: 'wss://westend-rpc.polkadot.io' + } + ] + + const westendChainOptions: PolkadotChainSettings = {} + + const ropsten = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints, westendChainOptions) + await ropsten.connect() + + // const ropstenEndpoints: EthereumChainEndpoint[] = [ + // { + // url: 'https://ropsten.infura.io/v3/fc379c787fde4363b91a61a345e3620a', + // // Web3 HttpProvider options - https://github.com/ethereum/web3.js/tree/1.x/packages/web3-providers-http#usage + // // options: { + // // timeout: 20000, + // // headers: [{ header_name: 'header-value' }], + // // }, + // }, + // ] + + // const ropstenChainOptions: EthereumChainSettings = { + // chainForkType: { + // chainName: 'ropsten', + // hardFork: 'istanbul', + // }, + // defaultTransactionSettings: { + // maxFeeIncreasePercentage: 20, + // executionPriority: TxExecutionPriority.Fast, + // }, + // } + + // EthereumRawTransaction type input for setFromRaw() + // Defaults all optional properties, so you can set from raw just with to & value OR data + const sampleSetFromRawTrx = { + to: '5CrMhng7eMrJqSQrnw4s7By1hspxxKiAjGifTmLtieAzc3U3', + value: '0x01', + data: '0x00', + } + + // const composeValueTransferParams: ValueTransferParams = { + // toAccountName: toChainEntityName('0x27105356F6C1ede0e92020e6225E46DC1F496b81'), + // amount: '0.000000000000000001', + // symbol: toEthereumSymbol(EthUnit.Ether), + // } + + // const composeEthTransferParams: EthTransferParams = { + // to: toChainEntityName('0x27105356F6C1ede0e92020e6225E46DC1F496b81'), + // value: '0.000000000000000001', + // } + + // const composeTokenTransferParams: TokenTransferParams = { + // contractName: toChainEntityName('0x04825941Ad80A6a869e85606b29c9D25144E91e6'), + // toAccountName: toChainEntityName('0x27105356F6C1ede0e92020e6225E46DC1F496b81'), + // symbol: toEthereumSymbol(EthUnit.Wei), + // // precision: 18, // precision should be provided if possible + // amount: '20.000000000000000000', // if precision isn't specified, token precision is infered from the number of digits after the decimal place + // } + + // const composeERC20TransferParams: Erc20TransferParams = { + // contractAddress: '0x27105356f6c1ede0e92020e6225e46dc1f496b81', + // from: '0x27105356F6C1ede0e92020e6225E46DC1F496b81', + // to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', + // precision: 18, // precision should be provided if possible + // value: '20', + // } + + // const composeERC20IssueParams: Erc20IssueParams = { + // contractAddress: '0x27105356f6c1ede0e92020e6225e46dc1f496b81', + // precision: 18, // precision should be provided if possible + // value: '20', + // } + + // const composeERC721TransferFromParams: Erc721TransferFromParams = { + // contractAddress: '0x27105356f6c1ede0e92020e6225e46dc1f496b81', + // transferFrom: '0x27105356F6C1ede0e92020e6225E46DC1F496b81', + // to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55', + // tokenId: 1, + // } + + // const defaultEthTxOptions: EthereumTransactionOptions = { + // chain: 'ropsten', + // hardfork: 'istanbul', + // } + + // // ---> Sign and send ethereum transfer with compose Action - using generic (cross-chain) native chain transfer action + // const transaction = ropsten.new.Transaction(defaultEthTxOptions) + // transaction.actions = [await ropsten.composeAction(ChainActionType.ValueTransfer, composeValueTransferParams)] + // console.log('transaction.actions[0]:', JSON.stringify(transaction.actions[0])) + // const decomposed = await ropsten.decomposeAction(transaction.actions[0]) + // console.log(JSON.stringify(decomposed)) + // const fee = await transaction.getSuggestedFee(TxExecutionPriority.Fast) + // await transaction.setDesiredFee(fee) + // await transaction.prepareToBeSigned() + // await transaction.validate() + // await transaction.sign([toEthereumPrivateKey(env.ROPSTEN_erc20acc_PRIVATE_KEY)]) + // console.log('raw transaction: ', transaction.raw) + // console.log('missing signatures: ', transaction.missingSignatures) + // console.log('transaction ID: ', transaction.transactionId) + // console.log('send response:', JSON.stringify(await transaction.send(ConfirmType.None))) + // console.log('send response:', JSON.stringify(await transaction.send(ConfirmType.After001))) // wait for transaction to complete on-chain before returning + // console.log(`actual cost of tx in ETH - available once tx is processed: ${await transaction.getActualCost()}`) // getActualCost will throw if called before tx is commited to chain + + // ---> Sign and send default transfer Transaction - using generic (cross-chain) token transfer action + // const transaction = ropsten.new.Transaction(defaultEthTxOptions) + // transaction.actions = [await ropsten.composeAction(ChainActionType.TokenTransfer, composeTokenTransferParams)] + // console.log(transaction.actions[0]) + // const decomposed = await ropsten.decomposeAction(transaction.actions[0]) + // console.log(decomposed) + // console.log( + // 'token value converted back using precision:', + // fromTokenValueString(decomposed[0]?.args?.amount, 10, composeTokenTransferParams?.precision), + // ) + // await transaction.prepareToBeSigned() + // await transaction.validate() + // await transaction.sign([toEthereumPrivateKey(env.ROPSTEN_erc20acc_PRIVATE_KEY)]) + // console.log('missing signatures: ', transaction.missingSignatures) + // console.log('send response:', JSON.stringify(await transaction.send())) + + // ---> Sign and send erc20 transfer Transaction + // const transaction = ropsten.new.Transaction(ropstenChainOptions) + // transaction.actions = [ await ropsten.composeAction(EthereumChainActionType.ERC20Transfer, composeERC20TransferParams) ] + // console.log(transaction.actions[0]) + // const decomposed = await ropsten.decomposeAction(transaction.actions[0]) + // console.log(decomposed) + // console.log( + // 'token value converted back using precision:', + // fromTokenValueString(decomposed[0]?.args?.amount, 10, composeERC20TransferParams?.precision), + // ) + // // const fee = await transaction.getSuggestedFee(TxExecutionPriority.Fast) + // // await transaction.setDesiredFee(fee) + // await transaction.prepareToBeSigned() + // await transaction.validate() + // await transaction.sign([toEthereumPrivateKey(env.ROPSTEN_erc20acc_PRIVATE_KEY)]) + // console.log('missing signatures: ', transaction.missingSignatures) + // console.log('send response:', JSON.stringify(await transaction.send())) + + // ---> Sign and send erc20 issue Transaction + // const transaction = ropsten.new.Transaction(defaultEthTxOptions) + // transaction.actions = [await ropsten.composeAction(EthereumChainActionType.ERC20Issue, composeERC20IssueParams)] + // console.log(transaction.actions[0]) + // const decomposed = await ropsten.decomposeAction(transaction.actions[0]) + // console.log(decomposed) + // await transaction.prepareToBeSigned() + // await transaction.validate() + // await transaction.sign([toEthereumPrivateKey(env.ROPSTEN_erc20acc_PRIVATE_KEY)]) + // console.log('missing signatures: ', transaction.missingSignatures) + // console.log('send response:', JSON.stringify(await transaction.send())) + + // // ---> Sign and send ethereum transfer with setFromRaw() + // const transaction = ropsten.new.Transaction(defaultEthTxOptions) + // await transaction.setFromRaw(sampleSetFromRawTrx) + // await transaction.prepareToBeSigned() + // const decomposed = await ropsten.decomposeAction(transaction.actions[0]) + // console.log('decomposd action:', decomposed) + // await transaction.validate() + // await transaction.sign([toEthereumPrivateKey(env.ROPSTEN_erc20acc_PRIVATE_KEY)]) + // console.log('raw transaction: ', JSON.stringify(transaction.raw)) + // console.log('missing signatures: ', transaction.missingSignatures) + // console.log('send response:', JSON.stringify(await transaction.send())) + + // ---> Compose & Decompose erc721 transferFrom Transaction + // const transaction = ropsten.new.Transaction(defaultEthTxOptions) + // transaction.actions = [ + // await ropsten.composeAction(EthereumChainActionType.ERC721TransferFrom, composeERC721TransferFromParams), + // ] + // console.log(transaction.actions[0]) + // const decomposed = await ropsten.decomposeAction(transaction.actions[0]) + // console.log(decomposed) + // await transaction.prepareToBeSigned() + // await transaction.validate() + } catch (error) { + console.log(error) + } + process.exit() +})() diff --git a/src/chains/polkadot_1/helpers/index.ts b/src/chains/polkadot_1/helpers/index.ts index 3f07f9b1..23ad598e 100644 --- a/src/chains/polkadot_1/helpers/index.ts +++ b/src/chains/polkadot_1/helpers/index.ts @@ -1,4 +1,4 @@ -import { PolkadotPublicKey } from "../models/cryptoModels"; +import { PolkadotPublicKey } from "../models"; import { isNullOrEmpty } from "../../../helpers"; import { PolkadotAddress } from "../models"; import { decodeAddress, encodeAddress } from '@polkadot/keyring' diff --git a/src/chains/polkadot_1/models/accountModels.ts b/src/chains/polkadot_1/models/accountModels.ts index 1def1d43..41569f9b 100644 --- a/src/chains/polkadot_1/models/accountModels.ts +++ b/src/chains/polkadot_1/models/accountModels.ts @@ -5,7 +5,7 @@ import { export type PolkadotCreateAccountOptions = { publicKey?: PolkadotPublicKey - newKeysOptions: PolkadotNewKeysOptions + newKeysOptions?: PolkadotNewKeysOptions } /** Type of account to create */ diff --git a/src/chains/polkadot_1/models/index.ts b/src/chains/polkadot_1/models/index.ts index 0b9274de..0593beea 100644 --- a/src/chains/polkadot_1/models/index.ts +++ b/src/chains/polkadot_1/models/index.ts @@ -1 +1,2 @@ -export * from './generalModels' \ No newline at end of file +export * from './generalModels' +export * from './cryptoModels' \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotAccount.ts b/src/chains/polkadot_1/polkadotAccount.ts index 777cee8d..b4638af2 100644 --- a/src/chains/polkadot_1/polkadotAccount.ts +++ b/src/chains/polkadot_1/polkadotAccount.ts @@ -1,10 +1,10 @@ import { notSupported } from '../../helpers' +import { throwNewError } from '../../errors' import { Account } from '../../interfaces' -import { PolkadotAddress, PolkadotPublicKey } from './models' import { PolkadotChainState } from './polkadotChainState' -import { throwNewError } from '../../errors' -import { isValidPolkadotAddress, isValidPolkadotPublicKey } from './helpers' +import { PolkadotAddress, PolkadotPublicKey } from './models' import { getPolkadotAddressFromPublicKey } from './polkadotCrypto' +import { isValidPolkadotAddress, isValidPolkadotPublicKey } from './helpers' export class PolkadotAccount implements Account { private _address: PolkadotAddress @@ -17,48 +17,61 @@ export class PolkadotAccount implements Account { this._chainState = chainState } + /** Whether the account is currently unused and can be reused - not supported in Polkadot */ get canBeRecycled(): boolean { return notSupported('PolkadotAccount.canBeRecycled') } + /** Polkadot address */ get name(): any { this.assertHasAddress() return this._address } + /** Public Key(s) associated with the account */ get publicKeys(): any { this.assertHasAddress() return this._publicKey } + /** Weather the account name can be used for new account */ isValidNewAccountName = async (accountName: PolkadotAddress): Promise => { return isValidPolkadotAddress(accountName) } + /** Sets the polkadot address + * Public key can only be obtained from a transaction signed by an polkadot address + * Only polkadot address is not enough to get public key + */ load = async (address?: PolkadotAddress): Promise => { this.assertValidPolkadotAddress(address) this._address = address } + /** Sets the polkadot public key and address */ setPublicKey = async (publicKey: PolkadotPublicKey) => { this.assertValidPolkadotPublickey(publicKey) this._publicKey = publicKey this._address = getPolkadotAddressFromPublicKey(publicKey) } + /** Polkadot has no account structure/registry on the chain */ get supportsOnChainAccountRegistry(): boolean { return false } + /** Polkadot accounts cannot be recycled as the private keys cannot be replaced */ get supportsRecycling(): boolean { return false } + /** JSON representation of address */ toJson() { this.assertHasAddress() return { address: this._address } } + /** Returns the address */ get value(): PolkadotAddress { return this._address } diff --git a/src/chains/polkadot_1/polkadotCreateAccount.ts b/src/chains/polkadot_1/polkadotCreateAccount.ts index 357214d8..2e4fafff 100644 --- a/src/chains/polkadot_1/polkadotCreateAccount.ts +++ b/src/chains/polkadot_1/polkadotCreateAccount.ts @@ -1,20 +1,23 @@ -import { Keyring } from '@polkadot/api' import { PolkadotCreateAccountOptions } from './models/accountModels' import { PolkadotChainState } from './polkadotChainState' -import { generateNewAccountPhrase } from './polkadotCrypto' +import { generateNewAccountPhrase, getKeypairFromPhrase, getPolkadotAddressFromPublicKey } from './polkadotCrypto' import { isValidPolkadotPublicKey, toPolkadotEntityName } from './helpers' -import { isNullOrEmpty, notSupported } from '../../helpers' +import { notSupported } from '../../helpers' import { CreateAccount } from '../../interfaces' import { throwNewError } from '../../errors' import { PolkadotAddress, PolkadotPublicKey, PolkadotKeypairType, - PolkadotKeyringPair + PolkadotKeypair, } from './models' +/** Helper class to compose a transaction for creating a new chain account + * Handles native accounts + * Generates new account keys if not provide + */ export class PolkadotCreateAccount implements CreateAccount { - private _accountName: string + private _accountName: PolkadotAddress private _accountType: PolkadotKeypairType @@ -22,40 +25,52 @@ export class PolkadotCreateAccount implements CreateAccount { private _options: PolkadotCreateAccountOptions - private _generatedKeyringPair: PolkadotKeyringPair + private _generatedKeypair: PolkadotKeypair constructor(chainState: PolkadotChainState, options?: PolkadotCreateAccountOptions) { this._chainState = chainState this._options = options } + /** Account name for the account to be created */ public get accountName(): any { return this._accountName } + /** Account type to be created */ public get accountType(): PolkadotKeypairType { return this._accountType } + /** Prefix that represents the address on which chain */ public getSS58Format(): number { return this._chainState.chainInfo.nativeInfo.SS58 } + /** Whether the account was recycled - not supported in Polkadot */ get didRecycleAccount() { return false } + /** The keys that were generated as part of the account creation process + * IMPORTANT: Be sure to always read and store these keys after creating an account + * This is the only way to retrieve the auto-generated private keys after an account is created + */ get generatedKeys() { - if (this._generatedKeyringPair) { - return this._generatedKeyringPair + if (this._generatedKeypair) { + return this._generatedKeypair } return null } + /** Account creation options */ get options() { return this._options } + /** Polkadot account creation does not require any on chain transactions. + * Hence there is no transaction object attached to PolkadotCreateAccount class. + */ get transaction(): any { throwNewError( 'Polkadot account creation does not require any on chain transactions. You should always first check the supportsTransactionToCreateAccount property - if false, transaction is not supported/required for this chain type', @@ -63,54 +78,67 @@ export class PolkadotCreateAccount implements CreateAccount { return null } + /** Polkadot does not require the chain to execute a createAccount transaction to create the account structure on-chain */ get supportsTransactionToCreateAccount(): boolean { return false } + /** Compose a transaction to send to the chain to create a new account + * Polkadot does not require a create account transaction to be sent to the chain + */ async composeTransaction(): Promise { notSupported('CreateAccount.composeTransaction') } + /** Determine if desired account name is usable for a new account. + * Recycling is not supported on Polkadot. + */ async determineNewAccountName(accountName: PolkadotAddress): Promise { return { alreadyExists: false, newAccountName: accountName, canRecycle: false } } + /** Returns the Polkadot Address as a Polkadot Account name for the public key provided in options + * Or generates a new mnemonic(phrase)/private/public/address + * Updates generatedKeys for the newly generated name (since name/account is derived from publicKey) + */ async generateAccountName(): Promise { const accountName = await this.generateAccountNameString() return toPolkadotEntityName(accountName) } + /** Returns a string of the Polkadot Address for the public key provide in options - OR generates a new mnemonic(phrase)/private/public/address */ async generateAccountNameString(): Promise { await this.generateKeysIfNeeded() return this.accountName as string } + /** Checks create options - if publicKeys are missing, + * autogenerate the public and private key pair and add them to options + */ async generateKeysIfNeeded() { let publicKey: PolkadotPublicKey this.assertValidOptionPublicKeys() - this.assertValidOptionNewKeys() publicKey = this?._options?.publicKey if (!publicKey) { await this.generateAccountKeys() } - this._accountName = this._generatedKeyringPair.address - this._accountType = this._generatedKeyringPair.type as PolkadotKeypairType + this._accountName = getPolkadotAddressFromPublicKey(this._generatedKeypair.publicKey) + this._accountType = this._options.newKeysOptions.type } - private async generateAccountKeys() { + private async generateAccountKeys(): Promise { const { newKeysOptions } = this._options - const { derivationPath } = newKeysOptions || {} - const overrideType = 'ed25519' + const { type } = newKeysOptions || {} + const overrideType = type || 'ed25519' const overridePhrase = generateNewAccountPhrase() - const keyring = new Keyring({ ss58Format: this.getSS58Format(), type: overrideType }) - const suri = derivationPath ? overridePhrase + '//' + derivationPath : overridePhrase - - const generatedKeyPair = keyring.createFromUri(`${suri}`) - this._generatedKeyringPair = generatedKeyPair - this._options.publicKey = this._generatedKeyringPair.publicKey as PolkadotPublicKey - this._options.newKeysOptions.phrase = overridePhrase - this._options.newKeysOptions.type = overrideType + + this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) + this._options.publicKey = this._generatedKeypair.publicKey + this._options.newKeysOptions = { + phrase: overridePhrase, + type: overrideType, + } } private assertValidOptionPublicKeys() { @@ -119,12 +147,4 @@ export class PolkadotCreateAccount implements CreateAccount { throwNewError('Invalid option - provided publicKey isnt valid') } } - - private assertValidOptionNewKeys() { - const { newKeysOptions } = this._options - const { phrase } = newKeysOptions || {} - if (isNullOrEmpty(phrase)) { - throwNewError('Invalid option - you must provide a phrase to generate new keys') - } - } } \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index 31a3211c..d14b249d 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -33,8 +33,7 @@ export function generateNewAccountPhrase(): string { return mnemonic } -export function generateNewKeyPair(type: PolkadotKeypairType): PolkadotKeypair { - const mnemonic = generateNewAccountPhrase() +export function getKeypairFromPhrase(mnemonic: string, type: PolkadotKeypairType): PolkadotKeypair { const seed = mnemonicToMiniSecret(mnemonic) const keyPair = keypairFromSeed[type](seed) return keyPair diff --git a/src/examples/multiplechains.ts b/src/examples/multiplechains.ts index 9448b66a..e7fea9ff 100644 --- a/src/examples/multiplechains.ts +++ b/src/examples/multiplechains.ts @@ -31,6 +31,12 @@ const ropstenChainOptions: EthereumChainForkType = { hardFork: 'istanbul', } +const westendEndpoints: ChainEndpoint[] = [ + { + url: 'wss://westend-rpc.polkadot.io' + } +] + // Example set of options to send tokens for each chain type const chainSendTokenData = { eos: { @@ -100,6 +106,7 @@ async function runFunctionsForMultipleChains() { const chains = [ new ChainFactory().create(ChainType.EosV2, kylinEndpoints), new ChainFactory().create(ChainType.EthereumV1, ropstenEndpoints, { chainForkType: ropstenChainOptions }), + new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints), ] // for each chain, connect to its network (to make sure the endpoint is available) @@ -110,24 +117,24 @@ async function runFunctionsForMultipleChains() { ) // Send Tokens - for each chain, we'll get token option and call the generic sendToken function - await Promise.all( - chains.map(async chain => { - const {chainType} = chain - const tokenData = chainType === ChainType.EosV2 ? chainSendTokenData.eos : chainSendTokenData.ethereum - const response = await sendToken(chain, tokenData) - console.log(`---> sendToken ${chain.chainType} response:`, JSON.stringify(response)) - }), - ) + // await Promise.all( + // chains.map(async chain => { + // const {chainType} = chain + // const tokenData = chainType === ChainType.EosV2 ? chainSendTokenData.eos : chainSendTokenData.ethereum + // const response = await sendToken(chain, tokenData) + // console.log(`---> sendToken ${chain.chainType} response:`, JSON.stringify(response)) + // }), + // ) // Send 'Currency' - for each chain, sends the native currency for the chain (e.g. 'eth' for Ethereum) - await Promise.all( - chains.map(async chain => { - const {chainType} = chain - const currencyData = chainType === ChainType.EosV2 ? chainSendCurrencyData.eos : chainSendCurrencyData.ethereum - const response = await sendCurrency(chain, currencyData) - console.log(`---> sendCurrency ${chain.chainType} response:`, JSON.stringify(response)) - }), - ) + // await Promise.all( + // chains.map(async chain => { + // const {chainType} = chain + // const currencyData = chainType === ChainType.EosV2 ? chainSendCurrencyData.eos : chainSendCurrencyData.ethereum + // const response = await sendCurrency(chain, currencyData) + // console.log(`---> sendCurrency ${chain.chainType} response:`, JSON.stringify(response)) + // }), + // ) } /** Run the example code automatically */ From 0a1b5bae9ad7fda0a9c89e3d47cc4d82f45bcbc2 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Tue, 2 Mar 2021 23:07:07 +0900 Subject: [PATCH 07/31] change manifest --- src/chains/polkadot_1/examples/parachain.ts | 51 ++++++++ src/chains/polkadot_1/models/generalModels.ts | 35 ++++- src/chains/polkadot_1/polkadotChainState.ts | 121 ++++++++++++++---- 3 files changed, 176 insertions(+), 31 deletions(-) create mode 100644 src/chains/polkadot_1/examples/parachain.ts diff --git a/src/chains/polkadot_1/examples/parachain.ts b/src/chains/polkadot_1/examples/parachain.ts new file mode 100644 index 00000000..52def57b --- /dev/null +++ b/src/chains/polkadot_1/examples/parachain.ts @@ -0,0 +1,51 @@ +import { ChainFactory, ChainType } from '../../../index' +import { PolkadotChainEndpoint, PolkadotChainSettings } from '../models' + +require('dotenv').config() + +const { env } = process + +/** + * Rococo relay-chain in which parachain features are availalbe. One of Polkadot testnet + */ +const rococoEndpoints: PolkadotChainEndpoint = { + url: 'wss://rococo-rpc.polkadot.io' +}; + +/** + * One of parachains connected to Rococo network + */ +const tickEndpoints: PolkadotChainEndpoint = { + url: 'wss://tick-rpc.polkadot.io' +} + +const tickChainSettings: PolkadotChainSettings = { + relayEndpoint: rococoEndpoints, + otherParachains: [] +} + +const createAccountOptions = { +}; + +(async () => { + try { + const chain = new ChainFactory().create(ChainType.PolkadotV1, [tickEndpoints], tickChainSettings) + await chain.connect() + console.debug(chain.chainInfo) + + const createAccount = chain.new.CreateAccount(createAccountOptions) + await createAccount.generateKeysIfNeeded() + // console.log('generatedKeys:', createAccount.generatedKeys) + // console.log('address:', createAccount.accountName) + const account = await chain.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') + // console.log('account', account) + + // const { password, salt } = createAccountOptions.newKeysOptions + // const decryptedPrivateKey = ropsten.decryptWithPassword(createAccount.generatedKeys.privateKey, password, { salt }) + // console.log('decrypted privateKey: ', decryptedPrivateKey) + + process.exit() + } catch (error) { + console.log(error) + } +})() \ No newline at end of file diff --git a/src/chains/polkadot_1/models/generalModels.ts b/src/chains/polkadot_1/models/generalModels.ts index 12abee51..9a2e5261 100644 --- a/src/chains/polkadot_1/models/generalModels.ts +++ b/src/chains/polkadot_1/models/generalModels.ts @@ -7,13 +7,33 @@ import { BlockNumber, Balance, Weight, Hash, } from '@polkadot/types/interfaces' import { ChainSymbolBrand, PublicKeyBrand, PrivateKeyBrand, } from '../../../models' export type PolkadotChainEndpoint = { + /** + * The rpc endpoint url of chain (parachain | relay-chain) + */ url: string - options?: { - headers?: { [key: string]: string } - } } -export type PolkadotChainSettings = {} +export type PolkadotChainManifest = { + /** + * In case, the chain is parachain + * Identifer referring to a specific parachain. + * The relay-chain runtime guarantees that this id is unique + * for the duration of any session. + * NOTE: https://w3f.github.io/parachain-implementers-guide/types/candidate.html#para-id + * + * If the id is -1, then the chain is relay-chain. + */ + id: number + endpoint: PolkadotChainEndpoint +} + +export type PolkadotChainSettings = { + /** + * In case the chain that we connect is a relay-chain, then the relayEndpoint is useless + */ + relayEndpoint?: PolkadotChainEndpoint + otherParachains: PolkadotChainManifest[] +} export type PolkadotChainInfo = { headBlockNumber: number @@ -24,9 +44,12 @@ export type PolkadotChainInfo = { export type PolkadotNativeInfo = { chain: string - transacitonByteFee: Balance - decimals: number + name: string SS58: number + tokenDecimals: Array + tokenSymbol: Array + transacitonByteFee: number + meta: any } export type PolkadotSymbol = string & ChainSymbolBrand diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts index dd0bf815..18cbd991 100644 --- a/src/chains/polkadot_1/polkadotChainState.ts +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -14,8 +14,14 @@ import { import { trimTrailingChars } from '../../helpers' import { throwAndLogError, throwNewError } from '../../errors' import BigNumber from 'bignumber.js' -import { ApiPromise, WsProvider } from '@polkadot/api' +import { ApiPromise, WsProvider, SubmittableResult } from '@polkadot/api' import { isU8a, u8aToString } from '@polkadot/util'; +import { createSubmittable } from '@polkadot/api/submittable' +import { SubmittableExtrinsic } from '@polkadot/api/submittable/types' +import { SubmittableExtrinsics, SubmittableExtrinsicFunction } from '@polkadot/api/types' +import { TypeDef } from '@polkadot/types/types' +import { GenericCall } from '@polkadot/types'; +import { getTypeDef } from '@polkadot/types/create'; export class PolkadotChainState { private _chainInfo: PolkadotChainInfo @@ -79,11 +85,16 @@ export class PolkadotChainState { public async getChainInfo(): Promise { try { - const [headBlockTime, chain, version, lastBlockHead] = await Promise.all([ + // console.log(this.extrinsicSectionOptions()) + console.log(this.extrinsicMethodOptions("xcmHandler")) + // this.buildExtrinsics("xcmHandler", "sudoSendHrmpXcm"); + const [headBlockTime, chain, name, version, lastBlockHead, stateMeta] = await Promise.all([ this._api.query.timestamp.now(), this._api.rpc.system.chain(), + this._api.rpc.system.name(), this._api.rpc.system.version(), this._api.rpc.chain.getHeader(), + this._api.rpc.state.getMetadata() ]) this._chainInfo = { @@ -92,9 +103,12 @@ export class PolkadotChainState { version: version.toString(), nativeInfo: { chain: chain.toString(), - transacitonByteFee: this._api.consts.transactionPayment.transactionByteFee, - decimals: this._api.registry.chainDecimals[0], + name: name.toString(), + transacitonByteFee: this._api.consts.transactionPayment.transactionByteFee.toNumber(), + tokenDecimals: this._api.registry.chainDecimals, SS58: this._api.registry.chainSS58, + tokenSymbol: this._api.registry.chainTokens, + meta: JSON.parse(JSON.stringify(stateMeta.toHuman())) } } return this._chainInfo @@ -135,10 +149,9 @@ export class PolkadotChainState { } } - public async estimateTxFee(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + public async estimateTxFee(sender: PolkadotKeyringPair, extrinsics: SubmittableExtrinsic<"promise">): Promise { try { - const info = await this._api.tx.balances.transfer(receiver, amount) - .paymentInfo(sender) + const info = await extrinsics.paymentInfo(sender) const paymentInfo: PolkadotPaymentInfo = { weight: info.weight, @@ -150,33 +163,91 @@ export class PolkadotChainState { throw error } } - - public async sendTransaction(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + // public async estimateTxFee(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + // try { + // const info = await this._api.tx.balances.transfer(receiver, amount) + // .paymentInfo(sender) + + // const paymentInfo: PolkadotPaymentInfo = { + // weight: info.weight, + // partialFee: info.partialFee + // } + + // return paymentInfo + // } catch (error) { + // throw error + // } + // } + + public async sendTransaction(sender: PolkadotKeyringPair, extrinsics: SubmittableExtrinsic<"promise">): Promise { try{ - const tx = await this._api.tx.balances.transfer(receiver, amount) - const paymentInfo = await tx.paymentInfo(sender) - const fee = paymentInfo.partialFee.toString() - const balanceInfo = await this.getBalance(sender.address) - const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) - - const bnFee = new BigNumber(fee, this._chainInfo.nativeInfo.decimals) - const bnAmount = new BigNumber(amount.toString(), this._chainInfo.nativeInfo.decimals) - const bnBalance = new BigNumber(balance.toString(), this._chainInfo.nativeInfo.decimals) - - if (bnFee.plus(bnAmount).isGreaterThan(bnBalance) ) { - throw new Error('Insufficient balance') - } - - const txHash = await tx.signAndSend(sender) + const txHash = await extrinsics.signAndSend(sender) return txHash } catch (error) { throw error } } + // public async sendTransaction(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + // try{ + // const tx = await this._api.tx.balances.transfer(receiver, amount) + // const paymentInfo = await tx.paymentInfo(sender) + // const fee = paymentInfo.partialFee.toString() + // const balanceInfo = await this.getBalance(sender.address) + // const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) + + // const bnFee = new BigNumber(fee, this._chainInfo.nativeInfo.tokenDecimals[0]) + // const bnAmount = new BigNumber(amount.toString(), this._chainInfo.nativeInfo.tokenDecimals[0]) + // const bnBalance = new BigNumber(balance.toString(), this._chainInfo.nativeInfo.tokenDecimals[0]) + + // if (bnFee.plus(bnAmount).isGreaterThan(bnBalance) ) { + // throw new Error('Insufficient balance') + // } + + // const txHash = await tx.signAndSend(sender) + // return txHash + // } catch (error) { + // throw error + // } + // } + + public async submitExtrinsics() { + // Submittable createSubmittable() + + } + + public buildExtrinsics(section: string, method: string) { + const sectionOptions = this.extrinsicSectionOptions() + if (!sectionOptions.filter(option => option === section).length) { + throwNewError("Not available Extrinsic Section Option") + } + const methodOptions = this.extrinsicMethodOptions(section) + if (!methodOptions.filter(option => option === method).length) { + throwNewError("Not available Extrinsic Method Option") + } + let fn: SubmittableExtrinsicFunction<"promise"> = this._api.tx[section][method] + console.log( this.getParams(fn) ) + } + + public extrinsicSectionOptions(): string[] { + return Object.keys(this._api.tx).sort().filter((name): number => Object.keys(this._api.tx[name]).length).map((name): string => name) + } + + public extrinsicMethodOptions(sectionName: string): string[] { + const section = this._api.tx[sectionName]; + return Object.keys(section).sort().map((name): string => name) + } + + public getParams({meta}: SubmittableExtrinsicFunction<"promise">): {name: string; type: TypeDef} [] { + return GenericCall.filterOrigin(meta).map((arg): {name: string; type: TypeDef} => ({ + name: arg.name.toString(), + type: getTypeDef(arg.type.toString()) + })) + } + public assertIsConnected(): void { if (!this._isConnected) { throwNewError('Not connected to chain') - } + } } } \ No newline at end of file From 260e15864e2f2b4f35ecb30faba07c1c5c93cd39 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Tue, 2 Mar 2021 08:34:10 -0800 Subject: [PATCH 08/31] algo - transaction options now supports expireSeconds --- src/chains/algorand_1/algoAction.ts | 19 ++++++++++++++++--- src/chains/algorand_1/algoConstants.ts | 4 +++- src/chains/algorand_1/algoTransaction.ts | 18 ++++++++++++++++-- .../algorand_1/models/transactionModels.ts | 2 ++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/chains/algorand_1/algoAction.ts b/src/chains/algorand_1/algoAction.ts index f3ed2877..80437160 100644 --- a/src/chains/algorand_1/algoAction.ts +++ b/src/chains/algorand_1/algoAction.ts @@ -2,13 +2,18 @@ import * as algosdk from 'algosdk' import { isNullOrEmpty, toBuffer, bufferToString, isAUint8Array, isAString, isAUint8ArrayArray } from '../../helpers' import { throwNewError } from '../../errors' import { + AlgorandTransactionOptions, AlgorandTxAction, AlgorandTxActionRaw, AlgorandTxActionSdkEncoded, AlgorandTxActionSdkEncodedFields, } from './models/transactionModels' import { AlgorandTxHeaderParams, AlgorandChainTransactionParamsStruct } from './models' -import { ALGORAND_TRX_COMFIRMATION_ROUNDS, ALGORAND_EMPTY_CONTRACT_NAME } from './algoConstants' +import { + ALGORAND_CHAIN_BLOCK_FREQUENCY, + ALGORAND_DEFAULT_TRANSACTION_VALID_BLOCKS, + ALGORAND_EMPTY_CONTRACT_NAME, +} from './algoConstants' import { toAlgorandAddressFromRaw, toRawAddressFromAlgoAddr } from './helpers' /** Helper class to ensure transaction actions properties are set correctly @@ -212,12 +217,20 @@ export class AlgorandActionHelper { /** Adds the latest transaction header fields (firstRound, etc.) from chain * Applies any that are not already provided in the action */ - applyCurrentTxHeaderParamsWhereNeeded(chainTxParams: AlgorandChainTransactionParamsStruct) { + applyCurrentTxHeaderParamsWhereNeeded( + chainTxParams: AlgorandChainTransactionParamsStruct, + transactionOptions?: AlgorandTransactionOptions, + ) { const rawAction = this.raw + // calculate last block + const numberOfBlockValidFor = transactionOptions?.expireSeconds + ? transactionOptions?.expireSeconds * ALGORAND_CHAIN_BLOCK_FREQUENCY + : ALGORAND_DEFAULT_TRANSACTION_VALID_BLOCKS + const lastValidBlock = rawAction.firstRound + numberOfBlockValidFor rawAction.genesisID = rawAction.genesisID || chainTxParams.genesisID rawAction.genesisHash = rawAction.genesisHash || toBuffer(chainTxParams.genesisHash, 'base64') rawAction.firstRound = rawAction.firstRound || chainTxParams.firstRound - rawAction.lastRound = rawAction.lastRound || rawAction.firstRound + ALGORAND_TRX_COMFIRMATION_ROUNDS + rawAction.lastRound = rawAction.lastRound || lastValidBlock rawAction.fee = rawAction.fee || chainTxParams.minFee rawAction.flatFee = true // since we're setting a fee, this will always be true - flatFee is just a hint to the AlgoSDK.Tx object which will set its own fee if this is not true } diff --git a/src/chains/algorand_1/algoConstants.ts b/src/chains/algorand_1/algoConstants.ts index c6e68ac7..3d07f104 100644 --- a/src/chains/algorand_1/algoConstants.ts +++ b/src/chains/algorand_1/algoConstants.ts @@ -9,7 +9,7 @@ export const PUBLIC_KEY_LENGTH = nacl.sign.publicKeyLength export const ALGORAND_EMPTY_CONTRACT_NAME = 'none' export const ALGORAND_POST_CONTENT_TYPE = { 'content-type': 'application/x-binary' } /** Number of rounds to wait after the first round for the transaction confirmation */ -export const ALGORAND_TRX_COMFIRMATION_ROUNDS = 1000 +export const ALGORAND_DEFAULT_TRANSACTION_VALID_BLOCKS = 1000 export const DEFAULT_TIMEOUT_FOR_TRX_CONFIRM = 500 export const DEFAULT_ALGO_UNIT = AlgorandUnit.Microalgo @@ -17,6 +17,8 @@ export const DEFAULT_ALGO_UNIT = AlgorandUnit.Microalgo export const DEFAULT_BLOCKS_TO_CHECK = 20 /** time to wait (in ms) between checking chain for a new block (to see if transaction appears within it) */ export const DEFAULT_CHECK_INTERVAL = 500 +/** time in seconds between blocks */ +export const ALGORAND_CHAIN_BLOCK_FREQUENCY = 2 /** number of times to attempt to read a chain endpoint before failing the read */ export const DEFAULT_GET_BLOCK_ATTEMPTS = 10 diff --git a/src/chains/algorand_1/algoTransaction.ts b/src/chains/algorand_1/algoTransaction.ts index e2775992..53a0cdcb 100644 --- a/src/chains/algorand_1/algoTransaction.ts +++ b/src/chains/algorand_1/algoTransaction.ts @@ -8,6 +8,7 @@ import { throwNewError } from '../../errors' import { byteArrayToHexString, hexStringToByteArray, + isANumber, isArrayLengthOne, isNullOrEmpty, notImplemented, @@ -79,7 +80,7 @@ export class AlgorandTransaction implements Transaction { constructor(chainState: AlgorandChainState, options?: AlgorandTransactionOptions) { this._chainState = chainState this.assertValidOptions(options) - this._options = options || {} + this.applyOptions(options) } /** Chain-specific values included in the transaction sent to the chain */ @@ -188,7 +189,7 @@ export class AlgorandTransaction implements Transaction { this._algoSdkTransaction = null } else { const chainTxHeaderParams = this._chainState.chainInfo.nativeInfo.transactionHeaderParams - this._actionHelper.applyCurrentTxHeaderParamsWhereNeeded(chainTxHeaderParams) + this._actionHelper.applyCurrentTxHeaderParamsWhereNeeded(chainTxHeaderParams, this.options) this._algoSdkTransaction = new AlgoTransactionClass(this._actionHelper.actionEncodedForSdk) } } @@ -579,6 +580,9 @@ export class AlgorandTransaction implements Transaction { /** Throws if from is not null or empty algorand argument */ private assertValidOptions(options: AlgorandTransactionOptions): void { + if (options?.expireSeconds && !isANumber(options.expireSeconds)) { + throwNewError('Invalid transaction options: ExpireSeconds is not a number') + } if (options?.multiSigOptions && options?.signerPublicKey) { throwNewError( 'Invalid transaction options: Provide multiSigOptions OR signerPublicKey - not both. The signerPublicKey is for non-multisig transasctions only', @@ -586,6 +590,16 @@ export class AlgorandTransaction implements Transaction { } } + /** apply options and/or use defaults */ + private applyOptions(options: AlgorandTransactionOptions) { + const { multiSigOptions, signerPublicKey } = options || {} + let { expireSeconds, fee, flatFee } = options || {} + expireSeconds = expireSeconds ?? this._chainState?.chainSettings?.defaultTransactionSettings?.expireSeconds + fee = fee ?? this._chainState?.chainSettings?.defaultTransactionSettings?.fee + flatFee = flatFee ?? this._chainState?.chainSettings?.defaultTransactionSettings?.flatFee + this._options = { expireSeconds, fee, flatFee, multiSigOptions, signerPublicKey } + } + /** Whether the transaction signature is valid for this transaction body and publicKey provided */ private isValidTxSignatureForPublicKey(signature: AlgorandSignature, publicKey: AlgorandPublicKey): boolean { if (!this.rawTransaction) return false diff --git a/src/chains/algorand_1/models/transactionModels.ts b/src/chains/algorand_1/models/transactionModels.ts index 3b6549e6..79c012a2 100644 --- a/src/chains/algorand_1/models/transactionModels.ts +++ b/src/chains/algorand_1/models/transactionModels.ts @@ -56,6 +56,8 @@ export type AlgorandSuggestedParams = { /** Transaction 'header' options set to chain along with the content type */ export type AlgorandTransactionOptions = { + /** Number of seconds after which transaction expires - must be submitted to the chain before then */ + expireSeconds?: number fee?: AlgorandValue flatFee?: boolean multiSigOptions?: AlgorandMultiSigOptions From 3ce0a64f42155ceafb5f0bec89aa704e1476b3cb Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Tue, 2 Mar 2021 13:47:49 -0800 Subject: [PATCH 09/31] WIP - polkadot --- package.json | 2 + parachains | 50 ++++ src/chains/ethereum_1/models/cryptoModels.ts | 2 +- src/chains/polkadot_1/ChainPolkadotV1 copy.ts | 214 +++++++++++++ src/chains/polkadot_1/models/cryptoModels.ts | 42 ++- src/chains/polkadot_1/models/generalModels.ts | 99 +++--- .../polkadot_1/models/polkadotStructures.ts | 8 +- .../polkadot_1/polkadotCreateAccount.ts | 27 +- src/chains/polkadot_1/polkadotCrypto.ts | 282 ++++++++++++++++-- src/examples/multiplechains.ts | 50 +++- 10 files changed, 659 insertions(+), 117 deletions(-) create mode 100644 parachains create mode 100644 src/chains/polkadot_1/ChainPolkadotV1 copy.ts diff --git a/package.json b/package.json index 41d9f5c0..215b6d0d 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ "dependencies": { "@aikon/sjcl": "^0.1.8", "@polkadot/api": "^3.6.4", + "@polkadot/util": "^5.9.2", + "@polkadot/util-crypto": "^5.9.2", "@types/bignumber.js": "^5.0.0", "@types/bn.js": "^4.11.6", "@types/bs58": "^4.0.1", diff --git a/parachains b/parachains new file mode 100644 index 00000000..66966940 --- /dev/null +++ b/parachains @@ -0,0 +1,50 @@ + + + +ParachainA Manifest +- DPOS +- curve type +- Account palate Type 1 +- Currency ABC +- Fee palate type 1 + +ParachainB +- POW +- curve type +- Account palate Type 2 +- Currency XYZ +- No Fees + +ParachainC +- POW +- curve type +- No Accounts +- No Currency + + +Developer Code + +- CreateAccount on chain +- Transfer Currency on chain +- ... +- compose transaction type X on chain +- sign transaction +- send transaction to chain +- wait for transaction confirmation + +chainJS != Poladot chain standard (parachains are diff) + + +para + +new ChainFactory(PolkaDotV1, options: {parachainManifest}) + + + +Developer App - Game + +(TX GAME123) -> Parachain X - general purpose contract chain (game contracts go here) + (tx 123) -> Parachain A - Marketplace for NFTs + (tx 456) -> Parachain B - Bridge to ETH (send 721) + Parachain C - service XYC + diff --git a/src/chains/ethereum_1/models/cryptoModels.ts b/src/chains/ethereum_1/models/cryptoModels.ts index 02cb38fb..b2c4e4f5 100644 --- a/src/chains/ethereum_1/models/cryptoModels.ts +++ b/src/chains/ethereum_1/models/cryptoModels.ts @@ -16,7 +16,7 @@ export type EthereumPublicKey = string & PublicKeyBrand /** a signature string - formatted correcly for ethereum */ export type EthereumSignature = ECDSASignature & SignatureBrand -/** key pair - in the format returned from algosdk */ +/** key pair - in the format returned from ethereum */ export type EthereumKeyPair = { publicKey: EthereumPublicKey privateKey: EthereumPrivateKey diff --git a/src/chains/polkadot_1/ChainPolkadotV1 copy.ts b/src/chains/polkadot_1/ChainPolkadotV1 copy.ts new file mode 100644 index 00000000..74b0acbf --- /dev/null +++ b/src/chains/polkadot_1/ChainPolkadotV1 copy.ts @@ -0,0 +1,214 @@ +import { + ChainActionType, + ChainInfo, + ChainType, + CryptoCurve, + ChainEntityName, + ChainDate, +} from '../../models' +import { + ChainError, +} from '../../errors' +import { Chain } from '../../interfaces' +import { + PolkadotChainEndpoint, + PolkadotChainSettings, + PolkadotNewKeysOptions, + PolkadotSymbol, + PolkadotAddress, +} from './models' +import { PolkadotChainState } from './polkadotChainState' +import { notImplemented } from '../../helpers' +import { PolkadotChainActionType } from './models/chainActionType' +import { + + PolkadotTransactionAction, +} from './models/transactionModels' +import { PolkadotDecomposeReturn } from './models/PolkadotStructures' +import { PolkadotAccount } from './polkadotAccount' +import { PolkadotTransaction } from './polkadotTransaction' + +import * as ethcrypto from '../ethereum_1/ethCrypto' +import { Asymmetric } from '../../crypto' +import { + isValidEthereumPrivateKey, + isValidEthereumPublicKey, + isValidEthereumDateString, + toEthereumEntityName, + toEthereumDate, + toEthereumPublicKey, + toEthereumPrivateKey, + toEthereumSignature, +} from '../ethereum_1/helpers' +import { PolkadotPublicKey } from './models' +import { SignedBlock } from '@polkadot/types/interfaces/runtime' +import { PolkadotCreateAccount } from './polkadotCreateAccount' +import { PolkadotCreateAccountOptions } from './models/accountModels' + +class ChainPolkadotV1 implements Chain { + private _endpoints: PolkadotChainEndpoint[] + + private _settings: PolkadotChainSettings + + private _chainState: PolkadotChainState + + constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { + this._endpoints = endpoints + this._settings = settings + this._chainState = new PolkadotChainState(endpoints, settings) + } + + public get isConnected(): boolean { + return this._chainState?.isConnected + } + + /** Connect to chain endpoint to verify that it is operational and to get latest block info */ + public connect(): Promise { + return this._chainState.connect() + } + + /** Returns chain type enum - resolves to chain family as a string e.g. 'polkadot' */ + public get chainType(): ChainType { + return ChainType.PolkadotV1 + } + + public get chainId(): string { + return this._chainState.chain + } + + public get chainInfo(): ChainInfo { + return this._chainState.chainInfo + } + + public composeAction = async ( + actionType: ChainActionType | PolkadotChainActionType, + args: any, + ): Promise => { + notImplemented() + return null + } + + public decomposeAction = async ( + action: PolkadotTransactionAction + ): Promise => { + notImplemented() + return null + } + + public get description(): string { + return 'Polkadot Chain' + } + + public get nativeToken(): { defaultUnit: string; symbol: PolkadotSymbol; tokenAddress: any } { + return null + } + + public async fetchBalance( + account: PolkadotAddress, + symbol: PolkadotSymbol, + tokenAddress?: PolkadotAddress, + ): Promise<{ balance: string }> { + return this._chainState.fetchBalance(account) + } + + public fetchContractData = ( + contract: string, + table: string, + owner: string, + indexNumber: number, + lowerRow: number, + upperRow: number, + limit: number, + reverseOrder: boolean, + showPayer: boolean, + keyType: string, + ): Promise => { + return null + } + + private newAccount = async (address?: PolkadotAddress): Promise => { + this.assertIsConnected() + const account = new PolkadotAccount(this._chainState) + if (address) { + await account.load(address) + } + return account + } + + private newCreateAccount = (options?: PolkadotCreateAccountOptions): any => { + this.assertIsConnected() + return new PolkadotCreateAccount(this._chainState, options) + } + + private newTransaction = (options?: any): PolkadotTransaction => { + this.assertIsConnected() + return new PolkadotTransaction(this._chainState, options) + } + + public new = { + Account: this.newAccount, + CreateAccount: this.newCreateAccount, + Transaction: this.newTransaction, + } + + public isValidEntityName = (value: string): boolean => { + notImplemented() + return false + } + + public isValidDate = (value: string): boolean => { + notImplemented() + return false + } + + public toEntityName = (value: string): ChainEntityName => { + return toEthereumEntityName(value) as ChainEntityName + } + + public toDate = (value: string | Date ): ChainDate => { + return toEthereumDate(value) as ChainDate + } + + public setPublicKey = (publicKey: PolkadotPublicKey) => { + notImplemented() + return '' + } + + public mapChainError = (error: Error): ChainError => { + notImplemented() + return new ChainError(null, null, null, error) + } + + public assertIsConnected(): void { + if (!this._chainState?.isConnected) { + throw new Error('Not connected to chain') + } + } + + cryptoCurve: CryptoCurve.Ed25519 + + decryptWithPassword = ethcrypto.decryptWithPassword + encryptWithPassword = ethcrypto.encryptWithPassword + decryptWithPrivateKey = ethcrypto.decryptWithPrivateKey + encryptWithPublicKey = ethcrypto.encryptWithPublicKey + decryptWithPrivateKeys = ethcrypto.decryptWithPrivateKeys + encryptWithPublicKeys = ethcrypto.encryptWithPublicKeys + getPublicKeyFromSignature = ethcrypto.getEthereumPublicKeyFromSignature + generateKeyPair = ethcrypto.generateKeyPair + isSymEncryptedDataString = ethcrypto.isSymEncryptedDataString + isAsymEncryptedDataString = Asymmetric.isAsymEncryptedDataString + toAsymEncryptedDataString = Asymmetric.toAsymEncryptedDataString + toSymEncryptedDataString = ethcrypto.toSymEncryptedDataString + toPublicKey = toEthereumPublicKey + toPrivateKey = toEthereumPrivateKey + toSignature = toEthereumSignature + + sign = ethcrypto.sign + verifySignedWithPublicKey = ethcrypto.verifySignedWithPublicKey + + isValidPrivateKey = isValidEthereumPrivateKey + isValidPublicKey = isValidEthereumPublicKey + isValidEthereumDate = isValidEthereumDateString +} + +export { ChainPolkadotV1 } \ No newline at end of file diff --git a/src/chains/polkadot_1/models/cryptoModels.ts b/src/chains/polkadot_1/models/cryptoModels.ts index f510025b..8c47986e 100644 --- a/src/chains/polkadot_1/models/cryptoModels.ts +++ b/src/chains/polkadot_1/models/cryptoModels.ts @@ -1,11 +1,37 @@ -import { - SignatureBrand, -} from '../../../models' +import { PublicKeyBrand, PrivateKeyBrand, SignatureBrand } from '../../../models' -export interface ECDSASignature { - v: number - r: Buffer - s: Buffer +export enum PolkadotCurve { + Ed25519 = 'ed25519', + Sr25519 = 'sr25519', + Secp256k1 = 'secp256k1', } -export type PolkadotSignature = ECDSASignature & SignatureBrand +export enum PolkadotKeyPairType { + Ed25519 = 'ed25519', + Sr25519 = 'sr25519', + Ecdsa = 'ecdsa', + Ethereum = 'ethereum', +} + +export type PolkadotNewKeysOptions = { + phrase?: string + keyType?: PolkadotKeyPairType + derivationPath?: string +} + +/** a private key string - formatted correctly for polkadot */ +export type PolkadotPrivateKey = string & PrivateKeyBrand + +/** a public key string - formatted correctly for polkadot */ +export type PolkadotPublicKey = string & PublicKeyBrand + +/** a signature string - formatted correcly for polkadot */ +export type PolkadotSignature = string & SignatureBrand // TODO: Use Polkadot SDK to define type + +/** key pair - in the format returned from algosdk */ +export type PolkadotKeypair = { + type: PolkadotKeyPairType + publicKey: PolkadotPublicKey + privateKey: PolkadotPrivateKey + // privateKeyEncrypted?: ModelsCryptoAes.AesEncryptedDataString +} diff --git a/src/chains/polkadot_1/models/generalModels.ts b/src/chains/polkadot_1/models/generalModels.ts index 9a2e5261..7224a7b9 100644 --- a/src/chains/polkadot_1/models/generalModels.ts +++ b/src/chains/polkadot_1/models/generalModels.ts @@ -1,66 +1,66 @@ import BN from 'bn.js' -import { AnyJson, AnyNumber, } from '@polkadot/types/types' +import { AnyJson, AnyNumber } from '@polkadot/types/types' import { AccountData } from '@polkadot/types/interfaces/balances' import { Compact } from '@polkadot/types' import { KeyringPair } from '@polkadot/keyring/types' -import { BlockNumber, Balance, Weight, Hash, } from '@polkadot/types/interfaces' -import { ChainSymbolBrand, PublicKeyBrand, PrivateKeyBrand, } from '../../../models' +import { BlockNumber, Balance, Weight, Hash } from '@polkadot/types/interfaces' +import { ChainSymbolBrand } from '../../../models' export type PolkadotChainEndpoint = { - /** - * The rpc endpoint url of chain (parachain | relay-chain) - */ - url: string + /** + * The rpc endpoint url of chain (parachain | relay-chain) + */ + url: string } export type PolkadotChainManifest = { - /** - * In case, the chain is parachain - * Identifer referring to a specific parachain. - * The relay-chain runtime guarantees that this id is unique - * for the duration of any session. - * NOTE: https://w3f.github.io/parachain-implementers-guide/types/candidate.html#para-id - * - * If the id is -1, then the chain is relay-chain. - */ - id: number - endpoint: PolkadotChainEndpoint + /** + * In case, the chain is parachain + * Identifer referring to a specific parachain. + * The relay-chain runtime guarantees that this id is unique + * for the duration of any session. + * NOTE: https://w3f.github.io/parachain-implementers-guide/types/candidate.html#para-id + * + * If the id is -1, then the chain is relay-chain. + */ + id: number + endpoint: PolkadotChainEndpoint } export type PolkadotChainSettings = { - /** - * In case the chain that we connect is a relay-chain, then the relayEndpoint is useless - */ - relayEndpoint?: PolkadotChainEndpoint - otherParachains: PolkadotChainManifest[] + /** + * In case the chain that we connect is a relay-chain, then the relayEndpoint is useless + */ + relayEndpoint?: PolkadotChainEndpoint + otherParachains: PolkadotChainManifest[] } -export type PolkadotChainInfo = { - headBlockNumber: number - headBlockTime: Date - version: string - nativeInfo: PolkadotNativeInfo +export type PolkadotChainInfo = { + headBlockNumber: number + headBlockTime: Date + version: string + nativeInfo: PolkadotNativeInfo } export type PolkadotNativeInfo = { - chain: string - name: string - SS58: number - tokenDecimals: Array - tokenSymbol: Array - transacitonByteFee: number - meta: any + chain: string + name: string + SS58: number + tokenDecimals: Array + tokenSymbol: Array + transacitonByteFee: number + meta: any } export type PolkadotSymbol = string & ChainSymbolBrand export type PolkadotChainSettingsCommunicationSettings = { - blocksToCheck: number - checkInterval: number - getBlockAttempts: number + blocksToCheck: number + checkInterval: number + getBlockAttempts: number } -export type PolkadotBlockNumber = BlockNumber | BN | BigInt | Uint8Array | number | string +export type PolkadotBlockNumber = BlockNumber | BN | BigInt | Uint8Array | number | string export type PolkadotBlock = Record @@ -71,27 +71,10 @@ export type PolkadotKeyringPair = KeyringPair export type DotAmount = Compact | AnyNumber | Uint8Array export type PolkadotPaymentInfo = { - weight: Weight - partialFee: Balance + weight: Weight + partialFee: Balance } export type PolkadotAccountBalance = AccountData export type PolkadotHash = Hash - -export type PolkadotKeypairType = 'ed25519' | 'sr25519' | 'ecdsa' - -export type PolkadotNewKeysOptions = { - phrase?: string - type?: PolkadotKeypairType - derivationPath?: string -} - -export type PolkadotKeypair = { - publicKey: PolkadotPublicKey - secretKey: PolkadotPrivateKey -} - -export type PolkadotPublicKey = Uint8Array & PublicKeyBrand - -export type PolkadotPrivateKey = Uint8Array & PrivateKeyBrand \ No newline at end of file diff --git a/src/chains/polkadot_1/models/polkadotStructures.ts b/src/chains/polkadot_1/models/polkadotStructures.ts index cc190675..b8aeb818 100644 --- a/src/chains/polkadot_1/models/polkadotStructures.ts +++ b/src/chains/polkadot_1/models/polkadotStructures.ts @@ -2,7 +2,7 @@ import { ChainActionType } from '../../../models' import { PolkadotChainActionType } from './chainActionType' export type PolkadotDecomposeReturn = { - chainActionType: ChainActionType | PolkadotChainActionType - args: any - partial: boolean -} \ No newline at end of file + chainActionType: ChainActionType | PolkadotChainActionType + args: any + partial: boolean +} diff --git a/src/chains/polkadot_1/polkadotCreateAccount.ts b/src/chains/polkadot_1/polkadotCreateAccount.ts index 2e4fafff..911b4b46 100644 --- a/src/chains/polkadot_1/polkadotCreateAccount.ts +++ b/src/chains/polkadot_1/polkadotCreateAccount.ts @@ -5,12 +5,7 @@ import { isValidPolkadotPublicKey, toPolkadotEntityName } from './helpers' import { notSupported } from '../../helpers' import { CreateAccount } from '../../interfaces' import { throwNewError } from '../../errors' -import { - PolkadotAddress, - PolkadotPublicKey, - PolkadotKeypairType, - PolkadotKeypair, -} from './models' +import { PolkadotAddress, PolkadotPublicKey, PolkadotCurve, PolkadotKeypair } from './models' /** Helper class to compose a transaction for creating a new chain account * Handles native accounts @@ -19,7 +14,7 @@ import { export class PolkadotCreateAccount implements CreateAccount { private _accountName: PolkadotAddress - private _accountType: PolkadotKeypairType + private _accountType: PolkadotCurve private _chainState: PolkadotChainState @@ -31,17 +26,17 @@ export class PolkadotCreateAccount implements CreateAccount { this._chainState = chainState this._options = options } - + /** Account name for the account to be created */ public get accountName(): any { return this._accountName } - + /** Account type to be created */ - public get accountType(): PolkadotKeypairType { + public get accountType(): PolkadotCurve { return this._accountType } - + /** Prefix that represents the address on which chain */ public getSS58Format(): number { return this._chainState.chainInfo.nativeInfo.SS58 @@ -124,20 +119,20 @@ export class PolkadotCreateAccount implements CreateAccount { await this.generateAccountKeys() } this._accountName = getPolkadotAddressFromPublicKey(this._generatedKeypair.publicKey) - this._accountType = this._options.newKeysOptions.type + this._accountType = this._options.newKeysOptions.curve } private async generateAccountKeys(): Promise { const { newKeysOptions } = this._options - const { type } = newKeysOptions || {} + const { curve: type } = newKeysOptions || {} const overrideType = type || 'ed25519' const overridePhrase = generateNewAccountPhrase() - + this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) this._options.publicKey = this._generatedKeypair.publicKey this._options.newKeysOptions = { phrase: overridePhrase, - type: overrideType, + curve: overrideType, } } @@ -147,4 +142,4 @@ export class PolkadotCreateAccount implements CreateAccount { throwNewError('Invalid option - provided publicKey isnt valid') } } -} \ No newline at end of file +} diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index d14b249d..ada03b86 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -1,27 +1,40 @@ -import { - PolkadotAddress, - PolkadotPublicKey, - PolkadotKeypair, - PolkadotKeypairType -} from "./models" import { u8aToHex } from '@polkadot/util' -import { - mnemonicGenerate, - mnemonicToMiniSecret, - signatureVerify, -} from '@polkadot/util-crypto' -import { - encodeAddress, - decodeAddress, - naclKeypairFromSeed as naclFromSeed, - schnorrkelKeypairFromSeed as schnorrkelFromSeed, - secp256k1KeypairFromSeed as secp256k1FromSeed, +import { + encodeAddress, + decodeAddress, + mnemonicGenerate, + mnemonicToMiniSecret, + naclKeypairFromSeed, + schnorrkelKeypairFromSeed, + secp256k1KeypairFromSeed, + signatureVerify, } from '@polkadot/util-crypto' +import { Keypair, Seedpair, KeypairType } from '@polkadot/util-crypto/types' +import secp256k1 from 'secp256k1' +import { + PolkadotAddress, + PolkadotCurve, + PolkadotKeypair, + PolkadotKeyPairType, + PolkadotPrivateKey, + PolkadotPublicKey, + PolkadotSignature, +} from './models' +import { AesCrypto, Asymmetric } from '../../crypto' +import { + notImplemented, + removeHexPrefix, + byteArrayToHexString, + hexStringToByteArray, + notSupported, +} from '../../helpers' +import { ensureEncryptedValueIsObject } from '../../crypto/genericCryptoHelpers' +import * as AsymmetricHelpers from '../../crypto/asymmetricHelpers' export const keypairFromSeed = { - ecdsa: (seed: Uint8Array): PolkadotKeypair => secp256k1FromSeed(seed) as PolkadotKeypair, - ed25519: (seed: Uint8Array): PolkadotKeypair => naclFromSeed(seed) as PolkadotKeypair, - sr25519: (seed: Uint8Array): PolkadotKeypair => schnorrkelFromSeed(seed) as PolkadotKeypair + ecdsa: (seed: Uint8Array): Keypair => secp256k1KeypairFromSeed(seed) as Keypair, + ed25519: (seed: Uint8Array): Keypair => naclKeypairFromSeed(seed) as Keypair, + sr25519: (seed: Uint8Array): Keypair => schnorrkelKeypairFromSeed(seed) as Keypair, } export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { @@ -33,15 +46,232 @@ export function generateNewAccountPhrase(): string { return mnemonic } -export function getKeypairFromPhrase(mnemonic: string, type: PolkadotKeypairType): PolkadotKeypair { +export function getKeypairFromPhrase(mnemonic: string, type: PolkadotCurve): PolkadotKeypair { const seed = mnemonicToMiniSecret(mnemonic) const keyPair = keypairFromSeed[type](seed) return keyPair } -export function verifySignatureWithAddress(signedMessage: string, signature: string, address: PolkadotPublicKey): boolean { - const publicKey = decodeAddress(address); - const hexPublicKey = u8aToHex(publicKey); +// export function verifySignatureWithAddress( +// signedMessage: string, +// signature: string, +// address: PolkadotPublicKey, +// ): boolean { +// const publicKey = decodeAddress(address) +// const hexPublicKey = u8aToHex(publicKey) + +// return signatureVerify(signedMessage, signature, hexPublicKey).isValid +// } + +// export function determineCurveFromAddress() {} + +// export function determineCurveFromKeyPair() { +// // itererate verify - trying each curve +// } + +const ETHEREUM_ASYMMETRIC_SCHEME_NAME = 'asym.chainjs.secp256k1.ethereum' + +// eslint-disable-next-line prefer-destructuring +export const defaultIter = AesCrypto.defaultIter +// eslint-disable-next-line prefer-destructuring +export const defaultMode = AesCrypto.defaultMode + +/** Verifies that the value is a valid, stringified JSON Encrypted object */ +export function isSymEncryptedDataString(value: string): value is AesCrypto.AesEncryptedDataString { + return AesCrypto.isAesEncryptedDataString(value) +} + +/** Ensures that the value comforms to a well-formed, stringified JSON Encrypted Object */ +export function toSymEncryptedDataString(value: any): AesCrypto.AesEncryptedDataString { + return AesCrypto.toAesEncryptedDataString(value) +} + +/** get uncompressed public key from EthereumPublicKey */ +export function uncompressPublicKey(publicKey: PolkadotPublicKey): string { + notImplemented() + // if already decompressed an not has trailing 04 + const cleanedPublicKey = removeHexPrefix(publicKey) + const testBuffer = Buffer.from(cleanedPublicKey, 'hex') + const prefixedPublicKey = testBuffer.length === 64 ? `04${cleanedPublicKey}` : cleanedPublicKey + const uncompressedPublicKey = byteArrayToHexString( + secp256k1.publicKeyConvert(hexStringToByteArray(prefixedPublicKey), false), + ) + return uncompressedPublicKey +} + +/** Decrypts the encrypted value using a password, and optional salt using AES algorithm and SHA256 hash function + * The encrypted value is either a stringified JSON object or a JSON object */ +export function decryptWithPassword( + encrypted: AesCrypto.AesEncryptedDataString | any, + password: string, + options: AesCrypto.AesEncryptionOptions, +): string { + notImplemented() + return AesCrypto.decryptWithPassword(encrypted, password, options) +} + +/** Encrypts a string using a password and optional salt */ +export function encryptWithPassword( + unencrypted: string, + password: string, + options: AesCrypto.AesEncryptionOptions, +): AesCrypto.AesEncryptedDataString { + notImplemented() + return AesCrypto.encryptWithPassword(unencrypted, password, options) +} + +/** Encrypts a string using a public key into a stringified JSON object + * The encrypted result can be decrypted with the matching private key */ +export async function encryptWithPublicKey( + unencrypted: string, + publicKey: PolkadotPublicKey, + options: Asymmetric.EciesOptions, +): Promise { + notImplemented() + const publicKeyUncompressed = uncompressPublicKey(publicKey) // should be hex string + const useOptions = { + ...options, + curveType: Asymmetric.EciesCurveType.Secp256k1, + scheme: ETHEREUM_ASYMMETRIC_SCHEME_NAME, + } + const response = Asymmetric.encryptWithPublicKey(publicKeyUncompressed, unencrypted, useOptions) + return Asymmetric.toAsymEncryptedDataString(JSON.stringify(response)) +} + +/** Decrypts the encrypted value using a private key + * The encrypted value is a stringified JSON object + * ... and must have been encrypted with the public key that matches the private ley provided */ +export async function decryptWithPrivateKey( + encrypted: Asymmetric.AsymmetricEncryptedDataString | Asymmetric.AsymmetricEncryptedData, + privateKey: PolkadotPrivateKey, + options: Asymmetric.EciesOptions, +): Promise { + notImplemented() + const useOptions = { ...options, curveType: Asymmetric.EciesCurveType.Secp256k1 } + const privateKeyHex = removeHexPrefix(privateKey) + const encryptedObject = ensureEncryptedValueIsObject(encrypted) + return Asymmetric.decryptWithPrivateKey(encryptedObject, privateKeyHex, useOptions) +} + +/** Encrypts a string using multiple assymmetric encryptions with multiple public keys - one after the other + * calls a helper function to perform the iterative wrapping + * the first parameter of the helper is a chain-specific function (in this file) to encryptWithPublicKey + * The result is stringified JSON object including an array of encryption results with the last one including the final cipertext + * Encrypts using publicKeys in the order they appear in the array */ +export async function encryptWithPublicKeys( + unencrypted: string, + publicKeys: PolkadotPublicKey[], + options?: Asymmetric.EciesOptions, +): Promise { + notImplemented() + return Asymmetric.toAsymEncryptedDataString( + await AsymmetricHelpers.encryptWithPublicKeys(encryptWithPublicKey, unencrypted, publicKeys, options), + ) +} + +/** Unwraps an object produced by encryptWithPublicKeys() - resulting in the original ecrypted string + * calls a helper function to perform the iterative unwrapping + * the first parameter of the helper is a chain-specific function (in this file) to decryptWithPrivateKey + * Decrypts using privateKeys that match the publicKeys provided in encryptWithPublicKeys() - provide the privateKeys in same order + * The result is the decrypted string */ +export async function decryptWithPrivateKeys( + encrypted: Asymmetric.AsymmetricEncryptedDataString, + privateKeys: PolkadotPublicKey[], +): Promise { + notImplemented() + return AsymmetricHelpers.decryptWithPrivateKeys(decryptWithPrivateKey, encrypted, privateKeys, {}) +} + +/** Signs data with private key */ +// export function sign(data: string | Buffer, privateKey: string): PolkadotSignature { +// notImplemented() +// // todo: data should be hashed first using ethereum-js-tx Transaction.prototype.hash +// const dataBuffer = toEthBuffer(data) +// const keyBuffer = toBuffer(privateKey, 'hex') +// return toEthereumSignature(ecsign(dataBuffer, keyBuffer)) +// } + +// /** Returns public key from ethereum signature */ +// export function getEthereumPublicKeyFromSignature( +// signature: EthereumSignature, +// data: string | Buffer, +// encoding: string, +// ): EthereumPublicKey { +// const { v, r, s } = signature +// return toEthereumPublicKey(ecrecover(toEthBuffer(data), v, r, s).toString()) +// } + +// /** Returns public key from ethereum address */ +// export function getEthereumAddressFromPublicKey(publicKey: EthereumPublicKey): EthereumAddress { +// return bufferToHex(publicToAddress(toEthBuffer(publicKey))) +// } + +/** Adds privateKeyEncrypted if missing by encrypting privateKey (using password) */ +function encryptAccountPrivateKeysIfNeeded( + keys: PolkadotKeypair, + password: string, + options: AesCrypto.AesEncryptionOptions, +): PolkadotKeypair { + // encrypt if not already encrypted + notImplemented() + const privateKeyEncrypted = keys?.privateKeyEncrypted + ? keys.privateKeyEncrypted + : encryptWithPassword(keys?.privateKey, password, options) + const encryptedKeys: PolkadotKeypair = { + privateKey: keys?.privateKey, + publicKey: keys?.publicKey, + privateKeyEncrypted, + } + return encryptedKeys +} + +/** Generates and returns a new public/private key pair */ +export async function generateKeyPair(): Promise { + notImplemented() + const wallet = Wallet.generate() + const privateKey: PolkadotPrivateKey = wallet.getPrivateKeyString() + const publicKey: PolkadotPublicKey = wallet.getPublicKeyString() + const keys: PolkadotKeypair = { privateKey, publicKey } + return keys +} - return signatureVerify(signedMessage, signature, hexPublicKey).isValid; -} \ No newline at end of file +/** Generates new public and private key pair + * Encrypts the private key using password and optional salt + */ +export async function generateNewAccountKeysAndEncryptPrivateKeys( + password: string, + overrideKeys: any, + options: AesCrypto.AesEncryptionOptions, +): Promise { + notImplemented() + const keys = await generateKeyPair() + const encryptedKeys = encryptAccountPrivateKeysIfNeeded(keys, password, options) + return encryptedKeys +} + +/** Verify that the signed data was signed using the given key (signed with the private key for the provided public key) */ +export function verifySignedWithPublicKey( + data: string | Buffer, + publicKey: PolkadotPublicKey, + signature: PolkadotSignature, +): boolean { + notImplemented() + return null +} + +/** determine crypto curve from key type */ +export function getCurveFromKeyType(keyType: PolkadotKeyPairType): PolkadotCurve { + switch (keyType) { + case PolkadotKeyPairType.Ecdsa: + return PolkadotCurve.Secp256k1 + case PolkadotKeyPairType.Ethereum: + return PolkadotCurve.Secp256k1 + case PolkadotKeyPairType.Ed25519: + return PolkadotCurve.Ed25519 + case PolkadotKeyPairType.Sr25519: + return PolkadotCurve.Sr25519 + default: + notSupported(`Keytype ${keyType}`) + return null + } +} diff --git a/src/examples/multiplechains.ts b/src/examples/multiplechains.ts index e7fea9ff..d2a1b484 100644 --- a/src/examples/multiplechains.ts +++ b/src/examples/multiplechains.ts @@ -5,7 +5,14 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-console */ import { ChainFactory, Chain } from '../index' -import { ChainActionType, ChainEndpoint, ConfirmType, ChainEntityNameBrand, ChainType } from '../models' +import { + ChainActionType, + ChainEndpoint, + ConfirmType, + ChainEntityNameBrand, + ChainType, + TxExecutionPriority, +} from '../models' import { EthereumChainForkType } from '../chains/ethereum_1/models' require('dotenv').config() @@ -33,8 +40,8 @@ const ropstenChainOptions: EthereumChainForkType = { const westendEndpoints: ChainEndpoint[] = [ { - url: 'wss://westend-rpc.polkadot.io' - } + url: 'wss://westend-rpc.polkadot.io', + }, ] // Example set of options to send tokens for each chain type @@ -83,6 +90,8 @@ const chainSendCurrencyData = { async function sendToken(chain: Chain, options: any) { const sendTokenTx = chain.new.Transaction() sendTokenTx.actions = [await chain.composeAction(ChainActionType.TokenTransfer, options.composeTokenTransferParams)] + const fee = await sendTokenTx.getSuggestedFee(TxExecutionPriority.Fast) + await sendTokenTx.setDesiredFee(fee) await sendTokenTx.prepareToBeSigned() await sendTokenTx.validate() await sendTokenTx.sign([options.privateKey]) @@ -93,7 +102,13 @@ async function sendToken(chain: Chain, options: any) { /** Send 'cryptocurrency' (value) between accounts on the chain */ async function sendCurrency(chain: Chain, options: any) { const sendCurrencyTx = chain.new.Transaction() - sendCurrencyTx.actions = [await chain.composeAction(ChainActionType.ValueTransfer, options.composeValueTransferParams)] + sendCurrencyTx.actions = [ + await chain.composeAction(ChainActionType.ValueTransfer, options.composeValueTransferParams), + ] + if (sendCurrencyTx.supportsFee) { + const fee = await sendCurrencyTx.getSuggestedFee(TxExecutionPriority.Fast) + await sendCurrencyTx.setDesiredFee(fee) + } await sendCurrencyTx.prepareToBeSigned() await sendCurrencyTx.validate() await sendCurrencyTx.sign([options.privateKey]) @@ -101,6 +116,33 @@ async function sendCurrency(chain: Chain, options: any) { return response } +/** Send 'cryptocurrency' (value) between accounts on the chain */ +async function createAccount(chain: Chain, options: any) { + // const sendCurrencyTx = chain.new.Transaction() + // sendCurrencyTx.actions = [await chain.composeAction(ChainActionType.ValueTransfer, options.composeValueTransferParams)] + // const fee = await sendCurrencyTx.getSuggestedFee(TxExecutionPriority.Fast) + // await sendCurrencyTx.setDesiredFee(fee) + // await sendCurrencyTx.prepareToBeSigned() + // await sendCurrencyTx.validate() + // await sendCurrencyTx.sign([options.privateKey]) + // const response = await sendCurrencyTx.send(ConfirmType.None) + // return response + const createAccountOptions = { + newKeysOptions: { + password: '2233', + salt: env.EOS_KYLIN_PK_SALT_V0, + }, + } + + const accountCreate = chain.new.CreateAccount(createAccountOptions) + await accountCreate.generateKeysIfNeeded() + console.log('generatedKeys:', accountCreate.generatedKeys) + console.log('address:', accountCreate.accountName) + const { password, salt } = createAccountOptions.newKeysOptions + // const decryptedPrivateKey = chain.decryptWithPassword(createAccount.generatedKeys.privateKey, password, { salt }) + // console.log('decrypted privateKey: ', decryptedPrivateKey) +} + /** Run the same functions (e.g. transfer a token) for one or more chains using the same code */ async function runFunctionsForMultipleChains() { const chains = [ From 27b6f5f07b649607ce50ea07869341ebebeed259 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 8 Mar 2021 10:19:38 -0800 Subject: [PATCH 10/31] add isHexString helper --- src/helpers.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers.ts b/src/helpers.ts index fbc6e744..bb7d011f 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -273,6 +273,11 @@ export function hasHexPrefix(value: any): boolean { return isAString(value) && (value as string).startsWith('0x') } +/** Return true if value is a hexidecimal encoded string (is prefixed by 0x) */ +export function isHexString(value: any): boolean { + return hasHexPrefix(value) +} + /** Checks that string starts with 0x - appends if not * Also converts hex chars to lowercase for consistency */ From aa06a339c7ab10140ddc87375d591564a564d873 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 8 Mar 2021 10:20:41 -0800 Subject: [PATCH 11/31] polka - reorganized cryptoModelHelpers --- .../polkadot_1/helpers/cryptoModelHelpers.ts | 154 ++++++++++++++++++ src/chains/polkadot_1/helpers/index.ts | 41 +---- 2 files changed, 158 insertions(+), 37 deletions(-) create mode 100644 src/chains/polkadot_1/helpers/cryptoModelHelpers.ts diff --git a/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts b/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts new file mode 100644 index 00000000..b1d03244 --- /dev/null +++ b/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts @@ -0,0 +1,154 @@ +import { + ensureHexPrefix, + isNullOrEmpty, + isABuffer, + isAString, + isHexString, + hexStringToByteArray, + byteArrayToHexString, + notSupported, +} from '../../../helpers' +import { PolkadotPublicKey, PolkadotPrivateKey, PolkadotSignature, PolkadotAddress, PolkadotCurve, PolkadotKeyPairType } from '../models' +import { decodeAddress, encodeAddress } from '@polkadot/keyring' +import { signatureVerify } from '@polkadot/util-crypto' +import { AesCrypto, Ed25519Crypto } from '../../../crypto' +import { throwNewError } from 'src/errors' + +/** determine crypto curve from key type */ +export function getCurveFromKeyType(keyPairType: PolkadotKeyPairType): PolkadotCurve { + switch (keyPairType) { + case PolkadotKeyPairType.Ecdsa: + return PolkadotCurve.Secp256k1 + case PolkadotKeyPairType.Ethereum: + return PolkadotCurve.Secp256k1 + case PolkadotKeyPairType.Ed25519: + return PolkadotCurve.Ed25519 + case PolkadotKeyPairType.Sr25519: + return PolkadotCurve.Sr25519 + default: + notSupported(`Keytype ${keyPairType}`) + return null + } +} + +// todo eth - this should not have copied code - is the bug worked-around? if not, we should consider using a diff library +// Reimplemented from ethereumjs-util module to workaround a current bug +/** Checks if a valid signature with ECDSASignature */ +export function isValidSignature(value: string | PolkadotSignature, keyPairType?: PolkadotKeyPairType): boolean { + // TODO + return true +} + +export function isValidPolkadotPublicKey( + value: string | PolkadotPublicKey, + keyPairType?: PolkadotKeyPairType, +): value is PolkadotPublicKey { + // TODO + return true + // if (!value) return false + // return isValidPublic(toEthBuffer(ensureHexPrefix(value))) +} + +export function isValidPolkadotPrivateKey( + value: PolkadotPrivateKey | string, + keyPairType?: PolkadotKeyPairType, +): value is PolkadotPrivateKey { + // TODO, if curve param provided, check is valid for that type of curve + // if no curve param, check each curve until valid match - or return false + // check is valid for any of the supported curves + return true + // if (!value) return false + // return isValidPrivate(toEthBuffer(ensureHexPrefix(value))) +} + +export function isValidPolkadotSignature(value: PolkadotSignature | string): value is PolkadotSignature { + // TODO + return true +} + +/** Whether value is well-formatted address */ +export function isValidPolkadotAddress(value: string | Buffer | PolkadotAddress): boolean { + // TODO: confirm this works with different curves + if (!value) return false + try { + encodeAddress(isHexString(value) ? hexStringToByteArray(value as string) : decodeAddress(value)) + } catch (error) { + return false + } + return true +} + +/** Accepts hex string checks if a valid ethereum public key + * Returns PolkadotPublicKey with prefix + */ +export function toPolkadotPublicKey(value: string): PolkadotPublicKey { + if (isValidPolkadotPublicKey(value)) { + return value as PolkadotPublicKey + } + throw new Error(`Not a valid polkadot public key:${value}.`) +} + +/** Accepts hex string checks if a valid ethereum private key + * Returns PolkadotPrivateKey with prefix + */ +export function toEthereumPrivateKey(value: string): PolkadotPrivateKey { + if (isValidPolkadotPrivateKey(value)) { + return value as PolkadotPrivateKey + } + throw new Error(`Not a valid polkadot private key:${value}.`) +} + +/** Verifies that the value is a valid, stringified JSON Encrypted object */ +export function isSymEncryptedDataString( + value: string, + keyPairType?: PolkadotKeyPairType, +): value is AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { + const curve = getCurveFromKeyType(keyPairType) + if (curve === PolkadotCurve.Secp256k1) return AesCrypto.isAesEncryptedDataString(value) + if (curve === PolkadotCurve.Ed25519) return Ed25519Crypto.isEd25519EncryptedDataString(value) + // if no curve param, check all possible options + return AesCrypto.isAesEncryptedDataString(value) || Ed25519Crypto.isEd25519EncryptedDataString(value) +} + +/** Ensures that the value comforms to a well-formed, stringified JSON Encrypted Object */ +export function toSymEncryptedDataString( + value: any, + keyPairType?: PolkadotKeyPairType, +): AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { + const curve = getCurveFromKeyType(keyPairType) + if (curve === PolkadotCurve.Secp256k1) return AesCrypto.toAesEncryptedDataString(value) + if (curve === PolkadotCurve.Ed25519) return Ed25519Crypto.toEd25519EncryptedDataString(value) + throw new Error(`Curve not supported ${curve}`) +} + +/** + * Returns PolkadotSignature + */ +export function toPolkadotSignature(value: string | PolkadotSignature): PolkadotSignature { + if (isValidPolkadotSignature(value)) { + return value + } + throw new Error(`Not a valid polkadot signature:${JSON.stringify(value)}.`) +} + +/** Accepts hex string checks if a valid address + * Returns PolkadotAddress with prefix + */ +export function toPolkadotAddress(value: string): PolkadotAddress { + if (isValidPolkadotAddress(value)) { + return value + } + throw new Error(`Not a valid polkadot address:${value}.`) +} + +/** verifies a signature is valid for a message body and address */ +export function verifySignatureWithAddress( + signedMessage: string, + signature: string, + address: PolkadotPublicKey, +): boolean { + const publicKey = decodeAddress(address) + const hexPublicKey = byteArrayToHexString(publicKey) + + return signatureVerify(signedMessage, signature, hexPublicKey).isValid +} diff --git a/src/chains/polkadot_1/helpers/index.ts b/src/chains/polkadot_1/helpers/index.ts index 23ad598e..ffc135c9 100644 --- a/src/chains/polkadot_1/helpers/index.ts +++ b/src/chains/polkadot_1/helpers/index.ts @@ -1,37 +1,4 @@ -import { PolkadotPublicKey } from "../models"; -import { isNullOrEmpty } from "../../../helpers"; -import { PolkadotAddress } from "../models"; -import { decodeAddress, encodeAddress } from '@polkadot/keyring' -import { hexToU8a, isHex } from '@polkadot/util' - -export function isValidPolkadotPublicKey(value: PolkadotPublicKey): boolean { - return true -} - -export function isValidPolkadotAddress(address: PolkadotAddress): boolean { - try { - encodeAddress( - isHex(address) - ? hexToU8a(address) - : decodeAddress(address) - ) - - return true - } catch (error) { - return false - } -} - -export function toPolkadotEntityName(name: string): PolkadotAddress { - if (isValidPolkadotAddress(name)) { - return name - } - - if (isNullOrEmpty(name)) { - return null - } - - throw new Error( - `Not a valid Ethereum entity :${name}. Ethereum entity can valid address, public key, private key or transaction data.`, - ) -} \ No newline at end of file +export * from './cryptoModelHelpers' +// export * from './generalHelpers' +// export * from './generalModelHelpers' +// export * from './transactionHelpers' From 6215c44e20141eb8b353b5c1e1013c444704cacb Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 8 Mar 2021 10:20:52 -0800 Subject: [PATCH 12/31] polka - added constants --- src/chains/polkadot_1/polkadotConstants.ts | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/chains/polkadot_1/polkadotConstants.ts diff --git a/src/chains/polkadot_1/polkadotConstants.ts b/src/chains/polkadot_1/polkadotConstants.ts new file mode 100644 index 00000000..c611b3c4 --- /dev/null +++ b/src/chains/polkadot_1/polkadotConstants.ts @@ -0,0 +1,25 @@ +// TODO: Update values for Polkadot + +import { PolkadotKeyPairType } from "./models" + +// sign transaction default parameters +export const TRANSACTION_ENCODING = 'utf8' +export const DEFAULT_TRANSACTION_EXPIRY_IN_SECONDS = 30 +export const DEFAULT_TRANSACTION_BLOCKS_BEHIND_REF_BLOCK = 3 +export const CHAIN_BLOCK_FREQUENCY = 0.5 // time between blocks produced in seconds + +// transaction confirmation default parameters +export const DEFAULT_BLOCKS_TO_CHECK = 20 +export const DEFAULT_CHECK_INTERVAL = 500 +export const DEFAULT_GET_BLOCK_ATTEMPTS = 10 + +// default unit for DOT transfers +// export const DEFAULT_POLKADOT_UNIT = EthUnit.Wei + +// token related +export const NATIVE_CHAIN_TOKEN_SYMBOL = 'DOT' +/** The chain address of the default token/currency contract (if any) */ +export const NATIVE_CHAIN_TOKEN_ADDRESS: any = null +export const DOT_TOKEN_PRECISION = 10 + +export const DEFAULT_POLKADOT_KEY_PAIR_TYPE = PolkadotKeyPairType.Ed25519 From f0d555459cf84506c931605ae1a444a72581dc05 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 8 Mar 2021 10:21:55 -0800 Subject: [PATCH 13/31] polka - WIP adding crypto functions --- src/chains/polkadot_1/polkadotCrypto.ts | 95 +++++++++---------------- 1 file changed, 35 insertions(+), 60 deletions(-) diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index ada03b86..ff9528dd 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -7,9 +7,8 @@ import { naclKeypairFromSeed, schnorrkelKeypairFromSeed, secp256k1KeypairFromSeed, - signatureVerify, } from '@polkadot/util-crypto' -import { Keypair, Seedpair, KeypairType } from '@polkadot/util-crypto/types' +import { Keypair, Seedpair } from '@polkadot/util-crypto/types' import secp256k1 from 'secp256k1' import { PolkadotAddress, @@ -30,11 +29,18 @@ import { } from '../../helpers' import { ensureEncryptedValueIsObject } from '../../crypto/genericCryptoHelpers' import * as AsymmetricHelpers from '../../crypto/asymmetricHelpers' +import { throwNewError } from '../../errors' -export const keypairFromSeed = { - ecdsa: (seed: Uint8Array): Keypair => secp256k1KeypairFromSeed(seed) as Keypair, - ed25519: (seed: Uint8Array): Keypair => naclKeypairFromSeed(seed) as Keypair, - sr25519: (seed: Uint8Array): Keypair => schnorrkelKeypairFromSeed(seed) as Keypair, +// TODO - should change depending on curve +const POLKADOT_ASYMMETRIC_SCHEME_NAME = 'asym.chainjs.secp256k1.polkadot' + +/** returns a keypair for a specific curve */ +export function generateKeypairFromSeed(seed: Uint8Array, curve: PolkadotCurve): Keypair { + if (curve === PolkadotCurve.Secp256k1) return secp256k1KeypairFromSeed(seed) as Keypair + if (curve === PolkadotCurve.Ed25519) return naclKeypairFromSeed(seed) as Keypair + if (curve === PolkadotCurve.Sr25519) return schnorrkelKeypairFromSeed(seed) as Keypair + throwNewError(`Curve type not supported: ${curve}`) + return null } export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { @@ -46,46 +52,23 @@ export function generateNewAccountPhrase(): string { return mnemonic } -export function getKeypairFromPhrase(mnemonic: string, type: PolkadotCurve): PolkadotKeypair { +export function getKeypairFromPhrase(mnemonic: string, curve: PolkadotCurve): Keypair { const seed = mnemonicToMiniSecret(mnemonic) - const keyPair = keypairFromSeed[type](seed) + const keyPair = generateKeypairFromSeed(seed, curve) return keyPair } -// export function verifySignatureWithAddress( -// signedMessage: string, -// signature: string, -// address: PolkadotPublicKey, -// ): boolean { -// const publicKey = decodeAddress(address) -// const hexPublicKey = u8aToHex(publicKey) - -// return signatureVerify(signedMessage, signature, hexPublicKey).isValid -// } - // export function determineCurveFromAddress() {} // export function determineCurveFromKeyPair() { // // itererate verify - trying each curve // } -const ETHEREUM_ASYMMETRIC_SCHEME_NAME = 'asym.chainjs.secp256k1.ethereum' - // eslint-disable-next-line prefer-destructuring export const defaultIter = AesCrypto.defaultIter // eslint-disable-next-line prefer-destructuring export const defaultMode = AesCrypto.defaultMode -/** Verifies that the value is a valid, stringified JSON Encrypted object */ -export function isSymEncryptedDataString(value: string): value is AesCrypto.AesEncryptedDataString { - return AesCrypto.isAesEncryptedDataString(value) -} - -/** Ensures that the value comforms to a well-formed, stringified JSON Encrypted Object */ -export function toSymEncryptedDataString(value: any): AesCrypto.AesEncryptedDataString { - return AesCrypto.toAesEncryptedDataString(value) -} - /** get uncompressed public key from EthereumPublicKey */ export function uncompressPublicKey(publicKey: PolkadotPublicKey): string { notImplemented() @@ -132,7 +115,7 @@ export async function encryptWithPublicKey( const useOptions = { ...options, curveType: Asymmetric.EciesCurveType.Secp256k1, - scheme: ETHEREUM_ASYMMETRIC_SCHEME_NAME, + scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME, } const response = Asymmetric.encryptWithPublicKey(publicKeyUncompressed, unencrypted, useOptions) return Asymmetric.toAsymEncryptedDataString(JSON.stringify(response)) @@ -201,10 +184,10 @@ export async function decryptWithPrivateKeys( // return toEthereumPublicKey(ecrecover(toEthBuffer(data), v, r, s).toString()) // } -// /** Returns public key from ethereum address */ -// export function getEthereumAddressFromPublicKey(publicKey: EthereumPublicKey): EthereumAddress { -// return bufferToHex(publicToAddress(toEthBuffer(publicKey))) -// } +/** Returns public key from polkadot address */ +export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { + notImplemented() +} /** Adds privateKeyEncrypted if missing by encrypting privateKey (using password) */ function encryptAccountPrivateKeysIfNeeded( @@ -218,6 +201,7 @@ function encryptAccountPrivateKeysIfNeeded( ? keys.privateKeyEncrypted : encryptWithPassword(keys?.privateKey, password, options) const encryptedKeys: PolkadotKeypair = { + type: keys?.type, privateKey: keys?.privateKey, publicKey: keys?.publicKey, privateKeyEncrypted, @@ -226,13 +210,21 @@ function encryptAccountPrivateKeysIfNeeded( } /** Generates and returns a new public/private key pair */ -export async function generateKeyPair(): Promise { - notImplemented() - const wallet = Wallet.generate() - const privateKey: PolkadotPrivateKey = wallet.getPrivateKeyString() - const publicKey: PolkadotPublicKey = wallet.getPublicKeyString() - const keys: PolkadotKeypair = { privateKey, publicKey } - return keys +export async function generateKeyPair( + keyPairType: PolkadotKeypairType, + phrase?: string, + derivationPath?: number, +): Promise { + const { newKeysOptions } = this._options + // const { curve: type } = newKeysOptions || {} + // const overrideType = type || 'ed25519' + // const overridePhrase = generateNewAccountPhrase() + // this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) + // this._options.publicKey = this._generatedKeypair.publicKey + // this._options.newKeysOptions = { + // phrase: overridePhrase, + // curve: overrideType, + // } } /** Generates new public and private key pair @@ -258,20 +250,3 @@ export function verifySignedWithPublicKey( notImplemented() return null } - -/** determine crypto curve from key type */ -export function getCurveFromKeyType(keyType: PolkadotKeyPairType): PolkadotCurve { - switch (keyType) { - case PolkadotKeyPairType.Ecdsa: - return PolkadotCurve.Secp256k1 - case PolkadotKeyPairType.Ethereum: - return PolkadotCurve.Secp256k1 - case PolkadotKeyPairType.Ed25519: - return PolkadotCurve.Ed25519 - case PolkadotKeyPairType.Sr25519: - return PolkadotCurve.Sr25519 - default: - notSupported(`Keytype ${keyType}`) - return null - } -} From ab4d7c6df019a7b53a0dc17ff280337ffd9eb1ab Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 8 Mar 2021 10:22:36 -0800 Subject: [PATCH 14/31] polka - added crypto models and helpers --- .../polkadot_1/helpers/generalModelHelpers.ts | 19 +++++++++++++++ src/chains/polkadot_1/models/accountModels.ts | 5 +--- src/chains/polkadot_1/models/cryptoModels.ts | 23 +++++++++++++++---- src/chains/polkadot_1/models/index.ts | 2 +- 4 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/chains/polkadot_1/helpers/generalModelHelpers.ts diff --git a/src/chains/polkadot_1/helpers/generalModelHelpers.ts b/src/chains/polkadot_1/helpers/generalModelHelpers.ts new file mode 100644 index 00000000..45d2fe60 --- /dev/null +++ b/src/chains/polkadot_1/helpers/generalModelHelpers.ts @@ -0,0 +1,19 @@ +import { isNullOrEmpty } from '../../../helpers' +import { PolkadotAddress } from '../models' +import { isValidPolkadotAddress } from './cryptoModelHelpers' + +export function toPolkadotEntityName(name: string): PolkadotAddress { + if (isValidPolkadotAddress(name)) { + return name + } + + if (isNullOrEmpty(name)) { + return null + } + + throw new Error( + `Not a valid Ethereum entity :${name}. Ethereum entity can valid address, public key, private key or transaction data.`, + ) +} + +// TODO - use other generalModelHelpers.ts as starting point for this file diff --git a/src/chains/polkadot_1/models/accountModels.ts b/src/chains/polkadot_1/models/accountModels.ts index 41569f9b..cc467163 100644 --- a/src/chains/polkadot_1/models/accountModels.ts +++ b/src/chains/polkadot_1/models/accountModels.ts @@ -1,7 +1,4 @@ -import { - PolkadotPublicKey, - PolkadotNewKeysOptions -} from './' +import { PolkadotPublicKey, PolkadotNewKeysOptions } from './' export type PolkadotCreateAccountOptions = { publicKey?: PolkadotPublicKey diff --git a/src/chains/polkadot_1/models/cryptoModels.ts b/src/chains/polkadot_1/models/cryptoModels.ts index 8c47986e..677d6923 100644 --- a/src/chains/polkadot_1/models/cryptoModels.ts +++ b/src/chains/polkadot_1/models/cryptoModels.ts @@ -1,4 +1,5 @@ -import { PublicKeyBrand, PrivateKeyBrand, SignatureBrand } from '../../../models' +import { Ed25519Crypto } from '../../../crypto' +import { PublicKeyBrand, PrivateKeyBrand, SignatureBrand, ModelsCryptoAes, ChainEntityNameBrand } from '../../../models' export enum PolkadotCurve { Ed25519 = 'ed25519', @@ -14,11 +15,17 @@ export enum PolkadotKeyPairType { } export type PolkadotNewKeysOptions = { + keyPairType?: PolkadotKeyPairType phrase?: string - keyType?: PolkadotKeyPairType derivationPath?: string } +/** an address string - formatted correctly for polkadot */ +export type PolkadotAddress = string + +/** will be used as accountName */ +export type PolkadotEntityName = string & ChainEntityNameBrand + /** a private key string - formatted correctly for polkadot */ export type PolkadotPrivateKey = string & PrivateKeyBrand @@ -26,12 +33,20 @@ export type PolkadotPrivateKey = string & PrivateKeyBrand export type PolkadotPublicKey = string & PublicKeyBrand /** a signature string - formatted correcly for polkadot */ -export type PolkadotSignature = string & SignatureBrand // TODO: Use Polkadot SDK to define type +export type PolkadotSignature = string & SignatureBrand /** key pair - in the format returned from algosdk */ export type PolkadotKeypair = { type: PolkadotKeyPairType publicKey: PolkadotPublicKey privateKey: PolkadotPrivateKey - // privateKeyEncrypted?: ModelsCryptoAes.AesEncryptedDataString + privateKeyEncrypted?: ModelsCryptoAes.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString } + +/** options used to convert a password and salt into a passowrd key */ +export type PolkadotEncryptionOptions = + | ModelsCryptoAes.AesEncryptionOptions + | Ed25519Crypto.Ed25519PasswordEncryptionOptions + +/** Additional parameters for encryption/decryption */ +export type EncryptionOptions = PolkadotEncryptionOptions diff --git a/src/chains/polkadot_1/models/index.ts b/src/chains/polkadot_1/models/index.ts index 0593beea..71ca45a7 100644 --- a/src/chains/polkadot_1/models/index.ts +++ b/src/chains/polkadot_1/models/index.ts @@ -1,2 +1,2 @@ export * from './generalModels' -export * from './cryptoModels' \ No newline at end of file +export * from './cryptoModels' From 80e3dfae19ee23f5ac9123f776aa81e3af07ae7d Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 8 Mar 2021 10:22:51 -0800 Subject: [PATCH 15/31] polka - WIP updating generateKeys, etc --- src/chains/polkadot_1/polkadotCreateAccount.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/chains/polkadot_1/polkadotCreateAccount.ts b/src/chains/polkadot_1/polkadotCreateAccount.ts index 911b4b46..5073518b 100644 --- a/src/chains/polkadot_1/polkadotCreateAccount.ts +++ b/src/chains/polkadot_1/polkadotCreateAccount.ts @@ -1,11 +1,12 @@ import { PolkadotCreateAccountOptions } from './models/accountModels' import { PolkadotChainState } from './polkadotChainState' -import { generateNewAccountPhrase, getKeypairFromPhrase, getPolkadotAddressFromPublicKey } from './polkadotCrypto' +import { generateKeyPair, generateNewAccountPhrase, getKeypairFromPhrase, getPolkadotAddressFromPublicKey } from './polkadotCrypto' import { isValidPolkadotPublicKey, toPolkadotEntityName } from './helpers' import { notSupported } from '../../helpers' import { CreateAccount } from '../../interfaces' import { throwNewError } from '../../errors' import { PolkadotAddress, PolkadotPublicKey, PolkadotCurve, PolkadotKeypair } from './models' +import { DEFAULT_POLKADOT_KEY_PAIR_TYPE } from './polkadotConstants' /** Helper class to compose a transaction for creating a new chain account * Handles native accounts @@ -119,20 +120,20 @@ export class PolkadotCreateAccount implements CreateAccount { await this.generateAccountKeys() } this._accountName = getPolkadotAddressFromPublicKey(this._generatedKeypair.publicKey) - this._accountType = this._options.newKeysOptions.curve + this._accountType = this._options.newKeysOptions.keyPaitType } private async generateAccountKeys(): Promise { const { newKeysOptions } = this._options - const { curve: type } = newKeysOptions || {} - const overrideType = type || 'ed25519' - const overridePhrase = generateNewAccountPhrase() + const { keyPairType, phrase: overridePhrase, derivationPath } = newKeysOptions || {} + const overrideType = keyPairType || DEFAULT_POLKADOT_KEY_PAIR_TYPE - this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) + // this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) + this._generatedKeypair = await generateKeyPair(keyPairType, phrase, derivationPath) this._options.publicKey = this._generatedKeypair.publicKey this._options.newKeysOptions = { phrase: overridePhrase, - curve: overrideType, + keyPairType: overrideType, } } From fdca4d61840996452d13b81e67f89443606c642a Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Tue, 9 Mar 2021 22:21:10 +0900 Subject: [PATCH 16/31] polkda - WIP adding main fn into polkadotCrypto --- src/chains/polkadot_1/polkadotCrypto.ts | 316 ++++++++++++++---------- 1 file changed, 189 insertions(+), 127 deletions(-) diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index ff9528dd..98886391 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -1,38 +1,37 @@ -import { u8aToHex } from '@polkadot/util' import { - encodeAddress, - decodeAddress, + keyExtractSuri, + keyFromPath, mnemonicGenerate, + mnemonicToLegacySeed, mnemonicToMiniSecret, naclKeypairFromSeed, schnorrkelKeypairFromSeed, secp256k1KeypairFromSeed, } from '@polkadot/util-crypto' -import { Keypair, Seedpair } from '@polkadot/util-crypto/types' +import { Keypair } from '@polkadot/util-crypto/types' +// import Keyring from '@polkadot/keyring' +import { isHex } from '@polkadot/util' import secp256k1 from 'secp256k1' import { - PolkadotAddress, PolkadotCurve, + PolkadotEncryptionOptions, PolkadotKeypair, PolkadotKeyPairType, PolkadotPrivateKey, PolkadotPublicKey, - PolkadotSignature, } from './models' -import { AesCrypto, Asymmetric } from '../../crypto' -import { - notImplemented, - removeHexPrefix, - byteArrayToHexString, - hexStringToByteArray, - notSupported, -} from '../../helpers' +import { AesCrypto, Asymmetric, Ed25519Crypto } from '../../crypto' +import { removeHexPrefix, byteArrayToHexString, hexStringToByteArray } from '../../helpers' import { ensureEncryptedValueIsObject } from '../../crypto/genericCryptoHelpers' -import * as AsymmetricHelpers from '../../crypto/asymmetricHelpers' +// import * as AsymmetricHelpers from '../../crypto/asymmetricHelpers' import { throwNewError } from '../../errors' +import { getCurveFromKeyType, toPolkadotPrivateKey, toPolkadotPublicKey, toSymEncryptedDataString } from './helpers' // TODO - should change depending on curve -const POLKADOT_ASYMMETRIC_SCHEME_NAME = 'asym.chainjs.secp256k1.polkadot' +const enum POLKADOT_ASYMMETRIC_SCHEME_NAME { + Ed25519 = 'asym.chainjs.ed25519.polkadot', + Secp256k1 = 'asym.chainjs.secp256k1.polkadot', +} /** returns a keypair for a specific curve */ export function generateKeypairFromSeed(seed: Uint8Array, curve: PolkadotCurve): Keypair { @@ -43,10 +42,6 @@ export function generateKeypairFromSeed(seed: Uint8Array, curve: PolkadotCurve): return null } -export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { - return encodeAddress(publicKey) -} - export function generateNewAccountPhrase(): string { const mnemonic = mnemonicGenerate() return mnemonic @@ -58,20 +53,8 @@ export function getKeypairFromPhrase(mnemonic: string, curve: PolkadotCurve): Ke return keyPair } -// export function determineCurveFromAddress() {} - -// export function determineCurveFromKeyPair() { -// // itererate verify - trying each curve -// } - -// eslint-disable-next-line prefer-destructuring -export const defaultIter = AesCrypto.defaultIter -// eslint-disable-next-line prefer-destructuring -export const defaultMode = AesCrypto.defaultMode - -/** get uncompressed public key from EthereumPublicKey */ +/** get uncompressed public key from SEecp256k1 key */ export function uncompressPublicKey(publicKey: PolkadotPublicKey): string { - notImplemented() // if already decompressed an not has trailing 04 const cleanedPublicKey = removeHexPrefix(publicKey) const testBuffer = Buffer.from(cleanedPublicKey, 'hex') @@ -82,25 +65,43 @@ export function uncompressPublicKey(publicKey: PolkadotPublicKey): string { return uncompressedPublicKey } -/** Decrypts the encrypted value using a password, and optional salt using AES algorithm and SHA256 hash function - * The encrypted value is either a stringified JSON object or a JSON object */ -export function decryptWithPassword( - encrypted: AesCrypto.AesEncryptedDataString | any, - password: string, - options: AesCrypto.AesEncryptionOptions, -): string { - notImplemented() - return AesCrypto.decryptWithPassword(encrypted, password, options) -} - /** Encrypts a string using a password and optional salt */ export function encryptWithPassword( unencrypted: string, password: string, - options: AesCrypto.AesEncryptionOptions, -): AesCrypto.AesEncryptedDataString { - notImplemented() - return AesCrypto.encryptWithPassword(unencrypted, password, options) + keypairType: PolkadotKeyPairType, + options: PolkadotEncryptionOptions, +): AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { + // TODO: Define Src25519 curve + const curve = getCurveFromKeyType(keypairType) + if (curve === PolkadotCurve.Ed25519) { + const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) + const encrypted = Ed25519Crypto.encrypt(unencrypted, passwordKey) + return toSymEncryptedDataString(encrypted, keypairType) + } + if (curve === PolkadotCurve.Secp256k1) return AesCrypto.encryptWithPassword(unencrypted, password, options) + // if no curve, throw an error - curve not supported + throw new Error(`Curve not supported ${curve}`) +} + +/** Decrypts the encrypted value using a password, and optional salt using secp256k1, and nacl + * The encrypted value is either a stringified JSON object or a JSON object */ +export function decryptWithPassword( + encrypted: AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString | any, + password: string, + keypairType: PolkadotKeyPairType, + options: PolkadotEncryptionOptions, +): string { + // TODO: Define Src25519 curve + const curve = getCurveFromKeyType(keypairType) + if (curve === PolkadotCurve.Ed25519) { + const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) + const decrypted = Ed25519Crypto.decrypt(encrypted, passwordKey) + return decrypted + } + if (curve === PolkadotCurve.Secp256k1) return AesCrypto.decryptWithPassword(encrypted, password, options) + // if no curve, throw an error - curve not supported + throw new Error(`Curve not supported ${curve}`) } /** Encrypts a string using a public key into a stringified JSON object @@ -108,15 +109,32 @@ export function encryptWithPassword( export async function encryptWithPublicKey( unencrypted: string, publicKey: PolkadotPublicKey, + keypairType: PolkadotKeyPairType, options: Asymmetric.EciesOptions, ): Promise { - notImplemented() - const publicKeyUncompressed = uncompressPublicKey(publicKey) // should be hex string - const useOptions = { - ...options, - curveType: Asymmetric.EciesCurveType.Secp256k1, - scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME, + // TODO: Define Src25519 curve + const curve = getCurveFromKeyType(keypairType) + let publicKeyUncompressed = '' + let useOptions = { ...options } + if (curve === PolkadotCurve.Secp256k1) { + publicKeyUncompressed = uncompressPublicKey(publicKey) + useOptions = { + ...useOptions, + curveType: Asymmetric.EciesCurveType.Secp256k1, + scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME.Secp256k1, + } + } else if (curve === PolkadotCurve.Ed25519) { + publicKeyUncompressed = publicKey + useOptions = { + ...useOptions, + curveType: Asymmetric.EciesCurveType.Ed25519, + scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME.Ed25519, + } + } else { + // if no curve, throw an error - not supported curve + throw new Error(`Curve not supported ${curve}`) } + const response = Asymmetric.encryptWithPublicKey(publicKeyUncompressed, unencrypted, useOptions) return Asymmetric.toAsymEncryptedDataString(JSON.stringify(response)) } @@ -127,13 +145,24 @@ export async function encryptWithPublicKey( export async function decryptWithPrivateKey( encrypted: Asymmetric.AsymmetricEncryptedDataString | Asymmetric.AsymmetricEncryptedData, privateKey: PolkadotPrivateKey, + keypairType: PolkadotKeyPairType, options: Asymmetric.EciesOptions, ): Promise { - notImplemented() - const useOptions = { ...options, curveType: Asymmetric.EciesCurveType.Secp256k1 } - const privateKeyHex = removeHexPrefix(privateKey) - const encryptedObject = ensureEncryptedValueIsObject(encrypted) - return Asymmetric.decryptWithPrivateKey(encryptedObject, privateKeyHex, useOptions) + const curve = getCurveFromKeyType(keypairType) + let useOptions = { ...options } + let privateKeyConverted = '' + if (curve === PolkadotCurve.Secp256k1) { + useOptions = { ...useOptions, curveType: Asymmetric.EciesCurveType.Secp256k1 } + privateKeyConverted = removeHexPrefix(privateKey) + } else if (curve === PolkadotCurve.Ed25519) { + useOptions = { ...useOptions, curveType: Asymmetric.EciesCurveType.Ed25519 } + privateKeyConverted = privateKey.slice(0, privateKey.length / 2) + } else { + // if no curve matched, throw an error - not supported curve + throw new Error(`Curve not supported ${curve}`) + } + const encryptedObject = ensureEncryptedValueIsObject(encrypted) as Asymmetric.AsymmetricEncryptedData + return Asymmetric.decryptWithPrivateKey(encryptedObject, privateKeyConverted, useOptions) } /** Encrypts a string using multiple assymmetric encryptions with multiple public keys - one after the other @@ -141,29 +170,34 @@ export async function decryptWithPrivateKey( * the first parameter of the helper is a chain-specific function (in this file) to encryptWithPublicKey * The result is stringified JSON object including an array of encryption results with the last one including the final cipertext * Encrypts using publicKeys in the order they appear in the array */ -export async function encryptWithPublicKeys( - unencrypted: string, - publicKeys: PolkadotPublicKey[], - options?: Asymmetric.EciesOptions, -): Promise { - notImplemented() - return Asymmetric.toAsymEncryptedDataString( - await AsymmetricHelpers.encryptWithPublicKeys(encryptWithPublicKey, unencrypted, publicKeys, options), - ) -} +// export async function encryptWithPublicKeys( +// unencrypted: string, +// publicKeys: PolkadotPublicKey[], +// keypairType: PolkadotKeyPairType[], +// options?: Asymmetric.EciesOptions, +// ): Promise { +// // TODO: Make sure to change asymmetricHelpers.encryptWithPublicKeys or nor +// notImplemented() +// return null +// return Asymmetric.toAsymEncryptedDataString( +// await AsymmetricHelpers.encryptWithPublicKeys(encryptWithPublicKey, unencrypted, publicKeys, options), +// ) +// } /** Unwraps an object produced by encryptWithPublicKeys() - resulting in the original ecrypted string * calls a helper function to perform the iterative unwrapping * the first parameter of the helper is a chain-specific function (in this file) to decryptWithPrivateKey * Decrypts using privateKeys that match the publicKeys provided in encryptWithPublicKeys() - provide the privateKeys in same order * The result is the decrypted string */ -export async function decryptWithPrivateKeys( - encrypted: Asymmetric.AsymmetricEncryptedDataString, - privateKeys: PolkadotPublicKey[], -): Promise { - notImplemented() - return AsymmetricHelpers.decryptWithPrivateKeys(decryptWithPrivateKey, encrypted, privateKeys, {}) -} +// export async function decryptWithPrivateKeys( +// encrypted: Asymmetric.AsymmetricEncryptedDataString, +// privateKeys: PolkadotPublicKey[], +// ): Promise { +// // TODO: Make sure to change asymmetricHelpers.encryptWithPublicKeys or nor +// notImplemented() +// return null +// return AsymmetricHelpers.decryptWithPrivateKeys(decryptWithPrivateKey, encrypted, privateKeys, {}) +// } /** Signs data with private key */ // export function sign(data: string | Buffer, privateKey: string): PolkadotSignature { @@ -174,79 +208,107 @@ export async function decryptWithPrivateKeys( // return toEthereumSignature(ecsign(dataBuffer, keyBuffer)) // } -// /** Returns public key from ethereum signature */ -// export function getEthereumPublicKeyFromSignature( -// signature: EthereumSignature, -// data: string | Buffer, -// encoding: string, -// ): EthereumPublicKey { -// const { v, r, s } = signature -// return toEthereumPublicKey(ecrecover(toEthBuffer(data), v, r, s).toString()) -// } +/** Generates and returns a new public/private key pair + * Note: Reference - createFromUri from @polkadot/keyring + * https://github.com/polkadot-js/common/blob/master/packages/keyring/src/keyring.ts#L197 + */ +export async function generateKeyPair( + keypairType: PolkadotKeyPairType, + mnemonic?: string, + derivationPath?: string, +): Promise { + const curve = getCurveFromKeyType(keypairType) + const overridePhrase = mnemonic !== undefined ? mnemonic : generateNewAccountPhrase() + const suri = derivationPath !== undefined ? `${overridePhrase}//${derivationPath}` : overridePhrase + const { password, path, phrase } = keyExtractSuri(suri) + let seed: Uint8Array + if (isHex(phrase, 256)) { + seed = hexStringToByteArray(phrase) + } else { + const str = phrase as string + const parts = str.split(' ') + if ([12, 15, 18, 21, 24].includes(parts.length)) { + seed = + keypairType === PolkadotKeyPairType.Ethereum + ? mnemonicToLegacySeed(phrase) + : mnemonicToMiniSecret(phrase, password) + } else { + throw new Error('Specified phrase is not a valild mnemonic and is invalid as a raw seed at > 32 bytes') + } + } -/** Returns public key from polkadot address */ -export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { - notImplemented() + // TODO: need to support ethereum type + const derivedKeypair = keyFromPath(generateKeypairFromSeed(seed, curve), path, keypairType) + const keypair: PolkadotKeypair = { + type: keypairType, + publicKey: toPolkadotPublicKey(byteArrayToHexString(derivedKeypair.publicKey)), + privateKey: toPolkadotPrivateKey(byteArrayToHexString(derivedKeypair.secretKey)), + } + + return keypair } /** Adds privateKeyEncrypted if missing by encrypting privateKey (using password) */ function encryptAccountPrivateKeysIfNeeded( keys: PolkadotKeypair, password: string, - options: AesCrypto.AesEncryptionOptions, + options: PolkadotEncryptionOptions, ): PolkadotKeypair { - // encrypt if not already encrypted - notImplemented() - const privateKeyEncrypted = keys?.privateKeyEncrypted + const privateKeyEncrypted = keys.privateKeyEncrypted ? keys.privateKeyEncrypted - : encryptWithPassword(keys?.privateKey, password, options) + : encryptWithPassword(keys.privateKey, password, keys.type, options) const encryptedKeys: PolkadotKeypair = { - type: keys?.type, - privateKey: keys?.privateKey, - publicKey: keys?.publicKey, + type: keys.type, + publicKey: keys.publicKey, + privateKey: keys.privateKey, privateKeyEncrypted, } return encryptedKeys } -/** Generates and returns a new public/private key pair */ -export async function generateKeyPair( - keyPairType: PolkadotKeypairType, - phrase?: string, - derivationPath?: number, -): Promise { - const { newKeysOptions } = this._options - // const { curve: type } = newKeysOptions || {} - // const overrideType = type || 'ed25519' - // const overridePhrase = generateNewAccountPhrase() - // this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) - // this._options.publicKey = this._generatedKeypair.publicKey - // this._options.newKeysOptions = { - // phrase: overridePhrase, - // curve: overrideType, - // } -} - /** Generates new public and private key pair * Encrypts the private key using password and optional salt */ export async function generateNewAccountKeysAndEncryptPrivateKeys( password: string, - overrideKeys: any, - options: AesCrypto.AesEncryptionOptions, + keypairType: PolkadotKeyPairType, + options: PolkadotEncryptionOptions, ): Promise { - notImplemented() - const keys = await generateKeyPair() + const keys = await generateKeyPair(keypairType) const encryptedKeys = encryptAccountPrivateKeysIfNeeded(keys, password, options) return encryptedKeys } -/** Verify that the signed data was signed using the given key (signed with the private key for the provided public key) */ -export function verifySignedWithPublicKey( - data: string | Buffer, - publicKey: PolkadotPublicKey, - signature: PolkadotSignature, -): boolean { - notImplemented() - return null -} +// export async function generateNewAccountKeysAndEncryptPrivateKeys(password: string, options: AlgorandNewKeysOptions) { +// } + +// export function determineCurveFromAddress() {} + +// export function determineCurveFromKeyPair() { +// // itererate verify - trying each curve +// } + +// /** Returns public key from ethereum signature */ +// export function getEthereumPublicKeyFromSignature( +// signature: EthereumSignature, +// data: string | Buffer, +// encoding: string, +// ): EthereumPublicKey { +// const { v, r, s } = signature +// return toEthereumPublicKey(ecrecover(toEthBuffer(data), v, r, s).toString()) +// } + +// /** Returns public key from polkadot address */ +// export function getPolkadotAddressFromPublicKey(publicKey: PolkadotPublicKey): PolkadotAddress { +// notImplemented() +// } + +// /** Verify that the signed data was signed using the given key (signed with the private key for the provided public key) */ +// export function verifySignedWithPublicKey( +// data: string | Buffer, +// publicKey: PolkadotPublicKey, +// signature: PolkadotSignature, +// ): boolean { +// notImplemented() +// return null +// } From e9ebcefe16c86109f82075ebe173da10a412a439 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Tue, 9 Mar 2021 07:20:52 -0800 Subject: [PATCH 17/31] polka - drop PolkadotCurve for CryptoCurve --- .../polkadot_1/helpers/cryptoModelHelpers.ts | 28 ++++++++++------- src/chains/polkadot_1/models/cryptoModels.ts | 6 ---- .../polkadot_1/polkadotCreateAccount.ts | 16 ++++++---- src/chains/polkadot_1/polkadotCrypto.ts | 30 +++++++++---------- src/models/cryptoModels.ts | 3 +- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts b/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts index b1d03244..0b81b214 100644 --- a/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts +++ b/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts @@ -8,23 +8,29 @@ import { byteArrayToHexString, notSupported, } from '../../../helpers' -import { PolkadotPublicKey, PolkadotPrivateKey, PolkadotSignature, PolkadotAddress, PolkadotCurve, PolkadotKeyPairType } from '../models' +import { + PolkadotPublicKey, + PolkadotPrivateKey, + PolkadotSignature, + PolkadotAddress, + PolkadotKeyPairType, +} from '../models' +import { CryptoCurve } from '../../../models' import { decodeAddress, encodeAddress } from '@polkadot/keyring' import { signatureVerify } from '@polkadot/util-crypto' import { AesCrypto, Ed25519Crypto } from '../../../crypto' -import { throwNewError } from 'src/errors' /** determine crypto curve from key type */ -export function getCurveFromKeyType(keyPairType: PolkadotKeyPairType): PolkadotCurve { +export function getCurveFromKeyType(keyPairType: PolkadotKeyPairType): CryptoCurve { switch (keyPairType) { case PolkadotKeyPairType.Ecdsa: - return PolkadotCurve.Secp256k1 + return CryptoCurve.Secp256k1 case PolkadotKeyPairType.Ethereum: - return PolkadotCurve.Secp256k1 + return CryptoCurve.Secp256k1 case PolkadotKeyPairType.Ed25519: - return PolkadotCurve.Ed25519 + return CryptoCurve.Ed25519 case PolkadotKeyPairType.Sr25519: - return PolkadotCurve.Sr25519 + return CryptoCurve.Sr25519 default: notSupported(`Keytype ${keyPairType}`) return null @@ -104,8 +110,8 @@ export function isSymEncryptedDataString( keyPairType?: PolkadotKeyPairType, ): value is AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { const curve = getCurveFromKeyType(keyPairType) - if (curve === PolkadotCurve.Secp256k1) return AesCrypto.isAesEncryptedDataString(value) - if (curve === PolkadotCurve.Ed25519) return Ed25519Crypto.isEd25519EncryptedDataString(value) + if (curve === CryptoCurve.Secp256k1) return AesCrypto.isAesEncryptedDataString(value) + if (curve === CryptoCurve.Ed25519) return Ed25519Crypto.isEd25519EncryptedDataString(value) // if no curve param, check all possible options return AesCrypto.isAesEncryptedDataString(value) || Ed25519Crypto.isEd25519EncryptedDataString(value) } @@ -116,8 +122,8 @@ export function toSymEncryptedDataString( keyPairType?: PolkadotKeyPairType, ): AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { const curve = getCurveFromKeyType(keyPairType) - if (curve === PolkadotCurve.Secp256k1) return AesCrypto.toAesEncryptedDataString(value) - if (curve === PolkadotCurve.Ed25519) return Ed25519Crypto.toEd25519EncryptedDataString(value) + if (curve === CryptoCurve.Secp256k1) return AesCrypto.toAesEncryptedDataString(value) + if (curve === CryptoCurve.Ed25519) return Ed25519Crypto.toEd25519EncryptedDataString(value) throw new Error(`Curve not supported ${curve}`) } diff --git a/src/chains/polkadot_1/models/cryptoModels.ts b/src/chains/polkadot_1/models/cryptoModels.ts index 677d6923..36054975 100644 --- a/src/chains/polkadot_1/models/cryptoModels.ts +++ b/src/chains/polkadot_1/models/cryptoModels.ts @@ -1,12 +1,6 @@ import { Ed25519Crypto } from '../../../crypto' import { PublicKeyBrand, PrivateKeyBrand, SignatureBrand, ModelsCryptoAes, ChainEntityNameBrand } from '../../../models' -export enum PolkadotCurve { - Ed25519 = 'ed25519', - Sr25519 = 'sr25519', - Secp256k1 = 'secp256k1', -} - export enum PolkadotKeyPairType { Ed25519 = 'ed25519', Sr25519 = 'sr25519', diff --git a/src/chains/polkadot_1/polkadotCreateAccount.ts b/src/chains/polkadot_1/polkadotCreateAccount.ts index 5073518b..351f102c 100644 --- a/src/chains/polkadot_1/polkadotCreateAccount.ts +++ b/src/chains/polkadot_1/polkadotCreateAccount.ts @@ -1,11 +1,17 @@ import { PolkadotCreateAccountOptions } from './models/accountModels' import { PolkadotChainState } from './polkadotChainState' -import { generateKeyPair, generateNewAccountPhrase, getKeypairFromPhrase, getPolkadotAddressFromPublicKey } from './polkadotCrypto' +import { + generateKeyPair, + generateNewAccountPhrase, + getKeypairFromPhrase, + getPolkadotAddressFromPublicKey, +} from './polkadotCrypto' import { isValidPolkadotPublicKey, toPolkadotEntityName } from './helpers' import { notSupported } from '../../helpers' import { CreateAccount } from '../../interfaces' import { throwNewError } from '../../errors' -import { PolkadotAddress, PolkadotPublicKey, PolkadotCurve, PolkadotKeypair } from './models' +import { PolkadotAddress, PolkadotPublicKey, PolkadotKeypair } from './models' +import { CryptoCurve } from '../../models' import { DEFAULT_POLKADOT_KEY_PAIR_TYPE } from './polkadotConstants' /** Helper class to compose a transaction for creating a new chain account @@ -15,7 +21,7 @@ import { DEFAULT_POLKADOT_KEY_PAIR_TYPE } from './polkadotConstants' export class PolkadotCreateAccount implements CreateAccount { private _accountName: PolkadotAddress - private _accountType: PolkadotCurve + private _accountType: CryptoCurve private _chainState: PolkadotChainState @@ -34,7 +40,7 @@ export class PolkadotCreateAccount implements CreateAccount { } /** Account type to be created */ - public get accountType(): PolkadotCurve { + public get accountType(): CryptoCurve { return this._accountType } @@ -120,7 +126,7 @@ export class PolkadotCreateAccount implements CreateAccount { await this.generateAccountKeys() } this._accountName = getPolkadotAddressFromPublicKey(this._generatedKeypair.publicKey) - this._accountType = this._options.newKeysOptions.keyPaitType + this._accountType = this._options.newKeysOptions.keyPairType } private async generateAccountKeys(): Promise { diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index 98886391..a1a7a117 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -13,13 +13,13 @@ import { Keypair } from '@polkadot/util-crypto/types' import { isHex } from '@polkadot/util' import secp256k1 from 'secp256k1' import { - PolkadotCurve, PolkadotEncryptionOptions, PolkadotKeypair, PolkadotKeyPairType, PolkadotPrivateKey, PolkadotPublicKey, } from './models' +import { CryptoCurve } from '../../models' import { AesCrypto, Asymmetric, Ed25519Crypto } from '../../crypto' import { removeHexPrefix, byteArrayToHexString, hexStringToByteArray } from '../../helpers' import { ensureEncryptedValueIsObject } from '../../crypto/genericCryptoHelpers' @@ -34,10 +34,10 @@ const enum POLKADOT_ASYMMETRIC_SCHEME_NAME { } /** returns a keypair for a specific curve */ -export function generateKeypairFromSeed(seed: Uint8Array, curve: PolkadotCurve): Keypair { - if (curve === PolkadotCurve.Secp256k1) return secp256k1KeypairFromSeed(seed) as Keypair - if (curve === PolkadotCurve.Ed25519) return naclKeypairFromSeed(seed) as Keypair - if (curve === PolkadotCurve.Sr25519) return schnorrkelKeypairFromSeed(seed) as Keypair +export function generateKeypairFromSeed(seed: Uint8Array, curve: CryptoCurve): Keypair { + if (curve === CryptoCurve.Secp256k1) return secp256k1KeypairFromSeed(seed) as Keypair + if (curve === CryptoCurve.Ed25519) return naclKeypairFromSeed(seed) as Keypair + if (curve === CryptoCurve.Sr25519) return schnorrkelKeypairFromSeed(seed) as Keypair throwNewError(`Curve type not supported: ${curve}`) return null } @@ -47,7 +47,7 @@ export function generateNewAccountPhrase(): string { return mnemonic } -export function getKeypairFromPhrase(mnemonic: string, curve: PolkadotCurve): Keypair { +export function getKeypairFromPhrase(mnemonic: string, curve: CryptoCurve): Keypair { const seed = mnemonicToMiniSecret(mnemonic) const keyPair = generateKeypairFromSeed(seed, curve) return keyPair @@ -74,12 +74,12 @@ export function encryptWithPassword( ): AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { // TODO: Define Src25519 curve const curve = getCurveFromKeyType(keypairType) - if (curve === PolkadotCurve.Ed25519) { + if (curve === CryptoCurve.Ed25519) { const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) const encrypted = Ed25519Crypto.encrypt(unencrypted, passwordKey) return toSymEncryptedDataString(encrypted, keypairType) } - if (curve === PolkadotCurve.Secp256k1) return AesCrypto.encryptWithPassword(unencrypted, password, options) + if (curve === CryptoCurve.Secp256k1) return AesCrypto.encryptWithPassword(unencrypted, password, options) // if no curve, throw an error - curve not supported throw new Error(`Curve not supported ${curve}`) } @@ -94,12 +94,12 @@ export function decryptWithPassword( ): string { // TODO: Define Src25519 curve const curve = getCurveFromKeyType(keypairType) - if (curve === PolkadotCurve.Ed25519) { + if (curve === CryptoCurve.Ed25519) { const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) const decrypted = Ed25519Crypto.decrypt(encrypted, passwordKey) return decrypted } - if (curve === PolkadotCurve.Secp256k1) return AesCrypto.decryptWithPassword(encrypted, password, options) + if (curve === CryptoCurve.Secp256k1) return AesCrypto.decryptWithPassword(encrypted, password, options) // if no curve, throw an error - curve not supported throw new Error(`Curve not supported ${curve}`) } @@ -116,14 +116,14 @@ export async function encryptWithPublicKey( const curve = getCurveFromKeyType(keypairType) let publicKeyUncompressed = '' let useOptions = { ...options } - if (curve === PolkadotCurve.Secp256k1) { + if (curve === CryptoCurve.Secp256k1) { publicKeyUncompressed = uncompressPublicKey(publicKey) useOptions = { ...useOptions, curveType: Asymmetric.EciesCurveType.Secp256k1, scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME.Secp256k1, } - } else if (curve === PolkadotCurve.Ed25519) { + } else if (curve === CryptoCurve.Ed25519) { publicKeyUncompressed = publicKey useOptions = { ...useOptions, @@ -151,10 +151,10 @@ export async function decryptWithPrivateKey( const curve = getCurveFromKeyType(keypairType) let useOptions = { ...options } let privateKeyConverted = '' - if (curve === PolkadotCurve.Secp256k1) { + if (curve === CryptoCurve.Secp256k1) { useOptions = { ...useOptions, curveType: Asymmetric.EciesCurveType.Secp256k1 } privateKeyConverted = removeHexPrefix(privateKey) - } else if (curve === PolkadotCurve.Ed25519) { + } else if (curve === CryptoCurve.Ed25519) { useOptions = { ...useOptions, curveType: Asymmetric.EciesCurveType.Ed25519 } privateKeyConverted = privateKey.slice(0, privateKey.length / 2) } else { @@ -218,7 +218,7 @@ export async function generateKeyPair( derivationPath?: string, ): Promise { const curve = getCurveFromKeyType(keypairType) - const overridePhrase = mnemonic !== undefined ? mnemonic : generateNewAccountPhrase() + const overridePhrase = mnemonic || generateNewAccountPhrase() const suri = derivationPath !== undefined ? `${overridePhrase}//${derivationPath}` : overridePhrase const { password, path, phrase } = keyExtractSuri(suri) let seed: Uint8Array diff --git a/src/models/cryptoModels.ts b/src/models/cryptoModels.ts index d5b8f3bd..7b1234db 100644 --- a/src/models/cryptoModels.ts +++ b/src/models/cryptoModels.ts @@ -41,8 +41,9 @@ type AccountKeysStruct = { } enum CryptoCurve { - Secp256k1 = 'secp256k1', Ed25519 = 'ed25519', + Secp256k1 = 'secp256k1', + Sr25519 = 'sr25519', } // exporting explicity in order to alias Models.. exports From 3fc0da18571cb0321fe9344fae78dcafb64a3a0e Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Tue, 9 Mar 2021 09:08:38 -0800 Subject: [PATCH 18/31] polka - refactor generateKeys() --- src/chains/polkadot_1/polkadotCrypto.ts | 106 ++++++++++++++---------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index a1a7a117..5a427cb8 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -12,6 +12,7 @@ import { Keypair } from '@polkadot/util-crypto/types' // import Keyring from '@polkadot/keyring' import { isHex } from '@polkadot/util' import secp256k1 from 'secp256k1' +import { DeriveJunction } from '@polkadot/util-crypto/key/DeriveJunction' import { PolkadotEncryptionOptions, PolkadotKeypair, @@ -19,9 +20,9 @@ import { PolkadotPrivateKey, PolkadotPublicKey, } from './models' -import { CryptoCurve } from '../../models' +import { CryptoCurve, PublicKey } from '../../models' import { AesCrypto, Asymmetric, Ed25519Crypto } from '../../crypto' -import { removeHexPrefix, byteArrayToHexString, hexStringToByteArray } from '../../helpers' +import { removeHexPrefix, byteArrayToHexString, hexStringToByteArray, notSupported } from '../../helpers' import { ensureEncryptedValueIsObject } from '../../crypto/genericCryptoHelpers' // import * as AsymmetricHelpers from '../../crypto/asymmetricHelpers' import { throwNewError } from '../../errors' @@ -53,8 +54,8 @@ export function getKeypairFromPhrase(mnemonic: string, curve: CryptoCurve): Keyp return keyPair } -/** get uncompressed public key from SEecp256k1 key */ -export function uncompressPublicKey(publicKey: PolkadotPublicKey): string { +/** get uncompressed public key from Ethereum key */ +export function uncompressEthereumPublicKey(publicKey: PolkadotPublicKey): string { // if already decompressed an not has trailing 04 const cleanedPublicKey = removeHexPrefix(publicKey) const testBuffer = Buffer.from(cleanedPublicKey, 'hex') @@ -104,6 +105,37 @@ export function decryptWithPassword( throw new Error(`Curve not supported ${curve}`) } +/** uncompress public key based on keypairType */ +export function uncompressPublicKey( + publicKey: PolkadotPublicKey, + keypairType: PolkadotKeyPairType, +): { + curveType: Asymmetric.EciesCurveType + publicKeyUncompressed: PublicKey + scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME +} { + let scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME + let curveType: Asymmetric.EciesCurveType + let publicKeyUncompressed + if (keypairType === PolkadotKeyPairType.Ecdsa) { + // TODO: confirm that ecdsa is uncompressed, might be same as Ethereum + publicKeyUncompressed = publicKey + } else if (keypairType === PolkadotKeyPairType.Ethereum) { + publicKeyUncompressed = uncompressEthereumPublicKey(publicKey) + curveType = Asymmetric.EciesCurveType.Secp256k1 + scheme = POLKADOT_ASYMMETRIC_SCHEME_NAME.Secp256k1 + } else if (keypairType === PolkadotKeyPairType.Ed25519) { + publicKeyUncompressed = publicKey + curveType = Asymmetric.EciesCurveType.Ed25519 + scheme = POLKADOT_ASYMMETRIC_SCHEME_NAME.Ed25519 + } else if (keypairType === PolkadotKeyPairType.Sr25519) { + // TODO: add + } else { + notSupported(`uncompressPublicKey keypairType: ${keypairType}`) + } + return { curveType, publicKeyUncompressed, scheme } +} + /** Encrypts a string using a public key into a stringified JSON object * The encrypted result can be decrypted with the matching private key */ export async function encryptWithPublicKey( @@ -112,33 +144,18 @@ export async function encryptWithPublicKey( keypairType: PolkadotKeyPairType, options: Asymmetric.EciesOptions, ): Promise { - // TODO: Define Src25519 curve - const curve = getCurveFromKeyType(keypairType) - let publicKeyUncompressed = '' - let useOptions = { ...options } - if (curve === CryptoCurve.Secp256k1) { - publicKeyUncompressed = uncompressPublicKey(publicKey) - useOptions = { - ...useOptions, - curveType: Asymmetric.EciesCurveType.Secp256k1, - scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME.Secp256k1, - } - } else if (curve === CryptoCurve.Ed25519) { - publicKeyUncompressed = publicKey - useOptions = { - ...useOptions, - curveType: Asymmetric.EciesCurveType.Ed25519, - scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME.Ed25519, - } - } else { - // if no curve, throw an error - not supported curve - throw new Error(`Curve not supported ${curve}`) + const { curveType, publicKeyUncompressed, scheme } = uncompressPublicKey(publicKey, keypairType) + const useOptions = { + ...options, + curveType, + scheme, } - const response = Asymmetric.encryptWithPublicKey(publicKeyUncompressed, unencrypted, useOptions) return Asymmetric.toAsymEncryptedDataString(JSON.stringify(response)) } +// TODO: Refactor - Tray - reuse functions across chains + /** Decrypts the encrypted value using a private key * The encrypted value is a stringified JSON object * ... and must have been encrypted with the public key that matches the private ley provided */ @@ -148,7 +165,7 @@ export async function decryptWithPrivateKey( keypairType: PolkadotKeyPairType, options: Asymmetric.EciesOptions, ): Promise { - const curve = getCurveFromKeyType(keypairType) + const curve = getCurveFromKeyType(keypairType) // TODO: Should be keypairtype not curve let useOptions = { ...options } let privateKeyConverted = '' if (curve === CryptoCurve.Secp256k1) { @@ -208,18 +225,13 @@ export async function decryptWithPrivateKey( // return toEthereumSignature(ecsign(dataBuffer, keyBuffer)) // } -/** Generates and returns a new public/private key pair - * Note: Reference - createFromUri from @polkadot/keyring - * https://github.com/polkadot-js/common/blob/master/packages/keyring/src/keyring.ts#L197 - */ -export async function generateKeyPair( +/** Derive a seed from a mnemoic (and optional derivation path) */ +function generateSeedFromMnemonic( keypairType: PolkadotKeyPairType, - mnemonic?: string, + mnemonic: string, derivationPath?: string, -): Promise { - const curve = getCurveFromKeyType(keypairType) - const overridePhrase = mnemonic || generateNewAccountPhrase() - const suri = derivationPath !== undefined ? `${overridePhrase}//${derivationPath}` : overridePhrase +): { seed: Uint8Array; path: DeriveJunction[] } { + const suri = derivationPath !== undefined ? `${mnemonic}//${derivationPath}` : mnemonic const { password, path, phrase } = keyExtractSuri(suri) let seed: Uint8Array if (isHex(phrase, 256)) { @@ -236,15 +248,28 @@ export async function generateKeyPair( throw new Error('Specified phrase is not a valild mnemonic and is invalid as a raw seed at > 32 bytes') } } + return { seed, path } +} - // TODO: need to support ethereum type +/** Generates and returns a new public/private key pair + * Supports optional key gen from mnemonic phase + * Note: Reference - createFromUri from @polkadot/keyring + * https://github.com/polkadot-js/common/blob/master/packages/keyring/src/keyring.ts#L197 + */ +export async function generateKeyPair( + keypairType: PolkadotKeyPairType, + mnemonic?: string, + derivationPath?: string, +): Promise { + const curve = getCurveFromKeyType(keypairType) + const overrideMnemonic = mnemonic || generateNewAccountPhrase() + const { seed, path } = generateSeedFromMnemonic(keypairType, overrideMnemonic, derivationPath) const derivedKeypair = keyFromPath(generateKeypairFromSeed(seed, curve), path, keypairType) const keypair: PolkadotKeypair = { type: keypairType, publicKey: toPolkadotPublicKey(byteArrayToHexString(derivedKeypair.publicKey)), privateKey: toPolkadotPrivateKey(byteArrayToHexString(derivedKeypair.secretKey)), } - return keypair } @@ -279,9 +304,6 @@ export async function generateNewAccountKeysAndEncryptPrivateKeys( return encryptedKeys } -// export async function generateNewAccountKeysAndEncryptPrivateKeys(password: string, options: AlgorandNewKeysOptions) { -// } - // export function determineCurveFromAddress() {} // export function determineCurveFromKeyPair() { From 321bd26d223ee5739aa1fc89d127a5315b2a08db Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Tue, 9 Mar 2021 09:11:25 -0800 Subject: [PATCH 19/31] polka - WIP account example --- src/chains/polkadot_1/examples/account.ts | 44 ++++++++++++++++------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/chains/polkadot_1/examples/account.ts b/src/chains/polkadot_1/examples/account.ts index 07211be3..153406e0 100644 --- a/src/chains/polkadot_1/examples/account.ts +++ b/src/chains/polkadot_1/examples/account.ts @@ -1,33 +1,51 @@ +import { createSecureContext } from 'tls' import { ChainFactory, ChainType } from '../../../index' import { PolkadotChainEndpoint } from '../models' require('dotenv').config() -const { env } = process - const westendEndpoints: PolkadotChainEndpoint[] = [ { - url: 'wss://westend-rpc.polkadot.io' - } + url: 'wss://westend-rpc.polkadot.io', + }, ] const createAccountOptions = { + // ... } -;(async () => { + +async function createAccount(paraChain: Chain) { try { - const westend = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints) - await westend.connect() - const createAccount = westend.new.CreateAccount(createAccountOptions) + await paraChain.connect() + const createAccount = paraChain.new.CreateAccount(createAccountOptions) await createAccount.generateKeysIfNeeded() console.log('generatedKeys:', createAccount.generatedKeys) console.log('address:', createAccount.accountName) - const account = await westend.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') + const account = await paraChain.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') console.log('account', account) - // const { password, salt } = createAccountOptions.newKeysOptions - // const decryptedPrivateKey = ropsten.decryptWithPassword(createAccount.generatedKeys.privateKey, password, { salt }) - // console.log('decrypted privateKey: ', decryptedPrivateKey) } catch (error) { console.log(error) } +} + +async function run() { + try { + const paraChainA = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints) + const paraChainB = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints) + const accountA = createAccount(paraChainA) + console.log('account', accountA) + const accountB = createAccount(paraChainB) + console.log('account', accountB) + } catch (error) { + console.log(error) + } +} + +;(async () => { + try { + await run() + } catch (error) { + console.log('Error:', error) + } + process.exit() })() -process.exit() \ No newline at end of file From 6b1f82701d4f27698025e2b5cbd33edc5dce9948 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Tue, 9 Mar 2021 09:14:45 -0800 Subject: [PATCH 20/31] polka - update PolkadotNativeInfo members --- src/chains/polkadot_1/models/generalModels.ts | 6 +- src/chains/polkadot_1/polkadotChainState.ts | 484 +++++++++--------- 2 files changed, 241 insertions(+), 249 deletions(-) diff --git a/src/chains/polkadot_1/models/generalModels.ts b/src/chains/polkadot_1/models/generalModels.ts index 7224a7b9..41a7fec4 100644 --- a/src/chains/polkadot_1/models/generalModels.ts +++ b/src/chains/polkadot_1/models/generalModels.ts @@ -46,10 +46,10 @@ export type PolkadotNativeInfo = { chain: string name: string SS58: number - tokenDecimals: Array - tokenSymbol: Array + tokenDecimals: number[] + tokenSymbols: string[] transacitonByteFee: number - meta: any + metadata: any } export type PolkadotSymbol = string & ChainSymbolBrand diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts index 18cbd991..cde17685 100644 --- a/src/chains/polkadot_1/polkadotChainState.ts +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -1,253 +1,245 @@ -import { - PolkadotBlock, - PolkadotBlockNumber, - PolkadotChainEndpoint, - PolkadotChainInfo, - PolkadotChainSettings, - PolkadotAddress, - PolkadotPaymentInfo, - PolkadotAccountBalance, - PolkadotHash, - PolkadotKeyringPair, - DotAmount, -} from './models' -import { trimTrailingChars } from '../../helpers' -import { throwAndLogError, throwNewError } from '../../errors' -import BigNumber from 'bignumber.js' import { ApiPromise, WsProvider, SubmittableResult } from '@polkadot/api' -import { isU8a, u8aToString } from '@polkadot/util'; +import { isU8a, u8aToString } from '@polkadot/util' import { createSubmittable } from '@polkadot/api/submittable' import { SubmittableExtrinsic } from '@polkadot/api/submittable/types' import { SubmittableExtrinsics, SubmittableExtrinsicFunction } from '@polkadot/api/types' import { TypeDef } from '@polkadot/types/types' -import { GenericCall } from '@polkadot/types'; -import { getTypeDef } from '@polkadot/types/create'; + +import { GenericCall } from '@polkadot/types' +import { getTypeDef } from '@polkadot/types/create' +import { + DotAmount, + PolkadotBlock, + PolkadotBlockNumber, + PolkadotChainEndpoint, + PolkadotChainInfo, + PolkadotChainSettings, + PolkadotAddress, + PolkadotPaymentInfo, + PolkadotAccountBalance, + PolkadotHash, + PolkadotKeyringPair, +} from './models' +import { trimTrailingChars } from '../../helpers' +import { throwAndLogError, throwNewError } from '../../errors' +import BigNumber from 'bignumber.js' + export class PolkadotChainState { - private _chainInfo: PolkadotChainInfo - - private _chainSettings: PolkadotChainSettings - - private _endpoints: PolkadotChainEndpoint[] - - private _activeEndpoint: PolkadotChainEndpoint - - private _isConnected: boolean = false - - private _api: ApiPromise - - private _provider: WsProvider - - constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { - this._endpoints = endpoints - this._chainSettings = settings - } - - public get activeEndpoint(): PolkadotChainEndpoint { - return this._activeEndpoint - } - - public get chain(): string { - this.assertIsConnected() - return this._chainInfo?.nativeInfo?.chain.toString() - } - - public get chainInfo(): PolkadotChainInfo { - this.assertIsConnected() - return this._chainInfo - } - - public get isConnected(): boolean { - return this._isConnected - } - - public get endpoints(): PolkadotChainEndpoint[] { - return this._endpoints - } - - public async connect(): Promise { - try { - if (!this._provider) { - const { url, endpoint } = this.selectEndpoint() - this._activeEndpoint = endpoint - this._provider = new WsProvider(url) - } - if (!this._api) { - this._api = await ApiPromise.create({ provider: this._provider }) - } - - await this.getChainInfo() - this._isConnected = true - } catch (error) { - throwAndLogError('Problem connection to api', 'apiConnectFailed', error) - } - } - - public async getChainInfo(): Promise { - try { - // console.log(this.extrinsicSectionOptions()) - console.log(this.extrinsicMethodOptions("xcmHandler")) - // this.buildExtrinsics("xcmHandler", "sudoSendHrmpXcm"); - const [headBlockTime, chain, name, version, lastBlockHead, stateMeta] = await Promise.all([ - this._api.query.timestamp.now(), - this._api.rpc.system.chain(), - this._api.rpc.system.name(), - this._api.rpc.system.version(), - this._api.rpc.chain.getHeader(), - this._api.rpc.state.getMetadata() - ]) - - this._chainInfo = { - headBlockNumber: lastBlockHead.number.toNumber(), - headBlockTime: new Date(headBlockTime.toNumber()), - version: version.toString(), - nativeInfo: { - chain: chain.toString(), - name: name.toString(), - transacitonByteFee: this._api.consts.transactionPayment.transactionByteFee.toNumber(), - tokenDecimals: this._api.registry.chainDecimals, - SS58: this._api.registry.chainSS58, - tokenSymbol: this._api.registry.chainTokens, - meta: JSON.parse(JSON.stringify(stateMeta.toHuman())) - } - } - return this._chainInfo - } catch (error) { - throwAndLogError('error', 'error', error) - } - } - - private selectEndpoint() : { url: string; endpoint: PolkadotChainEndpoint } { - const selectedEndpoint = this.endpoints[0] - const endpointUrl = new URL(selectedEndpoint.url) - return { url: trimTrailingChars(endpointUrl?.href, '/'), endpoint: selectedEndpoint } - } - - public async getBlock(blockNumber: PolkadotBlockNumber): Promise { - try { - const blockHash = await this._api.rpc.chain.getBlockHash(blockNumber) - const block = await this._api.rpc.chain.getBlock(blockHash) - return block.toHuman() - } catch (error) { - throw error - } - } - - public async fetchBalance(address: PolkadotAddress): Promise<{ balance: string}> { - const balanceInfo = await this.getBalance(isU8a(address) ? u8aToString(address) : address) - const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) - return { balance: balance.toString() } - } - - public async getBalance(address: string): Promise { - try { - const { data } = await this._api.query.system.account(address) - - return data - } catch (error) { - throw error - } - } - - public async estimateTxFee(sender: PolkadotKeyringPair, extrinsics: SubmittableExtrinsic<"promise">): Promise { - try { - const info = await extrinsics.paymentInfo(sender) - - const paymentInfo: PolkadotPaymentInfo = { - weight: info.weight, - partialFee: info.partialFee - } - - return paymentInfo - } catch (error) { - throw error - } - } - // public async estimateTxFee(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { - // try { - // const info = await this._api.tx.balances.transfer(receiver, amount) - // .paymentInfo(sender) - - // const paymentInfo: PolkadotPaymentInfo = { - // weight: info.weight, - // partialFee: info.partialFee - // } - - // return paymentInfo - // } catch (error) { - // throw error - // } - // } - - public async sendTransaction(sender: PolkadotKeyringPair, extrinsics: SubmittableExtrinsic<"promise">): Promise { - try{ - const txHash = await extrinsics.signAndSend(sender) - return txHash - } catch (error) { - throw error - } - } - - // public async sendTransaction(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { - // try{ - // const tx = await this._api.tx.balances.transfer(receiver, amount) - // const paymentInfo = await tx.paymentInfo(sender) - // const fee = paymentInfo.partialFee.toString() - // const balanceInfo = await this.getBalance(sender.address) - // const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) - - // const bnFee = new BigNumber(fee, this._chainInfo.nativeInfo.tokenDecimals[0]) - // const bnAmount = new BigNumber(amount.toString(), this._chainInfo.nativeInfo.tokenDecimals[0]) - // const bnBalance = new BigNumber(balance.toString(), this._chainInfo.nativeInfo.tokenDecimals[0]) - - // if (bnFee.plus(bnAmount).isGreaterThan(bnBalance) ) { - // throw new Error('Insufficient balance') - // } - - // const txHash = await tx.signAndSend(sender) - // return txHash - // } catch (error) { - // throw error - // } - // } - - public async submitExtrinsics() { - // Submittable createSubmittable() - - } - - public buildExtrinsics(section: string, method: string) { - const sectionOptions = this.extrinsicSectionOptions() - if (!sectionOptions.filter(option => option === section).length) { - throwNewError("Not available Extrinsic Section Option") - } - const methodOptions = this.extrinsicMethodOptions(section) - if (!methodOptions.filter(option => option === method).length) { - throwNewError("Not available Extrinsic Method Option") - } - let fn: SubmittableExtrinsicFunction<"promise"> = this._api.tx[section][method] - console.log( this.getParams(fn) ) - } - - public extrinsicSectionOptions(): string[] { - return Object.keys(this._api.tx).sort().filter((name): number => Object.keys(this._api.tx[name]).length).map((name): string => name) - } - - public extrinsicMethodOptions(sectionName: string): string[] { - const section = this._api.tx[sectionName]; - return Object.keys(section).sort().map((name): string => name) - } - - public getParams({meta}: SubmittableExtrinsicFunction<"promise">): {name: string; type: TypeDef} [] { - return GenericCall.filterOrigin(meta).map((arg): {name: string; type: TypeDef} => ({ - name: arg.name.toString(), - type: getTypeDef(arg.type.toString()) - })) - } - - public assertIsConnected(): void { - if (!this._isConnected) { - throwNewError('Not connected to chain') - } - } -} \ No newline at end of file + private _chainInfo: PolkadotChainInfo + + private _chainSettings: PolkadotChainSettings + + private _endpoints: PolkadotChainEndpoint[] + + private _activeEndpoint: PolkadotChainEndpoint + + private _isConnected: boolean = false + + private _api: ApiPromise + + private _provider: WsProvider + + constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { + this._endpoints = endpoints + this._chainSettings = settings + } + + public get activeEndpoint(): PolkadotChainEndpoint { + return this._activeEndpoint + } + + public get chain(): string { + this.assertIsConnected() + return this._chainInfo?.nativeInfo?.chain.toString() + } + + public get chainInfo(): PolkadotChainInfo { + this.assertIsConnected() + return this._chainInfo + } + + public get isConnected(): boolean { + return this._isConnected + } + + public get endpoints(): PolkadotChainEndpoint[] { + return this._endpoints + } + + public async connect(): Promise { + try { + if (!this._provider) { + const { url, endpoint } = this.selectEndpoint() + this._activeEndpoint = endpoint + this._provider = new WsProvider(url) + } + if (!this._api) { + this._api = await ApiPromise.create({ provider: this._provider }) + } + + await this.getChainInfo() + this._isConnected = true + } catch (error) { + throwAndLogError('Problem connection to api', 'apiConnectFailed', error) + } + } + + public async getChainInfo(): Promise { + try { + const [headBlockTime, chain, name, version, lastBlockHead, metadata] = await Promise.all([ + this._api.query.timestamp.now(), + this._api.rpc.system.chain(), + this._api.rpc.system.name(), + this._api.rpc.system.version(), + this._api.rpc.chain.getHeader(), + this._api.rpc.state.getMetadata(), + ]) + this._chainInfo = { + headBlockNumber: lastBlockHead.number.toNumber(), + headBlockTime: new Date(headBlockTime.toNumber()), + version: version.toString(), + nativeInfo: { + chain: chain.toString(), + name: name.toString(), + transacitonByteFee: this._api.consts.transactionPayment.transactionByteFee.toNumber(), + tokenDecimals: this._api.registry.chainDecimals, + SS58: this._api.registry.chainSS58, + tokenSymbols: this._api.registry.chainTokens, + metadata: metadata.toHuman(), + }, + } + } catch (error) { + throwAndLogError('error', 'error', error) + } + return this._chainInfo + } + + private selectEndpoint(): { url: string; endpoint: PolkadotChainEndpoint } { + const selectedEndpoint = this.endpoints[0] + const endpointUrl = new URL(selectedEndpoint.url) + return { url: trimTrailingChars(endpointUrl?.href, '/'), endpoint: selectedEndpoint } + } + + public async getBlock(blockNumber: PolkadotBlockNumber): Promise { + const blockHash = await this._api.rpc.chain.getBlockHash(blockNumber) + const block = await this._api.rpc.chain.getBlock(blockHash) + return block.toHuman() + } + + public async fetchBalance(address: PolkadotAddress): Promise<{ balance: string }> { + const balanceInfo = await this.getBalance(isU8a(address) ? u8aToString(address) : address) + const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) + return { balance: balance.toString() } + } + + public async getBalance(address: string): Promise { + const { data } = await this._api.query.system.account(address) + + return data + } + + public async estimateTxFee( + sender: PolkadotKeyringPair, + extrinsics: SubmittableExtrinsic<'promise'>, + ): Promise { + const info = await extrinsics.paymentInfo(sender) + + const paymentInfo: PolkadotPaymentInfo = { + weight: info.weight, + partialFee: info.partialFee, + } + + return paymentInfo + } + // public async estimateTxFee(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + // try { + // const info = await this._api.tx.balances.transfer(receiver, amount) + // .paymentInfo(sender) + + // const paymentInfo: PolkadotPaymentInfo = { + // weight: info.weight, + // partialFee: info.partialFee + // } + + // return paymentInfo + // } catch (error) { + // throw error + // } + // } + + public async sendTransaction( + sender: PolkadotKeyringPair, + extrinsics: SubmittableExtrinsic<'promise'>, + ): Promise { + const txHash = await extrinsics.signAndSend(sender) + return txHash + } + + // public async sendTransaction(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { + // try{ + // const tx = await this._api.tx.balances.transfer(receiver, amount) + // const paymentInfo = await tx.paymentInfo(sender) + // const fee = paymentInfo.partialFee.toString() + // const balanceInfo = await this.getBalance(sender.address) + // const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) + + // const bnFee = new BigNumber(fee, this._chainInfo.nativeInfo.tokenDecimals[0]) + // const bnAmount = new BigNumber(amount.toString(), this._chainInfo.nativeInfo.tokenDecimals[0]) + // const bnBalance = new BigNumber(balance.toString(), this._chainInfo.nativeInfo.tokenDecimals[0]) + + // if (bnFee.plus(bnAmount).isGreaterThan(bnBalance) ) { + // throw new Error('Insufficient balance') + // } + + // const txHash = await tx.signAndSend(sender) + // return txHash + // } catch (error) { + // throw error + // } + // } + + public async submitExtrinsics() { + // Submittable createSubmittable() + } + + public buildExtrinsics(section: string, method: string) { + const sectionOptions = this.extrinsicSectionOptions() + if (!sectionOptions.filter(option => option === section).length) { + throwNewError('Not available Extrinsic Section Option') + } + const methodOptions = this.extrinsicMethodOptions(section) + if (!methodOptions.filter(option => option === method).length) { + throwNewError('Not available Extrinsic Method Option') + } + const fn: SubmittableExtrinsicFunction<'promise'> = this._api.tx[section][method] + console.log(this.getParams(fn)) + } + + public extrinsicSectionOptions(): string[] { + return Object.keys(this._api.tx) + .sort() + .filter((name): number => Object.keys(this._api.tx[name]).length) + .map((name): string => name) + } + + public extrinsicMethodOptions(sectionName: string): string[] { + const section = this._api.tx[sectionName] + return Object.keys(section) + .sort() + .map((name): string => name) + } + + public getParams({ meta }: SubmittableExtrinsicFunction<'promise'>): { name: string; type: TypeDef }[] { + return GenericCall.filterOrigin(meta).map((arg): { name: string; type: TypeDef } => ({ + name: arg.name.toString(), + type: getTypeDef(arg.type.toString()), + })) + } + + public assertIsConnected(): void { + if (!this._isConnected) { + throwNewError('Not connected to chain') + } + } +} From 570246f52723fc620b984c999e9aea725eea2974 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Thu, 11 Mar 2021 00:02:26 +0900 Subject: [PATCH 21/31] polka - WIP adding crypto.ts example --- src/chains/polkadot_1/ChainPolkadotV1.ts | 99 ++++++++++++++---------- src/chains/polkadot_1/examples/crypto.ts | 59 ++++++++++++++ 2 files changed, 116 insertions(+), 42 deletions(-) create mode 100644 src/chains/polkadot_1/examples/crypto.ts diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts index 74b0acbf..0af52d90 100644 --- a/src/chains/polkadot_1/ChainPolkadotV1.ts +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -1,33 +1,22 @@ -import { - ChainActionType, - ChainInfo, - ChainType, - CryptoCurve, - ChainEntityName, - ChainDate, -} from '../../models' -import { - ChainError, -} from '../../errors' +import { ChainActionType, ChainInfo, ChainType, CryptoCurve, ChainEntityName, ChainDate } from '../../models' +import { ChainError } from '../../errors' import { Chain } from '../../interfaces' -import { - PolkadotChainEndpoint, - PolkadotChainSettings, - PolkadotNewKeysOptions, - PolkadotSymbol, - PolkadotAddress, +import { + PolkadotChainEndpoint, + PolkadotChainSettings, + PolkadotSymbol, + PolkadotAddress, + PolkadotDecomposeReturn, + PolkadotPublicKey, } from './models' import { PolkadotChainState } from './polkadotChainState' import { notImplemented } from '../../helpers' import { PolkadotChainActionType } from './models/chainActionType' -import { - - PolkadotTransactionAction, -} from './models/transactionModels' -import { PolkadotDecomposeReturn } from './models/PolkadotStructures' +import { PolkadotTransactionAction } from './models/transactionModels' import { PolkadotAccount } from './polkadotAccount' import { PolkadotTransaction } from './polkadotTransaction' +import * as polkadotCrypto from './polkadotCrypto' import * as ethcrypto from '../ethereum_1/ethCrypto' import { Asymmetric } from '../../crypto' import { @@ -40,10 +29,10 @@ import { toEthereumPrivateKey, toEthereumSignature, } from '../ethereum_1/helpers' -import { PolkadotPublicKey } from './models' -import { SignedBlock } from '@polkadot/types/interfaces/runtime' import { PolkadotCreateAccount } from './polkadotCreateAccount' import { PolkadotCreateAccountOptions } from './models/accountModels' +import { composeAction } from './polkadotCompose' +import { decomposeAction } from './polkadotDecompose' class ChainPolkadotV1 implements Chain { private _endpoints: PolkadotChainEndpoint[] @@ -72,7 +61,7 @@ class ChainPolkadotV1 implements Chain { return ChainType.PolkadotV1 } - public get chainId(): string { + public get chainId(): string { return this._chainState.chain } @@ -84,15 +73,11 @@ class ChainPolkadotV1 implements Chain { actionType: ChainActionType | PolkadotChainActionType, args: any, ): Promise => { - notImplemented() - return null + return composeAction(this._chainState, actionType, args) } - public decomposeAction = async ( - action: PolkadotTransactionAction - ): Promise => { - notImplemented() - return null + public decomposeAction = async (action: PolkadotTransactionAction): Promise => { + return decomposeAction(action) } public get description(): string { @@ -108,19 +93,29 @@ class ChainPolkadotV1 implements Chain { symbol: PolkadotSymbol, tokenAddress?: PolkadotAddress, ): Promise<{ balance: string }> { - return this._chainState.fetchBalance(account) + return this._chainState.fetchBalance(account, symbol, tokenAddress) } public fetchContractData = ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars contract: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars table: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars owner: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars indexNumber: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars lowerRow: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars upperRow: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars limit: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars reverseOrder: boolean, + // eslint-disable-next-line @typescript-eslint/no-unused-vars showPayer: boolean, + // eslint-disable-next-line @typescript-eslint/no-unused-vars keyType: string, ): Promise => { return null @@ -139,7 +134,7 @@ class ChainPolkadotV1 implements Chain { this.assertIsConnected() return new PolkadotCreateAccount(this._chainState, options) } - + private newTransaction = (options?: any): PolkadotTransaction => { this.assertIsConnected() return new PolkadotTransaction(this._chainState, options) @@ -151,11 +146,13 @@ class ChainPolkadotV1 implements Chain { Transaction: this.newTransaction, } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public isValidEntityName = (value: string): boolean => { notImplemented() return false } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public isValidDate = (value: string): boolean => { notImplemented() return false @@ -165,10 +162,11 @@ class ChainPolkadotV1 implements Chain { return toEthereumEntityName(value) as ChainEntityName } - public toDate = (value: string | Date ): ChainDate => { + public toDate = (value: string | Date): ChainDate => { return toEthereumDate(value) as ChainDate } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public setPublicKey = (publicKey: PolkadotPublicKey) => { notImplemented() return '' @@ -187,28 +185,45 @@ class ChainPolkadotV1 implements Chain { cryptoCurve: CryptoCurve.Ed25519 - decryptWithPassword = ethcrypto.decryptWithPassword - encryptWithPassword = ethcrypto.encryptWithPassword - decryptWithPrivateKey = ethcrypto.decryptWithPrivateKey - encryptWithPublicKey = ethcrypto.encryptWithPublicKey + decryptWithPassword = polkadotCrypto.decryptWithPassword + + encryptWithPassword = polkadotCrypto.encryptWithPassword + + decryptWithPrivateKey = polkadotCrypto.decryptWithPrivateKey + + encryptWithPublicKey = polkadotCrypto.encryptWithPublicKey + + generateKeyPair = polkadotCrypto.generateKeyPair + decryptWithPrivateKeys = ethcrypto.decryptWithPrivateKeys + encryptWithPublicKeys = ethcrypto.encryptWithPublicKeys + getPublicKeyFromSignature = ethcrypto.getEthereumPublicKeyFromSignature - generateKeyPair = ethcrypto.generateKeyPair + isSymEncryptedDataString = ethcrypto.isSymEncryptedDataString + isAsymEncryptedDataString = Asymmetric.isAsymEncryptedDataString + toAsymEncryptedDataString = Asymmetric.toAsymEncryptedDataString + toSymEncryptedDataString = ethcrypto.toSymEncryptedDataString + toPublicKey = toEthereumPublicKey + toPrivateKey = toEthereumPrivateKey + toSignature = toEthereumSignature - + sign = ethcrypto.sign + verifySignedWithPublicKey = ethcrypto.verifySignedWithPublicKey isValidPrivateKey = isValidEthereumPrivateKey + isValidPublicKey = isValidEthereumPublicKey + isValidEthereumDate = isValidEthereumDateString } -export { ChainPolkadotV1 } \ No newline at end of file +export { ChainPolkadotV1 } diff --git a/src/chains/polkadot_1/examples/crypto.ts b/src/chains/polkadot_1/examples/crypto.ts new file mode 100644 index 00000000..ce8ed818 --- /dev/null +++ b/src/chains/polkadot_1/examples/crypto.ts @@ -0,0 +1,59 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable max-len */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-console */ +import { ChainFactory, ChainType } from '../../../index' +import { toPolkadotPrivateKey, toPolkadotPublicKey } from '../helpers' + +require('dotenv').config() + +const { env } = process + +const algoApiKey = env.AGLORAND_API_KEY || 'missing api key' +const polkadotMainnetEndpoints = [{ + url: 'wss://rpc.polkadot.io', +}] + +async function run() { + /** Create Algorand chain instance */ + const para = new ChainFactory().create(ChainType.PolkadotV1, polkadotMainnetEndpoints) + await para.connect() + if (para.isConnected) { + console.log('Connected to %o', para.chainId) + } + + console.log('keyPair:', await para.generateKeyPair()) + + const toEncrypt = 'text to encrypt' + const encryptedBlob = await para.encryptWithPassword(toEncrypt, 'mypassword', { + salt: 'mysalt', + }) + console.log('encrypted blob:', encryptedBlob) + + const decryptedPayload = await para.decryptWithPassword(encryptedBlob, 'mypassword', { + salt: 'mysalt', + }) + console.log('decrypted payload:', decryptedPayload) + + const publicKey1 = toPolkadotPublicKey( + '0x2e438c99bd7ded27ed921919e1d5ee1d9b1528bb8a2f6c974362ad1a9ba7a6f59a452a0e4dfbc178ab5c5c090506bd7f0a6659fd3cf0cc769d6c17216d414163', + ) + + const privateKey1 = toPolkadotPrivateKey('0x7b0c4bdbc24fd7b6045e9001dbe93f1e46478dedcfcefbc42180ac79fd08ce28') + + const encrypted2 = await para.encryptWithPublicKey('text to encrypt 2', publicKey1) + console.log('encrypted text 2:', encrypted2) + const decrypted2 = await para.decryptWithPrivateKey(encrypted2, privateKey1) + console.log('decrypted text 2:', decrypted2) +} + +;(async () => { + try { + await run() + } catch (error) { + console.log('Error:', error) + } + process.exit() +})() From c592b4fd52cb72d24a7f96f4a3ce26deaba691c2 Mon Sep 17 00:00:00 2001 From: ryuheimat Date: Thu, 11 Mar 2021 00:03:01 +0900 Subject: [PATCH 22/31] polka - WIP clean up --- package.json | 2 + src/chains/polkadot_1/ChainPolkadotV1 copy.ts | 214 ------------------ src/chains/polkadot_1/examples/account.ts | 10 +- src/chains/polkadot_1/examples/parachain.ts | 22 +- src/chains/polkadot_1/examples/transaction.ts | 23 +- .../polkadot_1/helpers/cryptoModelHelpers.ts | 11 +- src/chains/polkadot_1/models/accountModels.ts | 2 +- .../polkadot_1/models/chainActionType.ts | 4 +- src/chains/polkadot_1/models/generalModels.ts | 2 - src/chains/polkadot_1/models/index.ts | 1 + src/chains/polkadot_1/polkadotAccount.ts | 29 ++- src/chains/polkadot_1/polkadotAction.ts | 5 +- src/chains/polkadot_1/polkadotChainState.ts | 139 ++++++------ .../polkadot_1/polkadotChainState_http.ts | 157 ------------- src/chains/polkadot_1/polkadotCompose.ts | 80 +++++++ src/chains/polkadot_1/polkadotConstants.ts | 2 +- .../polkadot_1/polkadotCreateAccount.ts | 73 +++--- src/chains/polkadot_1/polkadotCrypto.ts | 48 ++-- src/chains/polkadot_1/polkadotDecompose.ts | 69 ++++++ src/chains/polkadot_1/polkadotTransaction.ts | 22 +- 20 files changed, 355 insertions(+), 560 deletions(-) delete mode 100644 src/chains/polkadot_1/ChainPolkadotV1 copy.ts delete mode 100644 src/chains/polkadot_1/polkadotChainState_http.ts create mode 100644 src/chains/polkadot_1/polkadotCompose.ts create mode 100644 src/chains/polkadot_1/polkadotDecompose.ts diff --git a/package.json b/package.json index 215b6d0d..de627ab4 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ "dependencies": { "@aikon/sjcl": "^0.1.8", "@polkadot/api": "^3.6.4", + "@polkadot/keyring": "^6.0.5", + "@polkadot/types": "^4.0.3", "@polkadot/util": "^5.9.2", "@polkadot/util-crypto": "^5.9.2", "@types/bignumber.js": "^5.0.0", diff --git a/src/chains/polkadot_1/ChainPolkadotV1 copy.ts b/src/chains/polkadot_1/ChainPolkadotV1 copy.ts deleted file mode 100644 index 74b0acbf..00000000 --- a/src/chains/polkadot_1/ChainPolkadotV1 copy.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { - ChainActionType, - ChainInfo, - ChainType, - CryptoCurve, - ChainEntityName, - ChainDate, -} from '../../models' -import { - ChainError, -} from '../../errors' -import { Chain } from '../../interfaces' -import { - PolkadotChainEndpoint, - PolkadotChainSettings, - PolkadotNewKeysOptions, - PolkadotSymbol, - PolkadotAddress, -} from './models' -import { PolkadotChainState } from './polkadotChainState' -import { notImplemented } from '../../helpers' -import { PolkadotChainActionType } from './models/chainActionType' -import { - - PolkadotTransactionAction, -} from './models/transactionModels' -import { PolkadotDecomposeReturn } from './models/PolkadotStructures' -import { PolkadotAccount } from './polkadotAccount' -import { PolkadotTransaction } from './polkadotTransaction' - -import * as ethcrypto from '../ethereum_1/ethCrypto' -import { Asymmetric } from '../../crypto' -import { - isValidEthereumPrivateKey, - isValidEthereumPublicKey, - isValidEthereumDateString, - toEthereumEntityName, - toEthereumDate, - toEthereumPublicKey, - toEthereumPrivateKey, - toEthereumSignature, -} from '../ethereum_1/helpers' -import { PolkadotPublicKey } from './models' -import { SignedBlock } from '@polkadot/types/interfaces/runtime' -import { PolkadotCreateAccount } from './polkadotCreateAccount' -import { PolkadotCreateAccountOptions } from './models/accountModels' - -class ChainPolkadotV1 implements Chain { - private _endpoints: PolkadotChainEndpoint[] - - private _settings: PolkadotChainSettings - - private _chainState: PolkadotChainState - - constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { - this._endpoints = endpoints - this._settings = settings - this._chainState = new PolkadotChainState(endpoints, settings) - } - - public get isConnected(): boolean { - return this._chainState?.isConnected - } - - /** Connect to chain endpoint to verify that it is operational and to get latest block info */ - public connect(): Promise { - return this._chainState.connect() - } - - /** Returns chain type enum - resolves to chain family as a string e.g. 'polkadot' */ - public get chainType(): ChainType { - return ChainType.PolkadotV1 - } - - public get chainId(): string { - return this._chainState.chain - } - - public get chainInfo(): ChainInfo { - return this._chainState.chainInfo - } - - public composeAction = async ( - actionType: ChainActionType | PolkadotChainActionType, - args: any, - ): Promise => { - notImplemented() - return null - } - - public decomposeAction = async ( - action: PolkadotTransactionAction - ): Promise => { - notImplemented() - return null - } - - public get description(): string { - return 'Polkadot Chain' - } - - public get nativeToken(): { defaultUnit: string; symbol: PolkadotSymbol; tokenAddress: any } { - return null - } - - public async fetchBalance( - account: PolkadotAddress, - symbol: PolkadotSymbol, - tokenAddress?: PolkadotAddress, - ): Promise<{ balance: string }> { - return this._chainState.fetchBalance(account) - } - - public fetchContractData = ( - contract: string, - table: string, - owner: string, - indexNumber: number, - lowerRow: number, - upperRow: number, - limit: number, - reverseOrder: boolean, - showPayer: boolean, - keyType: string, - ): Promise => { - return null - } - - private newAccount = async (address?: PolkadotAddress): Promise => { - this.assertIsConnected() - const account = new PolkadotAccount(this._chainState) - if (address) { - await account.load(address) - } - return account - } - - private newCreateAccount = (options?: PolkadotCreateAccountOptions): any => { - this.assertIsConnected() - return new PolkadotCreateAccount(this._chainState, options) - } - - private newTransaction = (options?: any): PolkadotTransaction => { - this.assertIsConnected() - return new PolkadotTransaction(this._chainState, options) - } - - public new = { - Account: this.newAccount, - CreateAccount: this.newCreateAccount, - Transaction: this.newTransaction, - } - - public isValidEntityName = (value: string): boolean => { - notImplemented() - return false - } - - public isValidDate = (value: string): boolean => { - notImplemented() - return false - } - - public toEntityName = (value: string): ChainEntityName => { - return toEthereumEntityName(value) as ChainEntityName - } - - public toDate = (value: string | Date ): ChainDate => { - return toEthereumDate(value) as ChainDate - } - - public setPublicKey = (publicKey: PolkadotPublicKey) => { - notImplemented() - return '' - } - - public mapChainError = (error: Error): ChainError => { - notImplemented() - return new ChainError(null, null, null, error) - } - - public assertIsConnected(): void { - if (!this._chainState?.isConnected) { - throw new Error('Not connected to chain') - } - } - - cryptoCurve: CryptoCurve.Ed25519 - - decryptWithPassword = ethcrypto.decryptWithPassword - encryptWithPassword = ethcrypto.encryptWithPassword - decryptWithPrivateKey = ethcrypto.decryptWithPrivateKey - encryptWithPublicKey = ethcrypto.encryptWithPublicKey - decryptWithPrivateKeys = ethcrypto.decryptWithPrivateKeys - encryptWithPublicKeys = ethcrypto.encryptWithPublicKeys - getPublicKeyFromSignature = ethcrypto.getEthereumPublicKeyFromSignature - generateKeyPair = ethcrypto.generateKeyPair - isSymEncryptedDataString = ethcrypto.isSymEncryptedDataString - isAsymEncryptedDataString = Asymmetric.isAsymEncryptedDataString - toAsymEncryptedDataString = Asymmetric.toAsymEncryptedDataString - toSymEncryptedDataString = ethcrypto.toSymEncryptedDataString - toPublicKey = toEthereumPublicKey - toPrivateKey = toEthereumPrivateKey - toSignature = toEthereumSignature - - sign = ethcrypto.sign - verifySignedWithPublicKey = ethcrypto.verifySignedWithPublicKey - - isValidPrivateKey = isValidEthereumPrivateKey - isValidPublicKey = isValidEthereumPublicKey - isValidEthereumDate = isValidEthereumDateString -} - -export { ChainPolkadotV1 } \ No newline at end of file diff --git a/src/chains/polkadot_1/examples/account.ts b/src/chains/polkadot_1/examples/account.ts index 153406e0..ebb0bf94 100644 --- a/src/chains/polkadot_1/examples/account.ts +++ b/src/chains/polkadot_1/examples/account.ts @@ -1,4 +1,4 @@ -import { createSecureContext } from 'tls' +import { Chain } from '../../../interfaces' import { ChainFactory, ChainType } from '../../../index' import { PolkadotChainEndpoint } from '../models' @@ -17,10 +17,10 @@ const createAccountOptions = { async function createAccount(paraChain: Chain) { try { await paraChain.connect() - const createAccount = paraChain.new.CreateAccount(createAccountOptions) - await createAccount.generateKeysIfNeeded() - console.log('generatedKeys:', createAccount.generatedKeys) - console.log('address:', createAccount.accountName) + const createdAccount = paraChain.new.CreateAccount(createAccountOptions) + await createdAccount.generateKeysIfNeeded() + console.log('generatedKeys:', createdAccount.generatedKeys) + console.log('address:', createdAccount.accountName) const account = await paraChain.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') console.log('account', account) } catch (error) { diff --git a/src/chains/polkadot_1/examples/parachain.ts b/src/chains/polkadot_1/examples/parachain.ts index 52def57b..75f2bb05 100644 --- a/src/chains/polkadot_1/examples/parachain.ts +++ b/src/chains/polkadot_1/examples/parachain.ts @@ -3,31 +3,29 @@ import { PolkadotChainEndpoint, PolkadotChainSettings } from '../models' require('dotenv').config() -const { env } = process +// const { env } = process /** * Rococo relay-chain in which parachain features are availalbe. One of Polkadot testnet */ const rococoEndpoints: PolkadotChainEndpoint = { - url: 'wss://rococo-rpc.polkadot.io' -}; + url: 'wss://rococo-rpc.polkadot.io', +} /** * One of parachains connected to Rococo network */ const tickEndpoints: PolkadotChainEndpoint = { - url: 'wss://tick-rpc.polkadot.io' + url: 'wss://tick-rpc.polkadot.io', } const tickChainSettings: PolkadotChainSettings = { relayEndpoint: rococoEndpoints, - otherParachains: [] + otherParachains: [], } -const createAccountOptions = { -}; - -(async () => { +const createAccountOptions = {} +;(async () => { try { const chain = new ChainFactory().create(ChainType.PolkadotV1, [tickEndpoints], tickChainSettings) await chain.connect() @@ -37,9 +35,9 @@ const createAccountOptions = { await createAccount.generateKeysIfNeeded() // console.log('generatedKeys:', createAccount.generatedKeys) // console.log('address:', createAccount.accountName) - const account = await chain.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') + // const account = await chain.new.Account('5FkJuxShVBRJRirM3t3k5Y8XyDaxMi1c8hLdBsH2qeAYqAzF') // console.log('account', account) - + // const { password, salt } = createAccountOptions.newKeysOptions // const decryptedPrivateKey = ropsten.decryptWithPassword(createAccount.generatedKeys.privateKey, password, { salt }) // console.log('decrypted privateKey: ', decryptedPrivateKey) @@ -48,4 +46,4 @@ const createAccountOptions = { } catch (error) { console.log(error) } -})() \ No newline at end of file +})() diff --git a/src/chains/polkadot_1/examples/transaction.ts b/src/chains/polkadot_1/examples/transaction.ts index cbfbbcee..cb7c9296 100644 --- a/src/chains/polkadot_1/examples/transaction.ts +++ b/src/chains/polkadot_1/examples/transaction.ts @@ -3,25 +3,24 @@ /* eslint-disable @typescript-eslint/camelcase */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-console */ -import { ChainFactory, ChainType, } from '../../../index' -import { - PolkadotChainEndpoint, PolkadotChainSettings, -} from '../models' +import { ChainFactory, ChainType } from '../../../index' +import { PolkadotChainEndpoint, PolkadotChainSettings } from '../models' + require('dotenv').config() const { env } = process ;(async () => { try { - const westendEndpoints: PolkadotChainEndpoint[] = [ - { - url: 'wss://westend-rpc.polkadot.io' - } - ] + // const westendEndpoints: PolkadotChainEndpoint[] = [ + // { + // url: 'wss://westend-rpc.polkadot.io', + // }, + // ] - const westendChainOptions: PolkadotChainSettings = {} + // const westendChainOptions: PolkadotChainSettings = {} - const ropsten = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints, westendChainOptions) - await ropsten.connect() + // const ropsten = new ChainFactory().create(ChainType.PolkadotV1, westendEndpoints, westendChainOptions) + // await ropsten.connect() // const ropstenEndpoints: EthereumChainEndpoint[] = [ // { diff --git a/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts b/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts index 0b81b214..9d6d1c35 100644 --- a/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts +++ b/src/chains/polkadot_1/helpers/cryptoModelHelpers.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { decodeAddress, encodeAddress } from '@polkadot/keyring' +import { signatureVerify } from '@polkadot/util-crypto' import { ensureHexPrefix, isNullOrEmpty, @@ -16,8 +19,6 @@ import { PolkadotKeyPairType, } from '../models' import { CryptoCurve } from '../../../models' -import { decodeAddress, encodeAddress } from '@polkadot/keyring' -import { signatureVerify } from '@polkadot/util-crypto' import { AesCrypto, Ed25519Crypto } from '../../../crypto' /** determine crypto curve from key type */ @@ -84,7 +85,7 @@ export function isValidPolkadotAddress(value: string | Buffer | PolkadotAddress) return true } -/** Accepts hex string checks if a valid ethereum public key +/** Accepts hex string checks if a valid polkadot public key * Returns PolkadotPublicKey with prefix */ export function toPolkadotPublicKey(value: string): PolkadotPublicKey { @@ -94,10 +95,10 @@ export function toPolkadotPublicKey(value: string): PolkadotPublicKey { throw new Error(`Not a valid polkadot public key:${value}.`) } -/** Accepts hex string checks if a valid ethereum private key +/** Accepts hex string checks if a valid polkadot private key * Returns PolkadotPrivateKey with prefix */ -export function toEthereumPrivateKey(value: string): PolkadotPrivateKey { +export function toPolkadotPrivateKey(value: string): PolkadotPrivateKey { if (isValidPolkadotPrivateKey(value)) { return value as PolkadotPrivateKey } diff --git a/src/chains/polkadot_1/models/accountModels.ts b/src/chains/polkadot_1/models/accountModels.ts index cc467163..8bd72fa5 100644 --- a/src/chains/polkadot_1/models/accountModels.ts +++ b/src/chains/polkadot_1/models/accountModels.ts @@ -1,4 +1,4 @@ -import { PolkadotPublicKey, PolkadotNewKeysOptions } from './' +import { PolkadotPublicKey, PolkadotNewKeysOptions } from '.' export type PolkadotCreateAccountOptions = { publicKey?: PolkadotPublicKey diff --git a/src/chains/polkadot_1/models/chainActionType.ts b/src/chains/polkadot_1/models/chainActionType.ts index fb5efa4b..a14e8d9c 100644 --- a/src/chains/polkadot_1/models/chainActionType.ts +++ b/src/chains/polkadot_1/models/chainActionType.ts @@ -1,3 +1 @@ -export enum PolkadotChainActionType { - -} \ No newline at end of file +export enum PolkadotChainActionType {} diff --git a/src/chains/polkadot_1/models/generalModels.ts b/src/chains/polkadot_1/models/generalModels.ts index 41a7fec4..1b2b401c 100644 --- a/src/chains/polkadot_1/models/generalModels.ts +++ b/src/chains/polkadot_1/models/generalModels.ts @@ -64,8 +64,6 @@ export type PolkadotBlockNumber = BlockNumber | BN | BigInt | Uint8Array | numbe export type PolkadotBlock = Record -export type PolkadotAddress = string | Uint8Array - export type PolkadotKeyringPair = KeyringPair export type DotAmount = Compact | AnyNumber | Uint8Array diff --git a/src/chains/polkadot_1/models/index.ts b/src/chains/polkadot_1/models/index.ts index 71ca45a7..370845d8 100644 --- a/src/chains/polkadot_1/models/index.ts +++ b/src/chains/polkadot_1/models/index.ts @@ -1,2 +1,3 @@ export * from './generalModels' export * from './cryptoModels' +export * from './polkadotStructures' diff --git a/src/chains/polkadot_1/polkadotAccount.ts b/src/chains/polkadot_1/polkadotAccount.ts index b4638af2..159451a1 100644 --- a/src/chains/polkadot_1/polkadotAccount.ts +++ b/src/chains/polkadot_1/polkadotAccount.ts @@ -3,7 +3,6 @@ import { throwNewError } from '../../errors' import { Account } from '../../interfaces' import { PolkadotChainState } from './polkadotChainState' import { PolkadotAddress, PolkadotPublicKey } from './models' -import { getPolkadotAddressFromPublicKey } from './polkadotCrypto' import { isValidPolkadotAddress, isValidPolkadotPublicKey } from './helpers' export class PolkadotAccount implements Account { @@ -16,61 +15,61 @@ export class PolkadotAccount implements Account { constructor(chainState: PolkadotChainState) { this._chainState = chainState } - + /** Whether the account is currently unused and can be reused - not supported in Polkadot */ get canBeRecycled(): boolean { return notSupported('PolkadotAccount.canBeRecycled') } - + /** Polkadot address */ get name(): any { this.assertHasAddress() return this._address } - + /** Public Key(s) associated with the account */ get publicKeys(): any { this.assertHasAddress() return this._publicKey } - + /** Weather the account name can be used for new account */ isValidNewAccountName = async (accountName: PolkadotAddress): Promise => { return isValidPolkadotAddress(accountName) } - - /** Sets the polkadot address + + /** Sets the polkadot address * Public key can only be obtained from a transaction signed by an polkadot address * Only polkadot address is not enough to get public key - */ + */ load = async (address?: PolkadotAddress): Promise => { this.assertValidPolkadotAddress(address) this._address = address } - + /** Sets the polkadot public key and address */ setPublicKey = async (publicKey: PolkadotPublicKey) => { this.assertValidPolkadotPublickey(publicKey) this._publicKey = publicKey - this._address = getPolkadotAddressFromPublicKey(publicKey) + // this._address = getPolkadotAddressFromPublicKey(publicKey) } - + /** Polkadot has no account structure/registry on the chain */ get supportsOnChainAccountRegistry(): boolean { return false } - + /** Polkadot accounts cannot be recycled as the private keys cannot be replaced */ get supportsRecycling(): boolean { return false } - + /** JSON representation of address */ toJson() { this.assertHasAddress() return { address: this._address } } - + /** Returns the address */ get value(): PolkadotAddress { return this._address @@ -93,4 +92,4 @@ export class PolkadotAccount implements Account { throwNewError('Not a valid polkadot public key') } } -} \ No newline at end of file +} diff --git a/src/chains/polkadot_1/polkadotAction.ts b/src/chains/polkadot_1/polkadotAction.ts index 07e5e2d4..9dea0c08 100644 --- a/src/chains/polkadot_1/polkadotAction.ts +++ b/src/chains/polkadot_1/polkadotAction.ts @@ -3,7 +3,4 @@ export type ActionChainOptions = { hardfork: string } -export class PolkadotActionHelper { - constructor(actionInput: PolkadotActionHelper, chainOptions: ActionChainOptions) { - } -} \ No newline at end of file +export class PolkadotActionHelper {} diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts index cde17685..cbedf43d 100644 --- a/src/chains/polkadot_1/polkadotChainState.ts +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -1,14 +1,7 @@ -import { ApiPromise, WsProvider, SubmittableResult } from '@polkadot/api' -import { isU8a, u8aToString } from '@polkadot/util' -import { createSubmittable } from '@polkadot/api/submittable' +import { ApiPromise, WsProvider } from '@polkadot/api' +// import { isU8a, u8aToString } from '@polkadot/util' import { SubmittableExtrinsic } from '@polkadot/api/submittable/types' -import { SubmittableExtrinsics, SubmittableExtrinsicFunction } from '@polkadot/api/types' -import { TypeDef } from '@polkadot/types/types' - -import { GenericCall } from '@polkadot/types' -import { getTypeDef } from '@polkadot/types/create' import { - DotAmount, PolkadotBlock, PolkadotBlockNumber, PolkadotChainEndpoint, @@ -16,14 +9,15 @@ import { PolkadotChainSettings, PolkadotAddress, PolkadotPaymentInfo, + // eslint-disable-next-line @typescript-eslint/no-unused-vars PolkadotAccountBalance, + // eslint-disable-next-line @typescript-eslint/no-unused-vars PolkadotHash, PolkadotKeyringPair, + PolkadotSymbol, } from './models' -import { trimTrailingChars } from '../../helpers' +import { notImplemented, trimTrailingChars } from '../../helpers' import { throwAndLogError, throwNewError } from '../../errors' -import BigNumber from 'bignumber.js' - export class PolkadotChainState { private _chainInfo: PolkadotChainInfo @@ -127,30 +121,43 @@ export class PolkadotChainState { return block.toHuman() } - public async fetchBalance(address: PolkadotAddress): Promise<{ balance: string }> { - const balanceInfo = await this.getBalance(isU8a(address) ? u8aToString(address) : address) - const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) - return { balance: balance.toString() } + public async fetchBalance( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + address: PolkadotAddress, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + symbol: PolkadotSymbol, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + tokenAddress?: PolkadotAddress, + ): Promise<{ balance: string }> { + notImplemented() + return null + // const balanceInfo = await this.getBalance(isU8a(address) ? u8aToString(address) : address) + // const balance = balanceInfo.free.sub(balanceInfo.miscFrozen) + // return { balance: balance.toString() } } - public async getBalance(address: string): Promise { - const { data } = await this._api.query.system.account(address) + // public async getBalance(address: string): Promise { + // const { data } = await this._api.query.system.account(address) - return data - } + // return data + // } public async estimateTxFee( + // eslint-disable-next-line @typescript-eslint/no-unused-vars sender: PolkadotKeyringPair, + // eslint-disable-next-line @typescript-eslint/no-unused-vars extrinsics: SubmittableExtrinsic<'promise'>, ): Promise { - const info = await extrinsics.paymentInfo(sender) + notImplemented() + return null + // const info = await extrinsics.paymentInfo(sender) - const paymentInfo: PolkadotPaymentInfo = { - weight: info.weight, - partialFee: info.partialFee, - } + // const paymentInfo: PolkadotPaymentInfo = { + // weight: info.weight, + // partialFee: info.partialFee, + // } - return paymentInfo + // return paymentInfo } // public async estimateTxFee(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { // try { @@ -168,13 +175,13 @@ export class PolkadotChainState { // } // } - public async sendTransaction( - sender: PolkadotKeyringPair, - extrinsics: SubmittableExtrinsic<'promise'>, - ): Promise { - const txHash = await extrinsics.signAndSend(sender) - return txHash - } + // public async sendTransaction( + // sender: PolkadotKeyringPair, + // extrinsics: SubmittableExtrinsic<'promise'>, + // ): Promise { + // const txHash = await extrinsics.signAndSend(sender) + // return txHash + // } // public async sendTransaction(sender: PolkadotKeyringPair, receiver: PolkadotAddress, amount: DotAmount): Promise { // try{ @@ -199,43 +206,43 @@ export class PolkadotChainState { // } // } - public async submitExtrinsics() { - // Submittable createSubmittable() - } + // public async submitExtrinsics() { + // // Submittable createSubmittable() + // } - public buildExtrinsics(section: string, method: string) { - const sectionOptions = this.extrinsicSectionOptions() - if (!sectionOptions.filter(option => option === section).length) { - throwNewError('Not available Extrinsic Section Option') - } - const methodOptions = this.extrinsicMethodOptions(section) - if (!methodOptions.filter(option => option === method).length) { - throwNewError('Not available Extrinsic Method Option') - } - const fn: SubmittableExtrinsicFunction<'promise'> = this._api.tx[section][method] - console.log(this.getParams(fn)) - } + // public buildExtrinsics(section: string, method: string) { + // const sectionOptions = this.extrinsicSectionOptions() + // if (!sectionOptions.filter(option => option === section).length) { + // throwNewError('Not available Extrinsic Section Option') + // } + // const methodOptions = this.extrinsicMethodOptions(section) + // if (!methodOptions.filter(option => option === method).length) { + // throwNewError('Not available Extrinsic Method Option') + // } + // const fn: SubmittableExtrinsicFunction<'promise'> = this._api.tx[section][method] + // console.log(this.getParams(fn)) + // } - public extrinsicSectionOptions(): string[] { - return Object.keys(this._api.tx) - .sort() - .filter((name): number => Object.keys(this._api.tx[name]).length) - .map((name): string => name) - } + // public extrinsicSectionOptions(): string[] { + // return Object.keys(this._api.tx) + // .sort() + // .filter((name): number => Object.keys(this._api.tx[name]).length) + // .map((name): string => name) + // } - public extrinsicMethodOptions(sectionName: string): string[] { - const section = this._api.tx[sectionName] - return Object.keys(section) - .sort() - .map((name): string => name) - } + // public extrinsicMethodOptions(sectionName: string): string[] { + // const section = this._api.tx[sectionName] + // return Object.keys(section) + // .sort() + // .map((name): string => name) + // } - public getParams({ meta }: SubmittableExtrinsicFunction<'promise'>): { name: string; type: TypeDef }[] { - return GenericCall.filterOrigin(meta).map((arg): { name: string; type: TypeDef } => ({ - name: arg.name.toString(), - type: getTypeDef(arg.type.toString()), - })) - } + // public getParams({ meta }: SubmittableExtrinsicFunction<'promise'>): { name: string; type: TypeDef }[] { + // return GenericCall.filterOrigin(meta).map((arg): { name: string; type: TypeDef } => ({ + // name: arg.name.toString(), + // type: getTypeDef(arg.type.toString()), + // })) + // } public assertIsConnected(): void { if (!this._isConnected) { diff --git a/src/chains/polkadot_1/polkadotChainState_http.ts b/src/chains/polkadot_1/polkadotChainState_http.ts deleted file mode 100644 index ec120743..00000000 --- a/src/chains/polkadot_1/polkadotChainState_http.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { throwAndLogError, throwNewError } from '../../errors' -import { PolkadotBlock, PolkadotBlockNumber, PolkadotChainEndpoint, PolkadotChainInfo, PolkadotChainSettings } from './models' -import { ApiPromise } from '@polkadot/api' -import { HttpProvider } from '@polkadot/rpc-provider' -import { trimTrailingChars } from '../../helpers' -import { AnyTuple } from '@polkadot/types/types' -import { GenericExtrinsic } from '@polkadot/types' - -export class PolkadotChainState { - private _chainInfo: PolkadotChainInfo - - private _chainSettings: PolkadotChainSettings - - private _endpoints: PolkadotChainEndpoint[] - - private _activeEndpoint: PolkadotChainEndpoint - - private _isConnected: boolean = false - - private _isReady: boolean = false - - private _provider: HttpProvider - - constructor(endpoints: PolkadotChainEndpoint[], settings?: PolkadotChainSettings) { - this._endpoints = endpoints - this._chainSettings = settings - } - - public get activeEndpoint(): PolkadotChainEndpoint { - return this._activeEndpoint - } - - public get chain(): string { - this.assertIsConnected() - return this._chainInfo?.nativeInfo?.chain.toString() - } - - public get chainInfo(): PolkadotChainInfo { - this.assertIsConnected() - return this._chainInfo - } - - public get chainSettings(): PolkadotChainSettings { - return this._chainSettings - } - - public get endpoints(): PolkadotChainEndpoint[] { - return this._endpoints - } - - public get isConnected(): boolean { - return this._isConnected - } - - public async connect(): Promise { - try { - if (!this._provider) { - const { url, endpoint } = this.selectEndpoint() - this._activeEndpoint = endpoint - const httpProviderOptions = this.mapOptionsToHttpProviderOption(endpoint) - this._provider = new HttpProvider(url, httpProviderOptions) - } - await this.getChainInfo() - this._isConnected = true - } catch (error) { - throwAndLogError('Problem connecting to chain provider', 'chainProviderConnectFailed', error) - } - } - - public mapOptionsToHttpProviderOption(endpoint: PolkadotChainEndpoint): Record { - const headers : Record = {} - const headerObj = Object(endpoint.options?.headers) - Object.keys(headerObj).forEach(key => { - headers[key] = headerObj[key] - }) - return headers; - } - - public async getChainInfo(): Promise { - try { - const [chain, version, latestBlockHead, latestBlockHash] = await Promise.all([ - this._provider.send('system_chain', []), - this._provider.send('system_version', []), - this._provider.send('chain_getHeader', []), - this._provider.send('chain_getBlockHash', []), - ]) - const res = await this._provider.send('chain_getBlock', [latestBlockHash]) - let api = await this.createApi() - const block = api.createType('SignedBlock', res) - const extrinsics: GenericExtrinsic[] = block.block.extrinsics.toArray() - const timestamp: string = extrinsics.map(extrinsic => { - const typedExt = extrinsic.toHuman() - const jsonExt = JSON.parse(JSON.stringify(typedExt)) - if (jsonExt?.method?.section == 'timestamp') { - return jsonExt.method.args[0] - } - return '' - }).reduce((pV, cV) => pV != '' ? pV : cV, '') - this._chainInfo = { - headBlockNumber: parseInt((latestBlockHead?.number | 0).toString()), - headBlockTime: new Date( parseInt(timestamp.split(',').join('')) ), - version, - nativeInfo: { - chain: chain.toString(), - } - } - this.removeApi(api) - - return this._chainInfo - } catch (error) { - throwAndLogError('error', 'error', error) - } - } - - private createApi(): Promise { - return ApiPromise.create({provider: new HttpProvider('https://rpc.polkadot.io')}) - } - - private async removeApi(api: ApiPromise): Promise { - api?.disconnect() - } - - private selectEndpoint() : { url: string; endpoint: PolkadotChainEndpoint } { - const selectedEndpoint = this.endpoints[0] - const endpointUrl = new URL(selectedEndpoint.url) - return { url: trimTrailingChars(endpointUrl?.href, '/'), endpoint: selectedEndpoint } - } - - public async getBlock(blockNumber: PolkadotBlockNumber): Promise { - try { - this.assertIsConnected() - const blockHash = await this._provider.send('chain_getBlockHash', [blockNumber]) - const block = await this._provider.send('chain_getBlock', [blockHash]) - let api = await this.createApi() - const enBlock = api.createType('SignedBlock', block) - this.removeApi(api) - - return enBlock.toHuman() - } catch (error) { - throw error - } - } - - public async getTransactionCount(): Promise { - - } - - public async fetchBalance(): Promise { - - } - - public assertIsConnected(): void { - if (!this._isConnected) { - throwNewError('Not connected to chain') - } - } -} \ No newline at end of file diff --git a/src/chains/polkadot_1/polkadotCompose.ts b/src/chains/polkadot_1/polkadotCompose.ts new file mode 100644 index 00000000..4f18ae90 --- /dev/null +++ b/src/chains/polkadot_1/polkadotCompose.ts @@ -0,0 +1,80 @@ +import { ChainActionType } from '../../models' +import { notImplemented } from '../../helpers' +import { PolkadotChainState } from './polkadotChainState' +import { PolkadotChainActionType } from './models/chainActionType' +import { PolkadotTransactionAction } from './models/transactionModels' +// import { composeAction as TokenTransferTemplate } from './templates/chainActions/standard/token_transfer' +// import { composeAction as ValueTransferTemplate } from './templates/chainActions/standard/value_transfer' +// import { composeAction as ApplicationClearTemplate } from './templates/chainActions/chainSpecific/application_clear' +// import { composeAction as ApplicationCloseOutTemplate } from './templates/chainActions/chainSpecific/application_closeout' +// import { composeAction as ApplicationCreateTemplate } from './templates/chainActions/chainSpecific/application_create' +// import { composeAction as ApplicationDeleteTemplate } from './templates/chainActions/chainSpecific/application_delete' +// import { composeAction as ApplicationNoOpTemplate } from './templates/chainActions/chainSpecific/application_noOp' +// import { composeAction as ApplicationOptInTemplate } from './templates/chainActions/chainSpecific/application_optIn' +// import { composeAction as ApplicationUpdateTemplate } from './templates/chainActions/chainSpecific/application_update' +// import { composeAction as AssetConfigTemplate } from './templates/chainActions/chainSpecific/asset_config' +// import { composeAction as AssetCreateTemplate } from './templates/chainActions/chainSpecific/asset_create' +// import { composeAction as AssetDestroyTemplate } from './templates/chainActions/chainSpecific/asset_destroy' +// import { composeAction as AssetFreezeTemplate } from './templates/chainActions/chainSpecific/asset_freeze' +// import { composeAction as AssetTransferTemplate } from './templates/chainActions/chainSpecific/asset_transfer' +// import { composeAction as KeyRegistrationTemplate } from './templates/chainActions/chainSpecific/key_registration' +// import { composeAction as PaymentTemplate } from './templates/chainActions/chainSpecific/payment' + +// import { +// AlgorandChainActionType, +// AlgorandChainTransactionParamsStruct, +// AlgorandTxActionSdkEncoded, +// AlgorandTxHeaderParams, +// } from './models' +// import { AlgorandChainState } from './algoChainState' +// import { AlgorandActionHelper } from './algoAction' + +// map a key name to a function that returns an object +// const ComposeAction: { [key: string]: (args: any, suggestedParams: AlgorandTxHeaderParams) => any } = { +// // Standard actions +// TokenTransfer: TokenTransferTemplate, +// ValueTransfer: ValueTransferTemplate, +// // Algorand actions +// AssetConfig: AssetConfigTemplate, +// AssetCreate: AssetCreateTemplate, +// AssetDestroy: AssetDestroyTemplate, +// AssetFreeze: AssetFreezeTemplate, +// AssetTransfer: AssetTransferTemplate, +// AppClear: ApplicationClearTemplate, +// AppCloseOut: ApplicationCloseOutTemplate, +// AppCreate: ApplicationCreateTemplate, +// AppDelete: ApplicationDeleteTemplate, +// AppNoOp: ApplicationNoOpTemplate, +// AppOptIn: ApplicationOptInTemplate, +// AppUpdate: ApplicationUpdateTemplate, +// KeyRegistration: KeyRegistrationTemplate, +// Payment: PaymentTemplate, +// } + +/** Compose an object for a chain contract action */ +export async function composeAction( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + chainState: PolkadotChainState, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + chainActionType: ChainActionType | PolkadotChainActionType, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + args: any, +): Promise { + return notImplemented() + // if (!composerFunction) { + // notSupported(`ComposeAction:${chainActionType}`) + // } + + // let actionHelper = new AlgorandActionHelper(args) + // const chainTxHeaderParams: AlgorandChainTransactionParamsStruct = + // chainState.chainInfo?.nativeInfo?.transactionHeaderParams + // actionHelper.applyCurrentTxHeaderParamsWhereNeeded(chainTxHeaderParams) + // // seperate-out the action param values (required by compose functions) from the suggestedParams (headers) + // const sdkEncodedActionParams: AlgorandTxActionSdkEncoded = composerFunction( + // actionHelper.paramsOnly, + // actionHelper.transactionHeaderParams, + // ) + // // use AlgorandActionHelper to drop empty fields + // actionHelper = new AlgorandActionHelper(sdkEncodedActionParams as AlgorandTxActionSdkEncoded) + // return sdkEncodedActionParams +} diff --git a/src/chains/polkadot_1/polkadotConstants.ts b/src/chains/polkadot_1/polkadotConstants.ts index c611b3c4..17b1f9f6 100644 --- a/src/chains/polkadot_1/polkadotConstants.ts +++ b/src/chains/polkadot_1/polkadotConstants.ts @@ -1,6 +1,6 @@ // TODO: Update values for Polkadot -import { PolkadotKeyPairType } from "./models" +import { PolkadotKeyPairType } from './models' // sign transaction default parameters export const TRANSACTION_ENCODING = 'utf8' diff --git a/src/chains/polkadot_1/polkadotCreateAccount.ts b/src/chains/polkadot_1/polkadotCreateAccount.ts index 351f102c..09a544e8 100644 --- a/src/chains/polkadot_1/polkadotCreateAccount.ts +++ b/src/chains/polkadot_1/polkadotCreateAccount.ts @@ -1,18 +1,18 @@ import { PolkadotCreateAccountOptions } from './models/accountModels' import { PolkadotChainState } from './polkadotChainState' -import { - generateKeyPair, - generateNewAccountPhrase, - getKeypairFromPhrase, - getPolkadotAddressFromPublicKey, -} from './polkadotCrypto' -import { isValidPolkadotPublicKey, toPolkadotEntityName } from './helpers' -import { notSupported } from '../../helpers' +// import { +// generateKeyPair, +// generateNewAccountPhrase, +// getKeypairFromPhrase, +// getPolkadotAddressFromPublicKey, +// } from './polkadotCrypto' +import { isValidPolkadotPublicKey } from './helpers' +import { notImplemented, notSupported } from '../../helpers' import { CreateAccount } from '../../interfaces' import { throwNewError } from '../../errors' -import { PolkadotAddress, PolkadotPublicKey, PolkadotKeypair } from './models' +import { PolkadotAddress, PolkadotKeypair } from './models' import { CryptoCurve } from '../../models' -import { DEFAULT_POLKADOT_KEY_PAIR_TYPE } from './polkadotConstants' +// import { DEFAULT_POLKADOT_KEY_PAIR_TYPE } from './polkadotConstants' /** Helper class to compose a transaction for creating a new chain account * Handles native accounts @@ -104,8 +104,10 @@ export class PolkadotCreateAccount implements CreateAccount { * Updates generatedKeys for the newly generated name (since name/account is derived from publicKey) */ async generateAccountName(): Promise { - const accountName = await this.generateAccountNameString() - return toPolkadotEntityName(accountName) + notImplemented() + return null + // const accountName = await this.generateAccountNameString() + // return toPolkadotEntityName(accountName) } /** Returns a string of the Polkadot Address for the public key provide in options - OR generates a new mnemonic(phrase)/private/public/address */ @@ -118,30 +120,29 @@ export class PolkadotCreateAccount implements CreateAccount { * autogenerate the public and private key pair and add them to options */ async generateKeysIfNeeded() { - let publicKey: PolkadotPublicKey - this.assertValidOptionPublicKeys() - - publicKey = this?._options?.publicKey - if (!publicKey) { - await this.generateAccountKeys() - } - this._accountName = getPolkadotAddressFromPublicKey(this._generatedKeypair.publicKey) - this._accountType = this._options.newKeysOptions.keyPairType - } - - private async generateAccountKeys(): Promise { - const { newKeysOptions } = this._options - const { keyPairType, phrase: overridePhrase, derivationPath } = newKeysOptions || {} - const overrideType = keyPairType || DEFAULT_POLKADOT_KEY_PAIR_TYPE - - // this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) - this._generatedKeypair = await generateKeyPair(keyPairType, phrase, derivationPath) - this._options.publicKey = this._generatedKeypair.publicKey - this._options.newKeysOptions = { - phrase: overridePhrase, - keyPairType: overrideType, - } - } + // let publicKey: PolkadotPublicKey + // this.assertValidOptionPublicKeys() + // publicKey = this?._options?.publicKey + // if (!publicKey) { + // await this.generateAccountKeys() + // } + // this._accountName = getPolkadotAddressFromPublicKey(this._generatedKeypair.publicKey) + // this._accountType = this._options.newKeysOptions.keyPairType + } + + // private async generateAccountKeys(): Promise { + // const { newKeysOptions } = this._options + // const { keyPairType, phrase: overridePhrase, derivationPath } = newKeysOptions || {} + // const overrideType = keyPairType || DEFAULT_POLKADOT_KEY_PAIR_TYPE + + // // this._generatedKeypair = getKeypairFromPhrase(overridePhrase, overrideType) + // this._generatedKeypair = await generateKeyPair(keyPairType, phrase, derivationPath) + // this._options.publicKey = this._generatedKeypair.publicKey + // this._options.newKeysOptions = { + // phrase: overridePhrase, + // keyPairType: overrideType, + // } + // } private assertValidOptionPublicKeys() { const { publicKey } = this._options diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index 5a427cb8..bb903026 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -66,19 +66,27 @@ export function uncompressEthereumPublicKey(publicKey: PolkadotPublicKey): strin return uncompressedPublicKey } +/** get available keypairType */ +export function toValidKeypairType(keypairType: any): PolkadotKeyPairType { + if (keypairType in PolkadotKeyPairType) return keypairType + // TODO: Confirm default keypairType of chain + return PolkadotKeyPairType.Ethereum +} + /** Encrypts a string using a password and optional salt */ export function encryptWithPassword( unencrypted: string, password: string, - keypairType: PolkadotKeyPairType, options: PolkadotEncryptionOptions, + keypairType?: PolkadotKeyPairType, ): AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { // TODO: Define Src25519 curve - const curve = getCurveFromKeyType(keypairType) + const overrideKeypairType = toValidKeypairType(keypairType) + const curve = getCurveFromKeyType(overrideKeypairType) if (curve === CryptoCurve.Ed25519) { const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) const encrypted = Ed25519Crypto.encrypt(unencrypted, passwordKey) - return toSymEncryptedDataString(encrypted, keypairType) + return toSymEncryptedDataString(encrypted, overrideKeypairType) } if (curve === CryptoCurve.Secp256k1) return AesCrypto.encryptWithPassword(unencrypted, password, options) // if no curve, throw an error - curve not supported @@ -90,11 +98,12 @@ export function encryptWithPassword( export function decryptWithPassword( encrypted: AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString | any, password: string, - keypairType: PolkadotKeyPairType, options: PolkadotEncryptionOptions, + keypairType?: PolkadotKeyPairType, ): string { // TODO: Define Src25519 curve - const curve = getCurveFromKeyType(keypairType) + const overrideKeypairType = toValidKeypairType(keypairType) + const curve = getCurveFromKeyType(overrideKeypairType) if (curve === CryptoCurve.Ed25519) { const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) const decrypted = Ed25519Crypto.decrypt(encrypted, passwordKey) @@ -109,11 +118,7 @@ export function decryptWithPassword( export function uncompressPublicKey( publicKey: PolkadotPublicKey, keypairType: PolkadotKeyPairType, -): { - curveType: Asymmetric.EciesCurveType - publicKeyUncompressed: PublicKey - scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME -} { +): { curveType: Asymmetric.EciesCurveType; publicKeyUncompressed: PublicKey; scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME } { let scheme: POLKADOT_ASYMMETRIC_SCHEME_NAME let curveType: Asymmetric.EciesCurveType let publicKeyUncompressed @@ -141,10 +146,11 @@ export function uncompressPublicKey( export async function encryptWithPublicKey( unencrypted: string, publicKey: PolkadotPublicKey, - keypairType: PolkadotKeyPairType, options: Asymmetric.EciesOptions, + keypairType?: PolkadotKeyPairType, ): Promise { - const { curveType, publicKeyUncompressed, scheme } = uncompressPublicKey(publicKey, keypairType) + const overrideKeypairType = toValidKeypairType(keypairType) + const { curveType, publicKeyUncompressed, scheme } = uncompressPublicKey(publicKey, overrideKeypairType) const useOptions = { ...options, curveType, @@ -162,10 +168,11 @@ export async function encryptWithPublicKey( export async function decryptWithPrivateKey( encrypted: Asymmetric.AsymmetricEncryptedDataString | Asymmetric.AsymmetricEncryptedData, privateKey: PolkadotPrivateKey, - keypairType: PolkadotKeyPairType, options: Asymmetric.EciesOptions, + keypairType?: PolkadotKeyPairType, ): Promise { - const curve = getCurveFromKeyType(keypairType) // TODO: Should be keypairtype not curve + const overrideKeypairType = toValidKeypairType(keypairType) + const curve = getCurveFromKeyType(overrideKeypairType) // TODO: Should be keypairtype not curve let useOptions = { ...options } let privateKeyConverted = '' if (curve === CryptoCurve.Secp256k1) { @@ -257,16 +264,17 @@ function generateSeedFromMnemonic( * https://github.com/polkadot-js/common/blob/master/packages/keyring/src/keyring.ts#L197 */ export async function generateKeyPair( - keypairType: PolkadotKeyPairType, + keypairType?: PolkadotKeyPairType, mnemonic?: string, derivationPath?: string, ): Promise { - const curve = getCurveFromKeyType(keypairType) + const overrideKeypairType = toValidKeypairType(keypairType) + const curve = getCurveFromKeyType(overrideKeypairType) const overrideMnemonic = mnemonic || generateNewAccountPhrase() - const { seed, path } = generateSeedFromMnemonic(keypairType, overrideMnemonic, derivationPath) - const derivedKeypair = keyFromPath(generateKeypairFromSeed(seed, curve), path, keypairType) + const { seed, path } = generateSeedFromMnemonic(overrideKeypairType, overrideMnemonic, derivationPath) + const derivedKeypair = keyFromPath(generateKeypairFromSeed(seed, curve), path, overrideKeypairType) const keypair: PolkadotKeypair = { - type: keypairType, + type: overrideKeypairType, publicKey: toPolkadotPublicKey(byteArrayToHexString(derivedKeypair.publicKey)), privateKey: toPolkadotPrivateKey(byteArrayToHexString(derivedKeypair.secretKey)), } @@ -281,7 +289,7 @@ function encryptAccountPrivateKeysIfNeeded( ): PolkadotKeypair { const privateKeyEncrypted = keys.privateKeyEncrypted ? keys.privateKeyEncrypted - : encryptWithPassword(keys.privateKey, password, keys.type, options) + : encryptWithPassword(keys.privateKey, password, options, keys.type) const encryptedKeys: PolkadotKeypair = { type: keys.type, publicKey: keys.publicKey, diff --git a/src/chains/polkadot_1/polkadotDecompose.ts b/src/chains/polkadot_1/polkadotDecompose.ts new file mode 100644 index 00000000..19ba447f --- /dev/null +++ b/src/chains/polkadot_1/polkadotDecompose.ts @@ -0,0 +1,69 @@ +// import { AlgorandTxAction, AlgorandTxActionRaw, AlgorandTxActionSdkEncoded, AlgorandDecomposeReturn } from './models' +// import { isNullOrEmpty } from '../../helpers' +// import { decomposeAction as TokenTransferTemplate } from './templates/chainActions/standard/token_transfer' +// import { decomposeAction as ValueTransferTemplate } from './templates/chainActions/standard/value_transfer' +// import { decomposeAction as ApplicationClearTemplate } from './templates/chainActions/chainSpecific/application_clear' +// import { decomposeAction as ApplicationCloseOutTemplate } from './templates/chainActions/chainSpecific/application_closeout' +// import { decomposeAction as ApplicationCreateTemplate } from './templates/chainActions/chainSpecific/application_create' +// import { decomposeAction as ApplicationDeleteTemplate } from './templates/chainActions/chainSpecific/application_delete' +// import { decomposeAction as ApplicationNoOpTemplate } from './templates/chainActions/chainSpecific/application_noOp' +// import { decomposeAction as ApplicationOptInTemplate } from './templates/chainActions/chainSpecific/application_optIn' +// import { decomposeAction as ApplicationUpdateTemplate } from './templates/chainActions/chainSpecific/application_update' +// import { decomposeAction as AssetConfigTemplate } from './templates/chainActions/chainSpecific/asset_config' +// import { decomposeAction as AssetCreateTemplate } from './templates/chainActions/chainSpecific/asset_create' +// import { decomposeAction as AssetDestroyTemplate } from './templates/chainActions/chainSpecific/asset_destroy' +// import { decomposeAction as AssetFreezeTemplate } from './templates/chainActions/chainSpecific/asset_freeze' +// import { decomposeAction as AssetTransferTemplate } from './templates/chainActions/chainSpecific/asset_transfer' +// import { decomposeAction as KeyRegistrationTemplate } from './templates/chainActions/chainSpecific/key_registration' +// import { decomposeAction as PaymentTemplate } from './templates/chainActions/chainSpecific/payment' + +import { notImplemented } from '../../helpers' +import { PolkadotDecomposeReturn } from './models' +import { PolkadotTransactionAction } from './models/transactionModels' + +// map a key name to a function that returns an object +// const DecomposeAction: { [key: string]: (args: any) => any } = { +// // Standard actions +// TokenTransfer: TokenTransferTemplate, +// ValueTransfer: ValueTransferTemplate, +// // Algorand actions +// AssetConfig: AssetConfigTemplate, +// AssetCreate: AssetCreateTemplate, +// AssetDestroy: AssetDestroyTemplate, +// AssetFreeze: AssetFreezeTemplate, +// AssetTransfer: AssetTransferTemplate, +// AppClear: ApplicationClearTemplate, +// AppCloseOut: ApplicationCloseOutTemplate, +// AppCreate: ApplicationCreateTemplate, +// AppDelete: ApplicationDeleteTemplate, +// AppNoOp: ApplicationNoOpTemplate, +// AppOptIn: ApplicationOptInTemplate, +// AppUpdate: ApplicationUpdateTemplate, +// KeyRegistration: KeyRegistrationTemplate, +// Payment: PaymentTemplate, +// } + +/** Decompose a transaction action to determine its standard action type (if any) and retrieve its data */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export async function decomposeAction(action: PolkadotTransactionAction): Promise { + return notImplemented() + // const decomposeActionFuncs = Object.values(DecomposeAction) + // const decomposedActions: AlgorandDecomposeReturn[] = [] + + // // interate over all possible decompose and return all that can be decomposed (i.e returns a chainActionType from decomposeFunc) + // await Promise.all( + // decomposeActionFuncs.map(async (decomposeFunc: any) => { + // try { + // const { chainActionType, args } = (await decomposeFunc(action)) || {} + // if (chainActionType) { + // decomposedActions.push({ chainActionType, args }) + // } + // } catch (err) { + // // console.log('problem in decomposeAction:', err) + // } + // }), + // ) + + // // return null and not an empty array if no matches + // return !isNullOrEmpty(decomposedActions) ? decomposedActions : null +} diff --git a/src/chains/polkadot_1/polkadotTransaction.ts b/src/chains/polkadot_1/polkadotTransaction.ts index c80500e2..66c5221d 100644 --- a/src/chains/polkadot_1/polkadotTransaction.ts +++ b/src/chains/polkadot_1/polkadotTransaction.ts @@ -1,14 +1,19 @@ +/* eslint-disable max-len */ +/* eslint-disable import/no-unresolved */ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-console */ import { ConfirmType, TxExecutionPriority } from '../../models' import { notImplemented } from '../../helpers' import { Transaction } from '../../interfaces' -import { PolkadotSignature, } from './models/cryptoModels' -import { - PolkadotAddress, - PolkadotPublicKey, - PolkadotPrivateKey, - PolkadotChainSettingsCommunicationSettings, +import { PolkadotSignature } from './models/cryptoModels' +import { + PolkadotAddress, + PolkadotPublicKey, + PolkadotPrivateKey, + PolkadotChainSettingsCommunicationSettings, } from './models' -import { +import { PolkadotAddressBuffer, PolkadotTransactionOptions, PolkadotRawTransaction, @@ -292,6 +297,7 @@ export class PolkadotTransaction implements Transaction { * If gasLimitOverride is provided, gasPrice will be calculated and gasLimit will be set to gasLimitOverride * */ public async setDesiredFee(desiredFee: string, options?: PolkadotSetDesiredFeeOptions) { + notImplemented() } /** Hash of transaction - signature must be present to determine transactionId */ @@ -371,6 +377,8 @@ export class PolkadotTransaction implements Transaction { waitForConfirm: ConfirmType = ConfirmType.None, communicationSettings?: PolkadotChainSettingsCommunicationSettings, ): Promise { + notImplemented() + return null } // helpers From 27b896a75cd0002cc17976dc97f033011e704b86 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 08:11:41 -0800 Subject: [PATCH 23/31] polka - remove toValidKeypairType --- src/chains/polkadot_1/polkadotCrypto.ts | 40 +++++++++---------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/chains/polkadot_1/polkadotCrypto.ts b/src/chains/polkadot_1/polkadotCrypto.ts index bb903026..62f908b4 100644 --- a/src/chains/polkadot_1/polkadotCrypto.ts +++ b/src/chains/polkadot_1/polkadotCrypto.ts @@ -22,7 +22,7 @@ import { } from './models' import { CryptoCurve, PublicKey } from '../../models' import { AesCrypto, Asymmetric, Ed25519Crypto } from '../../crypto' -import { removeHexPrefix, byteArrayToHexString, hexStringToByteArray, notSupported } from '../../helpers' +import { removeHexPrefix, byteArrayToHexString, hexStringToByteArray, notSupported, isInEnum } from '../../helpers' import { ensureEncryptedValueIsObject } from '../../crypto/genericCryptoHelpers' // import * as AsymmetricHelpers from '../../crypto/asymmetricHelpers' import { throwNewError } from '../../errors' @@ -66,27 +66,19 @@ export function uncompressEthereumPublicKey(publicKey: PolkadotPublicKey): strin return uncompressedPublicKey } -/** get available keypairType */ -export function toValidKeypairType(keypairType: any): PolkadotKeyPairType { - if (keypairType in PolkadotKeyPairType) return keypairType - // TODO: Confirm default keypairType of chain - return PolkadotKeyPairType.Ethereum -} - /** Encrypts a string using a password and optional salt */ export function encryptWithPassword( unencrypted: string, password: string, options: PolkadotEncryptionOptions, - keypairType?: PolkadotKeyPairType, + keypairType?: PolkadotKeyPairType, // TODO: keypairType should be required ): AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString { // TODO: Define Src25519 curve - const overrideKeypairType = toValidKeypairType(keypairType) - const curve = getCurveFromKeyType(overrideKeypairType) + const curve = getCurveFromKeyType(keypairType) if (curve === CryptoCurve.Ed25519) { const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) const encrypted = Ed25519Crypto.encrypt(unencrypted, passwordKey) - return toSymEncryptedDataString(encrypted, overrideKeypairType) + return toSymEncryptedDataString(encrypted, keypairType) } if (curve === CryptoCurve.Secp256k1) return AesCrypto.encryptWithPassword(unencrypted, password, options) // if no curve, throw an error - curve not supported @@ -99,11 +91,10 @@ export function decryptWithPassword( encrypted: AesCrypto.AesEncryptedDataString | Ed25519Crypto.Ed25519EncryptedDataString | any, password: string, options: PolkadotEncryptionOptions, - keypairType?: PolkadotKeyPairType, + keypairType?: PolkadotKeyPairType, // TODO: keypairType should be required ): string { // TODO: Define Src25519 curve - const overrideKeypairType = toValidKeypairType(keypairType) - const curve = getCurveFromKeyType(overrideKeypairType) + const curve = getCurveFromKeyType(keypairType) if (curve === CryptoCurve.Ed25519) { const passwordKey = Ed25519Crypto.calculatePasswordByteArray(password, options) const decrypted = Ed25519Crypto.decrypt(encrypted, passwordKey) @@ -147,10 +138,9 @@ export async function encryptWithPublicKey( unencrypted: string, publicKey: PolkadotPublicKey, options: Asymmetric.EciesOptions, - keypairType?: PolkadotKeyPairType, + keypairType?: PolkadotKeyPairType, // TODO: keypairType should be required ): Promise { - const overrideKeypairType = toValidKeypairType(keypairType) - const { curveType, publicKeyUncompressed, scheme } = uncompressPublicKey(publicKey, overrideKeypairType) + const { curveType, publicKeyUncompressed, scheme } = uncompressPublicKey(publicKey, keypairType) const useOptions = { ...options, curveType, @@ -169,10 +159,9 @@ export async function decryptWithPrivateKey( encrypted: Asymmetric.AsymmetricEncryptedDataString | Asymmetric.AsymmetricEncryptedData, privateKey: PolkadotPrivateKey, options: Asymmetric.EciesOptions, - keypairType?: PolkadotKeyPairType, + keypairType?: PolkadotKeyPairType, // TODO: keypairType should be required ): Promise { - const overrideKeypairType = toValidKeypairType(keypairType) - const curve = getCurveFromKeyType(overrideKeypairType) // TODO: Should be keypairtype not curve + const curve = getCurveFromKeyType(keypairType) // TODO: Should be keypairtype not curve let useOptions = { ...options } let privateKeyConverted = '' if (curve === CryptoCurve.Secp256k1) { @@ -268,13 +257,12 @@ export async function generateKeyPair( mnemonic?: string, derivationPath?: string, ): Promise { - const overrideKeypairType = toValidKeypairType(keypairType) - const curve = getCurveFromKeyType(overrideKeypairType) + const curve = getCurveFromKeyType(keypairType) const overrideMnemonic = mnemonic || generateNewAccountPhrase() - const { seed, path } = generateSeedFromMnemonic(overrideKeypairType, overrideMnemonic, derivationPath) - const derivedKeypair = keyFromPath(generateKeypairFromSeed(seed, curve), path, overrideKeypairType) + const { seed, path } = generateSeedFromMnemonic(keypairType, overrideMnemonic, derivationPath) + const derivedKeypair = keyFromPath(generateKeypairFromSeed(seed, curve), path, keypairType) const keypair: PolkadotKeypair = { - type: overrideKeypairType, + type: keypairType, publicKey: toPolkadotPublicKey(byteArrayToHexString(derivedKeypair.publicKey)), privateKey: toPolkadotPrivateKey(byteArrayToHexString(derivedKeypair.secretKey)), } From a958d9774c6f6524a338b7ba4d4fd0184ff19e78 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:53:01 -0800 Subject: [PATCH 24/31] add not about node version --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36b363dd..d98ce1bf 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Just install the chainjs library to get started ``` To run ts files (and examples), use ts-node (with the --files option to include local customTypes) +Important: Node version 13 or greater is required to run polkadot examples - Hint: Use nvm to run run node on your local machine ```bash $ ts-node --files mycode.ts ``` From 53c8f3170d4193c0846bfc2d8007d550de064aae Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:53:46 -0800 Subject: [PATCH 25/31] polka - update polkdot libraries versions --- package-lock.json | 711 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 6 +- 2 files changed, 707 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 02a900cf..bccb9c34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@open-rights-exchange/chainjs", - "version": "0.27.8", + "version": "0.28.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -864,6 +864,680 @@ "@types/yargs": "^13.0.0" } }, + "@polkadot/api": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-4.0.3.tgz", + "integrity": "sha512-jZf/NBkj6Ao7hG3I0ay7zOyDZm21tdqNRqglagBI+9Nw3wPvPL2Dz/mnGQCaeSq/fv/frY6YZQvouj4gRQzGwQ==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/api-derive": "4.0.3", + "@polkadot/keyring": "^6.0.5", + "@polkadot/metadata": "4.0.3", + "@polkadot/rpc-core": "4.0.3", + "@polkadot/rpc-provider": "4.0.3", + "@polkadot/types": "4.0.3", + "@polkadot/types-known": "4.0.3", + "@polkadot/util": "^6.0.5", + "@polkadot/util-crypto": "^6.0.5", + "@polkadot/x-rxjs": "^6.0.5", + "bn.js": "^4.11.9", + "eventemitter3": "^4.0.7" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/api-derive": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-4.0.3.tgz", + "integrity": "sha512-ADHrIoYumHJBQuIdtDEX6LPiJVZmLGBlFvlkRGYsKL7qJzRZtkzfuNgd8i3cZVDKk9mlcpldmj1DTiN3KBjH0Q==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/api": "4.0.3", + "@polkadot/rpc-core": "4.0.3", + "@polkadot/types": "4.0.3", + "@polkadot/util": "^6.0.5", + "@polkadot/util-crypto": "^6.0.5", + "@polkadot/x-rxjs": "^6.0.5", + "bn.js": "^4.11.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/keyring": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-6.0.5.tgz", + "integrity": "sha512-v9Tmu+eGZnWpLzxHUj5AIvmfX0uCHWShtF90zvaLvMXLFRWfeuaFnZwdZ+fNkXsrbI0R/w1gRtpFqzsT7QUbVw==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/util": "6.0.5", + "@polkadot/util-crypto": "6.0.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/metadata": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/metadata/-/metadata-4.0.3.tgz", + "integrity": "sha512-w4QRpIendx0LWINS3o93weqrNenI4X5T2iOdiPYd+DkIj1k3GI9An5BWnta9e953xEtGstwW169PF/itWMKyTw==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/types": "4.0.3", + "@polkadot/types-known": "4.0.3", + "@polkadot/util": "^6.0.5", + "@polkadot/util-crypto": "^6.0.5", + "bn.js": "^4.11.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/networks": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-6.0.5.tgz", + "integrity": "sha512-QSa5RdK43yD4kLsZ6tXIB652bZircaVPOpsZ5JFzxCM4gdxHCSCUeXNVBeh3uqeB3FOZZYzSYeoZHLaXuT3yJw==", + "requires": { + "@babel/runtime": "^7.13.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/rpc-core": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-4.0.3.tgz", + "integrity": "sha512-BJD5OS9uYlNMNPwRSFB0oT7az9NXBapapcafi6g1O6d4rvDwmsiptKr4+hkoLhzpuZcx6rfYSsVf7oz1v1J9/g==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/metadata": "4.0.3", + "@polkadot/rpc-provider": "4.0.3", + "@polkadot/types": "4.0.3", + "@polkadot/util": "^6.0.5", + "@polkadot/x-rxjs": "^6.0.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/rpc-provider": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-4.0.3.tgz", + "integrity": "sha512-xddbODw+uMQrrdWWtKb39OwFqs6VFxvBHDjKmnB8IEUzKq2CIEDJG4qe3y2FfTeVCLWWxSmtxyOj0xo3jok3uw==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/types": "4.0.3", + "@polkadot/util": "^6.0.5", + "@polkadot/util-crypto": "^6.0.5", + "@polkadot/x-fetch": "^6.0.5", + "@polkadot/x-global": "^6.0.5", + "@polkadot/x-ws": "^6.0.5", + "bn.js": "^4.11.9", + "eventemitter3": "^4.0.7" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/types": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-4.0.3.tgz", + "integrity": "sha512-aLNugf0Zyde8gAkHtPh8Pp2Rw6XJUUIDe9v/Lc3siJji6aPJuzwHW9XoJYBw8A8pl0MbmrJk3js/o3hEKqmFqg==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/metadata": "4.0.3", + "@polkadot/util": "^6.0.5", + "@polkadot/util-crypto": "^6.0.5", + "@polkadot/x-rxjs": "^6.0.5", + "@types/bn.js": "^4.11.6", + "bn.js": "^4.11.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/types-known": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-4.0.3.tgz", + "integrity": "sha512-XF6Ft2L3zU0E294SpySFi0fv9JIrL0YM0ftOrvqagdXopchc9Sg9XTm3uoukrT8yVu5IVWjQHyk2NwqeAlNV4A==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/networks": "^6.0.5", + "@polkadot/types": "4.0.3", + "@polkadot/util": "^6.0.5", + "bn.js": "^4.11.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/util": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-6.0.5.tgz", + "integrity": "sha512-0EnYdGAXx/Y2MLgCKtlfdKVcURV+Twx+M+auljTeMK8226pR7xMblYuVuO5bxhPWBa1W7+iQloEZ0VRQrIoMDw==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/x-textdecoder": "6.0.5", + "@polkadot/x-textencoder": "6.0.5", + "@types/bn.js": "^4.11.6", + "bn.js": "^4.11.9", + "camelcase": "^5.3.1", + "ip-regex": "^4.3.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/util-crypto": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-6.0.5.tgz", + "integrity": "sha512-NlzmZzJ1vq2bjnQUU0MUocaT9vuIBGTlB/XCrCw94MyYqX19EllkOKLVMgu6o89xhYeP5rmASRQvTx9ZL9EzRw==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/networks": "6.0.5", + "@polkadot/util": "6.0.5", + "@polkadot/wasm-crypto": "^4.0.2", + "@polkadot/x-randomvalues": "6.0.5", + "base-x": "^3.0.8", + "base64-js": "^1.5.1", + "blakejs": "^1.1.0", + "bn.js": "^4.11.9", + "create-hash": "^1.2.0", + "elliptic": "^6.5.4", + "hash.js": "^1.1.7", + "js-sha3": "^0.8.0", + "scryptsy": "^2.1.0", + "tweetnacl": "^1.0.3", + "xxhashjs": "^0.2.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + } + } + }, + "@polkadot/wasm-crypto": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-4.0.2.tgz", + "integrity": "sha512-2h9FuQFkBc+B3TwSapt6LtyPvgtd0Hq9QsHW8g8FrmKBFRiiFKYRpfJKHCk0aCZzuRf9h95bQl/X6IXAIWF2ng==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/wasm-crypto-asmjs": "^4.0.2", + "@polkadot/wasm-crypto-wasm": "^4.0.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/wasm-crypto-asmjs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-4.0.2.tgz", + "integrity": "sha512-hlebqtGvfjg2ZNm4scwBGVHwOwfUhy2yw5RBHmPwkccUif3sIy4SAzstpcVBIVMdAEvo746bPWEInA8zJRcgJA==", + "requires": { + "@babel/runtime": "^7.13.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/wasm-crypto-wasm": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-4.0.2.tgz", + "integrity": "sha512-de/AfNPZ0uDKFWzOZ1rJCtaUbakGN29ks6IRYu6HZTRg7+RtqvE1rIkxabBvYgQVHIesmNwvEA9DlIkS6hYRFQ==", + "requires": { + "@babel/runtime": "^7.13.9" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/x-fetch": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-6.0.5.tgz", + "integrity": "sha512-LuyIxot8pLnYaYsR1xok7Bjm+s7wxYe27Y66THea6bDL3CrBPQdj74F9i0OIxD1GB+qJqh4mDApiGX3COqssvg==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/x-global": "6.0.5", + "@types/node-fetch": "^2.5.8", + "node-fetch": "^2.6.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@types/node-fetch": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", + "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/x-global": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-6.0.5.tgz", + "integrity": "sha512-KjQvICngNdB2Gno0TYJlgjKI0Ia0NPhN1BG6YzcKLO/5ZNzNHkLmowdNb5gprE8uCBnOFXXHwgZAE/nTYya2dg==", + "requires": { + "@babel/runtime": "^7.13.9", + "@types/node-fetch": "^2.5.8", + "node-fetch": "^2.6.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@types/node-fetch": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", + "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/x-randomvalues": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-6.0.5.tgz", + "integrity": "sha512-MZK6+35vk7hnLW+Jciu5pNwMOkaCRNdsTVfNimzaJpIi6hN27y1X2oD82SRln0X4mKh370eLbvP8i3ylOzWnww==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/x-global": "6.0.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/x-rxjs": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-rxjs/-/x-rxjs-6.0.5.tgz", + "integrity": "sha512-nwMaP69/RzdXbPn8XypIRagMpW46waSraQq4/tGb4h+/Qob+RHxCT68UHKz1gp7kzxhrf85LanE9410A6EYjRw==", + "requires": { + "@babel/runtime": "^7.13.9", + "rxjs": "^6.6.6" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "rxjs": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", + "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", + "requires": { + "tslib": "^1.9.0" + } + } + } + }, + "@polkadot/x-textdecoder": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-6.0.5.tgz", + "integrity": "sha512-Vd2OftcEYxg2jG37lJw5NcZotnOidinN84m1HJszLIQT9vZDnFfN60gobHsuzHaGjEDexe4wqe0PfbgA4MfWIQ==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/x-global": "6.0.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/x-textencoder": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-6.0.5.tgz", + "integrity": "sha512-wAheP9/kzpfBw5uU/jCnHtd9uN9XzUPYH81aPbx3X026dXNMa4xpOoroCfEuNu2RtFXm0ONuYfpHxvHUsst9lA==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/x-global": "6.0.5" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@polkadot/x-ws": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-6.0.5.tgz", + "integrity": "sha512-J2vUfDjWIwEB/FSIXKZxER2flWqRvWcxvAaN+w6dhxJw8BKl+NYKN8QRrlhcDvbk/PWEEtdjthwV3lyOoeGDLA==", + "requires": { + "@babel/runtime": "^7.13.9", + "@polkadot/x-global": "6.0.5", + "@types/websocket": "^1.0.2", + "websocket": "^1.0.33" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1012,8 +1686,7 @@ "@types/node": { "version": "13.13.38", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.38.tgz", - "integrity": "sha512-oxo8j9doh7ab9NwDA9bCeFfjHRF/uzk+fTljCy8lMjZ3YzZGAXNDKhTE3Byso/oy32UTUQIXB3HCVHu3d2T3xg==", - "dev": true + "integrity": "sha512-oxo8j9doh7ab9NwDA9bCeFfjHRF/uzk+fTljCy8lMjZ3YzZGAXNDKhTE3Byso/oy32UTUQIXB3HCVHu3d2T3xg==" }, "@types/node-fetch": { "version": "2.5.7", @@ -1077,6 +1750,14 @@ "resolved": "https://registry.npmjs.org/@types/text-encoding/-/text-encoding-0.0.35.tgz", "integrity": "sha512-jfo/A88XIiAweUa8np+1mPbm3h2w0s425YrI8t3wk5QxhH6UI7w517MboNVnGDeMSuoFwA8Rwmklno+FicvV4g==" }, + "@types/websocket": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.2.tgz", + "integrity": "sha512-B5m9aq7cbbD/5/jThEr33nUY8WEfVi6A2YKCTOvw5Ldy7mtsOkqRvGjnzy6g7iMMDsgu7xREuCzqATLDLQVKcQ==", + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "13.0.11", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", @@ -2019,8 +2700,7 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "capture-exit": { "version": "2.0.0", @@ -2450,6 +3130,11 @@ "cssom": "0.3.x" } }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=" + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -4586,6 +5271,11 @@ "loose-envify": "^1.0.0" } }, + "ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8195,8 +8885,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsutils": { "version": "3.17.1", @@ -9069,6 +9758,14 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, + "xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "requires": { + "cuint": "^0.2.2" + } + }, "y18n": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", diff --git a/package.json b/package.json index de627ab4..d08b620c 100644 --- a/package.json +++ b/package.json @@ -40,11 +40,11 @@ }, "dependencies": { "@aikon/sjcl": "^0.1.8", - "@polkadot/api": "^3.6.4", + "@polkadot/api": "^4.0.3", "@polkadot/keyring": "^6.0.5", "@polkadot/types": "^4.0.3", - "@polkadot/util": "^5.9.2", - "@polkadot/util-crypto": "^5.9.2", + "@polkadot/util": "^6.0.5", + "@polkadot/util-crypto": "^6.0.5", "@types/bignumber.js": "^5.0.0", "@types/bn.js": "^4.11.6", "@types/bs58": "^4.0.1", From 9660c661a1977515858dbe2dd9e3c0a4c7de1369 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:55:01 -0800 Subject: [PATCH 26/31] polka - type polkadot chain in example --- src/chains/polkadot_1/examples/crypto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/chains/polkadot_1/examples/crypto.ts b/src/chains/polkadot_1/examples/crypto.ts index ce8ed818..e40edacc 100644 --- a/src/chains/polkadot_1/examples/crypto.ts +++ b/src/chains/polkadot_1/examples/crypto.ts @@ -5,6 +5,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-console */ import { ChainFactory, ChainType } from '../../../index' +import { ChainPolkadotV1 } from '../ChainPolkadotV1' import { toPolkadotPrivateKey, toPolkadotPublicKey } from '../helpers' require('dotenv').config() @@ -18,7 +19,7 @@ const polkadotMainnetEndpoints = [{ async function run() { /** Create Algorand chain instance */ - const para = new ChainFactory().create(ChainType.PolkadotV1, polkadotMainnetEndpoints) + const para = (new ChainFactory().create(ChainType.PolkadotV1, polkadotMainnetEndpoints) as ChainPolkadotV1) await para.connect() if (para.isConnected) { console.log('Connected to %o', para.chainId) From f758cd92c88a6f888e19183d11b14176a6cdad28 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:57:18 -0800 Subject: [PATCH 27/31] add ChainFeature type --- src/models/generalModels.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/models/generalModels.ts b/src/models/generalModels.ts index 8fcab223..c5b7492c 100644 --- a/src/models/generalModels.ts +++ b/src/models/generalModels.ts @@ -42,6 +42,14 @@ export enum ChainType { PolkadotV1 = 'polkadot', } +/** Standard 'features' that each type of chain supports */ +export const enum ChainFeature { + Account = 'accounts', + AccountCreationTransaction = 'accountCreationTransaction', + Multisig = 'multisig', + TransactionFees = 'transactionFees', +} + /** Chain urls and related details used to connect to chain */ export type ChainEndpoint = { /** api endpoint url - including http(s):// prefix */ From dcdeade24c8fa4e3163624b39aee7c54c2b497dd Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:57:53 -0800 Subject: [PATCH 28/31] polka - use ChainFeature for supportsFee --- src/chains/polkadot_1/polkadotTransaction.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chains/polkadot_1/polkadotTransaction.ts b/src/chains/polkadot_1/polkadotTransaction.ts index 66c5221d..08196216 100644 --- a/src/chains/polkadot_1/polkadotTransaction.ts +++ b/src/chains/polkadot_1/polkadotTransaction.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/camelcase */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-console */ -import { ConfirmType, TxExecutionPriority } from '../../models' +import { ChainFeature, ConfirmType, TxExecutionPriority } from '../../models' import { notImplemented } from '../../helpers' import { Transaction } from '../../interfaces' import { PolkadotSignature } from './models/cryptoModels' @@ -265,7 +265,7 @@ export class PolkadotTransaction implements Transaction { /** Ethereum has a fee for transactions */ public get supportsFee(): boolean { - return true + return this._chainState.hasFeature(ChainFeature.TransactionFees) } /** Gets estimated cost in units of gas to execute this transaction (at current chain rates) */ From 8bf0844b4e783083ad1889fdade8dc3360a2c859 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:59:00 -0800 Subject: [PATCH 29/31] polka - use PolkadotChainInfo type --- src/chains/polkadot_1/ChainPolkadotV1.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/chains/polkadot_1/ChainPolkadotV1.ts b/src/chains/polkadot_1/ChainPolkadotV1.ts index 0af52d90..611b77a7 100644 --- a/src/chains/polkadot_1/ChainPolkadotV1.ts +++ b/src/chains/polkadot_1/ChainPolkadotV1.ts @@ -2,12 +2,13 @@ import { ChainActionType, ChainInfo, ChainType, CryptoCurve, ChainEntityName, Ch import { ChainError } from '../../errors' import { Chain } from '../../interfaces' import { + PolkadotAddress, PolkadotChainEndpoint, + PolkadotChainInfo, PolkadotChainSettings, - PolkadotSymbol, - PolkadotAddress, PolkadotDecomposeReturn, PolkadotPublicKey, + PolkadotSymbol, } from './models' import { PolkadotChainState } from './polkadotChainState' import { notImplemented } from '../../helpers' @@ -65,7 +66,7 @@ class ChainPolkadotV1 implements Chain { return this._chainState.chain } - public get chainInfo(): ChainInfo { + public get chainInfo(): PolkadotChainInfo { return this._chainState.chainInfo } From 82ebc7fcfe70e45a00e71028b3d869a60cd039af Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:59:32 -0800 Subject: [PATCH 30/31] polka - add hasFeature to chainState --- src/chains/polkadot_1/polkadotChainState.ts | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/chains/polkadot_1/polkadotChainState.ts b/src/chains/polkadot_1/polkadotChainState.ts index cbedf43d..9f17fa24 100644 --- a/src/chains/polkadot_1/polkadotChainState.ts +++ b/src/chains/polkadot_1/polkadotChainState.ts @@ -1,6 +1,7 @@ import { ApiPromise, WsProvider } from '@polkadot/api' // import { isU8a, u8aToString } from '@polkadot/util' import { SubmittableExtrinsic } from '@polkadot/api/submittable/types' +import { ChainFeature } from '../../models' import { PolkadotBlock, PolkadotBlockNumber, @@ -79,6 +80,38 @@ export class PolkadotChainState { } } + /** map our chain features names to polkadot metadata module name */ + mapFeatureToMetadataModuleName(feature: ChainFeature) { + switch (feature) { + case ChainFeature.Account: + return 'Account' + case ChainFeature.AccountCreationTransaction: + return 'Account' + case ChainFeature.Multisig: + return 'Multisig' + case ChainFeature.TransactionFees: + return 'TransactionPayment' + default: + return null + } + } + + /** get the module metadata for a named module */ + getMetadataModule(moduleName: string) { + // TODO: update to use types for PolkadotMetadata + return this.metadata?.V12?.modules.find((m: any) => m.name.toLowercase() === moduleName) + } + + /** Whether the current parachain has a specific feautre/module installed */ + public hasFeature(feature: ChainFeature): boolean { + const moduleName = this.mapFeatureToMetadataModuleName(feature) + return !!this.getMetadataModule(moduleName) + } + + get metadata() { + return this.chainInfo?.nativeInfo?.metadata + } + public async getChainInfo(): Promise { try { const [headBlockTime, chain, name, version, lastBlockHead, metadata] = await Promise.all([ From a41e1cffeb470c8d977e83814e42b4f03ae56490 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Wed, 10 Mar 2021 09:59:40 -0800 Subject: [PATCH 31/31] polka - add todo --- src/chains/polkadot_1/models/polkadotStructures.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/chains/polkadot_1/models/polkadotStructures.ts b/src/chains/polkadot_1/models/polkadotStructures.ts index b8aeb818..2b1f08b4 100644 --- a/src/chains/polkadot_1/models/polkadotStructures.ts +++ b/src/chains/polkadot_1/models/polkadotStructures.ts @@ -6,3 +6,5 @@ export type PolkadotDecomposeReturn = { args: any partial: boolean } + +// TODO: define types for PolkadotMetadata