Skip to content

Instantly share code, notes, and snippets.

@Emyrk
Last active September 10, 2019 20:53
Show Gist options
  • Save Emyrk/68f98b7aca65208269dded185682c972 to your computer and use it in GitHub Desktop.
Save Emyrk/68f98b7aca65208269dded185682c972 to your computer and use it in GitHub Desktop.
Pegnet Conversion Math
import (
"fmt"
"math/big"
)
// OraclePriceRecordAssetList is used such that the marshaling of the assets
// is in the same order, and we still can use map access in the code
// Key: Asset
// Value: Exchange rate to USD * 1e8
type OraclePriceRecordAssetList map[string]uint64
// ExchangeTo tells us how much we need to spend given the amount we want is fixed. All inputs must be in their
// lowest divisible unit as whole numbers.
// ?? FROM -> X TO
//
// X TO to_usd 1
// ------ * --------- = USD * -------- = FROM
// 1 1 from_usd
func (o OraclePriceRecordAssetList) ExchangeTo(from string, to string, want int64) (int64, error) {
return o.Exchange(to, want, from)
}
// ExchangeFrom tells us how much we need to spend given the amount we have is fixed. All inputs must be in their
// lowest divisible unit as whole numbers.
// X FROM -> ?? TO
//
// X FROM from_usd 1
// ------ * --------- = USD * -------- = TO
// 1 1 to_usd
//
func (o OraclePriceRecordAssetList) ExchangeFrom(from string, have int64, to string) (int64, error) {
return o.Exchange(from, have, to)
}
// Exchange will use big ints to avoid overflows. All inputs must be in their
// lowest divisible unit as whole numbers.
// TODO: Will we ever overflow a int64?
func (o OraclePriceRecordAssetList) Exchange(input string, amount int64, output string) (int64, error) {
fromRate, toRate, err := o.ExchangeRates(input, output)
if err != nil {
return 0, err
}
// Convert the rates to integers. Because these rates are in USD, we will switch all our inputs to
// 1e-8 fixed point. The `want` should already be in this format. This should be the most amount of
// accuracy a miner reports. Anything beyond the 8th decimal point, we cannot account for.
fr := big.NewInt(int64(fromRate))
tr := big.NewInt(int64(toRate ))
amt := big.NewInt(amount)
// Now we can run the conversion
// ALWAYS multiply first. If you do not adhere to the order of operations shown
// explicitly below, your answer will be incorrect. When doing a conversion,
// always multiply before you divide.
// (amt * fromrate) / torate
num := big.NewInt(0).Mul(amt, fr)
num = num.Div(num, tr)
return num.Int64(), nil
}
// ExchangeRates finds the exchange rates for FROM and TO in usd as the base pair.
func (o OraclePriceRecordAssetList) ExchangeRates(from, to string) (fromRate uint64, toRate uint64, err error) {
var ok bool
// First we need to ensure we have the pricing for each side of the exchange
fromRate, ok = o[from]
if !ok {
return 0, 0, fmt.Errorf("did not find a rate for %s", from)
}
toRate, ok = o[to]
if !ok {
return 0, 0, fmt.Errorf("did not find a rate for %s", to)
}
if toRate == 0 || fromRate == 0 {
return 0, 0, fmt.Errorf("one of the rates found is 0")
}
// fromRate / toRate
return fromRate, toRate, nil
}
@Emyrk
Copy link
Author

Emyrk commented Sep 10, 2019

L39-L60 is the implementation of the math for a conversion for the tx amount INPUT -> OUTPUT

Notes: We store price quotes as uint64, but a BigInt requires int64 as the input. We need to decide if we need to handle overflows, and keep balances as big ints vs int64s.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment