mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-19 05:43:51 +01:00
2021 06 07 dlc wallet pnl (#3229)
* WIP * Add profit and loss and rate of return for entire wallet * Fix rebase * Address part 1 of code review from Ben * Add unit test for wallet accounting
This commit is contained in:
parent
7ba7f8b9ba
commit
9431be2f25
@ -0,0 +1,10 @@
|
||||
package org.bitcoins.commons.serializers
|
||||
|
||||
object PicklerKeys {
|
||||
final val myCollateral: String = "myCollateral"
|
||||
final val theirCollateral: String = "theirCollateral"
|
||||
final val myPayout: String = "myPayout"
|
||||
final val theirPayout: String = "theirPayout"
|
||||
final val pnl: String = "pnl"
|
||||
final val rateOfReturn: String = "rateOfReturn"
|
||||
}
|
@ -9,6 +9,7 @@ import org.bitcoins.core.crypto.{
|
||||
MnemonicCode
|
||||
}
|
||||
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
|
||||
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCStatus._
|
||||
@ -260,10 +261,7 @@ object Picklers {
|
||||
)
|
||||
}
|
||||
|
||||
private val myPayoutKey: String = "myPayout"
|
||||
private val counterPartyPayoutKey: String = "counterPartyPayout"
|
||||
private val pnlKey: String = "pnl"
|
||||
private val rateOfReturnKey: String = "rateOfReturn"
|
||||
|
||||
implicit val claimedW: Writer[Claimed] = writer[Obj].comap { claimed =>
|
||||
import claimed._
|
||||
@ -296,11 +294,11 @@ object Picklers {
|
||||
"oracleSigs" -> oracleSigs.map(sig => Str(sig.hex)),
|
||||
"outcomes" -> outcomesJs,
|
||||
"oracles" -> oraclesJs,
|
||||
myPayoutKey -> Num(claimed.myPayout.satoshis.toLong.toDouble),
|
||||
PicklerKeys.myPayout -> Num(claimed.myPayout.satoshis.toLong.toDouble),
|
||||
counterPartyPayoutKey -> Num(
|
||||
claimed.counterPartyPayout.satoshis.toLong.toDouble),
|
||||
pnlKey -> Num(claimed.pnl.satoshis.toLong.toDouble),
|
||||
rateOfReturnKey -> Num(claimed.rateOfReturn.toDouble)
|
||||
PicklerKeys.pnl -> Num(claimed.pnl.satoshis.toLong.toDouble),
|
||||
PicklerKeys.rateOfReturn -> Num(claimed.rateOfReturn.toDouble)
|
||||
)
|
||||
}
|
||||
|
||||
@ -336,11 +334,12 @@ object Picklers {
|
||||
"oracleSigs" -> oracleSigs.map(sig => Str(sig.hex)),
|
||||
"outcomes" -> outcomesJs,
|
||||
"oracles" -> oraclesJs,
|
||||
myPayoutKey -> Num(remoteClaimed.myPayout.satoshis.toLong.toDouble),
|
||||
PicklerKeys.myPayout -> Num(
|
||||
remoteClaimed.myPayout.satoshis.toLong.toDouble),
|
||||
counterPartyPayoutKey -> Num(
|
||||
remoteClaimed.counterPartyPayout.satoshis.toLong.toDouble),
|
||||
pnlKey -> Num(remoteClaimed.pnl.satoshis.toLong.toDouble),
|
||||
rateOfReturnKey -> Num(remoteClaimed.rateOfReturn.toDouble)
|
||||
PicklerKeys.pnl -> Num(remoteClaimed.pnl.satoshis.toLong.toDouble),
|
||||
PicklerKeys.rateOfReturn -> Num(remoteClaimed.rateOfReturn.toDouble)
|
||||
)
|
||||
}
|
||||
|
||||
@ -363,11 +362,11 @@ object Picklers {
|
||||
"remoteCollateral" -> Num(remoteCollateral.satoshis.toLong.toDouble),
|
||||
"fundingTxId" -> Str(fundingTxId.hex),
|
||||
"closingTxId" -> Str(closingTxId.hex),
|
||||
myPayoutKey -> Num(refunded.myPayout.satoshis.toLong.toDouble),
|
||||
PicklerKeys.myPayout -> Num(refunded.myPayout.satoshis.toLong.toDouble),
|
||||
counterPartyPayoutKey -> Num(
|
||||
refunded.counterPartyPayout.satoshis.toLong.toDouble),
|
||||
pnlKey -> Num(refunded.pnl.satoshis.toLong.toDouble),
|
||||
rateOfReturnKey -> Num(refunded.rateOfReturn.toDouble)
|
||||
PicklerKeys.pnl -> Num(refunded.pnl.satoshis.toLong.toDouble),
|
||||
PicklerKeys.rateOfReturn -> Num(refunded.rateOfReturn.toDouble)
|
||||
)
|
||||
}
|
||||
|
||||
@ -441,7 +440,7 @@ object Picklers {
|
||||
throw new IllegalArgumentException(s"Unexpected outcome $signed")
|
||||
}
|
||||
|
||||
lazy val myPayoutJs = obj(myPayoutKey)
|
||||
lazy val myPayoutJs = obj(PicklerKeys.myPayout)
|
||||
lazy val myPayoutOpt = myPayoutJs.numOpt.map(sats => Satoshis(sats.toLong))
|
||||
lazy val theirPayoutJs = obj(counterPartyPayoutKey)
|
||||
lazy val theirPayoutOpt =
|
||||
@ -566,6 +565,23 @@ object Picklers {
|
||||
}
|
||||
}
|
||||
|
||||
implicit val dlcWalletAccountingWriter: Writer[DLCWalletAccounting] = {
|
||||
writer[Obj].comap { walletAccounting: DLCWalletAccounting =>
|
||||
Obj(
|
||||
PicklerKeys.myCollateral -> Num(
|
||||
walletAccounting.myCollateral.satoshis.toLong.toDouble),
|
||||
PicklerKeys.theirCollateral -> Num(
|
||||
walletAccounting.theirCollateral.satoshis.toLong.toDouble),
|
||||
PicklerKeys.myPayout -> Num(
|
||||
walletAccounting.myPayout.satoshis.toLong.toDouble),
|
||||
PicklerKeys.theirPayout -> Num(
|
||||
walletAccounting.theirPayout.satoshis.toLong.toDouble),
|
||||
PicklerKeys.pnl -> Num(walletAccounting.pnl.satoshis.toLong.toDouble),
|
||||
PicklerKeys.rateOfReturn -> Num(walletAccounting.rateOfReturn.toDouble)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
implicit val mnemonicCodePickler: ReadWriter[MnemonicCode] =
|
||||
readwriter[String].bimap(
|
||||
_.words.mkString(" "),
|
||||
|
@ -1729,6 +1729,8 @@ object ConsoleCli {
|
||||
case ZipDataDir(path) =>
|
||||
RequestParam("zipdatadir", Seq(up.writeJs(path)))
|
||||
|
||||
case GetDLCWalletAccounting =>
|
||||
RequestParam("getdlcwalletaccounting")
|
||||
case GetVersion =>
|
||||
// skip sending to server and just return version number of cli
|
||||
return Success(EnvUtil.getVersion)
|
||||
@ -1995,6 +1997,7 @@ object CliCommand {
|
||||
case class GetUnconfirmedBalance(isSats: Boolean) extends AppServerCliCommand
|
||||
case class GetBalances(isSats: Boolean) extends AppServerCliCommand
|
||||
case class GetAddressInfo(address: BitcoinAddress) extends AppServerCliCommand
|
||||
case object GetDLCWalletAccounting extends AppServerCliCommand
|
||||
|
||||
case class GetTransaction(txId: DoubleSha256DigestBE)
|
||||
extends AppServerCliCommand
|
||||
|
@ -13,6 +13,9 @@ object GlobalData {
|
||||
val currentReservedBalance: StringProperty = StringProperty("0")
|
||||
val currentTotalBalance: StringProperty = StringProperty("0")
|
||||
|
||||
val currentPNL: StringProperty = StringProperty("0")
|
||||
val rateOfReturn: StringProperty = StringProperty("0%")
|
||||
|
||||
val syncHeight: StringProperty = StringProperty("Syncing headers...")
|
||||
|
||||
var network: BitcoinNetwork = _
|
||||
|
@ -30,28 +30,35 @@ abstract class WalletGUI {
|
||||
dlcPane.model.setUp()
|
||||
}
|
||||
|
||||
private val satsProperty = StringProperty(" sats")
|
||||
|
||||
private lazy val confirmedText = new Label() {
|
||||
text <== StringProperty(
|
||||
"Confirmed balance:\t\t") + GlobalData.currentConfirmedBalance + StringProperty(
|
||||
" sats")
|
||||
"Confirmed balance:\t\t") + GlobalData.currentConfirmedBalance + satsProperty
|
||||
}
|
||||
|
||||
private lazy val unconfirmedText = new Label() {
|
||||
text <== StringProperty(
|
||||
"Unconfirmed balance:\t") + GlobalData.currentUnconfirmedBalance + StringProperty(
|
||||
" sats")
|
||||
"Unconfirmed balance:\t") + GlobalData.currentUnconfirmedBalance + satsProperty
|
||||
}
|
||||
|
||||
private lazy val reservedText = new Label() {
|
||||
text <== StringProperty(
|
||||
"Reserved balance:\t\t") + GlobalData.currentReservedBalance + StringProperty(
|
||||
" sats")
|
||||
"Reserved balance:\t\t") + GlobalData.currentReservedBalance + satsProperty
|
||||
}
|
||||
|
||||
private lazy val totalBalanceText = new Label() {
|
||||
text <== StringProperty(
|
||||
"Total balance:\t\t\t") + GlobalData.currentTotalBalance + StringProperty(
|
||||
" sats")
|
||||
"Total balance:\t\t\t") + GlobalData.currentTotalBalance + satsProperty
|
||||
}
|
||||
|
||||
private lazy val pnlText = new Label() {
|
||||
text <== StringProperty(
|
||||
"Profit and Loss:\t\t\t") + GlobalData.currentPNL + satsProperty
|
||||
}
|
||||
|
||||
private lazy val rateOfReturnText = new Label() {
|
||||
text <== StringProperty("Rate of Return:\t\t\t") + GlobalData.rateOfReturn
|
||||
}
|
||||
|
||||
private[gui] lazy val dlcPane = new DLCPane(glassPane)
|
||||
@ -66,6 +73,11 @@ abstract class WalletGUI {
|
||||
totalBalanceText)
|
||||
}
|
||||
|
||||
private lazy val walletAccountingBox = new VBox {
|
||||
spacing = 10
|
||||
children = Vector(pnlText, rateOfReturnText)
|
||||
}
|
||||
|
||||
private lazy val getNewAddressButton = new Button {
|
||||
text = "Get New Address"
|
||||
onAction = _ => model.onGetNewAddress()
|
||||
@ -84,7 +96,8 @@ abstract class WalletGUI {
|
||||
sendButton.prefWidth <== width
|
||||
getNewAddressButton.maxWidth = 300
|
||||
sendButton.maxWidth = 300
|
||||
children = Vector(balanceBox, getNewAddressButton, sendButton)
|
||||
children =
|
||||
Vector(balanceBox, walletAccountingBox, getNewAddressButton, sendButton)
|
||||
}
|
||||
|
||||
lazy val bottomStack: StackPane = new StackPane() {
|
||||
|
@ -1,9 +1,12 @@
|
||||
package org.bitcoins.gui
|
||||
|
||||
import akka.actor.{ActorSystem, Cancellable}
|
||||
import grizzled.slf4j.Logging
|
||||
import org.bitcoins.cli.CliCommand._
|
||||
import org.bitcoins.cli.ConsoleCli
|
||||
import org.bitcoins.commons.serializers.PicklerKeys
|
||||
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
|
||||
import org.bitcoins.core.dlc.accounting.RateOfReturnUtil
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||
import org.bitcoins.gui.dialog._
|
||||
@ -19,7 +22,8 @@ import scala.concurrent.duration.DurationInt
|
||||
import scala.concurrent.{Await, Promise}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class WalletGUIModel(dlcModel: DLCPaneModel)(implicit system: ActorSystem) {
|
||||
class WalletGUIModel(dlcModel: DLCPaneModel)(implicit system: ActorSystem)
|
||||
extends Logging {
|
||||
var taskRunner: TaskRunner = _
|
||||
import system.dispatcher
|
||||
|
||||
@ -33,6 +37,7 @@ class WalletGUIModel(dlcModel: DLCPaneModel)(implicit system: ActorSystem) {
|
||||
override def run(): Unit = {
|
||||
Platform.runLater {
|
||||
updateBalance()
|
||||
updateWalletAccounting()
|
||||
updateWalletInfo()
|
||||
dlcModel.updateDLCs()
|
||||
}
|
||||
@ -151,4 +156,19 @@ class WalletGUIModel(dlcModel: DLCPaneModel)(implicit system: ActorSystem) {
|
||||
}.showAndWait()
|
||||
}
|
||||
}
|
||||
|
||||
private def updateWalletAccounting(): Unit = {
|
||||
ConsoleCli.exec(GetDLCWalletAccounting, GlobalData.consoleCliConfig) match {
|
||||
case Failure(err) =>
|
||||
logger.error(s"Error fetching accounting", err)
|
||||
case Success(commandReturn) =>
|
||||
val json = ujson.read(commandReturn).obj
|
||||
val pnl = json(PicklerKeys.pnl).num.toLong.toString
|
||||
val rateOfReturn = json(PicklerKeys.rateOfReturn).num
|
||||
val rorPrettyPrint = RateOfReturnUtil.prettyPrint(rateOfReturn)
|
||||
GlobalData.currentPNL.value = pnl
|
||||
GlobalData.rateOfReturn.value = rorPrettyPrint
|
||||
()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import org.bitcoins.core.api.wallet.{AddressInfo, CoinSelectionAlgo}
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.number.{UInt32, UInt64}
|
||||
import org.bitcoins.core.protocol.BlockStamp.{
|
||||
@ -1705,5 +1706,27 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
}
|
||||
|
||||
"get wallet accounting" in {
|
||||
val accounting = DLCWalletAccounting(myCollateral = Satoshis.one,
|
||||
theirCollateral = Satoshis.one,
|
||||
myPayout = Satoshis(2),
|
||||
theirPayout = Satoshis.zero)
|
||||
|
||||
(mockWalletApi.getWalletAccounting: () => Future[DLCWalletAccounting])
|
||||
.expects()
|
||||
.returning(Future.successful(accounting))
|
||||
|
||||
val route = walletRoutes.handleCommand(
|
||||
ServerCommand("getdlcwalletaccounting", Arr()))
|
||||
|
||||
Get() ~> route ~> check {
|
||||
assert(contentType == `application/json`)
|
||||
val str = responseAs[String]
|
||||
val expected =
|
||||
s"""{"result":{"myCollateral":1,"theirCollateral":1,"myPayout":2,"theirPayout":0,"pnl":1,"rateOfReturn":1},"error":null}""".stripMargin
|
||||
assert(str == expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -794,6 +794,13 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
|
||||
Server.httpSuccess(fee.toString)
|
||||
}
|
||||
}
|
||||
|
||||
case ServerCommand("getdlcwalletaccounting", _) =>
|
||||
complete {
|
||||
wallet.getWalletAccounting().map { accounting =>
|
||||
Server.httpSuccess(writeJs(accounting))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns information about the state of our wallet */
|
||||
|
@ -21,7 +21,7 @@ class DLCAccountingTest extends BitcoinSUnitTest {
|
||||
|
||||
//we make 50,000 sats (their collateral) is the profit
|
||||
assert(accounting1.pnl == theirCollateral)
|
||||
assert(accounting1.rorPrettyPrint == "100%")
|
||||
assert(accounting1.rorPrettyPrint == "100.00%")
|
||||
}
|
||||
|
||||
it must "calculate basic pnl where we lose all funds" in {
|
||||
@ -30,16 +30,16 @@ class DLCAccountingTest extends BitcoinSUnitTest {
|
||||
|
||||
val accounting1 = DLCAccounting(
|
||||
dlcId = Sha256Digest.empty,
|
||||
myCollateral = Satoshis(50000),
|
||||
theirCollateral = Satoshis(50000),
|
||||
myCollateral = myCollateral,
|
||||
theirCollateral = theirCollateral,
|
||||
myPayout = Satoshis.zero,
|
||||
theirPayout = myCollateral + theirCollateral
|
||||
)
|
||||
|
||||
//we lose 50,000 sats (my collateral) is the loss
|
||||
assert(accounting1.pnl == Satoshis(-50000))
|
||||
assert(accounting1.pnl == -myCollateral)
|
||||
assert(accounting1.rateOfReturn == -1)
|
||||
assert(accounting1.rorPrettyPrint == "-100%")
|
||||
assert(accounting1.rorPrettyPrint == "-100.00%")
|
||||
}
|
||||
|
||||
it must "calculate basic pnl where funds are refunded" in {
|
||||
@ -48,8 +48,8 @@ class DLCAccountingTest extends BitcoinSUnitTest {
|
||||
|
||||
val accounting1 = DLCAccounting(
|
||||
dlcId = Sha256Digest.empty,
|
||||
myCollateral = Satoshis(50000),
|
||||
theirCollateral = Satoshis(50000),
|
||||
myCollateral = myCollateral,
|
||||
theirCollateral = theirCollateral,
|
||||
myPayout = myCollateral,
|
||||
theirPayout = theirCollateral
|
||||
)
|
||||
@ -57,6 +57,6 @@ class DLCAccountingTest extends BitcoinSUnitTest {
|
||||
//collateral refunded, so no pnl
|
||||
assert(accounting1.pnl == Satoshis.zero)
|
||||
assert(accounting1.rateOfReturn == 0)
|
||||
assert(accounting1.rorPrettyPrint == "0%")
|
||||
assert(accounting1.rorPrettyPrint == "0.00%")
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package org.bitcoins.core.dlc.accounting
|
||||
|
||||
import org.bitcoins.testkitcore.util.BitcoinSUnitTest
|
||||
|
||||
class RateOfReturnUtilTest extends BitcoinSUnitTest {
|
||||
behavior of "RateOfReturnUtil"
|
||||
|
||||
it must "pretty print strings with percentages correctly" in {
|
||||
RateOfReturnUtil.prettyPrint(0) must be("0.00%")
|
||||
RateOfReturnUtil.prettyPrint(-1) must be("-100.00%")
|
||||
RateOfReturnUtil.prettyPrint(1) must be("100.00%")
|
||||
RateOfReturnUtil.prettyPrint(1.23) must be("123.00%")
|
||||
RateOfReturnUtil.prettyPrint(1.23456) must be("123.46%")
|
||||
RateOfReturnUtil.prettyPrint(1.23454) must be("123.45%")
|
||||
}
|
||||
}
|
@ -8,19 +8,5 @@ case class DLCAccounting(
|
||||
myCollateral: CurrencyUnit,
|
||||
theirCollateral: CurrencyUnit,
|
||||
myPayout: CurrencyUnit,
|
||||
theirPayout: CurrencyUnit) {
|
||||
|
||||
/** Profit and loss for the DLC
|
||||
* @see https://www.investopedia.com/terms/p/plstatement.asp
|
||||
*/
|
||||
val pnl: CurrencyUnit = myPayout - myCollateral
|
||||
|
||||
/** Rate of return for the DLC
|
||||
* @see https://www.investopedia.com/terms/r/rateofreturn.asp
|
||||
*/
|
||||
val rateOfReturn: BigDecimal = pnl.toBigDecimal / myCollateral.toBigDecimal
|
||||
|
||||
val rorPrettyPrint: String = {
|
||||
RateOfReturnUtil.prettyPrint(rateOfReturn)
|
||||
}
|
||||
}
|
||||
theirPayout: CurrencyUnit)
|
||||
extends PayoutAccounting
|
||||
|
@ -0,0 +1,30 @@
|
||||
package org.bitcoins.core.dlc.accounting
|
||||
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits}
|
||||
|
||||
/** Similar to [[org.bitcoins.core.dlc.accounting.DLCAccounting]], but
|
||||
* represents the entire accounting for the wallet
|
||||
*/
|
||||
case class DLCWalletAccounting(
|
||||
myCollateral: CurrencyUnit,
|
||||
theirCollateral: CurrencyUnit,
|
||||
myPayout: CurrencyUnit,
|
||||
theirPayout: CurrencyUnit)
|
||||
extends PayoutAccounting
|
||||
|
||||
object DLCWalletAccounting {
|
||||
|
||||
def fromDLCAccounting(
|
||||
accountings: Vector[DLCAccounting]): DLCWalletAccounting = {
|
||||
val myCollateral =
|
||||
accountings.foldLeft(CurrencyUnits.zero)(_ + _.myCollateral)
|
||||
val theirCollateral =
|
||||
accountings.foldLeft(CurrencyUnits.zero)(_ + _.theirCollateral)
|
||||
|
||||
val myPayouts = accountings.foldLeft(CurrencyUnits.zero)(_ + _.myPayout)
|
||||
val theirPayouts =
|
||||
accountings.foldLeft(CurrencyUnits.zero)(_ + _.theirPayout)
|
||||
|
||||
DLCWalletAccounting(myCollateral, theirCollateral, myPayouts, theirPayouts)
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.bitcoins.core.dlc.accounting
|
||||
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
|
||||
/** Utility trait for metrics we need to do accounting */
|
||||
trait PayoutAccounting {
|
||||
def myCollateral: CurrencyUnit
|
||||
|
||||
def theirCollateral: CurrencyUnit
|
||||
|
||||
def myPayout: CurrencyUnit
|
||||
|
||||
def theirPayout: CurrencyUnit
|
||||
|
||||
/** Profit and loss for the DLC
|
||||
* @see https://www.investopedia.com/terms/p/plstatement.asp
|
||||
*/
|
||||
def pnl: CurrencyUnit = myPayout - myCollateral
|
||||
|
||||
/** Rate of return for the DLC
|
||||
* @see https://www.investopedia.com/terms/r/rateofreturn.asp
|
||||
*/
|
||||
def rateOfReturn: BigDecimal = pnl.toBigDecimal / myCollateral.toBigDecimal
|
||||
|
||||
def rorPrettyPrint: String = {
|
||||
RateOfReturnUtil.prettyPrint(rateOfReturn)
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@ package org.bitcoins.core.dlc.accounting
|
||||
|
||||
object RateOfReturnUtil {
|
||||
|
||||
/** @see https://alvinalexander.com/scala/how-to-format-numbers-commas-international-currency-in-scala/ */
|
||||
def prettyPrint(ror: BigDecimal): String = {
|
||||
(ror * 100).toString() + "%"
|
||||
val percent = ror * 100
|
||||
f"${percent}%1.2f" + "%"
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ sealed trait ClosedDLCStatus extends BroadcastedDLCStatus {
|
||||
def myPayout: CurrencyUnit
|
||||
def counterPartyPayout: CurrencyUnit
|
||||
|
||||
private def accounting: DLCAccounting = {
|
||||
def accounting: DLCAccounting = {
|
||||
DLCAccounting(dlcId,
|
||||
localCollateral,
|
||||
remoteCollateral,
|
||||
|
@ -7,6 +7,7 @@ import org.bitcoins.core.api.wallet.db._
|
||||
import org.bitcoins.core.config.BitcoinNetwork
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.number._
|
||||
import org.bitcoins.core.protocol._
|
||||
@ -1213,6 +1214,18 @@ abstract class DLCWallet
|
||||
} yield refundTx
|
||||
}
|
||||
|
||||
override def getWalletAccounting(): Future[DLCWalletAccounting] = {
|
||||
val dlcsF = listDLCs()
|
||||
for {
|
||||
dlcs <- dlcsF
|
||||
closed = dlcs.collect { case c: ClosedDLCStatus =>
|
||||
c
|
||||
} //only get closed dlcs for accounting
|
||||
accountings = closed.map(_.accounting)
|
||||
walletAccounting = DLCWalletAccounting.fromDLCAccounting(accountings)
|
||||
} yield walletAccounting
|
||||
}
|
||||
|
||||
override def listDLCs(): Future[Vector[DLCStatus]] = {
|
||||
for {
|
||||
ids <- dlcDAO.findAll().map(_.map(_.dlcId))
|
||||
|
@ -2,6 +2,7 @@ package org.bitcoins.dlc.wallet
|
||||
|
||||
import org.bitcoins.core.api.wallet._
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.{
|
||||
DLCAccept,
|
||||
@ -99,6 +100,9 @@ trait DLCWalletApi { self: WalletApi =>
|
||||
def findDLC(dlcId: Sha256Digest): Future[Option[DLCStatus]]
|
||||
|
||||
def cancelDLC(dlcId: Sha256Digest): Future[Unit]
|
||||
|
||||
/** Retrieves accounting and financial metrics for the entire dlc wallet */
|
||||
def getWalletAccounting(): Future[DLCWalletAccounting]
|
||||
}
|
||||
|
||||
/** An HDWallet that supports DLCs and both Neutrino and SPV methods of syncing */
|
||||
|
@ -1,17 +1,15 @@
|
||||
package org.bitcoins.dlc.wallet.accounting
|
||||
|
||||
import org.bitcoins.core.dlc.accounting.DLCAccounting
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.dlc.wallet.models.{DLCAcceptDb, DLCDb, DLCOfferDb}
|
||||
|
||||
object AccountingUtil {
|
||||
|
||||
/** Calculates the profit and loss for the given dlc */
|
||||
def calculatePnl(
|
||||
dlcDb: DLCDb,
|
||||
offerDb: DLCOfferDb,
|
||||
acceptDb: DLCAcceptDb,
|
||||
closingTx: Transaction): DLCAccounting = {
|
||||
def calculatePnl(financials: DLCAccountingDbs): DLCAccounting = {
|
||||
val dlcDb = financials.dlcDb
|
||||
val offerDb = financials.offerDb
|
||||
val acceptDb = financials.acceptDb
|
||||
val closingTx = financials.closingTx
|
||||
val (myCollateral, theirCollateral, myPayoutAddress, theirPayoutAddress) = {
|
||||
if (dlcDb.isInitiator) {
|
||||
val myCollateral = offerDb.collateral
|
||||
@ -45,4 +43,5 @@ object AccountingUtil {
|
||||
theirPayout = theirPayout
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package org.bitcoins.dlc.wallet.accounting
|
||||
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.dlc.wallet.models.{DLCAcceptDb, DLCDb, DLCOfferDb}
|
||||
|
||||
case class DLCAccountingDbs(
|
||||
dlcDb: DLCDb,
|
||||
offerDb: DLCOfferDb,
|
||||
acceptDb: DLCAcceptDb,
|
||||
closingTx: Transaction) {
|
||||
require(
|
||||
dlcDb.dlcId == offerDb.dlcId,
|
||||
s"dlcDb.dlcId not equal to offerDb.dlcId, got dlcDb.dlcId=${dlcDb.dlcId} offerDb.dlcId=${offerDb.dlcId}")
|
||||
require(
|
||||
offerDb.dlcId == acceptDb.dlcId,
|
||||
s"OfferDb and acceptDb not the same offerDb.dlcId=${offerDb.dlcId}, acceptDb.dlcId=${acceptDb.dlcId}")
|
||||
}
|
@ -8,6 +8,7 @@ import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.crypto.SchnorrDigitalSignature
|
||||
import org.bitcoins.dlc.wallet.accounting.AccountingUtil
|
||||
import org.bitcoins.dlc.wallet.models._
|
||||
import org.bitcoins.dlc.wallet.accounting.DLCAccountingDbs
|
||||
|
||||
object DLCStatusBuilder {
|
||||
|
||||
@ -114,8 +115,9 @@ object DLCStatusBuilder {
|
||||
)
|
||||
|
||||
val dlcId = dlcDb.dlcId
|
||||
val financials = DLCAccountingDbs(dlcDb, offerDb, acceptDb, closingTx)
|
||||
val accounting: DLCAccounting =
|
||||
AccountingUtil.calculatePnl(dlcDb, offerDb, acceptDb, closingTx)
|
||||
AccountingUtil.calculatePnl(financials)
|
||||
|
||||
val totalCollateral = contractData.totalCollateral
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user