decred.org/dcrdex@v1.0.5/dex/asset.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 dex 5 6 import ( 7 "fmt" 8 "math" 9 "strconv" 10 "strings" 11 "time" 12 ) 13 14 const ( 15 UnsupportedScriptError = ErrorKind("unsupported script type") 16 17 defaultLockTimeTaker = 8 * time.Hour 18 defaultlockTimeMaker = 20 * time.Hour 19 20 secondsPerMinute int64 = 60 21 secondsPerDay = 24 * 60 * secondsPerMinute 22 BondExpiryMainnet = 30 * secondsPerDay // 30 days 23 BondExpiryTestnet = 180 * secondsPerMinute // 3 hours 24 BondExpirySimnet = 4 * secondsPerMinute // 4 minutes 25 ) 26 27 var ( 28 // These string variables are defined to enable setting custom locktime values 29 // that may be used instead of `DefaultLockTimeTaker` and `DefaultLockTimeMaker` 30 // IF running on a test network (testnet or regtest) AND both values are set to 31 // valid duration strings. 32 // Values for both variables may be set at build time using linker flags e.g: 33 // go build -ldflags "-X 'decred.org/dcrdex/dex.testLockTimeTaker=10m' \ 34 // -X 'decred.org/dcrdex/dex.testLockTimeMaker=20m'" 35 // Same values should be set when building server and client binaries. 36 testLockTimeTaker string 37 testLockTimeMaker string 38 39 testLockTime struct { 40 taker time.Duration 41 maker time.Duration 42 } 43 ) 44 45 // Set test locktime values on init. Panics if invalid or 0-second duration 46 // strings are provided. 47 func init() { 48 if testLockTimeTaker == "" && testLockTimeMaker == "" { 49 testLockTime.taker, testLockTime.maker = defaultLockTimeTaker, defaultlockTimeMaker 50 return 51 } 52 testLockTime.taker, _ = time.ParseDuration(testLockTimeTaker) 53 if testLockTime.taker.Seconds() == 0 { 54 panic(fmt.Sprintf("invalid value for testLockTimeTaker: %q", testLockTimeTaker)) 55 } 56 testLockTime.maker, _ = time.ParseDuration(testLockTimeMaker) 57 if testLockTime.maker.Seconds() == 0 { 58 panic(fmt.Sprintf("invalid value for testLockTimeMaker: %q", testLockTimeMaker)) 59 } 60 } 61 62 // LockTimeTaker returns the taker locktime value that should be used by both 63 // client and server for the specified network. Mainnet uses a constant value 64 // while test networks support setting a custom value during build. 65 func LockTimeTaker(network Network) time.Duration { 66 if network == Mainnet { 67 return defaultLockTimeTaker 68 } 69 return testLockTime.taker 70 } 71 72 // LockTimeMaker returns the maker locktime value that should be used by both 73 // client and server for the specified network. Mainnet uses a constant value 74 // while test networks support setting a custom value during build. 75 func LockTimeMaker(network Network) time.Duration { 76 if network == Mainnet { 77 return defaultlockTimeMaker 78 } 79 return testLockTime.maker 80 } 81 82 // BondExpiry returns the bond expiry duration in seconds for a given network. 83 func BondExpiry(net Network) int64 { 84 switch net { 85 case Mainnet: 86 return BondExpiryMainnet 87 case Testnet: 88 return BondExpiryTestnet 89 default: // Regtest, Simnet, other 90 return BondExpirySimnet 91 } 92 } 93 94 // Network flags passed to asset backends to signify which network to use. 95 type Network uint8 96 97 const ( 98 Mainnet Network = iota 99 Testnet 100 Regtest 101 ) 102 103 // Simnet is an alias of Regtest. 104 const Simnet = Regtest 105 106 // String returns the string representation of a Network. 107 func (n Network) String() string { 108 switch n { 109 case Mainnet: 110 return "mainnet" 111 case Testnet: 112 return "testnet" 113 case Simnet: 114 return "simnet" 115 } 116 return "" 117 } 118 119 // NetFromString returns the Network for the given network name. 120 func NetFromString(net string) (Network, error) { 121 switch strings.ToLower(net) { 122 case "mainnet": 123 return Mainnet, nil 124 case "testnet": 125 return Testnet, nil 126 case "regtest", "regnet", "simnet": 127 return Simnet, nil 128 } 129 return 255, fmt.Errorf("unknown network %s", net) 130 } 131 132 // Asset is the configurable asset variables. 133 type Asset struct { 134 ID uint32 `json:"id"` 135 Symbol string `json:"symbol"` 136 Version uint32 `json:"version"` 137 MaxFeeRate uint64 `json:"maxFeeRate"` 138 SwapConf uint32 `json:"swapConf"` 139 UnitInfo UnitInfo `json:"unitInfo"` 140 } 141 142 // Denomination is a unit and its conversion factor. 143 type Denomination struct { 144 Unit string `json:"unit"` 145 ConversionFactor uint64 `json:"conversionFactor"` 146 } 147 148 // UnitInfo conveys information about the units and available denominations for 149 // an asset. 150 type UnitInfo struct { 151 // AtomicUnit is the name associated with the asset's integral unit of 152 // measure, e.g. satoshis, atoms, gwei (for DEX purposes). 153 AtomicUnit string `json:"atomicUnit"` 154 // Conventional is the conventionally-used denomination. 155 Conventional Denomination `json:"conventional"` 156 // Alternatives lists additionally available Denominations, and can be 157 // empty. 158 Alternatives []Denomination `json:"denominations"` 159 // FeeRateDenom is the denominator used for fee rates. 160 FeeRateDenom string `json:"feeRateDenom"` 161 } 162 163 // ConventionalString converts the quantity to conventional units, and returns 164 // the formatted float string. 165 func (ui *UnitInfo) ConventionalString(v uint64) string { 166 c := ui.Conventional.ConversionFactor 167 prec := int(math.Round(math.Log10(float64(c)))) // Assumes integer powers of 10 168 return strconv.FormatFloat(float64(v)/float64(c), 'f', prec, 64) 169 } 170 171 // FormatAtoms formats the atomic value as a string with units. The number is 172 // formatted with full precision. For small values, the value is formatted in 173 // atomic units, while for larger values the value is formatted in conventional 174 // units. 175 func (ui *UnitInfo) FormatAtoms(v uint64) string { 176 const bestDisplayOrder = 1 177 conv := float64(v) / float64(ui.Conventional.ConversionFactor) 178 convDelta := uint(math.Abs(math.Floor(math.Log10(conv)) - bestDisplayOrder)) 179 atomicDelta := uint(math.Abs(math.Floor(math.Log10(float64(v))) - bestDisplayOrder)) 180 if convDelta <= atomicDelta { 181 prec := int(math.Round(math.Log10(float64(ui.Conventional.ConversionFactor)))) 182 return fmt.Sprintf("%s %s", strconv.FormatFloat(conv, 'f', prec, 64), ui.Conventional.Unit) 183 } 184 return fmt.Sprintf("%d %s", v, ui.AtomicUnit) 185 } 186 187 func (ui *UnitInfo) FormatSignedAtoms(v int64, plusSign ...bool) string { 188 if v >= 0 { 189 if len(plusSign) > 0 && plusSign[0] { 190 return "+" + ui.FormatAtoms(uint64(v)) 191 } 192 return ui.FormatAtoms(uint64(v)) 193 } 194 return "-" + ui.FormatAtoms(uint64(-v)) 195 } 196 197 // Token is a generic representation of a token-type asset. 198 type Token struct { 199 // ParentID is the asset ID of the token's parent asset. 200 ParentID uint32 `json:"parentID"` 201 // Name is the display name of the token asset. 202 Name string `json:"name"` 203 // UnitInfo is the UnitInfo for the token. 204 UnitInfo UnitInfo `json:"unitInfo"` 205 } 206 207 // IntDivUp divides two integers, rounding up, without using floating point 208 // conversion. This will panic if the denominator is zero, just like regular 209 // integer division. This function should be used instead of ad hoc solutions or 210 // being lazy with floating point math. 211 func IntDivUp(val, div int64) int64 { 212 // https://github.com/rust-lang/rust/blob/343889b7234bf786e2bc673029467052f22fca08/library/core/src/num/uint_macros.rs#L2061 213 q, rem := val/div, val%div 214 if (rem > 0 && div > 0) || (rem < 0 && div < 0) { 215 q++ 216 } 217 return q 218 // return (val + div - 1) / div 219 }