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

com.nftco.flow.sdk.models.kt Maven / Gradle / Ivy

The newest version!
package com.nftco.flow.sdk

import com.google.protobuf.ByteString
import com.google.protobuf.UnsafeByteOperations
import com.nftco.flow.sdk.cadence.EventField
import com.nftco.flow.sdk.cadence.Field
import org.onflow.protobuf.access.Access
import org.onflow.protobuf.entities.*
import org.tdf.rlp.RLP
import org.tdf.rlp.RLPCodec
import java.io.Serializable
import java.math.BigDecimal
import java.math.BigInteger
import java.time.LocalDateTime

private const val FLOW_ID_SIZE_BYTES = 32
private const val FLOW_ADDRESS_SIZE_BYTES = 8

enum class FlowTransactionStatus(val num: Int) {
    UNKNOWN(0),
    PENDING(1),
    FINALIZED(2),
    EXECUTED(3),
    SEALED(4),
    EXPIRED(5);

    companion object {
        @JvmStatic
        fun of(num: Int): FlowTransactionStatus = values()
            .find { it.num == num }
            ?: throw IllegalArgumentException("Unknown TransactionStatus: $num")
    }
}

enum class FlowChainId(
    val id: String
) {
    UNKNOWN("unknown"),
    MAINNET("flow-mainnet"),
    TESTNET("flow-testnet"),
    CANARYNET("flow-canarynet"),
    EMULATOR("flow-emulator");

    companion object {
        @JvmStatic
        fun of(id: String): FlowChainId = values()
            .find { it.id == id }
            ?: UNKNOWN
    }
}

enum class SignatureAlgorithm(
    val algorithm: String,
    val curve: String,
    val id: String,
    val code: Int,
    val index: Int
) {
    UNKNOWN("unknown", "unknown", "unknown", -1, 0),
    ECDSA_P256("ECDSA", "P-256", "ECDSA_P256", 2, 1),
    ECDSA_SECP256k1("ECDSA", "secp256k1", "ECDSA_secp256k1", 3, 2);
    companion object {
        @JvmStatic
        fun fromCode(code: Int): SignatureAlgorithm = values()
            .find { it.code == code } ?: UNKNOWN

        @JvmStatic
        fun fromCadenceIndex(index: Int): SignatureAlgorithm = values()
            .find { it.index == index } ?: UNKNOWN
    }
}

enum class HashAlgorithm(
    val algorithm: String,
    val outputSize: Int,
    val id: String,
    val code: Int,
    val index: Int
) {
    UNKNOWN("unknown", -1, "unknown", -1, 0),
    SHA2_256("SHA-256", 256, "SHA256withECDSA", 1, 1),
    SHA2_384("SHA-384", 384, "SHA384withECDSA", 1, 2),
    SHA3_256("SHA3-256", 256, "SHA3-256withECDSA", 3, 3),
    SHA3_384("SHA3-384", 384, "SHA3-384withECDSA", 3, 4);
    companion object {
        @JvmStatic
        fun fromCode(code: Int): HashAlgorithm = values()
            .find { it.code == code } ?: UNKNOWN

        @JvmStatic
        fun fromCadenceIndex(index: Int): HashAlgorithm = values()
            .find { it.index == index } ?: UNKNOWN
    }
}

interface Signer {

    val hasher: Hasher

    fun sign(bytes: ByteArray): ByteArray

    fun signWithDomain(bytes: ByteArray, domain: ByteArray): ByteArray = sign(domain + bytes)

    fun signAsUser(bytes: ByteArray): ByteArray = signWithDomain(bytes, DomainTag.USER_DOMAIN_TAG)

    fun signAsTransaction(bytes: ByteArray): ByteArray = signWithDomain(bytes, DomainTag.TRANSACTION_DOMAIN_TAG)
}

interface Hasher {
    fun hash(bytes: ByteArray): ByteArray
    fun hashAsHexString(bytes: ByteArray): String = hash(bytes).bytesToHex()
}

