define Wallet class (#1509)

* define Wallet class
- Wallet class is a utility for deriving a wallet composed of a keypair (publicKey/privateKey).
- A wallet can be derived from either a seed, mnemnoic, or entropy (array of random numbers).
- It provides functionality to sign/verify transactions offline.
This commit is contained in:
Omar Khan
2021-08-11 17:24:33 -04:00
committed by Mayukha Vadari
parent b2b4b86f4d
commit 2347efc7d3
16 changed files with 494 additions and 28 deletions

View File

@@ -23,6 +23,8 @@
"@types/lodash": "^4.14.136",
"@types/ws": "^7.2.0",
"bignumber.js": "^9.0.0",
"bip32": "^2.0.6",
"bip39": "^3.0.4",
"https-proxy-agent": "^5.0.0",
"jsonschema": "1.2.2",
"lodash": "^4.17.4",

112
src/Wallet.ts Normal file
View File

@@ -0,0 +1,112 @@
import {fromSeed} from 'bip32'
import {mnemonicToSeedSync} from 'bip39'
import {decode, encodeForSigning} from 'ripple-binary-codec'
import {deriveKeypair, generateSeed, verify} from 'ripple-keypairs'
import ECDSA from './common/ecdsa'
import {SignedTransaction} from './common/types/objects'
import {signOffline} from './transaction/sign'
import {SignOptions} from './transaction/types'
import {ValidationError} from './common/errors'
/**
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
* A wallet can be derived from either a seed, mnemnoic, or entropy (array of random numbers).
* It provides functionality to sign/verify transactions offline.
*/
class Wallet {
readonly publicKey: string
readonly privateKey: string
private static readonly defaultAlgorithm: ECDSA = ECDSA.ed25519
private static readonly defaultDerivationPath: string = "m/44'/144'/0'/0/0"
constructor(publicKey: string, privateKey: string) {
this.publicKey = publicKey
this.privateKey = privateKey
}
/**
* Derives a wallet from a seed.
* @param {string} seed A string used to generate a keypair (publicKey/privateKey) to derive a wallet.
* @param {ECDSA} algorithm The digital signature algorithm to generate an address for.
* @returns {Wallet} A Wallet derived from a seed.
*/
static fromSeed(seed: string, algorithm: ECDSA = Wallet.defaultAlgorithm): Wallet {
return Wallet.deriveWallet(seed, algorithm)
}
/**
* Derives a wallet from a mnemonic.
* @param {string} mnemonic A string consisting of words (whitespace delimited) used to derive a wallet.
* @param {string} derivationPath The path to derive a keypair (publicKey/privateKey) from a seed (that was converted from a mnemonic).
* @returns {Wallet} A Wallet derived from a mnemonic.
*/
static fromMnemonic(
mnemonic: string,
derivationPath: string = Wallet.defaultDerivationPath
): Wallet {
const seed = mnemonicToSeedSync(mnemonic)
const masterNode = fromSeed(seed)
const node = masterNode.derivePath(derivationPath)
if (node.privateKey === undefined) {
throw new ValidationError('Unable to derive privateKey from mnemonic input')
}
const publicKey = Wallet.hexFromBuffer(node.publicKey)
const privateKey = Wallet.hexFromBuffer(node.privateKey)
return new Wallet(publicKey, `00${privateKey}`)
}
/**
* Derives a wallet from an entropy (array of random numbers).
* @param {Uint8Array | number[]} entropy An array of random numbers to generate a seed used to derive a wallet.
* @param {ECDSA} algorithm The digital signature algorithm to generate an address for.
* @returns {Wallet} A Wallet derived from an entropy.
*/
static fromEntropy(
entropy: Uint8Array | number[],
algorithm: ECDSA = Wallet.defaultAlgorithm
): Wallet {
const options = {
entropy: Uint8Array.from(entropy),
algorithm
}
const seed = generateSeed(options)
return Wallet.deriveWallet(seed, algorithm)
}
private static hexFromBuffer(buffer: Buffer): string {
return buffer.toString('hex').toUpperCase()
}
private static deriveWallet(seed: string, algorithm: ECDSA = Wallet.defaultAlgorithm): Wallet {
const {publicKey, privateKey} = deriveKeypair(seed, {algorithm})
return new Wallet(publicKey, privateKey)
}
/**
* Signs a transaction offline.
* @param {object} transaction A transaction to be signed offline.
* @param {SignOptions} options Options to include for signing.
* @returns {SignedTransaction} A signed transaction.
*/
signTransaction(
transaction: any, // TODO: transaction should be typed with Transaction type.
options: SignOptions = {signAs: ''}
): SignedTransaction {
return signOffline(this, JSON.stringify(transaction), options)
}
/**
* Verifies a signed transaction offline.
* @param {string} signedTransaction A signed transaction (hex string of signTransaction result) to be verified offline.
* @returns {boolean} Returns true if a signedTransaction is valid.
*/
verifyTransaction(signedTransaction: string): boolean {
const tx = decode(signedTransaction)
const messageHex: string = encodeForSigning(tx)
const signature = tx.TxnSignature
return verify(messageHex, signature, this.publicKey)
}
}
export default Wallet

View File

@@ -45,7 +45,7 @@ import prepareCheckCancel from './transaction/check-cancel'
import prepareCheckCash from './transaction/check-cash'
import prepareSettings from './transaction/settings'
import prepareTicketCreate from './transaction/ticket'
import sign from './transaction/sign'
import {sign} from './transaction/sign'
import combine from './transaction/combine'
import submit from './transaction/submit'
import {generateAddress, generateXAddress} from './offline/utils'

6
src/common/ecdsa.ts Normal file
View File

@@ -0,0 +1,6 @@
enum ECDSA {
ed25519 = 'ed25519',
secp256k1 = 'ecdsa-secp256k1',
}
export default ECDSA

View File

@@ -20,3 +20,8 @@ export interface OfferCreateTransaction {
Memos?: Memo[]
OfferSequence?: number
}
export interface SignedTransaction {
signedTransaction: string
id: string
}

View File

@@ -4,7 +4,9 @@ export * from './transaction/types'
export * from './common/types/objects/ledger'
export * from './offline/utils';
export * from './offline/utils'
// Broadcast api is experimental
export {RippleAPIBroadcast} from './broadcast'
export * from './Wallet'

View File

@@ -1,6 +1,7 @@
import {classicAddressToXAddress} from 'ripple-address-codec'
import keypairs from 'ripple-keypairs'
import {errors, validate} from '../common'
import ECDSA from '../common/ecdsa'
export type GeneratedAddress = {
xAddress: string
@@ -14,7 +15,7 @@ export interface GenerateAddressOptions {
entropy?: Uint8Array | number[]
// The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
algorithm?: 'ecdsa-secp256k1' | 'ed25519'
algorithm?: ECDSA
// Specifies whether the address is intended for use on a test network such as Testnet or Devnet.
// If `true`, the address should only be used for testing, and will start with `T`.
@@ -30,7 +31,7 @@ function generateAddressAPI(options: GenerateAddressOptions = {}): GeneratedAddr
try {
const generateSeedOptions: {
entropy?: Uint8Array
algorithm?: 'ecdsa-secp256k1' | 'ed25519'
algorithm?: ECDSA
} = {
algorithm: options.algorithm
}

View File

@@ -7,6 +7,8 @@ import {SignOptions, KeyPair, TransactionJSON} from './types'
import BigNumber from 'bignumber.js'
import {xrpToDrops} from '../common'
import {RippleAPI} from '..'
import Wallet from '../Wallet'
import {SignedTransaction} from '../common/types/objects'
const validate = utils.common.validate
function computeSignature(tx: object, privateKey: string, signAs?: string) {
@@ -23,8 +25,9 @@ function signWithKeypair(
options: SignOptions = {
signAs: ''
}
): {signedTransaction: string; id: string} {
): SignedTransaction {
validate.sign({txJSON, keypair})
const isOnline = !!api;
const tx = JSON.parse(txJSON)
if (tx.TxnSignature || tx.Signers) {
@@ -33,7 +36,9 @@ function signWithKeypair(
)
}
if (isOnline) {
checkFee(api, tx.Fee)
}
const txToSignAndEncode = Object.assign({}, tx)
@@ -219,7 +224,7 @@ function sign(
secret?: any,
options?: SignOptions,
keypair?: KeyPair
): {signedTransaction: string; id: string} {
): SignedTransaction {
if (typeof secret === 'string') {
// we can't validate that the secret matches the account because
// the secret could correspond to the regular key
@@ -241,4 +246,19 @@ function sign(
}
}
export default sign
// TODO: move this to Wallet class
function signOffline(
wallet: Wallet,
txJSON: string,
options?: SignOptions,
): SignedTransaction {
const {publicKey, privateKey} = wallet
return signWithKeypair(
null,
txJSON,
{publicKey, privateKey},
options,
)
}
export {sign, signOffline}

View File

@@ -2,6 +2,7 @@ import assert from 'assert-diff'
import responses from '../../fixtures/responses'
import {TestSuite} from '../../utils'
import {GenerateAddressOptions} from '../../../src/offline/generate-address'
import ECDSA from '../../../src/common/ecdsa'
const {generateAddress: RESPONSE_FIXTURES} = responses
/**
@@ -65,7 +66,7 @@ export default <TestSuite>{
'generateAddress with algorithm `ecdsa-secp256k1`': async (api) => {
// GIVEN we want to use 'ecdsa-secp256k1'
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1'}
const options: GenerateAddressOptions = {algorithm: ECDSA.secp256k1}
// WHEN generating an address
const account = api.generateAddress(options)
@@ -86,7 +87,7 @@ export default <TestSuite>{
'generateAddress with algorithm `ed25519`': async (api) => {
// GIVEN we want to use 'ed25519'
const options: GenerateAddressOptions = {algorithm: 'ed25519'}
const options: GenerateAddressOptions = {algorithm: ECDSA.ed25519}
// WHEN generating an address
const account = api.generateAddress(options)
@@ -105,7 +106,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ecdsa-secp256k1',
algorithm: ECDSA.secp256k1,
entropy: new Array(16).fill(0)
}
@@ -119,7 +120,7 @@ export default <TestSuite>{
'generateAddress with algorithm `ed25519` and given entropy': async (api) => {
// GIVEN we want to use 'ed25519' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ed25519',
algorithm: ECDSA.ed25519,
entropy: new Array(16).fill(0)
}
@@ -142,7 +143,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ecdsa-secp256k1',
algorithm: ECDSA.secp256k1,
entropy: new Array(16).fill(0),
includeClassicAddress: true
}
@@ -159,7 +160,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ed25519' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ed25519',
algorithm: ECDSA.ed25519,
entropy: new Array(16).fill(0),
includeClassicAddress: true
}
@@ -183,7 +184,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ecdsa-secp256k1',
algorithm: ECDSA.secp256k1,
entropy: new Array(16).fill(0),
includeClassicAddress: true,
test: true
@@ -205,7 +206,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ed25519' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ed25519',
algorithm: ECDSA.ed25519,
entropy: new Array(16).fill(0),
includeClassicAddress: true,
test: true

View File

@@ -1,6 +1,7 @@
import assert from 'assert-diff'
import responses from '../../fixtures/responses'
import {TestSuite} from '../../utils'
import ECDSA from '../../../src/common/ecdsa'
import {GenerateAddressOptions} from '../../../src/offline/generate-address'
/**
@@ -70,7 +71,7 @@ export default <TestSuite>{
'generateXAddress with algorithm `ecdsa-secp256k1`': async (api) => {
// GIVEN we want to use 'ecdsa-secp256k1'
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1'}
const options: GenerateAddressOptions = {algorithm: ECDSA.secp256k1}
// WHEN generating an X-address
const account = api.generateXAddress(options)
@@ -94,7 +95,7 @@ export default <TestSuite>{
'generateXAddress with algorithm `ed25519`': async (api) => {
// GIVEN we want to use 'ed25519'
const options: GenerateAddressOptions = {algorithm: 'ed25519'}
const options: GenerateAddressOptions = {algorithm: ECDSA.ed25519}
// WHEN generating an X-address
const account = api.generateXAddress(options)
@@ -116,7 +117,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ecdsa-secp256k1',
algorithm: ECDSA.secp256k1,
entropy: new Array(16).fill(0)
}
@@ -132,7 +133,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ed25519' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ed25519',
algorithm: ECDSA.ed25519,
entropy: new Array(16).fill(0)
}
@@ -151,7 +152,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ecdsa-secp256k1',
algorithm: ECDSA.secp256k1,
entropy: new Array(16).fill(0),
includeClassicAddress: true
}
@@ -168,7 +169,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ed25519' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ed25519',
algorithm: ECDSA.ed25519,
entropy: new Array(16).fill(0),
includeClassicAddress: true
}
@@ -190,7 +191,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ecdsa-secp256k1',
algorithm: ECDSA.secp256k1,
entropy: new Array(16).fill(0),
includeClassicAddress: true,
test: true
@@ -211,7 +212,7 @@ export default <TestSuite>{
) => {
// GIVEN we want to use 'ed25519' with entropy of zero
const options: GenerateAddressOptions = {
algorithm: 'ed25519',
algorithm: ECDSA.ed25519,
entropy: new Array(16).fill(0),
includeClassicAddress: true,
test: true

View File

@@ -0,0 +1,54 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
import ECDSA from '../../../src/common/ecdsa'
import Wallet from '../../../src/Wallet'
const entropy: number[] = new Array(16).fill(0)
const publicKey: string =
'0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284'
const privateKey: string =
'002512BBDFDBB77510883B7DCCBEF270B86DEAC8B64AC762873D75A1BEE6298665'
const publicKeyED25519: string =
'ED1A7C082846CFF58FF9A892BA4BA2593151CCF1DBA59F37714CC9ED39824AF85F'
const privateKeyED25519: string =
'ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'Wallet.fromEntropy with entropy only': async (api) => {
// WHEN deriving a wallet from an entropy
const wallet = Wallet.fromEntropy(entropy)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKeyED25519)
assert.equal(wallet.privateKey, privateKeyED25519)
},
'Wallet.fromEntropy with algorithm ecdsa-secp256k1': async (api) => {
// GIVEN an entropy using ecdsa-secp256k1
const algorithm = ECDSA.secp256k1
// WHEN deriving a wallet from an entropy
const wallet = Wallet.fromEntropy(entropy, algorithm)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKey)
assert.equal(wallet.privateKey, privateKey)
},
'Wallet.fromEntropy with algorithm ed25519': async (api) => {
// GIVEN an entropy using ed25519
const algorithm = ECDSA.ed25519
// WHEN deriving a wallet from an entropy
const wallet = Wallet.fromEntropy(entropy, algorithm)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKeyED25519)
assert.equal(wallet.privateKey, privateKeyED25519)
},
}

View File

@@ -0,0 +1,39 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
import Wallet from '../../../src/Wallet'
const mnemonic =
'try milk link drift aware pass obtain again music stick pluck fold'
const publicKey =
'0257B550BA2FDCCF0ADDA3DEB2A5411700F3ADFDCC7C68E1DCD1E2B63E6B0C63E6'
const privateKey =
'008F942B6E229C0E9CEE47E7A94253DABB6A9855F4BA2D8A741FA31851A1D423C3'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'Wallet.fromMnemonic using default derivation path': async (api) => {
// GIVEN no derivation path
// WHEN deriving a wallet from a mnemonic without a derivation path
const wallet = Wallet.fromMnemonic(mnemonic)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKey)
assert.equal(wallet.privateKey, privateKey)
},
'Wallet.fromMnemonic using an input derivation path': async (api) => {
// GIVEN a derivation path
const derivationPath = "m/44'/144'/0'/0/0"
// WHEN deriving a wallet from a mnemonic without a derivation path
const wallet = Wallet.fromMnemonic(mnemonic, derivationPath)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKey)
assert.equal(wallet.privateKey, privateKey)
},
}

View File

@@ -0,0 +1,50 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
import ECDSA from '../../../src/common/ecdsa'
import Wallet from '../../../src/Wallet'
const seed = 'ssL9dv2W5RK8L3tuzQxYY6EaZhSxW'
const publicKey =
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
const privateKey =
'00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'Wallet.fromSeed with empty options object': async (api) => {
// WHEN deriving a wallet from a seed
const wallet = Wallet.fromSeed(seed)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKey)
assert.equal(wallet.privateKey, privateKey)
},
'Wallet.fromSeed with algorithm ecdsa-secp256k1': async (api) => {
// GIVEN we want to use ecdsa-secp256k1
const algorithm = ECDSA.secp256k1
// WHEN deriving a wallet from a seed
const wallet = Wallet.fromSeed(seed, algorithm)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKey)
assert.equal(wallet.privateKey, privateKey)
},
'Wallet.fromSeed with algorithm ed25519': async (api) => {
// GIVEN we want to use ed25519
const algorithm = ECDSA.ed25519
// WHEN deriving a wallet from a seed
const wallet = Wallet.fromSeed(seed, algorithm)
// THEN we get a wallet with a keypair (publicKey/privateKey)
assert.equal(wallet.publicKey, publicKey)
assert.equal(wallet.privateKey, privateKey)
},
}

View File

@@ -0,0 +1,38 @@
import {RippleAPI} from 'ripple-api'
import {TestSuite} from '../../utils'
import Wallet from '../../../src/Wallet'
const {schemaValidator} = RippleAPI._PRIVATE
const publicKey =
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
const privateKey =
'00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F'
const address = 'rhvh5SrgBL5V8oeV9EpDuVszeJSSCEkbPc'
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'sign transaction offline with txJSON': async (api) => {
// GIVEN a transaction
const txJSON = {
TransactionType: 'Payment',
Account: address,
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
Amount: '20000000',
Sequence: 1,
Fee: '12',
SigningPubKey: publicKey
}
const wallet = new Wallet(publicKey, privateKey)
// WHEN signing a transaction offline
const signedTx: {signedTransaction: string; id: string} =
wallet.signTransaction(txJSON)
// THEN we get a signedTransaction
schemaValidator.schemaValidate('sign', signedTx)
},
}

View File

@@ -0,0 +1,46 @@
import assert from 'assert-diff'
import {TestSuite} from '../../utils'
import Wallet from 'ripple-api/Wallet'
const publicKey =
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
const privateKey =
'00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F'
const prepared = {
signedTransaction:
'1200002400000001614000000001312D0068400000000000000C7321030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D74473045022100CAF99A63B241F5F62B456C68A593D2835397101533BB5D0C4DC17362AC22046F022016A2CA2CF56E777B10E43B56541A4C2FB553E7E298CDD39F7A8A844DA491E51D81142AF1861DEC1316AEEC995C94FF9E2165B1B784608314FDB08D07AAA0EB711793A3027304D688E10C3648',
id: '30D9ECA2A7FB568C5A8607E5850D9567572A9E7C6094C26BEFD4DC4C2CF2657A'
}
/**
* Every test suite exports their tests in the default object.
* - Check out the "TestSuite" type for documentation on the interface.
* - Check out "test/api/index.ts" for more information about the test runner.
*/
export default <TestSuite>{
'verify transaction offline when a signed transaction is valid': async (api) => {
// GIVEN a transaction that has been signed by the same wallet
const wallet = new Wallet(publicKey, privateKey)
// WHEN verifying a signed transaction
const isVerified: boolean = wallet.verifyTransaction(prepared.signedTransaction)
// THEN we get a valid response
assert.equal(isVerified, true)
},
"verify transaction offline when signed transaction isn't valid": async (api) => {
// GIVEN a transaction that has been signed by a different wallet
const diffPublicKey =
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
const diffPrivateKey =
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A'
const wallet = new Wallet(diffPublicKey, diffPrivateKey)
// WHEN verifying a signed transaction
const isVerified: boolean = wallet.verifyTransaction(prepared.signedTransaction)
// THEN we get an invalid response
assert.equal(isVerified, false)
},
}

View File

@@ -323,6 +323,16 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.10.tgz#7aa732cc47341c12a16b7d562f519c2383b6d4fc"
integrity sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==
"@types/node@10.12.18":
version "10.12.18"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
"@types/node@11.11.6":
version "11.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a"
integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==
"@types/ws@^7.2.0":
version "7.4.7"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
@@ -731,7 +741,7 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base-x@3.0.8:
base-x@3.0.8, base-x@^3.0.2:
version "3.0.8"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d"
integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==
@@ -758,6 +768,36 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
bindings@^1.3.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
dependencies:
file-uri-to-path "1.0.0"
bip32@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134"
integrity sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==
dependencies:
"@types/node" "10.12.18"
bs58check "^2.1.1"
create-hash "^1.2.0"
create-hmac "^1.1.7"
tiny-secp256k1 "^1.1.3"
typeforce "^1.11.5"
wif "^2.0.6"
bip39@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0"
integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==
dependencies:
"@types/node" "11.11.6"
create-hash "^1.1.0"
pbkdf2 "^3.0.9"
randombytes "^2.0.1"
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
@@ -767,7 +807,7 @@ bl@^4.0.3:
inherits "^2.0.4"
readable-stream "^3.4.0"
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9:
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.8, bn.js@^4.11.9:
version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
@@ -872,6 +912,22 @@ browserslist@^4.14.5, browserslist@^4.16.6:
escalade "^3.1.1"
node-releases "^1.1.71"
bs58@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
dependencies:
base-x "^3.0.2"
bs58check@<3.0.0, bs58check@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==
dependencies:
bs58 "^4.0.0"
create-hash "^1.1.0"
safe-buffer "^5.1.2"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@@ -1381,7 +1437,7 @@ electron-to-chromium@^1.3.723:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.792.tgz#791b0d8fcf7411885d086193fb49aaef0c1594ca"
integrity sha512-RM2O2xrNarM7Cs+XF/OE2qX/aBROyOZqqgP+8FXMXSuWuUqCfUUzg7NytQrzZU3aSqk1Qq6zqnVkJsbfMkIatg==
elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4:
elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
@@ -1720,6 +1776,11 @@ file-entry-cache@^5.0.1:
dependencies:
flat-cache "^2.0.1"
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
filelist@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
@@ -2664,6 +2725,11 @@ mute-stream@0.0.8:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
nan@^2.13.2:
version "2.15.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
nanoid@3.1.23:
version "3.1.23"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
@@ -2920,7 +2986,7 @@ path-parse@^1.0.6:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
pbkdf2@^3.0.3:
pbkdf2@^3.0.3, pbkdf2@^3.0.9:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
@@ -3624,6 +3690,17 @@ through@^2.3.6, through@^2.3.8:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
tiny-secp256k1@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c"
integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==
dependencies:
bindings "^1.3.0"
bn.js "^4.11.8"
create-hmac "^1.1.7"
elliptic "^6.4.0"
nan "^2.13.2"
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -3732,6 +3809,11 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typeforce@^1.11.5:
version "1.18.0"
resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc"
integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==
typescript@^3.9.9:
version "3.9.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
@@ -4015,6 +4097,13 @@ wide-align@1.1.3:
dependencies:
string-width "^1.0.2 || 2"
wif@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704"
integrity sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=
dependencies:
bs58check "<3.0.0"
wildcard@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"