All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavesplatform.transaction.package.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform

import cats.data.ValidatedNel
import com.wavesplatform.account.PrivateKey
import com.wavesplatform.block.{Block, BlockSnapshot, MicroBlock}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.lang.ValidationError
import com.wavesplatform.state.StateSnapshot
import com.wavesplatform.transaction.Asset.IssuedAsset
import com.wavesplatform.transaction.assets.IssueTransaction
import com.wavesplatform.transaction.assets.exchange.Order
import com.wavesplatform.transaction.validation.TxValidator
import com.wavesplatform.utils.{EthEncoding, base58Length}
import eu.timepit.refined.api.Refined
import eu.timepit.refined.numeric.{Interval, NonNegative, Positive}
import play.api.libs.json.*
import supertagged.*
import supertagged.postfix.*

package object transaction {
  val AssetIdLength: Int       = com.wavesplatform.crypto.DigestLength
  val AssetIdStringLength: Int = base58Length(AssetIdLength)

  type DiscardedBlocks       = Seq[(Block, ByteStr, Option[BlockSnapshot])]
  type DiscardedMicroBlocks  = Seq[(MicroBlock, StateSnapshot)]
  type AuthorizedTransaction = Authorized & Transaction

  type TxType = Byte

  type TxVersion = Byte
  object TxVersion {
    val V1: TxVersion = 1.toByte
    val V2: TxVersion = 2.toByte
    val V3: TxVersion = 3.toByte
  }
  type TxTimestamp = Long
  type TxByteArray = Array[Byte]

  type TxPositiveAmount = Long Refined Positive
  object TxPositiveAmount extends RefinedTypeOps[TxPositiveAmount, Long]
  implicit val posAmountWrites: Writes[TxPositiveAmount] = Writes(v => JsNumber(v.value))

  type TxNonNegativeAmount = Long Refined NonNegative
  object TxNonNegativeAmount extends RefinedTypeOps[TxNonNegativeAmount, Long] {
    private val LongStringMaxLength = 20 // Long.MaxValue.toString.length

    implicit val reads: Reads[TxNonNegativeAmount] = Reads { json =>
      val r = json match {
        case JsString(s) =>
          if (s.length > LongStringMaxLength) JsError("error.expected.numberdigitlimit")
          else
            s.toLongOption match {
              case None    => JsError(JsonValidationError("error.expected.numberformatexception"))
              case Some(r) => JsSuccess(r)
            }

        case JsNumber(r) =>
          if (r.isValidLong) JsSuccess(r.toLongExact)
          else JsError(JsonValidationError("error.invalid.long"))

        case _ => JsError(JsonValidationError("error.expected.jsnumberorjsstring"))
      }

      r.flatMap { r =>
        if (r >= 0) JsSuccess(TxNonNegativeAmount.unsafeFrom(r))
        else JsError(JsonValidationError("error.expected.txnonnegativeamount"))
      }
    }
  }

  type TxDecimals = Byte Refined Interval.Closed[0, IssueTransaction.MaxAssetDecimals.type]
  object TxDecimals extends RefinedTypeOps[TxDecimals, Byte] {
    val errMsg = s"decimals should be in interval [0; ${IssueTransaction.MaxAssetDecimals}]"
  }

  type TxOrderPrice = Long Refined Positive
  object TxOrderPrice extends RefinedTypeOps[TxOrderPrice, Long] {
    val errMsg = "price should be > 0"
  }

  type TxExchangeAmount = Long Refined Interval.OpenClosed[0, Order.MaxAmount.type]
  object TxExchangeAmount extends RefinedTypeOps[TxExchangeAmount, Long] {
    val errMsg = s"amount should be in interval (0; ${Order.MaxAmount}]"
  }

  type TxExchangePrice = Long Refined Interval.OpenClosed[0, Order.MaxAmount.type]
  object TxExchangePrice extends RefinedTypeOps[TxExchangePrice, Long] {
    val errMsg = s"price should be in interval (0; ${Order.MaxAmount}]"
  }

  type TxMatcherFee = Long Refined Interval.Open[0, Order.MaxAmount.type]
  object TxMatcherFee extends RefinedTypeOps[TxMatcherFee, Long] {
    val errMsg = s"matcher fee should be in interval (0; ${Order.MaxAmount})"
  }

  implicit class TransactionValidationOps[T <: Transaction](val tx: T) extends AnyVal {
    def validatedNel(implicit validator: TxValidator[T]): ValidatedNel[ValidationError, T] = validator.validate(tx)
    def validatedEither(implicit validator: TxValidator[T]): Either[ValidationError, T]    = this.validatedNel.toEither.left.map(_.head)
  }

  implicit class TransactionSignOps[T](val tx: T) extends AnyVal {
    def signWith(privateKey: PrivateKey)(implicit sign: (T, PrivateKey) => T): T = sign(tx, privateKey)
  }

  object ERC20Address extends TaggedType[ByteStr] {
    def apply(bs: ByteStr): ERC20Address = {
      require(bs.arr.length == 20, "ERC20 token address length must be 20 bytes")
      bs @@ this
    }

    def apply(ia: IssuedAsset): ERC20Address = apply(ia.id.take(20))

    implicit val jsonFormat: Format[ERC20Address] = Format(
      implicitly[Reads[String]].map(str => ERC20Address(ByteStr(EthEncoding.toBytes(str)))),
      implicitly[Writes[String]].contramap((addr: ERC20Address) => EthEncoding.toHexString(addr.arr))
    )
  }
  type ERC20Address = ERC20Address.Type
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy