github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/dlc/contract.go (about) 1 package dlc 2 3 import ( 4 "fmt" 5 6 "github.com/mit-dci/lit/lnutil" 7 ) 8 9 const COINTYPE_NOT_SET = ^uint32(0) // Max Uint 10 11 // AddContract starts a new draft contract 12 func (mgr *DlcManager) AddContract() (*lnutil.DlcContract, error) { 13 var err error 14 15 c := new(lnutil.DlcContract) 16 c.Status = lnutil.ContractStatusDraft 17 c.CoinType = COINTYPE_NOT_SET 18 err = mgr.SaveContract(c) 19 if err != nil { 20 return nil, err 21 } 22 23 return c, nil 24 } 25 26 // SetContractOracle assigns a particular oracle to a contract - used for 27 // determining which pubkey A to use and can also allow for fetching R-points 28 // automatically when the oracle was imported from a REST api 29 func (mgr *DlcManager) SetContractOracle(cIdx, oIdx uint64) error { 30 31 c, err := mgr.LoadContract(cIdx) 32 if err != nil { 33 return err 34 } 35 36 if c.Status != lnutil.ContractStatusDraft { 37 return fmt.Errorf("You cannot change or set the oracle unless the" + 38 " contract is in Draft state") 39 } 40 41 o, err := mgr.LoadOracle(oIdx) 42 if err != nil { 43 return err 44 } 45 46 c.OracleA = o.A 47 48 // Reset the R point when changing the oracle 49 c.OracleR = [33]byte{} 50 51 mgr.SaveContract(c) 52 53 return nil 54 } 55 56 // SetContractSettlementTime sets the unix epoch at which the oracle will sign a 57 // message containing the value the contract pays out on. 58 func (mgr *DlcManager) SetContractSettlementTime(cIdx, time uint64) error { 59 c, err := mgr.LoadContract(cIdx) 60 if err != nil { 61 return err 62 } 63 64 if c.Status != lnutil.ContractStatusDraft { 65 return fmt.Errorf("You cannot change or set the settlement time" + 66 " unless the contract is in Draft state") 67 } 68 69 c.OracleTimestamp = time 70 71 // Reset the R point 72 c.OracleR = [33]byte{} 73 74 mgr.SaveContract(c) 75 76 return nil 77 } 78 79 // SetContractDatafeed will automatically fetch the R-point from the REST API, 80 // if an oracle is imported from a REST API. You need to set the settlement time 81 // first, because the R point is a key unique for the time and feed 82 func (mgr *DlcManager) SetContractDatafeed(cIdx, feed uint64) error { 83 c, err := mgr.LoadContract(cIdx) 84 if err != nil { 85 return err 86 } 87 88 if c.Status != lnutil.ContractStatusDraft { 89 return fmt.Errorf("You cannot change or set the Datafeed unless the" + 90 " contract is in Draft state") 91 } 92 93 if c.OracleTimestamp == 0 { 94 return fmt.Errorf("You need to set the settlement timestamp first," + 95 " otherwise no R point can be retrieved for the feed") 96 } 97 98 o, err := mgr.FindOracleByKey(c.OracleA) 99 if err != nil { 100 return err 101 } 102 103 c.OracleR, err = o.FetchRPoint(feed, c.OracleTimestamp) 104 if err != nil { 105 return err 106 } 107 108 err = mgr.SaveContract(c) 109 if err != nil { 110 return err 111 } 112 return nil 113 } 114 115 // SetContractRPoint allows you to manually set the R-point key if an oracle is 116 // not imported from a REST API 117 func (mgr *DlcManager) SetContractRPoint(cIdx uint64, rPoint [33]byte) error { 118 c, err := mgr.LoadContract(cIdx) 119 if err != nil { 120 return err 121 } 122 123 if c.Status != lnutil.ContractStatusDraft { 124 return fmt.Errorf("You cannot change or set the R-point unless the" + 125 " contract is in Draft state") 126 } 127 128 c.OracleR = rPoint 129 130 err = mgr.SaveContract(c) 131 if err != nil { 132 return err 133 } 134 135 return nil 136 } 137 138 // SetContractFunding sets the funding to the contract. It will specify how much 139 // we (the offering party) are funding, as well as 140 func (mgr *DlcManager) SetContractFunding(cIdx uint64, our, their int64) error { 141 c, err := mgr.LoadContract(cIdx) 142 if err != nil { 143 return err 144 } 145 146 if c.Status != lnutil.ContractStatusDraft { 147 return fmt.Errorf("You cannot change or set the funding unless the" + 148 " contract is in Draft state") 149 } 150 151 c.OurFundingAmount = our 152 c.TheirFundingAmount = their 153 154 // If the funding changes, the division needs to be re-set. 155 c.Division = nil 156 157 mgr.SaveContract(c) 158 159 return nil 160 } 161 162 // SetContractDivision sets the division of the contract settlement. If the 163 // oraclized value is valueAllOurs, then the entire value of the contract is 164 // payable to us. If the oraclized value is valueAllTheirs, then the entire 165 // value is paid to our peer. Between those, the value is divided linearly. 166 func (mgr *DlcManager) SetContractDivision(cIdx uint64, 167 valueAllOurs, valueAllTheirs int64) error { 168 c, err := mgr.LoadContract(cIdx) 169 if err != nil { 170 return err 171 } 172 173 if c.Status != lnutil.ContractStatusDraft { 174 return fmt.Errorf("You cannot change or set the division unless" + 175 " the contract is in Draft state") 176 } 177 178 rangeMin := valueAllTheirs - (valueAllOurs - valueAllTheirs) 179 rangeMax := valueAllOurs + (valueAllOurs - valueAllTheirs) 180 oursHighest := true 181 if valueAllTheirs > valueAllOurs { 182 oursHighest = false 183 rangeMin = valueAllOurs - (valueAllTheirs - valueAllOurs) 184 rangeMax = valueAllTheirs + (valueAllTheirs - valueAllOurs) 185 } 186 if rangeMin < 0 { 187 rangeMin = 0 188 } 189 190 totalContractValue := c.OurFundingAmount + c.TheirFundingAmount 191 fTotal := float64(totalContractValue) 192 fRange := float64(valueAllOurs - valueAllTheirs) 193 if !oursHighest { 194 fRange = float64(valueAllTheirs - valueAllOurs) 195 } 196 c.Division = make([]lnutil.DlcContractDivision, rangeMax-rangeMin+1) 197 for i := rangeMin; i <= rangeMax; i++ { 198 c.Division[i-rangeMin].OracleValue = i 199 200 if (oursHighest && i >= valueAllOurs) || 201 (!oursHighest && i <= valueAllOurs) { 202 c.Division[i-rangeMin].ValueOurs = totalContractValue 203 continue 204 } 205 206 if (oursHighest && i <= valueAllTheirs) || 207 (!oursHighest && i >= valueAllTheirs) { 208 c.Division[i-rangeMin].ValueOurs = 0 209 continue 210 } 211 212 idx := i - rangeMin 213 if oursHighest { 214 fCurInRange := float64(i - valueAllTheirs) 215 c.Division[idx].ValueOurs = int64(fTotal / fRange * fCurInRange) 216 } else { 217 fCurInRange := float64(i - valueAllOurs) 218 c.Division[idx].ValueOurs = int64(totalContractValue) 219 c.Division[idx].ValueOurs -= int64(fTotal / fRange * fCurInRange) 220 221 } 222 223 } 224 mgr.SaveContract(c) 225 226 return nil 227 } 228 229 // SetContractCoinType sets the cointype for a particular contract 230 func (mgr *DlcManager) SetContractCoinType(cIdx uint64, cointype uint32) error { 231 c, err := mgr.LoadContract(cIdx) 232 if err != nil { 233 return err 234 } 235 236 if c.Status != lnutil.ContractStatusDraft { 237 return fmt.Errorf("You cannot change or set the coin type unless" + 238 " the contract is in Draft state") 239 } 240 241 c.CoinType = cointype 242 243 mgr.SaveContract(c) 244 245 return nil 246 }