decred.org/dcrdex@v1.0.5/client/asset/doge/doge.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package doge 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "math" 11 "strconv" 12 13 "decred.org/dcrdex/client/asset" 14 "decred.org/dcrdex/client/asset/btc" 15 "decred.org/dcrdex/dex" 16 dexbtc "decred.org/dcrdex/dex/networks/btc" 17 dexdoge "decred.org/dcrdex/dex/networks/doge" 18 "github.com/btcsuite/btcd/chaincfg" 19 ) 20 21 const ( 22 version = 0 23 BipID = 3 24 25 dustLimit = 1_000_000 // sats => 0.01 DOGE, the "soft" limit (DEFAULT_DUST_LIMIT) 26 27 minNetworkVersion = 1140700 // v1.14.7.0-a6d122013 28 walletTypeRPC = "dogecoindRPC" 29 feeConfs = 10 30 ) 31 32 var ( 33 fallbackFeeKey = "fallbackfee" 34 configOpts = []*asset.ConfigOption{ 35 { 36 Key: "rpcuser", 37 DisplayName: "JSON-RPC Username", 38 Description: "Dogecoin's 'rpcuser' setting", 39 }, 40 { 41 Key: "rpcpassword", 42 DisplayName: "JSON-RPC Password", 43 Description: "Dogecoin's 'rpcpassword' setting", 44 NoEcho: true, 45 }, 46 { 47 Key: "rpcbind", 48 DisplayName: "JSON-RPC Address", 49 Description: "<addr> or <addr>:<port> (default 'localhost')", 50 }, 51 { 52 Key: "rpcport", 53 DisplayName: "JSON-RPC Port", 54 Description: "Port for RPC connections (if not set in Address)", 55 }, 56 { 57 Key: fallbackFeeKey, 58 DisplayName: "Fallback fee rate", 59 Description: "Dogecoin's 'fallbackfee' rate. Units: DOGE/kB", 60 DefaultValue: strconv.FormatFloat(dexdoge.DefaultFee*1000/1e8, 'f', -1, 64), 61 }, 62 { 63 Key: "feeratelimit", 64 DisplayName: "Highest acceptable fee rate", 65 Description: "This is the highest network fee rate you are willing to " + 66 "pay on swap transactions. If feeratelimit is lower than a market's " + 67 "maxfeerate, you will not be able to trade on that market with this " + 68 "wallet. Units: BTC/kB", 69 DefaultValue: strconv.FormatFloat(dexdoge.DefaultFeeRateLimit*1000/1e8, 'f', -1, 64), 70 }, 71 { 72 Key: "txsplit", 73 DisplayName: "Pre-split funding inputs", 74 Description: "When placing an order, create a \"split\" transaction to fund the order without locking more of the wallet balance than " + 75 "necessary. Otherwise, excess funds may be reserved to fund the order until the first swap contract is broadcast " + 76 "during match settlement, or the order is canceled. This an extra transaction for which network mining fees are paid. " + 77 "Used only for standing-type orders, e.g. limit orders without immediate time-in-force.", 78 IsBoolean: true, 79 }, 80 { 81 Key: "apifeefallback", 82 DisplayName: "External fee rate estimates", 83 Description: "Allow fee rate estimation from a block explorer API. " + 84 "This is useful as a fallback for SPV wallets and RPC wallets " + 85 "that have recently been started.", 86 IsBoolean: true, 87 DefaultValue: "true", 88 }, 89 } 90 // WalletInfo defines some general information about a Dogecoin wallet. 91 WalletInfo = &asset.WalletInfo{ 92 Name: "Dogecoin", 93 SupportedVersions: []uint32{version}, 94 UnitInfo: dexdoge.UnitInfo, 95 AvailableWallets: []*asset.WalletDefinition{{ 96 Type: walletTypeRPC, 97 Tab: "External", 98 Description: "Connect to dogecoind", 99 DefaultConfigPath: dexbtc.SystemConfigPath("dogecoin"), 100 ConfigOpts: configOpts, 101 }}, 102 } 103 ) 104 105 func init() { 106 asset.Register(BipID, &Driver{}) 107 } 108 109 // Driver implements asset.Driver. 110 type Driver struct{} 111 112 // Open creates the DOGE exchange wallet. Start the wallet with its Run method. 113 func (d *Driver) Open(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network) (asset.Wallet, error) { 114 return NewWallet(cfg, logger, network) 115 } 116 117 // DecodeCoinID creates a human-readable representation of a coin ID for 118 // Dogecoin. 119 func (d *Driver) DecodeCoinID(coinID []byte) (string, error) { 120 // Dogecoin and Bitcoin have the same tx hash and output format. 121 return (&btc.Driver{}).DecodeCoinID(coinID) 122 } 123 124 // Info returns basic information about the wallet and asset. 125 func (d *Driver) Info() *asset.WalletInfo { 126 return WalletInfo 127 } 128 129 // MinLotSize calculates the minimum bond size for a given fee rate that avoids 130 // dust outputs on the swap and refund txs, assuming the maxFeeRate doesn't 131 // change. 132 func (d *Driver) MinLotSize(maxFeeRate uint64) uint64 { 133 return dustLimit + dexbtc.RedeemSwapTxSize(false)*maxFeeRate 134 } 135 136 // NewWallet is the exported constructor by which the DEX will import the 137 // exchange wallet. The wallet will shut down when the provided context is 138 // canceled. The configPath can be an empty string, in which case the standard 139 // system location of the dogecoind config file is assumed. 140 func NewWallet(cfg *asset.WalletConfig, logger dex.Logger, network dex.Network) (asset.Wallet, error) { 141 var params *chaincfg.Params 142 switch network { 143 case dex.Mainnet: 144 params = dexdoge.MainNetParams 145 case dex.Testnet: 146 params = dexdoge.TestNet4Params 147 case dex.Regtest: 148 params = dexdoge.RegressionNetParams 149 default: 150 return nil, fmt.Errorf("unknown network ID %v", network) 151 } 152 153 // Designate the clone ports. These will be overwritten by any explicit 154 // settings in the configuration file. 155 ports := dexbtc.NetPorts{ 156 Mainnet: "22555", 157 Testnet: "44555", 158 Simnet: "18332", 159 } 160 cloneCFG := &btc.BTCCloneCFG{ 161 WalletCFG: cfg, 162 MinNetworkVersion: minNetworkVersion, 163 WalletInfo: WalletInfo, 164 Symbol: "doge", 165 Logger: logger, 166 Network: network, 167 ChainParams: params, 168 Ports: ports, 169 DefaultFallbackFee: dexdoge.DefaultFee, 170 DefaultFeeRateLimit: dexdoge.DefaultFeeRateLimit, 171 LegacyBalance: true, 172 Segwit: false, 173 InitTxSize: dexbtc.InitTxSize, 174 InitTxSizeBase: dexbtc.InitTxSizeBase, 175 OmitAddressType: true, 176 LegacySignTxRPC: true, 177 LegacyValidateAddressRPC: true, 178 BooleanGetBlockRPC: true, 179 SingularWallet: true, 180 UnlockSpends: true, 181 ConstantDustLimit: dustLimit, 182 FeeEstimator: estimateFee, 183 ExternalFeeEstimator: externalFeeRate, 184 BlockDeserializer: dexdoge.DeserializeBlock, 185 AssetID: BipID, 186 } 187 188 return btc.BTCCloneWallet(cloneCFG) 189 } 190 191 // NOTE: btc.(*baseWallet).feeRate calls the local and external fee estimators 192 // in sequence, applying the limits configured in baseWallet. 193 194 func estimateFee(ctx context.Context, cl btc.RawRequester, _ uint64) (uint64, error) { 195 confArg, err := json.Marshal(feeConfs) 196 if err != nil { 197 return 0, err 198 } 199 resp, err := cl.RawRequest(ctx, "estimatefee", []json.RawMessage{confArg}) 200 if err != nil { 201 return 0, err 202 } 203 var feeRate float64 204 err = json.Unmarshal(resp, &feeRate) 205 if err != nil { 206 return 0, err 207 } 208 if feeRate <= 0 { 209 return 0, nil 210 } 211 // estimatefee is f#$%ed 212 // https://github.com/decred/dcrdex/pull/1558#discussion_r850061882 213 if feeRate > dexdoge.DefaultFeeRateLimit/1e5 { 214 return dexdoge.DefaultFee, nil 215 } 216 return uint64(math.Round(feeRate * 1e5)), nil 217 } 218 219 // DRAFT TODO: Fee rate -1 for testnet. Just use mainnet? 220 var bitcoreFeeRate = btc.BitcoreRateFetcher("DOGE") 221 222 // externalFeeRate returns a fee rate for the network. If an error is 223 // encountered fetching the testnet fee rate, we will try to return the 224 // mainnet fee rate. 225 func externalFeeRate(ctx context.Context, net dex.Network) (uint64, error) { 226 feeRate, err := bitcoreFeeRate(ctx, net) 227 if err == nil || net != dex.Testnet { 228 return feeRate, err 229 } 230 return bitcoreFeeRate(ctx, dex.Mainnet) 231 }