github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/pricing/uniswap.go (about)

     1  package pricing
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/articulate"
     8  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
     9  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/call"
    10  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc"
    11  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    12  )
    13  
    14  var (
    15  	daiAddress                = base.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f") // DAI
    16  	wethAddress               = base.HexToAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2") // WETH
    17  	uniswapFactoryV2          = base.HexToAddress("0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f")
    18  	uniswapFactoryV2_deployed = base.Blknum(10000835) // why query for this immutable value each time we need it?
    19  )
    20  
    21  // priceUsdUniswap returns the price of the given asset in USD as of the given block number.
    22  func priceUsdUniswap(conn *rpc.Connection, statement *types.Statement) (price base.Float, source string, err error) {
    23  	multiplier := base.Float(1.0)
    24  	var first base.Address
    25  	var second base.Address
    26  	if statement.IsEth() {
    27  		first = daiAddress
    28  		second = wethAddress
    29  
    30  	} else {
    31  		temp := *statement
    32  		temp.AssetAddr = base.FAKE_ETH_ADDRESS
    33  		temp.AssetSymbol = "WEI"
    34  		multiplier, _, err = priceUsdUniswap(conn, &temp)
    35  		if err != nil {
    36  			return 0.0, "not-priced", err
    37  		}
    38  		first = wethAddress
    39  		second = statement.AssetAddr
    40  	}
    41  
    42  	reversed := false
    43  	if first.Hex() > second.Hex() {
    44  		save := second
    45  		second = first
    46  		first = save
    47  		reversed = true
    48  	}
    49  
    50  	theCall1 := fmt.Sprintf("getPair(%s, %s)", first.Hex(), second.Hex())
    51  	contractCall, _, err := call.NewContractCall(conn, uniswapFactoryV2, theCall1)
    52  	if err != nil {
    53  		wrapped := fmt.Errorf("the --call value provided (%s) was not found: %s", theCall1, err)
    54  		return 0.0, "not-priced", wrapped
    55  	}
    56  	contractCall.BlockNumber = statement.BlockNumber
    57  
    58  	artFunc := func(str string, function *types.Function) error {
    59  		return articulate.ArticulateFunction(function, "", str[2:])
    60  	}
    61  	result, err := contractCall.Call(artFunc)
    62  	if err != nil {
    63  		return 0.0, "not-priced", err
    64  	}
    65  	pairAddress := base.HexToAddress(result.Values["val_0"])
    66  	if pairAddress.IsZero() {
    67  		msg := fmt.Sprintf("no pair found for %s and %s", first.Hex(), second.Hex())
    68  		return 0.0, "not-priced", errors.New(msg)
    69  	}
    70  	theCall2 := "getReserves()"
    71  	contractCall, _, err = call.NewContractCall(conn, pairAddress, theCall2)
    72  	if err != nil {
    73  		wrapped := fmt.Errorf("the --call value provided (%s) was not found: %s", theCall2, err)
    74  		return 0.0, "not-priced", wrapped
    75  	}
    76  	contractCall.BlockNumber = statement.BlockNumber
    77  	result, err = contractCall.Call(artFunc)
    78  	if err != nil {
    79  		return 0.0, "not-priced", err
    80  	}
    81  	reserve0 := new(base.Ether)
    82  	if result.Values != nil && (result.Values["_reserve0"] == "" || result.Values["_reserve0"] == "0") {
    83  		reserve0.SetString("1")
    84  	} else {
    85  		reserve0.SetString(result.Values["_reserve0"])
    86  	}
    87  	reserve1 := new(base.Ether)
    88  	if result.Values != nil && (result.Values["_reserve1"] == "" || result.Values["_reserve1"] == "0") {
    89  		reserve1.SetString("1")
    90  	} else {
    91  		reserve1.SetString(result.Values["_reserve1"])
    92  	}
    93  	bigPrice := new(base.Ether)
    94  	bigPrice = bigPrice.Quo(reserve0, reserve1)
    95  
    96  	price = base.Float(bigPrice.Float64())
    97  	price *= multiplier
    98  	source = "uniswap"
    99  
   100  	r := priceDebugger{
   101  		address:     statement.AssetAddr,
   102  		symbol:      statement.AssetSymbol,
   103  		blockNumber: statement.BlockNumber,
   104  		source1:     uniswapFactoryV2,
   105  		theCall1:    theCall1,
   106  		source2:     pairAddress,
   107  		theCall2:    theCall2,
   108  		first:       first,
   109  		second:      second,
   110  		reversed:    reversed,
   111  		float0:      reserve0,
   112  		float1:      reserve1,
   113  		float2:      new(base.Ether).SetFloat64(float64(multiplier)),
   114  		bigPrice:    bigPrice,
   115  		price:       price,
   116  		source:      source,
   117  	}
   118  	r.report("using Uniswap")
   119  
   120  	return price, source, nil
   121  }