Skip to content

Commit

Permalink
client/eth: Fix init estimates.
Browse files Browse the repository at this point in the history
Make estimates aware of the max gas limit for transactions that we later
use when trading. Use one fourth of the block gas limit or the tx fee
limit per max fee per gas as the top end, whichever is lower. Set
simnet's block gas limit to 30 million, the same as test and mainnet.
  • Loading branch information
JoeGruffins committed Feb 16, 2023
1 parent c6a4c34 commit 7222c01
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 22 deletions.
82 changes: 69 additions & 13 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ const (
// coinIDTakerFoundMakerRedemption is a prefix to identify one of CoinID formats,
// see DecodeCoinID func for details.
coinIDTakerFoundMakerRedemption = "TakerFoundMakerRedemption:"

// maxTxFeeGwei is the default max amout of eth that can be used in one
// transaction. This is set by the host in the case of providers. The
// internal node currently has no max but also cannot be used since the
// merge.
//
// TODO: Find a way to ask the host about their config set max fee and
// gas values.
maxTxFeeGwei = 1_000_000_000
)

var (
Expand Down Expand Up @@ -170,6 +179,23 @@ var (
0, // branch 0
0, // index 0
}

// perTxGasLimit is the most gas we can use on a transaction. It is the
// lower of either the per tx or per block gas limit.
perTxGasLimit = func() uint64 {
// blockGasLimit is the amount of gas we can use in one transaction
// according to the block gas limit.
blockGasLimit := ethconfig.Defaults.Miner.GasCeil / maxProportionOfBlockGasLimitToUse

// txGasLimit is the amount of gas we can use in one transaction
// according to the default transaction gas fee limit.
txGasLimit := uint64(maxTxFeeGwei / defaultGasFeeLimit)

if blockGasLimit > txGasLimit {
return txGasLimit
}
return blockGasLimit
}()
)

// WalletConfig are wallet-level configuration settings.
Expand Down Expand Up @@ -619,7 +645,7 @@ func createWallet(createWalletParams *asset.CreateWalletParams, skipConnect bool
}

// NewWallet is the exported constructor by which the DEX will import the
// exchange wallet. It starts an internal light node.
// exchange wallet.
func NewWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network) (w *ETHWallet, err error) {
// var cl ethFetcher
switch assetCFG.Type {
Expand Down Expand Up @@ -654,7 +680,6 @@ func NewWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network)
monitoredTxs: make(map[common.Hash]*monitoredTx),
}

gasCeil := ethconfig.Defaults.Miner.GasCeil
var maxSwapGas, maxRedeemGas uint64
for _, gases := range dexeth.VersionedGases {
if gases.Swap > maxSwapGas {
Expand All @@ -676,8 +701,8 @@ func NewWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network)
evmify: dexeth.GweiToWei,
atomize: dexeth.WeiToGwei,
atomicUnit: dexeth.UnitInfo.AtomicUnit,
maxSwapsInTx: gasCeil / maxProportionOfBlockGasLimitToUse / maxSwapGas,
maxRedeemsInTx: gasCeil / maxProportionOfBlockGasLimitToUse / maxRedeemGas,
maxSwapsInTx: perTxGasLimit / maxSwapGas,
maxRedeemsInTx: perTxGasLimit / maxRedeemGas,
}

aw.wallets = map[uint32]*assetWallet{
Expand Down Expand Up @@ -933,7 +958,6 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet,
return nil, fmt.Errorf("could not find token with ID %d on network %s", w.assetID, w.net)
}

gasCeil := ethconfig.Defaults.Miner.GasCeil
var maxSwapGas, maxRedeemGas uint64
for _, contract := range netToken.SwapContracts {
if contract.Gas.Swap > maxSwapGas {
Expand All @@ -955,8 +979,8 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet,
evmify: token.AtomicToEVM,
atomize: token.EVMToAtomic,
atomicUnit: token.UnitInfo.AtomicUnit,
maxSwapsInTx: gasCeil / maxProportionOfBlockGasLimitToUse / maxSwapGas,
maxRedeemsInTx: gasCeil / maxProportionOfBlockGasLimitToUse / maxRedeemGas,
maxSwapsInTx: perTxGasLimit / maxSwapGas,
maxRedeemsInTx: perTxGasLimit / maxRedeemGas,
}

w.baseWallet.walletsMtx.Lock()
Expand Down Expand Up @@ -1031,7 +1055,6 @@ 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)
}
Expand Down Expand Up @@ -1119,17 +1142,18 @@ func (w *assetWallet) maxOrder(lotSize uint64, feeSuggestion, maxFeeRate uint64,
return nil, fmt.Errorf("gasEstimate error: %w", err)
}

