diff --git a/client/asset/eth/chaincfg.go b/client/asset/eth/chaincfg.go index 9be8b5a22f..4e9d9ab29d 100644 --- a/client/asset/eth/chaincfg.go +++ b/client/asset/eth/chaincfg.go @@ -55,7 +55,7 @@ func NetworkCompatibilityData(net dex.Network) (c CompatibilityData, err error) return testnetCompatibilityData, nil case dex.Simnet: default: - return c, nil + return c, fmt.Errorf("No compatibility data for network # %d", net) } // simnet tDir, err := simnetDataDir() diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index 573451c4a3..e1005f931b 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -129,7 +129,7 @@ var ( }, } // WalletInfo defines some general information about a Ethereum wallet. - WalletInfo = &asset.WalletInfo{ + WalletInfo = asset.WalletInfo{ Name: "Ethereum", Version: 0, // SupportedVersions: For Ethereum, the server backend maintains a @@ -183,16 +183,22 @@ var ( // 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. -func perTxGasLimit(minerCeil uint64) uint64 { +func perTxGasLimit(gasFeeLimit uint64) uint64 { + // maxProportionOfBlockGasLimitToUse sets the maximum proportion of a + // block's gas limit that a swap and redeem transaction will use. Since it + // is set to 4, the max that will be used is 25% (1/4) of the block's gas + // limit. + const maxProportionOfBlockGasLimitToUse = 4 + // blockGasLimit is the amount of gas we can use in one transaction // according to the block gas limit. // Ethereum GasCeil: 30_000_000, Polygon: 8_000_000 - blockGasLimit := minerCeil / maxProportionOfBlockGasLimitToUse + 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) + txGasLimit := maxTxFeeGwei / gasFeeLimit if blockGasLimit > txGasLimit { return txGasLimit @@ -276,7 +282,8 @@ func (d *Driver) DecodeCoinID(coinID []byte) (string, error) { // Info returns basic information about the wallet and asset. func (d *Driver) Info() *asset.WalletInfo { - return WalletInfo + wi := WalletInfo + return &wi } // Exists checks the existence of the wallet. @@ -438,7 +445,7 @@ var _ asset.Wallet = (*TokenWallet)(nil) var _ asset.AccountLocker = (*ETHWallet)(nil) var _ asset.AccountLocker = (*TokenWallet)(nil) var _ asset.TokenMaster = (*ETHWallet)(nil) -var _ asset.WalletRestorer = (*assetWallet)(nil) +var _ asset.WalletRestorer = (*ETHWallet)(nil) var _ asset.LiveReconfigurer = (*ETHWallet)(nil) var _ asset.LiveReconfigurer = (*TokenWallet)(nil) var _ asset.TxFeeEstimator = (*ETHWallet)(nil) @@ -458,7 +465,6 @@ type baseWallet struct { log dex.Logger dir string walletType string - txGasLimit uint64 baseChainID uint32 chainCfg *params.ChainConfig @@ -502,11 +508,14 @@ type assetWallet struct { log dex.Logger ui dex.UnitInfo connected atomic.Bool - wi *asset.WalletInfo + wi asset.WalletInfo versionedContracts map[uint32]common.Address versionedGases map[uint32]*dexeth.Gases + maxSwapGas uint64 + maxRedeemGas uint64 + lockedFunds struct { mtx sync.RWMutex initiateReserves uint64 @@ -556,14 +565,18 @@ type TokenWallet struct { netToken *dexeth.NetToken } -// maxProportionOfBlockGasLimitToUse sets the maximum proportion of a block's -// gas limit that a swap and redeem transaction will use. Since it is set to -// 4, the max that will be used is 25% (1/4) of the block's gas limit. -const maxProportionOfBlockGasLimitToUse = 4 +func (w *assetWallet) maxSwapsAndRedeems() (maxSwaps, maxRedeems uint64) { + txGasLimit := perTxGasLimit(atomic.LoadUint64(&w.gasFeeLimitV)) + return txGasLimit / w.maxSwapGas, txGasLimit / w.maxRedeemGas +} // Info returns basic information about the wallet and asset. func (w *assetWallet) Info() *asset.WalletInfo { - return w.wi + wi := w.wi + maxSwaps, maxRedeems := w.maxSwapsAndRedeems() + wi.MaxSwapsInTx = maxSwaps + wi.MaxRedeemsInTx = maxRedeems + return &wi } // genWalletSeed uses the wallet seed passed from core as the entropy for @@ -684,7 +697,6 @@ func newWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network) } } } - wi := *WalletInfo return NewEVMWallet(&EVMWalletConfig{ BaseChainID: BipID, @@ -695,8 +707,7 @@ func newWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network) Tokens: dexeth.Tokens, Logger: logger, BaseChainContracts: contracts, - WalletInfo: &wi, - MinerBlockGasCeil: ethconfig.Defaults.Miner.GasCeil, + WalletInfo: WalletInfo, Net: net, }) } @@ -711,8 +722,7 @@ type EVMWalletConfig struct { Tokens map[uint32]*dexeth.Token Logger dex.Logger BaseChainContracts map[uint32]common.Address - MinerBlockGasCeil uint64 - WalletInfo *asset.WalletInfo + WalletInfo asset.WalletInfo Net dex.Network } @@ -742,8 +752,6 @@ func NewEVMWallet(cfg *EVMWalletConfig) (w *ETHWallet, err error) { gasFeeLimit = defaultGasFeeLimit } - txGasLimit := perTxGasLimit(cfg.MinerBlockGasCeil) - eth := &baseWallet{ net: cfg.Net, baseChainID: cfg.BaseChainID, @@ -754,7 +762,6 @@ func NewEVMWallet(cfg *EVMWalletConfig) (w *ETHWallet, err error) { log: cfg.Logger, dir: cfg.AssetCfg.DataDir, walletType: cfg.AssetCfg.Type, - txGasLimit: txGasLimit, settings: cfg.AssetCfg.Settings, gasFeeLimitV: gasFeeLimit, wallets: make(map[uint32]*assetWallet), @@ -772,6 +779,8 @@ func NewEVMWallet(cfg *EVMWalletConfig) (w *ETHWallet, err error) { } } + txGasLimit := perTxGasLimit(gasFeeLimit) + if maxSwapGas == 0 || txGasLimit < maxSwapGas { return nil, errors.New("max swaps cannot be zero or undefined") } @@ -779,16 +788,14 @@ func NewEVMWallet(cfg *EVMWalletConfig) (w *ETHWallet, err error) { return nil, errors.New("max redeems cannot be zero or undefined") } - wi := cfg.WalletInfo - wi.MaxSwapsInTx = txGasLimit / maxSwapGas - wi.MaxRedeemsInTx = txGasLimit / maxRedeemGas - aw := &assetWallet{ baseWallet: eth, log: cfg.Logger, assetID: assetID, versionedContracts: cfg.BaseChainContracts, versionedGases: cfg.VersionedGases, + maxSwapGas: maxSwapGas, + maxRedeemGas: maxRedeemGas, tipChange: cfg.AssetCfg.TipChange, findRedemptionReqs: make(map[[32]byte]*findRedemptionRequest), pendingApprovals: make(map[uint32]*pendingApproval), @@ -799,11 +806,13 @@ func NewEVMWallet(cfg *EVMWalletConfig) (w *ETHWallet, err error) { atomize: dexeth.WeiToGwei, ui: dexeth.UnitInfo, pendingTxCheckBal: new(big.Int), - wi: wi, + wi: cfg.WalletInfo, } + maxSwaps, maxRedeems := aw.maxSwapsAndRedeems() + cfg.Logger.Infof("ETH wallet will support a maximum of %d swaps and %d redeems per transaction.", - wi.MaxSwapsInTx, wi.MaxRedeemsInTx) + maxSwaps, maxRedeems) aw.wallets = map[uint32]*assetWallet{ assetID: aw, @@ -1089,10 +1098,12 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet, } } - if maxSwapGas == 0 || w.txGasLimit < maxSwapGas { + txGasLimit := perTxGasLimit(atomic.LoadUint64(&w.gasFeeLimitV)) + + if maxSwapGas == 0 || txGasLimit < maxSwapGas { return nil, errors.New("max swaps cannot be zero or undefined") } - if maxRedeemGas == 0 || w.txGasLimit < maxRedeemGas { + if maxRedeemGas == 0 || txGasLimit < maxRedeemGas { return nil, errors.New("max redeems cannot be zero or undefined") } @@ -1103,21 +1114,14 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet, gases[ver] = &c.Gas } - wi := &asset.WalletInfo{ - Name: token.Name, - Version: w.wi.Version, - SupportedVersions: w.wi.SupportedVersions, - UnitInfo: token.UnitInfo, - MaxSwapsInTx: w.txGasLimit / maxSwapGas, - MaxRedeemsInTx: w.txGasLimit / maxRedeemGas, - } - aw := &assetWallet{ baseWallet: w.baseWallet, log: w.baseWallet.log.SubLogger(strings.ToUpper(dex.BipIDSymbol(tokenCfg.AssetID))), assetID: tokenCfg.AssetID, versionedContracts: contracts, versionedGases: gases, + maxSwapGas: maxSwapGas, + maxRedeemGas: maxRedeemGas, tipChange: tokenCfg.TipChange, peersChange: tokenCfg.PeersChange, findRedemptionReqs: make(map[[32]byte]*findRedemptionRequest), @@ -1127,8 +1131,13 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet, evmify: token.AtomicToEVM, atomize: token.EVMToAtomic, ui: token.UnitInfo, - wi: wi, - pendingTxCheckBal: new(big.Int), + wi: asset.WalletInfo{ + Name: token.Name, + Version: w.wi.Version, + SupportedVersions: w.wi.SupportedVersions, + UnitInfo: token.UnitInfo, + }, + pendingTxCheckBal: new(big.Int), } w.baseWallet.walletsMtx.Lock() @@ -1717,9 +1726,10 @@ func (w *assetWallet) swapGas(n int, ver uint32) (oneSwap, nSwap uint64, err err // determining the maximum number of swaps that can be in one // transaction. Limit our gas estimate to the same number of swaps. nMax := n + maxSwaps, _ := w.maxSwapsAndRedeems() var nRemain, nFull int - if uint64(n) > w.wi.MaxSwapsInTx { - nMax = int(w.wi.MaxSwapsInTx) + if uint64(n) > maxSwaps { + nMax = int(maxSwaps) nFull = n / nMax nSwap = (oneSwap + uint64(nMax-1)*g.SwapAdd) * uint64(nFull) nRemain = n % nMax @@ -3150,7 +3160,7 @@ func (w *TokenWallet) EstimateSendTxFee(addr string, value, _ uint64, subtract b // RestorationInfo returns information about how to restore the wallet in // various external wallets. -func (w *assetWallet) RestorationInfo(seed []byte) ([]*asset.WalletRestoration, error) { +func (w *ETHWallet) RestorationInfo(seed []byte) ([]*asset.WalletRestoration, error) { privateKey, zero, err := privKeyFromSeed(seed) if err != nil { return nil, err diff --git a/client/asset/eth/eth_test.go b/client/asset/eth/eth_test.go index 9135a99878..cc2efe6c70 100644 --- a/client/asset/eth/eth_test.go +++ b/client/asset/eth/eth_test.go @@ -605,10 +605,6 @@ func tassetWallet(assetID uint32) (asset.Wallet, *assetWallet, *tMempoolNode, co } } - wi := *WalletInfo - wi.MaxSwapsInTx = 40 - wi.MaxRedeemsInTx = 60 - aw := &assetWallet{ baseWallet: &baseWallet{ baseChainID: BipID, @@ -625,6 +621,8 @@ func tassetWallet(assetID uint32) (asset.Wallet, *assetWallet, *tMempoolNode, co pendingTxs: make(map[common.Hash]*pendingTx), }, versionedGases: versionedGases, + maxSwapGas: versionedGases[0].Swap, + maxRedeemGas: versionedGases[0].Redeem, log: tLogger.SubLogger(strings.ToUpper(dex.BipIDSymbol(assetID))), assetID: assetID, contractors: map[uint32]contractor{0: c}, @@ -635,7 +633,7 @@ func tassetWallet(assetID uint32) (asset.Wallet, *assetWallet, *tMempoolNode, co pendingApprovals: make(map[uint32]*pendingApproval), approvalCache: make(map[uint32]bool), // move up after review - wi: &wi, + wi: WalletInfo, } aw.wallets = map[uint32]*assetWallet{ BipID: aw, diff --git a/client/asset/polygon/chaincfg.go b/client/asset/polygon/chaincfg.go index 79438146a7..f29db694f7 100644 --- a/client/asset/polygon/chaincfg.go +++ b/client/asset/polygon/chaincfg.go @@ -45,7 +45,7 @@ func NetworkCompatibilityData(net dex.Network) (c eth.CompatibilityData, err err return testnetCompatibilityData, nil case dex.Simnet: default: - return c, nil + return c, fmt.Errorf("No compatibility data for network # %d", net) } // simnet tDir, err := simnetDataDir() diff --git a/client/asset/polygon/polygon.go b/client/asset/polygon/polygon.go index c7affbee8c..ac71f7a509 100644 --- a/client/asset/polygon/polygon.go +++ b/client/asset/polygon/polygon.go @@ -37,7 +37,6 @@ const ( BipID = 966 walletTypeRPC = "rpc" walletTypeToken = "token" - minerGasCeil = 8_000_000 // config.Defaults.Miner.GasCeil ) var ( @@ -45,7 +44,7 @@ var ( usdcTokenID, _ = dex.BipSymbolID("usdc.polygon") // WalletInfo defines some general information about a Polygon Wallet(EVM // Compatible). - WalletInfo = &asset.WalletInfo{ + WalletInfo = asset.WalletInfo{ Name: "Polygon", Version: 0, SupportedVersions: []uint32{0}, @@ -87,7 +86,6 @@ func (d *Driver) Open(cfg *asset.WalletConfig, logger dex.Logger, net dex.Networ } } } - wi := *WalletInfo // BipID, chainCfg, cfg, &t, dexpolygon.VersionedGases, dexpolygon.Tokens, logger, net return eth.NewEVMWallet(ð.EVMWalletConfig{ BaseChainID: BipID, @@ -98,8 +96,7 @@ func (d *Driver) Open(cfg *asset.WalletConfig, logger dex.Logger, net dex.Networ Tokens: dexpolygon.Tokens, Logger: logger, BaseChainContracts: contracts, - MinerBlockGasCeil: minerGasCeil, - WalletInfo: &wi, + WalletInfo: WalletInfo, Net: net, }) } @@ -109,7 +106,8 @@ func (d *Driver) DecodeCoinID(coinID []byte) (string, error) { } func (d *Driver) Info() *asset.WalletInfo { - return WalletInfo + wi := WalletInfo + return &wi } func (d *Driver) Exists(walletType, dataDir string, settings map[string]string, net dex.Network) (bool, error) { diff --git a/client/cmd/assetseed/main.go b/client/cmd/assetseed/main.go index cf31841248..85da557d77 100644 --- a/client/cmd/assetseed/main.go +++ b/client/cmd/assetseed/main.go @@ -9,6 +9,7 @@ import ( "fmt" "os" + "decred.org/dcrdex/client/asset" "decred.org/dcrdex/client/core" ) @@ -36,6 +37,11 @@ func main() { os.Exit(1) } + if tkn := asset.TokenInfo(uint32(assetID)); tkn != nil { + fmt.Fprintf(os.Stderr, "this is a token. did you want asset ID %d for %s?\n", tkn.ParentID, asset.Asset(tkn.ParentID).Info.Name) + os.Exit(1) + } + seed, _ := core.AssetSeedAndPass(uint32(assetID), appSeedB) fmt.Printf("%x\n", seed) diff --git a/client/core/core.go b/client/core/core.go index 79236aecd1..5723788d9f 100644 --- a/client/core/core.go +++ b/client/core/core.go @@ -2657,6 +2657,11 @@ func (c *Core) assetSeedAndPass(assetID uint32, crypter encrypt.Crypter) (seed, return nil, nil, errors.New("no v2 credentials stored") } + if tkn := asset.TokenInfo(assetID); tkn != nil { + return nil, nil, fmt.Errorf("%s is a token. assets seeds are for base chains onlyu. did you want %s", + tkn.Name, asset.Asset(tkn.ParentID).Info.Name) + } + appSeed, err := crypter.Decrypt(creds.EncSeed) if err != nil { return nil, nil, fmt.Errorf("app seed decryption error: %w", err) @@ -2671,11 +2676,21 @@ func (c *Core) assetSeedAndPass(assetID uint32, crypter encrypt.Crypter) (seed, // on external wallet software and their key derivation paths, this seed may be // usable for accessing funds outside of DEX applications, e.g. btcwallet. func AssetSeedAndPass(assetID uint32, appSeed []byte) ([]byte, []byte) { + const accountBasedSeedAssetID = 60 // ETH seedAssetID := assetID if ai, _ := asset.Info(assetID); ai != nil && ai.IsAccountBased { - const accountBasedSeedAssetID = 60 // ETH seedAssetID = accountBasedSeedAssetID } + // Tokens asset IDs shouldn't be passed in, but if they are, return the seed + // for the parent ID. + if tkn := asset.TokenInfo(assetID); tkn != nil { + if ai, _ := asset.Info(tkn.ParentID); ai != nil { + if ai.IsAccountBased { + seedAssetID = accountBasedSeedAssetID + } + } + } + b := make([]byte, len(appSeed)+4) copy(b, appSeed) binary.BigEndian.PutUint32(b[len(appSeed):], seedAssetID) diff --git a/client/webserver/live_test.go b/client/webserver/live_test.go index c82314f49d..7a40523b3f 100644 --- a/client/webserver/live_test.go +++ b/client/webserver/live_test.go @@ -1382,7 +1382,7 @@ var winfos = map[uint32]*asset.WalletInfo{ ConfigOpts: configOpts, }}, }, - 60: eth.WalletInfo, + 60: ð.WalletInfo, 145: { Version: 0, SupportedVersions: []uint32{0}, diff --git a/client/webserver/site/src/html/bodybuilder.tmpl b/client/webserver/site/src/html/bodybuilder.tmpl index 8140d13fbf..afdec11e0e 100644 --- a/client/webserver/site/src/html/bodybuilder.tmpl +++ b/client/webserver/site/src/html/bodybuilder.tmpl @@ -9,7 +9,7 @@ {{.Title}} - +