Skip to content

Commit

Permalink
fix: readd priceoracle
Browse files Browse the repository at this point in the history
  • Loading branch information
metacertain committed Jun 3, 2021
1 parent 2c1bdb2 commit ef69f8c
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 0 deletions.
48 changes: 48 additions & 0 deletions pkg/settlement/swap/priceoracle/mock/priceoracle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mock

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/common"
)

type Service struct {
rate *big.Int
deduct *big.Int
}

func New(rate *big.Int, deduct *big.Int) Service {
return Service{
rate: rate,
deduct: deduct,
}
}

func (s Service) Start() {
}

func (s Service) GetPrice(ctx context.Context) (*big.Int, *big.Int, error) {
return s.rate, s.deduct, nil
}

func (s Service) CurrentRates() (exchangeRate *big.Int, deduction *big.Int, err error) {
return s.rate, s.deduct, nil
}

func (s Service) Close() error {
return nil
}

func DiscoverPriceOracleAddress(chainID int64) (priceOracleAddress common.Address, found bool) {
return common.Address{}, false
}

func (s Service) SetValues(rate *big.Int, deduct *big.Int) {
s.rate = rate
s.deduct = deduct
}
154 changes: 154 additions & 0 deletions pkg/settlement/swap/priceoracle/priceoracle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package priceoracle

import (
"context"
"errors"
"io"
"math/big"
"time"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/transaction"
"github.com/ethersphere/go-price-oracle-abi/priceoracleabi"
)

var (
errDecodeABI = errors.New("could not decode abi data")
goerliContractAddress = common.HexToAddress("0x0c9de531dcb38b758fe8a2c163444a5e54ee0db2")
)

type service struct {
logger logging.Logger
priceOracleAddress common.Address
transactionService transaction.Service
exchangeRate *big.Int
deduction *big.Int
timeDivisor int64
quitC chan struct{}
}

type Service interface {
io.Closer
// Deposit starts depositing erc20 token into the chequebook. This returns once the transactions has been broadcast.
CurrentRates() (exchangeRate *big.Int, deduction *big.Int, err error)
GetPrice(ctx context.Context) (*big.Int, *big.Int, error)
Start()
}

var (
priceOracleABI = transaction.ParseABIUnchecked(priceoracleabi.PriceOracleABIv0_1_0)
)

func New(logger logging.Logger, priceOracleAddress common.Address, transactionService transaction.Service, timeDivisor int64) Service {
return &service{
logger: logger,
priceOracleAddress: priceOracleAddress,
transactionService: transactionService,
exchangeRate: big.NewInt(0),
deduction: nil,
quitC: make(chan struct{}),
timeDivisor: timeDivisor,
}
}

func (s *service) Start() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer cancel()
<-s.quitC
}()

go func() {
defer cancel()
for {
exchangeRate, deduction, err := s.getPrice(ctx)
if err != nil {
s.logger.Errorf("could not get price: %v", err)
}

s.exchangeRate = exchangeRate
s.deduction = deduction

ts := time.Now().Unix()

timeUntilNextPoll := time.Duration(s.timeDivisor-ts%s.timeDivisor) * time.Second

s.logger.Tracef("updated exchange rate to %d and deduction to %d", exchangeRate, deduction)

select {
case <-s.quitC:
return
case <-time.After(timeUntilNextPoll):
}
}
}()
}

func (s *service) GetPrice(ctx context.Context) (*big.Int, *big.Int, error) {
return s.getPrice(ctx)
}

func (s *service) getPrice(ctx context.Context) (*big.Int, *big.Int, error) {
callData, err := priceOracleABI.Pack("getPrice")
if err != nil {
return nil, nil, err
}
result, err := s.transactionService.Call(ctx, &transaction.TxRequest{
To: &s.priceOracleAddress,
Data: callData,
})
if err != nil {
return nil, nil, err
}

results, err := priceOracleABI.Unpack("getPrice", result)
if err != nil {
return nil, nil, err
}

if len(results) != 2 {
return nil, nil, errDecodeABI
}

exchangeRate, ok := abi.ConvertType(results[0], new(big.Int)).(*big.Int)
if !ok || exchangeRate == nil {
return nil, nil, errDecodeABI
}

deduction, ok := abi.ConvertType(results[1], new(big.Int)).(*big.Int)
if !ok || deduction == nil {
return nil, nil, errDecodeABI
}

return exchangeRate, deduction, nil
}

func (s *service) CurrentRates() (exchangeRate *big.Int, deduction *big.Int, err error) {
if s.exchangeRate.Cmp(big.NewInt(0)) == 0 {
return nil, nil, errors.New("exchange rate not yet available")
}
if s.deduction == nil {
return nil, nil, errors.New("deduction amount not yet available")
}
return s.exchangeRate, s.deduction, nil
}

func (s *service) Close() error {
close(s.quitC)
return nil
}

// DiscoverPriceOracleAddress returns the canonical price oracle for this chainID
func DiscoverPriceOracleAddress(chainID int64) (priceOracleAddress common.Address, found bool) {
if chainID == 5 {
// goerli
return goerliContractAddress, true
}
return common.Address{}, false
}
61 changes: 61 additions & 0 deletions pkg/settlement/swap/priceoracle/priceoracle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package priceoracle_test

import (
"context"
"io/ioutil"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/settlement/swap/priceoracle"
"github.com/ethersphere/bee/pkg/transaction"
transactionmock "github.com/ethersphere/bee/pkg/transaction/mock"
"github.com/ethersphere/go-price-oracle-abi/priceoracleabi"
)

var (
priceOracleABI = transaction.ParseABIUnchecked(priceoracleabi.PriceOracleABIv0_1_0)
)

func TestExchangeGetPrice(t *testing.T) {
priceOracleAddress := common.HexToAddress("0xabcd")

expectedPrice := big.NewInt(100)
expectedDeduce := big.NewInt(200)

result := make([]byte, 64)
expectedPrice.FillBytes(result[0:32])
expectedDeduce.FillBytes(result[32:64])

ex := priceoracle.New(
logging.New(ioutil.Discard, 0),
priceOracleAddress,
transactionmock.New(
transactionmock.WithABICall(
&priceOracleABI,
priceOracleAddress,
result,
"getPrice",
),
),
1,
)

price, deduce, err := ex.GetPrice(context.Background())
if err != nil {
t.Fatal(err)
}

if expectedPrice.Cmp(price) != 0 {
t.Fatalf("got wrong price. wanted %d, got %d", expectedPrice, price)
}

if expectedDeduce.Cmp(deduce) != 0 {
t.Fatalf("got wrong deduce. wanted %d, got %d", expectedDeduce, deduce)
}
}

0 comments on commit ef69f8c

Please sign in to comment.