refundCost := g.Refund * maxFeeRate
oneFee := g.oneGas * maxFeeRate
var lots uint64
if feeWallet == nil {
lots = balance.Available / (lotSize + oneFee)
lots = balance.Available / (lotSize + oneFee + refundCost)
} else { // token
lots = balance.Available / lotSize
parentBal, err := feeWallet.Balance()
if err != nil {
return nil, fmt.Errorf("error getting base chain balance: %w", err)
}
feeLots := parentBal.Available / oneFee
feeLots := parentBal.Available / (oneFee + refundCost)
if feeLots < lots {
w.log.Infof("MaxOrder reducing lots because of low fee reserves: %d -> %d", lots, feeLots)
lots = feeLots
Expand Down Expand Up @@ -1451,13 +1475,45 @@ func (w *assetWallet) swapGas(n int, ver uint32) (oneSwap, nSwap uint64, approve
// If we've approved the contract to transfer, we can get a live
// estimate to double check.

// The amount we can estimate and ultimately the amount we can use in a
// single transaction is limited by the block gas limit or the tx gas
// limit. Estimate the number of swaps we can safely estimate here.
nMax := n
var nRemain, nFull int
if n > 1 && nSwap > perTxGasLimit {
nMax = 1
gas := perTxGasLimit - g.Swap
nMax += int(gas / g.SwapAdd)
nFull = n / nMax
nSwap = (oneSwap + uint64(nMax-1)*g.SwapAdd) * uint64(nFull)
nRemain = n % nMax
if nRemain != 0 {
nSwap += oneSwap + uint64(nRemain-1)*g.SwapAdd
}
}

// If a live estimate is greater than our estimate from configured values,
// use the live estimate with a warning.
if gasEst, err := w.estimateInitGas(w.ctx, n, ver); err != nil {
gasEst, err := w.estimateInitGas(w.ctx, nMax, ver)
if err != nil {
w.log.Errorf("(%d) error estimating swap gas: %v", w.assetID, err)
// TODO: investigate "gas required exceeds allowance".
return 0, 0, false, err
} else if gasEst > nSwap {
}
if nMax != n {
// If we needed to adjust the max earlier, and the estimate did
// not error, mulitply the estimates and add the estimate of the
// remainder.
gasEst *= uint64(nFull)
if nRemain > 0 {
remainEst, err := w.estimateInitGas(w.ctx, nRemain, ver)
if err != nil {
w.log.Errorf("(%d) error estimating swap gas for remainder: %v", w.assetID, err)
return 0, 0, false, err
}
gasEst += remainEst
}
}
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)
nSwap = gasEst * 11 / 10 // 10% buffer
if n == 1 && nSwap > oneSwap {
Expand Down
15 changes: 8 additions & 7 deletions client/asset/eth/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1305,9 +1305,10 @@ func TestPreSwap(t *testing.T) {
const feeSuggestion = 90
const lotSize = 10e9
oneFee := ethGases.Swap * tETH.MaxFeeRate
oneLock := lotSize + oneFee
refund := ethGases.Refund * tETH.MaxFeeRate
oneLock := lotSize + oneFee + refund

oneFeeToken := tokenGases.Swap * tToken.MaxFeeRate
oneFeeToken := tokenGases.Swap*tToken.MaxFeeRate + tokenGases.Refund*tToken.MaxFeeRate

tests := []struct {
name string
Expand Down Expand Up @@ -4574,17 +4575,17 @@ func testMaxSwapRedeemLots(t *testing.T, assetID uint32) {

info := wallet.Info()
if assetID == BipID {
if info.MaxSwapsInTx != 55 {
t.Fatalf("expected 55 for max swaps but got %d", info.MaxSwapsInTx)
if info.MaxSwapsInTx != 37 {
t.Fatalf("expected 37 for max swaps but got %d", info.MaxSwapsInTx)
}
if info.MaxRedeemsInTx != 119 {
if info.MaxRedeemsInTx != 79 {
t.Fatalf("expected 119 for max redemptions but got %d", info.MaxRedeemsInTx)
}
} else {
if info.MaxSwapsInTx != 43 {
if info.MaxSwapsInTx != 28 {
t.Fatalf("expected 43 for max swaps but got %d", info.MaxSwapsInTx)
}
if info.MaxRedeemsInTx != 107 {
if info.MaxRedeemsInTx != 71 {
t.Fatalf("expected 107 for max redemptions but got %d", info.MaxRedeemsInTx)
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/asset/eth/fundingcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type fundingCoin struct {

// String creates a human readable string.
func (c *fundingCoin) String() string {
return fmt.Sprintf("address: %v, amount:%x", c.addr, c.amt)
return fmt.Sprintf("address: %v, amount: %d", c.addr, c.amt)
}

// ID utf-8 encodes the account address. This ID will be sent to the server as
Expand Down
2 changes: 2 additions & 0 deletions dex/testing/eth/create-node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ if [ "${CHAIN_ADDRESS}" != "_" ]; then
[Eth.Miner]
Etherbase = "0x${CHAIN_ADDRESS}"
GasFloor = 30000000
GasCeil = 30000000
EOF
fi

Expand Down
2 changes: 1 addition & 1 deletion dex/testing/eth/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ cat > "${NODES_ROOT}/genesis.json" <<EOF
}
},
"difficulty": "1",
"gasLimit": "12487783",
"gasLimit": "30000000",
"extradata": "0x00000000000000000000000000000000000000000000000000000000000000009ebba10a6136607688ca4f27fab70e23938cd0270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"alloc": {
"18d65fb8d60c1199bb1ad381be47aa692b482605": {
Expand Down

0 comments on commit 7222c01

Please sign in to comment.