From 19d5405087208ab3b2ad1da6fe63f66aa221928d Mon Sep 17 00:00:00 2001 From: nabarun Date: Fri, 1 Apr 2022 18:02:56 +0530 Subject: [PATCH] Implement pattern from dxns-registry-client --- .gitignore | 2 + package.json | 17 ++- src/account.ts | 70 +++++++++ src/bonds.test.ts | 28 ++++ src/index.test.ts | 41 ------ src/index.ts | 327 +++++++++++++++++------------------------ src/registry-client.ts | 47 ++++++ src/testing/helper.ts | 17 +++ src/txbuilder.ts | 25 ++++ tsconfig.json | 12 +- yarn.lock | 62 +++++++- 11 files changed, 407 insertions(+), 241 deletions(-) create mode 100644 src/account.ts create mode 100644 src/bonds.test.ts delete mode 100644 src/index.test.ts create mode 100644 src/registry-client.ts create mode 100644 src/testing/helper.ts create mode 100644 src/txbuilder.ts diff --git a/.gitignore b/.gitignore index 3c3629e..31e2bca 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules + +dist diff --git a/package.json b/package.json index bca68ed..a3b156b 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,15 @@ { "name": "chiba-clonk-client", "version": "1.0.0", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", "repository": "git@github.com:vulcanize/chiba-clonk-client.git", "author": "contact@deepstacksoft.com", "devDependencies": { + "@types/is-url": "^1.2.30", "@types/jest": "^27.4.1", + "@types/ripemd160": "^2.0.0", + "@types/secp256k1": "^4.0.3", "jest": "^27.5.1", "protoc-gen-ts": "^0.8.2", "ts-jest": "^27.1.3", @@ -13,14 +17,21 @@ "typescript": "^4.6.2" }, "dependencies": { + "@cosmjs/crypto": "^0.28.1", + "@cosmjs/encoding": "^0.28.1", "@cosmjs/proto-signing": "^0.28.0", "@cosmjs/stargate": "^0.28.0", "@metamask/eth-sig-util": "^4.0.0", "axios": "^0.26.1", "ethers": "^5.6.1", - "evmosjs": "^0.2.2" + "evmosjs": "^0.2.2", + "is-url": "^1.2.4", + "js-sha256": "^0.9.0", + "ripemd160": "^2.0.2", + "secp256k1": "^4.0.3" }, "scripts": { - "test": "jest" + "test": "jest --runInBand", + "build": "tsc" } } diff --git a/src/account.ts b/src/account.ts new file mode 100644 index 0000000..76cd363 --- /dev/null +++ b/src/account.ts @@ -0,0 +1,70 @@ +import assert from 'assert'; +import { MessageTypes, signTypedData, SignTypedDataVersion } from '@metamask/eth-sig-util'; +import { Secp256k1 } from "@cosmjs/crypto"; + +interface TypedMessageDomain { + name?: string; + version?: string; + chainId?: number; + verifyingContract?: string; + salt?: ArrayBuffer; +} + +/** + * Registry account. + */ +// TODO(egor): This is a wrapper around the private key and doesn't have any account related stuff (e.g. account number/sequence). Maybe rename to Key? +export class Account { + _privateKey: Buffer + _publicKey?: Uint8Array + + /** + * New Account. + * @param {buffer} privateKey + */ + constructor(privateKey: Buffer) { + assert(privateKey); + + this._privateKey = privateKey; + } + + get privateKey() { + return this._privateKey; + } + + async init () { + // Generate public key. + const keypair = await Secp256k1.makeKeypair(this._privateKey); + + const compressed = Secp256k1.compressPubkey(keypair.pubkey); + this._publicKey = compressed + } + + /** + * Get private key. + */ + getPrivateKey() { + return this._privateKey.toString('hex'); + } + + /** + * Sign message. + */ + sign(message: any) { + assert(message); + const eipMessageDomain: any = message.eipToSign.domain; + + const signature = signTypedData({ + data: { + types: message.eipToSign.types as MessageTypes, + primaryType: message.eipToSign.primaryType, + domain: eipMessageDomain as TypedMessageDomain, + message: message.eipToSign.message as Record + }, + privateKey: this._privateKey, + version: SignTypedDataVersion.V4 + }) + + return signature; + } +} diff --git a/src/bonds.test.ts b/src/bonds.test.ts new file mode 100644 index 0000000..cc1b077 --- /dev/null +++ b/src/bonds.test.ts @@ -0,0 +1,28 @@ +import { Registry } from './index'; +import { getConfig } from './testing/helper'; + +const { mockServer, chibaClonk: { chainId, endpoint, privateKey, accountAddress, fee } } = getConfig(); + +jest.setTimeout(90 * 1000); + +const bondTests = () => { + let registry: Registry; + let bondId1: string; + + beforeAll(async () => { + registry = new Registry(endpoint, chainId); + }); + + test('Create bond.', async () => { + bondId1 = await registry.getNextBondId(accountAddress); + expect(bondId1).toBeDefined(); + await registry.createBond({ denom: 'aphoton', amount: '100' }, accountAddress, privateKey, fee); + }) +}; + +if (mockServer) { + // Required as jest complains if file has no tests. + test('skipping bond tests', () => {}); +} else { + describe('Bonds', bondTests); +} diff --git a/src/index.test.ts b/src/index.test.ts deleted file mode 100644 index cde89e2..0000000 --- a/src/index.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createBond, sendDeposit, sendTokens, sendVote } from './index' - -const SENDER_ADDRESS = 'ethm1kgwzff36qmx5tvfvfr7wvdurp5mr25csyqxgdm'; -const SENDER_PRIVATE_KEY = '12e94bcc0daecd936b499f3eeb3b3b76ac1410cbaff2ee6c6f64d768453db0cf'; -const TO_ADDRESS = 'ethm1e6r855un2ufnne9cdpujvan5srxjand37pepuz'; - -test('Send tokens', async () => { - await sendTokens(SENDER_PRIVATE_KEY, SENDER_ADDRESS, TO_ADDRESS) -}); - -describe('Gov module', () => { - test('Send deposit', async () => { - const depositParams = { - proposalId: 1, - amount: '10', - denom: 'aphoton', - } - - await sendDeposit(SENDER_PRIVATE_KEY, SENDER_ADDRESS, depositParams) - }) - - test('Send vote', async () => { - const voteParams = { - proposalId: 1, - option: 1 - } - - await sendVote(SENDER_PRIVATE_KEY, SENDER_ADDRESS, voteParams) - }) -}) - -describe('Bond module', () => { - test('Create bond', async () => { - const bondParams = { - amount: '100', - denom: 'aphoton', - } - - await createBond(SENDER_PRIVATE_KEY, SENDER_ADDRESS, bondParams) - }) -}) diff --git a/src/index.ts b/src/index.ts index ab5d89d..4d08f1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,199 +1,144 @@ -import axios from "axios"; -import { MessageTypes, signTypedData, SignTypedDataVersion } from "@metamask/eth-sig-util"; -import { generateEndpointAccount, generateEndpointBroadcast, generatePostBodyBroadcast } from '@tharsis/provider'; + +import isUrl from 'is-url'; +import { sha256 } from 'js-sha256'; +import { generatePostBodyBroadcast } from '@tharsis/provider'; import { - createMessageSend, - createTxRawEIP712, - signatureToWeb3Extension, - createTxMsgVote, Chain, Sender, - MessageMsgVote + Fee, } from '@tharsis/transactions' -import { createTxMsgDeposit, MessageMsgDeposit } from "./gov"; -import { createTxMsgCreateBond, createTxMsgRefillBond, MessageMsgCreateBond, MessageMsgRefillBond } from "./bond"; +import { createTxMsgCreateBond, MessageMsgCreateBond } from "./bond"; +import { RegistryClient } from "./registry-client"; +import { Account } from "./account"; +import { createTransaction } from "./txbuilder"; -const ETHERMINT_REST_ENDPOINT = 'http://127.0.0.1:1317' +const DEFAULT_WRITE_ERROR = 'Unable to write to chiba-clonk.'; -interface TypedMessageDomain { - name?: string; - version?: string; - chainId?: number; - verifyingContract?: string; - salt?: ArrayBuffer; -} - -export const sendTokens = async (senderPrivateKey: string, senderAddress: string, destinationAddress: string) => { - let { data: addrData} = await axios.get(`${ETHERMINT_REST_ENDPOINT}${generateEndpointAccount(senderAddress)}`) - - const chain = { - chainId: 9000, - cosmosChainId: 'ethermint_9000-1', - } - - const sender = { - accountAddress: addrData.account.base_account.address, - sequence: addrData.account.base_account.sequence, - accountNumber: addrData.account.base_account.account_number, - pubkey: addrData.account.base_account.pub_key.key, - } - - const fee = { - amount: '20', - denom: 'aphoton', - gas: '200000', - } - - const memo = '' - - const params = { - destinationAddress: destinationAddress, - amount: '10', - denom: 'aphoton', - } - - // Create a MsgSend transaction. - const msg = createMessageSend(chain, sender, fee, memo, params) - - await signAndSendMessage(senderPrivateKey, chain, sender, msg) -} - -export const sendVote = async (senderPrivateKey: string, senderAddress: string, params: MessageMsgVote) => { - let { data: addrData} = await axios.get(`${ETHERMINT_REST_ENDPOINT}${generateEndpointAccount(senderAddress)}`) - - const chain = { - chainId: 9000, - cosmosChainId: 'ethermint_9000-1', - } - - const sender = { - accountAddress: addrData.account.base_account.address, - sequence: addrData.account.base_account.sequence, - accountNumber: addrData.account.base_account.account_number, - pubkey: addrData.account.base_account.pub_key.key, - } - - const fee = { - amount: '20', - denom: 'aphoton', - gas: '200000', - } - - const memo = '' - - const msg = createTxMsgVote(chain, sender, fee, memo, params) - await signAndSendMessage(senderPrivateKey, chain, sender, msg) -} - -export const sendDeposit = async (senderPrivateKey: string, senderAddress: string, params: MessageMsgDeposit) => { - let { data: addrData} = await axios.get(`${ETHERMINT_REST_ENDPOINT}${generateEndpointAccount(senderAddress)}`) - - const chain = { - chainId: 9000, - cosmosChainId: 'ethermint_9000-1', - } - - const sender = { - accountAddress: addrData.account.base_account.address, - sequence: addrData.account.base_account.sequence, - accountNumber: addrData.account.base_account.account_number, - pubkey: addrData.account.base_account.pub_key.key, - } - - const fee = { - amount: '20', - denom: 'aphoton', - gas: '200000', - } - - const memo = '' - - const msg = createTxMsgDeposit(chain, sender, fee, memo, params) - await signAndSendMessage(senderPrivateKey, chain, sender, msg) -} - -export const createBond = async (senderPrivateKey: string, senderAddress: string, params: MessageMsgCreateBond) => { - let { data: addrData} = await axios.get(`${ETHERMINT_REST_ENDPOINT}${generateEndpointAccount(senderAddress)}`) - - const chain = { - chainId: 9000, - cosmosChainId: 'ethermint_9000-1', - } - - const sender = { - accountAddress: addrData.account.base_account.address, - sequence: addrData.account.base_account.sequence, - accountNumber: addrData.account.base_account.account_number, - pubkey: addrData.account.base_account.pub_key.key, - } - - const fee = { - amount: '20', - denom: 'aphoton', - gas: '200000', - } - - const memo = '' - - const msg = createTxMsgCreateBond(chain, sender, fee, memo, params) - await signAndSendMessage(senderPrivateKey, chain, sender, msg) -} - -export const refillBond = async (senderPrivateKey: string, senderAddress: string, params: MessageMsgRefillBond) => { - let { data: addrData} = await axios.get(`${ETHERMINT_REST_ENDPOINT}${generateEndpointAccount(senderAddress)}`) - - const chain = { - chainId: 9000, - cosmosChainId: 'ethermint_9000-1', - } - - const sender = { - accountAddress: addrData.account.base_account.address, - sequence: addrData.account.base_account.sequence, - accountNumber: addrData.account.base_account.account_number, - pubkey: addrData.account.base_account.pub_key.key, - } - - const fee = { - amount: '20', - denom: 'aphoton', - gas: '200000', - } - - const memo = '' - - const msg = createTxMsgRefillBond(chain, sender, fee, memo, params) - await signAndSendMessage(senderPrivateKey, chain, sender, msg) -} - -const signAndSendMessage = async (senderPrivateKey: string, chain: Chain, sender: Sender, msg: any) => { - const eipMessageDomain: any = msg.eipToSign.domain; - - // Sign transaction. - const signature = signTypedData({ - data: { - types: msg.eipToSign.types as MessageTypes, - primaryType: msg.eipToSign.primaryType, - domain: eipMessageDomain as TypedMessageDomain, - message: msg.eipToSign.message as Record - }, - privateKey: Buffer.from(senderPrivateKey, 'hex'), - version: SignTypedDataVersion.V4 - }) - - let extension = signatureToWeb3Extension(chain, sender, signature) - - // Create the txRaw. - let rawTx = createTxRawEIP712(msg.legacyAmino.body, msg.legacyAmino.authInfo, extension) - - const body = generatePostBodyBroadcast(rawTx) - - // Broadcast transaction. - return axios.post( - `${ETHERMINT_REST_ENDPOINT}${generateEndpointBroadcast()}`, - JSON.parse(body) - ) - - // TODO: Check for successful broadcast. +export const DEFAULT_CHAIN_ID = 'ethermint_9000-1'; + +// Parse Tx response from cosmos-sdk. +export const parseTxResponse = (result: any) => { + const { txhash: hash, height, ...txResponse } = result; + txResponse.data = txResponse.data && Buffer.from(txResponse.data, 'base64').toString('utf8'); + txResponse.log = JSON.parse(txResponse.raw_log); + + txResponse.events.forEach((event:any) => { + event.attributes = event.attributes.map(({ key, value }: { key: string, value: string }) => ({ + key: Buffer.from(key, 'base64').toString('utf8'), + value: Buffer.from(value, 'base64').toString('utf8') + })); + }); + + return { hash, height, ...txResponse }; +}; + +export const isKeyValid = (key: string) => key && key.match(/^[0-9a-fA-F]{64}$/); + +export class Registry { + _endpoint: string + _chain: Chain + _client: RegistryClient + + static processWriteError(error: Error) { + /** + Example: + + { + message: '{"code":18,"data":null,"log":"invalid request: Name already reserved.: failed to execute message; message index: 0","info":"","gasWanted":"200000","gasUsed":"86717","events":[],"codespace":"sdk"}', + path: [ 'submit' ] + }g + */ + const message = JSON.parse(error.message); + return message.log || DEFAULT_WRITE_ERROR; + } + + constructor(url: string, cosmosChainId = DEFAULT_CHAIN_ID) { + if (!isUrl(url)) { + throw new Error('Path to a registry GQL endpoint should be provided.'); + } + + this._endpoint = url; + this._client = new RegistryClient(url); + + this._chain = { + chainId: 9000, + cosmosChainId + } + } + + /** + * Get account by addresses. + */ + async getAccount(address: string) { + return this._client.getAccount(address); + } + + /** + * Computes the next bondId for the given account private key. + */ + async getNextBondId(address: string) { + let result; + + try { + const { account } = await this.getAccount(address); + const accountObj = account.base_account; + + const nextSeq = parseInt(accountObj.sequence, 10) + 1; + result = sha256(`${accountObj.address}:${accountObj.number}:${nextSeq}`); + } catch (err: any) { + const error = err[0] || err; + throw new Error(Registry.processWriteError(error)); + } + + return result; + } + + /** + * Create bond. + */ + async createBond(params: MessageMsgCreateBond, senderAddress: string, privateKey: string, fee: Fee) { + let result; + + try { + const { account: { base_account: accountInfo } } = await this.getAccount(senderAddress); + + const sender = { + accountAddress: accountInfo.address, + sequence: accountInfo.sequence, + accountNumber: accountInfo.account_number, + pubkey: accountInfo.pub_key.key, + } + + const msg = createTxMsgCreateBond(this._chain, sender, fee, '', params) + result = await this._submitTx(msg, privateKey, sender); + } catch (err: any) { + const error = err[0] || err; + throw new Error(Registry.processWriteError(error)); + } + + return parseTxResponse(result); + } + + /** + * Submit a generic Tx to the chain. + */ + async _submitTx(message: any, privateKey: string, sender: Sender) { + // Check private key. + if (!isKeyValid(privateKey)) { + throw new Error('Registry privateKey should be a hex string.'); + } + + // Check that the account exists on-chain. + const account = new Account(Buffer.from(privateKey, 'hex')); + + // Generate signed Tx. + const transaction = createTransaction(message, account, sender, this._chain); + + const tx = generatePostBodyBroadcast(transaction) + + // Submit Tx to chain. + const { tx_response: response } = await this._client.submit(tx); + return response; + } } diff --git a/src/registry-client.ts b/src/registry-client.ts new file mode 100644 index 0000000..14f6fe4 --- /dev/null +++ b/src/registry-client.ts @@ -0,0 +1,47 @@ +import assert from 'assert'; +import axios from 'axios'; +import { generateEndpointAccount, generateEndpointBroadcast, generatePostBodyBroadcast } from '@tharsis/provider'; + +/** + * Registry + */ +export class RegistryClient { + _endpoint: string + + /** + * New Client. + * @param {string} endpoint + * @param {object} options + */ + constructor(endpoint: string) { + assert(endpoint); + + this._endpoint = endpoint; + } + + /** + * Fetch Account. + */ + async getAccount(address: string) { + assert(address); + + let { data } = await axios.get(`${this._endpoint}${generateEndpointAccount(address)}`) + + return data + } + + /** + * Submit transaction. + */ + async submit(tx: string) { + assert(tx); + + // Broadcast transaction. + const { data } = await axios.post( + `${this._endpoint}${generateEndpointBroadcast()}`, + tx + ) + + return data; + } +} diff --git a/src/testing/helper.ts b/src/testing/helper.ts new file mode 100644 index 0000000..03ae96a --- /dev/null +++ b/src/testing/helper.ts @@ -0,0 +1,17 @@ +const DEFAULT_PRIVATE_KEY = '0451f0bd95c855d52e76cdc8dd06f29097b944bfef26d3455725157f9133f4e0'; +const DEFAULT_ADDRESS = 'ethm19n3je0lhuk0w9kmkftsuw4etn8lmpu3jjfayeh' + +export const getConfig = () => ({ + mockServer: process.env.MOCK_SERVER || false, + chibaClonk: { + chainId: process.env.CHIBA_CLONK_CHAIN_ID || 'ethermint_9000-1', + privateKey: DEFAULT_PRIVATE_KEY, + accountAddress: DEFAULT_ADDRESS, + endpoint: process.env.CHIBA_CLONK_ENDPOINT || 'http://localhost:1317', + fee: { + amount: '20', + denom: 'aphoton', + gas: '200000', + } + } +}); diff --git a/src/txbuilder.ts b/src/txbuilder.ts new file mode 100644 index 0000000..5dfb289 --- /dev/null +++ b/src/txbuilder.ts @@ -0,0 +1,25 @@ +import assert from 'assert'; +import { + createTxRawEIP712, + signatureToWeb3Extension, + Chain, + Sender +} from '@tharsis/transactions' + +import { Account } from './account'; + +/** + * Generate a cosmos-sdk transaction. + */ +export const createTransaction = (message: any, account: Account, sender: Sender, chain: Chain) => { + assert(message); + assert(account); + + // Sign transaction. + const signature = account.sign(message); + + let extension = signatureToWeb3Extension(chain, sender, signature) + + // Create the txRaw. + return createTxRawEIP712(message.legacyAmino.body, message.legacyAmino.authInfo, extension) +}; diff --git a/tsconfig.json b/tsconfig.json index 5679481..9c5585c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -42,12 +42,12 @@ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ @@ -97,5 +97,11 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "src/**/*.test.ts" + ] } diff --git a/yarn.lock b/yarn.lock index ac24ad9..71c852b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -324,6 +324,19 @@ elliptic "^6.5.3" libsodium-wrappers "^0.7.6" +"@cosmjs/crypto@^0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@cosmjs/crypto/-/crypto-0.28.1.tgz#2c7ec4bbda6dd23eee7171e5897588203c5610f6" + integrity sha512-QLgP+xvd3X4vNU9PPnEGc1PI5qctgg1o6ANivqHgiJdX2bFolsqCqFQDs1rvGf8GWLJ2eGwXZPX1c/QK0bT9+A== + dependencies: + "@cosmjs/encoding" "0.28.1" + "@cosmjs/math" "0.28.1" + "@cosmjs/utils" "0.28.1" + "@noble/hashes" "^1" + bn.js "^5.2.0" + elliptic "^6.5.3" + libsodium-wrappers "^0.7.6" + "@cosmjs/encoding@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.28.0.tgz#e93f1ad0ee887074bb094e9c1c388bdb479c1abb" @@ -333,6 +346,15 @@ bech32 "^1.1.4" readonly-date "^1.0.0" +"@cosmjs/encoding@0.28.1", "@cosmjs/encoding@^0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@cosmjs/encoding/-/encoding-0.28.1.tgz#e7214a29d73847c23e5ae28adeec081c3b1e0f92" + integrity sha512-FqKc+P5rBKq8hW2WHF6L8dmSJhy9mVBDhnJNCLUwyiKBywY9m4BZNTa0mPVQSXISx/c5DPbpJ5SChGL72qNgBw== + dependencies: + base64-js "^1.3.0" + bech32 "^1.1.4" + readonly-date "^1.0.0" + "@cosmjs/json-rpc@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@cosmjs/json-rpc/-/json-rpc-0.28.0.tgz#cbb8ee4c47bb9d2fbd0f70395d3e715097962eb2" @@ -348,6 +370,13 @@ dependencies: bn.js "^5.2.0" +"@cosmjs/math@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@cosmjs/math/-/math-0.28.1.tgz#3f2bbf14674f7a0f70a09413ba0bba140e77f1cc" + integrity sha512-rF5q4BSwNBo0kNBi8asaoHsRx/TchJ/P4IlRjXY8UGCfKCkSRQEID3ffgE8naXf+BDn5x4cSC8da3xy/aCZpAA== + dependencies: + bn.js "^5.2.0" + "@cosmjs/proto-signing@0.28.0", "@cosmjs/proto-signing@^0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@cosmjs/proto-signing/-/proto-signing-0.28.0.tgz#d0e6880a6cb0115c78a5bca9cc89490e4b5e5e69" @@ -415,6 +444,11 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.28.0.tgz#a2fefb68b7e2dddabf27f739a0f51578f7ebb4dc" integrity sha512-1Um7h2a20ipbvEw0dzKPHL8qTbH5YY9ND0u5XxlVaCxaYDMTpzjjPiQD+Offxx/28afi8cuHWDbJc45dJXoCAg== +"@cosmjs/utils@0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.28.1.tgz#3c5043c6b4f92a2eba1aba63bed737b542b21662" + integrity sha512-PhdsifctdpMUXeWQjbQiHeOCOhWtK/OXdEG3E2PvvYxlmWHNu1faio+u2gZU6PPjL+qgqlAu92sybwsw/TRa+w== + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -1180,6 +1214,11 @@ dependencies: "@types/node" "*" +"@types/is-url@^1.2.30": + version "1.2.30" + resolved "https://registry.yarnpkg.com/@types/is-url/-/is-url-1.2.30.tgz#85567e8bee4fee69202bc3448f9fb34b0d56c50a" + integrity sha512-AnlNFwjzC8XLda5VjRl4ItSd8qp8pSNowvsut0WwQyBWHpOxjxRJm8iO6uETWqEyLdYdb9/1j+Qd9gQ4l5I4fw== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -1234,7 +1273,14 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== -"@types/secp256k1@^4.0.1": +"@types/ripemd160@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/ripemd160/-/ripemd160-2.0.0.tgz#d33e49cf66edf4668828030d4aa80116bbf8ae81" + integrity sha512-LD6AO/+8cAa1ghXax9NG9iPDLPUEGB2WWPjd//04KYfXxTwHvlDEfL0NRjrM5z9XWBi6WbKw75Are0rDyn3PSA== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1", "@types/secp256k1@^4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== @@ -2341,6 +2387,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2798,6 +2849,11 @@ jest@^27.5.1: import-local "^3.0.2" jest-cli "^27.5.1" +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + js-sha3@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -3340,7 +3396,7 @@ ripemd160-min@0.0.6: resolved "https://registry.yarnpkg.com/ripemd160-min/-/ripemd160-min-0.0.6.tgz#a904b77658114474d02503e819dcc55853b67e62" integrity sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A== -ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -3382,7 +3438,7 @@ scrypt-js@3.0.1, scrypt-js@^3.0.0: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -secp256k1@^4.0.1: +secp256k1@^4.0.1, secp256k1@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==