data class FlowAccount(
    val address: FlowAddress,
    val balance: BigDecimal,
    @Deprecated(
        message = "use contracts instead",
        replaceWith = ReplaceWith("contracts")
    )
    val code: FlowCode,
    val keys: List,
    val contracts: Map
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: AccountOuterClass.Account): FlowAccount = FlowAccount(
            address = FlowAddress.of(value.address.toByteArray()),
            balance = BigDecimal(java.lang.Long.toUnsignedString(value.balance)).movePointLeft(8),
            code = FlowCode(value.code.toByteArray()),
            keys = value.keysList.map { FlowAccountKey.of(it) },
            contracts = value.contractsMap.mapValues { FlowCode(it.value.toByteArray()) }
        )
    }

    @JvmOverloads
    @Suppress("DEPRECATION")
    fun builder(builder: AccountOuterClass.Account.Builder = AccountOuterClass.Account.newBuilder()): AccountOuterClass.Account.Builder {
        return builder
            .setAddress(address.byteStringValue)
            .setBalance(balance.movePointRight(8).toLong())
            .setCode(code.byteStringValue)
            .addAllKeys(keys.map { it.builder().build() })
            .putAllContracts(contracts.mapValues { it.value.byteStringValue })
    }

    /**
     * Returns the index of the public key on the account, or -1 if not found.
     */
    fun getKeyIndex(publicKey: String): Int {
        return this.keys
            .filter { !it.revoked }
            .find {
                it.publicKey.base16Value.lowercase().endsWith(publicKey.lowercase())
                    || publicKey.lowercase().endsWith(it.publicKey.base16Value.lowercase())
            }
            ?.id
            ?: -1
    }
}

data class FlowAccountKey(
    val id: Int = -1,
    val publicKey: FlowPublicKey,
    val signAlgo: SignatureAlgorithm,
    val hashAlgo: HashAlgorithm,
    val weight: Int,
    val sequenceNumber: Int = -1,
    val revoked: Boolean = false
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: AccountOuterClass.AccountKey): FlowAccountKey = FlowAccountKey(
            id = value.index,
            publicKey = FlowPublicKey(value.publicKey.toByteArray()),
            signAlgo = SignatureAlgorithm.fromCode(value.signAlgo),
            hashAlgo = HashAlgorithm.fromCode(value.hashAlgo),
            weight = value.weight,
            sequenceNumber = value.sequenceNumber,
            revoked = value.revoked
        )
    }

    @JvmOverloads
    fun builder(builder: AccountOuterClass.AccountKey.Builder = AccountOuterClass.AccountKey.newBuilder()): AccountOuterClass.AccountKey.Builder {
        return builder
            .setIndex(id)
            .setPublicKey(publicKey.byteStringValue)
            .setSignAlgo(signAlgo.code)
            .setHashAlgo(hashAlgo.code)
            .setWeight(weight)
            .setSequenceNumber(sequenceNumber)
            .setRevoked(revoked)
    }

    val encoded: ByteArray get() = RLPCodec.encode(
        arrayOf(
            publicKey.bytes,
            signAlgo.code,
            hashAlgo.code,
            weight
        )
    )
}

data class FlowEventResult(
    val blockId: FlowId,
    val blockHeight: Long,
    val blockTimestamp: LocalDateTime,
    val events: List
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: Access.EventsResponse.Result): FlowEventResult = FlowEventResult(
            blockId = FlowId.of(value.blockId.toByteArray()),
            blockHeight = value.blockHeight,
            blockTimestamp = value.blockTimestamp.asLocalDateTime(),
            events = value.eventsList.map { FlowEvent.of(it) }
        )
    }

    @JvmOverloads
    fun builder(builder: Access.EventsResponse.Result.Builder = Access.EventsResponse.Result.newBuilder()): Access.EventsResponse.Result.Builder {
        return builder
            .setBlockId(blockId.byteStringValue)
            .setBlockHeight(blockHeight)
            .setBlockTimestamp(blockTimestamp.asTimestamp())
            .addAllEvents(events.map { it.builder().build() })
    }
}

