diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index 4dddf38fa7..4fef299410 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -59,7 +59,7 @@ func init() { asset.Register(BipID, &Driver{}) // Test token registerToken(simnetTokenID, "A token wallet for the DEX test token. Used for testing DEX software.", dex.Simnet) - registerToken(usdcTokenID, "The USDC Ethereum ERC20 token.", dex.Testnet) + registerToken(usdcTokenID, "The USDC Ethereum ERC20 token.", dex.Mainnet, dex.Testnet) } const ( @@ -455,11 +455,11 @@ type baseWallet struct { // assetWallet satisfies the dex.Wallet interface. type assetWallet struct { *baseWallet - assetID uint32 - tipChange func(error) - log dex.Logger - atomicUnit string - connected atomic.Bool + assetID uint32 + tipChange func(error) + log dex.Logger + ui dex.UnitInfo + connected atomic.Bool lockedFunds struct { mtx sync.RWMutex @@ -684,11 +684,14 @@ func NewWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network) contractors: make(map[uint32]contractor), evmify: dexeth.GweiToWei, atomize: dexeth.WeiToGwei, - atomicUnit: dexeth.UnitInfo.AtomicUnit, + ui: dexeth.UnitInfo, maxSwapsInTx: perTxGasLimit / maxSwapGas, maxRedeemsInTx: perTxGasLimit / maxRedeemGas, } + logger.Infof("ETH wallet will support a maximum of %d swaps and %d redeems per transaction.", + aw.maxSwapsInTx, aw.maxRedeemsInTx) + aw.wallets = map[uint32]*assetWallet{ BipID: aw, } @@ -985,7 +988,7 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet, contractors: make(map[uint32]contractor), evmify: token.AtomicToEVM, atomize: token.EVMToAtomic, - atomicUnit: token.UnitInfo.AtomicUnit, + ui: token.UnitInfo, maxSwapsInTx: perTxGasLimit / maxSwapGas, maxRedeemsInTx: perTxGasLimit / maxRedeemGas, } @@ -1016,6 +1019,10 @@ func (eth *baseWallet) OwnsDepositAddress(address string) (bool, error) { return addr == eth.addr, nil } +func (w *assetWallet) amtString(amt uint64) string { + return fmt.Sprintf("%s %s", w.ui.ConventionalString(amt), w.ui.Conventional.Unit) +} + // fundReserveType represents the various uses for which funds need to be locked: // initiations, redemptions, and refunds. type fundReserveType uint32 @@ -1063,7 +1070,7 @@ func (w *assetWallet) lockFunds(amt uint64, t fundReserveType) error { if balance.Available < amt { return fmt.Errorf("attempting to lock more %s for %s than is currently available. %d > %d %s", - dex.BipIDSymbol(w.assetID), t, amt, balance.Available, w.atomicUnit) + dex.BipIDSymbol(w.assetID), t, amt, balance.Available, w.ui.AtomicUnit) } w.lockedFunds.mtx.Lock() @@ -1387,6 +1394,8 @@ func (w *ETHWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, error // some work for the caller as well. We can't just always do it that way and // remove RedeemN, since we can't guarantee that the redemption asset is in // our fee-family. though it could still be an AccountRedeemer. + w.log.Debugf("Locking %s to swap %s in up to %d swaps at a fee rate of %d gwei/gas using up to %d gas per swap", + w.amtString(ethToLock), w.amtString(ord.Value), ord.MaxSwapCount, ord.MaxFeeRate, g.Swap) coin := w.createFundingCoin(ethToLock) @@ -1428,6 +1437,8 @@ func (w *TokenWallet) FundOrder(ord *asset.Order) (asset.Coins, []dex.Bytes, err } }() + w.log.Debugf("Locking %s to swap %s in up to %d swaps at a fee rate of %d gwei/gas using up to %d gas per swap", + w.parent.amtString(ethToLock), w.amtString(ord.Value), ord.MaxSwapCount, ord.MaxFeeRate, g.Swap) if err := w.parent.lockFunds(ethToLock, initiationReserve); err != nil { return nil, nil, err } @@ -1554,7 +1565,7 @@ func (w *assetWallet) swapGas(n int, ver uint32) (oneSwap, nSwap uint64, approve } } if gasEst > nSwap { - w.log.Warnf("Swap gas estimate %d is greater than the server's configured value %d. Using live estimate + 10%.", gasEst, nSwap) + w.log.Warnf("Swap gas estimate %d is greater than the server's configured value %d. Using live estimate + 10%%.", gasEst, nSwap) nSwap = gasEst * 11 / 10 // 10% buffer if n == 1 && nSwap > oneSwap { oneSwap = nSwap @@ -2150,7 +2161,7 @@ func (w *TokenWallet) maybeApproveTokenSwapContract(ver uint32, maxFeeRate, swap return fmt.Errorf("parent balance %d doesn't cover contract approval (%d) and tx fees (%d)", ethBal.Available, approveGas*maxFeeRate, swapReserves) } - tx, err := w.approveToken(unlimitedAllowance, maxFeeRate, ver) + tx, err := w.approveToken(unlimitedAllowance, maxFeeRate, approveGas, ver) if err != nil { return fmt.Errorf("token contract approval error (using max fee rate %d): %w", maxFeeRate, err) } @@ -2178,10 +2189,10 @@ func (w *assetWallet) tokenAllowance() (allowance *big.Int, err error) { // approveToken approves the token swap contract to spend tokens on behalf of // account handled by the wallet. -func (w *assetWallet) approveToken(amount *big.Int, maxFeeRate uint64, contractVer uint32) (tx *types.Transaction, err error) { +func (w *assetWallet) approveToken(amount *big.Int, maxFeeRate, gasLimit uint64, contractVer uint32) (tx *types.Transaction, err error) { w.nonceSendMtx.Lock() defer w.nonceSendMtx.Unlock() - txOpts, err := w.node.txOpts(w.ctx, 0, approveGas, dexeth.GweiToWei(maxFeeRate), nil) + txOpts, err := w.node.txOpts(w.ctx, 0, gasLimit, dexeth.GweiToWei(maxFeeRate), nil) if err != nil { return nil, fmt.Errorf("addSignerToOpts error: %w", err) } @@ -2744,7 +2755,7 @@ func (w *TokenWallet) canSend(value uint64, isPreEstimate bool) (uint64, *big.In } avail := bal.Available if avail < value { - return 0, nil, fmt.Errorf("not enough tokens: have %[1]d %[3]s need %[2]d %[3]s", avail, value, w.atomicUnit) + return 0, nil, fmt.Errorf("not enough tokens: have %[1]d %[3]s need %[2]d %[3]s", avail, value, w.ui.AtomicUnit) } ethBal, err := w.parent.Balance() @@ -3417,7 +3428,7 @@ func (w *TokenWallet) ConfirmRedemption(coinID dex.Bytes, redemption *asset.Rede } const ( - txConfsNeededToConfirm = 10 + txConfsNeededToConfirm = 3 blocksToWaitBeforeCoinNotFound = 10 blocksToWaitBeforeCheckingIfReplaced = 10 ) diff --git a/client/asset/eth/eth_test.go b/client/asset/eth/eth_test.go index a56f81be89..5449d2d341 100644 --- a/client/asset/eth/eth_test.go +++ b/client/asset/eth/eth_test.go @@ -559,6 +559,7 @@ func newTestNode(assetID uint32) *testNode { approveTx: types.NewTransaction(4, common.Address{0x34}, big.NewInt(1e9), defaultGasFeeLimit, big.NewInt(2e9), nil), } if assetID != BipID { + ttc.tContractor.gasEstimates = &tokenGases c = ttc } @@ -3778,10 +3779,10 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { blockSubmitted: 9, }, }, - bestBlock: 13, + bestBlock: 11, // tx.height + txConfsNeededToConfirm - 2 expectedResult: &asset.ConfirmRedemptionStatus{ - Confs: 4, - Req: 10, + Confs: txConfsNeededToConfirm - 1, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(3, 200, redeem0Data), }, baseFee: dexeth.GweiToWei(100), @@ -3810,10 +3811,10 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { }, }, expectedMonitoredTxs: map[common.Hash]*monitoredTx{}, - bestBlock: 19, + bestBlock: 12, // tx.height + txConfsNeededToConfirm expectedResult: &asset.ConfirmRedemptionStatus{ - Confs: 10, - Req: 10, + Confs: txConfsNeededToConfirm, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(3, 200, redeem0Data), }, receipt: &types.Receipt{ @@ -3861,7 +3862,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 19, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(4, 123, redeem0Data), }, redeemTx: toEthTx(4, 123, redeem0Data), @@ -3927,14 +3928,14 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 13, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(4, 123, redeem0Data), }, redeemTx: toEthTx(4, 123, redeem0Data), baseFee: dexeth.GweiToWei(100), }, { - name: "not in monitored txs, found by geth, < 10 confirmations", + name: "not in monitored txs, found by geth, < 3 confirmations", coinID: toEthTxCoinID(3, 200, redeem0Data), redemption: assetRedemption(secretHashes[0], secrets[0]), getTxResMap: map[common.Hash]*txData{ @@ -3951,11 +3952,11 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { }, }, monitoredTxs: map[common.Hash]*monitoredTx{}, - bestBlock: 13, + bestBlock: 11, expectedResult: &asset.ConfirmRedemptionStatus{ - Confs: 4, - Req: 10, + Confs: 2, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(3, 200, redeem0Data), }, baseFee: dexeth.GweiToWei(100), @@ -3993,7 +3994,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 13, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(4, 123, redeem0and1Data), }, expectedResubmittedRedemptions: []*asset.Redemption{ @@ -4036,7 +4037,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 13, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(4, 123, redeem0Data), }, expectedResubmittedRedemptions: []*asset.Redemption{ @@ -4087,7 +4088,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 22, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 2, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(5, 200, redeem0Data), }, baseFee: dexeth.GweiToWei(100), @@ -4109,10 +4110,10 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { State: dexeth.SSRedeemed, }, }, - bestBlock: 13, + bestBlock: 3, // txConfsNeededToConfirm + tx.height + 1 expectedResult: &asset.ConfirmRedemptionStatus{ - Confs: 10, - Req: 10, + Confs: txConfsNeededToConfirm, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(3, 200, redeem0Data), }, baseFee: dexeth.GweiToWei(100), @@ -4154,7 +4155,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 13, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(4, 123, redeem0Data), }, expectedResubmittedRedemptions: []*asset.Redemption{ @@ -4201,7 +4202,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 13, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(4, 123, redeem0Data), }, expectedResubmittedRedemptions: []*asset.Redemption{ @@ -4243,7 +4244,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { bestBlock: 13, expectedResult: &asset.ConfirmRedemptionStatus{ Confs: 0, - Req: 10, + Req: txConfsNeededToConfirm, CoinID: toEthTxCoinID(3, 200, redeem0Data), }, expectSentSignedTransaction: toEthTx(3, 200, redeem0Data), @@ -4281,6 +4282,7 @@ func testConfirmRedemption(t *testing.T, assetID uint32) { } for _, test := range tests { + fmt.Printf("###### %s ###### \n", test.name) node.getTxResMap = make(map[common.Hash]*tGetTxRes) for hash, txData := range test.getTxResMap { node.getTxResMap[hash] = &tGetTxRes{ @@ -4601,18 +4603,18 @@ func testMaxSwapRedeemLots(t *testing.T, assetID uint32) { info := wallet.Info() if assetID == BipID { - if info.MaxSwapsInTx != 37 { - t.Fatalf("expected 37 for max swaps but got %d", info.MaxSwapsInTx) + if info.MaxSwapsInTx != 28 { + t.Fatalf("expected 28 for max swaps but got %d", info.MaxSwapsInTx) } - if info.MaxRedeemsInTx != 79 { - t.Fatalf("expected 119 for max redemptions but got %d", info.MaxRedeemsInTx) + if info.MaxRedeemsInTx != 63 { + t.Fatalf("expected 63 for max redemptions but got %d", info.MaxRedeemsInTx) } } else { if info.MaxSwapsInTx != 28 { - t.Fatalf("expected 43 for max swaps but got %d", info.MaxSwapsInTx) + t.Fatalf("expected 28 for max swaps but got %d", info.MaxSwapsInTx) } if info.MaxRedeemsInTx != 71 { - t.Fatalf("expected 107 for max redemptions but got %d", info.MaxRedeemsInTx) + t.Fatalf("expected 71 for max redemptions but got %d", info.MaxRedeemsInTx) } } } diff --git a/client/asset/eth/node.go b/client/asset/eth/node.go index 3b2ba4621e..2c538e33d9 100644 --- a/client/asset/eth/node.go +++ b/client/asset/eth/node.go @@ -158,9 +158,7 @@ func prepareNode(cfg *nodeConfig) (*node.Node, error) { case dex.Testnet: urls = params.GoerliBootnodes case dex.Mainnet: - // urls = params.MainnetBootnodes - // TODO: Allow. - return nil, fmt.Errorf("eth cannot be used on mainnet") + urls = params.MainnetBootnodes default: return nil, fmt.Errorf("unknown network ID: %d", uint8(cfg.net)) } @@ -210,9 +208,8 @@ func ethChainConfig(network dex.Network) (c ethconfig.Config, err error) { ethCfg.Genesis = core.DefaultGoerliGenesisBlock() ethCfg.NetworkId = params.GoerliChainConfig.ChainID.Uint64() case dex.Mainnet: - // urls = params.MainnetBootnodes - // TODO: Allow. - return c, fmt.Errorf("eth cannot be used on mainnet") + ethCfg.Genesis = core.DefaultGenesisBlock() + ethCfg.NetworkId = params.MainnetChainConfig.ChainID.Uint64() default: return c, fmt.Errorf("unknown network ID: %d", uint8(network)) } diff --git a/client/asset/eth/nodeclient.go b/client/asset/eth/nodeclient.go index 0b21950d10..2cfa64a059 100644 --- a/client/asset/eth/nodeclient.go +++ b/client/asset/eth/nodeclient.go @@ -29,10 +29,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" ) -const ( - contractVersionNewest = ^uint32(0) - approveGas = 4e5 -) +const contractVersionNewest = ^uint32(0) var ( // https://github.com/ethereum/go-ethereum/blob/16341e05636fd088aa04a27fca6dc5cda5dbab8f/eth/backend.go#L110-L113 diff --git a/client/core/bookie.go b/client/core/bookie.go index 6d28988e84..b21dd73f07 100644 --- a/client/core/bookie.go +++ b/client/core/bookie.go @@ -984,10 +984,7 @@ func handlePriceUpdateNote(c *Core, dc *dexConnection, msg *msgjson.Message) err } mktName, err := dex.MarketName(spot.BaseID, spot.QuoteID) if err != nil { - // It's possible for the dex server to have a market with coin - // ID's we do not know, especially in the case of eth tokens. - c.log.Debugf("error parsing market for base = %d, quote = %d: %v", spot.BaseID, spot.QuoteID, err) - return nil + return nil // it's just an asset we don't support, don't insert, but don't spam } dc.spotsMtx.Lock() dc.spots[mktName] = spot diff --git a/dex/networks/eth/params.go b/dex/networks/eth/params.go index 9fa31011f2..c64f5034bb 100644 --- a/dex/networks/eth/params.go +++ b/dex/networks/eth/params.go @@ -44,7 +44,7 @@ var ( ContractAddresses = map[uint32]map[dex.Network]common.Address{ 0: { - dex.Mainnet: common.Address{}, + dex.Mainnet: common.HexToAddress("0x8C17e4968B6903E1601be82Ca989c5B5E2c7b400"), dex.Simnet: common.HexToAddress("0x2f68e723b8989ba1c6a9f03e42f33cb7dc9d606f"), dex.Testnet: common.HexToAddress("0x198463496037754564e9bea5418Bf4117Db0520C"), }, @@ -52,11 +52,11 @@ var ( ) var v0Gases = &Gases{ - Swap: 135000, - SwapAdd: 113000, - Redeem: 63000, - RedeemAdd: 32000, - Refund: 43000, + Swap: 174500, // 134,500 actual -- https://goerli.etherscan.io/tx/0xa17b6edeaf79791b5fc9232dc05a56d43f3a67845f3248e763b77162fae9b181, verified on mainnet + SwapAdd: 146400, // 112,600 actual (247,100 for 2) -- https://goerli.etherscan.io/tx/0xa4fc65b8001bf8c44f1079b3d97adf42eb1097658e360b9033596253b0cbbd04, verified on mainnet + Redeem: 78600, // 60,456 actual -- https://goerli.etherscan.io/tx/0x5b22c48052df4a8ecd03a31b62e5015e6afe18c9ffb05e6cdd77396dfc3ca917, verified on mainnet + 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 + Refund: 57000, // 43,014 actual -- https://goerli.etherscan.io/tx/0x586ed4cb7dab043f98d4cc08930d9eb291b0052d140d949b20232ceb6ad15f25 } // LoadGenesisFile loads a Genesis config from a json file. @@ -240,9 +240,6 @@ var testTokenID, _ = dex.BipSymbolID("dextt.eth") var usdcTokenID, _ = dex.BipSymbolID("usdc.eth") // Gases lists the expected gas required for various DEX and wallet operations. -// ★★ IMPORTANT ★★ By policy, clients should allow servers to adjust Swap and -// Redeem gas values in their *dex.Asset up to but not over 2x the value listed -// in VersionedGases or Tokens. type Gases struct { // Approve is the amount of gas needed to approve the swap contract for // transferring tokens. diff --git a/dex/networks/eth/tokens.go b/dex/networks/eth/tokens.go index 4dbec1eaac..e499107dea 100644 --- a/dex/networks/eth/tokens.go +++ b/dex/networks/eth/tokens.go @@ -85,11 +85,11 @@ var Tokens = map[uint32]*Token{ }, }, NetTokens: map[dex.Network]*NetToken{ - dex.Mainnet: { + dex.Mainnet: { // no dextt on mainnet Address: common.Address{}, SwapContracts: map[uint32]*SwapContract{}, }, - dex.Testnet: { + dex.Testnet: { // no dextt on goerli Address: common.Address{}, SwapContracts: map[uint32]*SwapContract{}, }, @@ -159,8 +159,24 @@ var Tokens = map[uint32]*Token{ }, NetTokens: map[dex.Network]*NetToken{ dex.Mainnet: { - Address: common.Address{}, - SwapContracts: map[uint32]*SwapContract{}, + Address: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), // https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + SwapContracts: map[uint32]*SwapContract{ + 0: { // https://etherscan.io/address/0x1bbd020ddd6dc01f974aa74d2d727b2a6782f32d#code + Address: common.HexToAddress("0x1bbd020DDD6dc01f974Aa74D2D727B2A6782F32D"), + // USDC's contract is upgradable, using a proxy call, so + // gas cost could change without notice, so we do not + // want to set limits too low, even with live estimates. + Gas: Gases{ + Swap: 242_000, + SwapAdd: 146_400, + Redeem: 102_700, + RedeemAdd: 31_600, + Refund: 77_000, + Approve: 78_400, + Transfer: 85_100, + }, + }, + }, }, dex.Testnet: { Address: common.HexToAddress("0x07865c6e87b9f70255377e024ace6630c1eaa37f"), @@ -197,18 +213,20 @@ var Tokens = map[uint32]*Token{ // map. // Average of 5 transfers: 51820 // [65500 48400 48400 48400 48400] - Swap: 175_000, - SwapAdd: 115_000, - Redeem: 87_000, - RedeemAdd: 33_000, - Refund: 67_000, - Approve: 65_000, - Transfer: 70_000, + // + // Then buffered by about 30%... + Swap: 242_000, // actual ~187,880 -- https://goerli.etherscan.io/tx/0x352baccafa96bb09d5c118f8dcce26e34267beb8bcda9c026f8d5353abea50fd, verified on mainnet at 188,013 gas + SwapAdd: 146_400, // actual ~112,639 (300,519 for 2) -- https://goerli.etherscan.io/tx/0x97f9a1ed69883a6e701f37883ef74d79a709e0edfc4a45987fa659700663f40e + Redeem: 109_000, // actual ~83,850 (initial receive, subsequent ~79,012) -- https://goerli.etherscan.io/tx/0x96f007036b01eb2e44615dc67d3e99748bc133496187348b2af26834f46bfdc8, verified on mainnet at 79,113 gas for subsequent + RedeemAdd: 31_600, // actual ~31,641 (110,653 for 2) -- https://goerli.etherscan.io/tx/0xcf717512796868273ed93c37fa139973c9b8305a736c4a3b50ac9f35ae747f99 + Refund: 77_000, // actual ~59,152 -- https://goerli.etherscan.io/tx/0xc5692ad0e6d86b721af75ff3b4b7c2e17d939918db030ebf5444ccf840c7a90b + Approve: 78_400, // actual ~60,190 (initial) -- https://goerli.etherscan.io/tx/0xd695fd174dede7bb798488ead7fed5ef33bcd79932b0fa35db0d17c84c97a8a1, verified on mainnet at 60,311 + Transfer: 85_100, // actual ~65,524 (initial receive, subsequent 48,424) }, }, }, }, - dex.Simnet: { + dex.Simnet: { // no usdc on simnet, dextt instead Address: common.Address{}, SwapContracts: map[uint32]*SwapContract{}, }, diff --git a/server/asset/eth/eth.go b/server/asset/eth/eth.go index 3ff1f9e580..5e63cba37b 100644 --- a/server/asset/eth/eth.go +++ b/server/asset/eth/eth.go @@ -22,7 +22,6 @@ import ( "decred.org/dcrdex/server/asset" "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -82,15 +81,19 @@ func init() { registerToken(testTokenID, 0) registerToken(usdcID, 0) + + if blockPollIntervalStr != "" { + blockPollInterval, _ = time.ParseDuration(blockPollIntervalStr) + if blockPollInterval < time.Second { + panic(fmt.Sprintf("invalid value for blockPollIntervalStr: %q", blockPollIntervalStr)) + } + } } const ( BipID = 60 ethContractVersion = 0 version = 0 - // The blockPollInterval is the delay between calls to bestBlockHash to - // check for new blocks. - blockPollInterval = time.Second ) var ( @@ -103,6 +106,12 @@ var ( testTokenID, _ = dex.BipSymbolID("dextt.eth") usdcID, _ = dex.BipSymbolID("usdc.eth") + + // blockPollInterval is the delay between calls to bestBlockHash to check + // for new blocks. Modify at compile time via blockPollIntervalStr: + // go build -tags lgpl -ldflags "-X 'decred.org/dcrdex/server/asset/eth.blockPollIntervalStr=10s'" + blockPollInterval = time.Second + blockPollIntervalStr string ) type driverBase struct { @@ -163,7 +172,6 @@ type ethFetcher interface { headerByHeight(ctx context.Context, height uint64) (*types.Header, error) connect(ctx context.Context) error suggestGasTipCap(ctx context.Context) (*big.Int, error) - syncProgress(ctx context.Context) (*ethereum.SyncProgress, error) transaction(ctx context.Context, hash common.Hash) (tx *types.Transaction, isMempool bool, err error) // token- and asset-specific methods loadToken(ctx context.Context, assetID uint32) error @@ -261,16 +269,6 @@ func unconnectedETH(logger dex.Logger, net dex.Network) (*ETHBackend, error) { // NewBackend is the exported constructor by which the DEX will import the // Backend. func NewBackend(configPath string, log dex.Logger, net dex.Network) (*ETHBackend, error) { - switch net { - case dex.Simnet: - case dex.Testnet: - case dex.Mainnet: - // TODO: Allow. When? - return nil, fmt.Errorf("eth cannot be used on mainnet") - default: - return nil, fmt.Errorf("unknown network ID: %d", net) - } - file, err := os.Open(configPath) if err != nil { return nil, err @@ -310,7 +308,7 @@ func NewBackend(configPath string, log dex.Logger, net dex.Network) (*ETHBackend return nil, fmt.Errorf("no contract address for eth version %d on %s", ethContractVersion, eth.net) } - eth.node = newRPCClient(eth.net, endpoints, ethContractAddr, log.SubLogger("RPC")) + eth.node = newRPCClient(net, endpoints, ethContractAddr, log.SubLogger("RPC")) return eth, nil } @@ -588,16 +586,6 @@ func (eth *baseBackend) ValidateSecret(secret, contractData []byte) bool { // Synced is true if the blockchain is ready for action. func (eth *baseBackend) Synced() (bool, error) { - // node.SyncProgress will return nil both before syncing has begun and - // after it has finished. In order to discern when syncing has begun, - // check that the best header came in under MaxBlockInterval. - sp, err := eth.node.syncProgress(eth.ctx) - if err != nil { - return false, err - } - if sp != nil { - return false, nil - } bh, err := eth.node.bestHeader(eth.ctx) if err != nil { return false, err @@ -727,6 +715,8 @@ func (eth *ETHBackend) poll(ctx context.Context) { // run processes the queue and monitors the application context. func (eth *ETHBackend) run(ctx context.Context) { + eth.baseLogger.Infof("Starting ETH block polling with interval of %v", + blockPollInterval) blockPoll := time.NewTicker(blockPollInterval) defer blockPoll.Stop() diff --git a/server/asset/eth/eth_test.go b/server/asset/eth/eth_test.go index 93fcd09c96..1951d6bd53 100644 --- a/server/asset/eth/eth_test.go +++ b/server/asset/eth/eth_test.go @@ -350,10 +350,8 @@ func TestSynced(t *testing.T) { wantErr, wantSynced bool }{{ name: "ok synced", + subSecs: dexeth.MaxBlockInterval - 1, wantSynced: true, - }, { - name: "ok syncing", - syncProg: new(ethereum.SyncProgress), }, { name: "ok header too old", subSecs: dexeth.MaxBlockInterval, @@ -361,10 +359,6 @@ func TestSynced(t *testing.T) { name: "best header error", bestHdrErr: errors.New(""), wantErr: true, - }, { - name: "sync progress error", - syncProgErr: errors.New(""), - wantErr: true, }} for _, test := range tests { diff --git a/server/asset/eth/rpcclient.go b/server/asset/eth/rpcclient.go index d3b8050c65..0fb8757ad2 100644 --- a/server/asset/eth/rpcclient.go +++ b/server/asset/eth/rpcclient.go @@ -404,14 +404,6 @@ func (c *rpcclient) suggestGasTipCap(ctx context.Context) (tipCap *big.Int, err }) } -// syncProgress return the current sync progress. Returns no error and nil when not syncing. -func (c *rpcclient) syncProgress(ctx context.Context) (prog *ethereum.SyncProgress, err error) { - return prog, c.withClient(func(ec *ethConn) error { - prog, err = ec.SyncProgress(ctx) - return err - }) -} - // blockNumber gets the chain length at the time of calling. func (c *rpcclient) blockNumber(ctx context.Context) (bn uint64, err error) { return bn, c.withClient(func(ec *ethConn) error { diff --git a/server/asset/eth/rpcclient_harness_test.go b/server/asset/eth/rpcclient_harness_test.go index a0db2eae77..8c6d8aca58 100644 --- a/server/asset/eth/rpcclient_harness_test.go +++ b/server/asset/eth/rpcclient_harness_test.go @@ -105,13 +105,6 @@ func TestBlockNumber(t *testing.T) { } } -func TestSyncProgress(t *testing.T) { - _, err := ethClient.syncProgress(ctx) - if err != nil { - t.Fatal(err) - } -} - func TestSuggestGasTipCap(t *testing.T) { _, err := ethClient.suggestGasTipCap(ctx) if err != nil {