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 }