// https://github.com/onflow/flow-go-sdk/blob/878e5e586e0f060b88c6036cf4b0f6a7ab66d198/client/client.go#L515

data class FlowEvent(
    val type: String,
    val transactionId: FlowId,
    val transactionIndex: Int,
    val eventIndex: Int,
    val payload: FlowEventPayload
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: EventOuterClass.Event): FlowEvent = FlowEvent(
            type = value.type,
            transactionId = FlowId.of(value.transactionId.toByteArray()),
            transactionIndex = value.transactionIndex,
            eventIndex = value.eventIndex,
            payload = FlowEventPayload(value.payload.toByteArray())
        )
    }

    val id: String get() = event.id!!
    val event: EventField get() = payload.jsonCadence as EventField

    fun > getField(name: String): T? = event[name]
    @Suppress("UNCHECKED_CAST")
    operator fun  get(name: String): T? = getField>(name) as T
    operator fun contains(name: String): Boolean = name in event

    @JvmOverloads
    fun builder(builder: EventOuterClass.Event.Builder = EventOuterClass.Event.newBuilder()): EventOuterClass.Event.Builder {
        return builder
            .setType(type)
            .setTransactionId(transactionId.byteStringValue)
            .setTransactionIndex(transactionIndex)
            .setEventIndex(eventIndex)
            .setPayload(payload.byteStringValue)
    }
}

data class FlowTransactionResult(
    val status: FlowTransactionStatus,
    val statusCode: Int,
    val errorMessage: String,
    val events: List
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: Access.TransactionResultResponse): FlowTransactionResult = FlowTransactionResult(
            status = FlowTransactionStatus.of(value.statusValue),
            statusCode = value.statusCode,
            errorMessage = value.errorMessage,
            events = value.eventsList.map { FlowEvent.of(it) }
        )
    }

    @JvmOverloads
    fun builder(builder: Access.TransactionResultResponse.Builder = Access.TransactionResultResponse.newBuilder()): Access.TransactionResultResponse.Builder {
        return builder
            .setStatus(TransactionOuterClass.TransactionStatus.valueOf(status.name))
            .setStatusCode(statusCode)
            .setErrorMessage(errorMessage)
            .addAllEvents(events.map { it.builder().build() })
    }

    @JvmOverloads
    fun throwOnError(validStatusCodes: Set = setOf(0)): FlowTransactionResult {
        if (statusCode !in validStatusCodes) {
            throw FlowException("Transaction failed with code $statusCode:\n$errorMessage")
        }
        return this
    }

    @JvmOverloads
    fun getEventsOfType(type: String, exact: Boolean = false, expectedCount: Int? = null): List {
        val ret = this.events
            .filter { if (exact) { it.type == type } else { it.type.endsWith(type) } }
            .map { it.event }
        check(expectedCount == null || ret.size == expectedCount) { "Expected $expectedCount events of type $type but there were ${ret.size}" }
        return ret
    }
}

internal class Payload(
    @RLP(0) val script: ByteArray,
    @RLP(1) val arguments: List,
    @RLP(2) val referenceBlockId: ByteArray,
    @RLP(3) val gasLimit: Long,
    @RLP(4) val proposalKeyAddress: ByteArray,
    @RLP(5) val proposalKeyIndex: Long,
    @RLP(6) val proposalKeySequenceNumber: Long,
    @RLP(7) val payer: ByteArray,
    @RLP(8) val authorizers: List
) {
    // no-arg constructor required for decoding
    constructor() : this(byteArrayOf(), listOf(), byteArrayOf(), 0, byteArrayOf(), 0, 0, byteArrayOf(), listOf())
}

