diff --git a/client/asset/bch/bch.go b/client/asset/bch/bch.go index 502653d831..2938a5a18d 100644 --- a/client/asset/bch/bch.go +++ b/client/asset/bch/bch.go @@ -89,6 +89,7 @@ var ( rpcWalletDefinition, // electrumWalletDefinition, // getinfo RPC needs backport: https://github.com/Electron-Cash/Electron-Cash/pull/2399 }, + BlockchainClass: asset.BlockchainClassUTXO, } externalFeeRate = btc.BitcoreRateFetcher("BCH") diff --git a/client/asset/btc/btc.go b/client/asset/btc/btc.go index 0e09d5cf82..7937319c4a 100644 --- a/client/asset/btc/btc.go +++ b/client/asset/btc/btc.go @@ -216,6 +216,7 @@ var ( electrumWalletDefinition, }, LegacyWalletIndex: 1, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/dash/dash.go b/client/asset/dash/dash.go index 508abbd9d9..bc8d9676de 100644 --- a/client/asset/dash/dash.go +++ b/client/asset/dash/dash.go @@ -74,6 +74,7 @@ var ( ConfigOpts: configOpts, }, }, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/dcr/dcr.go b/client/asset/dcr/dcr.go index ff557bdffd..aa95f8fc98 100644 --- a/client/asset/dcr/dcr.go +++ b/client/asset/dcr/dcr.go @@ -289,6 +289,7 @@ var ( MultiFundingOpts: multiFundingOpts, }, }, + BlockchainClass: asset.BlockchainClassUTXO, } swapFeeBumpKey = "swapfeebump" splitKey = "swapsplit" diff --git a/client/asset/dgb/dgb.go b/client/asset/dgb/dgb.go index 962fa5ce0c..8290c107b3 100644 --- a/client/asset/dgb/dgb.go +++ b/client/asset/dgb/dgb.go @@ -78,6 +78,7 @@ var ( DefaultConfigPath: dexbtc.SystemConfigPath("digibyte"), ConfigOpts: configOpts, }}, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/doge/doge.go b/client/asset/doge/doge.go index 97ee04ff33..175ce8c11c 100644 --- a/client/asset/doge/doge.go +++ b/client/asset/doge/doge.go @@ -98,6 +98,7 @@ var ( DefaultConfigPath: dexbtc.SystemConfigPath("dogecoin"), ConfigOpts: configOpts, }}, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/eth/eth.go b/client/asset/eth/eth.go index 8eef685728..a29c1bd579 100644 --- a/client/asset/eth/eth.go +++ b/client/asset/eth/eth.go @@ -182,7 +182,7 @@ var ( // MaxSwapsInTx and MaxRedeemsInTx are set in (Wallet).Info, since // the value cannot be known until we connect and get network info. }, - IsAccountBased: true, + BlockchainClass: asset.BlockchainClassEVM, } // unlimitedAllowance is the maximum supported allowance for an erc20 @@ -660,7 +660,7 @@ func CreateEVMWallet(chainID int64, createWalletParams *asset.CreateWalletParams func newWallet(assetCFG *asset.WalletConfig, logger dex.Logger, net dex.Network) (w *ETHWallet, err error) { chainCfg, err := ChainConfig(net) if err != nil { - return nil, fmt.Errorf("failed to locate Ethereum genesis configuration for network %s", net) + return nil, fmt.Errorf("failed to locate Ethereum genesis configuration for network %s: %v", net, err) } comp, err := NetworkCompatibilityData(net) if err != nil { @@ -1284,6 +1284,7 @@ func (w *ETHWallet) OpenTokenWallet(tokenCfg *asset.TokenConfig) (asset.Wallet, Name: token.Name, SupportedVersions: w.wi.SupportedVersions, UnitInfo: token.UnitInfo, + BlockchainClass: asset.BlockchainClassEVM, }, pendingTxCheckBal: new(big.Int), } diff --git a/client/asset/firo/firo.go b/client/asset/firo/firo.go index b2349da9f0..383836c8ff 100644 --- a/client/asset/firo/firo.go +++ b/client/asset/firo/firo.go @@ -84,6 +84,7 @@ var ( ConfigOpts: append(btc.ElectrumConfigOpts, btc.CommonConfigOpts("FIRO", true)...), }, }, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/interface.go b/client/asset/interface.go index 68af08d7f4..00b8b531ea 100644 --- a/client/asset/interface.go +++ b/client/asset/interface.go @@ -304,6 +304,17 @@ type Token struct { ContractAddress string `json:"contractAddress"` // Set in SetNetwork } +type BlockchainClass string + +const ( + BlockchainClassUTXO BlockchainClass = "UTXO" + BlockchainClassEVM BlockchainClass = "EVM" +) + +func (c BlockchainClass) IsEVM() bool { + return c == BlockchainClassEVM +} + // WalletInfo is auxiliary information about an ExchangeWallet. type WalletInfo struct { // Name is the display name for the currency, e.g. "Decred" @@ -331,10 +342,8 @@ type WalletInfo struct { // MaxRedeemsInTx is the max amount of redemptions that this wallet can do // in a single transaction. MaxRedeemsInTx uint64 - // IsAccountBased should be set to true for account-based (EVM) assets, so - // that a common seed will be generated and wallets will generate the - // same address. - IsAccountBased bool + // BlockchainClass is the type of the wallet's blockchain. + BlockchainClass BlockchainClass } // ConfigOption is a wallet configuration option. diff --git a/client/asset/ltc/ltc.go b/client/asset/ltc/ltc.go index 385f1a21f6..75e0ea4d07 100644 --- a/client/asset/ltc/ltc.go +++ b/client/asset/ltc/ltc.go @@ -79,6 +79,7 @@ var ( rpcWalletDefinition, electrumWalletDefinition, }, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/polygon/polygon.go b/client/asset/polygon/polygon.go index 6e65cf8dff..6ba83da800 100644 --- a/client/asset/polygon/polygon.go +++ b/client/asset/polygon/polygon.go @@ -87,7 +87,7 @@ var ( // MaxSwapsInTx and MaxRedeemsInTx are set in (Wallet).Info, since // the value cannot be known until we connect and get network info. }, - IsAccountBased: true, + BlockchainClass: asset.BlockchainClassEVM, } ) diff --git a/client/asset/zcl/zcl.go b/client/asset/zcl/zcl.go index c7cb75c7d0..2d00548238 100644 --- a/client/asset/zcl/zcl.go +++ b/client/asset/zcl/zcl.go @@ -99,6 +99,7 @@ var ( ConfigOpts: configOpts, NoAuth: true, }}, + BlockchainClass: asset.BlockchainClassUTXO, } ) diff --git a/client/asset/zec/zec.go b/client/asset/zec/zec.go index bd68bb57d1..ff714bf086 100644 --- a/client/asset/zec/zec.go +++ b/client/asset/zec/zec.go @@ -139,6 +139,7 @@ var ( ConfigOpts: configOpts, NoAuth: true, }}, + BlockchainClass: asset.BlockchainClassUTXO, } feeReservesPerLot = dexzec.TxFeesZIP317(dexbtc.RedeemP2PKHInputSize+1, 2*dexbtc.P2PKHOutputSize+1, 0, 0, 0, 0) diff --git a/client/core/core.go b/client/core/core.go index f3572b99d2..499fa444de 100644 --- a/client/core/core.go +++ b/client/core/core.go @@ -1529,9 +1529,6 @@ type Core struct { reFiat chan struct{} - pendingWalletsMtx sync.RWMutex - pendingWallets map[uint32]bool - notes chan asset.WalletNotification pokesCache *pokesCache @@ -1672,7 +1669,6 @@ func New(cfg *Config) (*Core, error) { fiatRateSources: make(map[string]*commonRateSource), reFiat: make(chan struct{}, 1), - pendingWallets: make(map[uint32]bool), notes: make(chan asset.WalletNotification, 128), requestedActions: make(map[string]*asset.ActionRequiredNote), @@ -2020,11 +2016,41 @@ func (c *Core) dexConnections() []*dexConnection { // wallet gets the wallet for the specified asset ID in a thread-safe way. func (c *Core) wallet(assetID uint32) (*xcWallet, bool) { c.walletMtx.RLock() - defer c.walletMtx.RUnlock() w, found := c.wallets[assetID] + c.walletMtx.RUnlock() return w, found } +func (c *Core) initializeTokenWallet(tokenID uint32, tkn *asset.Token) error { + if _, found := c.wallet(tkn.ParentID); !found { + return fmt.Errorf("no parent wallet %d for token %s", tkn.ParentID, tkn.Name) + } + dbWallet, err := c.createTokenDBWallet(tokenID, tkn, &WalletForm{ + AssetID: tokenID, + Config: make(map[string]string), + Type: tkn.Definition.Type, + }) + if err != nil { + return fmt.Errorf("error creating %s token wallet with existing %s parent wallet: %w", + dex.BipIDSymbol(tokenID), dex.BipIDSymbol(tkn.ParentID), err) + } + w, err := c.loadXCWallet(dbWallet) + if err != nil { + return fmt.Errorf("error loading newly created %s token wallet: %w", tkn.Name, err) + } + bals := &WalletBalance{Balance: &db.Balance{Balance: asset.Balance{Other: make(map[asset.BalanceCategory]asset.CustomBalance)}}} + w.setBalance(bals) // update xcWallet's WalletBalance + dbWallet.Balance = bals.Balance + // Store the wallet in the database. + err = c.db.UpdateWallet(dbWallet) + if err != nil { + return fmt.Errorf("error storing new token wallet credentials: %w", err) + } + c.log.Infof("Token wallet for %s automatically created", dex.BipIDSymbol(tokenID)) + c.updateWallet(tokenID, w) + return nil +} + // encryptionKey retrieves the application encryption key. The password is used // to recreate the outer key/crypter, which is then used to decode and recreate // the inner key/crypter. @@ -2449,28 +2475,6 @@ func (c *Core) SupportedAssets() map[uint32]*SupportedAsset { return c.assetMap() } -func (c *Core) walletCreationPending(tokenID uint32) bool { - c.pendingWalletsMtx.RLock() - defer c.pendingWalletsMtx.RUnlock() - return c.pendingWallets[tokenID] -} - -func (c *Core) setWalletCreationPending(tokenID uint32) error { - c.pendingWalletsMtx.Lock() - defer c.pendingWalletsMtx.Unlock() - if c.pendingWallets[tokenID] { - return fmt.Errorf("creation already pending for %s", unbip(tokenID)) - } - c.pendingWallets[tokenID] = true - return nil -} - -func (c *Core) setWalletCreationComplete(tokenID uint32) { - c.pendingWalletsMtx.Lock() - delete(c.pendingWallets, tokenID) - c.pendingWalletsMtx.Unlock() -} - // assetMap returns a map of asset information for supported assets. func (c *Core) assetMap() map[uint32]*SupportedAsset { supported := asset.Assets() @@ -2498,13 +2502,12 @@ func (c *Core) assetMap() map[uint32]*SupportedAsset { wallet = w.state() } assets[tokenID] = &SupportedAsset{ - ID: tokenID, - Symbol: dex.BipIDSymbol(tokenID), - Wallet: wallet, - Token: token, - Name: token.Name, - UnitInfo: token.UnitInfo, - WalletCreationPending: c.walletCreationPending(tokenID), + ID: tokenID, + Symbol: dex.BipIDSymbol(tokenID), + Wallet: wallet, + Token: token, + Name: token.Name, + UnitInfo: token.UnitInfo, } } } @@ -2535,13 +2538,12 @@ func (c *Core) asset(assetID uint32) *SupportedAsset { } return &SupportedAsset{ - ID: assetID, - Symbol: dex.BipIDSymbol(assetID), - Wallet: wallet, - Token: token, - Name: token.Name, - UnitInfo: token.UnitInfo, - WalletCreationPending: c.walletCreationPending(assetID), + ID: assetID, + Symbol: dex.BipIDSymbol(assetID), + Wallet: wallet, + Token: token, + Name: token.Name, + UnitInfo: token.UnitInfo, } } @@ -2571,132 +2573,42 @@ func (c *Core) requestedActionsList() []*asset.ActionRequiredNote { // CreateWallet creates a new exchange wallet. func (c *Core) CreateWallet(appPW, walletPW []byte, form *WalletForm) error { - assetID := form.AssetID - symbol := unbip(assetID) - _, exists := c.wallet(assetID) - if exists { - return fmt.Errorf("%s wallet already exists", symbol) - } - crypter, err := c.encryptionKey(appPW) if err != nil { return err } - - var creationQueued bool - defer func() { - if !creationQueued { - crypter.Close() - } - }() - - // If this isn't a token, easy route. - token := asset.TokenInfo(assetID) - if token == nil { - _, err = c.createWalletOrToken(crypter, walletPW, form) - return err - } - - // Prevent two different tokens from trying to create the parent simultaneously. - if err = c.setWalletCreationPending(token.ParentID); err != nil { - return err - } - defer c.setWalletCreationComplete(token.ParentID) - - // If the parent already exists, easy route. - _, found := c.wallet(token.ParentID) - if found { - _, err = c.createWalletOrToken(crypter, walletPW, form) - return err - } - - // Double-registration mode. The parent wallet will be created - // synchronously, then a goroutine is launched to wait for the parent to - // sync before creating the token wallet. The caller can get information - // about the asynchronous creation from WalletCreationNote notifications. - - // First check that they configured the parent asset. - if form.ParentForm == nil { - return fmt.Errorf("no parent wallet %d for token %d (%s), and no parent asset configuration provided", - token.ParentID, assetID, unbip(assetID)) - } - if form.ParentForm.AssetID != token.ParentID { - return fmt.Errorf("parent form asset ID %d is not expected value %d", - form.ParentForm.AssetID, token.ParentID) - } - - // Create the parent synchronously. - parentWallet, err := c.createWalletOrToken(crypter, walletPW, form.ParentForm) - if err != nil { - return fmt.Errorf("error creating parent wallet: %v", err) - } - - if err = c.setWalletCreationPending(assetID); err != nil { - return err - } - - // Start a goroutine to wait until the parent wallet is synced, and then - // begin creation of the token wallet. - c.wg.Add(1) - - c.notify(newWalletCreationNote(TopicCreationQueued, "", "", db.Data, assetID)) - - go func() { - defer c.wg.Done() - defer c.setWalletCreationComplete(assetID) - defer crypter.Close() - - for { - parentWallet.mtx.RLock() - synced := parentWallet.syncStatus.Synced - parentWallet.mtx.RUnlock() - if synced { - break - } - select { - case <-c.ctx.Done(): - return - case <-time.After(time.Second): - } - } - // If there was a walletPW provided, it was for the parent wallet, so - // use nil here. - if _, err := c.createWalletOrToken(crypter, nil, form); err != nil { - c.log.Errorf("failed to create token wallet: %v", err) - subject, details := c.formatDetails(TopicQueuedCreationFailed, unbip(token.ParentID), symbol) - c.notify(newWalletCreationNote(TopicQueuedCreationFailed, subject, details, db.ErrorLevel, assetID)) - } else { - c.notify(newWalletCreationNote(TopicQueuedCreationSuccess, "", "", db.Data, assetID)) - } - }() - creationQueued = true - return nil + return c.createWallet(crypter, walletPW, form) } -func (c *Core) createWalletOrToken(crypter encrypt.Crypter, walletPW []byte, form *WalletForm) (wallet *xcWallet, err error) { +func (c *Core) createWallet(crypter encrypt.Crypter, walletPW []byte, form *WalletForm) (err error) { assetID := form.AssetID symbol := unbip(assetID) + _, exists := c.wallet(assetID) + if exists { + return fmt.Errorf("%s wallet already exists", symbol) + } + token := asset.TokenInfo(assetID) var dbWallet *db.Wallet - if token != nil { - dbWallet, err = c.createTokenWallet(assetID, token, form) + if token == nil { + dbWallet, err = c.createDBWallet(crypter, form, walletPW) } else { - dbWallet, err = c.createWallet(crypter, walletPW, assetID, form) + dbWallet, err = c.createTokenDBWallet(assetID, token, form) } if err != nil { - return nil, err + return err } - wallet, err = c.loadWallet(dbWallet) + wallet, err := c.loadXCWallet(dbWallet) if err != nil { - return nil, fmt.Errorf("error loading wallet for %d -> %s: %w", assetID, symbol, err) + return fmt.Errorf("error loading wallet for %d -> %s: %w", assetID, symbol, err) } // Block PeersChange until we know this wallet is ready. atomic.StoreUint32(wallet.broadcasting, 0) dbWallet.Address, err = c.connectWallet(wallet) if err != nil { - return nil, err + return err } if c.cfg.UnlockCoinsOnLogin { @@ -2705,16 +2617,16 @@ func (c *Core) createWalletOrToken(crypter encrypt.Crypter, walletPW []byte, for } } - initErr := func(s string, a ...any) (*xcWallet, error) { + initErr := func(s string, a ...any) error { _ = wallet.Lock(2 * time.Second) // just try, but don't confuse the user with an error wallet.Disconnect() - return nil, fmt.Errorf(s, a...) + return fmt.Errorf(s, a...) } err = c.unlockWallet(crypter, wallet) // no-op if !wallet.Wallet.Locked() && len(encPW) == 0 if err != nil { wallet.Disconnect() - return nil, fmt.Errorf("%s wallet authentication error: %w", symbol, err) + return fmt.Errorf("%s wallet authentication error: %w", symbol, err) } balances, err := c.walletBalance(wallet) @@ -2742,11 +2654,25 @@ func (c *Core) createWalletOrToken(crypter encrypt.Crypter, walletPW []byte, for c.notify(newWalletStateNote(wallet.state())) c.walletCheckAndNotify(wallet) - return wallet, nil + // Create all token wallets + if token == nil { + for tokenID, tkn := range asset.Asset(assetID).Tokens { + form := &WalletForm{ + AssetID: tokenID, + Config: make(map[string]string), + Type: tkn.Definition.Type, + } + if err := c.createWallet(crypter, nil, form); err != nil { + c.log.Errorf("Error creating token %s wallet for parent %s", tkn.Name, symbol) + } + } + } + + return nil } -func (c *Core) createWallet(crypter encrypt.Crypter, walletPW []byte, assetID uint32, form *WalletForm) (*db.Wallet, error) { - walletDef, err := asset.WalletDef(assetID, form.Type) +func (c *Core) createDBWallet(crypter encrypt.Crypter, form *WalletForm, walletPW []byte) (*db.Wallet, error) { + walletDef, err := asset.WalletDef(form.AssetID, form.Type) if err != nil { return nil, newError(assetSupportErr, "asset.WalletDef error: %w", err) } @@ -2776,7 +2702,7 @@ func (c *Core) createWallet(crypter encrypt.Crypter, walletPW []byte, assetID ui if len(walletPW) > 0 { return nil, errors.New("external password incompatible with seeded wallet") } - walletPW, err = c.createSeededWallet(assetID, crypter, form) + walletPW, err = c.createSeededWallet(form.AssetID, crypter, form) if err != nil { return nil, err } @@ -2792,14 +2718,14 @@ func (c *Core) createWallet(crypter encrypt.Crypter, walletPW []byte, assetID ui return &db.Wallet{ Type: walletDef.Type, - AssetID: assetID, + AssetID: form.AssetID, Settings: form.Config, EncryptedPW: encPW, // Balance and Address are set after connect. }, nil } -func (c *Core) createTokenWallet(tokenID uint32, token *asset.Token, form *WalletForm) (*db.Wallet, error) { +func (c *Core) createTokenDBWallet(tokenID uint32, token *asset.Token, form *WalletForm) (*db.Wallet, error) { wallet, found := c.wallet(token.ParentID) if !found { return nil, fmt.Errorf("no parent wallet %d for token %d (%s)", token.ParentID, tokenID, unbip(tokenID)) @@ -2889,14 +2815,14 @@ func (c *Core) assetSeedAndPass(assetID uint32, crypter encrypt.Crypter) (seed, func AssetSeedAndPass(assetID uint32, appSeed []byte) ([]byte, []byte) { const accountBasedSeedAssetID = 60 // ETH seedAssetID := assetID - if ai, _ := asset.Info(assetID); ai != nil && ai.IsAccountBased { + if ai, _ := asset.Info(assetID); ai != nil && ai.BlockchainClass.IsEVM() { 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 { + if ai.BlockchainClass.IsEVM() { seedAssetID = accountBasedSeedAssetID } } @@ -2924,7 +2850,7 @@ func (c *Core) assetDataBackupDirectory(assetID uint32) string { // loadWallet uses the data from the database to construct a new exchange // wallet. The returned wallet is running but not connected. -func (c *Core) loadWallet(dbWallet *db.Wallet) (*xcWallet, error) { +func (c *Core) loadXCWallet(dbWallet *db.Wallet) (*xcWallet, error) { var parent *xcWallet assetID := dbWallet.AssetID @@ -2969,7 +2895,6 @@ func (c *Core) loadWallet(dbWallet *db.Wallet) (*xcWallet, error) { var w asset.Wallet var err error if token == nil { - walletCfg := &asset.WalletConfig{ Type: dbWallet.Type, Settings: dbWallet.Settings, @@ -3351,7 +3276,7 @@ func (c *Core) RecoverWallet(assetID uint32, appPW []byte, force bool) error { return fmt.Errorf("error creating wallet: %w", err) } - newWallet, err := c.loadWallet(dbWallet) + newWallet, err := c.loadXCWallet(dbWallet) if err != nil { return newError(walletErr, "error loading wallet for %d -> %s: %w", assetID, unbip(assetID), err) @@ -3716,7 +3641,7 @@ func (c *Core) ReconfigureWallet(appPW, newWalletPW []byte, form *WalletForm) er } // Reload the wallet with the new settings. - wallet, err := c.loadWallet(dbWallet) + wallet, err := c.loadXCWallet(dbWallet) if err != nil { return newError(walletErr, "error loading wallet for %d -> %s: %w", assetID, unbip(assetID), err) @@ -3819,7 +3744,7 @@ func (c *Core) ReconfigureWallet(appPW, newWalletPW []byte, form *WalletForm) er continue } tokenWallet.Disconnect() - tokenWallet, err = c.loadWallet(tokenDBWallet) + tokenWallet, err = c.loadXCWallet(tokenDBWallet) if err != nil { c.log.Errorf("Error loading wallet for token %s: %w", unbip(tokenID), err) continue @@ -7186,13 +7111,15 @@ func (c *Core) initialize() error { wg.Wait() c.log.Infof("Connected to %d of %d DEX servers", liveConns, len(accts)) + existingTokenWallets := make(map[uint32]bool) for _, dbWallet := range dbWallets { - if asset.Asset(dbWallet.AssetID) == nil && asset.TokenInfo(dbWallet.AssetID) == nil { + tkn := asset.TokenInfo(dbWallet.AssetID) + if asset.Asset(dbWallet.AssetID) == nil && tkn == nil { c.log.Infof("Wallet for asset %s no longer supported", dex.BipIDSymbol(dbWallet.AssetID)) continue } assetID := dbWallet.AssetID - wallet, err := c.loadWallet(dbWallet) + wallet, err := c.loadXCWallet(dbWallet) if err != nil { c.log.Errorf("error loading %d -> %s wallet: %v", assetID, unbip(assetID), err) continue @@ -7200,6 +7127,27 @@ func (c *Core) initialize() error { // Wallet is loaded from the DB, but not yet connected. c.log.Tracef("Loaded %s wallet configuration.", unbip(assetID)) c.updateWallet(assetID, wallet) + + if tkn != nil { + existingTokenWallets[dbWallet.AssetID] = true + } + } + + // Check for missing token wallets + for _, dbWallet := range dbWallets { + a := asset.Asset(dbWallet.AssetID) + if a == nil { + continue + } + for tokenID, tkn := range a.Tokens { + if existingTokenWallets[tokenID] { + continue + } + // Let's create the missing token wallet + if err := c.initializeTokenWallet(tokenID, tkn); err != nil { + c.log.Errorf("Couldn't create missing token wallet: %v", err) + } + } } // Check DB for active orders on any DEX. diff --git a/client/core/types.go b/client/core/types.go index f05a9f806e..0a9f3acb9f 100644 --- a/client/core/types.go +++ b/client/core/types.go @@ -86,14 +86,6 @@ type WalletForm struct { AssetID uint32 Config map[string]string Type string - // ParentForm is the configuration settings for a parent asset. If this is a - // token whose parent asset needs configuration, a non-nil ParentForm can be - // supplied. This will cause CreateWallet to run in a special mode which - // will create the parent asset's wallet synchronously, but schedule the - // creation of the token wallet to occur asynchronously after the parent - // wallet is fully synced, sending NoteTypeCreateWallet notifications to - // update with progress. - ParentForm *WalletForm } // WalletBalance is an exchange wallet's balance which includes various locked @@ -124,12 +116,12 @@ type WalletState struct { AssetID uint32 `json:"assetID"` Version uint32 `json:"version"` WalletType string `json:"type"` + Class asset.BlockchainClass `json:"class"` Traits asset.WalletTrait `json:"traits"` Open bool `json:"open"` Running bool `json:"running"` Balance *WalletBalance `json:"balance"` Address string `json:"address"` - Units string `json:"units"` Encrypted bool `json:"encrypted"` PeerCount uint32 `json:"peerCount"` Synced bool `json:"synced"` @@ -203,9 +195,6 @@ type SupportedAsset struct { // Token is only populated for token assets. Token *asset.Token `json:"token"` UnitInfo dex.UnitInfo `json:"unitInfo"` - // WalletCreationPending will be true if this wallet's parent wallet is - // being synced before this wallet is created. - WalletCreationPending bool `json:"walletCreationPending"` } // BondOptionsForm is used from the settings page to change the auto-bond diff --git a/client/core/wallet.go b/client/core/wallet.go index dd27454d41..eb9a8c3d7d 100644 --- a/client/core/wallet.go +++ b/client/core/wallet.go @@ -295,13 +295,13 @@ func (w *xcWallet) state() *WalletState { Running: w.connector.On(), Balance: w.balance, Address: w.address, - Units: winfo.UnitInfo.AtomicUnit, Encrypted: len(w.encPass) > 0, PeerCount: peerCount, Synced: w.syncStatus.Synced, SyncProgress: w.syncStatus.BlockProgress(), SyncStatus: w.syncStatus, WalletType: w.walletType, + Class: winfo.BlockchainClass, Traits: w.traits, Disabled: w.disabled, Approved: tokenApprovals, diff --git a/client/webserver/api.go b/client/webserver/api.go index 9dde92640e..1a8fb4a92d 100644 --- a/client/webserver/api.go +++ b/client/webserver/api.go @@ -461,20 +461,11 @@ func (s *WebServer) apiNewWallet(w http.ResponseWriter, r *http.Request) { return } defer zero(pass) - var parentForm *core.WalletForm - if f := form.ParentForm; f != nil { - parentForm = &core.WalletForm{ - AssetID: f.AssetID, - Config: f.Config, - Type: f.WalletType, - } - } // Wallet does not exist yet. Try to create it. err = s.core.CreateWallet(pass, form.Pass, &core.WalletForm{ - AssetID: form.AssetID, - Type: form.WalletType, - Config: form.Config, - ParentForm: parentForm, + AssetID: form.AssetID, + Type: form.WalletType, + Config: form.Config, }) if err != nil { s.writeAPIError(w, fmt.Errorf("error creating %s wallet: %w", unbip(form.AssetID), err)) diff --git a/client/webserver/jsintl.go b/client/webserver/jsintl.go index 1ac06d56f3..bc28f8c390 100644 --- a/client/webserver/jsintl.go +++ b/client/webserver/jsintl.go @@ -53,7 +53,6 @@ const ( changeWalletTypeID = "CHANGE_WALLET_TYPE" keepWalletTypeID = "KEEP_WALLET_TYPE" walletReadyID = "WALLET_READY" - walletPendingID = "WALLET_PENDING" setupNeededID = "SETUP_NEEDED" sendSuccessID = "SEND_SUCCESS" reconfigSuccessID = "RECONFIG_SUCCESS" @@ -274,7 +273,6 @@ var enUS = map[string]*intl.Translation{ changeWalletTypeID: {T: "change the wallet type"}, keepWalletTypeID: {T: "don't change the wallet type"}, setupNeededID: {T: "Setup Needed"}, - walletPendingID: {T: "Creating Wallet"}, sendSuccessID: {T: "{{ assetName }} Sent!"}, reconfigSuccessID: {T: "Wallet Reconfigured!"}, rescanStartedID: {T: "Wallet Rescan Running"}, @@ -548,7 +546,6 @@ var zhCN = map[string]*intl.Translation{ changeWalletTypeID: {T: "切换钱包类型"}, keepWalletTypeID: {T: "不要切换钱包类型"}, setupNeededID: {T: "需要设置"}, - walletPendingID: {T: "创建钱包"}, sendSuccessID: {T: "{{ assetName }} 发送!"}, reconfigSuccessID: {T: "钱包重置!"}, rescanStartedID: {T: "钱包扫描运行中"}, @@ -772,7 +769,6 @@ var plPL = map[string]*intl.Translation{ txTypeAccelerationID: {T: "Przyspieszenie"}, orderBttnQtyErrID: {T: "Ilość zamówień musi zostać określona."}, botTypeSimpleArbID: {T: "Prosty arbitraż"}, - walletPendingID: {T: "Tworzenie portfela"}, bondReservesID: {T: "Rezerwy kaucji"}, invalidValueID: {T: "nieprawidłowa wartość"}, disabledMsgID: {T: "portfel jest wyłączony"}, @@ -946,7 +942,6 @@ var deDE = map[string]*intl.Translation{ changeWalletTypeID: {T: "den Wallet-Typ ändern"}, keepWalletTypeID: {T: "den Wallet-Typ nicht ändern"}, setupNeededID: {T: "Einrichtung erforderlich"}, - walletPendingID: {T: "Erstelle Wallet"}, sendSuccessID: {T: "{{ assetName }} gesendet!"}, reconfigSuccessID: {T: "Wallet neu konfiguriert!"}, rescanStartedID: {T: "Wallet Rescan läuft"}, @@ -1159,7 +1154,6 @@ var ar = map[string]*intl.Translation{ keepWalletTypeID: {T: "لا تغير نوع المحفظة"}, walletReadyID: {T: "المحفظة جاهزة"}, setupNeededID: {T: "الإعداد مطلوب"}, - walletPendingID: {T: "إنشاء المحفظة"}, sendSuccessID: {T: "{{ assetName }} تم الإرسال!"}, reconfigSuccessID: {T: "تمت إعادة تهيئة المحفظة!!"}, rescanStartedID: {T: "إعادة فحص المحفظة قيد التشغيل"}, diff --git a/client/webserver/live_test.go b/client/webserver/live_test.go index 86aff69cad..01c8d4f8cc 100644 --- a/client/webserver/live_test.go +++ b/client/webserver/live_test.go @@ -57,10 +57,9 @@ const ( var ( tCtx context.Context - maxDelay = time.Second * 4 - epochDuration = time.Second * 30 // milliseconds - feedPeriod = time.Second * 10 - creationPendingAsset uint32 = 0xFFFFFFFF + maxDelay = time.Second * 4 + epochDuration = time.Second * 30 // milliseconds + feedPeriod = time.Second * 10 forceDisconnectWallet bool wipeWalletBalance bool gapWidthFactor = 1.0 // Should be 0 < gapWidthFactor <= 1.0 @@ -226,14 +225,13 @@ func mkSupportedAsset(symbol string, state *core.WalletState) *core.SupportedAss } return &core.SupportedAsset{ - ID: assetID, - Symbol: symbol, - Info: winfo, - Wallet: state, - Token: tinfos[assetID], - Name: name, - UnitInfo: unitInfo, - WalletCreationPending: assetID == atomic.LoadUint32(&creationPendingAsset), + ID: assetID, + Symbol: symbol, + Info: winfo, + Wallet: state, + Token: tinfos[assetID], + Name: name, + UnitInfo: unitInfo, } } @@ -1389,35 +1387,7 @@ func (c *TCore) CreateWallet(appPW, walletPW []byte, form *core.WalletForm) erro c.mtx.Lock() defer c.mtx.Unlock() - // If this is a token, simulate parent syncing. - token := asset.TokenInfo(form.AssetID) - if token == nil || form.ParentForm == nil { - c.createWallet(form, false) - return nil - } - - atomic.StoreUint32(&creationPendingAsset, form.AssetID) - - synced := c.createWallet(form.ParentForm, false) - - c.noteFeed <- &core.WalletCreationNote{ - Notification: db.NewNotification(core.NoteTypeCreateWallet, core.TopicCreationQueued, "", "", db.Data), - AssetID: form.AssetID, - } - - go func() { - <-synced - defer atomic.StoreUint32(&creationPendingAsset, 0xFFFFFFFF) - if doubleCreateAsyncErr { - c.noteFeed <- &core.WalletCreationNote{ - Notification: db.NewNotification(core.NoteTypeCreateWallet, core.TopicQueuedCreationFailed, - "Test Error", "This failed because doubleCreateAsyncErr is true in live_test.go", db.Data), - AssetID: form.AssetID, - } - return - } - c.createWallet(form, true) - }() + c.createWallet(form, false) return nil } diff --git a/client/webserver/locales/de-de.go b/client/webserver/locales/de-de.go index a7b122c1c2..0da68403ae 100644 --- a/client/webserver/locales/de-de.go +++ b/client/webserver/locales/de-de.go @@ -612,8 +612,6 @@ var DeDE = map[string]*intl.Translation{ "err_with_cex_creds": {T: "Bei der Verbindung mit diesen Anmeldedaten ist ein Fehler aufgetreten"}, "approve_token_wallet_addr": {T: ` Adresse:`}, "Available fee balance": {T: "Verfügbares Gebührenguthaben"}, - "add_provider_tooltip": {T: "Du verwendest öffentliche RPC-Anbieter. Diese Anbieter könnten veraltet oder unzuverlässig werden. Wenn möglich, konfiguriere stattdessen deine eigenen vertrauenswürdigen Anbieter an."}, - "add_custom_rpc_provider": {T: "RPC Anbieter hinzufügen"}, "Profit": {T: "Profit"}, "Inventory": {T: "Inventar"}, "Booked orders": {T: "Gebuchte Aufträge"}, diff --git a/client/webserver/locales/en-us.go b/client/webserver/locales/en-us.go index 0db0ae7c77..afcf08ae93 100644 --- a/client/webserver/locales/en-us.go +++ b/client/webserver/locales/en-us.go @@ -617,8 +617,6 @@ var EnUS = map[string]*intl.Translation{ "err_with_cex_creds": {T: "There was an error encountered connecting with these credentials"}, "approve_token_wallet_addr": {T: ` address:`}, "Available fee balance": {T: "Available fee balance"}, - "add_provider_tooltip": {T: "You are using the default set of public RPC providers. These providers could become outdated or unreliable. Specify your own trusted provider instead."}, - "add_custom_rpc_provider": {T: "Add a custom RPC provider"}, "Profit": {T: "Profit"}, "Inventory": {T: "Inventory"}, "Booked orders": {T: "Booked orders"}, diff --git a/client/webserver/locales/zh-cn.go b/client/webserver/locales/zh-cn.go index 5c6a6c1fb6..a0b0b68de6 100644 --- a/client/webserver/locales/zh-cn.go +++ b/client/webserver/locales/zh-cn.go @@ -612,8 +612,6 @@ var ZhCN = map[string]*intl.Translation{ "err_with_cex_creds": {T: "连接这些凭证时遇到错误"}, "approve_token_wallet_addr": {T: ` address: 地址:`}, "Available fee balance": {T: "可用费用余额"}, - "add_provider_tooltip": {T: "您正在使用默认的公共 RPC 提供商。这些提供商可能会过时或不可靠。请指定您信任的提供商。"}, - "add_custom_rpc_provider": {T: "添加自定义RPC提供商"}, "Profit": {T: "利润"}, "Inventory": {T: "库存"}, "Booked orders": {T: "已预定订单"}, diff --git a/client/webserver/middleware.go b/client/webserver/middleware.go index f028dfa43c..d5c92ab2ea 100644 --- a/client/webserver/middleware.go +++ b/client/webserver/middleware.go @@ -30,7 +30,7 @@ func (s *WebServer) securityMiddleware(next http.Handler) http.Handler { w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("Referrer-Policy", "no-referrer") w.Header().Set("Content-Security-Policy", s.csp) - w.Header().Set("Feature-Policy", "geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr 'self'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'none'; fullscreen 'self'; payment 'none'") + w.Header().Set("Permissions-Policy", "geolocation=(), midi=(), sync-xhr=(self), microphone=(), camera=(), magnetometer=(), gyroscope=(), fullscreen=(self), payment=()") next.ServeHTTP(w, r) }) } diff --git a/client/webserver/site/src/css/components.scss b/client/webserver/site/src/css/components.scss index af2a3b3d3e..4135709b44 100644 --- a/client/webserver/site/src/css/components.scss +++ b/client/webserver/site/src/css/components.scss @@ -29,11 +29,21 @@ button { font-size: .9rem; } + &.micro { + padding: 0.1rem 0.25rem; + font-size: .8rem; + } + &.large { padding: 0.5rem 1rem; font-size: 1.25rem; } + &.noborder { + border: none; + border-radius: 0; + } + &.feature { background-color: var(--btn-feature-bg); border-color: var(--btn-feature-border-color); @@ -150,6 +160,14 @@ table { background-color: #7772; } } + + &.no-bottom-border > tbody { + border-bottom-style: none; + } + + & > thead.unbold th { + font-weight: normal; + } } a { @@ -160,12 +178,6 @@ a { } } -@include media-breakpoint-up(md) { - table#walletInfoTable { - width: auto; - } -} - table.reg-asset-markets { @extend .stylish-overflow; diff --git a/client/webserver/site/src/css/main.scss b/client/webserver/site/src/css/main.scss index 60db707897..3ab1f8e529 100644 --- a/client/webserver/site/src/css/main.scss +++ b/client/webserver/site/src/css/main.scss @@ -216,7 +216,9 @@ z-index: 1000; } [data-unit-box] { - cursor: default; + @extend .hoverbg; + + cursor: pointer; position: relative; overflow: visible; diff --git a/client/webserver/site/src/css/wallets.scss b/client/webserver/site/src/css/wallets.scss index 3d14469964..a51e9c1062 100644 --- a/client/webserver/site/src/css/wallets.scss +++ b/client/webserver/site/src/css/wallets.scss @@ -1,4 +1,4 @@ -.walletspage { +[data-handler=wallets] { .ico-unlocked { color: var(--indicator-good); } @@ -110,18 +110,6 @@ #walletInfo { border-left: none; - - table#walletInfoTable { - td { - padding: 2px 5px 2px 0; - line-height: 1; - - &:last-child { - text-align: right; - padding-left: 1rem; - } - } - } } #earlierTxs, #txViewBlockExplorer { @@ -160,6 +148,18 @@ width: 18px; height: 18px; } + + &:not(.multinet) .multinetonly { + display: none !important; + } + + &.multinet .uninetonly { + display: none !important; + } + + &:not(.token) .tokenonly { + display: none !important; + } } .peers-table-icon { @@ -187,6 +187,13 @@ .negative-tx { color: $danger; } + + #docs { + .plainlink:visited, + .plainlink:hover { + text-decoration: none; + } + } } @include media-breakpoint-up(xl) { @@ -209,7 +216,7 @@ @include stylish-overflow; } - .walletspage { + [data-handler=wallets] { #walletDetailsBox { border-bottom: none !important; } @@ -225,7 +232,7 @@ } @include media-breakpoint-up(sm) { - .walletspage { + [data-handler=wallets] { #walletDetailsBox { #assetLogo { width: 40px; @@ -278,7 +285,7 @@ } @include media-breakpoint-up(md) { - .walletspage { + [data-handler=wallets] { #sendReceive { border-bottom: none; } diff --git a/client/webserver/site/src/html/docs.tmpl b/client/webserver/site/src/html/docs.tmpl new file mode 100644 index 0000000000..c0f200a298 --- /dev/null +++ b/client/webserver/site/src/html/docs.tmpl @@ -0,0 +1,212 @@ +{{define "dcrDocs"}} +
+
About Decred
+
+ Decred is a blockchain-based cryptocurrency with a strong focus on community input, open governance, and sustainable funding for development. + Decred uses a hybrid proof-of-work (PoW) / proof-of-stake (PoS) mining model that gives ultra-high security with a fraction of the resource requirements. + Decred's on-chain governance system is tied in to the PoS system, and includes the world's first fully-decentralized development treasury fully-controlled by the stakeholders. + Combined with an on-chain voting mechanism, this means that stakeholders have complete control over all blockchain upgrades and development funding. + This is an alignment of incentives that no other blockchain can boast. +
+
+ Website + Documentation +
+
+{{end}} + +{{define "btcDocs"}} +
+
About Bitcoin
+
+ Bitcoin is the first and most widely recognized cryptocurrency, created in 2009 by the pseudonymous developer Satoshi Nakamoto. + It operates on a decentralized, peer-to-peer network using blockchain technology, where transactions are recorded transparently and securely without the need for intermediaries. + Bitcoin relies on a proof-of-work (PoW) consensus mechanism, where miners validate transactions and secure the network by solving complex cryptographic puzzles. + With a fixed supply of 21 million coins, Bitcoin is often considered digital gold, serving as a store of value, hedge against inflation, and alternative to traditional fiat currencies. + Its decentralized nature and limited supply make it a key asset in the cryptocurrency and financial ecosystems. +
+
+ Introduction Video + Learn More +
+
+{{end}} + +{{define "ethDocs"}} +
+
About Ethereum
+
+ Ethereum is a decentralized, open-source blockchain platform that enables smart contracts and decentralized applications (dApps) to run without intermediaries. + Launched in 2015 by Vitalik Buterin and others, it introduced the Ethereum Virtual Machine (EVM), allowing developers to execute code on a global, distributed network. + Its native cryptocurrency, Ether (ETH), is used for transactions, staking, and gas fees that power computations. + Ethereum transitioned from a proof-of-work (PoW) to a proof-of-stake (PoS) consensus mechanism with the Merge in 2022, improving energy efficiency and security. + As a foundational layer for DeFi, NFTs, and other Web3 applications, Ethereum continues to evolve with scalability upgrades like rollups and sharding. +
+
+ Website + Documentation +
+
+{{end}} + +{{define "usdcDocs"}} +
+
About USDC
+
+ USDC (USD Coin) is a fully collateralized, fiat-backed stablecoin pegged 1:1 to the U.S. dollar, issued by Circle and governed by the Centre consortium. + It operates on multiple blockchains, including Ethereum, Polygon, and Base, and is widely used for payments, remittances, and decentralized finance (DeFi). + Each USDC token is backed by dollar-denominated reserves held in regulated financial institutions, with regular audits to ensure transparency. + Unlike algorithmic stablecoins, USDC maintains its peg through direct redemption mechanisms, allowing users to exchange it for USD at any time. + It is one of the most trusted stablecoins due to its regulatory compliance and transparency. +
+
+ Website +
+
+{{end}} + +{{define "usdtDocs"}} +
+
About USD Tether
+
+ USDT (Tether) is a widely used stablecoin pegged 1:1 to the U.S. dollar, issued by Tether Limited. + It operates on multiple blockchains, including Ethereum, Polygon, and Base, facilitating fast and low-cost transactions in crypto markets. + USDT is commonly used for trading, remittances, and decentralized finance (DeFi), offering liquidity and stability in the volatile crypto ecosystem. + Unlike fully-regulated stablecoins, Tether has faced scrutiny over its reserve transparency, though it claims to be backed by cash, cash equivalents, and other assets. + Despite regulatory concerns, USDT remains the most traded stablecoin and a dominant force in the digital asset space. +
+
+ Website +
+
+{{end}} + +{{define "ltcDocs"}} +
+
About Litecoin
+
+ Litecoin is a decentralized cryptocurrency created in 2011 by former Google engineer Charlie Lee as a faster and more lightweight alternative to Bitcoin. + It uses a proof-of-work (PoW) consensus mechanism with the Scrypt hashing algorithm, allowing for quicker block times (2.5 minutes vs. Bitcoin's 10 minutes) and lower transaction fees. + While often considered the "silver to Bitcoin's gold," Litecoin maintains strong network security and decentralization while offering improved scalability. + It has been a testing ground for innovations like Segregated Witness (SegWit) and the Lightning Network, which were later adopted by Bitcoin. + Despite facing competition from newer blockchains, Litecoin remains one of the most widely used and trusted cryptocurrencies. +
+
+ Website + Documentation +
+
+{{end}} + +{{define "dogeDocs"}} +
+
About Dogecoin
+
+ Dogecoin is a decentralized, open-source cryptocurrency created in 2013 by Billy Markus and Jackson Palmer as a lighthearted alternative to Bitcoin, featuring the Shiba Inu meme as its mascot. + Initially a joke, Dogecoin gained a strong community and became popular for tipping, charitable donations, and microtransactions due to its low fees and fast transaction speeds. + It operates on a proof-of-work (PoW) system similar to Litecoin, with an unlimited supply to encourage spending rather than hoarding. + Over time, Dogecoin has seen mainstream attention, including endorsements from figures like Elon Musk, and remains a widely recognized and actively traded cryptocurrency. +
+
+ Website + Documentation +
+
+{{end}} + +{{define "dashDocs"}} +
+
About Dash
+
+ Dash is a decentralized cryptocurrency launched in 2014 as a fork of Bitcoin, designed for fast, low-cost digital payments. + Dash features innovations like InstantSend, which enables near-instant transactions, and ChainLocks, which enhances security against 51% attacks. + It also has a unique two-tier network with masternodes that facilitate governance, private transactions (via PrivateSend), and network upgrades. + With a self-funding treasury system, Dash continues to evolve as a payment-focused cryptocurrency used for remittances and merchant adoption worldwide. +
+
+ Website + Documentation +
+
+{{end}} + +{{define "dgbDocs"}} +
+
About Digibyte
+
+ DigiByte is a decentralized, open-source cryptocurrency launched in 2014 by Jared Tate, designed to improve speed, security, and scalability compared to Bitcoin. + It features a multi-algorithm proof-of-work (PoW) consensus system, utilizing five different mining algorithms to enhance security and decentralization. + DigiByte also introduced DigiShield, an advanced difficulty adjustment mechanism that helps protect against mining fluctuations. + With 15-second block times, it offers significantly faster transactions than Bitcoin and many other cryptocurrencies. + Despite being a grassroots project without corporate backing, DigiByte has built a strong community and remains focused on decentralized digital payments and blockchain security applications. +
+
+ Website +
+
+{{end}} + +{{define "zecDocs"}} +
+
About Zcash
+
+ Zcash is a privacy-focused cryptocurrency launched in 2016, based on Bitcoin's code but with enhanced anonymity features. + It utilizes zero-knowledge proofs (zk-SNARKs) to enable fully private transactions while still being verifiable on the blockchain. + Users can choose between transparent and shielded addresses, allowing flexibility in privacy. + Zcash maintains a fixed supply of 21 million coins, similar to Bitcoin, and uses a proof-of-work (PoW) consensus mechanism for network security. + While it provides financial privacy, Zcash also supports compliance features for regulated institutions, making it one of the most advanced privacy coins in the cryptocurrency space. +
+
+ Website + Documentation +
+
+{{end}} + +{{define "firoDocs"}} +
+
About Firo
+
+ Firo, formerly known as Zcoin, is a privacy-focused cryptocurrency launched in 2016 that enhances transactional anonymity using advanced cryptographic techniques. + It originally implemented the Zerocoin protocol for privacy but later transitioned to Lelantus and Lelantus Spark, which provide trustless, on-chain privacy without the need for a trusted setup. + Firo uses a hybrid proof-of-work (PoW) and masternode system, which helps secure the network while enabling features like instant and private transactions. + With a strong emphasis on financial privacy and decentralization, Firo continues to be a leading privacy coin, balancing anonymity, usability, and security in the cryptocurrency ecosystem. +
+
+ Website +
+
+{{end}} + +{{define "bchDocs"}} +
+
About Bitcoin Cash
+
+ Bitcoin Cash is a decentralized cryptocurrency that emerged in 2017 as a hard fork of Bitcoin, aiming to improve scalability and transaction efficiency. + It increases the block size limit to 32MB, allowing for faster and cheaper transactions compared to Bitcoin's 1MB blocks. + Bitcoin Cash retains Bitcoin's proof-of-work (PoW) consensus mechanism but focuses on being a peer-to-peer electronic cash system, prioritizing usability for everyday payments. + Despite ideological and technical splits within its community—leading to further forks like Bitcoin SV (BSV)—BCH remains one of the most widely used cryptocurrencies for low-cost, fast transactions and merchant adoption. +
+
+ Website +
+
+{{end}} + +{{define "polDocs"}} +
+
About Polygon
+
+ Polygon is a layer-2 scaling solution for Ethereum that enhances transaction speed and reduces costs while maintaining security and decentralization. + Originally launched as Matic Network in 2017, it rebranded to Polygon in 2021, expanding its vision to become a multi-chain ecosystem supporting various scaling technologies, + including sidechains, rollups, and zero-knowledge (ZK) proofs. + Polygon enables developers to build and deploy scalable decentralized applications (dApps) with lower gas fees while leveraging Ethereum's robust security. + Its native token, POL, is used for governance, staking, and transaction fees. + With growing adoption in DeFi, gaming, and enterprise blockchain solutions, Polygon plays a key role in Ethereum's scalability and mass adoption. +
+
+ Website + Documentation +
+
+{{end}} diff --git a/client/webserver/site/src/html/forms.tmpl b/client/webserver/site/src/html/forms.tmpl index ed7f2a3433..7f1ed3a545 100644 --- a/client/webserver/site/src/html/forms.tmpl +++ b/client/webserver/site/src/html/forms.tmpl @@ -95,50 +95,49 @@
-
-
- [[[Synchronizing]]] . -
- -
- % -
-
- [[[wallet_wait_synced]]]. -
-
{{end}} {{define "depositAddress"}}
[[[Receive]]] - - + +
-
- [[[Token on]]] - - -
-
- -
-
-
- - - [[[copied]]] +
+
Select a Network
+
+
+
+
+ [[[Token on]]] + + +
+
+ +
+
+
+ + + [[[copied]]] +
+
-
-
-
-
- +
+
+
+
+ +
+
-
{{end}} {{define "certPicker"}} diff --git a/client/webserver/site/src/html/markets.tmpl b/client/webserver/site/src/html/markets.tmpl index 78915a9629..d990a1baab 100644 --- a/client/webserver/site/src/html/markets.tmpl +++ b/client/webserver/site/src/html/markets.tmpl @@ -414,12 +414,6 @@
-
- -
{{- /* REPUTATION */ -}} diff --git a/client/webserver/site/src/html/wallets.tmpl b/client/webserver/site/src/html/wallets.tmpl index 0ebc13ef2a..5ae6effda3 100644 --- a/client/webserver/site/src/html/wallets.tmpl +++ b/client/webserver/site/src/html/wallets.tmpl @@ -1,6 +1,6 @@ {{define "wallets"}} {{template "top" .}} -
+
@@ -58,201 +58,277 @@
{{- /* WALLET DETAILS */ -}} -