{"id":19503465,"url":"https://github.com/exonum/exonum-client","last_synced_at":"2026-03-08T18:38:24.656Z","repository":{"id":51005033,"uuid":"86123088","full_name":"exonum/exonum-client","owner":"exonum","description":"JavaScript client for Exonum blockchain","archived":false,"fork":false,"pushed_at":"2021-12-29T09:49:24.000Z","size":3872,"stargazers_count":64,"open_issues_count":9,"forks_count":33,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-22T03:29:09.102Z","etag":null,"topics":["blockchain","cryptography","ed25519","exonum","merkle-tree","sha256"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exonum.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-25T01:08:04.000Z","updated_at":"2025-01-28T15:13:22.000Z","dependencies_parsed_at":"2022-09-05T18:51:06.138Z","dependency_job_id":null,"html_url":"https://github.com/exonum/exonum-client","commit_stats":null,"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exonum%2Fexonum-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exonum%2Fexonum-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exonum%2Fexonum-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exonum%2Fexonum-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exonum","download_url":"https://codeload.github.com/exonum/exonum-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250917285,"owners_count":21507561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","cryptography","ed25519","exonum","merkle-tree","sha256"],"created_at":"2024-11-10T22:21:33.341Z","updated_at":"2026-03-08T18:38:24.604Z","avatar_url":"https://github.com/exonum.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Light Client for Exonum Blockchain\n\n[![Build status][travis-image]][travis-url]\n[![npm version][npmjs-image]][npmjs-url]\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![js-standard-style][codestyle-image]][codestyle-url]\n\n[travis-image]: https://img.shields.io/travis/exonum/exonum-client/master.svg\n[travis-url]: https://travis-ci.com/exonum/exonum-client\n[npmjs-image]: https://img.shields.io/npm/v/exonum-client.svg\n[npmjs-url]: https://www.npmjs.com/package/exonum-client\n[coveralls-image]: https://coveralls.io/repos/github/exonum/exonum-client/badge.svg?branch=master\n[coveralls-url]: https://coveralls.io/github/exonum/exonum-client?branch=master\n[codestyle-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg\n[codestyle-url]: http://standardjs.com\n\nA JavaScript library to work with Exonum blockchain from browser and Node.js.\nUsed to sign transactions before sending to blockchain and verify blockchain responses using cryptographic proofs.\nContains numerous helper functions. Find out more information about the [architecture and tasks][docs:clients] of light clients in Exonum.\n\nIf you are using Exonum in your project and want to be listed on our website \u0026 GitHub list — write\nus a line to \u003ccontact@exonum.com\u003e.\n\nLibrary compatibility with Exonum core:\n\n| JavaScript light client | Exonum core |\n|---|---|\n| 0.18.4 | 1.0.* |\n| 0.18.3 | 1.0.0-rc.1 |\n| 0.17.1 | 0.12.* |\n| 0.16.9 | 0.11.* |\n| 0.16.9 | 0.10.* |\n| 0.13.0 | 0.9.* |\n| 0.10.2 | 0.8.* |\n| 0.9.0 | 0.7.* |\n| 0.6.1 | 0.6.* |\n| 0.6.1 | 0.5.* |\n| 0.3.0 | 0.4.0 |\n| 0.3.0 | 0.3.0 |\n| 0.2.0 | 0.2.0 |\n| 0.1.1 | 0.1.* |\n\n* [Getting started](#getting-started)\n* [Data types](#data-types)\n* [Hash](#hash)\n* [Signature](#signature)\n  * [Sign data](#sign-data)\n  * [Verify signature](#verify-signature)\n* [Transactions](#transactions)\n  * [Define transaction](#define-transaction)\n  * [Sign transaction](#sign-transaction)\n  * [Send transaction](#send-transaction)\n  * [Send multiple transactions](#send-multiple-transactions)\n* [Cryptographic proofs](#cryptographic-proofs)\n  * [Merkle tree proof](#merkle-tree-proof)\n  * [Map proof](#map-proof)\n* [Integrity checks](#integrity-checks)\n  * [Verify block](#verify-block)\n  * [Verify table](#verify-table)\n  * [Built-in structures](#built-in-structures)\n* [Helpers](#helpers)\n  * [Generate key pair](#generate-key-pair)\n  * [Get random number](#get-random-number)\n  * [Converters](#converters)\n    * [Hexadecimal to Uint8Array](#hexadecimal-to-uint8array)\n    * [Hexadecimal to String](#hexadecimal-to-string)\n    * [Uint8Array to Hexadecimal](#uint8array-to-hexadecimal)\n    * [Binary String to Uint8Array](#binary-string-to-uint8array)\n    * [Binary String to Hexadecimal](#binary-string-to-hexadecimal)\n    * [String to Uint8Array](#string-to-uint8array)\n* [Contributing](#contributing)\n  * [Coding standards](#coding-standards)\n  * [Test coverage](#test-coverage)\n* [Changelog](#changelog)\n* [License](#license)\n\n## Getting started\n\nThere are several options to include light client library in the application:\n\nThe preferred way is to install Exonum Client as a [package][npmjs] from npm registry:\n\n```sh\nnpm install exonum-client\n```\n\nOtherwise you can download the source code from GitHub and compile it before use in browser.\n\nInclude in browser:\n\n```html\n\u003cscript src=\"node_modules/exonum-client/dist/exonum-client.min.js\"\u003e\u003c/script\u003e\n```\n\nUsage in Node.js:\n\n```javascript\nlet Exonum = require('exonum-client')\n```\n\n## Data types\n\nExonum uses [protobufjs][protobufjs] library to serialize structured data into [protobuf][protobuf] format.\n\nEach transaction is [signed](#sign-data) before sending into blockchain.\nBefore the transaction is signed it is converted into byte array under the hood.\n\nThe data received from the blockchain should be converted into byte array under the hood\nbefore it will be possible to [verify proof of its existence](#cryptographic-proofs) using cryptographic algorithm.\n\nDeveloper can both define data structures on the fly or use precompiled stubs with data structures.\n\nTo define Protobuf structures use [protobufjs][protobufjs] library.\n\nExample:\n\n```javascript\nconst MessageSchema = new Type('CustomMessage')\n  .add(new Field('balance', 1, 'uint32'))\n  .add(new Field('name', 2, 'string'))\nconst Message = Exonum.newType(MessageSchema)\n```\n\n**Exonum.newType** function requires a single argument of `protobuf.Type` type.\n\n## Hash\n\nExonum uses [cryptographic hashes][docs:glossary:hash] of certain data for [transactions](#transactions) and\n[proofs](#cryptographic-proofs).\n\nDifferent signatures of the `hash` function are possible:\n\n```javascript\nExonum.hash(data, type)\ntype.hash(data)\n```\n\n| Argument | Description | Type |\n|---|---|---|\n| **data** | Data to be processed using a hash function. | `Object` |\n| **type** | Definition of the data type. | [Custom data type](#define-data-type) or [transaction](#define-transaction). |\n\nAn example of hash calculation:\n\n```javascript\n// Define a data structure\nconst Message = new Type('User')\n  .add(new Field('balance', 1, 'uint32'))\n  .add(new Field('name', 2, 'string'))\n// Define a data type\nconst User = Exonum.newType(Message)\n\n// Data to hash\nconst data = {\n  balance: 100,\n  name: 'John Doe'\n}\n// Get a hash\nconst hash = User.hash(data)\n```\n\nIt is also possible to get a hash from byte array:\n\n```javascript\nExonum.hash(buffer)\n```\n\n| Argument | Description | Type |\n|---|---|---|\n| **buffer** | Byte array. | `Array` or `Uint8Array`. |\n\nAn example of byte array hash calculation:\n\n```javascript\nconst arr = [8, 100, 18, 8, 74, 111, 104, 110, 32, 68, 111, 101]\nconst hash = Exonum.hash(arr)\n```\n\n## Signature\n\nThe procedure for [**signing data**](#sign-data) using signing key pair\nand [**verifying of obtained signature**](#verify-signature) is commonly used\nin the process of data exchange between the client and the service.  \n\n*Built-in [**Exonum.keyPair**](#generate-key-pair) helper function can be used\nto generate a new random signing key pair.*\n\n### Sign data\n\nThe signature can be obtained using the **secret key** of the signing pair.\n\nThere are three possible signatures of the `sign` function:\n\n```javascript\nExonum.sign(secretKey, data, type)\ntype.sign(secretKey, data)\nExonum.sign(secretKey, buffer)\n```\n\n| Argument | Description | Type |\n|---|---|---|\n| **secretKey** | Secret key as hexadecimal string. | `String` |\n| **data** | Data to be signed. | `Object` |\n| **type** | Definition of the data type. | [Custom data type](#define-data-type). |\n| **buffer** | Byte array. | `Array` or `Uint8Array`. |\n\nThe `sign` function returns value as hexadecimal `String`.\n\n### Verify signature\n\nThe signature can be verified using the **author's public key**.\n\nThere are two possible signatures of the `verifySignature` function:\n\n```javascript\nExonum.verifySignature(signature, publicKey, data, type)\ntype.verifySignature(signature, publicKey, data)\n```\n\n| Argument | Description | Type |\n|---|---|---|\n| **signature** | Signature as hexadecimal string. | `String` |\n| **publicKey** | Author's public key as hexadecimal string. | `String` |\n| **data** | Data that has been signed. | `Object` |\n| **type** | Definition of the data type. | [Custom data type](#define-data-type). |\n\nThe `verifySignature` function returns value of `Boolean` type.\n\nAn example of signature creation and verification:\n\n```javascript\n// Define a data structure\nconst Message = new Type('User')\n  .add(new Field('balance', 1, 'uint32'))\n  .add(new Field('name', 2, 'string'))\nconst User = Exonum.newType(Message)\n\n// Define a signing key pair\nconst keyPair = Exonum.keyPair()\n\n// Data that has been hashed\nconst data = {\n  balance: 100,\n  name: 'John Doe'\n}\n// Signature obtained upon signing using secret key\nconst signature = Exonum.sign(keyPair.secretKey, data, User)\n// Verify the signature\nconst result = Exonum.verifySignature(signature, keyPair.publicKey, data, User)\n```\n\n## Transactions\n\nTransaction in Exonum is an operation to change the data stored in blockchain.\nTransaction processing rules is a part of business logic implemented\nin a [service][docs:architecture:services].\n\nSending data to the blockchain from a light client consist of 3 steps:\n\n1. Describe the fields of transaction using [custom data types](#define-data-type)\n2. [Sign](#sign-data) data of transaction using signing key pair\n3. [Send transaction](#send-single-transaction) to the blockchain\n\nRead more about [transactions][docs:architecture:transactions] in Exonum,\nor see [the example of their usage](examples/transactions.js).\n\n### Define transaction\n\nAn example of a transaction definition:\n\n```javascript\nconst Transaction = new Type('CustomMessage')\n  .add(new Field('to', 2, 'string'))\n  .add(new Field('amount', 3, 'uint32'))\n\nconst SendFunds = new Exonum.Transaction({\n  schema: Transaction,\n  service_id: 130,\n  method_id: 0\n})\n```\n\n**Exonum.Transaction** constructor requires a single argument of `Object` type with\nthe next structure:\n\n| Property | Description | Type |\n|---|---|---|\n| **schema** | Protobuf data structure. | `Object` |\n| **service_id** | [Service ID][docs:architecture:serialization:service-id]. | `Number` |\n| **method_id** | [Method ID][docs:architecture:serialization:message-id]. | `Number` |\n\n`schema` structure is identical to that of [custom data type](#define-data-type).\n\n### Sign transaction\n\nAn example of a transaction signing:\n\n```javascript\n// Signing key pair\nconst keyPair = Exonum.keyPair()\n\n// Transaction data to be signed\nconst data = {\n  from: 'John',\n  to: 'Adam',\n  amount: 50\n}\n\n// Create a signed transaction\nconst signed = SendFunds.create(data, keyPair)\n```\n\n### Send transaction\n\nTo submit transaction to the blockchain `send` function can be used.\n\n```javascript\nExonum.send(explorerBasePath, transaction, attempts, timeout)\n```\n\n| Property | Description | Type |\n|---|---|---|\n| **explorerBasePath** | API address of transaction explorer on a blockchain node. | `String` |\n| **transaction** | Signed transaction bytes. | `String`, `Uint8Array` or `Array`-like |\n| **attempts** | Number of attempts to check transaction status. Pass `0` in case you do not need to verify if the transaction is accepted to the block. *Optional. Default value is `10`.* | `Number` |\n| **timeout** | Timeout between attempts to check transaction status. *Optional. Default value is `500`.* | `Number` |\n\nThe `send` function returns a `Promise` with the transaction hash.\nThe promise resolves when the transaction is committed (accepted to a block).\n\nAn example of a transaction sending:\n\n```javascript\n// Define transaction explorer address\nconst explorerBasePath = 'http://127.0.0.1:8200/api/explorer/v1/transactions'\nconst transactionHash = await Exonum.send(explorerBasePath, signed.serialize())\n```\n\n### Send multiple transactions\n\nTo submit multiple transactions to the blockchain `sendQueue` function can be used.\nTransactions will be sent in the order specified by the caller.\nEach transaction from the queue will be sent to the blockchain only after the previous transaction\nis committed.\n\n```javascript\nExonum.sendQueue(explorerBasePath, transactions, attempts, timeout)\n```\n\n| Property | Description | Type |\n|---|---|---|\n| **explorerBasePath** | API address of transaction explorer on a blockchain node. | `String` |\n| **transactions** | List of transactions. | `Array` |\n| **attempts** | Number of attempts to check each transaction status. Pass `0` in case you do not need to verify if the transactions are accepted to the block. *Optional. Default value is `10`.* | `Number` |\n| **timeout** | Timeout between attempts to check each transaction status. *Optional. Default value is `500`.* | `Number` |\n\nThe `sendQueue` function returns a `Promise` with an array of transaction hashes.\nThe promise resolves when all transactions are committed.\n\n## Cryptographic proofs\n\nA cryptographic proof is a format in which a Exonum node can provide sensitive data from a blockchain.\nThese proofs are based on [Merkle trees][docs:glossary:merkle-tree] and their variants.\n\nLight client library validates the cryptographic proof and can prove the integrity and reliability\nof the received data.\n\nRead more about design of [cryptographic proofs][docs:advanced:merkelized-list] in Exonum.\n\n### Merkle tree proof\n\n```javascript\nconst proof = new Exonum.ListProof(json, ValueType)\nconsole.log(proof.entries)\n```\n\nThe `ListProof` class is used to validate proofs for Merkelized lists.\n\n| Argument | Description | Type |\n|---|---|---|\n| **json** | The JSON presentation of the proof obtained from a full node. | `Object` |\n| **ValueType** | Data type for values in the Merkelized list. | [Custom data type](#define-data-type) |\n\nThe returned object has the following fields:\n\n| Field | Description | Type |\n|---|---|---|\n| **merkleRoot** | Hexadecimal hash of the root of the underlying Merkelized list | `String` |\n| **entries** | Elements that are proven to exist in the list, together with their indexes | `Array\u003c{ index: number, value: V }\u003e` |\n| **length** | List length | `Number` |\n\nSee an [example of using a `ListProof`](examples/list-proof.js).\n\n### Map proof\n\n```javascript\nconst proof = new Exonum.MapProof(json, KeyType, ValueType)\nconsole.log(proof.entries)\n```\n\nThe `MapProof` class is used to validate proofs for Merkelized maps.\n\n| Argument | Description | Type |\n|---|---|---|\n| **json** | The JSON presentation of the proof obtained from a full node. | `Object` |\n| **KeyType** | Data type for keys in the Merkelized map. | [Custom](#define-data-type) or built-in data type |\n| **ValueType** | Data type for values in the Merkelized map. | [Custom data type](#define-data-type) |\n\nKeys in a map proof can either be *hashed* (which is the default option)\nor *raw*. To obtain a raw version for `KeyType`, use `MapProof.rawKey(KeyType)`.\nThe key type is determined by the service developer when the service schema\nis created. Raw keys minimize the amount of hashing, but require that the underlying type\nhas fixed-width binary serialization.\n\nThe returned object has the following fields:\n\n| Field | Description | Type |\n|---|---|---|\n| **merkleRoot** | Hexadecimal hash of the root of the underlying Merkelized map | `String` |\n| **missingKeys** | Set of keys which the proof asserts as missing from the map | `Set\u003cKeyType\u003e` |\n| **entries** | Map of key-value pairs that the are proven to exist in the map | `Map\u003cKeyType, ValueType\u003e` |\n\nSee an [example of using a `MapProof`](examples/map-proof.js).\n\n## Integrity checks\n\n### Verify block\n\n```javascript\nExonum.verifyBlock(data, validators)\n```\n\nEach new block in Exonum blockchain is signed by [validators][docs:glossary:validator].\nTo prove the integrity and reliability of the block, it is necessary to verify their signatures.\nThe signature of each validator are stored in the precommits.\n\nThe `verifyBlock` function throws an error if a block is invalid.\n\n| Argument | Description | Type |\n|---|---|---|\n| **data** | Structure with block and precommits. | `Object` |\n| **validators** | An array of validators public keys as a hexadecimal strings. | `Array` |\n\n### Verify table\n\n```javascript\nExonum.verifyTable(proof, stateHash, fullTableName)\n```\n\nVerify table existence in the root tree.\n\nReturns root hash for the table as hexadecimal `String`.\n\n| Argument | Description | Type |\n|---|---|---|\n| **proof** | The JSON presentation of the proof obtained from a full node. | `Object` |\n| **stateHash** | Hash of current blockchain state stored in each block. | `String` |\n| **fullTableName** | Name of the table, such as `token.wallets`. | `String` |\n\n### Built-in structures\n\nThe library exports Protobuf declarations from the core crate.\nConsult [Protobuf files included into the library](proto) for more details.\n\n## Helpers\n\n### Generate key pair\n\n```javascript\nconst pair = Exonum.keyPair()\n```\n\n```javascript\n{\n  publicKey: \"...\", // 32-byte public key\n  secretKey: \"...\" // 64-byte secret key\n}\n```\n\n**Exonum.keyPair** function generates a new random [Ed25519][docs:glossary:digital-signature]\nsigning key pair using the [TweetNaCl][tweetnacl:key-pair] cryptographic library.\n\n### Get random number\n\n```javascript\nconst rand = Exonum.randomUint64()\n```\n\n**Exonum.randomUint64** function generates a new random `Uint64` number of cryptographic quality\nusing the [TweetNaCl][tweetnacl:random-bytes] cryptographic library.\n\n### Converters\n\n#### Hexadecimal to `Uint8Array`\n\n```javascript\nconst hex = '674718178bd97d3ac5953d0d8e5649ea373c4d98b3b61befd5699800eaa8513b'\nExonum.hexadecimalToUint8Array(hex)\n```\n\n#### Hexadecimal to String\n\n```javascript\nconst hex = '674718178bd97d3ac5953d0d8e5649ea373c4d98b3b61befd5699800eaa8513b'\nExonum.hexadecimalToBinaryString(hex)\n```\n\n#### `Uint8Array` to Hexadecimal\n\n```javascript\nconst arr = new Uint8Array([103, 71, 24, 23, 139, 217, 125, 58, 197, 149, 61])\nExonum.uint8ArrayToHexadecimal(arr)\n```\n\n#### `Uint8Array` to Binary String\n\n```javascript\nconst arr = new Uint8Array([103, 71, 24, 23, 139, 217, 125, 58, 197, 149, 61])\nExonum.uint8ArrayToBinaryString(arr)\n```\n\n#### Binary String to `Uint8Array`\n\n```javascript\nconst str = '0110011101000111000110000001011110001011110110010111110100111010'\nExonum.binaryStringToUint8Array(str)\n```\n\n#### Binary String to Hexadecimal\n\n```javascript\nconst str = '0110011101000111000110000001011110001011110110010111110100111010'\nExonum.binaryStringToHexadecimal(str)\n```\n\n#### String to Uint8Array\n\n```javascript\nconst str = 'Hello world'\nExonum.stringToUint8Array(str)\n```\n\n## Contributing\n\nThe contributing to the Exonum Client is based on the same principles and rules as\n[the contributing to exonum-core][contributing].\n\n### Coding standards\n\nThe coding standards are described in the [`.eslintrc`](.eslintrc.json) file.\n\nTo help developers define and maintain consistent coding styles between different editors and IDEs\nwe used [`.editorconfig`](.editorconfig) configuration file.\n\n### Test coverage\n\nAll functions must include relevant unit tests.\nThis applies to both of adding new features and fixing existed bugs.\n\n## Changelog\n\nDetailed changes for each release are documented in the [CHANGELOG](CHANGELOG.md) file.\n\n## Other languages support\n\nLight Clients for [Java][lc-java] and [Python][lc-python]\n\n## License\n\nExonum Client is licensed under the Apache License (Version 2.0). See [LICENSE](LICENSE) for details.\n\n[docs:clients]: https://exonum.com/doc/version/latest/architecture/clients\n[docs:architecture:services]: https://exonum.com/doc/version/latest/architecture/services\n[docs:architecture:serialization]: https://exonum.com/doc/version/latest/architecture/serialization\n[docs:architecture:serialization:network-id]: https://exonum.com/doc/version/latest/architecture/serialization/#etwork-id\n[docs:architecture:serialization:protocol-version]: https://exonum.com/doc/version/latest/architecture/serialization/#protocol-version\n[docs:architecture:serialization:service-id]: https://exonum.com/doc/version/latest/architecture/serialization/#service-id\n[docs:architecture:serialization:message-id]: https://exonum.com/doc/version/latest/architecture/serialization/#message-id\n[docs:architecture:transactions]: https://exonum.com/doc/version/latest/architecture/transactions\n[docs:advanced:merkelized-list]: https://exonum.com/doc/version/latest/advanced/merkelized-list\n[docs:glossary:digital-signature]: https://exonum.com/doc/version/latest/glossary/#digital-signature\n[docs:glossary:hash]: https://exonum.com/doc/version/latest/glossary/#hash\n[docs:glossary:blockchain-state]: https://exonum.com/doc/version/latest/glossary/#blockchain-state\n[docs:glossary:merkle-tree]: https://exonum.com/doc/version/latest/glossary/#merkle-tree\n[docs:glossary:validator]: https://exonum.com/doc/version/latest/glossary/#validator\n[npmjs]: https://www.npmjs.com/package/exonum-client\n[gitter]: https://gitter.im/exonum/exonum\n[twitter]: https://twitter.com/ExonumPlatform\n[newsletter]: https://exonum.com/#newsletter\n[contributing]: https://exonum.com/doc/version/latest/contributing/\n[is-safe-integer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger\n[vector-structure]: https://doc.rust-lang.org/std/vec/struct.Vec.html\n[tweetnacl:key-pair]: https://github.com/dchest/tweetnacl-js#naclsignkeypair\n[tweetnacl:random-bytes]: https://github.com/dchest/tweetnacl-js#random-bytes-generation\n[protobuf]: https://developers.google.com/protocol-buffers/\n[protobufjs]: https://www.npmjs.com/package/protobufjs\n[lc-java]: https://github.com/exonum/exonum-java-binding/tree/master/exonum-light-client\n[lc-python]: https://github.com/exonum/exonum-python-client\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexonum%2Fexonum-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexonum%2Fexonum-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexonum%2Fexonum-client/lists"}