diff --git a/server/app/com/xsn/explorer/models/Transaction.scala b/server/app/com/xsn/explorer/models/Transaction.scala new file mode 100644 index 0000000..eaa88f0 --- /dev/null +++ b/server/app/com/xsn/explorer/models/Transaction.scala @@ -0,0 +1,36 @@ +package com.xsn.explorer.models + +import play.api.libs.functional.syntax._ +import play.api.libs.json._ + +case class Transaction( + id: TransactionId, + size: Int, + blockhash: Blockhash, + time: Long, + blocktime: Long, + confirmations: Int, + vin: Option[TransactionVIN], + vout: List[TransactionVOUT], +) + +object Transaction { + + implicit val reads: Reads[Transaction] = { + val builder = (__ \ 'txid).read[TransactionId] and + (__ \ 'size).read[Int] and + (__ \ 'blockhash).read[Blockhash] and + (__ \ 'time).read[Long] and + (__ \ 'blocktime).read[Long] and + (__ \ 'confirmations).read[Int] and + (__ \ 'vout).read[List[TransactionVOUT]] and + (__ \ 'vin).readNullable[List[JsValue]] + .map(_ getOrElse List.empty) + .map { list => list.flatMap(_.asOpt[TransactionVIN]) } + .map(_.headOption) + + builder.apply { (id, size, blockHash, time, blockTime, confirmations, vout, vin) => + Transaction(id, size, blockHash, time, blockTime, confirmations, vin, vout) + } + } +} diff --git a/server/app/com/xsn/explorer/models/TransactionId.scala b/server/app/com/xsn/explorer/models/TransactionId.scala index 3cae0fa..e1bcfef 100644 --- a/server/app/com/xsn/explorer/models/TransactionId.scala +++ b/server/app/com/xsn/explorer/models/TransactionId.scala @@ -1,5 +1,7 @@ package com.xsn.explorer.models +import play.api.libs.json._ + class TransactionId private (val string: String) extends AnyVal object TransactionId { @@ -15,4 +17,16 @@ object TransactionId { None } } + + implicit val reads: Reads[TransactionId] = Reads { json => + json.validate[String].flatMap { string => + from(string) + .map(JsSuccess.apply(_)) + .getOrElse { + JsError.apply("Invalid transaction") + } + } + } + + implicit val writes: Writes[TransactionId] = Writes { obj => JsString(obj.string) } } diff --git a/server/app/com/xsn/explorer/models/TransactionVIN.scala b/server/app/com/xsn/explorer/models/TransactionVIN.scala new file mode 100644 index 0000000..8e4c047 --- /dev/null +++ b/server/app/com/xsn/explorer/models/TransactionVIN.scala @@ -0,0 +1,17 @@ +package com.xsn.explorer.models + +import play.api.libs.functional.syntax._ +import play.api.libs.json.{Reads, __} + +case class TransactionVIN(txid: TransactionId, voutIndex: Int) + +object TransactionVIN { + + implicit val reads: Reads[TransactionVIN] = { + val builder = (__ \ 'txid).read[TransactionId] and (__ \ 'vout).read[Int] + + builder.apply { (txid, index) => + TransactionVIN(txid, index) + } + } +} diff --git a/server/app/com/xsn/explorer/models/TransactionVOUT.scala b/server/app/com/xsn/explorer/models/TransactionVOUT.scala new file mode 100644 index 0000000..a3a5ae0 --- /dev/null +++ b/server/app/com/xsn/explorer/models/TransactionVOUT.scala @@ -0,0 +1,32 @@ +package com.xsn.explorer.models + +import play.api.libs.functional.syntax._ +import play.api.libs.json.{JsObject, Reads, __} + +case class TransactionVOUT( + value: BigDecimal, + n: Int, + scriptPubKeyType: String, + address: Option[Address]) + +object TransactionVOUT { + + implicit val reads: Reads[TransactionVOUT] = { + val builder = (__ \ 'value).read[BigDecimal] and + (__ \ 'n).read[Int] and + (__ \ 'scriptPubKey).read[JsObject].map { json => + val t = (json \ "type").as[String] + val a = (json \ "addresses") + .asOpt[List[String]] + .flatMap(_.headOption) + .flatMap(Address.from) + + (t, a) + } + + builder.apply { (value, n, tuple) => + val (scriptType, address) = tuple + TransactionVOUT(value, n, scriptType, address) + } + } +}