internal class PayloadEnvelope(
    @RLP(0) val payload: Payload,
    @RLP(1) val payloadSignatures: List
)

internal class PaymentEnvelope(
    @RLP(0) val payloadEnvelope: PayloadEnvelope,
    @RLP(1) val envelopeSignatures: List
)

internal class TransactionEnvelope(
    @RLP(0) val payload: Payload = Payload(),
    @RLP(1) val payloadSignatures: List = emptyList(),
    @RLP(2) val envelopeSignatures: List = emptyList()
)

internal class EnvelopeSignature(
    @RLP(0) val signerIndex: Int,
    @RLP(1) val keyIndex: Int,
    @RLP(2) val signature: ByteArray
) {
    // no-arg constructor required for decoding
    constructor() : this(0, 0, byteArrayOf())
}

data class FlowTransaction(
    val script: FlowScript,
    val arguments: List,
    val referenceBlockId: FlowId,
    val gasLimit: Long,
    val proposalKey: FlowTransactionProposalKey,
    val payerAddress: FlowAddress,
    val authorizers: List,
    val payloadSignatures: List = emptyList(),
    val envelopeSignatures: List = emptyList()
) : Serializable {

    private val payload: Payload
        get() = Payload(
            script = script.bytes,
            arguments = arguments.map { it.bytes },
            referenceBlockId = referenceBlockId.bytes,
            gasLimit = gasLimit,
            proposalKeyAddress = proposalKey.address.bytes,
            proposalKeyIndex = proposalKey.keyIndex.toLong(), // TODO: type mismatch here
            proposalKeySequenceNumber = proposalKey.sequenceNumber,
            payer = payerAddress.bytes,
            authorizers = authorizers.map { it.bytes }
        )

    private val authorization: PayloadEnvelope
        get() = PayloadEnvelope(
            payload = payload,
            payloadSignatures = payloadSignatures.map {
                EnvelopeSignature(
                    signerIndex = it.signerIndex,
                    keyIndex = it.keyIndex,
                    signature = it.signature.bytes
                )
            }
        )

    private val payment: PaymentEnvelope
        get() = PaymentEnvelope(
            payloadEnvelope = authorization,
            envelopeSignatures = envelopeSignatures.map {
                EnvelopeSignature(
                    signerIndex = it.signerIndex,
                    keyIndex = it.keyIndex,
                    signature = it.signature.bytes
                )
            }
        )

    private val transaction: TransactionEnvelope
        get() = TransactionEnvelope(
            payload = payload,
            payloadSignatures = payloadSignatures.map {
                EnvelopeSignature(
                    signerIndex = it.signerIndex,
                    keyIndex = it.keyIndex,
                    signature = it.signature.bytes
                )
            },
            envelopeSignatures = envelopeSignatures.map {
                EnvelopeSignature(
                    signerIndex = it.signerIndex,
                    keyIndex = it.keyIndex,
                    signature = it.signature.bytes
                )
            }
        )

    val canonicalPayload: ByteArray get() = RLPCodec.encode(payload)
    val canonicalAuthorizationEnvelope: ByteArray get() = RLPCodec.encode(authorization)
    val canonicalPaymentEnvelope: ByteArray get() = RLPCodec.encode(payment)
    val canonicalTransaction: ByteArray get() = RLPCodec.encode(transaction)
    val id: FlowId get() = FlowId.of(canonicalTransaction.sha3256Hash())

    val signerList: List get() {
        val ret = mutableListOf()
        val seen = mutableSetOf()
        val addSigner = fun(address: FlowAddress) {
            if (address in seen) {
                return
            }
            ret.add(address)
            seen.add(address)
        }
        addSigner(proposalKey.address)
        addSigner(payerAddress)
        authorizers.forEach(addSigner)
        return ret
    }

    val signerMap: Map get() {
        return signerList.withIndex()
            .map { it.value to it.index }
            .toMap()
    }

    companion object {
        @JvmStatic
        fun of(value: TransactionOuterClass.Transaction): FlowTransaction = FlowTransaction(
            script = FlowScript(value.script.toByteArray()),
            arguments = value.argumentsList.map { FlowArgument(it.toByteArray()) },
            referenceBlockId = FlowId.of(value.referenceBlockId.toByteArray()),
            gasLimit = value.gasLimit,
            proposalKey = FlowTransactionProposalKey.of(value.proposalKey),
            payerAddress = FlowAddress.of(value.payer.toByteArray()),
            authorizers = value.authorizersList.map { FlowAddress.of(it.toByteArray()) },
            payloadSignatures = value.payloadSignaturesList.map { FlowTransactionSignature.of(it) },
            envelopeSignatures = value.envelopeSignaturesList.map { FlowTransactionSignature.of(it) }
        )

        @JvmStatic
        fun of(bytes: ByteArray): FlowTransaction {
            val txEnvelope: TransactionEnvelope = RLPCodec.decode(bytes, TransactionEnvelope::class.java)
            var tx = FlowTransaction(
                script = FlowScript(txEnvelope.payload.script),
                arguments = txEnvelope.payload.arguments.map { FlowArgument(it) },
                referenceBlockId = FlowId.of(txEnvelope.payload.referenceBlockId),
                gasLimit = txEnvelope.payload.gasLimit,
                proposalKey = FlowTransactionProposalKey(
                    FlowAddress.of(txEnvelope.payload.proposalKeyAddress),
                    txEnvelope.payload.proposalKeyIndex.toInt(),
                    txEnvelope.payload.proposalKeySequenceNumber
                ),
                payerAddress = FlowAddress.of(txEnvelope.payload.payer),
                authorizers = txEnvelope.payload.authorizers.map { FlowAddress.of(it) }
            )

            txEnvelope.payloadSignatures.map {
                tx = tx.addPayloadSignature(tx.signerList[it.signerIndex], it.keyIndex, FlowSignature(it.signature))
            }
            txEnvelope.envelopeSignatures.map {
                tx = tx.addEnvelopeSignature(tx.signerList[it.signerIndex], it.keyIndex, FlowSignature(it.signature))
            }
            return tx
        }
    }

    @JvmOverloads
    fun builder(builder: TransactionOuterClass.Transaction.Builder = TransactionOuterClass.Transaction.newBuilder()): TransactionOuterClass.Transaction.Builder {
        return builder
            .setScript(script.byteStringValue)
            .addAllArguments(arguments.map { it.byteStringValue })
            .setReferenceBlockId(referenceBlockId.byteStringValue)
            .setGasLimit(gasLimit)
            .setProposalKey(proposalKey.builder().build())
            .setPayer(payerAddress.byteStringValue)
            .addAllAuthorizers(authorizers.map { it.byteStringValue })
            .addAllPayloadSignatures(payloadSignatures.map { it.builder().build() })
            .addAllEnvelopeSignatures(envelopeSignatures.map { it.builder().build() })
    }

    fun addPayloadSignature(address: FlowAddress, keyIndex: Int, signer: Signer): FlowTransaction {
        return addPayloadSignature(address, keyIndex, FlowSignature(signer.signAsTransaction(canonicalPayload)))
    }

    fun addPayloadSignature(address: FlowAddress, keyIndex: Int, signature: FlowSignature): FlowTransaction {
        val payloadSignatures = this.payloadSignatures.toMutableList()
        payloadSignatures.add(
            FlowTransactionSignature(
                address = address,
                signerIndex = signerMap[address] ?: -1,
                keyIndex = keyIndex,
                signature = signature
            )
        )
        return this.copy(
            payloadSignatures = payloadSignatures.sortedWith(compareBy { it.signerIndex }.thenBy { it.keyIndex })
        ).updateSignerIndices()
    }

    fun addEnvelopeSignature(address: FlowAddress, keyIndex: Int, signer: Signer): FlowTransaction {
        return addEnvelopeSignature(address, keyIndex, FlowSignature(signer.signAsTransaction(canonicalAuthorizationEnvelope)))
    }

    fun addEnvelopeSignature(address: FlowAddress, keyIndex: Int, signature: FlowSignature): FlowTransaction {
        val envelopeSignatures = this.envelopeSignatures.toMutableList()
        envelopeSignatures.add(
            FlowTransactionSignature(
                address = address,
                signerIndex = signerMap[address] ?: -1,
                keyIndex = keyIndex,
                signature = signature
            )
        )
        return this.copy(
            envelopeSignatures = envelopeSignatures.sortedWith(compareBy { it.signerIndex }.thenBy { it.keyIndex })
        ).updateSignerIndices()
    }

    fun updateSignerIndices(): FlowTransaction {
        val map = signerMap
        val payloadSignatures = this.payloadSignatures.toMutableList()
        for ((i, sig) in payloadSignatures.withIndex()) {
            if (map.containsKey(sig.address)) {
                continue
            }
            payloadSignatures[i] = payloadSignatures[i].copy(
                signerIndex = i
            )
        }
        val envelopeSignatures = this.envelopeSignatures.toMutableList()
        for ((i, sig) in envelopeSignatures.withIndex()) {
            if (map.containsKey(sig.address)) {
                continue
            }
            envelopeSignatures[i] = envelopeSignatures[i].copy(
                signerIndex = i
            )
        }
        return this.copy(
            payloadSignatures = payloadSignatures,
            envelopeSignatures = envelopeSignatures
        )
    }
}

data class FlowTransactionProposalKey(
    val address: FlowAddress,
    val keyIndex: Int,
    val sequenceNumber: Long
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: TransactionOuterClass.Transaction.ProposalKey): FlowTransactionProposalKey =
            FlowTransactionProposalKey(
                address = FlowAddress.of(value.address.toByteArray()),
                keyIndex = value.keyId,
                sequenceNumber = value.sequenceNumber
            )
    }

    @JvmOverloads
    fun builder(builder: TransactionOuterClass.Transaction.ProposalKey.Builder = TransactionOuterClass.Transaction.ProposalKey.newBuilder()): TransactionOuterClass.Transaction.ProposalKey.Builder {
        return builder
            .setAddress(address.byteStringValue)
            .setKeyId(keyIndex)
            .setSequenceNumber(sequenceNumber)
    }
}

data class FlowTransactionSignature(
    val address: FlowAddress,
    val signerIndex: Int,
    val keyIndex: Int,
    val signature: FlowSignature
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: TransactionOuterClass.Transaction.Signature): FlowTransactionSignature =
            FlowTransactionSignature(
                address = FlowAddress.of(value.address.toByteArray()),
                signerIndex = value.keyId,
                keyIndex = value.keyId,
                signature = FlowSignature(value.signature.toByteArray())
            )
    }

    @JvmOverloads
    fun builder(builder: TransactionOuterClass.Transaction.Signature.Builder = TransactionOuterClass.Transaction.Signature.newBuilder()): TransactionOuterClass.Transaction.Signature.Builder {
        return builder
            .setAddress(address.byteStringValue)
            .setKeyId(keyIndex)
            .setSignature(signature.byteStringValue)
    }
}

data class FlowBlockHeader(
    val id: FlowId,
    val parentId: FlowId,
    val height: Long
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: BlockHeaderOuterClass.BlockHeader): FlowBlockHeader = FlowBlockHeader(
            id = FlowId.of(value.id.toByteArray()),
            parentId = FlowId.of(value.parentId.toByteArray()),
            height = value.height
        )
    }

    @JvmOverloads
    fun builder(builder: BlockHeaderOuterClass.BlockHeader.Builder = BlockHeaderOuterClass.BlockHeader.newBuilder()): BlockHeaderOuterClass.BlockHeader.Builder {
        return builder
            .setId(id.byteStringValue)
            .setParentId(parentId.byteStringValue)
            .setHeight(height)
    }
}

data class FlowBlock(
    val id: FlowId,
    val parentId: FlowId,
    val height: Long,
    val timestamp: LocalDateTime,
    val collectionGuarantees: List,
    val blockSeals: List,
    val signatures: List
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: BlockOuterClass.Block) = FlowBlock(
            id = FlowId.of(value.id.toByteArray()),
            parentId = FlowId.of(value.parentId.toByteArray()),
            height = value.height,
            timestamp = value.timestamp.asLocalDateTime(),
            collectionGuarantees = value.collectionGuaranteesList.map { FlowCollectionGuarantee.of(it) },
            blockSeals = value.blockSealsList.map { FlowBlockSeal.of(it) },
            signatures = value.signaturesList.map { FlowSignature(it.toByteArray()) },
        )
    }

    @JvmOverloads
    fun builder(builder: BlockOuterClass.Block.Builder = BlockOuterClass.Block.newBuilder()): BlockOuterClass.Block.Builder {
        return builder
            .setId(id.byteStringValue)
            .setParentId(parentId.byteStringValue)
            .setHeight(height)
            .setTimestamp(timestamp.asTimestamp())
            .addAllCollectionGuarantees(collectionGuarantees.map { it.builder().build() })
            .addAllBlockSeals(blockSeals.map { it.builder().build() })
            .addAllSignatures(signatures.map { it.byteStringValue })
    }
}

data class FlowCollectionGuarantee(
    val id: FlowId,
    val signatures: List
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: CollectionOuterClass.CollectionGuarantee) = FlowCollectionGuarantee(
            id = FlowId.of(value.collectionId.toByteArray()),
            signatures = value.signaturesList.map { FlowSignature(it.toByteArray()) }
        )
    }

    @JvmOverloads
    fun builder(builder: CollectionOuterClass.CollectionGuarantee.Builder = CollectionOuterClass.CollectionGuarantee.newBuilder()): CollectionOuterClass.CollectionGuarantee.Builder {
        return builder
            .setCollectionId(id.byteStringValue)
            .addAllSignatures(signatures.map { it.byteStringValue })
    }
}

data class FlowBlockSeal(
    val id: FlowId,
    val executionReceiptId: FlowId,
    val executionReceiptSignatures: List,
    val resultApprovalSignatures: List
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: BlockSealOuterClass.BlockSeal) = FlowBlockSeal(
            id = FlowId.of(value.blockId.toByteArray()),
            executionReceiptId = FlowId.of(value.executionReceiptId.toByteArray()),
            executionReceiptSignatures = value.executionReceiptSignaturesList.map { FlowSignature(it.toByteArray()) },
            resultApprovalSignatures = value.executionReceiptSignaturesList.map { FlowSignature(it.toByteArray()) }
        )
    }

    @JvmOverloads
    fun builder(builder: BlockSealOuterClass.BlockSeal.Builder = BlockSealOuterClass.BlockSeal.newBuilder()): BlockSealOuterClass.BlockSeal.Builder {
        return builder
            .setBlockId(id.byteStringValue)
            .setExecutionReceiptId(executionReceiptId.byteStringValue)
            .addAllExecutionReceiptSignatures(executionReceiptSignatures.map { it.byteStringValue })
            .addAllResultApprovalSignatures(resultApprovalSignatures.map { it.byteStringValue })
    }
}

