@kava-labs/kava-tools
Advanced tools
Comparing version 0.2.2 to 0.4.0
@@ -11,3 +11,3 @@ require('dotenv').config(); | ||
class PriceOracle { | ||
constructor(marketIDs, expiry, expiryThreshold, deviation) { | ||
constructor(marketIDs, expiry, expiryThreshold, deviation, fee) { | ||
if (!marketIDs) { | ||
@@ -25,2 +25,5 @@ throw new Error('must specify at least one market ID'); | ||
} | ||
if (!fee) { | ||
throw new Error('must specify fee') | ||
} | ||
@@ -30,4 +33,4 @@ // Validate each market ID on Binance and CoinGecko | ||
try { | ||
utils.loadBinanceMarket(marketIDs[i]); | ||
utils.loadCoinGeckoMarket(marketIDs[i]); | ||
utils.loadPrimaryMarket(marketIDs[i]); | ||
utils.loadBackupMarket(marketIDs[i]); | ||
} catch (e) { | ||
@@ -44,2 +47,3 @@ console.log("couldn't load remote market from market ID, error:", e); | ||
this.deviation = deviation; | ||
this.fee = fee | ||
} | ||
@@ -126,3 +130,3 @@ | ||
try { | ||
await this.client.checkTxHash(txHash, 25000); | ||
await this.client.checkTxHash(txHash, 120000); | ||
} catch (e) { | ||
@@ -133,3 +137,3 @@ checkTxError = true | ||
try { | ||
await this.client.checkTxHash(txHash, 25000); | ||
await this.client.checkTxHash(txHash, 120000); | ||
} catch (error) { | ||
@@ -151,3 +155,3 @@ console.log(`Tx not accepted by chain: ${error}`); | ||
try { | ||
res = await this.fetchPriceBinance(marketID); | ||
res = await this.fetchPrimaryPrice(marketID); | ||
if (!res.success) { | ||
@@ -160,4 +164,4 @@ binanceError = true | ||
if (binanceError) { | ||
console.log("trying coingecko after error") | ||
res = await this.fetchPriceCoinGecko(marketID); | ||
console.log("trying backup price source after error") | ||
res = await this.fetchBackupPrice(marketID); | ||
} | ||
@@ -167,2 +171,29 @@ return res; | ||
/** | ||
* Fetches price from the primary source for a market | ||
* @param {String} marketID the market's ID | ||
*/ | ||
async fetchPrimaryPrice(marketID) { | ||
switch (marketID) { | ||
case 'usdx:usd': | ||
return this.fetchPriceBitmax(marketID) | ||
default: | ||
return this.fetchPriceBinance(marketID) | ||
} | ||
} | ||
/** | ||
* Fetches price from the backup source for a market | ||
* @param {String} marketID the market's ID | ||
*/ | ||
async fetchBackupPrice(marketID) { | ||
switch (marketID) { | ||
case 'usdx:usd': | ||
return this.fetchPriceBitmax(marketID) | ||
default: | ||
return this.fetchPriceCoinGecko(marketID) | ||
} | ||
} | ||
/** | ||
@@ -198,2 +229,17 @@ * Fetches price from Binance | ||
/** | ||
* Fetches price from Coin Gecko | ||
* @param {String} marketID the market's ID | ||
*/ | ||
async fetchPriceBitmax(marketID) { | ||
let retreivedPrice; | ||
try { | ||
retreivedPrice = await prices.getBitmaxPrice(marketID); | ||
} catch (e) { | ||
console.log(`could not get ${marketID} price from Bitmax`); | ||
return { price: null, success: false }; | ||
} | ||
return { price: retreivedPrice, success: true }; | ||
} | ||
/** | ||
@@ -265,3 +311,3 @@ * Validates price post against expiration time and derivation threshold | ||
); | ||
return await this.client.postPrice(marketID, newPrice, newExpiry, undefined, sequence); | ||
return await this.client.postPrice(marketID, newPrice, newExpiry, this.fee, sequence); | ||
} | ||
@@ -268,0 +314,0 @@ |
@@ -77,5 +77,35 @@ require('log-timestamp'); | ||
var getBitmaxPrice = async (marketID) => { | ||
try { | ||
var url = coinUtils.loadBitmaxQuery(marketID) | ||
} catch (e) { | ||
throw new Error(`could not load ${marketID} query from bitmax`) | ||
} | ||
try { | ||
var priceFetch = await axios.get(url) | ||
} catch(e) { | ||
console.log(e) | ||
throw new Error(`could not fetch ${marketID} price from bitmax`) | ||
} | ||
try { | ||
const proposedPrice = coinUtils.postProcessBitmaxPrice( | ||
marketID, | ||
priceFetch.data.data | ||
) | ||
if (!proposedPrice) { | ||
throw new Error(`could not post-process ${marketID} from bitmax`) | ||
} | ||
return proposedPrice | ||
} catch (e) { | ||
console.log(e) | ||
console.log(`failure to post-process bitmax price request for ${marketID} | ||
data: ${priceFetch.data}`) | ||
throw new Error(`could not post-process ${marketID} price for bitmax`) | ||
} | ||
} | ||
module.exports.prices = { | ||
getBinancePrice, | ||
getCoinGeckoPrice, | ||
getBitmaxPrice, | ||
}; |
# Oracle | ||
Client software for running an oracle on the kava blockchain. Currently tested against kava-testnet-8000. | ||
Client software for running an oracle on the kava blockchain. Currently tested against kava-testnet-11000. | ||
## How it works | ||
At the specified crontab frequency, the oracle client will query the Binance v3 API for price information about that asset. If Binance is down, it will fallback to CoinGecko. If the price meets the threshold for posting (default is 0.5% change from the previous posted price), is will submit a `postprice` transaction to the blockchain along with a time when that price should be considered expired. At the end of each block, the median price of all oracles is selected as the asset's current price. | ||
## Requirements | ||
* NodeJS - tested against version 10.x+ | ||
* npm | ||
* yarn | ||
@@ -19,3 +23,3 @@ ## Setup | ||
``` | ||
npm i | ||
yarn | ||
``` | ||
@@ -30,3 +34,3 @@ | ||
# the chain-id | ||
CHAIN_ID="kava-testnet-8000" | ||
CHAIN_ID="kava-testnet-11000" | ||
@@ -36,4 +40,4 @@ # REST endpoint | ||
# Cron tab for how frequently prices will be posted (ex: 1 minute) | ||
CRONTAB="* * * * *" | ||
# Cron tab for how frequently prices will be posted (ex: 5 minutes) | ||
CRONTAB="*/5 * * * *" | ||
@@ -44,3 +48,3 @@ # bip39 mnemonic of oracle | ||
# List of markets the oracle will post prices for. See pricefeed parameters for the list of active markets. | ||
MARKET_IDS="bnb:usd,bnb:usd:30" | ||
MARKET_IDS="bnb:usd,bnb:usd:30,btc:usd,btc:usd:30,xrp:usd,xrp:usd:30,busd:usd,busd:usd:30,kava:usd,kava:usd:30,hard:usd,hard:usd:30,usdx:usd" | ||
@@ -59,2 +63,5 @@ # percentage deviation from previous price needed to trigger a new price - (example 0.5%) | ||
LEGACY_HD_PATH="false" | ||
# if the oracle should include a fee when posting prices (in integer ukava units). Example will pay 10000ukava (.01 KAVA) | ||
FEE="10000" | ||
``` | ||
@@ -93,5 +100,1 @@ | ||
To run the oracle from a Chainlink node, you need to configure an external adapter for interacting with kava. Instructions can be found at [chainlink-adapter-kava](https://github.com/Kava-Labs/external-adapters-js/tree/master/kava) | ||
## How it works | ||
At the specified crontab frequency, the oracle client will query the Binance v3 API for price information about that asset. If Binance is down, it will fallback to CoinGecko. If the price meets the threshold for posting (default is 0.5% change from the previous posted price), is will submit a `postprice` transaction to the blockchain along with a time when that price should be considered expired. At the end of each block, the median price of all oracles is selected as the asset's current price. |
@@ -15,2 +15,5 @@ const util = require('util'); | ||
); | ||
const BITMAX_V1_TICKER_REQUEST = util.format( | ||
'https://bitmax.io/api/pro/v1/ticker?symbol=%s/%s' | ||
) | ||
@@ -46,3 +49,3 @@ const loadCoinGeckoMarket = (marketID) => { | ||
default: | ||
throw `invalid market id ${marketID}`; | ||
throw `invalid coin gecko market id ${marketID}`; | ||
} | ||
@@ -106,3 +109,3 @@ }; | ||
default: | ||
throw `invalid market id ${marketID}`; | ||
throw `invalid coingecko market id ${marketID}`; | ||
} | ||
@@ -137,2 +140,20 @@ }; | ||
const loadPrimaryMarket = (marketID) => { | ||
switch (marketID) { | ||
case 'usdx:usd': | ||
return loadBitmaxMarket(marketID) | ||
default: | ||
return loadBinanceMarket(marketID) | ||
} | ||
} | ||
const loadBackupMarket = (marketID) => { | ||
switch (marketID) { | ||
case 'usdx:usd': | ||
return loadBitmaxMarket(marketID) | ||
default: | ||
return loadCoinGeckoMarket(marketID) | ||
} | ||
} | ||
const loadBinanceMarket = (marketID) => { | ||
@@ -167,6 +188,15 @@ switch (marketID) { | ||
default: | ||
throw `invalid market id ${marketID}`; | ||
throw `invalid binance market id ${marketID}`; | ||
} | ||
}; | ||
const loadBitmaxMarket = (marketID) => { | ||
switch (marketID) { | ||
case 'usdx:usd': | ||
return 'USDXUSDT'; | ||
default: | ||
throw `invalid bitmax market id ${marketID}` | ||
} | ||
} | ||
const loadBinanceQuery = (marketID) => { | ||
@@ -201,3 +231,3 @@ switch (marketID) { | ||
default: | ||
throw `invalid market id ${marketID}`; | ||
throw `invalid binance market id ${marketID}`; | ||
} | ||
@@ -252,2 +282,18 @@ }; | ||
const loadBitmaxQuery = (marketID) => { | ||
switch (marketID) { | ||
case 'usdx:usd': | ||
return util.format(BITMAX_V1_TICKER_REQUEST, "USDX", "USDT"); | ||
default: | ||
throw `invalid bitmax market id ${marketID}` | ||
} | ||
} | ||
const postProcessBitmaxPrice = (marketID, data) => { | ||
switch (marketID) { | ||
default: | ||
return data.close; | ||
} | ||
}; | ||
module.exports.utils = { | ||
@@ -257,7 +303,12 @@ loadCoinGeckoMarket, | ||
postProcessCoinGeckoPrice, | ||
loadPrimaryMarket, | ||
loadBackupMarket, | ||
loadBinanceMarket, | ||
loadBinanceQuery, | ||
loadBitmaxMarket, | ||
loadBitmaxQuery, | ||
postProcessBinancePrice, | ||
getPreviousPrice, | ||
getPercentChange, | ||
postProcessBitmaxPrice | ||
}; |
{ | ||
"name": "@kava-labs/kava-tools", | ||
"description": "Tools for interacting with the Kava blockchain", | ||
"version": "0.2.2", | ||
"version": "0.4.0", | ||
"license": "Apache-2.0", | ||
@@ -17,4 +17,4 @@ "main": "index.js", | ||
"dependencies": { | ||
"@kava-labs/javascript-sdk": "^3.0.0-beta.1", | ||
"axios": "^0.19.2", | ||
"@kava-labs/javascript-sdk": "^4.0.1-beta.1", | ||
"axios": "^0.21.1", | ||
"coingecko-api": "^1.0.10", | ||
@@ -21,0 +21,0 @@ "dotenv": "^8.2.0", |
@@ -1,2 +0,2 @@ | ||
require('dotenv').config(); | ||
require('dotenv').config({path: process.env.ENV_FILE} ); | ||
const PriceOracle = require('..').PriceOracle; | ||
@@ -13,2 +13,4 @@ const cron = require('node-cron'); | ||
const deviation = process.env.DEVIATION; | ||
let feeAmount = process.env.FEE; | ||
let fee = { amount: [], gas: String(150000) } | ||
let legacyHDPath = false | ||
@@ -18,5 +20,8 @@ if (process.env.LEGACY_HD_PATH === 'true') { | ||
} | ||
if (feeAmount !== "") { | ||
fee = { amount: [{denom: "ukava", amount: feeAmount }], gas: String(150000) } | ||
} | ||
// Initiate price oracle | ||
oracle = new PriceOracle(marketIDs, expiry, expiryThreshold, deviation); | ||
oracle = new PriceOracle(marketIDs, expiry, expiryThreshold, deviation, fee); | ||
await oracle.initClient(lcdURL, mnemonic, legacyHDPath); | ||
@@ -23,0 +28,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
416485
28
1468
32
+ Added@kava-labs/javascript-sdk@4.0.1-beta.2(transitive)
+ Addedaxios@0.21.4(transitive)
+ Addedfollow-redirects@1.15.9(transitive)
- Removed@kava-labs/javascript-sdk@3.0.0-beta.4(transitive)
Updatedaxios@^0.21.1