├── .eslintrc.json
├── .gitignore
├── .husky
└── pre-commit
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── javascript-sdk.iml
├── modules.xml
└── vcs.xml
├── .npmignore
├── .prettierrc
├── LICENSE
├── Makefile
├── README.md
├── docs
└── Protobuf.md
├── example.env
├── example
├── cdp.ts
├── hard.ts
├── query_auctions.ts
├── query_cdps.ts
├── query_swaps.ts
├── static
│ └── env.ts
├── swap.ts
├── swap_incoming.ts
├── swap_outgoing.ts
└── transfer.ts
├── package-lock.json
├── package.json
├── src
├── client
│ ├── hard
│ │ └── index.ts
│ ├── index.ts
│ └── swap
│ │ └── index.ts
├── crypto
│ └── index.ts
├── index.test.ts
├── index.ts
├── msg
│ ├── cosmos
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── earn
│ │ └── index.ts
│ ├── evmutil
│ │ └── index.ts
│ ├── hard
│ │ └── index.ts
│ ├── index.ts
│ ├── kava
│ │ └── index.ts
│ ├── liquid
│ │ └── index.ts
│ ├── router
│ │ └── index.ts
│ ├── savings
│ │ └── index.ts
│ └── swap
│ │ └── index.ts
├── tx
│ └── index.ts
├── types
│ ├── Address.ts
│ ├── Coin.ts
│ ├── DenomToClaim.ts
│ ├── Message.ts
│ ├── Strategy.ts
│ ├── VoteType.ts
│ ├── Wallet.ts
│ └── index.ts
└── utils
│ └── index.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
7 | "parser": "@typescript-eslint/parser",
8 | "parserOptions": {
9 | "ecmaVersion": 2020,
10 | "sourceType": "module"
11 | },
12 | "plugins": ["@typescript-eslint"],
13 | "rules": {}
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # build dir
5 | lib
6 | src/proto
7 | .build
8 |
9 | .eslintcache
10 |
11 | # Environment configuration for running examples
12 | .env
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/javascript-sdk.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # misc
5 | package-lock.json
6 |
7 | # examples
8 | example
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ROOT_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
2 | BUILD_DIR := $(ROOT_DIR)/.build
3 | OUT_DIR := $(ROOT_DIR)/src/proto
4 |
5 | KAVA_TAG ?= release/v0.24.x
6 | KAVA_PROTO_DIR = $(BUILD_DIR)/kava/proto
7 | KAVA_THIRD_PARTY_PROTO_DIR = $(BUILD_DIR)/kava/third_party/proto
8 |
9 | NPM_BIN_DIR := $(ROOT_DIR)/node_modules/.bin
10 | TS_PROTO_PLUGIN_PATH := $(NPM_BIN_DIR)/protoc-gen-ts_proto
11 |
12 | .PHONY: all
13 | all: proto-deps proto-gen
14 |
15 | .PHONY: proto-deps
16 | proto-deps: clean
17 | mkdir -p $(BUILD_DIR) && \
18 | cd $(BUILD_DIR) && \
19 | git clone https://github.com/kava-labs/kava.git --no-checkout --depth 1 --filter=blob:none --sparse && \
20 | cd $(BUILD_DIR)/kava && \
21 | git sparse-checkout init &&\
22 | git sparse-checkout add proto third_party && \
23 | git fetch origin $(KAVA_TAG) --depth 1 && \
24 | git checkout FETCH_HEAD
25 |
26 | .PHONY: proto-gen
27 | proto-gen:
28 | mkdir -p $(OUT_DIR) && \
29 | protoc \
30 | --plugin="protoc-gen-ts_proto=$(TS_PROTO_PLUGIN_PATH)" \
31 | --ts_proto_out="$(OUT_DIR)" \
32 | --ts_proto_opt="esModuleInterop=true,forceLong=long,useOptionals=messages,useExactTypes=false" \
33 | --proto_path="$(KAVA_PROTO_DIR)" \
34 | --proto_path="$(KAVA_THIRD_PARTY_PROTO_DIR)" \
35 | $(shell find $(KAVA_PROTO_DIR) $(KAVA_THIRD_PARTY_PROTO_DIR) -path -prune -o -name '*.proto' -print0 | xargs -0)
36 |
37 | .PHONY: clean
38 | clean:
39 | rm -rf $(BUILD_DIR)
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kava JavaScript SDK
2 |
3 | The Kava JavaScript SDK allows browsers and node.js clients to interact with Kava. Core functionality and query examples are in the `examples` folder.
4 |
5 | - 🟡 client - client that implements Kava transactions and messages.
6 | - 🟡 tx - Kava transaction types.
7 | - 🟢 msg - Kava message types.
8 | - 🟡 crypto - core cryptographic functions.
9 | - 🟢 utils - utility functions such as client-side secret generation.
10 |
11 | ## Proceed with Caution
12 |
13 | Due to limited resources on our team, some parts of the SDK are better maintained than others.
14 |
15 | - 🟡 Modules marked yellow are partially maintained and may be only partially functional.
16 | - 🟢 Modules marked green are best maintained and most reliable. Functionality should be up-to-date and functional in the latest stable or beta release.
17 |
18 | We welcome outside contributions to help keep the SDK as useful and up-to-date as possible.
19 |
20 | ## Installation
21 |
22 | Install the package via npm.
23 |
24 | ```bash
25 | npm install @kava-labs/javascript-sdk
26 | ```
27 |
28 | ## Examples
29 |
30 | Examples are still being updated to TypeScript, but can be run with the proper env config and typechecking disabled.
31 |
32 | ## Network Information
33 |
34 | ### Mainnet
35 |
36 | - Chain ID: kava-9
37 | - REST API endpoint: https://api.kava.io
38 | - Binance Chain mainnet REST API endpoint: https://dex.binance.org/
39 |
40 | ### Testnet
41 |
42 | - Chain ID: kava-testnet-14000
43 | - REST API endpoint: https://api.testnet.kava.io
44 | - Binance Chain testnet REST API endpoint: https://testnet-dex.binance.org/
45 |
46 | ### Binance Chain
47 |
48 | - Chain ID: Binance-Chain-Tigris
49 | - Binance Chain mainnet REST API endpoint: https://dex.binance.org/
50 |
51 | ### Deputy Addresses
52 |
53 | **BNB**
54 |
55 | - bnb1jh7uv2rm6339yue8k4mj9406k3509kr4wt5nxn
56 | - kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6
57 |
58 | **BTCB**
59 |
60 | - bnb1xz3xqf4p2ygrw9lhp5g5df4ep4nd20vsywnmpr
61 | - kava14qsmvzprqvhwmgql9fr0u3zv9n2qla8zhnm5pc
62 |
63 | **BUSD**
64 |
65 | - bnb10zq89008gmedc6rrwzdfukjk94swynd7dl97w8
66 | - kava1hh4x3a4suu5zyaeauvmv7ypf7w9llwlfufjmuu
67 |
68 | **XRPB**
69 |
70 | - bnb15jzuvvg2kf0fka3fl2c8rx0kc3g6wkmvsqhgnh
71 | - kava1c0ju5vnwgpgxnrktfnkccuth9xqc68dcdpzpas
72 |
73 | ## Client Setup
74 |
75 | The client requires an address mnemonic and the url of Kava's REST api endpoint.
76 |
77 | ```javascript
78 | const Kava = require('@kava-labs/javascript-sdk');
79 |
80 | var main = async () => {
81 | const mnemonic = 'secret words that unlock a kava address';
82 | const testnetUrl = 'https://api.data-testnet-12000.kava.io'; // testnet REST api endpoint
83 |
84 | // Declare a new Kava client, set wallet, and initialize
85 | let client = new Kava.KavaClient(testnetUrl);
86 | client.setWallet(mnemonic);
87 | client.setBroadcastMode('async');
88 | await client.initChain();
89 |
90 | // ...transfer coins, bid on an auction, create a CDP, etc.
91 | };
92 | ```
93 |
94 | ## Client Usage
95 |
96 | The following selected examples demonstrate basic client usage. Detailed examples can be found in the `examples` directory of the repository. It contains complete code examples for transferring funds from Binance Chain to Kava, opening a CDP, and transferring funds back to Binance Chain.
97 |
98 | ### Transfer coins
99 |
100 | ```javascript
101 | // Import Kava and initialize client...
102 |
103 | // Load coins and transfer to recipient's address
104 | const coins = Kava.utils.formatCoins(1, 'kava');
105 | const recipient = 'kava1c84ezutjcgrsxarjq5mzsxxz2k9znn94zxmqjz';
106 | const txHash = await client.transfer(recipient, coins);
107 |
108 | // Check the resulting tx hash
109 | const txRes = await client.checkTxHash(txHash, 15000); // 15 second timeout
110 | console.log('Tx result:', txRes.raw_log);
111 | ```
112 |
113 | ### Create CDP
114 |
115 | Collateralized debt positions have a minimum value of 10 USD and must be overcollateralized above a certain percentage threshold. Supported collateral coin types, their supply limits, and their minimum overcollateralization ratios can be checked at https://api.data.kava.io/cdp/parameters.
116 |
117 | While USDX has 6 decimals, our example collateral coin BNB has 8. We'll need to apply each asset's conversion factor before sending the transaction.
118 |
119 | ```javascript
120 | const BNB_CONVERSION_FACTOR = 10 ** 8;
121 | const USDX_CONVERSION_FACTOR = 10 ** 6;
122 |
123 | // Apply conversion factor
124 | const principalAmount = 10 * USDX_CONVERSION_FACTOR;
125 | const collateralAmount = 2 * BNB_CONVERSION_FACTOR;
126 |
127 | // Load principal, collateral as formatted coins and set up collateral type
128 | const principal = Kava.utils.formatCoin(principalAmount, 'usdx');
129 | const collateral = Kava.utils.formatCoin(collateralAmount, 'bnb');
130 | const collateralType = 'bnb-a';
131 |
132 | // Send create CDP tx using Kava client
133 | const txHashCDP = await client.createCDP(principal, collateral, collateralType);
134 | console.log('Create CDP tx hash (Kava): '.concat(txHashCDP));
135 |
136 | // Check the tx hash
137 | const txRes = await client.checkTxHash(txHashCDP, 15000);
138 | console.log('\nTx result:', txRes);
139 | ```
140 |
141 | ### Transferring funds to Kava
142 |
143 | Kava supports secure transfers of BNB from Binance Chain to Kava and back via atomic swaps. The [bep3-deputy](https://github.com/binance-chain/bep3-deputy) process sits between the two blockchains and services swaps by relaying information back and forth.
144 |
145 | Swaps use a simple secret sharing scheme. A secret random number is generated on the client and hashed with a timestamp in order to create a random number hash that's stored with the swap. The swap can be securely claimed on the opposite chain using the secret random number. Swaps expire after n blocks, a duration that can be modified via the height span parameter. Once expired, the swap can be refunded.
146 |
147 | BEP3 transfer user steps
148 |
149 | 1. Create an atomic swap on Binance Chain (note: atomic swaps are called HTLTs on Binance Chain)
150 | The deputy will automatically relay the swap from Kava to Binance Chain
151 | 2a. Claim the atomic swap on Kava within swap's height span. Users have about 30 minutes to claim a swap after it is created.
152 | 2b. Refund the atomic swap on Kava after the swap's height span - this happens if the swap is not claimed in time.
153 |
154 | ### Create swap
155 |
156 | In order for an address to submit a swap on Kava it must hold pegged bnb tokens. The Binance Chain [docs](https://docs.binance.org/atomic-swap.html) describe how to create a swap on Binance Chain with BNB. When creating the swap on Binance Chain make sure to use the deputy's Binance Chain address as the swap's `recipient` and the deputy's Kava address as the swap's `senderOtherChain` or the deputy will not relay the swap.
157 |
158 | Users create outgoing swaps on Kava by entering the deputy's Kava address in the recipient field. The following example is for the testnet. See full code examples for creating and claiming a swap between Kava and Binance Chain, see `incoming_swap.js` and `outgoing_swap.js` in the examples folder.
159 |
160 | ```javascript
161 | // Import utils
162 | const utils = kava.utils;
163 |
164 | // Declare addresses involved in the swap
165 | const recipient = 'kava1tfvn5t8qwngqd2q427za2mel48pcus3z9u73fl'; // deputy's address on kava testnet
166 | const recipientOtherChain = 'tbnb1hc0gvpxgw78ky9ay6xfql8jw9lry9ftc5g7ddj'; // user's address on bnbchain testnet
167 | const senderOtherChain = 'tbnb1mdvtph9y0agm4nx7dcl86t7nuvt5mtcul8zld6'; // deputy's address on bnbchain testnet
168 |
169 | // Set up swap parameters
170 | const amount = 1000000;
171 | const asset = 'bnb';
172 | const coins = utils.formatCoins(amount, asset);
173 | const heightSpan = '250';
174 |
175 | // Generate random number hash from timestamp and hex-encoded random number
176 | const randomNumber = utils.generateRandomNumber();
177 | const timestamp = Math.floor(Date.now() / 1000);
178 | const randomNumberHash = utils.calculateRandomNumberHash(
179 | randomNumber,
180 | timestamp
181 | );
182 | console.log('\nSecret random number:', randomNumber.toUpperCase());
183 |
184 | // Calculate the expected swap ID on Kava
185 | const kavaSwapID = utils.calculateSwapID(
186 | randomNumberHash,
187 | client.wallet.address,
188 | senderOtherChain
189 | );
190 | console.log('Expected Kava swap ID:', kavaSwapID);
191 |
192 | // Calculate the expected swap ID on Bnbchain
193 | const bnbchainSwapID = utils.calculateSwapID(
194 | randomNumberHash,
195 | senderOtherChain,
196 | client.wallet.address
197 | );
198 | console.log('Expected Bnbchain swap ID:', bnbchainSwapID);
199 |
200 | // Create the swap
201 | console.log('Sending createSwap transaction...');
202 | const txHash = await client.createSwap(
203 | recipient,
204 | recipientOtherChain,
205 | senderOtherChain,
206 | randomNumberHash,
207 | timestamp,
208 | coins,
209 | heightSpan
210 | );
211 |
212 | // Check the claim tx hash
213 | const txRes = await client.checkTxHash(txHash, 15000);
214 | console.log('\nTx result:', txRes.raw_log);
215 | ```
216 |
217 | Note: swap height span must be within range 220-270, you can check the current mainet BEP3 module parameters at https://api.data.kava.io/bep3/parameters.
218 |
219 | ### Claim swap
220 |
221 | Only active swaps can be claimed. Anyone can send the claim request, but funds will only be released to the intended recipient if the secret random number matches the random number hash. A successful claim sends funds exclusively to the intended recipient's address.
222 |
223 | ```javascript
224 | // Use the secret random number from swap creation
225 | const randomNumber =
226 | 'e8eae926261ab77d018202434791a335249b470246a7b02e28c3b2fb6ffad8f3';
227 | const swapID =
228 | 'e897e4ee12b4d6ec4776a5d30300a7e3bb1f62b0c49c3e05ad2e6aae1279c940';
229 |
230 | const txHash = await client.claimSwap(swapID, randomNumber);
231 | ```
232 |
233 | ### Refund swap
234 |
235 | Only expired swaps can be refunded. Anyone can send the refund request, but funds are always returned to the swap's original creator.
236 |
237 | ```javascript
238 | const swapID =
239 | 'e897e4ee12b4d6ec4776a5d30300a7e3bb1f62b0c49c3e05ad2e6aae1279c940';
240 |
241 | const txHash = await client.refundSwap(swapID);
242 | ```
243 |
244 | ## Contributing
245 |
246 | Kava is an open source project and contributions to the Kava JavaScript SDK are welcome. If you'd like contribute, please open an issue or pull request.
247 |
--------------------------------------------------------------------------------
/docs/Protobuf.md:
--------------------------------------------------------------------------------
1 | # Protobuf
2 |
3 | ## Build
4 |
5 | 1. Run `make` to download the protobuf definitions from Kava
6 | (You can also specify a version, e.g. `KAVA_TAG=v0.16.0 make all`)
7 |
8 | ### Troubleshooting
9 |
10 | 1. `brew install protobuf`
11 | 2. `brew install git` (Makes sure you have the latest version of Git that supports the `sparse` checkout (2.34.1 or later))
12 |
--------------------------------------------------------------------------------
/example.env:
--------------------------------------------------------------------------------
1 | KAVA_ADDRESS=
2 | KAVA_MNEMONIC=
3 | DEPUTY_ADDRESS=
4 | DEPUTY_MNEMONIC=
5 | BINANCE_ADDRESS=
6 | BINANCE_MNEMONIC=
7 | BINANCE_DEPUTY_ADDRESS=
8 | BINANCE_LOCAL_ENDPOINT=
9 |
--------------------------------------------------------------------------------
/example/cdp.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const kavaUtils = require('../src/utils').utils;
3 | const KavaClient = require('../src/client').KavaClient;
4 |
5 | const BNB_CONVERSION_FACTOR = 10 ** 8;
6 | const USDX_CONVERSION_FACTOR = 10 ** 6;
7 |
8 | var main = async () => {
9 | // Start new Kava client
10 | const kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
11 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
12 | kavaClient.setBroadcastMode('async');
13 | await kavaClient.initChain();
14 |
15 | // Get minimum principal amount required for CDP creation
16 | const paramsCDP = await kavaClient.getParamsCDP();
17 | const debtParam = paramsCDP.debt_param;
18 | const principalAmount = Number(debtParam.debt_floor);
19 | console.log('Minimum principal:', principalAmount + 'usdx');
20 |
21 | // Calculate collateral required for this principal amount
22 | const bnbValuation = await kavaClient.getPrice('bnb:usd');
23 | const equivalentCollateral =
24 | Number(principalAmount) / Number(bnbValuation.price);
25 |
26 | // Assuming the collateralization ratio is 200%, we'll do 210%
27 | const rawRequiredAmount = equivalentCollateral * 2.1;
28 | const adjustedAmount =
29 | (rawRequiredAmount / USDX_CONVERSION_FACTOR) * BNB_CONVERSION_FACTOR;
30 | const collateralAmount = adjustedAmount.toFixed(0);
31 | console.log('Required collateral:', collateralAmount + 'bnb');
32 |
33 | // Confirm that our account has sufficient funds
34 | try {
35 | const account = await kavaClient.getAccount(kavaClient.wallet.address);
36 | const coins = account.value.coins;
37 | const bnbBalance = coins.find((coin) => coin.denom == 'bnb').amount;
38 | if (bnbBalance * BNB_CONVERSION_FACTOR < Number(collateralAmount)) {
39 | throw { message: 'Account only has ' + bnbBalance + 'bnb' };
40 | }
41 | } catch (err) {
42 | console.log('Error:', err.message);
43 | return;
44 | }
45 |
46 | // Load principal, collateral as formatted coins
47 | const principal = kavaUtils.formatCoin(principalAmount, 'usdx');
48 | const collateral = kavaUtils.formatCoin(collateralAmount, 'bnb');
49 | const collateralType = 'bnb-a';
50 |
51 | // Send create CDP tx using Kava client
52 | const txHashCDP = await kavaClient.createCDP(
53 | principal,
54 | collateral,
55 | collateralType
56 | );
57 | console.log('Create CDP tx hash (Kava): '.concat(txHashCDP));
58 |
59 | // Check the claim tx hash
60 | const txRes = await kavaClient.checkTxHash(txHashCDP, 15000);
61 | console.log('\nTx result:', txRes);
62 | };
63 |
64 | main();
65 |
--------------------------------------------------------------------------------
/example/hard.ts:
--------------------------------------------------------------------------------
1 | import { env as Env } from './static/env';
2 | import { KavaClient } from '../src/client';
3 | import { utils as kavaUtils } from '../src/utils';
4 |
5 | var main = async () => {
6 | // Start new Kava client
7 | const kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
8 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
9 | await kavaClient.initChain();
10 |
11 | // Deposit coins into hard's BNB pool
12 | const despoitCoins = kavaUtils.formatCoins(100, 'bnb');
13 | const depositRes = await kavaClient.hard.deposit(despoitCoins);
14 | console.log('Deposit tx:', depositRes);
15 |
16 | await sleep(5000); // Wait 5 seconds
17 |
18 | // Withdraw coins from hard's BNB pool
19 | const withdrawCoins = kavaUtils.formatCoins(20, 'bnb');
20 | const withdrawRes = await kavaClient.hard.withdraw(withdrawCoins);
21 | console.log('Withdraw tx:', withdrawRes);
22 |
23 | await sleep(5000); // Wait 5 seconds
24 |
25 | // Claim hard rewards
26 | const args = { owner: Env.KavaAccount.Testnet.Address, type: 'hard' };
27 | const claim = await kavaClient.getRewards(args);
28 | if (claim) {
29 | const claimRes = await kavaClient.claimHardLiquidityProviderReward('small');
30 | console.log('Claim tx:', claimRes);
31 | }
32 | };
33 |
34 | // Sleep is a wait function
35 | function sleep(ms) {
36 | return new Promise((resolve) => setTimeout(resolve, ms));
37 | }
38 |
39 | main();
40 |
--------------------------------------------------------------------------------
/example/query_auctions.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const KavaClient = require('../src/client').KavaClient;
3 |
4 | var main = async () => {
5 | // Start new Kava client
6 | kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
7 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
8 | kavaClient.setBroadcastMode('async');
9 | await kavaClient.initChain();
10 |
11 | // Query all Auctions
12 | const allAuctions = await kavaClient.getAuctions();
13 | console.log('All Auctions:', allAuctions);
14 |
15 | // Query only Auctions that meet our filter
16 | const args = {
17 | type: 'collateral',
18 | phase: 'forward',
19 | denom: 'bnb',
20 | owner: 'kava1g0qywkx6mt5jmvefv6hs7c7h333qas5ks63a6t',
21 | page: 1,
22 | limit: 1000,
23 | };
24 | const filteredAuctions = await kavaClient.getAuctions(args);
25 | console.log('Filtered Auctions:', filteredAuctions);
26 | };
27 |
28 | main();
29 |
--------------------------------------------------------------------------------
/example/query_cdps.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const KavaClient = require('../src/client').KavaClient;
3 |
4 | var main = async () => {
5 | // Start new Kava client
6 | kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
7 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
8 | kavaClient.setBroadcastMode('async');
9 | await kavaClient.initChain();
10 |
11 | // Query all CDPs
12 | const allCdps = await kavaClient.getCDPs();
13 | console.log('All CDPs:', allCdps);
14 |
15 | // Query only CDPs that meet our filter
16 | const args = {
17 | collateral_type: 'bnb-a',
18 | id: '52',
19 | ratio: '3.15',
20 | owner: 'kava1g0qywkx6mt5jmvefv6hs7c7h333qas5ks63a6t',
21 | page: 1,
22 | limit: 1000,
23 | };
24 | const filteredCDPs = await kavaClient.getCDPs(args);
25 | console.log('Filtered CDPs:', filteredCDPs);
26 | };
27 |
28 | main();
29 |
--------------------------------------------------------------------------------
/example/query_swaps.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const KavaClient = require('../src/client').KavaClient;
3 |
4 | var main = async () => {
5 | // Start new Kava client
6 | kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
7 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
8 | kavaClient.setBroadcastMode('async');
9 | await kavaClient.initChain();
10 |
11 | // Query all swaps
12 | const allSwaps = await kavaClient.getSwaps();
13 | console.log('All swaps:', allSwaps);
14 |
15 | // Query only swaps that meet our filter
16 | const args = {
17 | direction: 'Incoming',
18 | status: 'Completed',
19 | involve: '',
20 | expiration: '1273092',
21 | page: 1,
22 | limit: 1000,
23 | };
24 | const filteredSwaps = await kavaClient.getSwaps(args);
25 | console.log('Filtered swaps:', filteredSwaps);
26 | };
27 |
28 | main();
29 |
--------------------------------------------------------------------------------
/example/static/env.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-var-requires
2 | require('dotenv').config();
3 |
4 | const KavaAccount = {
5 | Local: {
6 | Address: process.env.KAVA_ADDRESS,
7 | Mnemonic: process.env.KAVA_MNEMONIC,
8 | },
9 | Testnet: {
10 | Address: '',
11 | Mnemonic: '',
12 | },
13 | Mainnet: {
14 | Address: '',
15 | Mnemonic: '',
16 | },
17 | };
18 |
19 | const KavaEndpoints = {
20 | Local: 'http://localhost:1317',
21 | Testnet: 'https://kava-testnet-8000.kava.io',
22 | Mainnet: 'https://kava3.data.kava.io',
23 | };
24 |
25 | const KavaDeputy = {
26 | Local: {
27 | Address: process.env.DEPUTY_ADDRESS,
28 | Mnemonic: process.env.DEPUTY_MNEMONIC,
29 | },
30 | Testnet: 'kava1tfvn5t8qwngqd2q427za2mel48pcus3z9u73fl',
31 | Mainnet: 'kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6',
32 | };
33 |
34 | const BinanceAccount = {
35 | Local: {
36 | Address: process.env.BINANCE_ADDRESS,
37 | Mnemonic: process.env.BINANCE_MNEMONIC,
38 | },
39 | Testnet: {
40 | Address: '',
41 | Mnemonic: '',
42 | },
43 | Mainnet: {
44 | Address: '',
45 | Mnemonic: '',
46 | },
47 | };
48 |
49 | const BinanceEndpoints = {
50 | Local: process.env.BINANCE_LOCAL_ENDPOINT,
51 | Testnet: 'https://testnet-dex.binance.org',
52 | Mainnet: 'https://dex.binance.org/',
53 | };
54 |
55 | const BinanceDeputy = {
56 | Local: process.env.BINANCE_DEPUTY_ADDRESS,
57 | Testnet: 'tbnb1et8vmd0dgvswjnyaf73ez8ye0jehc8a7t7fljv',
58 | Mainnet: 'bnb1jh7uv2rm6339yue8k4mj9406k3509kr4wt5nxn',
59 | };
60 |
61 | export const env = {
62 | KavaAccount,
63 | KavaEndpoints,
64 | KavaDeputy,
65 | BinanceAccount,
66 | BinanceEndpoints,
67 | BinanceDeputy,
68 | };
69 |
--------------------------------------------------------------------------------
/example/swap.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const KavaClient = require('../src/client').KavaClient;
3 | const kavaUtils = require('../src/utils').utils;
4 |
5 | var main = async () => {
6 | // Start new Kava client
7 | kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
8 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
9 | await kavaClient.initChain();
10 |
11 | // deposit coins to the swap bnb:usdx pool
12 | const tokenA = kavaUtils.formatCoins(20, 'bnb');
13 | const tokenB = kavaUtils.formatCoins(10, 'usdx');
14 | const slippage = '0.010000000000000000';
15 | // create a deadline of 30 seconds from now for the max amount of time to wait
16 | // for the transaction to be processed
17 | const deadline = kavaUtils.calculateUnixTime(30);
18 | const depositRes = await kavaClient.swap.deposit(
19 | tokenA,
20 | tokenB,
21 | slippage,
22 | deadline
23 | );
24 | console.log('Deposit tx:', depositRes);
25 |
26 | await sleep(5000); // Wait 5 seconds
27 |
28 | // withdraw coins from the swap bnb:usdx pool
29 | const shares = 100;
30 | const minTokenA = kavaUtils.formatCoins(20, 'bnb');
31 | const minTokenB = kavaUtils.formatCoins(10, 'usdx');
32 | const deadline = kavaUtils.calculateUnixTime(30);
33 | const withdrawRes = await kavaClient.swap.withdraw(tokenA, tokenB, deadline);
34 | console.log('Withdraw tx:', withdrawRes);
35 |
36 | await sleep(5000); // Wait 5 seconds
37 |
38 | // Attempt a swap to a valid tradable pool with an exact tokenA
39 | // coins to deposit to the pool
40 | const exactTokenA = kavaUtils.formatCoins(100, 'bnb');
41 | // coins to withdraw from the pool
42 | const tokenB = kavaUtils.formatCoins(10, 'usdx');
43 | const slippage = '0.010000000000000000';
44 | const deadline = kavaUtils.calculateUnixTime(30);
45 |
46 | const swapExactForRes = await kavaClient.swap.newMsgSwapExactForTokens(
47 | exactTokenA,
48 | tokenB,
49 | slippage,
50 | deadline
51 | );
52 | console.log('Swap Exact For tx:', swapExactForRes);
53 |
54 | await sleep(5000); // Wait 5 seconds
55 |
56 | // Attempt a swap to a valid tradable pool with an exact tokenB
57 | // coins to deposit to the pool
58 | const tokenA = kavaUtils.formatCoins(100, 'bnb');
59 | // coins to withdraw from the pool
60 | const exactTokenB = kavaUtils.formatCoins(10, 'usdx');
61 | const slippage = '0.010000000000000000';
62 | const deadline = kavaUtils.calculateUnixTime(30);
63 |
64 | const swapForExactRes = await kavaClient.swap.newMsgSwapForExactTokens(
65 | tokenA,
66 | exactTokenB,
67 | slippage,
68 | deadline
69 | );
70 | console.log('Swap For Exact tx:', swapForExactRes);
71 |
72 | await sleep(5000); // Wait 5 seconds
73 |
74 | // claim swap rewards
75 | const multiplierName = 'large';
76 | const denomsToClaim = ['swp'];
77 |
78 | const args = { owner: Env.KavaAccount.Testnet.Address, type: 'swap' };
79 | const claims = await kavaClient.getRewards(args);
80 | if (claims) {
81 | const claimRes = await kavaClient.claimSwapReward(
82 | multiplierName,
83 | denomsToClaim
84 | );
85 | console.log('Claim tx:', claimRes);
86 | }
87 |
88 | await sleep(5000); // Wait 5 seconds
89 | };
90 |
91 | // Sleep is a wait function
92 | function sleep(ms) {
93 | return new Promise((resolve) => setTimeout(resolve, ms));
94 | }
95 |
96 | main();
97 |
--------------------------------------------------------------------------------
/example/swap_incoming.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const kavaUtils = require('../src/utils').utils;
3 | const KavaClient = require('../src/client').KavaClient;
4 | const BnbApiClient = require('@binance-chain/javascript-sdk');
5 | const bnbCrypto = BnbApiClient.crypto;
6 |
7 | const BNB_CONVERSION_FACTOR = 10 ** 8;
8 |
9 | var main = async () => {
10 | await incomingSwap();
11 | };
12 |
13 | var incomingSwap = async () => {
14 | // Start new Kava client
15 | kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
16 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
17 | kavaClient.setBroadcastMode('async');
18 | await kavaClient.initChain();
19 |
20 | // Start Binance Chain client
21 | const bnbClient = await new BnbApiClient(Env.BinanceEndpoints.Testnet);
22 | bnbClient.chooseNetwork('testnet');
23 | const privateKey = bnbCrypto.getPrivateKeyFromMnemonic(
24 | Env.BinanceAccount.Testnet.Mnemonic
25 | );
26 | bnbClient.setPrivateKey(privateKey);
27 | await bnbClient.initChain();
28 |
29 | // -------------------------------------------------------------------------------
30 | // Binance Chain blockchain interaction
31 | // -------------------------------------------------------------------------------
32 | // Assets involved in the swap
33 | const asset = 'BNB';
34 | const amount = 1 * BNB_CONVERSION_FACTOR;
35 |
36 | // Addresses involved in the swap
37 | const sender = Env.BinanceAccount.Testnet.Address; // user's address on Binance Chain
38 | const recipient = Env.BinanceDeputy.Testnet; // deputy's address on Binance Chain
39 | const senderOtherChain = Env.KavaDeputy.Testnet; // deputy's address on Kava
40 | const recipientOtherChain = Env.KavaAccount.Testnet.Address; // user's address on Kava
41 |
42 | // Format asset/amount parameters as tokens, expectedIncome
43 | const tokens = [
44 | {
45 | denom: asset,
46 | amount: amount,
47 | },
48 | ];
49 | const expectedIncome = [String(amount), ':', asset].join('');
50 |
51 | // Number of blocks that swap will be active
52 | const heightSpan = 10001;
53 |
54 | // Generate random number hash from timestamp and hex-encoded random number
55 | let randomNumber = kavaUtils.generateRandomNumber();
56 | const timestamp = Math.floor(Date.now() / 1000);
57 | const randomNumberHash = kavaUtils.calculateRandomNumberHash(
58 | randomNumber,
59 | timestamp
60 | );
61 | console.log('Secret random number:', randomNumber);
62 |
63 | printSwapIDs(randomNumberHash, sender, senderOtherChain);
64 |
65 | // Send create swap tx using Binance Chain client
66 | const res = await bnbClient.swap.HTLT(
67 | sender,
68 | recipient,
69 | recipientOtherChain,
70 | senderOtherChain,
71 | randomNumberHash,
72 | timestamp,
73 | tokens,
74 | expectedIncome,
75 | heightSpan,
76 | true
77 | );
78 |
79 | if (res && res.status == 200) {
80 | console.log('\nCreate swap tx hash (Binance Chain): ', res.result[0].hash);
81 | } else {
82 | console.log('Tx error:', res);
83 | return;
84 | }
85 | // Wait for deputy to see the new swap on Binance Chain and relay it to Kava
86 | console.log('Waiting for deputy to witness and relay the swap...');
87 |
88 | // Calculate the expected swap ID on Kava
89 | const expectedKavaSwapID = kavaUtils.calculateSwapID(
90 | randomNumberHash,
91 | senderOtherChain,
92 | sender
93 | );
94 |
95 | await sleep(45000); // 45 seconds
96 | await kavaClient.getSwap(expectedKavaSwapID);
97 |
98 | // -------------------------------------------------------------------------------
99 | // Kava blockchain interaction
100 | // -------------------------------------------------------------------------------
101 |
102 | // Send claim swap tx using Kava client
103 | const txHashClaim = await kavaClient.claimSwap(
104 | expectedKavaSwapID,
105 | randomNumber
106 | );
107 | console.log('Claim swap tx hash (Kava): '.concat(txHashClaim));
108 |
109 | // Check the claim tx hash
110 | const txRes = await kavaClient.checkTxHash(txHashClaim, 15000);
111 | console.log('\nTx result:', txRes.raw_log);
112 | };
113 |
114 | // Print swap IDs
115 | var printSwapIDs = (randomNumberHash, sender, senderOtherChain) => {
116 | // Calculate the expected swap ID on origin chain
117 | const originChainSwapID = kavaUtils.calculateSwapID(
118 | randomNumberHash,
119 | sender,
120 | senderOtherChain
121 | );
122 |
123 | // Calculate the expected swap ID on destination chain
124 | const destChainSwapID = kavaUtils.calculateSwapID(
125 | randomNumberHash,
126 | senderOtherChain,
127 | sender
128 | );
129 |
130 | console.log('Expected Bnbchain swap ID:', originChainSwapID);
131 | console.log('Expected Kava swap ID:', destChainSwapID);
132 | };
133 |
134 | // Sleep is a wait function
135 | function sleep(ms) {
136 | return new Promise((resolve) => setTimeout(resolve, ms));
137 | }
138 |
139 | main();
140 |
--------------------------------------------------------------------------------
/example/swap_outgoing.ts:
--------------------------------------------------------------------------------
1 | const Env = require('./static/env').env;
2 | const kavaUtils = require('../src/utils').utils;
3 | const KavaClient = require('../src/client').KavaClient;
4 | const BnbApiClient = require('@binance-chain/javascript-sdk');
5 | const bnbCrypto = BnbApiClient.crypto;
6 |
7 | var main = async () => {
8 | await outgoingSwap();
9 | };
10 |
11 | var outgoingSwap = async () => {
12 | // Start new Kava client
13 | kavaClient = new KavaClient(Env.KavaEndpoints.Testnet);
14 | kavaClient.setWallet(Env.KavaAccount.Testnet.Mnemonic);
15 | kavaClient.setBroadcastMode('async');
16 | await kavaClient.initChain();
17 |
18 | // Start Binance Chain client
19 | const bnbClient = await new BnbApiClient(Env.BinanceEndpoints.Testnet);
20 | bnbClient.chooseNetwork('testnet');
21 | const privateKey = bnbCrypto.getPrivateKeyFromMnemonic(
22 | Env.BinanceAccount.Testnet.Mnemonic
23 | );
24 | bnbClient.setPrivateKey(privateKey);
25 | await bnbClient.initChain();
26 |
27 | // -------------------------------------------------------------------------------
28 | // Kava blockchain interaction
29 | // -------------------------------------------------------------------------------
30 | const sender = Env.KavaAccount.Testnet.Address; // user's address on Binance Chain
31 | const recipient = Env.KavaDeputy.Testnet; // deputy's address on kava
32 | const recipientOtherChain = Env.BinanceAccount.Testnet.Address; // user's address on bnbchain
33 | const senderOtherChain = Env.BinanceDeputy.Testnet; // deputy's address on bnbchain
34 |
35 | // Set up params
36 | const asset = 'bnb';
37 | const amount = 10000000;
38 |
39 | const coins = kavaUtils.formatCoins(amount, asset);
40 | const heightSpan = '250';
41 |
42 | // Generate random number hash from timestamp and hex-encoded random number
43 | const randomNumber = kavaUtils.generateRandomNumber();
44 | const timestamp = Math.floor(Date.now() / 1000);
45 | const randomNumberHash = kavaUtils.calculateRandomNumberHash(
46 | randomNumber,
47 | timestamp
48 | );
49 | console.log('\nSecret random number:', randomNumber.toUpperCase());
50 |
51 | printSwapIDs(randomNumberHash, sender, senderOtherChain);
52 |
53 | const txHash = await kavaClient.createSwap(
54 | recipient,
55 | recipientOtherChain,
56 | senderOtherChain,
57 | randomNumberHash,
58 | timestamp,
59 | coins,
60 | heightSpan
61 | );
62 |
63 | console.log('\nTx hash (Create swap on Kava):', txHash);
64 |
65 | // Wait for deputy to see the new swap on Kava and relay it to Binance Chain
66 | console.log('Waiting for deputy to witness and relay the swap...');
67 | await sleep(45000); // 45 seconds
68 |
69 | // -------------------------------------------------------------------------------
70 | // Binance Chain blockchain interaction
71 | // -------------------------------------------------------------------------------
72 | // Calculate the expected swap ID on Bnbchain
73 | const expectedBnbchainSwapID = kavaUtils.calculateSwapID(
74 | randomNumberHash,
75 | senderOtherChain,
76 | sender
77 | );
78 |
79 | const res = await bnbClient.swap.claimHTLT(
80 | Env.BinanceAccount.Testnet.Address,
81 | expectedBnbchainSwapID,
82 | randomNumber
83 | ); // Binance-chain
84 | if (res && res.status == 200) {
85 | console.log('Claim swap tx hash (Binance Chain): ', res.result[0].hash);
86 | } else {
87 | console.log('Tx error:', res);
88 | return;
89 | }
90 | };
91 |
92 | // Print swap IDs
93 | var printSwapIDs = (randomNumberHash, sender, senderOtherChain) => {
94 | // Calculate the expected swap ID on origin chain
95 | const originChainSwapID = kavaUtils.calculateSwapID(
96 | randomNumberHash,
97 | sender,
98 | senderOtherChain
99 | );
100 |
101 | // Calculate the expected swap ID on destination chain
102 | const destChainSwapID = kavaUtils.calculateSwapID(
103 | randomNumberHash,
104 | senderOtherChain,
105 | sender
106 | );
107 |
108 | console.log('Expected Kava swap ID:', originChainSwapID.toUpperCase());
109 | console.log('Expected BnbChain swap ID:', destChainSwapID.toUpperCase());
110 | };
111 |
112 | // Sleep is a wait function
113 | function sleep(ms) {
114 | return new Promise((resolve) => setTimeout(resolve, ms));
115 | }
116 |
117 | main();
118 |
--------------------------------------------------------------------------------
/example/transfer.ts:
--------------------------------------------------------------------------------
1 | import { utils } from '../src/utils';
2 | import { env } from './static/env';
3 | import { KavaClient } from '../src/client';
4 |
5 | const KAVA_CONVERSION_FACTOR = 10 ** 6;
6 |
7 | var main = async () => {
8 | const recipient = 'kava1g0qywkx6mt5jmvefv6hs7c7h333qas5ks63a6t';
9 |
10 | // Start new Kava client
11 | const kavaClient = new KavaClient(env.KavaEndpoints.Testnet);
12 | kavaClient.setWallet(env.KavaAccount.Testnet.Mnemonic);
13 | kavaClient.setBroadcastMode('async');
14 | await kavaClient.initChain();
15 |
16 | // First let's check our account balances
17 | let balances = await kavaClient.getBalances(env.KavaAccount.Testnet.Address);
18 | console.log('Balances:', balances);
19 | // Print our KAVA balance (if we have one)
20 | let kavaBalance = balances?.find(
21 | (item) => item.denom.toUpperCase() === 'UKAVA'
22 | );
23 | if (kavaBalance) {
24 | console.log(
25 | '\tBalance (kava):',
26 | Number(kavaBalance.amount) / KAVA_CONVERSION_FACTOR
27 | );
28 | }
29 |
30 | // Transfer 1 kava to recipient's address
31 | const coins = utils.formatCoins(1 * KAVA_CONVERSION_FACTOR, 'ukava');
32 | const txHash = await kavaClient.transfer(recipient, coins);
33 | console.log('Tx hash:', txHash);
34 |
35 | // Check the resulting tx hash
36 | const txRes = await kavaClient.checkTxHash(txHash, 15000); // 15 seconds
37 | console.log('Tx result:', txRes);
38 | };
39 |
40 | main();
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kava-labs/javascript-sdk",
3 | "version": "14.0.2",
4 | "description": "Supports interaction with the Kava blockchain via a REST api",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "scripts": {
8 | "build": "make clean && make -j1 all && tsc",
9 | "format": "prettier --write \"src/**/*.ts\"",
10 | "postversion": "git push && git push --tags",
11 | "prepare": "npm run build && husky install",
12 | "version": "npm run format && git add -A src",
13 | "check": "tsc -p tsconfig.json"
14 | },
15 | "dependencies": {
16 | "@cosmjs/proto-signing": "^0.32.4",
17 | "@cosmjs/stargate": "^0.32.4",
18 | "@kava-labs/sig": "^0.1.0",
19 | "axios": "^1.6.5",
20 | "bech32": "^1.1.3",
21 | "big.js": "^5.2.2",
22 | "bip39": "^3.0.2",
23 | "crypto-js": "^4.0.0",
24 | "ethers": "^5.6.2",
25 | "long": "^5.2.0",
26 | "protobufjs": "^6.11.2",
27 | "url": "^0.11.0"
28 | },
29 | "devDependencies": {
30 | "@binance-chain/javascript-sdk": "^4.2.2",
31 | "@typescript-eslint/eslint-plugin": "^5.6.0",
32 | "@typescript-eslint/parser": "^5.6.0",
33 | "dotenv": "^14.3.0",
34 | "eslint": "^8.4.1",
35 | "husky": "^7.0.1",
36 | "jest": "^27.3.1",
37 | "lint-staged": "^11.1.1",
38 | "prettier": "2.0.5",
39 | "ts-jest": "^27.0.7",
40 | "ts-proto": "^1.150.0",
41 | "typescript": "^4.9.3"
42 | },
43 | "lint-staged": {
44 | "*.{js,jsx,ts,tsx}": "eslint --cache --fix",
45 | "*.{js,ts,jsx,tsx,css,md,yml,json,html}": "prettier --write"
46 | },
47 | "husky": {
48 | "hooks": {
49 | "pre-commit": "lint-staged"
50 | }
51 | },
52 | "jest": {
53 | "preset": "ts-jest",
54 | "testEnvironment": "node"
55 | },
56 | "repository": {
57 | "type": "git",
58 | "url": "git+https://github.com/Kava-Labs/javascript-sdk.git"
59 | },
60 | "author": "Kava Labs",
61 | "license": "Apache-2.0",
62 | "bugs": {
63 | "url": "https://github.com/Kava-Labs/javascript-sdk/issues"
64 | },
65 | "homepage": "https://github.com/Kava-Labs/javascript-sdk#readme"
66 | }
67 |
--------------------------------------------------------------------------------
/src/client/hard/index.ts:
--------------------------------------------------------------------------------
1 | import { tx } from '../../tx';
2 | import { msg } from '../../msg';
3 | import { KavaClient } from '..';
4 | import { Coin } from '../../types/Coin';
5 |
6 | const DEFAULT_GAS = 300000;
7 |
8 | const api = {
9 | getParams: '/hard/parameters',
10 | getModAccounts: '/hard/accounts',
11 | getDeposits: '/hard/deposits',
12 | getTotalDeposited: '/hard/total-deposited',
13 | getBorrows: '/hard/borrows',
14 | getTotalBorrowed: '/hard/total-borrowed',
15 | };
16 |
17 | export class Hard {
18 | // @ts-ignore
19 | public kavaClient: KavaClient;
20 | public static instance: Hard;
21 |
22 | constructor(kavaClient: KavaClient) {
23 | if (!Hard.instance) {
24 | this.kavaClient = kavaClient;
25 | Hard.instance = this;
26 | }
27 | return Hard.instance;
28 | }
29 |
30 | /**
31 | * Get the params of the hard module
32 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
33 | * @return {Promise}
34 | */
35 | async getParams(timeout = 2000) {
36 | const res = await tx.getTx(api.getParams, this.kavaClient.baseURI, timeout);
37 | if (res && res.data) {
38 | return res.data.result;
39 | }
40 | }
41 |
42 | /**
43 | * Get module accounts associated with the hard module
44 | * @param {Object} args optional arguments {name: "hard"|"hard_lp_distribution"}
45 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
46 | * @return {Promise}
47 | */
48 | async getModAccounts(args = {}, timeout = 2000) {
49 | const res = await tx.getTx(
50 | api.getModAccounts,
51 | this.kavaClient.baseURI,
52 | timeout,
53 | args
54 | );
55 | if (res && res.data) {
56 | return res.data.result;
57 | }
58 | }
59 |
60 | /**
61 | * Get hard deposits
62 | * @param {Object} args optional arguments {deposit_denom: "btc", deposit_type: "btc-a", owner: "kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny"}
63 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
64 | * @return {Promise}
65 | */
66 | async getDeposits(args = {}, timeout = 2000) {
67 | const res = await tx.getTx(
68 | api.getDeposits,
69 | this.kavaClient.baseURI,
70 | timeout,
71 | args
72 | );
73 | if (res && res.data) {
74 | return res.data.result;
75 | }
76 | }
77 |
78 | /**
79 | * Get hard total-deposited
80 | * @param {Object} args optional arguments {denom: "btc"}
81 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
82 | * @return {Promise}
83 | */
84 | async getTotalDeposited(args = {}, timeout = 2000) {
85 | const res = await tx.getTx(
86 | api.getTotalDeposited,
87 | this.kavaClient.baseURI,
88 | timeout,
89 | args
90 | );
91 | if (res && res.data) {
92 | return res.data.result;
93 | }
94 | }
95 |
96 | /**
97 | * Get hard borrows
98 | * @param {Object} args optional arguments {owner: "kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny" denom: "btc" }
99 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
100 | * @return {Promise}
101 | */
102 | async getBorrows(args = {}, timeout = 2000) {
103 | const res = await tx.getTx(
104 | api.getBorrows,
105 | this.kavaClient.baseURI,
106 | timeout,
107 | args
108 | );
109 | if (res && res.data) {
110 | return res.data.result;
111 | }
112 | }
113 |
114 | /**
115 | * Get hard total-borrowed
116 | * @param {Object} args optional arguments {denom: "btc"}
117 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
118 | * @return {Promise}
119 | */
120 | async getTotalBorrowed(args = {}, timeout = 2000) {
121 | const res = await tx.getTx(
122 | api.getTotalBorrowed,
123 | this.kavaClient.baseURI,
124 | timeout,
125 | args
126 | );
127 | if (res && res.data) {
128 | return res.data.result;
129 | }
130 | }
131 |
132 | /**
133 | * Deposit funds to a liquidity pool
134 | * @param {Object} amount the coins to be deposited
135 | * @param {Number} gas optional gas amount
136 | * @param {String} sequence optional account sequence
137 | * @return {Promise}
138 | */
139 | async deposit(amount: Coin[], gas = DEFAULT_GAS, sequence = null) {
140 | if (!this.kavaClient.wallet) {
141 | throw Error('Wallet has not yet been initialized');
142 | }
143 | const msgDeposit = msg.hard.newMsgDeposit(
144 | this.kavaClient.wallet.address,
145 | amount
146 | );
147 | const fee = { amount: [], gas: String(gas) };
148 | return await this.kavaClient.sendTx([msgDeposit], fee, sequence);
149 | }
150 |
151 | /**
152 | * Withdraw funds from a liquidity pool
153 | * @param {Object} amount the coins to be deposited
154 | * @param {Number} gas optional gas amount
155 | * @param {String} sequence optional account sequence
156 | * @return {Promise}
157 | */
158 | async withdraw(amount: Coin[], gas = DEFAULT_GAS, sequence = null) {
159 | if (!this.kavaClient.wallet) {
160 | throw Error('Wallet has not yet been initialized');
161 | }
162 | const msgWithdraw = msg.hard.newMsgWithdraw(
163 | this.kavaClient.wallet.address,
164 | amount
165 | );
166 | const fee = { amount: [], gas: String(gas) };
167 | return await this.kavaClient.sendTx([msgWithdraw], fee, sequence);
168 | }
169 |
170 | /**
171 | * Borrow available funds from a liquidity pool
172 | * @param {Object} amount the coins to be deposited
173 | * @param {Number} gas optional gas amount
174 | * @param {String} sequence optional account sequence
175 | * @return {Promise}
176 | */
177 | async borrow(amount: Coin[], gas = DEFAULT_GAS, sequence = null) {
178 | if (!this.kavaClient.wallet) {
179 | throw Error('Wallet has not yet been initialized');
180 | }
181 | const msgBorrow = msg.hard.newMsgBorrow(
182 | this.kavaClient.wallet.address,
183 | amount
184 | );
185 | const fee = { amount: [], gas: String(gas) };
186 | return await this.kavaClient.sendTx([msgBorrow], fee, sequence);
187 | }
188 |
189 | /**
190 | * Repay funds borrowed from a liquidity pool
191 | * @param {Object} amount the coins to be deposited
192 | * @param {Number} gas optional gas amount
193 | * @param {String} sequence optional account sequence
194 | * @return {Promise}
195 | */
196 | async repay(amount: Coin[], gas = DEFAULT_GAS, sequence = null) {
197 | if (!this.kavaClient.wallet) {
198 | throw Error('Wallet has not yet been initialized');
199 | }
200 | const msgRepay = msg.hard.newMsgRepay(
201 | this.kavaClient.wallet.address,
202 | this.kavaClient.wallet.address,
203 | amount
204 | );
205 | const fee = { amount: [], gas: String(gas) };
206 | return await this.kavaClient.sendTx([msgRepay], fee, sequence);
207 | }
208 |
209 | /**
210 | * Attempt to liquidate a borrower that's over their loan-to-value ratio
211 | * @param {String} borrower the borrower to be liquidated
212 | * @param {Number} gas optional gas amount
213 | * @param {String} sequence optional account sequence
214 | * @return {Promise}
215 | */
216 | async liquidate(borrower: string, gas = DEFAULT_GAS, sequence = null) {
217 | if (!this.kavaClient.wallet) {
218 | throw Error('Wallet has not yet been initialized');
219 | }
220 | const msgLiquidate = msg.hard.newMsgLiquidate(
221 | this.kavaClient.wallet.address,
222 | borrower
223 | );
224 | const fee = { amount: [], gas: String(gas) };
225 | return await this.kavaClient.sendTx([msgLiquidate], fee, sequence);
226 | }
227 | }
228 |
229 | module.exports.Hard = Hard;
230 |
--------------------------------------------------------------------------------
/src/client/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-explicit-any */
2 | const sig = require('@kava-labs/sig');
3 | import { tx } from '../tx';
4 | import { msg } from '../msg';
5 | import { Hard } from './hard';
6 | import { Swap } from './swap';
7 | import { DenomToClaim } from '../types/DenomToClaim';
8 | import { VoteType } from '../types/VoteType';
9 | import { Wallet } from '../types/Wallet';
10 | import { Coin } from '../types/Coin';
11 | import { Slip10RawIndex, HdPath } from '@cosmjs/crypto';
12 | import { DirectSecp256k1HdWallet, Registry } from '@cosmjs/proto-signing';
13 | import { SigningStargateClient } from '@cosmjs/stargate';
14 | import { MsgPostPrice } from '../proto/kava/pricefeed/v1beta1/tx';
15 | import { Decimal } from '@cosmjs/math';
16 | import { MsgRefundAtomicSwap } from '../proto/kava/bep3/v1beta1/tx';
17 |
18 | const KAVA_PREFIX = 'kava';
19 | const DERIVATION_PATH = "m/44'/459'/0'/0/0";
20 | const DERIVATION_PATH_LEGACY = "m/44'/118'/0'/0/0";
21 | const DEFAULT_FEE = { amount: [], gas: String(300000) };
22 | const DEFAULT_CDP_FEE = { amount: [], gas: String(650000) };
23 |
24 | const POST_PRICE_MSG_URL = '/kava.pricefeed.v1beta1.MsgPostPrice';
25 | const REFUND_SWAP_MSG_URL = '/kava.bep3.v1beta1.MsgRefundAtomicSwap';
26 |
27 | const api = {
28 | txs: '/cosmos/tx/v1beta1/txs',
29 | nodeInfo: '/cosmos/base/tendermint/v1beta1/node_info',
30 | getBlock: '/blocks',
31 | getLatestBlock: '/blocks/latest',
32 | getLatestValidatorSet: '/validatorsets/latest',
33 | getValidatorSet: '/validatorsets/',
34 | getParamsPricefeed: '/pricefeed/parameters',
35 | getParamsAuction: '/auction/parameters',
36 | getParamsCDP: '/cdp/parameters',
37 | getParamsBEP3: '/bep3/parameters',
38 | getParamsIncentive: '/incentive/parameters',
39 | getParamsCommittee: '/committee/parameters',
40 | getParamsIssuance: '/issuance/parameters',
41 | getAccount: '/auth/accounts',
42 | getBalances: '/bank/balances',
43 | getSupply: '/supply/total',
44 | getMarkets: 'pricefeed/markets',
45 | getOracles: 'pricefeed/oracles',
46 | getPrice: '/pricefeed/price',
47 | getRawPrices: '/kava/pricefeed/v1beta1/rawprices',
48 | getSwap: 'bep3/swap',
49 | getSwaps: 'kava/bep3/v1beta1/atomicswaps',
50 | getAssetSupply: 'bep3/supply',
51 | getAssetSupplies: 'bep3/supplies',
52 | getCDP: 'cdp/cdps/cdp',
53 | getCDPs: '/cdp/cdps',
54 | getCDPsByCollateralType: '/cdp/cdps/collateralType',
55 | getCDPsRatio: '/cdp/cdps/ratio',
56 | getDeposits: '/cdp/cdps/deposits',
57 | getAuction: '/auction/auctions',
58 | getAuctions: '/auction/auctions',
59 | getRewards: '/incentive/rewards',
60 | getCommittee: '/committee/committees', // endpoint also used by getCommitteeProposals
61 | getCommittees: '/committee/committees',
62 | getProposal: '/committee/proposals', // endpoint also used by getProposer, getProposalTally, getProposalVotes
63 | getDistributionRewards: '/distribution/delegators',
64 | getDelegations: '/staking/delegators',
65 | };
66 |
67 | /**
68 | * The Kava client.
69 | */
70 | export class KavaClient {
71 | public baseURI: string;
72 | public rpcURI: string;
73 | public broadcastMode: string;
74 | public hard: Hard;
75 | public swap: Swap;
76 | public wallet?: Wallet;
77 | public chainID?: string;
78 | public accNum?: string;
79 | public newWallet?: DirectSecp256k1HdWallet;
80 | public rpcClient?: SigningStargateClient;
81 |
82 | /**
83 | * @param {String} server Kava public url
84 | */
85 | constructor(server: string, rcpUri: string) {
86 | if (!server) {
87 | throw new Error('Kava server should not be null');
88 | }
89 | this.rpcURI = rcpUri;
90 | this.baseURI = server;
91 | this.broadcastMode = 'sync'; // default broadcast mode
92 | this.hard = new Hard(this);
93 | this.swap = new Swap(this);
94 | }
95 |
96 | /**
97 | * Initialize the client with the chain's ID. Asynchronous.
98 | * @return {Promise}
99 | */
100 | async initChain() {
101 | if (!this.chainID) {
102 | const res = await tx.getTx(api.nodeInfo, this.baseURI);
103 | this.chainID = res?.data?.default_node_info?.network;
104 | }
105 | return this;
106 | }
107 |
108 | /**
109 | * Manually set the chain's ID
110 | * @param {String} chainID Kava chain ID
111 | */
112 | setChainID(chainID: string) {
113 | if (!chainID) {
114 | throw new Error('chainID cannot be undefined');
115 | }
116 | this.chainID = chainID;
117 | return this;
118 | }
119 |
120 | /**
121 | * Manually set the wallet's account number
122 | * @param {String} accNum Account number of the Kava address
123 | */
124 | setAccountNumber(accNum: string) {
125 | if (!accNum) {
126 | throw new Error('account number cannot be undefined');
127 | }
128 | this.accNum = String(accNum);
129 | return this;
130 | }
131 |
132 | /**
133 | * Set broadcast mode
134 | * @param {String} mode transaction broadcast mode
135 | */
136 | setBroadcastMode(mode: string) {
137 | if (!mode) {
138 | throw new Error('broadcast mode cannot be undefined');
139 | }
140 | if (mode != 'async' && mode != 'sync' && mode != 'block') {
141 | throw new Error(
142 | [
143 | 'invalid broadcast mode ',
144 | mode,
145 | ' - must be async, sync, or block',
146 | ].join(' ')
147 | );
148 | }
149 | this.broadcastMode = String(mode);
150 | return this;
151 | }
152 |
153 | /**
154 | * Set the client's wallet which is used for signature generation
155 | * @param {String} mnemonic Kava address mnemonic
156 | * @param {String} password optional param for wallet password
157 | * @param {boolean} legacy optional param to use the legacy coin type
158 | * @return {Promise}
159 | */
160 | setWallet(mnemonic: string, password = '', legacy = false) {
161 | if (!mnemonic) {
162 | throw new Error('mnemonic cannot be undefined');
163 | }
164 | const derivationPath = legacy ? DERIVATION_PATH_LEGACY : DERIVATION_PATH;
165 | this.wallet = sig.createWalletFromMnemonic(
166 | mnemonic,
167 | password,
168 | KAVA_PREFIX,
169 | derivationPath
170 | );
171 | return this;
172 | }
173 |
174 | async setNewWallet(mnemonic: string, legacy = false) {
175 | const hdPath: HdPath = [
176 | Slip10RawIndex.hardened(44),
177 | legacy ? Slip10RawIndex.hardened(118) : Slip10RawIndex.hardened(459),
178 | Slip10RawIndex.hardened(0),
179 | Slip10RawIndex.normal(0),
180 | Slip10RawIndex.normal(0),
181 | ];
182 | this.newWallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
183 | prefix: 'kava',
184 | hdPaths: [hdPath],
185 | });
186 |
187 | // register type urls and create rpc client
188 | const registry = new Registry();
189 | registry.register(POST_PRICE_MSG_URL, MsgPostPrice);
190 | registry.register(REFUND_SWAP_MSG_URL, MsgRefundAtomicSwap);
191 | this.rpcClient = await SigningStargateClient.connectWithSigner(
192 | this.rpcURI,
193 | this.newWallet,
194 | { registry }
195 | );
196 | }
197 |
198 | /**
199 | * Load account number, sequence, and package with chain ID for signature
200 | * @param {String} sequence Kava address sequence
201 | * @return {Promise}
202 | */
203 | async prepareSignInfo(sequence: string | null) {
204 | let signInfo;
205 | if (sequence && this.accNum != null) {
206 | // Prepare signing info from manually set values
207 | signInfo = {
208 | chain_id: this.chainID,
209 | account_number: String(this.accNum),
210 | sequence: String(sequence),
211 | };
212 | } else {
213 | if (!this.wallet) {
214 | throw Error('Wallet has not yet been initialized');
215 | }
216 | // Load meta data from the account's chain state
217 | const meta = await tx.loadMetaData(this.wallet.address, this.baseURI);
218 | // Select manually set values over automatically pulled values
219 | signInfo = {
220 | chain_id: this.chainID,
221 | account_number:
222 | this.accNum != null
223 | ? String(this.accNum)
224 | : String(meta.account_number),
225 | sequence: sequence ? String(sequence) : String(meta.sequence),
226 | };
227 | }
228 | return signInfo;
229 | }
230 |
231 | /**
232 | * Sends messages to the Kava blockchain
233 | * @param {Array} msgs an array of msgs to be sent
234 | * @param {Object} fee the transaction's fee that includes gas amount
235 | * @param {String} sequence account sequence
236 | * @return {Promise}
237 | */
238 | async sendTx(msgs: any[], fee: any, sequence: string | null) {
239 | if (!this.newWallet) {
240 | throw Error('new wallet is not initialized, it is required to send tx.');
241 | }
242 | if (!this.rpcClient) {
243 | throw Error('rpc client is not initialized, it is required to send tx.');
244 | }
245 | const [firstAccount] = await this.newWallet.getAccounts();
246 | const defaultFee = { amount: [], gas: '300000' };
247 | const result = await this.rpcClient.signAndBroadcast(
248 | firstAccount.address,
249 | msgs,
250 | fee ? fee : defaultFee
251 | );
252 | return result.transactionHash;
253 | }
254 |
255 | /***************************************************
256 | * Tendermint
257 | ***************************************************/
258 | /**
259 | * Get the latest block
260 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
261 | * @return {Promise}
262 | */
263 | async getLatestBlock(timeout = 2000) {
264 | const res = await tx.getTx(api.getLatestBlock, this.baseURI, timeout);
265 | if (res && res.data) {
266 | return res.data;
267 | }
268 | }
269 |
270 | /**
271 | * Get a block at a specific height
272 | * @param {Number} height the block's height
273 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
274 | * @return {Promise}
275 | */
276 | async getBlock(height: number, timeout = 2000) {
277 | const path = api.getBlock + '/' + String(height);
278 | const res = await tx.getTx(path, this.baseURI, timeout);
279 | if (res && res.data) {
280 | return res.data;
281 | }
282 | }
283 |
284 | /**
285 | * Get the latest set of validators
286 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
287 | * @return {Promise}
288 | */
289 | async getLatestValidatorSet(timeout = 2000) {
290 | const res = await tx.getTx(
291 | api.getLatestValidatorSet,
292 | this.baseURI,
293 | timeout
294 | );
295 | if (res && res.data) {
296 | return res.data;
297 | }
298 | }
299 |
300 | /**
301 | * Get a set of validators at a specific block height
302 | * @param {Number} height the block's height
303 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
304 | * @return {Promise}
305 | */
306 | async getValidatorSet(height: number, timeout = 2000) {
307 | const path = api.getValidatorSet + '/' + String(height);
308 | const res = await tx.getTx(path, this.baseURI, timeout);
309 | if (res && res.data) {
310 | return res.data;
311 | }
312 | }
313 |
314 | /**
315 | * Checks a transaction hash for on-chain results
316 | * @param {String} txHash the transaction's hash
317 | * @param {Number} timeout milliseconds until the transaction will be considered not found
318 | * @return {Promise}
319 | */
320 | async checkTxHash(txHash: string, timeout = 10000) {
321 | const path = api.txs + '/' + txHash;
322 | let res;
323 |
324 | // Query the chain for a transaction with this hash
325 | try {
326 | res = await tx.getTx(path, this.baseURI, timeout);
327 | } catch (e) {
328 | throw new Error(`tx not found: ${e}`);
329 | }
330 |
331 | // If the transaction is found, check that it was accepted by the chain
332 | try {
333 | if (res?.data?.code) {
334 | throw new Error(`tx not accepted by chain: "${res.data.raw_log}"`);
335 | }
336 | } catch (e) {
337 | console.log('\n' + e);
338 | }
339 |
340 | return res.data;
341 | }
342 |
343 | /***************************************************
344 | * Cosmos SDK
345 | ***************************************************/
346 | /**
347 | * Get information about an account
348 | * @param {String} address account to query
349 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
350 | * @return {Promise}
351 | */
352 | async getAccount(address: string, timeout = 2000) {
353 | const path = api.getAccount + '/' + address;
354 | const res = await tx.getTx(path, this.baseURI, timeout);
355 | if (res && res.data) {
356 | return res.data.result;
357 | }
358 | }
359 |
360 | /**
361 | * Get an account's balances
362 | * @param {String} address account to query
363 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
364 | * @return {Promise}
365 | */
366 | async getBalances(address: string, timeout = 2000) {
367 | const path = api.getBalances + '/' + address;
368 | const res = await tx.getTx(path, this.baseURI, timeout);
369 | if (res && res.data) {
370 | return res.data.result as Coin[];
371 | }
372 | }
373 |
374 | /**
375 | * Get an account's delegators reward
376 | * @param {String} address account to query
377 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
378 | * @return {Promise}
379 | */
380 | async getDistributionRewards(address: string, timeout = 2000) {
381 | const path = api.getDistributionRewards + '/' + address + '/rewards';
382 | const res = await tx.getTx(path, this.baseURI, timeout);
383 | if (res && res.data) {
384 | return res.data.result;
385 | }
386 | }
387 |
388 | /**
389 | * Get an account's delegations
390 | * @param {String} address account to query
391 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
392 | * @return {Promise}
393 | */
394 | async getDelegations(address: string, timeout = 2000) {
395 | const path = api.getDelegations + '/' + address + '/delegations';
396 | const res = await tx.getTx(path, this.baseURI, timeout);
397 | if (res && res.data) {
398 | return res.data.result;
399 | }
400 | }
401 |
402 | /**
403 | * Get the total supply of coins on the chain
404 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
405 | * @return {Promise}
406 | */
407 | async getSupply(timeout = 2000) {
408 | const res = await tx.getTx(api.getSupply, this.baseURI, timeout);
409 | if (res && res.data) {
410 | return res.data.result;
411 | }
412 | }
413 |
414 | /**
415 | * Get the total supply of coins on the chain
416 | * @param {String} denom the name of the asset whose total supply will be queried
417 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
418 | * @return {Promise}
419 | */
420 | async getSupplyOf(denom: string, timeout = 2000) {
421 | const path = api.getSupply + '/' + denom;
422 | const res = await tx.getTx(path, this.baseURI, timeout);
423 | if (res && res.data) {
424 | return res.data.result;
425 | }
426 | }
427 |
428 | /**
429 | * Sends coins to an address
430 | * @param {String} recipient address that will receive coins
431 | * @param {String} coins amount of coins to send
432 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
433 | * @param {String} sequence optional account sequence
434 | * @return {Promise}
435 | */
436 | async transfer(
437 | recipient: string,
438 | coins: Coin[],
439 | fee = DEFAULT_FEE,
440 | sequence = null
441 | ) {
442 | if (!this.wallet) {
443 | throw Error('Wallet has not yet been initialized');
444 | }
445 | const msgSend = msg.cosmos.newMsgSend(
446 | this.wallet.address,
447 | recipient,
448 | coins
449 | );
450 | return await this.sendTx([msgSend], fee, sequence);
451 | }
452 |
453 | /***************************************************
454 | * Pricefeed
455 | ***************************************************/
456 | /**
457 | * Get the params of the pricefeed module
458 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
459 | * @return {Promise}
460 | */
461 | async getParamsPricefeed(timeout = 2000) {
462 | const res = await tx.getTx(api.getParamsPricefeed, this.baseURI, timeout);
463 | if (res && res.data) {
464 | return res.data.result;
465 | }
466 | }
467 |
468 | /**
469 | * Get the current system price of an asset
470 | * @param {String} market asset's market identifier
471 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
472 | * @return {Promise}
473 | */
474 | async getPrice(market: string, timeout = 2000) {
475 | const path = api.getPrice + '/' + market;
476 | const res = await tx.getTx(path, this.baseURI, timeout);
477 | if (res && res.data) {
478 | return res.data.result;
479 | }
480 | }
481 |
482 | /**
483 | * Get all active oracle prices for an asset
484 | * @param {String} market asset's market identifier
485 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
486 | * @return {Promise}
487 | */
488 | async getRawPrices(market: string, timeout = 2000) {
489 | const path = api.getRawPrices + '/' + market;
490 | const res = await tx.getTx(path, this.baseURI, timeout);
491 | if (res && res.data) {
492 | return res.data.raw_prices;
493 | }
494 | }
495 |
496 | /**
497 | * Get all active markets
498 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
499 | * @return {Promise}
500 | */
501 | async getMarkets(timeout = 2000) {
502 | const res = await tx.getTx(api.getMarkets, this.baseURI, timeout);
503 | if (res && res.data) {
504 | return res.data.result;
505 | }
506 | }
507 |
508 | /**
509 | * Get all active oracles for an asset
510 | * @param {String} denom asset's name
511 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
512 | * @return {Promise}
513 | */
514 | async getOracles(denom: string, timeout = 2000) {
515 | const path = api.getOracles + '/' + denom;
516 | const res = await tx.getTx(path, this.baseURI, timeout);
517 | if (res && res.data) {
518 | return res.data.result;
519 | }
520 | }
521 |
522 | /**
523 | * Allows oracles to post an asset's price to the pricefeed
524 | * @param {String} marketID the asset's on chain market ID, such as 'btc:usd'
525 | * @param {String} price the asset's price
526 | * @param {String} expiry time duration that this price is valid for
527 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
528 | * @param {String} sequence optional account sequence
529 | * @return {Promise}
530 | */
531 | async postPrice(
532 | marketID: string,
533 | price: string,
534 | expiry: Date,
535 | fee = DEFAULT_FEE,
536 | sequence = null
537 | ) {
538 | if (!this.newWallet) {
539 | throw Error('Wallet has not yet been initialized');
540 | }
541 | const [account] = await this.newWallet.getAccounts();
542 | const parsed = Decimal.fromUserInput(price, 18);
543 | const msgPostPrice = {
544 | typeUrl: POST_PRICE_MSG_URL,
545 | value: MsgPostPrice.create({
546 | from: account.address,
547 | marketId: marketID,
548 | price: parsed.atomics,
549 | expiry,
550 | }),
551 | };
552 | return await this.sendTx([msgPostPrice], fee, sequence);
553 | }
554 |
555 | /***************************************************
556 | * Auction
557 | ***************************************************/
558 | /**
559 | * Get the params of the auction module
560 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
561 | * @return {Promise}
562 | */
563 | async getParamsAuction(timeout = 2000) {
564 | const res = await tx.getTx(api.getParamsAuction, this.baseURI, timeout);
565 | if (res && res.data) {
566 | return res.data.result;
567 | }
568 | }
569 |
570 | /**
571 | * Get auction by ID
572 | * @param {String} id auctions unique identifier
573 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
574 | * @return {Promise}
575 | */
576 | async getAuction(id: string, timeout = 2000) {
577 | const path = api.getAuction + '/' + id;
578 | const res = await tx.getTx(path, this.baseURI, timeout);
579 | if (res && res.data) {
580 | return res.data.result;
581 | }
582 | }
583 |
584 | /**
585 | * Get auctions, filterable by args.
586 | * @param {Object} args request args as JSON. Example: {type: "collateral", denom: "btc", owner: "kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny"}
587 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
588 | * @return {Promise}
589 | */
590 | async getAuctions(args = {}, timeout = 2000) {
591 | const res = await tx.getTx(api.getAuctions, this.baseURI, timeout, args);
592 | if (res && res.data) {
593 | return res.data.result;
594 | }
595 | }
596 |
597 | /**
598 | * Place a bid on an auction
599 | * @param {String} auctionID the unique ID of the auction
600 | * @param {String} amount the coins amount to bid
601 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
602 | * @param {String} sequence optional account sequence
603 | * @return {Promise}
604 | */
605 | async placeBid(
606 | auctionID: string,
607 | amount: Coin,
608 | fee = DEFAULT_FEE,
609 | sequence = null
610 | ) {
611 | if (!this.wallet) {
612 | throw Error('Wallet has not yet been initialized');
613 | }
614 | const msgPlaceBid = msg.kava.newMsgPlaceBid(
615 | auctionID,
616 | this.wallet.address,
617 | amount
618 | );
619 | return await this.sendTx([msgPlaceBid], fee, sequence);
620 | }
621 |
622 | /***************************************************
623 | * CDP
624 | ***************************************************/
625 | /**
626 | * Get the params of the cdp module
627 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
628 | * @return {Promise}
629 | */
630 | async getParamsCDP(timeout = 2000) {
631 | const res = await tx.getTx(api.getParamsCDP, this.baseURI, timeout);
632 | if (res && res.data) {
633 | return res.data.result;
634 | }
635 | }
636 |
637 | /**
638 | * Get CDP if one exists for an owner and asset type
639 | * @param {String} owner address of the CDP's owner
640 | * @param {String} collateralType type of the CDP's collateral asset
641 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
642 | * @return {Promise}
643 | */
644 | async getCDP(owner: string, collateralType: string, timeout = 2000) {
645 | const path = api.getCDP + '/' + owner + '/' + collateralType;
646 | const res = await tx.getTx(path, this.baseURI, timeout);
647 | if (res && res.data) {
648 | return res.data.result;
649 | }
650 | }
651 |
652 | /**
653 | * Get all CDPs by filterable args
654 | * @param {Object} args request args as JSON. Example: {collateral-type: "btc-a", id: "52", ratio: "2.75", owner: "kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny"}
655 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
656 | * @return {Promise}
657 | */
658 | async getCDPs(args = {}, timeout = 2000) {
659 | const res = await tx.getTx(api.getCDPs, this.baseURI, timeout, args);
660 | if (res && res.data) {
661 | return res.data.result;
662 | }
663 | }
664 |
665 | /**
666 | * Get all CDPs for an asset type
667 | * @param {String} collateralType type of the CDP's collateral asset
668 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
669 | * @return {Promise}
670 | */
671 | async getCDPsByCollateralType(collateralType: string, timeout = 2000) {
672 | const path = api.getCDPsByCollateralType + '/' + collateralType;
673 | const res = await tx.getTx(path, this.baseURI, timeout);
674 | if (res && res.data) {
675 | return res.data.result;
676 | }
677 | }
678 |
679 | /**
680 | * Get all CDPs for an asset that are under the collateralization ratio specified
681 | * @param {String} collateralType type of the CDP's collateral asset
682 | * @param {String} ratio upper collateralization ratio limit of the query
683 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
684 | * @return {Promise}
685 | */
686 | async getCDPsByRatio(collateralType: string, ratio: string, timeout = 2000) {
687 | const path = api.getCDPsRatio + '/' + collateralType + '/' + ratio;
688 | const res = await tx.getTx(path, this.baseURI, timeout);
689 | if (res && res.data) {
690 | return res.data.result;
691 | }
692 | }
693 |
694 | /**
695 | * Get all deposits for the CDP with the specified owner and collateral type
696 | * @param {String} owner the address that owns the CDP
697 | * @param {String} collateralType denom of the CDP's collateral asset
698 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
699 | * @return {Promise}
700 | */
701 | async getDeposits(owner: string, collateralType: string, timeout = 2000) {
702 | const path = api.getDeposits + '/' + owner + '/' + collateralType;
703 | const res = await tx.getTx(path, this.baseURI, timeout);
704 | if (res && res.data) {
705 | return res.data.result;
706 | }
707 | }
708 |
709 | /**
710 | * Create a collateralized debt position
711 | * @param {String} principal the coins that will be drawn as debt
712 | * @param {String} collateral the coins that will be held as collateral
713 | * @param {String} collateralType the CDP's collateral type
714 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
715 | * @param {String} sequence optional account sequence
716 | * @return {Promise}
717 | */
718 | async createCDP(
719 | principal: Coin,
720 | collateral: Coin,
721 | collateralType: string,
722 | fee = DEFAULT_CDP_FEE,
723 | sequence = null
724 | ) {
725 | if (!this.wallet) {
726 | throw Error('Wallet has not yet been initialized');
727 | }
728 | const msgCreateCDP = msg.kava.newMsgCreateCDP(
729 | this.wallet.address,
730 | principal,
731 | collateral,
732 | collateralType
733 | );
734 | return await this.sendTx([msgCreateCDP], fee, sequence);
735 | }
736 |
737 | /**
738 | * Deposit collateral into a collateralized debt position
739 | * @param {String} owner the owner of the CDP
740 | * @param {String} collateral the coins that will deposited as additional collateral
741 | * @param {String} collateralType the CDP's collateral type
742 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
743 | * @param {String} sequence optional account sequence
744 | * @return {Promise}
745 | */
746 | async deposit(
747 | owner: string,
748 | collateral: Coin,
749 | collateralType: string,
750 | fee = DEFAULT_CDP_FEE,
751 | sequence = null
752 | ) {
753 | if (!this.wallet) {
754 | throw Error('Wallet has not yet been initialized');
755 | }
756 | const msgDeposit = msg.kava.newMsgDeposit(
757 | owner,
758 | this.wallet.address,
759 | collateral,
760 | collateralType
761 | );
762 | return await this.sendTx([msgDeposit], fee, sequence);
763 | }
764 |
765 | /**
766 | * Withdraw collateral from a collateralized debt position
767 | * @param {String} owner the owner of the CDP
768 | * @param {String} collateral the coins that will withdrawn from existing collateral
769 | * @param {String} collateralType the CDP's collateral type
770 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
771 | * @param {String} sequence optional account sequence
772 | * @return {Promise}
773 | */
774 | async withdraw(
775 | owner: string,
776 | collateral: Coin,
777 | collateralType: string,
778 | fee = DEFAULT_CDP_FEE,
779 | sequence = null
780 | ) {
781 | if (!this.wallet) {
782 | throw Error('Wallet has not yet been initialized');
783 | }
784 | const msgWithdraw = msg.kava.newMsgWithdraw(
785 | owner,
786 | this.wallet.address,
787 | collateral,
788 | collateralType
789 | );
790 | return await this.sendTx([msgWithdraw], fee, sequence);
791 | }
792 |
793 | /**
794 | * Draw additional debt from a collateralized debt position
795 | * @param {String} collateralType the CDP's collateral type
796 | * @param {String} principal the coins that will be drawn as additional principal
797 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
798 | * @param {String} sequence optional account sequence
799 | * @return {Promise}
800 | */
801 | async drawDebt(
802 | collateralType: string,
803 | principal: Coin,
804 | fee = DEFAULT_CDP_FEE,
805 | sequence = null
806 | ) {
807 | if (!this.wallet) {
808 | throw Error('Wallet has not yet been initialized');
809 | }
810 | const msgDrawDebt = msg.kava.newMsgDrawDebt(
811 | this.wallet.address,
812 | collateralType,
813 | principal
814 | );
815 | return await this.sendTx([msgDrawDebt], fee, sequence);
816 | }
817 |
818 | /**
819 | * Repay debt by returning principal to a collateralized debt position
820 | * @param {String} collateralType the CDP's collateral type
821 | * @param {String} payment the amount of pricipal to be repaid
822 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
823 | * @param {String} sequence optional account sequence
824 | * @return {Promise}
825 | */
826 | async repayDebt(
827 | collateralType: string,
828 | payment: Coin,
829 | fee = DEFAULT_CDP_FEE,
830 | sequence = null
831 | ) {
832 | if (!this.wallet) {
833 | throw Error('Wallet has not yet been initialized');
834 | }
835 | const msgRepayDebt = msg.kava.newMsgRepayDebt(
836 | this.wallet.address,
837 | collateralType,
838 | payment
839 | );
840 | return await this.sendTx([msgRepayDebt], fee, sequence);
841 | }
842 |
843 | /**
844 | * Attempt to liquidate a borrower that's over their loan-to-value ratio
845 | * @param {String} borrower the borrower to be liquidated
846 | * @param {String} collateralType the collateral type to be liquidated
847 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
848 | * @param {String} sequence optional account sequence
849 | * @return {Promise}
850 | */
851 | async liquidate(
852 | borrower: string,
853 | collateralType: string,
854 | fee = DEFAULT_CDP_FEE,
855 | sequence = null
856 | ) {
857 | if (!this.wallet) {
858 | throw Error('Wallet has not yet been initialized');
859 | }
860 | const msgLiquidate = msg.kava.newMsgLiquidate(
861 | this.wallet.address,
862 | borrower,
863 | collateralType
864 | );
865 | return await this.sendTx([msgLiquidate], fee, sequence);
866 | }
867 |
868 | /***************************************************
869 | * BEP3
870 | ***************************************************/
871 | /**
872 | * Get the params of the bep3 module
873 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
874 | * @return {Promise}
875 | */
876 | async getParamsBEP3(timeout = 2000) {
877 | const res = await tx.getTx(api.getParamsBEP3, this.baseURI, timeout);
878 | if (res && res.data) {
879 | return res.data.result;
880 | }
881 | }
882 |
883 | /**
884 | * Get a swap by its ID
885 | * @param {String} swapID the swap's unique identifier
886 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
887 | * @return {Promise}
888 | */
889 | async getSwap(swapID: string, timeout = 2000) {
890 | const path = api.getSwap + '/' + swapID;
891 | const res = await tx.getTx(path, this.baseURI, timeout);
892 | if (res && res.data) {
893 | return res.data.result;
894 | }
895 | }
896 |
897 | /**
898 | * Get swaps, filterable by args.
899 | * @param {Object} args request args as JSON. Example: {status: "Open", direction: "Incoming"}
900 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
901 | * @return {Promise}
902 | */
903 | async getSwaps(args = {}, timeout = 2000) {
904 | const res = await tx.getTx(api.getSwaps, this.baseURI, timeout, args);
905 | if (res && res.data) {
906 | return res.data.atomic_swaps;
907 | }
908 | }
909 |
910 | /**
911 | * Get an asset's total supply by its denom
912 | * @param {String} assetDenom the asset's denom
913 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
914 | * @return {Promise}
915 | */
916 | async getAssetSupply(assetDenom: string, timeout = 2000) {
917 | const path = api.getAssetSupply + '/' + assetDenom;
918 | const res = await tx.getTx(path, this.baseURI, timeout);
919 | if (res && res.data) {
920 | return res.data.result;
921 | }
922 | }
923 |
924 | /**
925 | * Get all supplies
926 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
927 | * @return {Promise}
928 | */
929 | async getAssetSupplies(timeout = 2000) {
930 | const res = await tx.getTx(api.getAssetSupplies, this.baseURI, timeout);
931 | if (res && res.data) {
932 | return res.data.result;
933 | }
934 | }
935 |
936 | /**
937 | * Create an atomic swap
938 | * @param {String} recipient the receiver's address on kava
939 | * @param {String} recipientOtherChain the receiver's address on the other chain
940 | * @param {String} senderOtherChain the sender's address on the other chain
941 | * @param {String} randomNumberHash resulting hex-encoded hash from sha256(timestamp, random number)
942 | * @param {String} timestamp the timestamp in unix, must be within 15-30 minutes of current time
943 | * @param {String} amount the amount in coins to be transferred
944 | * @param {String} heightSpan the number of blocks that this swap will be active/claimable
945 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
946 | * @param {String} sequence optional account sequence
947 | * @return {Promise}
948 | */
949 | async createSwap(
950 | recipient: string,
951 | recipientOtherChain: string,
952 | senderOtherChain: string,
953 | randomNumberHash: string,
954 | timestamp: number,
955 | amount: Coin[],
956 | heightSpan: number,
957 | fee = DEFAULT_FEE,
958 | sequence = null
959 | ) {
960 | if (!this.wallet) {
961 | throw Error('Wallet has not yet been initialized');
962 | }
963 | const msgCreateAtomicSwap = msg.kava.newMsgCreateAtomicSwap(
964 | this.wallet.address,
965 | recipient,
966 | recipientOtherChain,
967 | senderOtherChain,
968 | randomNumberHash.toUpperCase(),
969 | timestamp,
970 | amount,
971 | heightSpan
972 | );
973 | return await this.sendTx([msgCreateAtomicSwap], fee, sequence);
974 | }
975 |
976 | /**
977 | * Claim an atomic swap
978 | * @param {String} swapID the swap's unique identifier
979 | * @param {String} randomNumber the secret random number used to generate this swap's random number hash
980 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
981 | * @param {String} sequence optional account sequence
982 | * @return {Promise}
983 | */
984 | async claimSwap(
985 | swapID: string,
986 | randomNumber: string,
987 | fee = DEFAULT_FEE,
988 | sequence = null
989 | ) {
990 | if (!this.wallet) {
991 | throw Error('Wallet has not yet been initialized');
992 | }
993 | const msgClaimAtomicSwap = msg.kava.newMsgClaimAtomicSwap(
994 | this.wallet.address,
995 | swapID.toUpperCase(),
996 | randomNumber.toUpperCase()
997 | );
998 | return await this.sendTx([msgClaimAtomicSwap], fee, sequence);
999 | }
1000 |
1001 | /**
1002 | * Refund an atomic swap
1003 | * @param {String} swapID the swap's unique identifier
1004 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1005 | * @param {String} sequence optional account sequence
1006 | * @return {Promise}
1007 | */
1008 | async refundSwap(swapID: string, fee = DEFAULT_FEE, sequence = null) {
1009 | if (!this.newWallet) {
1010 | throw Error('New wallet has not yet been initialized');
1011 | }
1012 | const [account] = await this.newWallet.getAccounts();
1013 | const msgRefundSwap = {
1014 | typeUrl: REFUND_SWAP_MSG_URL,
1015 | value: MsgRefundAtomicSwap.create({
1016 | from: account.address,
1017 | swapId: swapID.toUpperCase(),
1018 | }),
1019 | };
1020 | return await this.sendTx([msgRefundSwap], fee, sequence);
1021 | }
1022 |
1023 | /***************************************************
1024 | * Incentive
1025 | ***************************************************/
1026 | /**
1027 | * Get the params of the incentive module
1028 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1029 | * @return {Promise}
1030 | */
1031 | async getParamsIncentive(timeout = 2000) {
1032 | const res = await tx.getTx(api.getParamsIncentive, this.baseURI, timeout);
1033 | if (res && res.data) {
1034 | return res.data.result;
1035 | }
1036 | }
1037 |
1038 | /**
1039 | * Get the claims of an address for a specific denom
1040 | * @param {Number} args query arguments
1041 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1042 | * @return {Promise}
1043 | */
1044 | async getRewards(args = {}, timeout = 2000) {
1045 | const path = api.getRewards;
1046 | const res = await tx.getTx(path, this.baseURI, timeout, args);
1047 | if (res && res.data) {
1048 | return res.data.result;
1049 | }
1050 | }
1051 |
1052 | /**
1053 | * Claim USDX minting reward using a specific multiplier
1054 | * @param {String} multiplierName the multiplier to claim with, such as 'small' or 'large'
1055 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1056 | * @param {String} sequence optional account sequence
1057 | * @return {Promise}
1058 | */
1059 | async claimUSDXMintingReward(
1060 | multiplierName: string,
1061 | fee = DEFAULT_FEE,
1062 | sequence = null
1063 | ) {
1064 | if (!this.wallet) {
1065 | throw Error('Wallet has not yet been initialized');
1066 | }
1067 | const msgClaimUSDXMintingReward = msg.kava.newMsgClaimUSDXMintingReward(
1068 | this.wallet.address,
1069 | multiplierName
1070 | );
1071 | return await this.sendTx([msgClaimUSDXMintingReward], fee, sequence);
1072 | }
1073 |
1074 | /**
1075 | * Claim Hard protocol reward using a specific multiplier and denoms
1076 | * @params {Array} choose which denom(s) of your rewards that you would like to claim
1077 | * and at which multiplier you claim them
1078 | * [{ denom: 'hard', multiplierName: 'large'}, { denom: 'ukava', multiplierName: 'small' }]
1079 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1080 | * @param {String} sequence optional account sequence
1081 | * @return {Promise}
1082 | */
1083 | async claimHardReward(
1084 | denomsToClaim: DenomToClaim[],
1085 | fee = DEFAULT_FEE,
1086 | sequence = null
1087 | ) {
1088 | if (!this.wallet) {
1089 | throw Error('Wallet has not yet been initialized');
1090 | }
1091 | const msgClaimHardReward = msg.kava.newMsgClaimHardReward(
1092 | this.wallet.address,
1093 | denomsToClaim
1094 | );
1095 | return await this.sendTx([msgClaimHardReward], fee, sequence);
1096 | }
1097 |
1098 | /**
1099 | * Claim Delegator reward using a specific multiplier and denoms
1100 | * @params {Array} choose which denom(s) of your rewards that you would like to claim
1101 | * and at which multiplier you claim them
1102 | * [{ denom: 'hard', multiplierName: 'large'}, { denom: 'ukava', multiplierName: 'small' }]
1103 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1104 | * @param {String} sequence optional account sequence
1105 | * @return {Promise}
1106 | */
1107 | async claimDelegatorReward(
1108 | denomsToClaim: DenomToClaim[],
1109 | fee = DEFAULT_FEE,
1110 | sequence = null
1111 | ) {
1112 | if (!this.wallet) {
1113 | throw Error('Wallet has not yet been initialized');
1114 | }
1115 | const msgClaimDelegatorReward = msg.kava.newMsgClaimDelegatorReward(
1116 | this.wallet.address,
1117 | denomsToClaim
1118 | );
1119 | return await this.sendTx([msgClaimDelegatorReward], fee, sequence);
1120 | }
1121 |
1122 | /**
1123 | * Claim swap reward using a specific multiplier and denoms
1124 | * @params {Array} choose which denom(s) of your rewards that you would like to claim
1125 | * and at which multiplier you claim them
1126 | * [{ denom: 'hard', multiplierName: 'large'}, { denom: 'ukava', multiplierName: 'small' }]
1127 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1128 | * @param {String} sequence optional account sequence
1129 | * @return {Promise}
1130 | */
1131 | async claimSwapReward(
1132 | denomsToClaim: DenomToClaim[],
1133 | fee = DEFAULT_FEE,
1134 | sequence = null
1135 | ) {
1136 | if (!this.wallet) {
1137 | throw Error('Wallet has not yet been initialized');
1138 | }
1139 | const msgClaimSwapReward = msg.kava.newMsgClaimSwapReward(
1140 | this.wallet.address,
1141 | denomsToClaim
1142 | );
1143 | return await this.sendTx([msgClaimSwapReward], fee, sequence);
1144 | }
1145 |
1146 | /***************************************************
1147 | * Committee
1148 | ***************************************************/
1149 | /**
1150 | * Get the params of the committee module
1151 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1152 | * @return {Promise}
1153 | */
1154 | async getParamsCommittee(timeout = 2000) {
1155 | const res = await tx.getTx(api.getParamsCommittee, this.baseURI, timeout);
1156 | if (res && res.data) {
1157 | return res.data.result;
1158 | }
1159 | }
1160 |
1161 | /**
1162 | * Get a committee by ID
1163 | * @param {Number} committeeID unique identifier of the committee to be queried
1164 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1165 | * @return {Promise}
1166 | */
1167 | async getCommittee(committeeID: number, timeout = 2000) {
1168 | const path = api.getCommittee + '/' + committeeID;
1169 | const res = await tx.getTx(path, this.baseURI, timeout);
1170 | if (res && res.data) {
1171 | return res.data.result;
1172 | }
1173 | }
1174 |
1175 | /**
1176 | * Get all committees
1177 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1178 | * @return {Promise}
1179 | */
1180 | async getCommittees(timeout = 2000) {
1181 | const res = await tx.getTx(api.getCommittees, this.baseURI, timeout);
1182 | if (res && res.data) {
1183 | return res.data.result;
1184 | }
1185 | }
1186 |
1187 | /**
1188 | * Get all proposals by a committee
1189 | * @param {Number} committeeID unique identifier of the committee whose proposals will be queried
1190 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1191 | * @return {Promise}
1192 | */
1193 | async getCommitteeProposals(committeeID: number, timeout = 2000) {
1194 | const path = api.getCommittee + '/' + committeeID + '/proposals';
1195 | const res = await tx.getTx(path, this.baseURI, timeout);
1196 | if (res && res.data) {
1197 | return res.data.result;
1198 | }
1199 | }
1200 |
1201 | /**
1202 | * Get a proposal by ID
1203 | * @param {Number} proposalID unique identifier of the proposal to be queried
1204 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1205 | * @return {Promise}
1206 | */
1207 | async getProposal(proposalID: number, timeout = 2000) {
1208 | const path = api.getProposal + '/' + proposalID;
1209 | const res = await tx.getTx(path, this.baseURI, timeout);
1210 | if (res && res.data) {
1211 | return res.data.result;
1212 | }
1213 | }
1214 |
1215 | /**
1216 | * Get a proposal's proposer by proposal ID
1217 | * @param {Number} proposalID unique identifier of the proposal whose proposer will be queried
1218 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1219 | * @return {Promise}
1220 | */
1221 | async getProposer(proposalID: number, timeout = 2000) {
1222 | const path = api.getProposal + '/' + proposalID + '/proposer';
1223 | const res = await tx.getTx(path, this.baseURI, timeout);
1224 | if (res && res.data) {
1225 | return res.data.result;
1226 | }
1227 | }
1228 |
1229 | /**
1230 | * Get a proposal's tally by proposal ID
1231 | * @param {Number} proposalID unique identifier of the proposal whose tally will be queried
1232 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1233 | * @return {Promise}
1234 | */
1235 | async getProposalTally(proposalID: number, timeout = 2000) {
1236 | const path = api.getProposal + '/' + proposalID + '/tally';
1237 | const res = await tx.getTx(path, this.baseURI, timeout);
1238 | if (res && res.data) {
1239 | return res.data.result;
1240 | }
1241 | }
1242 |
1243 | /**
1244 | * Get a proposal's votes by proposal ID
1245 | * @param {Number} proposalID unique identifier of the proposal whose votes will be queried
1246 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1247 | * @return {Promise}
1248 | */
1249 | async getProposalVotes(proposalID: number, timeout = 2000) {
1250 | const path = api.getProposal + '/' + proposalID + '/votes';
1251 | const res = await tx.getTx(path, this.baseURI, timeout);
1252 | if (res && res.data) {
1253 | return res.data.result;
1254 | }
1255 | }
1256 |
1257 | /**
1258 | * Submit a public proposal by a selected committee (must be a member)
1259 | * @param {String} proposal the proposal to be submitted
1260 | * @param {String} committeeID the unique identifier of the committee
1261 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1262 | * @param {String} sequence optional account sequence
1263 | * @return {Promise}
1264 | */
1265 | async submitCommitteeProposal(
1266 | proposal: string,
1267 | committeeID: string,
1268 | fee = DEFAULT_FEE,
1269 | sequence = null
1270 | ) {
1271 | if (!this.wallet) {
1272 | throw Error('Wallet has not yet been initialized');
1273 | }
1274 | const msgSubmitProposal = msg.kava.newMsgSubmitProposal(
1275 | proposal,
1276 | this.wallet.address,
1277 | committeeID
1278 | );
1279 | return await this.sendTx([msgSubmitProposal], fee, sequence);
1280 | }
1281 |
1282 | /**
1283 | * Vote on a public proposal by ID
1284 | * @param {String} proposalID the unique identifier of the proposal
1285 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1286 | * @param {String} sequence optional account sequence
1287 | * @return {Promise}
1288 | */
1289 | async voteOnCommitteeProposal(
1290 | proposalID: string,
1291 | voteType: VoteType,
1292 | fee = DEFAULT_FEE,
1293 | sequence = null
1294 | ) {
1295 | if (!this.wallet) {
1296 | throw Error('Wallet has not yet been initialized');
1297 | }
1298 | const msgVote = msg.kava.newMsgVote(
1299 | proposalID,
1300 | this.wallet.address,
1301 | voteType
1302 | );
1303 | return await this.sendTx([msgVote], fee, sequence);
1304 | }
1305 |
1306 | /***************************************************
1307 | * Issuance
1308 | ***************************************************/
1309 | /**
1310 | * Get the params of the issuance module
1311 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
1312 | * @return {Promise}
1313 | */
1314 | async getParamsIssuance(timeout = 2000) {
1315 | const res = await tx.getTx(api.getParamsIssuance, this.baseURI, timeout);
1316 | if (res && res.data) {
1317 | return res.data.result;
1318 | }
1319 | }
1320 |
1321 | /**
1322 | * Issues (mints) coins to a recipient address
1323 | * @param {String} tokens coins to be issued
1324 | * @param {String} receiver the recipient of the newly issued coins
1325 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1326 | * @param {String} sequence optional account sequence
1327 | * @return {Promise}
1328 | */
1329 | async issueTokens(
1330 | tokens: Coin[],
1331 | receiver: string,
1332 | fee = DEFAULT_FEE,
1333 | sequence = null
1334 | ) {
1335 | if (!this.wallet) {
1336 | throw Error('Wallet has not yet been initialized');
1337 | }
1338 | const msgIssueTokens = msg.kava.newMsgIssueTokens(
1339 | this.wallet.address,
1340 | tokens,
1341 | receiver
1342 | );
1343 | return await this.sendTx([msgIssueTokens], fee, sequence);
1344 | }
1345 |
1346 | /**
1347 | * Redeems tokens
1348 | * @param {String} tokens coins to be redeemed
1349 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1350 | * @param {String} sequence optional account sequence
1351 | * @return {Promise}
1352 | */
1353 | async redeemTokens(tokens: Coin[], fee = DEFAULT_FEE, sequence = null) {
1354 | if (!this.wallet) {
1355 | throw Error('Wallet has not yet been initialized');
1356 | }
1357 | const msgRedeemTokens = msg.kava.newMsgRedeemTokens(
1358 | this.wallet.address,
1359 | tokens
1360 | );
1361 | return await this.sendTx([msgRedeemTokens], fee, sequence);
1362 | }
1363 |
1364 | /**
1365 | * Blocks an address from interacting with a specific token denom
1366 | * @param {String} denom the asset denom the address will be blocked from using
1367 | * @param {String} blockedAddress the address to be blocked
1368 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1369 | * @param {String} sequence optional account sequence
1370 | * @return {Promise}
1371 | */
1372 | async blockAddress(
1373 | denom: string,
1374 | blockedAddress: string,
1375 | fee = DEFAULT_FEE,
1376 | sequence = null
1377 | ) {
1378 | if (!this.wallet) {
1379 | throw Error('Wallet has not yet been initialized');
1380 | }
1381 | const msgBlockAddress = msg.kava.newMsgBlockAddress(
1382 | this.wallet.address,
1383 | denom,
1384 | blockedAddress
1385 | );
1386 | return await this.sendTx([msgBlockAddress], fee, sequence);
1387 | }
1388 |
1389 | /**
1390 | * Unblocks an address that's blocked from interacting with a specific token denom
1391 | * @param {String} denom the asset denom the address will be unblocked from using
1392 | * @param {String} address the address to be unblocked
1393 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1394 | * @param {String} sequence optional account sequence
1395 | * @return {Promise}
1396 | */
1397 | async unblockAddress(
1398 | denom: string,
1399 | blockedAddress: string,
1400 | fee = DEFAULT_FEE,
1401 | sequence = null
1402 | ) {
1403 | if (!this.wallet) {
1404 | throw Error('Wallet has not yet been initialized');
1405 | }
1406 | const msgUnblockAddress = msg.kava.newMsgUnblockAddress(
1407 | this.wallet.address,
1408 | denom,
1409 | blockedAddress
1410 | );
1411 | return await this.sendTx([msgUnblockAddress], fee, sequence);
1412 | }
1413 |
1414 | /**
1415 | * Updates the paused/unpaused status for a specific token denom
1416 | * @param {String} denom the asset denom whose status will be updated
1417 | * @param {String} status bool representing the token's new active/inactive status
1418 | * @param {Object} fee optional fee consisting of { amount: [Coins], gas: String(Number) }
1419 | * @param {String} sequence optional account sequence
1420 | * @return {Promise}
1421 | */
1422 | async setPauseStatus(
1423 | denom: string,
1424 | status: string,
1425 | fee = DEFAULT_FEE,
1426 | sequence = null
1427 | ) {
1428 | if (!this.wallet) {
1429 | throw Error('Wallet has not yet been initialized');
1430 | }
1431 | const msgSetPauseStatus = msg.kava.newMsgSetPauseStatus(
1432 | this.wallet.address,
1433 | denom,
1434 | status
1435 | );
1436 | return await this.sendTx([msgSetPauseStatus], fee, sequence);
1437 | }
1438 | }
1439 |
--------------------------------------------------------------------------------
/src/client/swap/index.ts:
--------------------------------------------------------------------------------
1 | import { tx } from '../../tx';
2 | import { msg } from '../../msg';
3 | import { KavaClient } from '..';
4 | import { Coin } from '../../types/Coin';
5 |
6 | const DEFAULT_GAS = 300000;
7 |
8 | const api = {
9 | getParams: '/swap/parameters',
10 | getDeposits: '/swap/deposits',
11 | getPool: '/swap/pool',
12 | getPools: '/swap/pools',
13 | };
14 |
15 | export class Swap {
16 | // @ts-ignore
17 | public kavaClient: KavaClient;
18 | public static instance: Swap;
19 |
20 | constructor(kavaClient: KavaClient) {
21 | if (!Swap.instance) {
22 | this.kavaClient = kavaClient;
23 | Swap.instance = this;
24 | }
25 | return Swap.instance;
26 | }
27 |
28 | /**
29 | * Get the params of the swap module
30 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
31 | * @return {Promise}
32 | */
33 | async getParams(timeout = 2000) {
34 | const res = await tx.getTx(api.getParams, this.kavaClient.baseURI, timeout);
35 | if (res && res.data) {
36 | return res.data.result;
37 | }
38 | }
39 |
40 | /**
41 | * Get swap deposits
42 | * @param {Object} args optional arguments {owner: "kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny", pool: "bnb:usdx"}
43 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
44 | * @return {Promise}
45 | */
46 | async getDeposits(args = {}, timeout = 2000) {
47 | const res = await tx.getTx(
48 | api.getDeposits,
49 | this.kavaClient.baseURI,
50 | timeout,
51 | args
52 | );
53 | if (res && res.data) {
54 | return res.data.result;
55 | }
56 | }
57 |
58 | /**
59 | * Get swap pool
60 | * @param {Object} args required arguments {pool: "bnb:usdx"}
61 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
62 | * @return {Promise}
63 | */
64 | async getPool(args = {}, timeout = 2000) {
65 | const res = await tx.getTx(
66 | api.getPool,
67 | this.kavaClient.baseURI,
68 | timeout,
69 | args
70 | );
71 | if (res && res.data) {
72 | return res.data.result;
73 | }
74 | }
75 |
76 | /**
77 | * Get swap pools
78 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
79 | * @return {Promise}
80 | */
81 | async getPools(args = {}, timeout = 2000) {
82 | const res = await tx.getTx(
83 | api.getPools,
84 | this.kavaClient.baseURI,
85 | timeout,
86 | args
87 | );
88 | if (res && res.data) {
89 | return res.data.result;
90 | }
91 | }
92 |
93 | /**
94 | * Deposit funds to a liquidity pool
95 | * @param {Object} amount of token a coins to be deposited
96 | * @param {Object} amount of token b coins to be deposited
97 | * @param {string} max slippage you're willing to accept
98 | * @param {string} deadline time to complete the transaction (unix timestamp seconds, UTC timezone)
99 | * @param {Number} gas optional gas amount
100 | * @param {String} sequence optional account sequence
101 | * @return {Promise}
102 | */
103 | async deposit(
104 | tokenA: Coin,
105 | tokenB: Coin,
106 | slippage: string,
107 | deadline: string,
108 | gas = DEFAULT_GAS,
109 | sequence = null
110 | ) {
111 | if (!this.kavaClient.wallet) {
112 | throw Error('Wallet has not yet been initialized');
113 | }
114 | const msgDeposit = msg.swap.newMsgDeposit(
115 | this.kavaClient.wallet.address,
116 | tokenA,
117 | tokenB,
118 | slippage,
119 | deadline
120 | );
121 | const fee = { amount: [], gas: String(gas) };
122 | return await this.kavaClient.sendTx([msgDeposit], fee, sequence);
123 | }
124 |
125 | /**
126 | * Withdraw funds from a liquidity pool
127 | * @param {Object} amount the coins to be deposited
128 | * @param {Number} number of shares to be withdrawn
129 | * @param {Object} min amount of token a coins to be withdrawn
130 | * @param {Object} min amount of token b coins to be withdrawn
131 | * @param {string} deadline time to complete the transaction (unix timestamp seconds, UTC timezone)
132 | * @param {Number} gas optional gas amount
133 | * @param {String} sequence optional account sequence
134 | * @return {Promise}
135 | */
136 | async withdraw(
137 | shares: any,
138 | minTokenA: Coin,
139 | minTokenB: Coin,
140 | deadline: string,
141 | gas = DEFAULT_GAS,
142 | sequence = null
143 | ) {
144 | if (!this.kavaClient.wallet) {
145 | throw Error('Wallet has not yet been initialized');
146 | }
147 | const msgWithdraw = msg.swap.newMsgWithdraw(
148 | this.kavaClient.wallet.address,
149 | shares,
150 | minTokenA,
151 | minTokenB,
152 | deadline
153 | );
154 | const fee = { amount: [], gas: String(gas) };
155 | return await this.kavaClient.sendTx([msgWithdraw], fee, sequence);
156 | }
157 |
158 | /**
159 | * Swap an exact number of token a for an estimated amount of token b
160 | * @param {Object} amount of tokens to be be put into the system
161 | * @param {Object} expected amount of coins to be returned
162 | * @param {string} max slippage you're willing to accept
163 | * @param {string} deadline time to complete the transaction (unix timestamp seconds, UTC timezone)
164 | * @param {Number} gas optional gas amount
165 | * @param {String} sequence optional account sequence
166 | * @return {Promise}
167 | */
168 | async swapExactForTokens(
169 | exactTokenA: Coin,
170 | tokenB: Coin,
171 | slippage: string,
172 | deadline: string,
173 | gas = DEFAULT_GAS,
174 | sequence = null
175 | ) {
176 | if (!this.kavaClient.wallet) {
177 | throw Error('Wallet has not yet been initialized');
178 | }
179 | const msgSwapExactForTokens = msg.swap.newMsgSwapExactForTokens(
180 | this.kavaClient.wallet.address,
181 | exactTokenA,
182 | tokenB,
183 | slippage,
184 | deadline
185 | );
186 | const fee = { amount: [], gas: String(gas) };
187 | return await this.kavaClient.sendTx([msgSwapExactForTokens], fee, sequence);
188 | }
189 |
190 | /**
191 | * Swap an exact number of token b to be returned for an for an estimated amount of token a to input
192 | * @param {Object} expected amount of coins to be put into the system
193 | * @param {Object} amount of tokens to be returned from the system
194 | * @param {string} max slippage you're willing to accept
195 | * @param {string} deadline time to complete the transaction (unix timestamp seconds, UTC timezone)
196 | * @param {Number} gas optional gas amount
197 | * @param {String} sequence optional account sequence
198 | * @return {Promise}
199 | */
200 | async swapForExactTokens(
201 | tokenA: Coin,
202 | exactTokenB: Coin,
203 | slippage: string,
204 | deadline: string,
205 | gas = DEFAULT_GAS,
206 | sequence = null
207 | ) {
208 | if (!this.kavaClient.wallet) {
209 | throw Error('Wallet has not yet been initialized');
210 | }
211 | const msgSwapForExactTokens = msg.swap.newMsgSwapForExactTokens(
212 | this.kavaClient.wallet.address,
213 | tokenA,
214 | exactTokenB,
215 | slippage,
216 | deadline
217 | );
218 | const fee = { amount: [], gas: String(gas) };
219 | return await this.kavaClient.sendTx([msgSwapForExactTokens], fee, sequence);
220 | }
221 | }
222 |
223 | module.exports.Swap = Swap;
224 |
--------------------------------------------------------------------------------
/src/crypto/index.ts:
--------------------------------------------------------------------------------
1 | const sig = require('@kava-labs/sig');
2 | import bech32 from 'bech32';
3 | import bip39 from 'bip39';
4 |
5 | const KAVA_PREFIX = 'kava';
6 | const MNEMONIC_LEN = 256;
7 | const DECODED_ADDRESS_LEN = 20;
8 | const DERIVATION_PATH = "m/44'/459'/0'/0/0";
9 | const DERIVATION_PATH_LEGACY = "m/44'/118'/0'/0/0";
10 |
11 | /**
12 | * Generates mnemonic phrase words using random entropy.
13 | */
14 | const generateMnemonic = () => bip39.generateMnemonic(MNEMONIC_LEN);
15 |
16 | /**
17 | * Loads a key pair from a mnemonic phrase.
18 | * @param {string} mnemonic the mnemonic from which to generate the key pair
19 | * @param {boolean} legacy optional boolean to use the legacy coin type
20 | */
21 | const getAddressFromMnemonic = (mnemonic: string, legacy = false) => {
22 | const derivationPath = legacy ? DERIVATION_PATH_LEGACY : DERIVATION_PATH;
23 | const masterKey = sig.createMasterKeyFromMnemonic(mnemonic);
24 | const keyPair = sig.createKeyPairFromMasterKey(masterKey, derivationPath);
25 | return sig.createAddress(keyPair.publicKey, KAVA_PREFIX);
26 | };
27 |
28 | /**
29 | * Decodes an address in bech32 format.
30 | * @param {string} value the bech32 address to decode
31 | */
32 | const decodeAddress = (value: string) => {
33 | const decodeAddress = bech32.decode(value);
34 | return Buffer.from(bech32.fromWords(decodeAddress.words));
35 | };
36 |
37 | /**
38 | * Checks whether an address is valid.
39 | * @param {string} address the bech32 address to decode
40 | * @param {string} hrp the prefix to check for the bech32 address
41 | * @return {boolean}
42 | */
43 | const checkAddress = (address: string, hrp: string) => {
44 | try {
45 | if (!address.startsWith(hrp)) {
46 | return false;
47 | }
48 |
49 | const decodedAddress = bech32.decode(address);
50 | const decodedAddressLength = decodeAddress(address).length;
51 | if (
52 | decodedAddressLength === DECODED_ADDRESS_LEN &&
53 | decodedAddress.prefix === hrp
54 | ) {
55 | return true;
56 | }
57 |
58 | return false;
59 | } catch (err) {
60 | return false;
61 | }
62 | };
63 |
64 | export const crypto = {
65 | generateMnemonic,
66 | getAddressFromMnemonic,
67 | decodeAddress,
68 | checkAddress,
69 | };
70 |
--------------------------------------------------------------------------------
/src/index.test.ts:
--------------------------------------------------------------------------------
1 | import Kava from './';
2 | import { utils, crypto, msg, tx } from './';
3 |
4 | describe('SDK exports', () => {
5 | it('should export Kava as a default', () => {
6 | expect(Kava).toBeDefined();
7 | });
8 | it('should contain expected modules', () => {
9 | expect(Kava.utils).toBeDefined();
10 | expect(Kava.tx).toBeDefined();
11 | expect(Kava.msg).toBeDefined();
12 | expect(Kava.crypto).toBeDefined();
13 | });
14 | it('should export each module individually', () => {
15 | expect(utils).toBeDefined();
16 | expect(tx).toBeDefined();
17 | expect(msg).toBeDefined();
18 | expect(crypto).toBeDefined();
19 | });
20 |
21 | it('derives the correct kava address from ox address', () => {
22 | expect(
23 | utils.kavaToEthAddress('kava1vlpsrmdyuywvaqrv7rx6xga224sqfwz3fyfhwq')
24 | ).toEqual('0x67C301eDA4E11Cce806Cf0CDa323aA556004b851');
25 | });
26 |
27 | it('derives the correct 0x address from a kava address', () => {
28 | expect(
29 | utils.ethToKavaAddress('0x7Bbf300890857b8c241b219C6a489431669b3aFA')
30 | ).toEqual('kava10wlnqzyss4accfqmyxwx5jy5x9nfkwh6qm7n4t');
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { tx } from './tx';
2 | import { msg } from './msg';
3 | import { utils } from './utils';
4 | import { crypto } from './crypto';
5 | import { KavaClient } from './client';
6 | export * from './types';
7 |
8 | const Kava = {
9 | tx,
10 | msg,
11 | utils,
12 | crypto,
13 | KavaClient,
14 | };
15 |
16 | export { tx, msg, utils, crypto, KavaClient };
17 |
18 | export default Kava;
19 |
--------------------------------------------------------------------------------
/src/msg/cosmos/index.test.ts:
--------------------------------------------------------------------------------
1 | import { cosmos } from '.';
2 |
3 | describe('Cosmos messages', () => {
4 | describe('newMsgSend', () => {
5 | it('should sort the coins passed in by denom', () => {
6 | const coins = [
7 | {
8 | denom: 'ukava',
9 | amount: '30000000',
10 | },
11 | {
12 | denom: 'btcb',
13 | amount: '10000000',
14 | },
15 | {
16 | denom: 'xrpb',
17 | amount: '40000000',
18 | },
19 | {
20 | denom: 'swp',
21 | amount: '20000000',
22 | },
23 | ];
24 | const message = cosmos.newMsgSend('kavafrom', 'kavato', coins);
25 | expect(message).toStrictEqual({
26 | type: 'cosmos-sdk/MsgSend',
27 | value: {
28 | from_address: 'kavafrom',
29 | to_address: 'kavato',
30 | amount: [
31 | {
32 | denom: 'btcb',
33 | amount: '10000000',
34 | },
35 | {
36 | denom: 'swp',
37 | amount: '20000000',
38 | },
39 | {
40 | denom: 'ukava',
41 | amount: '30000000',
42 | },
43 | {
44 | denom: 'xrpb',
45 | amount: '40000000',
46 | },
47 | ],
48 | },
49 | });
50 | });
51 | });
52 |
53 | describe('newMsgTransfer', () => {
54 | it('should generate a well-formed transfer', () => {
55 | const coin = {
56 | denom: 'ukava',
57 | amount: '10000000',
58 | };
59 | const message = cosmos.newMsgTransfer(
60 | 'transfer',
61 | 'channel-0',
62 | coin,
63 | 'kava1w66puffhccjck70hw75wu3v92tshw5rmdxp8hb',
64 | 'kava1a22puffhccjck70hw75wu3v92tshw5rmdxp6xz',
65 | 1638988347480000
66 | );
67 | const expected = {
68 | type: 'cosmos-sdk/MsgTransfer',
69 | value: {
70 | source_port: 'transfer',
71 | source_channel: 'channel-0',
72 | token: {
73 | denom: 'ukava',
74 | amount: '10000000',
75 | },
76 | sender: 'kava1w66puffhccjck70hw75wu3v92tshw5rmdxp8hb',
77 | receiver: 'kava1a22puffhccjck70hw75wu3v92tshw5rmdxp6xz',
78 | timeoutHeight: 0,
79 | timeoutTimestamp: 1638988347480000,
80 | },
81 | };
82 | expect(message).toEqual(expected);
83 | });
84 | });
85 | });
86 | describe('staking delegations messages', () => {
87 | test('newMsgDelegate', () => {
88 | const message = cosmos.newMsgDelegate(
89 | 'kava1w66puffhccjck70hw75wu3v92tshw5rmdxp8hb',
90 | 'kava1a22puffhccjck70hw75wu3v92tshw5rmdxp6xz',
91 | {
92 | denom: 'ukava',
93 | amount: '10000000',
94 | }
95 | );
96 | const expected = {
97 | type: 'cosmos-sdk/MsgDelegate',
98 | value: {
99 | delegator_address: 'kava1w66puffhccjck70hw75wu3v92tshw5rmdxp8hb',
100 | validator_address: 'kava1a22puffhccjck70hw75wu3v92tshw5rmdxp6xz',
101 | amount: {
102 | denom: 'ukava',
103 | amount: '10000000',
104 | },
105 | },
106 | };
107 | expect(message).toEqual(expected);
108 | });
109 | test('newMsgUnDelegate', () => {
110 | const message = cosmos.newMsgUnDelegate(
111 | 'kava1w66puffhccjck70hw75wu3v92tshw5rmdxp8hb',
112 | 'kava1a22puffhccjck70hw75wu3v92tshw5rmdxp6xz',
113 | {
114 | denom: 'ukava',
115 | amount: '10000000',
116 | }
117 | );
118 | const expected = {
119 | type: 'cosmos-sdk/MsgUndelegate',
120 | value: {
121 | delegator_address: 'kava1w66puffhccjck70hw75wu3v92tshw5rmdxp8hb',
122 | validator_address: 'kava1a22puffhccjck70hw75wu3v92tshw5rmdxp6xz',
123 | amount: {
124 | denom: 'ukava',
125 | amount: '10000000',
126 | },
127 | },
128 | };
129 | expect(message).toEqual(expected);
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/src/msg/cosmos/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 | import { Message } from '../../types/Message';
3 | import { VoteType } from '../../types/VoteType';
4 |
5 | const FEE_DEFAULT = { amount: [], gas: '300000' };
6 |
7 | /**
8 | * Creates a new StdTx from some messages with a default fee
9 | * @param {Object} msgs an array of msgs to be included in the transaction
10 | * @param {Object} fee optional fee
11 | * @param {Object} memo optional memo
12 | * @param {Object} signatures generated when an address signs a data package, required for tx confirmation
13 | * @return {Promise}
14 | */
15 | function newStdTx(
16 | msgs: Message[],
17 | fee = FEE_DEFAULT,
18 | memo = '',
19 | signatures = null
20 | ) {
21 | return {
22 | type: 'cosmos-sdk/StdTx',
23 | value: {
24 | msg: msgs,
25 | fee: fee,
26 | signatures: signatures,
27 | memo: memo,
28 | },
29 | };
30 | }
31 |
32 | function newMsgSend(address: string, to: string, coins: Coin[]) {
33 | const sendTx = {
34 | type: 'cosmos-sdk/MsgSend',
35 | value: {
36 | from_address: address,
37 | to_address: to,
38 | amount: coins.sort((coinA, coinB) =>
39 | coinA.denom > coinB.denom ? 1 : -1
40 | ),
41 | },
42 | };
43 | return sendTx;
44 | }
45 |
46 | function newMsgVoteGovernance(
47 | proposalID: string,
48 | voter: string,
49 | voteType: VoteType
50 | ) {
51 | return {
52 | type: 'cosmos-sdk/MsgVote',
53 | value: {
54 | voter: voter,
55 | proposal_id: proposalID,
56 | option: voteType,
57 | },
58 | };
59 | }
60 |
61 | /**
62 | * Creates an IBC transfer
63 | * @param {String} sourcePort the port identifier, we would expect to always be "transfer" * @param {String} sourcePort the port identifier, we would expect to always be "transfer"
64 | * @param {String} source_channel the channel identifier
65 | * @param {Coin} token
66 | * @param {String} sender address of sender on the origin chain
67 | * @param {String} receiver address of recipient on the destination chain
68 | * @param {Integer} timeoutTimestamp nanoseconds to allow transfer to complete
69 |
70 | */
71 | function newMsgTransfer(
72 | sourcePort: string,
73 | sourceChannel: string,
74 | token: Coin,
75 | sender: string,
76 | receiver: string,
77 | timeoutTimestamp: number
78 | ) {
79 | return {
80 | type: 'cosmos-sdk/MsgTransfer',
81 | value: {
82 | source_port: sourcePort,
83 | source_channel: sourceChannel,
84 | token: token,
85 | sender: sender,
86 | receiver: receiver,
87 | timeout_height: {},
88 | timeout_timestamp: timeoutTimestamp.toString(),
89 | },
90 | };
91 | }
92 |
93 | export function newMsgDelegate(
94 | delegatorAddress: string,
95 | validatorAddress: string,
96 | amount: Coin
97 | ) {
98 | return {
99 | type: 'cosmos-sdk/MsgDelegate',
100 | value: {
101 | delegator_address: delegatorAddress,
102 | validator_address: validatorAddress,
103 | amount,
104 | },
105 | };
106 | }
107 |
108 | export function newMsgUnDelegate(
109 | delegatorAddress: string,
110 | validatorAddress: string,
111 | amount: Coin
112 | ) {
113 | return {
114 | type: 'cosmos-sdk/MsgUndelegate',
115 | value: {
116 | delegator_address: delegatorAddress,
117 | validator_address: validatorAddress,
118 | amount,
119 | },
120 | };
121 | }
122 |
123 | export const cosmos = {
124 | newStdTx,
125 | newMsgSend,
126 | newMsgVoteGovernance,
127 | newMsgTransfer,
128 | newMsgDelegate,
129 | newMsgUnDelegate,
130 | };
131 |
--------------------------------------------------------------------------------
/src/msg/earn/index.ts:
--------------------------------------------------------------------------------
1 | import { Strategy, Coin } from '../../types';
2 |
3 | function newMsgDeposit(depositor: string, amount: Coin, strategy: Strategy) {
4 | return {
5 | type: 'earn/MsgDeposit',
6 | value: {
7 | depositor,
8 | amount,
9 | strategy,
10 | },
11 | };
12 | }
13 |
14 | function newMsgWithdraw(from: string, amount: Coin, strategy: Strategy) {
15 | return {
16 | type: 'earn/MsgWithdraw',
17 | value: {
18 | from,
19 | amount,
20 | strategy,
21 | },
22 | };
23 | }
24 |
25 | export const earn = {
26 | newMsgDeposit,
27 | newMsgWithdraw,
28 | };
29 |
--------------------------------------------------------------------------------
/src/msg/evmutil/index.ts:
--------------------------------------------------------------------------------
1 | import { InternalEVMAddress, Coin } from '../../types';
2 |
3 | function newMsgConvertERC20ToCoin(
4 | initiator: InternalEVMAddress,
5 | receiver: string,
6 | kava_erc20_address: InternalEVMAddress,
7 | amount: string
8 | ) {
9 | return {
10 | type: 'evmutil/MsgConvertERC20ToCoin',
11 | value: {
12 | initiator,
13 | receiver,
14 | kava_erc20_address,
15 | amount,
16 | },
17 | };
18 | }
19 |
20 | function newMsgConvertCoinToERC20(
21 | initiator: InternalEVMAddress,
22 | receiver: string,
23 | amount: Coin
24 | ) {
25 | return {
26 | type: 'evmutil/MsgConvertCoinToERC20',
27 | value: {
28 | initiator,
29 | receiver,
30 | amount,
31 | },
32 | };
33 | }
34 |
35 | function newMsgConvertCosmosCoinToERC20(
36 | initiator: InternalEVMAddress,
37 | receiver: string,
38 | amount: Coin
39 | ) {
40 | return {
41 | type: 'evmutil/MsgConvertCosmosCoinToERC20',
42 | value: {
43 | initiator,
44 | receiver,
45 | amount,
46 | },
47 | };
48 | }
49 |
50 | function newMsgConvertCosmosCoinFromERC20(
51 | initiator: InternalEVMAddress,
52 | receiver: string,
53 | amount: Coin
54 | ) {
55 | return {
56 | type: 'evmutil/MsgConvertCosmosCoinFromERC20',
57 | value: {
58 | initiator,
59 | receiver,
60 | amount,
61 | },
62 | };
63 | }
64 |
65 | export const evmutil = {
66 | newMsgConvertERC20ToCoin,
67 | newMsgConvertCoinToERC20,
68 | newMsgConvertCosmosCoinToERC20,
69 | newMsgConvertCosmosCoinFromERC20,
70 | };
71 |
--------------------------------------------------------------------------------
/src/msg/hard/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 |
3 | function newMsgDeposit(depositor: string, amount: Coin[]) {
4 | return {
5 | type: 'hard/MsgDeposit',
6 | value: {
7 | depositor: depositor,
8 | amount: amount,
9 | },
10 | };
11 | }
12 |
13 | function newMsgWithdraw(depositor: string, amount: Coin[]) {
14 | return {
15 | type: 'hard/MsgWithdraw',
16 | value: {
17 | depositor: depositor,
18 | amount: amount,
19 | },
20 | };
21 | }
22 |
23 | function newMsgBorrow(borrower: string, amount: Coin[]) {
24 | return {
25 | type: 'hard/MsgBorrow',
26 | value: {
27 | borrower: borrower,
28 | amount: amount,
29 | },
30 | };
31 | }
32 |
33 | function newMsgRepay(sender: string, owner: string, amount: Coin[]) {
34 | return {
35 | type: 'hard/MsgRepay',
36 | value: {
37 | sender: sender,
38 | owner: owner,
39 | amount: amount,
40 | },
41 | };
42 | }
43 |
44 | function newMsgLiquidate(keeper: string, borrower: string) {
45 | return {
46 | type: 'hard/MsgLiquidate',
47 | value: {
48 | keeper: keeper,
49 | borrower: borrower,
50 | },
51 | };
52 | }
53 |
54 | export const hard = {
55 | newMsgDeposit,
56 | newMsgWithdraw,
57 | newMsgBorrow,
58 | newMsgRepay,
59 | newMsgLiquidate,
60 | };
61 |
--------------------------------------------------------------------------------
/src/msg/index.ts:
--------------------------------------------------------------------------------
1 | import { cosmos } from './cosmos';
2 | import { earn } from './earn';
3 | import { evmutil } from './evmutil';
4 | import { hard } from './hard';
5 | import { kava } from './kava';
6 | import { liquid } from './liquid';
7 | import { router } from './router';
8 | import { savings } from './savings';
9 | import { swap } from './swap';
10 |
11 | export const msg = {
12 | cosmos,
13 | earn,
14 | evmutil,
15 | hard,
16 | kava,
17 | liquid,
18 | router,
19 | savings,
20 | swap,
21 | };
22 |
--------------------------------------------------------------------------------
/src/msg/kava/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 | import { DenomToClaim } from '../../types/DenomToClaim';
3 | import { VoteType } from '../../types/VoteType';
4 |
5 | /***************************************************
6 | * Auction
7 | ***************************************************/
8 |
9 | function newMsgPlaceBid(auctionID: string, bidder: string, amount: Coin) {
10 | return {
11 | type: 'auction/MsgPlaceBid',
12 | value: {
13 | auction_id: auctionID,
14 | bidder: bidder,
15 | amount: amount,
16 | },
17 | };
18 | }
19 |
20 | /***************************************************
21 | * BEP3
22 | ***************************************************/
23 |
24 | // newMsgCreateAtomicSwap creates a new MsgCreateAtomicSwap
25 | function newMsgCreateAtomicSwap(
26 | from: string,
27 | to: string,
28 | recipientOtherChain: string,
29 | senderOtherChain: string,
30 | randomNumberHash: string,
31 | timestamp: number,
32 | amount: Coin[],
33 | heightSpan: number
34 | ) {
35 | return {
36 | type: 'bep3/MsgCreateAtomicSwap',
37 | value: {
38 | from,
39 | to,
40 | recipient_other_chain: recipientOtherChain,
41 | sender_other_chain: senderOtherChain,
42 | random_number_hash: randomNumberHash,
43 | timestamp: String(timestamp),
44 | amount: amount,
45 | height_span: String(heightSpan),
46 | },
47 | };
48 | }
49 |
50 | // newMsgClaimAtomicSwap creates a new MsgClaimAtomicSwap
51 | function newMsgClaimAtomicSwap(
52 | from: string,
53 | swapID: string,
54 | randomNumber: string
55 | ) {
56 | return {
57 | type: 'bep3/MsgClaimAtomicSwap',
58 | value: {
59 | from,
60 | swap_id: swapID,
61 | random_number: randomNumber,
62 | },
63 | };
64 | }
65 |
66 | // newMsgRefundAtomicSwap creates a new MsgRefundAtomicSwap
67 | function newMsgRefundAtomicSwap(from: string, swapID: string) {
68 | return {
69 | type: 'bep3/MsgRefundAtomicSwap',
70 | value: {
71 | from,
72 | swap_id: swapID,
73 | },
74 | };
75 | }
76 |
77 | /***************************************************
78 | * CDP
79 | ***************************************************/
80 |
81 | function newMsgCreateCDP(
82 | sender: string,
83 | principal: Coin,
84 | collateral: Coin,
85 | collateralType: string
86 | ) {
87 | return {
88 | type: 'cdp/MsgCreateCDP',
89 | value: {
90 | sender: sender,
91 | principal: principal,
92 | collateral: collateral,
93 | collateral_type: collateralType,
94 | },
95 | };
96 | }
97 |
98 | function newMsgDeposit(
99 | owner: string,
100 | depositor: string,
101 | collateral: Coin,
102 | collateralType: string
103 | ) {
104 | return {
105 | type: 'cdp/MsgDeposit',
106 | value: {
107 | owner: owner,
108 | depositor: depositor,
109 | collateral: collateral,
110 | collateral_type: collateralType,
111 | },
112 | };
113 | }
114 |
115 | function newMsgWithdraw(
116 | owner: string,
117 | depositor: string,
118 | collateral: Coin,
119 | collateralType: string
120 | ) {
121 | return {
122 | type: 'cdp/MsgWithdraw',
123 | value: {
124 | owner: owner,
125 | depositor: depositor,
126 | collateral: collateral,
127 | collateral_type: collateralType,
128 | },
129 | };
130 | }
131 |
132 | function newMsgDrawDebt(
133 | sender: string,
134 | collateralType: string,
135 | principal: Coin
136 | ) {
137 | return {
138 | type: 'cdp/MsgDrawDebt',
139 | value: {
140 | sender: sender,
141 | collateral_type: collateralType,
142 | principal: principal,
143 | },
144 | };
145 | }
146 |
147 | function newMsgRepayDebt(
148 | sender: string,
149 | collateralType: string,
150 | payment: Coin
151 | ) {
152 | return {
153 | type: 'cdp/MsgRepayDebt',
154 | value: {
155 | sender: sender,
156 | collateral_type: collateralType,
157 | payment: payment,
158 | },
159 | };
160 | }
161 |
162 | function newMsgLiquidate(
163 | keeper: string,
164 | borrower: string,
165 | collateralType: string
166 | ) {
167 | return {
168 | type: 'cdp/MsgLiquidate',
169 | value: {
170 | keeper: keeper,
171 | borrower: borrower,
172 | collateral_type: collateralType,
173 | },
174 | };
175 | }
176 |
177 | /***************************************************
178 | * Committee
179 | ***************************************************/
180 |
181 | function newMsgSubmitProposal(
182 | pubProposal: string,
183 | proposer: string,
184 | committeeID: string
185 | ) {
186 | return {
187 | type: 'kava/MsgSubmitProposal',
188 | value: {
189 | pub_proposal: pubProposal,
190 | proposer: proposer,
191 | committee_id: String(committeeID),
192 | },
193 | };
194 | }
195 |
196 | function newMsgVote(proposalID: string, voter: string, voteType: VoteType) {
197 | return {
198 | type: 'kava/MsgVote',
199 | value: {
200 | proposal_id: String(proposalID),
201 | voter: voter,
202 | vote_type: voteType,
203 | },
204 | };
205 | }
206 |
207 | /***************************************************
208 | * Incentive
209 | ***************************************************/
210 |
211 | function newMsgClaimUSDXMintingReward(sender: string, multiplierName: string) {
212 | return {
213 | type: 'incentive/MsgClaimUSDXMintingReward',
214 | value: {
215 | sender: sender,
216 | multiplier_name: multiplierName,
217 | },
218 | };
219 | }
220 |
221 | function newMsgClaimHardReward(sender: string, denomsToClaim: DenomToClaim[]) {
222 | return {
223 | type: 'incentive/MsgClaimHardReward',
224 | value: {
225 | sender: sender,
226 | denoms_to_claim: denomsToClaim,
227 | },
228 | };
229 | }
230 |
231 | function newMsgClaimDelegatorReward(
232 | sender: string,
233 | denomsToClaim: DenomToClaim[]
234 | ) {
235 | return {
236 | type: 'incentive/MsgClaimDelegatorReward',
237 | value: {
238 | sender: sender,
239 | denoms_to_claim: denomsToClaim,
240 | },
241 | };
242 | }
243 |
244 | function newMsgClaimSwapReward(sender: string, denomsToClaim: DenomToClaim[]) {
245 | return {
246 | type: 'incentive/MsgClaimSwapReward',
247 | value: {
248 | sender: sender,
249 | denoms_to_claim: denomsToClaim,
250 | },
251 | };
252 | }
253 |
254 | function newMsgClaimSavingsReward(
255 | sender: string,
256 | denomsToClaim: DenomToClaim[]
257 | ) {
258 | return {
259 | type: 'incentive/MsgClaimSavingsReward',
260 | value: {
261 | sender: sender,
262 | denoms_to_claim: denomsToClaim,
263 | },
264 | };
265 | }
266 |
267 | function newMsgClaimEarnReward(sender: string, denomsToClaim: DenomToClaim[]) {
268 | return {
269 | type: 'incentive/MsgClaimEarnReward',
270 | value: {
271 | sender,
272 | denoms_to_claim: denomsToClaim,
273 | },
274 | };
275 | }
276 |
277 | /***************************************************
278 | * Issuance
279 | ***************************************************/
280 |
281 | function newMsgIssueTokens(sender: string, tokens: Coin[], receiver: string) {
282 | return {
283 | type: 'issuance/MsgIssueTokens',
284 | value: {
285 | sender: sender,
286 | tokens: tokens,
287 | receiver: receiver,
288 | },
289 | };
290 | }
291 |
292 | function newMsgRedeemTokens(sender: string, tokens: Coin[]) {
293 | return {
294 | type: 'issuance/MsgRedeemTokens',
295 | value: {
296 | sender: sender,
297 | tokens: tokens,
298 | },
299 | };
300 | }
301 |
302 | function newMsgBlockAddress(
303 | sender: string,
304 | denom: string,
305 | blockedAddress: string
306 | ) {
307 | return {
308 | type: 'issuance/MsgBlockAddress',
309 | value: {
310 | sender: sender,
311 | denom: denom,
312 | blocked_address: blockedAddress,
313 | },
314 | };
315 | }
316 |
317 | function newMsgUnblockAddress(sender: string, denom: string, address: string) {
318 | return {
319 | type: 'issuance/MsgUnblockAddress',
320 | value: {
321 | sender: sender,
322 | denom: denom,
323 | address: address,
324 | },
325 | };
326 | }
327 |
328 | function newMsgSetPauseStatus(sender: string, denom: string, status: string) {
329 | return {
330 | type: 'issuance/MsgChangePauseStatus',
331 | value: {
332 | sender: sender,
333 | denom: denom,
334 | status: status,
335 | },
336 | };
337 | }
338 |
339 | /***************************************************
340 | * Pricefeed
341 | ***************************************************/
342 |
343 | function newMsgPostPrice(
344 | from: string,
345 | marketID: string,
346 | price: string,
347 | expiry: string
348 | ) {
349 | return {
350 | type: 'pricefeed/MsgPostPrice',
351 | value: {
352 | from: from,
353 | market_id: marketID,
354 | price: price,
355 | expiry: expiry,
356 | },
357 | };
358 | }
359 |
360 | export const kava = {
361 | newMsgPlaceBid,
362 | newMsgCreateAtomicSwap,
363 | newMsgClaimAtomicSwap,
364 | newMsgRefundAtomicSwap,
365 | newMsgCreateCDP,
366 | newMsgDeposit,
367 | newMsgWithdraw,
368 | newMsgDrawDebt,
369 | newMsgRepayDebt,
370 | newMsgLiquidate,
371 | newMsgSubmitProposal,
372 | newMsgVote,
373 | newMsgClaimUSDXMintingReward,
374 | newMsgClaimHardReward,
375 | newMsgClaimDelegatorReward,
376 | newMsgClaimSwapReward,
377 | newMsgClaimSavingsReward,
378 | newMsgClaimEarnReward,
379 | newMsgIssueTokens,
380 | newMsgRedeemTokens,
381 | newMsgBlockAddress,
382 | newMsgUnblockAddress,
383 | newMsgSetPauseStatus,
384 | newMsgPostPrice,
385 | };
386 |
--------------------------------------------------------------------------------
/src/msg/liquid/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 |
3 | function newMsgMintDerivative(sender: string, validator: string, amount: Coin) {
4 | return {
5 | type: 'liquid/MsgMintDerivative',
6 | value: {
7 | sender,
8 | validator,
9 | amount,
10 | },
11 | };
12 | }
13 |
14 | function newMsgBurnDerivative(sender: string, validator: string, amount: Coin) {
15 | return {
16 | type: 'liquid/MsgBurnDerivative',
17 | value: {
18 | sender,
19 | validator,
20 | amount,
21 | },
22 | };
23 | }
24 |
25 | export const liquid = {
26 | newMsgMintDerivative,
27 | newMsgBurnDerivative,
28 | };
29 |
--------------------------------------------------------------------------------
/src/msg/router/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 |
3 | function newMsgMintDeposit(
4 | depositor: string,
5 | validator: string,
6 | amount?: Coin
7 | ) {
8 | return {
9 | type: 'router/MsgMintDeposit',
10 | value: {
11 | depositor,
12 | validator,
13 | amount,
14 | },
15 | };
16 | }
17 |
18 | function newMsgDelegateMintDeposit(
19 | depositor: string,
20 | validator: string,
21 | amount?: Coin
22 | ) {
23 | return {
24 | type: 'router/MsgDelegateMintDeposit',
25 | value: {
26 | depositor,
27 | validator,
28 | amount,
29 | },
30 | };
31 | }
32 |
33 | function newMsgWithdrawBurn(from: string, validator: string, amount?: Coin) {
34 | return {
35 | type: 'router/MsgWithdrawBurn',
36 | value: {
37 | from,
38 | validator,
39 | amount,
40 | },
41 | };
42 | }
43 |
44 | function newMsgWithdrawBurnUndelegate(
45 | from: string,
46 | validator: string,
47 | amount?: Coin
48 | ) {
49 | return {
50 | type: 'router/MsgWithdrawBurnUndelegate',
51 | value: {
52 | from,
53 | validator,
54 | amount,
55 | },
56 | };
57 | }
58 |
59 | export const router = {
60 | newMsgMintDeposit,
61 | newMsgDelegateMintDeposit,
62 | newMsgWithdrawBurn,
63 | newMsgWithdrawBurnUndelegate,
64 | };
65 |
--------------------------------------------------------------------------------
/src/msg/savings/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 |
3 | function newMsgDeposit(depositor: string, amount: Coin[]) {
4 | return {
5 | type: 'savings/MsgDeposit',
6 | value: {
7 | depositor,
8 | amount,
9 | },
10 | };
11 | }
12 |
13 | function newMsgWithdraw(depositor: string, amount: Coin[]) {
14 | return {
15 | type: 'savings/MsgWithdraw',
16 | value: {
17 | depositor,
18 | amount,
19 | },
20 | };
21 | }
22 |
23 | export const savings = {
24 | newMsgDeposit,
25 | newMsgWithdraw,
26 | };
27 |
--------------------------------------------------------------------------------
/src/msg/swap/index.ts:
--------------------------------------------------------------------------------
1 | import { Coin } from '../../types/Coin';
2 |
3 | function newMsgDeposit(
4 | depositor: string,
5 | tokenA: Coin,
6 | tokenB: Coin,
7 | slippage: string,
8 | deadline: string
9 | ) {
10 | return {
11 | type: 'swap/MsgDeposit',
12 | value: {
13 | depositor: depositor,
14 | token_a: tokenA,
15 | token_b: tokenB,
16 | slippage: slippage,
17 | deadline: deadline,
18 | },
19 | };
20 | }
21 |
22 | function newMsgWithdraw(
23 | from: string,
24 | shares: any,
25 | minTokenA: Coin,
26 | minTokenB: Coin,
27 | deadline: string
28 | ) {
29 | return {
30 | type: 'swap/MsgWithdraw',
31 | value: {
32 | from: from,
33 | shares: shares,
34 | min_token_a: minTokenA,
35 | min_token_b: minTokenB,
36 | deadline: deadline,
37 | },
38 | };
39 | }
40 |
41 | function newMsgSwapExactForTokens(
42 | requester: string,
43 | exactTokenA: Coin,
44 | tokenB: Coin,
45 | slippage: string,
46 | deadline: string
47 | ) {
48 | return {
49 | type: 'swap/MsgSwapExactForTokens',
50 | value: {
51 | requester: requester,
52 | exact_token_a: exactTokenA,
53 | token_b: tokenB,
54 | slippage: slippage,
55 | deadline: deadline,
56 | },
57 | };
58 | }
59 |
60 | function newMsgSwapForExactTokens(
61 | requester: string,
62 | tokenA: Coin,
63 | exactTokenB: Coin,
64 | slippage: string,
65 | deadline: string
66 | ) {
67 | return {
68 | type: 'swap/MsgSwapForExactTokens',
69 | value: {
70 | requester: requester,
71 | token_a: tokenA,
72 | exact_token_b: exactTokenB,
73 | slippage: slippage,
74 | deadline: deadline,
75 | },
76 | };
77 | }
78 |
79 | export const swap = {
80 | newMsgDeposit,
81 | newMsgWithdraw,
82 | newMsgSwapExactForTokens,
83 | newMsgSwapForExactTokens,
84 | };
85 |
--------------------------------------------------------------------------------
/src/tx/index.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-var-requires
2 | const sig = require('@kava-labs/sig');
3 | import axios, { AxiosError } from 'axios';
4 | import { URL } from 'url';
5 |
6 | const api = {
7 | getAccount: '/cosmos/auth/v1beta1/accounts',
8 | postTx: '/cosmos/tx/v1beta1/txs',
9 | };
10 |
11 | /**
12 | * Sends an HTTP GET request to Kava
13 | * @param {String} path the request's url extension
14 | * @param {String} base the request's base url
15 | * @return {Promise}
16 | */
17 | async function getTx(path: string, base: string, timeout = 5000, args = {}) {
18 | const requestUrl = new URL(path, base).toString();
19 |
20 | return await retry(
21 | axios.get,
22 | axios,
23 | [applyRequestArgs(requestUrl, args)],
24 | Math.floor(timeout / 1000),
25 | 1000,
26 | false
27 | );
28 | }
29 |
30 | /**
31 | * Apply args to an HTTP GET request's url
32 | * @param {String} url the request's url (base + path extension)
33 | * @param {String} args the request's http arguments in JSON e.g. {status: 'Open'}
34 | * @return {String}
35 | */
36 | function applyRequestArgs(url: string, args: Record = {}) {
37 | const search = [];
38 | for (const k in args) {
39 | search.push(`${k}=${args[k]}`);
40 | }
41 | return `${url}?${search.join('&')}`;
42 | }
43 |
44 | type RetryFunction = (
45 | fn: any,
46 | thisArg: any,
47 | args: string[],
48 | retriesLeft: number,
49 | interval: number,
50 | exponential: boolean
51 | ) => Promise;
52 |
53 | /**
54 | * Retries the given function until it succeeds given a number of retries and an interval between them. They are set
55 | * by default to retry 5 times with 1sec in between. There's also a flag for exponential back-off.
56 | * source (with minor edits): https://gitlab.com/snippets/1775781
57 | * @param {Function} fn - Returns a promise
58 | * @param {Object} thisArg - the object that will be the 'this' argument to the input function
59 | * @param {Array} args - array of arguments to call the input function with
60 | * @param {Number} retriesLeft - Number of retries. If >1 will keep retrying
61 | * @param {Number} interval - milliseconds between retries. If exponential set to true will be doubled each retry
62 | * @param {Boolean} exponential - Flag for exponential back-off mode
63 | * @return {Promise<*>}
64 | */
65 | const retry: RetryFunction = async (
66 | fn: any,
67 | thisArg: any,
68 | args: string[],
69 | retriesLeft = 5,
70 | interval = 1000,
71 | exponential = false
72 | ) => {
73 | try {
74 | const result = await fn.apply(thisArg, args);
75 | return result;
76 | } catch (error) {
77 | if (retriesLeft) {
78 | await new Promise((r) => setTimeout(r, interval));
79 | return retry(
80 | fn,
81 | thisArg,
82 | args,
83 | retriesLeft - 1,
84 | exponential ? interval * 2 : interval,
85 | exponential
86 | );
87 | } else
88 | throw new Error(`Max retries reached:
89 | error: ${error}`);
90 | }
91 | };
92 |
93 | /**
94 | * Loads an account's account number and sequence from Kava
95 | * @param {String} address the address to be fetched
96 | * @param {String} base the request's base url
97 | * @param {Number} timeout request is attempted every 1000 milliseconds until millisecond timeout is reached
98 | * @return {Promise}
99 | */
100 | async function loadMetaData(address: string, base: string, timeout = 2000) {
101 | const path = `${api.getAccount}/${address}`;
102 | const res = await getTx(path, base, timeout);
103 | const account = res?.data?.account;
104 | let seqNum = account?.sequence || '0';
105 | let accNum = account?.account_number;
106 | const vestingBaseAcct = account?.base_vesting_account?.base_account;
107 | if (vestingBaseAcct) {
108 | seqNum = vestingBaseAcct?.sequence || '0';
109 | accNum = vestingBaseAcct?.account_number;
110 | }
111 | if (!(accNum || seqNum)) {
112 | throw new Error(
113 | 'account number or sequence number from rest server are undefined'
114 | );
115 | }
116 |
117 | const signMetaData = {
118 | account_number: accNum,
119 | sequence: seqNum,
120 | };
121 |
122 | return signMetaData;
123 | }
124 |
125 | /**
126 | * Packages, signs, and verifies a transaction
127 | * @param {Object} tx an unsigned tx object
128 | * @param {Object} signMetaData contains account number, sequence, and chain ID
129 | * @param {Object} wallet the wallet that will be used to sign the tx
130 | * @return {Promise}
131 | */
132 | function signTx(tx: any, signMetaData: any, wallet: any) {
133 | tx = sig.signTx(tx, signMetaData, wallet);
134 | if (!sig.verifyTx(tx, signMetaData)) {
135 | throw new Error('problem signing tx, generated signature is invalid');
136 | }
137 | return tx;
138 | }
139 |
140 | /**
141 | * Sends an HTTP POST request containing a signed transaction to Kava
142 | * @param {Object} tx a signed tx
143 | * @param {String} base the request's base url
144 | * @param {String} mode transaction broadcast mode
145 | * @return {Promise}
146 | */
147 | async function broadcastTx(tx: any, base: string, mode: string) {
148 | let txRes;
149 | try {
150 | const url = new URL(api.postTx, base).toString();
151 | txRes = await axios.post(url, { tx_bytes: tx, mode: mode });
152 | } catch (err) {
153 | if (axios.isAxiosError(err)) {
154 | logErr(err);
155 | }
156 | }
157 |
158 | // Check for and handle any tendermint errors
159 | const rsp = txRes?.data?.tx_response;
160 | try {
161 | if (rsp?.code) {
162 | throw new Error(`tx not accepted by chain: ${rsp?.raw_log}`);
163 | }
164 | } catch (err) {
165 | return err;
166 | }
167 |
168 | return rsp?.txhash;
169 | }
170 |
171 | /**
172 | * Parses and logs tx-related errors
173 | * @param {AxiosError} err an error resulting from a tx-related action
174 | */
175 | const logErr = (err: AxiosError) => {
176 | // Load status, status text, and error
177 | const status = err.response?.status;
178 | const statusText = err.response?.statusText;
179 | const error = err.response?.data;
180 |
181 | // Log status, status text, and error, or if unidentified, log network error
182 | status ? console.log('Status:', status) : null;
183 | statusText ? console.log('Status text:', statusText) : null;
184 | error ? console.log('Error:', error) : null;
185 | if (!status && !statusText && !error) {
186 | console.log('Network error:', err);
187 | }
188 | };
189 |
190 | export const tx = {
191 | getTx,
192 | loadMetaData,
193 | signTx,
194 | broadcastTx,
195 | logErr,
196 | };
197 |
--------------------------------------------------------------------------------
/src/types/Address.ts:
--------------------------------------------------------------------------------
1 | export type InternalEVMAddress = string;
2 |
--------------------------------------------------------------------------------
/src/types/Coin.ts:
--------------------------------------------------------------------------------
1 | export type Coin = {
2 | amount: string;
3 | denom: string;
4 | };
5 |
--------------------------------------------------------------------------------
/src/types/DenomToClaim.ts:
--------------------------------------------------------------------------------
1 | export type DenomToClaim = {
2 | denom: string;
3 | multiplier_name: string;
4 | };
5 |
--------------------------------------------------------------------------------
/src/types/Message.ts:
--------------------------------------------------------------------------------
1 | export type Message = {
2 | type: string;
3 | value: T;
4 | };
5 |
--------------------------------------------------------------------------------
/src/types/Strategy.ts:
--------------------------------------------------------------------------------
1 | export enum Strategy {
2 | STRATEGY_TYPE_UNSPECIFIED = 0,
3 | STRATEGY_TYPE_HARD = 1,
4 | STRATEGY_TYPE_SAVINGS = 2,
5 | }
6 |
--------------------------------------------------------------------------------
/src/types/VoteType.ts:
--------------------------------------------------------------------------------
1 | export enum VoteType {
2 | YES = 1,
3 | ABSTAIN = 2,
4 | NO = 3,
5 | }
6 |
--------------------------------------------------------------------------------
/src/types/Wallet.ts:
--------------------------------------------------------------------------------
1 | export type Wallet = {
2 | address: string;
3 | };
4 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Address';
2 | export * from './Coin';
3 | export * from './DenomToClaim';
4 | export * from './Strategy';
5 | export * from './VoteType';
6 | export * from './Wallet';
7 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import SHA256 from 'crypto-js/sha256';
2 | import hexEncoding from 'crypto-js/enc-hex';
3 | import Big from 'big.js';
4 | import cryptoRand from 'crypto';
5 | import { crypto } from '../crypto';
6 | import { ethers } from 'ethers';
7 | import bech32 from 'bech32';
8 |
9 | const RandomNumberLength = 64;
10 |
11 | // Precision is relative to KAVA or 10**6
12 | const precision: Record = {
13 | kava: 1,
14 | ukava: Math.pow(10, 6),
15 | };
16 |
17 | /**
18 | * Computes a single SHA256 digest.
19 | * @param {string} hex message to hash
20 | * @returns {string} hash output
21 | */
22 | const sha256 = (hex: string) => {
23 | if (typeof hex !== 'string') throw new Error('sha256 expects a hex string');
24 | if (hex.length % 2 !== 0)
25 | throw new Error(`invalid hex string length: ${hex}`);
26 | const hexEncoded = hexEncoding.parse(hex);
27 | return SHA256(hexEncoded).toString();
28 | };
29 |
30 | /**
31 | * Generates a hex-encoded 256-bit random number
32 | * @returns {string} the hex-encoded number
33 | */
34 | const generateRandomNumber = () => {
35 | return cryptoRand
36 | .randomBytes(Math.ceil(RandomNumberLength / 2))
37 | .toString('hex')
38 | .slice(0, RandomNumberLength);
39 | };
40 |
41 | /**
42 | * Computes sha256 of random number and timestamp
43 | * @param {String} randomNumber
44 | * @param {Number} timestamp
45 | * @returns {string} sha256 result
46 | */
47 | const calculateRandomNumberHash = (randomNumber: string, timestamp: number) => {
48 | const timestampHexStr = timestamp.toString(16);
49 | let timestampHexStrFormat = timestampHexStr;
50 | for (let i = 0; i < 16 - timestampHexStr.length; i++) {
51 | timestampHexStrFormat = '0' + timestampHexStrFormat;
52 | }
53 | const timestampBytes = Buffer.from(timestampHexStrFormat, 'hex');
54 | const newBuffer = Buffer.concat([
55 | Buffer.from(randomNumber, 'hex'),
56 | timestampBytes,
57 | ]);
58 | return sha256(newBuffer.toString('hex'));
59 | };
60 |
61 | /**
62 | * Computes swapID
63 | * @param {String} randomNumberHash
64 | * @param {String} sender
65 | * @param {String} senderOtherChain
66 | * @returns {string} sha256 result
67 | */
68 | const calculateSwapID = (
69 | randomNumberHash: string,
70 | sender: string,
71 | senderOtherChain: string
72 | ) => {
73 | const randomNumberHashBytes = Buffer.from(randomNumberHash, 'hex');
74 | const senderBytes = crypto.decodeAddress(sender);
75 | const sendOtherChainBytes = Buffer.from(
76 | senderOtherChain.toLowerCase(),
77 | 'utf8'
78 | );
79 | const newBuffer = Buffer.concat([
80 | randomNumberHashBytes,
81 | senderBytes,
82 | sendOtherChainBytes,
83 | ]);
84 | return sha256(newBuffer.toString('hex'));
85 | };
86 |
87 | /**
88 | * Converts coin decimals between kava and ukava
89 | * @param {String} inputAmount value of the input asset
90 | * @param {String} inputDenom denom of the input asset
91 | * @param {String} outputDenom denom of the output asset
92 | * @return {object} coins result
93 | */
94 | const convertCoinDecimals = (
95 | inputAmount: string,
96 | inputDenom: string,
97 | outputDenom: string
98 | ) => {
99 | const amount = new Big(inputAmount);
100 |
101 | try {
102 | if (!precision[inputDenom] || !precision[outputDenom]) {
103 | throw new Error('Invalid asset pairing for decimal conversion.');
104 | }
105 | } catch (err) {
106 | if (err instanceof Error) {
107 | console.log('Error:', err.message);
108 | }
109 | return;
110 | }
111 |
112 | const amountString = amount
113 | .mul(precision[outputDenom])
114 | .div(precision[inputDenom])
115 | .toString();
116 |
117 | return formatCoins(amountString, outputDenom);
118 | };
119 |
120 | /**
121 | * Formats a denom and amount into Cosmos-SDK compatible sdk.Coin object
122 | * @param {String} amount value of the asset
123 | * @param {String} denom name of the asset
124 | * @return {object} resulting formatted coin
125 | */
126 | const formatCoin = (amount: string | number, denom: string) => {
127 | return {
128 | denom: String(denom),
129 | amount: String(amount),
130 | };
131 | };
132 |
133 | /**
134 | * Formats a denom and amount into Cosmos-SDK compatible sdk.Coins object
135 | * @param {String} amount value of the asset
136 | * @param {String} denom name of the asset
137 | * @return {object} resulting formatted coins
138 | */
139 | const formatCoins = (amount: string | number, denom: string) => {
140 | return [
141 | {
142 | denom: String(denom),
143 | amount: String(amount),
144 | },
145 | ];
146 | };
147 |
148 | /**
149 | * Formats an array of denoms and amounts into Cosmos-SDK compatible sdk.Coins object
150 | * @param {String} amounts an array of asset amounts
151 | * @param {String} denoms an array of asset denoms
152 | * @return {object} resulting formatted coins
153 | */
154 | const formatMultiCoins = (amounts: string[], denoms: string[]) => {
155 | try {
156 | if (amounts.length != denoms.length) {
157 | throw new Error('Every amount must have exactly 1 corresponding denom.');
158 | }
159 | } catch (err) {
160 | if (err instanceof Error) {
161 | console.log('Error:', err.message);
162 | }
163 | return;
164 | }
165 |
166 | const coins = [];
167 | for (let i = 0; i < amounts.length; i++) {
168 | const coin = formatCoin(amounts[i], denoms[i]);
169 | coins.push(coin);
170 | }
171 | return coins;
172 | };
173 |
174 | /**
175 | * Takes current day and adds desired amount of seconds and converts to unix time
176 | * @param {number} seconds for the transaction to process
177 | * @return {object} resulting in a unix timestring, defaulting to 1 day
178 | */
179 |
180 | const calculateUnixTime = (seconds = 86400) => {
181 | return String(Math.floor(Date.now() / 1000) + seconds);
182 | };
183 |
184 | /**
185 | *
186 | * @param kavaAddress string
187 | * @returns string representing eth address from given kava address
188 | */
189 | export function kavaToEthAddress(kavaAddress: string) {
190 | return ethers.utils.getAddress(
191 | ethers.utils.hexlify(bech32.fromWords(bech32.decode(kavaAddress).words))
192 | );
193 | }
194 |
195 | /**
196 | *
197 | * @param ethereumAddress string
198 | * @returns string representing kava address from give eth address
199 | */
200 | export function ethToKavaAddress(ethereumAddress: string) {
201 | return bech32.encode(
202 | 'kava',
203 | bech32.toWords(
204 | ethers.utils.arrayify(ethers.utils.getAddress(ethereumAddress))
205 | )
206 | );
207 | }
208 |
209 | export const utils = {
210 | generateRandomNumber,
211 | calculateRandomNumberHash,
212 | calculateSwapID,
213 | calculateUnixTime,
214 | convertCoinDecimals,
215 | formatCoin,
216 | formatCoins,
217 | formatMultiCoins,
218 | ethToKavaAddress,
219 | kavaToEthAddress,
220 | };
221 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Node 14",
4 |
5 | "compilerOptions": {
6 | "downlevelIteration": true,
7 | "lib": ["es2020"],
8 | "module": "commonjs",
9 | "target": "es5",
10 | "declaration": true,
11 | "esModuleInterop": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "outDir": "./lib",
14 | "skipLibCheck": true,
15 | "strict": true,
16 | "typeRoots": ["./node_modules/@types/", "./src/@types"]
17 | },
18 | "include": ["src"]
19 | }
20 |
--------------------------------------------------------------------------------