Skip to content

Commit

Permalink
Merge 3f3b127 into 6b2244b
Browse files Browse the repository at this point in the history
  • Loading branch information
Emyrk committed Aug 16, 2019
2 parents 6b2244b + 3f3b127 commit 96a22d2
Show file tree
Hide file tree
Showing 14 changed files with 304 additions and 42 deletions.
16 changes: 10 additions & 6 deletions common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
ConfigPegnetNetwork = "Miner.Network"

ConfigCoinMarketCapKey = "Oracle.CoinMarketCapKey"
Config1ForgeKey = "Oracle.1ForgeKey"
)

// DefaultConfigOptions gives us the ability to add configurable settings that really
Expand Down Expand Up @@ -90,19 +91,22 @@ func NewUnitTestConfigProvider() *UnitTestConfigProvider {
APILayerKey=CHANGEME
OpenExchangeRatesKey=CHANGEME
CoinMarketCapKey=CHANGEME
1ForgeKey=CHANGEME
[OracleDataSources]
APILayer=1
ExchangeRates=3
OpenExchangeRates=2
FreeForexAPI=-1
APILayer=-1
ExchangeRates=-1
OpenExchangeRates=-1
1Forge=-1
# Crypto
CoinMarketCap=3
CoinCap=4
CoinMarketCap=-1
CoinCap=-1
# Commodities
Kitco=10
Kitco=-1
`
return d
Expand Down
33 changes: 22 additions & 11 deletions defaultconfig.ini
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,36 @@
OpenExchangeRatesKey=CHANGEME
# Must get an api key here https://coinmarketcap.com/api/
CoinMarketCapKey=CHANGEME
# Must get an api key here https://1forge.com/forex-data-api
1ForgeKey=CHANGEME


# This section must ONLY include data sources and their priorities. Any configuration
# related to a source should be specified in the [Oracle] section.
# -1 == disabled
[OracleDataSources]
FreeForexAPI=1
APILayer=3 # Paid source
ExchangeRates=9 # Daily prices, rank it low
OpenExchangeRates=2
# Always rank this the highest at 0. It pegs USD at 1USD = 1USD.
# This is not a website, this is a hardcoded '1'. Don't change this
FixedUSD=0

# Crypto
CoinMarketCap=4
CoinCap=5
# Paid Sources
APILayer=-1
1Forge=1

# Commodities
Kitco=10 # Web scraping, rank it low
# Free sources, signup required
CoinMarketCap=-1

# Free sources, no signup required
FreeForexAPI=2
CoinCap=3


# Web scraping, rank it low
Kitco=10


# This section should be done with caution. There is no error handling
# if you put in a bad order or use datasources that you did not enable.
[OracleAssetDataSourcesPriority]
# Overrride BTC order
XBT=CoinMarketCap,OpenExchangeRates,CoinCap
# Example to overrride BTC order
# XBT=CoinMarketCap,OpenExchangeRates,CoinCap
172 changes: 172 additions & 0 deletions polling/1forge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package polling

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"

"github.com/cenkalti/backoff"
"github.com/pegnet/pegnet/common"
"github.com/zpatrick/go-config"
)

// OneForgeDataSource is the datasource at https://1forge.com
type OneForgeDataSource struct {
config *config.Config
apikey string
}

func NewOneForgeDataSourceDataSource(config *config.Config) (*OneForgeDataSource, error) {
s := new(OneForgeDataSource)
s.config = config

var err error
// Load api key
s.apikey, err = s.config.String(common.Config1ForgeKey)
if err != nil {
return nil, err
}

return s, nil
}

func (d *OneForgeDataSource) Name() string {
return "1Forge"
}

func (d *OneForgeDataSource) Url() string {
return "https://1forge.com"
}

func (d *OneForgeDataSource) ApiUrl() string {
return "https://forex.1forge.com/1.0.3/"
}

func (d *OneForgeDataSource) SupportedPegs() []string {
// Does not have all the currencies, commodities, or crypto
return common.MergeLists([]string{"EUR", "JPY", "GBP", "CAD", "CHF", "SGD", "HKD", "MXN"}, []string{"XAU", "XAG"}, []string{"XBT", "XBC", "LTC", "ETH", "DASH"})
}

// AssetMapping changes some asset symbols to others to match 1forge
func (d *OneForgeDataSource) AssetMapping() map[string]string {
return map[string]string{
"XBT": "BTC",
"XBC": "BCH",
"DASH": "DSH",
}
}

func (d *OneForgeDataSource) FetchPegPrices() (peg PegAssets, err error) {
resp, err := d.Call1Forge()
if err != nil {
return nil, err
}

peg = make(map[string]PegItem)

respRates := make(map[string]OneForgeDataSourceRate)
for _, r := range resp {
respRates[r.Symbol] = r
}

mapping := d.AssetMapping()

// Look for each asset we support
for _, asset := range d.SupportedPegs() {

assetSym := asset
if v, ok := mapping[asset]; ok {
assetSym = v
}

index := fmt.Sprintf("%sUSD", assetSym)
currency, ok := respRates[index]
if !ok {
continue
}

timestamp := time.Unix(currency.Timestamp, 0)
peg[asset] = PegItem{Value: currency.Price, WhenUnix: timestamp.Unix(), When: timestamp}
}

return
}

func (d *OneForgeDataSource) FetchPegPrice(peg string) (i PegItem, err error) {
return FetchPegPrice(peg, d.FetchPegPrices)
}

func (d *OneForgeDataSource) Call1Forge() ([]OneForgeDataSourceRate, error) {
var resp []OneForgeDataSourceRate

operation := func() error {
data, err := d.FetchPeggedPrices()
if err != nil {
return err
}

resp, err = d.ParseFetchedPrices(data)
if err != nil {
// Try the other variation
return err
}
return nil
}

err := backoff.Retry(operation, PollingExponentialBackOff())
return resp, err
}

func (d *OneForgeDataSource) ParseFetchedPrices(data []byte) ([]OneForgeDataSourceRate, error) {
var resp []OneForgeDataSourceRate

err := json.Unmarshal(data, &resp)
if err != nil {
return nil, err
}
return resp, nil
}

func (d *OneForgeDataSource) FetchPeggedPrices() ([]byte, error) {
client := NewHTTPClient()
req, err := http.NewRequest("GET", d.ApiUrl()+"quotes", nil)
if err != nil {
return nil, err
}

mapping := d.AssetMapping()

var ids []string
for _, asset := range d.SupportedPegs() {
assetSym := asset
if v, ok := mapping[asset]; ok {
assetSym = v
}
ids = append(ids, assetSym+"USD")
}

q := url.Values{}
q.Add("pairs", strings.Join(ids, ","))
q.Add("api_key", d.apikey)
req.URL.RawQuery = q.Encode()

resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return ioutil.ReadAll(resp.Body)
}

type OneForgeDataSourceRate struct {
Symbol string `json:"symbol"`
Bid float64 `json:"bid"`
Ask float64 `json:"ask"`
Price float64 `json:"price"`
Timestamp int64 `json:"timestamp"`
}
18 changes: 18 additions & 0 deletions polling/1forge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) of parts are held by the various contributors (see the CLA)
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
package polling_test

import (
"testing"
)

func TestFixed1ForgePeggedAssets(t *testing.T) {
FixedDataSourceTest(t, "1Forge", []byte(oneForgeResp))
}

// Needs a key
//func TestActual1ForgePeggedAssets(t *testing.T) {
// ActualDataSourceTest(t, "1Forge")
//}

var oneForgeResp = `[{"symbol":"EURUSD","bid":1.10911,"ask":1.10911,"price":1.10911,"timestamp":1565971649},{"symbol":"JPYUSD","bid":0.00940673,"ask":0.00940689,"price":0.00940681,"timestamp":1565971649},{"symbol":"GBPUSD","bid":1.21479,"ask":1.21481,"price":1.2148,"timestamp":1565971649},{"symbol":"CADUSD","bid":0.752808,"ask":0.752819,"price":0.752814,"timestamp":1565971649},{"symbol":"CHFUSD","bid":1.02032,"ask":1.02034,"price":1.02033,"timestamp":1565971649},{"symbol":"SGDUSD","bid":0.72198,"ask":0.722006,"price":0.721993,"timestamp":1565971649},{"symbol":"HKDUSD","bid":0.127504,"ask":0.127507,"price":0.127506,"timestamp":1565971649},{"symbol":"MXNUSD","bid":0.0511065,"ask":0.0511099,"price":0.0511082,"timestamp":1565971649},{"symbol":"XAUUSD","bid":1512.68,"ask":1512.79,"price":1512.735,"timestamp":1565971649},{"symbol":"XAGUSD","bid":17.142,"ask":17.147,"price":17.1445,"timestamp":1565971649},{"symbol":"BTCUSD","bid":10335.2,"ask":10349.36,"price":10342.28,"timestamp":1565971649},{"symbol":"BCHUSD","bid":308.341,"ask":311.448,"price":309.8945,"timestamp":1565971649},{"symbol":"LTCUSD","bid":74.81,"ask":75.5,"price":75.155,"timestamp":1565971649},{"symbol":"ETHUSD","bid":184.31,"ask":186.64,"price":185.475,"timestamp":1565971649},{"symbol":"DSHUSD","bid":93.5413,"ask":94.4832,"price":94.0122,"timestamp":1565971649}]`
3 changes: 1 addition & 2 deletions polling/apilayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import (

// APILayerDataSource is the datasource at http://www.apilayer.net
type APILayerDataSource struct {
config *config.Config
lastPeg PegAssets
config *config.Config
}

func NewAPILayerDataSource(config *config.Config) (*APILayerDataSource, error) {
Expand Down
22 changes: 15 additions & 7 deletions polling/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ var dLog = log.WithField("id", "DataSources")
// The reason I have the `new(DataSource` is so I can get the name, url, and supported
// pegs from this map. It's useful in the cmdline to fetch the datasources dynamically.
var AllDataSources = map[string]IDataSource{
"APILayer": new(APILayerDataSource),
"CoinCap": new(CoinCapDataSource),
"ExchangeRates": new(ExchangeRatesDataSource),
"Kitco": new(KitcoDataSource),
"OpenExchangeRates": new(OpenExchangeRatesDataSource),
"CoinMarketCap": new(CoinMarketCapDataSource),
"FreeForexAPI": new(FreeForexAPIDataSource),
"APILayer": new(APILayerDataSource),
"CoinCap": new(CoinCapDataSource),
"FixedUSD": new(FixedUSDDataSource),
// ExchangeRates is daily, don't show people this
//"ExchangeRates": new(ExchangeRatesDataSource),
"Kitco": new(KitcoDataSource),
// OpenExchangeRates is hourly, don't show people this
//"OpenExchangeRates": new(OpenExchangeRatesDataSource),
"CoinMarketCap": new(CoinMarketCapDataSource),
"FreeForexAPI": new(FreeForexAPIDataSource),
"1Forge": new(OneForgeDataSource),
}

func AllDataSourcesList() []string {
Expand Down Expand Up @@ -81,6 +85,10 @@ func NewDataSource(source string, config *config.Config) (IDataSource, error) {
ds, err = NewCoinMarketCapDataSource(config)
case "FreeForexAPI":
ds, err = NewFreeForexAPIDataSource(config)
case "1Forge":
ds, err = NewOneForgeDataSourceDataSource(config)
case "FixedUSD":
ds, err = NewFixedUSDDataSource(config)
case "UnitTest": // This will fail outside a unit test
ds, err = NewTestingDataSource(config, source)
default:
Expand Down
3 changes: 1 addition & 2 deletions polling/coincap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import (

// CoinCapDataSource is the datasource at https://coincap.io/
type CoinCapDataSource struct {
config *config.Config
lastPeg PegAssets
config *config.Config
}

func NewCoinCapDataSource(config *config.Config) (*CoinCapDataSource, error) {
Expand Down
5 changes: 2 additions & 3 deletions polling/coinmarketcap.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ import (

// CoinMarketCapDataSource is the datasource at https://coinmarketcap.com/
type CoinMarketCapDataSource struct {
config *config.Config
lastPeg PegAssets
apikey string
config *config.Config
apikey string
}

func NewCoinMarketCapDataSource(config *config.Config) (*CoinMarketCapDataSource, error) {
Expand Down
3 changes: 1 addition & 2 deletions polling/exchangeratesapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import (

// ExchangeRatesDataSource is the datasource at "https://exchangeratesapi.io"
type ExchangeRatesDataSource struct {
config *config.Config
lastPeg PegAssets
config *config.Config
}

func NewExchangeRatesDataSource(config *config.Config) (*ExchangeRatesDataSource, error) {
Expand Down
Loading

0 comments on commit 96a22d2

Please sign in to comment.