Skip to content

Commit

Permalink
Add SDR calculation (#30)
Browse files Browse the repository at this point in the history
* - implement SDR price calculation from sdr basket settings

* - added log output

* - only calculate SDR if not provided from price server

* - wrong type of variable

* - fixed typo
- add fixed price for USD

* - type fixed

* - move logic from feeder to price server

* - move to adjustPrices

* - fixed invalid structure of prices

* - allow sdr basket being passed by env

* - make SDR a correct fiat pair

* - rework parsing of website

* - remove logs

* - bump version
  • Loading branch information
StrathCole authored Sep 17, 2024
1 parent ba0ca90 commit a238d31
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 15 deletions.
4 changes: 2 additions & 2 deletions feeder/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion feeder/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@classic-terra/oracle-feeder",
"version": "3.1.4",
"version": "3.1.5",
"main": "src/index.ts",
"license": "Apache-2.0",
"scripts": {
Expand Down
8 changes: 8 additions & 0 deletions price-server/config/default-sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,4 +587,12 @@ module.exports = {
apiKey: '', // necessary
},
},
sdrBasket: {
// to calculate SDR value if not available from fiat providers
USD: '0.57813',
EUR: '0.37379',
JPY: '13.452',
CNY: '1.0993',
GBP: '0.080870',
},
}
10 changes: 10 additions & 0 deletions price-server/config/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,14 @@ module.exports = {
apiKey: process.env.FIAT_PROVIDER_ALPHA_VANTAGE_API_KEY || '', // necessary
},
},
sdrBasket: process.env.SDR_BASKET
? JSON.parse(process.env.SDR_BASKET)
: {
// to calculate SDR value if not available from fiat providers
USD: '0.57813',
EUR: '0.37379',
JPY: '13.452',
CNY: '1.0993',
GBP: '0.080870',
},
}
4 changes: 2 additions & 2 deletions price-server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion price-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@classic-terra/price-server",
"version": "3.1.4",
"version": "3.1.5",
"main": "src/main.ts",
"license": "Apache-2.0",
"scripts": {
Expand Down
63 changes: 62 additions & 1 deletion price-server/src/provider/fiat/FiatProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as config from 'config'
import { Provider, ProviderOptions } from 'provider/base'
import { PriceBySymbol, Provider, ProviderOptions } from 'provider/base'
import * as logger from 'lib/logger'
import { CurrencyLayer, AlphaVantage, Fixer, ExchangeRate, Fer, Frankfurter, Fastforex, IMF } from './quoter'
import BigNumber from 'bignumber.js'
import { getBaseCurrency } from 'lib/currency'

class FiatProvider extends Provider {
constructor(options: ProviderOptions) {
Expand Down Expand Up @@ -32,6 +35,54 @@ class FiatProvider extends Provider {
await this.tick(Date.now())
}

protected calculateSDR(prices: PriceBySymbol): BigNumber | undefined {
if (!config.sdrBasket) {
logger.error(`calculateSDR: config.sdrBasket not found`)
return undefined
}

const priceList = Object.keys(prices).map((symbol) => ({
denom: getBaseCurrency(symbol),
price: prices[symbol].toFixed(8),
}))

// check if all prices from the basket are available
for (const denom of Object.keys(config.sdrBasket)) {
if (denom === 'USD') {
continue
}

if (!priceList.find((p) => p.denom === denom)) {
logger.error(`calculateSDR price for ${denom} not found`)
return undefined
}
}

// calculate SDR price
let sdrPrice: BigNumber | undefined = undefined

try {
sdrPrice = Object.entries(config.sdrBasket).reduce((acc, [denom, weight]: [string, string]) => {
const price = denom === 'USD' ? BigNumber(1) : priceList.find((p) => p.denom === denom)?.price || BigNumber(0)
if (!price) {
throw new Error(`price for ${denom} not found`)
}
return acc.plus(new BigNumber(price).times(weight))
}, new BigNumber(0))
} catch (err) {
logger.error(`getPrices: error calculating SDR price: ${err.message}`)
return undefined
}

if (!sdrPrice) {
logger.error(`getPrices: error calculating SDR price`)
return undefined
}

logger.info(`getPrices: calculated SDR price: ${sdrPrice.toString()}`)
return sdrPrice
}

protected adjustPrices(): void {
for (const symbol of this.symbols) {
delete this.priceBySymbol[symbol]
Expand All @@ -46,6 +97,16 @@ class FiatProvider extends Provider {
}
}
}

if (!this.priceBySymbol['SDR/USD']) {
logger.info(`No SDR price found, falling back to calculation.`)
const sdrPrice = this.calculateSDR(this.priceBySymbol)
if (sdrPrice && sdrPrice.isNaN() === false) {
this.priceBySymbol['SDR/USD'] = sdrPrice
} else {
logger.error(`No SDR price found, calculation failed.`)
}
}
}
}

Expand Down
24 changes: 16 additions & 8 deletions price-server/src/provider/fiat/quoter/IMF.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ const SDR_VALUATION_URL = 'https://www.imf.org/external/np/fin/data/rms_sdrv.asp
async function fetchQuote() {
const text = await fetch(SDR_VALUATION_URL).then((res) => res.text())

const doc = parse(text)
const tds = doc.querySelectorAll('.tightest td')
const idx = tds.findIndex((el) => el.structuredText === ' SDR1 = US$')

if (idx === -1) {
throw new Error('cannot find SDR/USD element from HTML document')
let idx = -1
const tables = text.match(/<table[^>]*>([\s\S]*?)<\/table>/gi) || []
for (const table of tables) {
const doc = parse(table.trim())
const tds = doc.querySelectorAll('td')

idx = tds.findIndex((el) => {
return el.structuredText.trim() === 'SDR1 = US$'
})

if (idx !== -1) {
// sample format: ' 1.32149 2'
return num(tds[idx + 1].structuredText.split(' ')[1])
}
}

// sample format: ' 1.32149 2'
return num(tds[idx + 1].structuredText.split(' ')[1])
// nothing found
throw new Error('cannot find SDR/USD element from HTML document')
}

// fetchQuote().then(console.log).catch(console.error) // For test
Expand Down

0 comments on commit a238d31

Please sign in to comment.