data class FlowCollection(
    val id: FlowId,
    val transactionIds: List
) : Serializable {
    companion object {
        @JvmStatic
        fun of(value: CollectionOuterClass.Collection) = FlowCollection(
            id = FlowId.of(value.id.toByteArray()),
            transactionIds = value.transactionIdsList.map { FlowId.of(it.toByteArray()) }
        )
    }

    @JvmOverloads
    fun builder(builder: CollectionOuterClass.Collection.Builder = CollectionOuterClass.Collection.newBuilder()): CollectionOuterClass.Collection.Builder {
        return builder
            .setId(id.byteStringValue)
            .addAllTransactionIds(transactionIds.map { it.byteStringValue })
    }
}

interface BytesHolder {
    val bytes: ByteArray
    val base16Value: String get() = bytes.bytesToHex()
    val stringValue: String get() = String(bytes)
    val byteStringValue: ByteString get() = UnsafeByteOperations.unsafeWrap(bytes)
    val integerValue: BigInteger get() = BigInteger(base16Value, 16)
}

data class FlowAddress private constructor(override val bytes: ByteArray) : Serializable, BytesHolder {
    companion object {
        @JvmStatic
        fun of(bytes: ByteArray): FlowAddress = FlowAddress(fixedSize(bytes, FLOW_ADDRESS_SIZE_BYTES))
    }
    constructor(hex: String) : this(fixedSize(hex.hexToBytes(), FLOW_ADDRESS_SIZE_BYTES))
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowAddress
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    val formatted: String = "0x$base16Value"

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowArgument(override val bytes: ByteArray) : Serializable, BytesHolder {

    constructor(jsonCadence: Field<*>) : this(Flow.encodeJsonCadence(jsonCadence))

    private var _jsonCadence: Field<*>? = null
    val jsonCadence: Field<*>
        get() {
            if (_jsonCadence == null) {
                _jsonCadence = Flow.decodeJsonCadence(bytes)
            }
            return _jsonCadence!!
        }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowArgument
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowScript(override val bytes: ByteArray) : Serializable, BytesHolder {
    constructor(script: String) : this(script.encodeToByteArray())
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowScript
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowScriptResponse(override val bytes: ByteArray) : Serializable, BytesHolder {

    constructor(jsonCadence: Field<*>) : this(Flow.encodeJsonCadence(jsonCadence))

    private var _jsonCadence: Field<*>? = null
    val jsonCadence: Field<*>
        get() {
            if (_jsonCadence == null) {
                _jsonCadence = Flow.decodeJsonCadence(bytes)
            }
            return _jsonCadence!!
        }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowScriptResponse
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowSignature(override val bytes: ByteArray) : Serializable, BytesHolder {
    constructor(hex: String) : this(hex.hexToBytes())
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowSignature
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowId private constructor(override val bytes: ByteArray) : Serializable, BytesHolder {
    companion object {
        @JvmStatic
        fun of(bytes: ByteArray): FlowId = FlowId(fixedSize(bytes, FLOW_ID_SIZE_BYTES))
    }
    constructor(hex: String) : this(fixedSize(hex.hexToBytes(), FLOW_ID_SIZE_BYTES))
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowId
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowCode(override val bytes: ByteArray) : Serializable, BytesHolder {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowCode
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowPublicKey(override val bytes: ByteArray) : Serializable, BytesHolder {
    constructor(hex: String) : this(hex.hexToBytes())
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowPublicKey
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowSnapshot(override val bytes: ByteArray) : Serializable, BytesHolder {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowSnapshot
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}

data class FlowEventPayload(override val bytes: ByteArray) : Serializable, BytesHolder {

    constructor(jasonCadence: Field<*>) : this(Flow.encodeJsonCadence(jasonCadence))

    private var _jsonCadence: Field<*>? = null
    val jsonCadence: Field<*>
        get() {
            if (_jsonCadence == null) {
                _jsonCadence = Flow.decodeJsonCadence(bytes)
            }
            return _jsonCadence!!
        }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as FlowEventPayload
        if (!bytes.contentEquals(other.bytes)) return false
        return true
    }

    override fun hashCode(): Int {
        return bytes.contentHashCode()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy