decred.org/dcrdex@v1.0.5/dex/networks/eth/params.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package eth
     5  
     6  import (
     7  	"encoding/binary"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"math"
    12  	"math/big"
    13  	"os"
    14  	"time"
    15  
    16  	"decred.org/dcrdex/dex"
    17  	v0 "decred.org/dcrdex/dex/networks/eth/contracts/v0"
    18  	"github.com/ethereum/go-ethereum/common"
    19  	"github.com/ethereum/go-ethereum/core"
    20  )
    21  
    22  const (
    23  	// GweiFactor is the amount of wei in one gwei.
    24  	GweiFactor = 1e9
    25  	// MaxBlockInterval is the number of seconds since the last header came
    26  	// in over which we consider the chain to be out of sync.
    27  	MaxBlockInterval = 180
    28  	EthBipID         = 60
    29  	MinGasTipCap     = 2 //gwei
    30  )
    31  
    32  // These are the chain IDs of the various Ethereum network supported.
    33  const (
    34  	MainnetChainID = 1
    35  	TestnetChainID = 5  // Görli
    36  	SimnetChainID  = 42 // see dex/testing/eth/harness.sh
    37  )
    38  
    39  var (
    40  	// ChainIDs is a map of the network name to it's chain ID.
    41  	ChainIDs = map[dex.Network]int64{
    42  		dex.Mainnet: MainnetChainID,
    43  		dex.Testnet: TestnetChainID,
    44  		dex.Simnet:  SimnetChainID,
    45  	}
    46  
    47  	UnitInfo = dex.UnitInfo{
    48  		AtomicUnit: "gwei",
    49  		Conventional: dex.Denomination{
    50  			Unit:             "ETH",
    51  			ConversionFactor: 1e9,
    52  		},
    53  		Alternatives: []dex.Denomination{
    54  			{
    55  				Unit:             "Szabos",
    56  				ConversionFactor: 1e6,
    57  			},
    58  			{
    59  				Unit:             "Finneys",
    60  				ConversionFactor: 1e3,
    61  			},
    62  		},
    63  		FeeRateDenom: "gas",
    64  	}
    65  
    66  	VersionedGases = map[uint32]*Gases{
    67  		0: v0Gases,
    68  	}
    69  
    70  	ContractAddresses = map[uint32]map[dex.Network]common.Address{
    71  		0: {
    72  			dex.Mainnet: common.HexToAddress("0x8C17e4968B6903E1601be82Ca989c5B5E2c7b400"),
    73  			dex.Testnet: common.HexToAddress("0x73bc803A2604b2c58B8680c3CE1b14489842EF16"), // tx 0xb24b44beebc0e34fa57bd9f08f9aaf70f40c654f3ddbe0b15dd942ee23ce02f4
    74  			dex.Simnet:  common.HexToAddress("0x2f68e723b8989ba1c6a9f03e42f33cb7dc9d606f"),
    75  		},
    76  	}
    77  
    78  	MultiBalanceAddresses = map[dex.Network]common.Address{
    79  		dex.Mainnet: common.HexToAddress("0x73bc803A2604b2c58B8680c3CE1b14489842EF16"), // tx 0xaf6cb861578c0ded0750397d7e044a7dd86c94aa47211d02188e146a2424dda4
    80  		dex.Testnet: common.HexToAddress("0x8Bd6F6dBe69588D94953EE289Fd3E1db3e8dB43D"), // tx 0x46a416344927a8d1f33865374e9b9e824249980da8d34f6c3214a1ee036ca5fe
    81  	}
    82  )
    83  
    84  var v0Gases = &Gases{
    85  	Swap:      174500, // 134,500 actual -- https://goerli.etherscan.io/tx/0xa17b6edeaf79791b5fc9232dc05a56d43f3a67845f3248e763b77162fae9b181, verified on mainnet
    86  	SwapAdd:   146400, // 112,600 actual (247,100 for 2) -- https://goerli.etherscan.io/tx/0xa4fc65b8001bf8c44f1079b3d97adf42eb1097658e360b9033596253b0cbbd04, verified on mainnet
    87  	Redeem:    78600,  // 60,456 actual -- https://goerli.etherscan.io/tx/0x5b22c48052df4a8ecd03a31b62e5015e6afe18c9ffb05e6cdd77396dfc3ca917, verified on mainnet
    88  	RedeemAdd: 41000,  // 31,672 actual (92,083 for 2, 123,724 for 3) -- https://goerli.etherscan.io/tx/0xae424cc9b0d43bf934112245cb74ab9eca9c2611eabcd6257b6ec258b071c1e6, https://goerli.etherscan.io/tx/0x7ba7cb945da108d39a5a0ac580d4841c4017a32cd0e244f26845c6ed501d2475, verified on mainnet
    89  	Refund:    57000,  // 43,014 actual -- https://goerli.etherscan.io/tx/0x586ed4cb7dab043f98d4cc08930d9eb291b0052d140d949b20232ceb6ad15f25
    90  }
    91  
    92  // LoadGenesisFile loads a Genesis config from a json file.
    93  func LoadGenesisFile(genesisFile string) (*core.Genesis, error) {
    94  	fid, err := os.Open(genesisFile)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	defer fid.Close()
    99  
   100  	var genesis core.Genesis
   101  	err = json.NewDecoder(fid).Decode(&genesis)
   102  	if err != nil {
   103  		return nil, fmt.Errorf("unable to unmarshal simnet genesis: %v", err)
   104  	}
   105  	return &genesis, nil
   106  }
   107  
   108  // EncodeContractData packs the contract version and the secret hash into a byte
   109  // slice for communicating a swap's identity.
   110  func EncodeContractData(contractVersion uint32, swapKey [SecretHashSize]byte) []byte {
   111  	b := make([]byte, SecretHashSize+4)
   112  	binary.BigEndian.PutUint32(b[:4], contractVersion)
   113  	copy(b[4:], swapKey[:])
   114  	return b
   115  }
   116  
   117  // DecodeContractData unpacks the contract version and secret hash.
   118  func DecodeContractData(data []byte) (contractVersion uint32, swapKey [SecretHashSize]byte, err error) {
   119  	if len(data) != SecretHashSize+4 {
   120  		err = errors.New("invalid swap data")
   121  		return
   122  	}
   123  	contractVersion = binary.BigEndian.Uint32(data[:4])
   124  	copy(swapKey[:], data[4:])
   125  	return
   126  }
   127  
   128  // InitGas calculates the gas required for a batch of n inits.
   129  func InitGas(n int, contractVer uint32) uint64 {
   130  	if n == 0 {
   131  		return 0
   132  	}
   133  	g, ok := VersionedGases[contractVer]
   134  	if !ok {
   135  		return math.MaxUint64
   136  	}
   137  	return g.SwapN(n)
   138  }
   139  
   140  // RedeemGas calculates the gas required for a batch of n redemptions.
   141  func RedeemGas(n int, contractVer uint32) uint64 {
   142  	if n == 0 {
   143  		return 0
   144  	}
   145  	g, ok := VersionedGases[contractVer]
   146  	if !ok {
   147  		return math.MaxUint64
   148  	}
   149  	return g.RedeemN(n)
   150  }
   151  
   152  // RefundGas calculates the gas required for a refund.
   153  func RefundGas(contractVer uint32) uint64 {
   154  	g, ok := VersionedGases[contractVer]
   155  	if !ok {
   156  		return math.MaxUint64
   157  	}
   158  	return g.Refund
   159  }
   160  
   161  var (
   162  	// add before diving by gweiFactorBig to take the ceiling.
   163  	gweiCeilAddend = big.NewInt(GweiFactor - 1)
   164  	gweiFactorBig  = big.NewInt(GweiFactor)
   165  )
   166  
   167  // GweiToWei converts uint64 Gwei to *big.Int Wei.
   168  func GweiToWei(v uint64) *big.Int {
   169  	return new(big.Int).Mul(big.NewInt(int64(v)), gweiFactorBig)
   170  }
   171  
   172  // WeiToGweiFloor converts *big.Int Wei to uint64 Gwei. If v is determined to be
   173  // unsuitable for a uint64, zero is returned. For values that are not even
   174  // multiples of 1 gwei, this function returns the floor.
   175  func WeiToGwei(v *big.Int) uint64 {
   176  	vGwei := new(big.Int).Div(v, gweiFactorBig)
   177  	if vGwei.IsUint64() {
   178  		return vGwei.Uint64()
   179  	}
   180  	return 0
   181  }
   182  
   183  // WeiToGweiCeil converts *big.Int Wei to uint64 Gwei. If v is determined to be
   184  // unsuitable for a uint64, zero is returned. For values that are not even
   185  // multiples of 1 gwei, this function returns the ceiling. In general,
   186  // WeiToWeiCeil should be used with gwei-unit fee rates are generated or
   187  // validated and WeiToGwei should be used with balances and values.
   188  func WeiToGweiCeil(v *big.Int) uint64 {
   189  	vGwei := new(big.Int).Div(new(big.Int).Add(v, gweiCeilAddend), big.NewInt(GweiFactor))
   190  	if vGwei.IsUint64() {
   191  		return vGwei.Uint64()
   192  	}
   193  	return 0
   194  }
   195  
   196  // WeiToGweiSafe converts a *big.Int in wei (1e18 unit) to gwei (1e9 unit) as
   197  // a uint64. Errors if the amount of gwei is too big to fit fully into a uint64.
   198  // For values that are not even multiples of 1 gwei, this function returns the
   199  // ceiling. As such, WeiToGweiSafe is more suitable for validating or generating
   200  // fee rates. If balance or value validation is the goal, use truncation e.g.
   201  // WeiToGwei.
   202  func WeiToGweiSafe(wei *big.Int) (uint64, error) {
   203  	if wei.Cmp(new(big.Int)) == -1 {
   204  		return 0, fmt.Errorf("wei must be non-negative")
   205  	}
   206  	gwei := new(big.Int).Div(new(big.Int).Add(wei, gweiCeilAddend), gweiFactorBig)
   207  	if !gwei.IsUint64() {
   208  		return 0, fmt.Errorf("%v gwei is too big for a uint64", gwei)
   209  	}
   210  	return gwei.Uint64(), nil
   211  }
   212  
   213  // SwapStep is the state of a swap and corresponds to values in the Solidity
   214  // swap contract.
   215  type SwapStep uint8
   216  
   217  // Swap states represent the status of a swap. The default state of a swap is
   218  // SSNone. A swap in status SSNone does not exist. SSInitiated indicates that a
   219  // party has initiated the swap and funds have been sent to the contract.
   220  // SSRedeemed indicates a successful swap where the participant was able to
   221  // redeem with the secret hash. SSRefunded indicates a failed swap, where the
   222  // initiating party refunded their coins after the locktime passed. A swap no
   223  // longer changes states after reaching SSRedeemed or SSRefunded.
   224  const (
   225  	// SSNone indicates that the swap is not initiated. This is the default
   226  	// state of a swap.
   227  	SSNone SwapStep = iota
   228  	// SSInitiated indicates that the swap has been initiated.
   229  	SSInitiated
   230  	// SSRedeemed indicates that the swap was initiated and then redeemed.
   231  	// This is one of two possible end states of a swap.
   232  	SSRedeemed
   233  	// SSRefunded indicates that the swap was initiated and then refunded.
   234  	// This is one of two possible end states of a swap.
   235  	SSRefunded
   236  )
   237  
   238  // String satisfies the Stringer interface.
   239  func (ss SwapStep) String() string {
   240  	switch ss {
   241  	case SSNone:
   242  		return "none"
   243  	case SSInitiated:
   244  		return "initiated"
   245  	case SSRedeemed:
   246  		return "redeemed"
   247  	case SSRefunded:
   248  		return "refunded"
   249  	}
   250  	return "unknown"
   251  }
   252  
   253  // SwapState is the current state of an in-process swap.
   254  type SwapState struct {
   255  	BlockHeight uint64
   256  	LockTime    time.Time
   257  	Secret      [32]byte
   258  	Initiator   common.Address
   259  	Participant common.Address
   260  	Value       *big.Int
   261  	State       SwapStep
   262  }
   263  
   264  // SwapStateFromV0 converts a version 0 contract *ETHSwapSwap to the generalized
   265  // *SwapState type.
   266  func SwapStateFromV0(state *v0.ETHSwapSwap) *SwapState {
   267  	return &SwapState{
   268  		BlockHeight: state.InitBlockNumber.Uint64(),
   269  		LockTime:    time.Unix(state.RefundBlockTimestamp.Int64(), 0),
   270  		Secret:      state.Secret,
   271  		Initiator:   state.Initiator,
   272  		Participant: state.Participant,
   273  		Value:       state.Value,
   274  		State:       SwapStep(state.State),
   275  	}
   276  }
   277  
   278  // Initiation is the data used to initiate a swap.
   279  type Initiation struct {
   280  	LockTime    time.Time
   281  	SecretHash  [32]byte
   282  	Participant common.Address
   283  	Value       *big.Int
   284  }
   285  
   286  // Redemption is the data used to redeem a swap.
   287  type Redemption struct {
   288  	Secret     [32]byte
   289  	SecretHash [32]byte
   290  }
   291  
   292  var (
   293  	usdcTokenID, _  = dex.BipSymbolID("usdc.eth")
   294  	usdtTokenID, _  = dex.BipSymbolID("usdt.eth")
   295  	maticTokenID, _ = dex.BipSymbolID("matic.eth") // old matic 0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0
   296  )
   297  
   298  // Gases lists the expected gas required for various DEX and wallet operations.
   299  type Gases struct {
   300  	// Approve is the amount of gas needed to approve the swap contract for
   301  	// transferring tokens. The first approval for an address uses more gas than
   302  	// subsequent approvals for the same address.
   303  	Approve uint64 `json:"approve"`
   304  	// Transfer is the amount of gas needed to transfer tokens. The first
   305  	// transfer to an address uses more gas than subsequent transfers to the
   306  	// same address.
   307  	Transfer uint64 `json:"transfer"`
   308  	// Swap is the amount of gas needed to initialize a single ethereum swap.
   309  	Swap uint64 `json:"swap"`
   310  	// SwapAdd is the amount of gas needed to initialize additional swaps in
   311  	// the same transaction.
   312  	SwapAdd uint64 `json:"swapAdd"`
   313  	// Redeem is the amount of gas it costs to redeem a swap.
   314  	Redeem uint64 `json:"redeem"`
   315  	// RedeemAdd is the amount of gas needed to redeem additional swaps in the
   316  	// same transaction.
   317  	RedeemAdd uint64 `json:"redeemAdd"`
   318  	// Refund is the amount of gas needed to refund a swap.
   319  	Refund uint64 `json:"refund"`
   320  }
   321  
   322  // SwapN calculates the gas needed to initiate n swaps.
   323  func (g *Gases) SwapN(n int) uint64 {
   324  	if n <= 0 {
   325  		return 0
   326  	}
   327  	return g.Swap + g.SwapAdd*(uint64(n)-1)
   328  }
   329  
   330  // RedeemN calculates the gas needed to redeem n swaps.
   331  func (g *Gases) RedeemN(n int) uint64 {
   332  	if n <= 0 {
   333  		return 0
   334  	}
   335  	return g.Redeem + g.RedeemAdd*(uint64(n)-1)
   336  }