diff --git a/lib/build.gradle b/lib/build.gradle index d683a25..039b5d7 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -63,7 +63,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0' // SolanaKT - implementation 'com.github.metaplex-foundation:SolanaKT:1.1.5' + implementation 'com.github.metaplex-foundation:SolanaKT:2.0.0' // Moshi implementation "com.squareup.moshi:moshi:1.13.0" diff --git a/lib/src/main/java/com/metaplex/lib/Metaplex.kt b/lib/src/main/java/com/metaplex/lib/Metaplex.kt index 6a1e43f..66cd543 100644 --- a/lib/src/main/java/com/metaplex/lib/Metaplex.kt +++ b/lib/src/main/java/com/metaplex/lib/Metaplex.kt @@ -8,9 +8,6 @@ import com.metaplex.lib.modules.nfts.NftClient import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.modules.candymachines.CandyMachineClient import com.metaplex.lib.modules.candymachinesv2.CandyMachineV2Client -import com.solana.core.PublicKey -import com.solana.models.buffer.BufferInfo -import com.solana.vendor.borshj.BorshCodable class Metaplex(val connection: Connection, private var identityDriver: IdentityDriver, @@ -35,20 +32,4 @@ class Metaplex(val connection: Connection, this.storageDriver = storageDriver return this.storageDriver } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("connection.getAccountInfo(account)")) - fun getAccountInfo(account: PublicKey, - decodeTo: Class, - onComplete: ((Result>) -> Unit)){ - this.connection.getAccountInfo(account, decodeTo, onComplete) - } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("connection.getMultipleAccountsInfo(accounts)")) - fun getMultipleAccountsInfo( - accounts: List, - decodeTo: Class, - onComplete: ((Result?>>) -> Unit) - ) { - this.connection.getMultipleAccountsInfo(accounts, decodeTo, onComplete) - } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/drivers/solana/AccountResponse.kt b/lib/src/main/java/com/metaplex/lib/drivers/solana/AccountResponse.kt index 4780649..c2c003f 100644 --- a/lib/src/main/java/com/metaplex/lib/drivers/solana/AccountResponse.kt +++ b/lib/src/main/java/com/metaplex/lib/drivers/solana/AccountResponse.kt @@ -8,13 +8,9 @@ package com.metaplex.lib.drivers.solana import com.metaplex.lib.serialization.serializers.base64.BorshAsBase64JsonArraySerializer -import com.metaplex.lib.serialization.serializers.solana.AnchorAccountSerializer import com.metaplex.lib.serialization.serializers.solana.PublicKeyAs32ByteSerializer import com.metaplex.lib.serialization.serializers.solana.SolanaResponseSerializer import com.solana.core.PublicKey -import com.solana.models.buffer.Buffer -import com.solana.models.buffer.BufferInfo -import com.solana.vendor.borshj.BorshCodable import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -25,9 +21,6 @@ import kotlinx.serialization.builtins.nullable data class AccountInfo(val data: D?, val executable: Boolean, val lamports: Long, val owner: String?, val rentEpoch: Long) -internal fun AccountInfo.toBufferInfo() = - BufferInfo(data?.let { Buffer(data as T) }, executable, lamports, owner, rentEpoch) - @Serializable data class AccountInfoWithPublicKey

(val account: AccountInfo

, @SerialName("pubkey") val publicKey: String) @@ -35,18 +28,10 @@ data class AccountInfoWithPublicKey

(val account: AccountInfo

, @SerialName( data class AccountPublicKey(@Serializable(with = PublicKeyAs32ByteSerializer::class) val publicKey: PublicKey) internal fun SolanaAccountSerializer(serializer: KSerializer) = - AccountInfoSerializer( - BorshAsBase64JsonArraySerializer( - AnchorAccountSerializer(serializer.descriptor.serialName, serializer) - ) - ) + AccountInfoSerializer(BorshAsBase64JsonArraySerializer(serializer)) internal fun MultipleAccountsSerializer(serializer: KSerializer) = - MultipleAccountsInfoSerializer( - BorshAsBase64JsonArraySerializer( - AnchorAccountSerializer(serializer.descriptor.serialName, serializer) - ) - ) + MultipleAccountsInfoSerializer(BorshAsBase64JsonArraySerializer(serializer)) internal fun ProgramAccountsSerializer(serializer: KSerializer) = ListSerializer( @@ -55,12 +40,6 @@ internal fun ProgramAccountsSerializer(serializer: KSerializer) = ).nullable ) -internal inline fun SolanaAccountSerializer() = - AccountInfoSerializer(BorshAsBase64JsonArraySerializer(AnchorAccountSerializer())) - -internal inline fun MultipleAccountsSerializer() = - MultipleAccountsInfoSerializer(BorshAsBase64JsonArraySerializer(AnchorAccountSerializer())) - private fun AccountInfoSerializer(serializer: KSerializer) = SolanaResponseSerializer(AccountInfo.serializer(serializer)) diff --git a/lib/src/main/java/com/metaplex/lib/drivers/solana/Connection.kt b/lib/src/main/java/com/metaplex/lib/drivers/solana/Connection.kt index 9274223..3fc8cd9 100644 --- a/lib/src/main/java/com/metaplex/lib/drivers/solana/Connection.kt +++ b/lib/src/main/java/com/metaplex/lib/drivers/solana/Connection.kt @@ -8,15 +8,11 @@ package com.metaplex.lib.drivers.solana import android.util.Base64 -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.drivers.rpc.RpcRequest import com.solana.core.PublicKey import com.solana.core.Transaction -import com.solana.models.ProgramAccount import com.solana.models.ProgramAccountConfig import com.solana.models.SignatureStatusRequestConfiguration -import com.solana.models.buffer.BufferInfo -import com.solana.vendor.borshj.BorshCodable import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.serializer import kotlinx.serialization.serializer @@ -47,37 +43,6 @@ interface Connection { suspend fun getSignatureStatuses(signatures: List, configs: SignatureStatusRequestConfiguration?) : Result> - - //region DEPRECATED METHODS - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, - ReplaceWith("getAccountInfo(serializer, account)")) - fun getAccountInfo(account: PublicKey, - decodeTo: Class, - onComplete: ((Result>) -> Unit)) - - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, - ReplaceWith("getMultipleAccountsInfo(serializer, accounts)")) - fun getMultipleAccountsInfo( - accounts: List, - decodeTo: Class, - onComplete: ((Result?>>) -> Unit) - ) - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, - ReplaceWith("getProgramAccounts(serializer, account, programAccountConfig)")) - fun getProgramAccounts(account: PublicKey, - programAccountConfig: ProgramAccountConfig, - decodeTo: Class, - onComplete: (Result>>) -> Unit - ) - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, - ReplaceWith("getSignatureStatuses(signatures, configs)")) - fun getSignatureStatuses(signatures: List, - configs: SignatureStatusRequestConfiguration?, - onComplete: ((Result) -> Unit)) - //endregion } //region ERRORS diff --git a/lib/src/main/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriver.kt b/lib/src/main/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriver.kt index 0e7d0cb..c566a42 100644 --- a/lib/src/main/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriver.kt +++ b/lib/src/main/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriver.kt @@ -10,25 +10,14 @@ package com.metaplex.lib.drivers.solana import com.metaplex.lib.drivers.rpc.JdkRpcDriver import com.metaplex.lib.drivers.rpc.JsonRpcDriver import com.metaplex.lib.drivers.rpc.RpcRequest -import com.metaplex.lib.serialization.serializers.legacy.BorshCodeableSerializer -import com.metaplex.lib.programs.token_metadata.MasterEditionAccountJsonAdapterFactory -import com.metaplex.lib.programs.token_metadata.MasterEditionAccountRule -import com.metaplex.lib.programs.token_metadata.accounts.* -import com.metaplex.lib.shared.AccountPublicKeyJsonAdapterFactory -import com.metaplex.lib.shared.AccountPublicKeyRule -import com.solana.api.Api +import com.solana.api.getAccountInfo import com.solana.core.PublicKey import com.solana.models.ProgramAccountConfig import com.solana.models.SignatureStatusRequestConfiguration -import com.solana.models.buffer.BufferInfo -import com.solana.networking.NetworkingRouter -import com.solana.networking.NetworkingRouterConfig -import com.solana.networking.RPCEndpoint -import com.solana.vendor.borshj.BorshCodable -import kotlinx.coroutines.CoroutineScope +import com.solana.networking.* import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.JsonElement /** * [Connection] implementation that wraps the legacy async-callback API into the @@ -39,39 +28,14 @@ import kotlinx.serialization.KSerializer * * @author Funkatronics */ -class SolanaConnectionDriver(private val rpcService: JsonRpcDriver, - override val transactionOptions: TransactionOptions = TransactionOptions() -) - : Connection { - - private var endpoint: RPCEndpoint = RPCEndpoint.mainnetBetaSolana +class SolanaConnectionDriver( + private val rpcService: JsonRpcDriver, + override val transactionOptions: TransactionOptions = TransactionOptions() +) : Connection { @JvmOverloads constructor(endpoint: RPCEndpoint, rpcService: JsonRpcDriver = JdkRpcDriver(endpoint.url)) - : this(rpcService) { this.endpoint = endpoint } - - // Some things are still using this, so need to keep a reference to it here - @Deprecated("Deprecated, use an RPC Driver implementation instead") - val solanaRPC: Api = Api( - NetworkingRouter(endpoint, - config = NetworkingRouterConfig( - listOf( - MetadataAccountRule(), - MetaplexDataRule(), - MetaplexCollectionRule(), - AccountPublicKeyRule(), - MasterEditionAccountRule(), - MetaplexCreatorRule() - ), - listOf( - MetadataAccountJsonAdapterFactory(), - MetaplexDataAdapterJsonAdapterFactory(), - AccountPublicKeyJsonAdapterFactory(), - MasterEditionAccountJsonAdapterFactory() - ) - ) - ) - ) + : this(rpcService) //region CONNECTION override suspend fun get(request: RpcRequest, serializer: KSerializer): Result = @@ -146,56 +110,4 @@ class SolanaConnectionDriver(private val rpcService: JsonRpcDriver, else result as Result> // safe cast, null case handled above } //endregion - - //region DEPRECATED METHODS - override fun getAccountInfo( - account: PublicKey, - decodeTo: Class, - onComplete: (Result>) -> Unit - ) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getAccountInfo(BorshCodeableSerializer(decodeTo), account) - .map { it.toBufferInfo() }) - } - } - - override fun getMultipleAccountsInfo( - accounts: List, - decodeTo: Class, - onComplete: (Result?>>) -> Unit - ) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getMultipleAccountsInfo(BorshCodeableSerializer(decodeTo), accounts) - .map { it.map { it?.toBufferInfo() } }) - } - } - - override fun getProgramAccounts( - account: PublicKey, - programAccountConfig: ProgramAccountConfig, - decodeTo: Class, - onComplete: (Result>>) -> Unit) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getProgramAccounts(BorshCodeableSerializer(decodeTo), account, programAccountConfig) - .map { it.map { - com.solana.models.ProgramAccount(it.account.toBufferInfo(), it.publicKey) - } }) - } - } - - override fun getSignatureStatuses(signatures: List, - configs: SignatureStatusRequestConfiguration?, - onComplete: ((Result) -> Unit)) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getSignatureStatuses(signatures, configs).map { - com.solana.models.SignatureStatus(it.map { sigStatus -> - com.solana.models.SignatureStatus.Value( - sigStatus.slot, sigStatus.confirmations, - sigStatus.err, sigStatus.confirmationStatus - ) - }) - }) - } - } - //endregion } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/experimental/jen/candymachine/Instructions.kt b/lib/src/main/java/com/metaplex/lib/experimental/jen/candymachine/Instructions.kt index 9e4e58c..9e3e052 100644 --- a/lib/src/main/java/com/metaplex/lib/experimental/jen/candymachine/Instructions.kt +++ b/lib/src/main/java/com/metaplex/lib/experimental/jen/candymachine/Instructions.kt @@ -60,7 +60,7 @@ object CandyMachineInstructions { AccountMeta(collectionAuthorityRecord, false, true), AccountMeta(tokenMetadataProgram, false, false), AccountMeta(systemProgram, false, false), - AccountMeta(Sysvar.SYSVAR_RENT_ADDRESS, false, false) + AccountMeta(Sysvar.SYSVAR_RENT_PUBKEY, false, false) ), Borsh.encodeToByteArray( AnchorInstructionSerializer("initialize"), diff --git a/lib/src/main/java/com/metaplex/lib/extensions/Result+Extensions.kt b/lib/src/main/java/com/metaplex/lib/extensions/Result+Extensions.kt new file mode 100644 index 0000000..787a5b1 --- /dev/null +++ b/lib/src/main/java/com/metaplex/lib/extensions/Result+Extensions.kt @@ -0,0 +1,35 @@ +/* + * Connection+Extensions + * Metaplex + * + * Created by Funkatronics on 10/19/2022 + */ + +package com.metaplex.lib.extensions + +import com.metaplex.lib.drivers.solana.Connection +import com.metaplex.lib.drivers.solana.TransactionOptions +import kotlinx.coroutines.isActive +import kotlinx.coroutines.withTimeout + +suspend fun Result.confirmTransaction( + connection: Connection, transactionOptions: TransactionOptions = connection.transactionOptions +): Result = withTimeout(transactionOptions.timeout.toMillis()) { + + suspend fun confirmationStatus() = + connection.getSignatureStatuses(listOf(getOrThrow()), null) + .getOrNull()?.first()?.confirmationStatus + + // wait for desired transaction status + while(confirmationStatus() != transactionOptions.commitment.toString()) { + + // wait a bit before retrying + val millis = System.currentTimeMillis() + var inc = 0 + while(System.currentTimeMillis() - millis < 300 && isActive) { inc++ } + + if (!isActive) break // breakout after timeout + } + + this@confirmTransaction // return result +} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/extensions/Transaction+Extensions.kt b/lib/src/main/java/com/metaplex/lib/extensions/Transaction+Extensions.kt index b8b8522..236ee54 100644 --- a/lib/src/main/java/com/metaplex/lib/extensions/Transaction+Extensions.kt +++ b/lib/src/main/java/com/metaplex/lib/extensions/Transaction+Extensions.kt @@ -10,7 +10,7 @@ package com.metaplex.lib.extensions import android.util.Base64 import com.metaplex.lib.drivers.indenty.IdentityDriver import com.metaplex.lib.drivers.solana.* -import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.Transaction import com.solana.vendor.ShortvecEncoding import com.solana.vendor.TweetNaclFast @@ -24,7 +24,7 @@ import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible suspend fun Transaction.sign(connection: Connection, payer: IdentityDriver, - additionalSigners: List = listOf()): Result { + additionalSigners: List = listOf()): Result { // set block hash setRecentBlockHash(connection.getRecentBlockhash().getOrElse { @@ -39,48 +39,7 @@ suspend fun Transaction.sign(connection: Connection, payer: IdentityDriver, result.onSuccess { signedTx -> // sign with additional signers - // cant sign directly because Transaction.sign() does not account for previous signatures -// signedTx.sign(additionalSigners) - - // need to modify the signing logic of Transaction to make this work - // (signing payer with Identity Driver, then signing with list of Account) - - // have to use reflection (gross) because these are private to Transaction - // this is temporary. I will make a change to SolanaKt, or use the partial signing - val serializedMessage: ByteArray? = - Transaction::class.memberProperties.find { it.name == "serializedMessage" } - ?.let { - it.isAccessible = true - it.get(this) as ByteArray - } - - val signatures: MutableList = - Transaction::class.memberProperties.find { it.name == "signatures" }?.let { - it.isAccessible = true - it.get(this) as MutableList - } ?: mutableListOf() - - // require serializedMessage and signature list, safe to force unwrap after this - require(serializedMessage != null && signatures.isNotEmpty()) - - // taken from Transaction.sign() - for (signer in additionalSigners) { - val signatureProvider = TweetNaclFast.Signature(ByteArray(0), signer.secretKey) - val signature = signatureProvider.detached(serializedMessage) - signatures.add(Base58.encode(signature)) - } - - // taken from Transaction.serialize() - val signaturesSize = signatures.size - val signaturesLength = ShortvecEncoding.encodeLength(signaturesSize) - val out = ByteBuffer.allocate(signaturesLength.size - + signaturesSize * Transaction.SIGNATURE_LENGTH + serializedMessage.size) - out.put(signaturesLength) - for (signature in signatures) { - val rawSignature = Base58.decode(signature) - out.put(rawSignature) - } - out.put(serializedMessage) + additionalSigners.forEach { signedTx.partialSign(it) } // resume continuation.resumeWith(Result.success(Result.success(signedTx))) @@ -92,7 +51,7 @@ suspend fun Transaction.sign(connection: Connection, payer: IdentityDriver, } suspend fun Transaction.signAndSend(connection: Connection, payer: IdentityDriver, - additionalSigners: List = listOf()): Result { + additionalSigners: List = listOf()): Result { val signedTxn: Transaction = sign(connection, payer, additionalSigners).getOrElse { return Result.failure(it) @@ -106,7 +65,7 @@ suspend fun Transaction.signAndSend(connection: Connection, payer: IdentityDrive ) } -suspend fun Transaction.signAndSend(connection: Connection, signers: List = listOf(), +suspend fun Transaction.signAndSend(connection: Connection, signers: List = listOf(), recentBlockhash: String? = null): Result { // set block hash @@ -122,25 +81,7 @@ suspend fun Transaction.signAndSend(connection: Connection, signers: List = listOf(), + connection: Connection, payer: IdentityDriver, additionalSigners: List = listOf(), transactionOptions: TransactionOptions = connection.transactionOptions -): Result = - signAndSend(connection, payer, additionalSigners).also { - withTimeout(transactionOptions.timeout.toMillis()) { - - suspend fun confirmationStatus() = - connection.getSignatureStatuses(listOf(it.getOrThrow()), null) - .getOrNull()?.first()?.confirmationStatus - - // wait for desired transaction status - while(confirmationStatus() != transactionOptions.commitment.toString()) { - - // wait a bit before retrying - val millis = System.currentTimeMillis() - var inc = 0 - while(System.currentTimeMillis() - millis < 300 && isActive) { inc++ } - - if (!isActive) break // breakout after timeout - } - } - } +): Result = signAndSend(connection, payer, additionalSigners) + .confirmTransaction(connection, transactionOptions) diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionHouseClient.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionHouseClient.kt index c55cdf7..aec868f 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionHouseClient.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionHouseClient.kt @@ -153,20 +153,4 @@ class AuctionHouseClient( connection, dispatcher) .build().getOrThrow().signSendAndConfirm(connection, signer) } -} - -// TODO: should move this stuff to appropriate places -const val SYSVAR_INSTRUCTIONS_PUBKEY = "Sysvar1nstructions1111111111111111111111111" - -// cherry picked from SolanaKT -fun PublicKey.Companion.associatedTokenAddress(walletAddress: PublicKey, - tokenMintAddress: PublicKey) -: PublicKey.ProgramDerivedAddress = - findProgramAddress( - listOf( - walletAddress.toByteArray(), - TokenProgram.PROGRAM_ID.toByteArray(), - tokenMintAddress.toByteArray() - ), - AssociatedTokenProgram.SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID - ) \ No newline at end of file +} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionsClient.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionsClient.kt index a6bfa22..8d046b8 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionsClient.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/AuctionsClient.kt @@ -8,7 +8,6 @@ package com.metaplex.lib.modules.auctions import com.metaplex.lib.drivers.indenty.IdentityDriver -import com.metaplex.lib.drivers.solana.getAccountInfo import com.metaplex.lib.modules.auctions.models.AuctionHouse import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.drivers.solana.TransactionOptions @@ -18,9 +17,12 @@ import com.metaplex.lib.extensions.signSendAndConfirm import com.metaplex.lib.modules.auctions.builders.CreateAuctionHouseTransactionBuilder import com.metaplex.lib.modules.auctions.models.Bid import com.metaplex.lib.modules.auctions.models.Listing -import com.metaplex.lib.modules.auctions.operations.FindAuctionHouseByAddressOperation +import com.metaplex.lib.modules.auctions.operations.FindAuctionHouseByAddressOperationHandler +import com.metaplex.lib.modules.auctions.operations.FindBidByReceiptAddressOperationHandler +import com.metaplex.lib.modules.auctions.operations.FindListingByReceiptAddressOperationHandler import com.metaplex.lib.modules.token.WRAPPED_SOL_MINT_ADDRESS import com.metaplex.lib.modules.token.operations.FindTokenMetadataAccountOperation +import com.metaplex.lib.serialization.serializers.solana.AnchorAccountSerializer import com.solana.core.PublicKey import kotlinx.coroutines.* @@ -37,9 +39,7 @@ class AuctionsClient(val connection: Connection, val signer: IdentityDriver, * Attempts to find an AuctionHouse account on chain via its [address] */ suspend fun findAuctionHouseByAddress(address: PublicKey): Result = - withContext(dispatcher) { - FindAuctionHouseByAddressOperation(connection).run(address) - } + FindAuctionHouseByAddressOperationHandler(connection, dispatcher).handle(address) /** * Attempts to find an AuctionHouse account on chain via its [creator] address and @@ -52,74 +52,18 @@ class AuctionsClient(val connection: Connection, val signer: IdentityDriver, /** * Attempts to find a Listing account on chain via its receipt [address] */ - suspend fun findListingByReceipt(address: PublicKey): Result

{ - connection.apply { - return getAccountInfo(address).map { - it.data!!.let { receipt ->// safe unwrap, successful result will not have null - val auctionHouse = findAuctionHouseByAddress(receipt.auctionHouse).getOrThrow() - val metadata = FindTokenMetadataAccountOperation(connection) - .run(receipt.metadata).getOrThrow().data!! - - Listing(auctionHouse, - receipt.seller, - auctionHouse.authority, - null, - metadata.mint, - PublicKey.associatedTokenAddress(receipt.seller, metadata.mint).address, - receipt.price.toLong(), - receipt.tokenSize.toLong(), - receipt.bookkeeper, - receipt.canceledAt - ) - } - } - } - } + suspend fun findListingByReceipt(address: PublicKey): Result = + FindListingByReceiptAddressOperationHandler(connection, dispatcher).handle(address) /** * Attempts to find a Listing account on chain via its receipt [address] */ - suspend fun findBidByReceipt(address: PublicKey): Result { - connection.apply { - return getAccountInfo(address).map { - it.data!!.let { receipt ->// safe unwrap, successful result will not have null - val auctionHouse = findAuctionHouseByAddress(receipt.auctionHouse).getOrThrow() - val metadata = FindTokenMetadataAccountOperation(connection) - .run(receipt.metadata).getOrThrow().data!! - - Bid(auctionHouse, - metadata.mint, - receipt.buyer, - auctionHouse.authority, - null, - null, - receipt.price.toLong(), - receipt.tokenSize.toLong(), - receipt.bookkeeper, - receipt.canceledAt - ) - } - } - } - } + suspend fun findBidByReceipt(address: PublicKey): Result = + FindBidByReceiptAddressOperationHandler(connection, dispatcher).handle(address) /** - * Async-callback version of [findAuctionHouseByAddress] + * Creates a new Auction House instance on chain with the supplied parameters */ - fun findAuctionHouseByAddress(address: PublicKey, onComplete: (Result) -> Unit) = - CoroutineScope(Dispatchers.IO).launch { - onComplete(findAuctionHouseByAddress(address)) - } - - /** - * Async-callback version of [findAuctionHouseByCreatorAndMint] - */ - fun findAuctionHouseByCreatorAndMint(creator: PublicKey, mint: PublicKey, - onComplete: (Result) -> Unit) = - CoroutineScope(Dispatchers.IO).launch { - onComplete(findAuctionHouseByCreatorAndMint(creator, mint)) - } - suspend fun createAuctionHouse( sellerFeeBasisPoints: Int, canChangeSalePrice: Boolean = false, diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseBuyTransactionBuilder.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseBuyTransactionBuilder.kt index d08b4a3..017cb85 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseBuyTransactionBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseBuyTransactionBuilder.kt @@ -9,8 +9,6 @@ package com.metaplex.lib.modules.auctions.builders import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouseInstructions -import com.metaplex.lib.modules.auctions.SYSVAR_INSTRUCTIONS_PUBKEY -import com.metaplex.lib.modules.auctions.associatedTokenAddress import com.metaplex.lib.modules.auctions.models.* import com.metaplex.lib.shared.builders.TransactionBuilder import com.solana.core.* @@ -62,7 +60,7 @@ class AuctionHouseBuyTransactionBuilder( systemProgram = SystemProgram.PROGRAM_ID, ataProgram = AssociatedTokenProgram.SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, programAsSigner = programAsSigner.address, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, freeTradeStateBump = freeTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), programAsSignerBump = programAsSigner.nonce.toUByte(), @@ -89,7 +87,7 @@ class AuctionHouseBuyTransactionBuilder( systemProgram = SystemProgram.PROGRAM_ID, ataProgram = AssociatedTokenProgram.SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, programAsSigner = programAsSigner.address, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, freeTradeStateBump = freeTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), programAsSignerBump = programAsSigner.nonce.toUByte(), @@ -121,8 +119,8 @@ class AuctionHouseBuyTransactionBuilder( bidReceipt = bidReceipt.address, bookkeeper = bookkeeper, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, - instruction = PublicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + rent = Sysvar.SYSVAR_RENT_PUBKEY, + instruction = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, purchaseReceiptBump = purchaseReceipt.nonce.toUByte() ) ) diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseCancelTransactionBuilder.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseCancelTransactionBuilder.kt index 24af5db..155c1a4 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseCancelTransactionBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/AuctionHouseCancelTransactionBuilder.kt @@ -9,11 +9,10 @@ package com.metaplex.lib.modules.auctions.builders import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouseInstructions -import com.metaplex.lib.modules.auctions.SYSVAR_INSTRUCTIONS_PUBKEY -import com.metaplex.lib.modules.auctions.associatedTokenAddress import com.metaplex.lib.modules.auctions.models.* import com.metaplex.lib.shared.builders.TransactionBuilder import com.solana.core.PublicKey +import com.solana.core.Sysvar import com.solana.core.Transaction import com.solana.programs.SystemProgram import com.solana.programs.TokenProgram @@ -86,12 +85,12 @@ class AuctionHouseCancelTransactionBuilder( addInstruction( when (mode) { Mode.LISTING -> AuctionHouseInstructions.cancelListingReceipt( receipt = purchaseReceipt, - instruction = PublicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + instruction = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, systemProgram = SystemProgram.PROGRAM_ID ) Mode.BID -> AuctionHouseInstructions.cancelBidReceipt( receipt = purchaseReceipt, - instruction = PublicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + instruction = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, systemProgram = SystemProgram.PROGRAM_ID ) }) diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/CreateAuctionHouseTransactionBuilder.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/CreateAuctionHouseTransactionBuilder.kt index fbd3780..feee0f3 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/CreateAuctionHouseTransactionBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/builders/CreateAuctionHouseTransactionBuilder.kt @@ -9,7 +9,6 @@ package com.metaplex.lib.modules.auctions.builders import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouseInstructions -import com.metaplex.lib.modules.auctions.associatedTokenAddress import com.metaplex.lib.modules.auctions.models.AuctionHouse import com.metaplex.lib.modules.auctions.models.feeAccountPda import com.metaplex.lib.modules.auctions.models.treasuryAccountPda @@ -60,7 +59,7 @@ class CreateAuctionHouseTransactionBuilder(val auctionHouse: AuctionHouse, tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, ataProgram = AssociatedTokenProgram.SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, bump = auctionHousePda.nonce.toUByte(), feePayerBump = feeAccountPda.nonce.toUByte(), treasuryBump = treasuryAccountPda.nonce.toUByte(), diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Bid.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Bid.kt index 8c7e4e7..abcd174 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Bid.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Bid.kt @@ -8,8 +8,6 @@ package com.metaplex.lib.modules.auctions.models import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouseInstructions -import com.metaplex.lib.modules.auctions.SYSVAR_INSTRUCTIONS_PUBKEY -import com.metaplex.lib.modules.auctions.associatedTokenAddress import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.solana.core.PublicKey import com.solana.core.Sysvar @@ -65,7 +63,7 @@ fun Bid.buildTransaction(printReceipt: Boolean = true) = Transaction().apply { ahAuctioneerPda = auctionHouse.auctioneerPda(authority), tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, tradeStateBump = buyerTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), buyerPrice = price.toULong(), tokenSize = tokens.toULong() @@ -86,7 +84,7 @@ fun Bid.buildTransaction(printReceipt: Boolean = true) = Transaction().apply { ahAuctioneerPda = auctionHouse.auctioneerPda(authority), tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, tradeStateBump = buyerTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), buyerPrice = price.toULong(), tokenSize = tokens.toULong() @@ -106,7 +104,7 @@ fun Bid.buildTransaction(printReceipt: Boolean = true) = Transaction().apply { tokenAccount = tokenAccount, tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, tradeStateBump = buyerTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), buyerPrice = price.toULong(), tokenSize = tokens.toULong() @@ -125,7 +123,7 @@ fun Bid.buildTransaction(printReceipt: Boolean = true) = Transaction().apply { tokenAccount = buyerTokenAccount, tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, tradeStateBump = buyerTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), buyerPrice = price.toULong(), tokenSize = tokens.toULong() @@ -141,9 +139,9 @@ fun Bid.buildTransaction(printReceipt: Boolean = true) = Transaction().apply { AuctionHouseInstructions.printBidReceipt( receipt = receipt.address, bookkeeper = bookkeeper, - instruction = PublicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + instruction = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, receiptBump = receipt.nonce.toUByte() ) ) diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Listing.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Listing.kt index 01dc7d9..d630e99 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Listing.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Listing.kt @@ -8,8 +8,6 @@ package com.metaplex.lib.modules.auctions.models import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouseInstructions -import com.metaplex.lib.modules.auctions.SYSVAR_INSTRUCTIONS_PUBKEY -import com.metaplex.lib.modules.auctions.associatedTokenAddress import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.solana.core.PublicKey import com.solana.core.Sysvar @@ -66,7 +64,7 @@ internal fun Listing.buildTransaction(printReceipt: Boolean = true) = Transactio ahAuctioneerPda = auctionHouse.auctioneerPda(authority), tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, tradeStateBump = sellerTradeState.nonce.toUByte(), freeTradeStateBump = freeWalletTradeState.nonce.toUByte(), programAsSignerBump = programAsSigner.nonce.toUByte(), @@ -84,7 +82,7 @@ internal fun Listing.buildTransaction(printReceipt: Boolean = true) = Transactio tokenAccount = tokenAccount, tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, tradeStateBump = sellerTradeState.nonce.toUByte(), freeTradeStateBump = freeWalletTradeState.nonce.toUByte(), programAsSignerBump = programAsSigner.nonce.toUByte(), @@ -101,9 +99,9 @@ internal fun Listing.buildTransaction(printReceipt: Boolean = true) = Transactio AuctionHouseInstructions.printListingReceipt( receipt = receipt.address, bookkeeper = bookkeeper, - instruction = PublicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + instruction = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, receiptBump = receipt.nonce.toUByte() ) ) diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Purchase.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Purchase.kt index e3be812..fc0606a 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Purchase.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/models/Purchase.kt @@ -8,9 +8,6 @@ package com.metaplex.lib.modules.auctions.models import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouseInstructions -import com.metaplex.lib.modules.auctions.SYSVAR_INSTRUCTIONS_PUBKEY -import com.metaplex.lib.modules.auctions.associatedTokenAddress -import com.metaplex.lib.modules.nfts.operations.FindNftByMintOperation import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.solana.core.* import com.solana.programs.AssociatedTokenProgram @@ -74,7 +71,7 @@ fun Purchase.buildTransaction(printReceipt: Boolean = true) = Transaction().appl systemProgram = SystemProgram.PROGRAM_ID, ataProgram = AssociatedTokenProgram.SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, programAsSigner = programAsSigner.address, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, freeTradeStateBump = freeTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), programAsSignerBump = programAsSigner.nonce.toUByte(), @@ -101,7 +98,7 @@ fun Purchase.buildTransaction(printReceipt: Boolean = true) = Transaction().appl systemProgram = SystemProgram.PROGRAM_ID, ataProgram = AssociatedTokenProgram.SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, programAsSigner = programAsSigner.address, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, freeTradeStateBump = freeTradeState.nonce.toUByte(), escrowPaymentBump = escrowPayment.nonce.toUByte(), programAsSignerBump = programAsSigner.nonce.toUByte(), @@ -133,8 +130,8 @@ fun Purchase.buildTransaction(printReceipt: Boolean = true) = Transaction().appl bidReceipt = bidReceipt.address, bookkeeper = bookkeeper, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, - instruction = PublicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + rent = Sysvar.SYSVAR_RENT_PUBKEY, + instruction = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, purchaseReceiptBump = purchaseReceipt.nonce.toUByte() ) ) diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperation.kt similarity index 50% rename from lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationHandler.kt rename to lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperation.kt index f056585..23cff23 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperation.kt @@ -7,19 +7,19 @@ package com.metaplex.lib.modules.auctions.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE -import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.drivers.solana.getAccountInfo import com.metaplex.lib.modules.auctions.models.AuctionHouse +import com.metaplex.lib.serialization.serializers.solana.AnchorAccountSerializer import com.metaplex.lib.shared.* import com.solana.core.PublicKey import kotlinx.coroutines.* +import com.metaplex.lib.experimental.jen.auctionhouse.AuctionHouse as AuctionHouseAccount + class FindAuctionHouseByAddressOperation(override val connection: Connection) : SuspendOperation { override suspend fun run(input: PublicKey): Result = - connection.getAccountInfo(input) + connection.getAccountInfo(AnchorAccountSerializer(), input) .map { it.data!!.let { // safe unwrap, successful result will not have null AuctionHouse( @@ -40,38 +40,7 @@ class FindAuctionHouseByAddressOperation(override val connection: Connection) class FindAuctionHouseByAddressOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler { - - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} - override suspend fun handle(input: PublicKey): Result = withContext(dispatcher) { FindAuctionHouseByAddressOperation(connection).run(input) } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: OperationResult) - : OperationResult = - operation.flatMap { mintKey -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKey) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb( - ResultWithCustomError.failure( - OperationError.GetMetadataAccountInfoError(java.lang.RuntimeException(it)))) - } - } - } - } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindBidByReceiptAddressOperation.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindBidByReceiptAddressOperation.kt new file mode 100644 index 0000000..259c849 --- /dev/null +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindBidByReceiptAddressOperation.kt @@ -0,0 +1,52 @@ +/* + * FindBidByReceiptAddressOperation + * Metaplex + * + * Created by Funkatronics on 10/24/2022 + */ + +package com.metaplex.lib.modules.auctions.operations + +import com.metaplex.lib.drivers.solana.Connection +import com.metaplex.lib.experimental.jen.auctionhouse.BidReceipt +import com.metaplex.lib.modules.auctions.models.Bid +import com.metaplex.lib.modules.token.operations.FindTokenMetadataAccountOperation +import com.metaplex.lib.serialization.serializers.solana.AnchorAccountSerializer +import com.metaplex.lib.shared.* +import com.solana.core.PublicKey +import kotlinx.coroutines.* + +class FindBidReceiptByAddressOperation(override val connection: Connection) + : SuspendOperation { + override suspend fun run(input: PublicKey): Result = + connection.getAccountInfo(AnchorAccountSerializer(), input).map { + it.data!! // safe unwrap, successful result will not have null + } +} + +class FindBidByReceiptAddressOperationHandler(override val connection: Connection, + override val dispatcher: CoroutineDispatcher = Dispatchers.IO) + : OperationHandler { + override suspend fun handle(input: PublicKey): Result = withContext(dispatcher) { + FindBidReceiptByAddressOperation(connection).run(input).map { receipt -> + + val auctionHouse = FindAuctionHouseByAddressOperation(connection) + .run(receipt.auctionHouse).getOrThrow() + + val metadata = FindTokenMetadataAccountOperation(connection) + .run(receipt.metadata).getOrThrow().data!! + + Bid(auctionHouse, + metadata.mint, + receipt.buyer, + auctionHouse.authority, + null, + null, + receipt.price.toLong(), + receipt.tokenSize.toLong(), + receipt.bookkeeper, + receipt.canceledAt + ) + } + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindListingByReceiptAddressOperation.kt b/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindListingByReceiptAddressOperation.kt new file mode 100644 index 0000000..0303b5d --- /dev/null +++ b/lib/src/main/java/com/metaplex/lib/modules/auctions/operations/FindListingByReceiptAddressOperation.kt @@ -0,0 +1,52 @@ +/* + * FindListingByReceiptAddressOperation + * Metaplex + * + * Created by Funkatronics on 10/24/2022 + */ + +package com.metaplex.lib.modules.auctions.operations + +import com.metaplex.lib.drivers.solana.Connection +import com.metaplex.lib.experimental.jen.auctionhouse.ListingReceipt +import com.metaplex.lib.modules.auctions.models.Listing +import com.metaplex.lib.modules.token.operations.FindTokenMetadataAccountOperation +import com.metaplex.lib.serialization.serializers.solana.AnchorAccountSerializer +import com.metaplex.lib.shared.* +import com.solana.core.PublicKey +import kotlinx.coroutines.* + +class FindListingReceiptByAddressOperation(override val connection: Connection) + : SuspendOperation { + override suspend fun run(input: PublicKey): Result = + connection.getAccountInfo(AnchorAccountSerializer(), input).map { + it.data!! // safe unwrap, successful result will not have null + } +} + +class FindListingByReceiptAddressOperationHandler(override val connection: Connection, + override val dispatcher: CoroutineDispatcher = Dispatchers.IO) + : OperationHandler { + override suspend fun handle(input: PublicKey): Result = withContext(dispatcher) { + FindListingReceiptByAddressOperation(connection).run(input).map { receipt -> + + val auctionHouse = FindAuctionHouseByAddressOperation(connection) + .run(receipt.auctionHouse).getOrThrow() + + val metadata = FindTokenMetadataAccountOperation(connection) + .run(receipt.metadata).getOrThrow().data!! + + Listing(auctionHouse, + receipt.seller, + auctionHouse.authority, + null, + metadata.mint, + PublicKey.associatedTokenAddress(receipt.seller, metadata.mint).address, + receipt.price.toLong(), + receipt.tokenSize.toLong(), + receipt.bookkeeper, + receipt.canceledAt + ) + } + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/candymachines/CandyMachineClient.kt b/lib/src/main/java/com/metaplex/lib/modules/candymachines/CandyMachineClient.kt index fa6e94c..6114d6e 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/candymachines/CandyMachineClient.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/candymachines/CandyMachineClient.kt @@ -22,7 +22,7 @@ import com.metaplex.lib.modules.candymachines.builders.SetCollectionTransactionB import com.metaplex.lib.modules.candymachines.models.CandyMachineItem import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.modules.nfts.operations.FindNftByMintOnChainOperationHandler -import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -40,7 +40,7 @@ class CandyMachineClient(val connection: Connection, val signer: IdentityDriver, transactionOptions: TransactionOptions = txOptions ): Result = runCatching { - val candyMachineAccount = Account() + val candyMachineAccount = HotAccount() val candyMachineAddress = candyMachineAccount.publicKey CandyMachine( @@ -89,7 +89,7 @@ class CandyMachineClient(val connection: Connection, val signer: IdentityDriver, transactionOptions: TransactionOptions = txOptions): Result = runCatching { - val newMintAccount = Account() + val newMintAccount = HotAccount() MintNftTransactionBuilder(candyMachine, newMintAccount.publicKey, signer.publicKey, connection, dispatcher) diff --git a/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/CandyMachineV2Client.kt b/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/CandyMachineV2Client.kt index 5bdb44c..4b9e451 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/CandyMachineV2Client.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/CandyMachineV2Client.kt @@ -17,7 +17,7 @@ import com.metaplex.lib.modules.candymachinesv2.models.CandyMachineV2 import com.metaplex.lib.modules.candymachinesv2.operations.FindCandyMachineV2ByAddressOperationHandler import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.modules.nfts.operations.FindNftByMintOnChainOperationHandler -import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -35,7 +35,7 @@ class CandyMachineV2Client(val connection: Connection, val signer: IdentityDrive transactionOptions: TransactionOptions = txOptions ): Result = runCatching { - val candyMachineAccount = Account() + val candyMachineAccount = HotAccount() val candyMachineAddress = candyMachineAccount.publicKey CandyMachineV2( @@ -60,7 +60,7 @@ class CandyMachineV2Client(val connection: Connection, val signer: IdentityDrive candyMachine: CandyMachineV2, transactionOptions: TransactionOptions = txOptions) : Result = runCatching { - val newMintAccount = Account() + val newMintAccount = HotAccount() MintNftTransactionBuilder(candyMachine, newMintAccount.publicKey, signer.publicKey, connection, dispatcher) .build() diff --git a/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/CreateCandyMachineV2TransactionBuilder.kt b/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/CreateCandyMachineV2TransactionBuilder.kt index 82f2fa8..d79f72a 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/CreateCandyMachineV2TransactionBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/CreateCandyMachineV2TransactionBuilder.kt @@ -50,7 +50,7 @@ class CreateCandyMachineV2TransactionBuilder(val candyMachine: CandyMachineV2, p authority = authority, payer = payer, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, data = CandyMachineData( uuid = uuid, price = price.toULong(), diff --git a/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/MintNftTransactionBuilder.kt b/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/MintNftTransactionBuilder.kt index 842149d..b2a471a 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/MintNftTransactionBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/candymachinesv2/builders/MintNftTransactionBuilder.kt @@ -51,7 +51,7 @@ class MintNftTransactionBuilder(val candyMachine: CandyMachineV2, val newMint: P tokenMetadataProgram = TokenMetadataProgram.publicKey, tokenProgram = TokenProgram.publicKey, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, clock = Sysvar.SYSVAR_CLOCK_PUBKEY, recentBlockhashes = Sysvar.SYSVAR_SLOT_HASHES_PUBKEY, instructionSysvarAccount = Sysvar.SYSVAR_INSTRUCTIONS_PUBKEY, diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/JsonMetadataTask.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/JsonMetadataTask.kt index e8e40e9..d0709b8 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/JsonMetadataTask.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/JsonMetadataTask.kt @@ -10,7 +10,6 @@ import com.metaplex.lib.modules.nfts.models.JsonMetadataFileAdapter import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.modules.token.models.Token import com.metaplex.lib.shared.ResultWithCustomError -import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonDataException import com.squareup.moshi.Moshi import kotlinx.coroutines.CoroutineScope diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/NftClient.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/NftClient.kt index e29367e..39afdc2 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/NftClient.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/NftClient.kt @@ -1,25 +1,19 @@ package com.metaplex.lib.modules.nfts -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.indenty.IdentityDriver import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.drivers.solana.TransactionOptions import com.metaplex.lib.extensions.signSendAndConfirm -import com.metaplex.lib.modules.candymachines.CandyMachineClient import com.metaplex.lib.modules.nfts.builders.CreateNftTransactionBuilder import com.metaplex.lib.modules.nfts.models.Metadata import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.modules.nfts.operations.* import com.metaplex.lib.modules.token.TokenClient -import com.metaplex.lib.shared.OperationError -import com.metaplex.lib.shared.ResultWithCustomError -import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch /** * NFT Client @@ -61,7 +55,7 @@ class NftClient(private val connection: Connection, val signer: IdentityDriver, transactionOptions: TransactionOptions = txOptions ): Result = runCatching { - val newMintAccount = Account() + val newMintAccount = HotAccount() CreateNftTransactionBuilder(newMintAccount.publicKey, metadata, isCollection, signer.publicKey, connection, dispatcher) @@ -71,81 +65,4 @@ class NftClient(private val connection: Connection, val signer: IdentityDriver, return FindNftByMintOnChainOperationHandler(connection, dispatcher) .handle(newMintAccount.publicKey) } - - //region DEPRECATED METHODS - //region ASYNC-CALLBACK API - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("findByMint(mintKey)")) - fun findByMint(mintKey: PublicKey, - onComplete: (ResultWithCustomError) -> Unit) { - CoroutineScope(dispatcher).launch { findByMint(mintKey) - .onSuccess { onComplete(ResultWithCustomError.success(it)) } - .onFailure { onComplete(ResultWithCustomError.failure(it as OperationError)) } - } - } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("findAllByMintList(mintKeys)")) - fun findAllByMintList(mintKeys: List, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - CoroutineScope(dispatcher).launch { findAllByMintList(mintKeys) - .onSuccess { onComplete(ResultWithCustomError.success(it)) } - .onFailure { onComplete(ResultWithCustomError.failure(it as OperationError)) } - } - } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("findAllByOwner(mintKeys)")) - fun findAllByOwner(publicKey: PublicKey, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - CoroutineScope(dispatcher).launch { findAllByOwner(publicKey) - .onSuccess { onComplete(ResultWithCustomError.success(it)) } - .onFailure { onComplete(ResultWithCustomError.failure(it as OperationError)) } - } - } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("findAllByCreator(mintKeys)")) - fun findAllByCreator(creator: PublicKey, position: Int? = 1, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - CoroutineScope(dispatcher).launch { findAllByCreator(creator, position) - .onSuccess { onComplete(ResultWithCustomError.success(it)) } - .onFailure { onComplete(ResultWithCustomError.failure(it as OperationError)) } - } - } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("findAllByCandyMachine(mintKeys)")) - fun findAllByCandyMachine(candyMachine: PublicKey, version: UInt? = 2U, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - CoroutineScope(dispatcher).launch { findAllByCandyMachine(candyMachine, version) - .onSuccess { onComplete(ResultWithCustomError.success(it)) } - .onFailure { onComplete(ResultWithCustomError.failure(it as OperationError)) } - } - } - //endregion - - //region LEGACY API - @Deprecated("This method is obsolete and has been replaced by findByMint()", - ReplaceWith("findByMint(mintKey, onComplete)"), DeprecationLevel.WARNING) - fun findNftByMint(mintKey: PublicKey, onComplete: (ResultWithCustomError) -> Unit){ - findByMint(mintKey, onComplete) - } - - @Deprecated("This method is obsolete and has been replaced by findAllByMintList()", - ReplaceWith("findAllByMintList(mintKeys, onComplete)"), DeprecationLevel.WARNING) - fun findNftByMintList(mintKeys: List, onComplete: (ResultWithCustomError, OperationError>) -> Unit){ - findAllByMintList(mintKeys, onComplete) - } - - @Deprecated("This method is obsolete and has been replaced by findAllByOwner()", - ReplaceWith("findAllByOwner(publicKey, onComplete)"), DeprecationLevel.WARNING) - fun findNftsByOwner(publicKey: PublicKey, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - findAllByOwner(publicKey, onComplete) - } - - @Deprecated("This method is obsolete and has been replaced by findAllByCreator()", - ReplaceWith("findAllByCreator(creator, position, onComplete)"), DeprecationLevel.WARNING) - fun findNftsByCreator(creator: PublicKey, position: Int? = 1, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - findAllByCreator(creator, position, onComplete) - } - - @Deprecated("This method is obsolete and has been replaced by findAllByCandyMachine()", - ReplaceWith("findAllByCandyMachine(candyMachine, version, onComplete)"), DeprecationLevel.WARNING) - fun findNftsByCandyMachine(candyMachine: PublicKey, version: UInt? = 2U, onComplete: (ResultWithCustomError, OperationError>) -> Unit) { - findAllByCandyMachine(candyMachine, version, onComplete) - } - //endregion - //endregion } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/builders/CreateNftTransactionBuilder.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/builders/CreateNftTransactionBuilder.kt index ef1ab94..e0929a9 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/builders/CreateNftTransactionBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/builders/CreateNftTransactionBuilder.kt @@ -44,7 +44,7 @@ class CreateNftTransactionBuilder( payer = payer, updateAuthority = payer, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, createMetadataAccountArgsV3 = CreateMetadataAccountArgsV3( DataV2( name = metadata.name, @@ -72,7 +72,7 @@ class CreateNftTransactionBuilder( metadata = newMetadata, tokenProgram = TokenProgram.PROGRAM_ID, systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, + rent = Sysvar.SYSVAR_RENT_PUBKEY, createMasterEditionArgs = CreateMasterEditionArgs(0.toULong()) )) }) diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftByMintOnChainOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftByMintOnChainOperationHandler.kt index d2296b8..336d254 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftByMintOnChainOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftByMintOnChainOperationHandler.kt @@ -1,6 +1,5 @@ package com.metaplex.lib.modules.nfts.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.modules.nfts.models.NFT @@ -10,25 +9,12 @@ import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.metaplex.lib.shared.* import com.solana.core.PublicKey import kotlinx.coroutines.* -import java.lang.RuntimeException - -typealias FindNftByMintOperation = OperationResult class FindNftByMintOnChainOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler { - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} + constructor(metaplex: Metaplex) : this(metaplex.connection) override suspend fun handle(input: PublicKey): Result = withContext(dispatcher) { @@ -53,20 +39,4 @@ class FindNftByMintOnChainOperationHandler(override val connection: Connection, Result.failure(OperationError.NilDataOnAccount) } } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: FindNftByMintOperation): OperationResult = - operation.flatMap { mintKey -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKey) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb(ResultWithCustomError.failure( - OperationError.GetMetadataAccountInfoError(RuntimeException(it)))) - } - } - } - } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCandyMachineOnChainOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCandyMachineOnChainOperationHandler.kt index e6aad54..8d797b2 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCandyMachineOnChainOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCandyMachineOnChainOperationHandler.kt @@ -1,42 +1,24 @@ package com.metaplex.lib.modules.nfts.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.modules.nfts.models.MetaplexContstants import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.shared.* import com.solana.core.PublicKey import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.bitcoinj.core.Base58 -import java.lang.RuntimeException data class FindNftsByCandyMachineInput( val candyMachine : PublicKey, val version : UInt?, ) -typealias FindNftsByCandyMachineOperation = OperationResult - val candyMachineId = PublicKey("cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ") class FindNftsByCandyMachineOnChainOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler> { - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} + constructor(metaplex: Metaplex) : this(metaplex.connection) override suspend fun handle(input: FindNftsByCandyMachineInput): Result> { val cmAddress = if((input.version ?: 2) == 2){ @@ -54,20 +36,4 @@ class FindNftsByCandyMachineOnChainOperationHandler(override val connection: Con return FindNftsByCreatorOnChainOperationHandler(connection, dispatcher) .handle(FindNftsByCreatorInput(cmAddress, 1)) } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: FindNftsByCandyMachineOperation): OperationResult, OperationError> { - return operation.flatMap { mintKey -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKey) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb(ResultWithCustomError.failure(it as OperationError)) - } - } - } - } - } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCreatorOnChainOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCreatorOnChainOperationHandler.kt index b01c6df..70b7c57 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCreatorOnChainOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByCreatorOnChainOperationHandler.kt @@ -1,6 +1,5 @@ package com.metaplex.lib.modules.nfts.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.modules.nfts.models.NFT @@ -8,32 +7,18 @@ import com.metaplex.lib.programs.token_metadata.TokenMetadataProgram import com.metaplex.lib.shared.* import com.solana.core.PublicKey import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch data class FindNftsByCreatorInput ( val creator: PublicKey, val position: Int? ) -typealias FindNftsByCreatorOperation = OperationResult - class FindNftsByCreatorOnChainOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler> { - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} + constructor(metaplex: Metaplex) : this(metaplex.connection) val metadataV1GpaBuilder = TokenMetadataProgram.metadataV1Accounts(this.connection) @@ -41,7 +26,7 @@ class FindNftsByCreatorOnChainOperationHandler(override val connection: Connecti metadataV1GpaBuilder .selectMint() .whereCreator(input.position ?: 1, input.creator) - .getSuspend() + .get() .getOrElse { return Result.failure(OperationError.GetFindNftsByCreatorOperation(it)) }.mapNotNull { @@ -49,20 +34,4 @@ class FindNftsByCreatorOnChainOperationHandler(override val connection: Connecti }.let { publicKeys -> FindNftsByMintListOnChainOperationHandler(connection, dispatcher).handle(publicKeys) } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: FindNftsByCreatorOperation): OperationResult, OperationError> = - operation.flatMap { mintKeys -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKeys) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb(ResultWithCustomError.failure(it as? OperationError - ?: OperationError.GetFindNftsByCreatorOperation(it))) - } - } - } - } } diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByMintListOnChainOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByMintListOnChainOperationHandler.kt index cb3a47a..8fdbf91 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByMintListOnChainOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByMintListOnChainOperationHandler.kt @@ -1,32 +1,19 @@ package com.metaplex.lib.modules.nfts.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.serialization.serializers.legacy.BorshCodeableSerializer import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.metaplex.lib.shared.* import com.solana.core.PublicKey -import kotlinx.coroutines.* - -typealias FindNftsByMintListOperation = OperationResult, OperationError> +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers class FindNftsByMintListOnChainOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler, List> { - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} + constructor(metaplex: Metaplex) : this(metaplex.connection) // Rather than refactoring GmaBuilder to use coroutines, I just pulled the required logic // out and implemented it here. In the future we can refactor GmaBuilder if needed @@ -38,37 +25,12 @@ class FindNftsByMintListOnChainOperationHandler(override val connection: Connect ?: return Result.failure(OperationError.CouldNotFindPDA) }.chunked(chunkSize).map { chunk -> // TODO: how can I parallelize this? - gma(chunk).getOrElse { + connection.getMultipleAccountsInfo(MetadataAccount.serializer(), chunk).getOrElse { return Result.failure(OperationError.GmaBuilderError(it)) } }.flatten().map { account -> - if (account.exists && account.metadata != null) - NFT(account.metadata, null) - else null - }) - - private suspend fun gma(publicKeys: List): Result> = - connection.getMultipleAccountsInfo( - BorshCodeableSerializer(MetadataAccount::class.java), publicKeys).map { - publicKeys.zip(it) { publicKey, account -> - val metadata = account?.data as? MetadataAccount - MaybeAccountInfoWithPublicKey(publicKey, metadata != null, metadata) - } - } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: FindNftsByMintListOperation): OperationResult, OperationError> = - operation.flatMap { mintKeys -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKeys) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb(ResultWithCustomError.failure(it as? OperationError - ?: OperationError.GmaBuilderError(it))) - } - } + account?.data?.let { + NFT(account.data, null) } - } + }) } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandler.kt index 1b85fe1..89a8228 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandler.kt @@ -1,8 +1,6 @@ package com.metaplex.lib.modules.nfts.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex -import com.metaplex.lib.drivers.solana.AccountPublicKey import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.modules.nfts.models.NFT import com.metaplex.lib.programs.token_metadata.gpa_builders.TokenGpaBuilder @@ -10,27 +8,13 @@ import com.metaplex.lib.programs.tokens.TokenProgram import com.metaplex.lib.shared.* import com.solana.core.PublicKey import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -typealias FindNftsByOwnerOperation = OperationResult class FindNftsByOwnerOnChainOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler> { - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} + constructor(metaplex: Metaplex) : this(metaplex.connection) var tokenGpaBuilder: TokenGpaBuilder = TokenProgram.tokenAccounts(this.connection) @@ -39,7 +23,7 @@ class FindNftsByOwnerOnChainOperationHandler(override val connection: Connection .selectMint() .whereOwner(input) .whereAmount(1) - .getSuspend() + .get() .getOrElse { return Result.failure(OperationError.GetFindNftsByOwnerOperation(it)) }.mapNotNull { @@ -47,20 +31,4 @@ class FindNftsByOwnerOnChainOperationHandler(override val connection: Connection }.let { publicKeys -> FindNftsByMintListOnChainOperationHandler(connection, dispatcher).handle(publicKeys) } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: FindNftsByOwnerOperation): OperationResult, OperationError> = - operation.flatMap { mintKeys -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKeys) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb(ResultWithCustomError.failure(it as? OperationError - ?: OperationError.GetFindNftsByOwnerOperation(it))) - } - } - } - } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/token/builders/CreateMintWithTokenInstruction.kt b/lib/src/main/java/com/metaplex/lib/modules/token/builders/CreateMintWithTokenInstruction.kt index 9596ac8..d84d5aa 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/token/builders/CreateMintWithTokenInstruction.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/token/builders/CreateMintWithTokenInstruction.kt @@ -7,9 +7,6 @@ package com.metaplex.lib.modules.token.builders -import com.metaplex.lib.extensions.initializeMint -import com.metaplex.lib.extensions.mintTo -import com.metaplex.lib.modules.auctions.associatedTokenAddress import com.metaplex.lib.modules.token.MINT_SIZE import com.metaplex.lib.modules.token.MIN_RENT_FOR_MINT import com.metaplex.lib.programs.tokens.TokenProgram diff --git a/lib/src/main/java/com/metaplex/lib/modules/token/models/Token.kt b/lib/src/main/java/com/metaplex/lib/modules/token/models/Token.kt index 84119c5..65ee8c3 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/token/models/Token.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/token/models/Token.kt @@ -20,12 +20,12 @@ open class Token (val metadataAccount: MetadataAccount) { val name: String = metadataAccount.data.name val symbol: String = metadataAccount.data.symbol val uri: String = metadataAccount.data.uri - val sellerFeeBasisPoints: Int = metadataAccount.data.sellerFeeBasisPoints - val creators: Array = metadataAccount.data.creators + val sellerFeeBasisPoints: Int = metadataAccount.data.sellerFeeBasisPoints.toInt() + val creators: Array = metadataAccount.data.creators ?: arrayOf() val primarySaleHappened: Boolean = metadataAccount.primarySaleHappened val isMutable: Boolean = metadataAccount.isMutable - val editionNonce: Int? = metadataAccount.editionNonce - val tokenStandard: MetaplexTokenStandard? = metadataAccount.tokenStandard + val editionNonce: Int? = metadataAccount.editionNonce?.toInt() + val tokenStandard: MetaplexTokenStandard? = metadataAccount.tokenStandard ?: MetaplexTokenStandard.NonFungible val collection: MetaplexCollection? = metadataAccount.collection val collectionDetails: MetaplexCollectionDetails? = metadataAccount.collectionDetails } diff --git a/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindFungibleTokenByMintOnChainOperationHandler.kt b/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindFungibleTokenByMintOnChainOperationHandler.kt index 81149e1..1ef0c18 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindFungibleTokenByMintOnChainOperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindFungibleTokenByMintOnChainOperationHandler.kt @@ -1,6 +1,5 @@ package com.metaplex.lib.modules.token.operations -import com.metaplex.lib.ASYNC_CALLBACK_DEPRECATION_MESSAGE import com.metaplex.lib.Metaplex import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.modules.token.models.FungibleToken @@ -8,25 +7,12 @@ import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.metaplex.lib.shared.* import com.solana.core.PublicKey import kotlinx.coroutines.* -import java.lang.RuntimeException - -typealias FindFungibleTokenByMintOperation = OperationResult class FindFungibleTokenByMintOnChainOperationHandler(override val connection: Connection, override val dispatcher: CoroutineDispatcher = Dispatchers.IO) : OperationHandler { - override var metaplex: Metaplex - get() = maybeMetaplex ?: throw IllegalStateException( - "Metaplex object was not injected, and dependency forwarding is obsolete and has been " + - "replaced with direct dependency injection") - set(value) { - maybeMetaplex = value - } - - private var maybeMetaplex: Metaplex? = null - - constructor(metaplex: Metaplex) : this(metaplex.connection) { this.maybeMetaplex = metaplex} + constructor(metaplex: Metaplex) : this(metaplex.connection) override suspend fun handle(input: PublicKey): Result = withContext(dispatcher) { FindTokenMetadataAccountOperation(connection).run(MetadataAccount.pda(input).getOrThrows()) @@ -34,20 +20,4 @@ class FindFungibleTokenByMintOnChainOperationHandler(override val connection: Co it.data?.let { FungibleToken(it) } ?: throw OperationError.NilDataOnAccount } } - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - override fun handle(operation: FindFungibleTokenByMintOperation): OperationResult { - return operation.flatMap { mintKey -> - OperationResult { cb -> - CoroutineScope(dispatcher).launch { - handle(mintKey) - .onSuccess { buffer -> - cb(ResultWithCustomError.success(buffer)) - }.onFailure { - cb(ResultWithCustomError.failure(OperationError.GetMetadataAccountInfoError(RuntimeException(it)))) - } - } - } - } - } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMasterEditionAccountOperation.kt b/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMasterEditionAccountOperation.kt index 32d8ec9..32fffab 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMasterEditionAccountOperation.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMasterEditionAccountOperation.kt @@ -8,18 +8,20 @@ package com.metaplex.lib.modules.token.operations import com.metaplex.lib.drivers.solana.AccountInfo +import com.metaplex.lib.drivers.solana.AccountRequest import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.serialization.serializers.legacy.BorshCodeableSerializer +import com.metaplex.lib.drivers.solana.NullResultError import com.metaplex.lib.programs.token_metadata.MasterEditionAccount +import com.metaplex.lib.programs.token_metadata.MasterEditionAccountSerializer +import com.metaplex.lib.serialization.serializers.base64.BorshAsBase64JsonArraySerializer +import com.metaplex.lib.serialization.serializers.solana.ByteDiscriminatorSerializer +import com.metaplex.lib.serialization.serializers.solana.SolanaResponseSerializer import com.metaplex.lib.shared.SuspendOperation import com.solana.core.PublicKey class FindTokenMasterEditionAccountOperation(override val connection: Connection) : SuspendOperation> { override suspend fun run(input: PublicKey): Result> = - @Suppress("UNCHECKED_CAST") - connection.getAccountInfo(BorshCodeableSerializer(MasterEditionAccount::class.java), - MasterEditionAccount.pda(input).getOrThrows()).map { - it as AccountInfo // safe cast, we know the returned type - } + connection.getAccountInfo(MasterEditionAccountSerializer, + MasterEditionAccount.pda(input).getOrThrows()) } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMetadataAccountOperation.kt b/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMetadataAccountOperation.kt index 80fb061..24f4f9c 100644 --- a/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMetadataAccountOperation.kt +++ b/lib/src/main/java/com/metaplex/lib/modules/token/operations/FindTokenMetadataAccountOperation.kt @@ -7,9 +7,7 @@ package com.metaplex.lib.modules.token.operations -import com.metaplex.lib.drivers.solana.AccountInfo -import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.serialization.serializers.legacy.BorshCodeableSerializer +import com.metaplex.lib.drivers.solana.* import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.metaplex.lib.shared.SuspendOperation import com.solana.core.PublicKey @@ -17,8 +15,5 @@ import com.solana.core.PublicKey class FindTokenMetadataAccountOperation(override val connection: Connection) : SuspendOperation> { override suspend fun run(input: PublicKey): Result> = - @Suppress("UNCHECKED_CAST") - connection.getAccountInfo(BorshCodeableSerializer(MetadataAccount::class.java), input).map { - it as AccountInfo // safe cast, we know the returned type - } + connection.getAccountInfo(MetadataAccount.serializer(), input) } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MasterEditionAccount.kt b/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MasterEditionAccount.kt index 27574eb..4df4cd9 100644 --- a/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MasterEditionAccount.kt +++ b/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MasterEditionAccount.kt @@ -1,20 +1,22 @@ +@file:UseSerializers(PublicKeyAs32ByteSerializer::class) + package com.metaplex.lib.programs.token_metadata -import com.google.protobuf.UInt64Value import com.metaplex.lib.modules.nfts.models.MetaplexContstants -import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount -import com.metaplex.lib.programs.token_metadata.accounts.MetaplexDataRule +import com.metaplex.lib.serialization.serializers.solana.PublicKeyAs32ByteSerializer import com.metaplex.lib.shared.OperationError import com.metaplex.lib.shared.ResultWithCustomError import com.solana.core.PublicKey -import com.solana.core.PublicKeyRule -import com.solana.models.buffer.Buffer -import com.solana.networking.MoshiAdapterFactory -import com.solana.vendor.borshj.* -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.bitcoinj.core.Base58 -import java.lang.UnsupportedOperationException + +// TODO: Deprecate objects in this file. +// this will be replaced with beet kt generated code in the near future enum class MetadataKey { Uninitialized, // 0 @@ -29,51 +31,8 @@ enum class MetadataKey { CollectionAuthorityRecord, // 9 } -class MasterEditionAccountJsonAdapterFactory: MoshiAdapterFactory { - override fun create(borsh: Borsh): Object { - return MasterEditionAccountJsonAdapter(borsh) - } -} - -class MasterEditionAccountJsonAdapter(val borsh: Borsh): Object() { - @FromJson - fun fromJson(rawData: Any): Buffer { - return Buffer.create(borsh, rawData, MasterEditionAccount::class.java) - } - - @ToJson - fun toJson(masterEditionAccount: Buffer): String { - throw UnsupportedOperationException() - } -} - -class MasterEditionAccountRule( - override val clazz: Class = MasterEditionAccount::class.java -) : BorshRule { - override fun read(input: BorshInput): MasterEditionAccount { - val key: Int = input.read().toInt() - val masterEditionVersion = if (key == MetadataKey.MasterEditionV1.ordinal) { - val masterEditionV1 = MasterEditionV1AccountRule().read(input) - MasterEditionVersion.masterEditionV1(masterEditionV1) - } else { - val masterEditionV2 = MasterEditionV2AccountRule().read(input) - MasterEditionVersion.masterEditionV2(masterEditionV2) - } - return MasterEditionAccount(key, masterEditionVersion) - } - - override fun write(obj: Any, output: BorshOutput): Self { - throw UnsupportedOperationException() - } -} -sealed class MasterEditionVersion { - data class masterEditionV1(val masterEditionV1: MasterEditionV1): MasterEditionVersion() - data class masterEditionV2(val masterEditionV2: MasterEditionV2): MasterEditionVersion() -} -data class MasterEditionAccount( - val type: Int, - val masterEditionVersion: MasterEditionVersion -): BorshCodable { +@Serializable +data class MasterEditionAccount(val type: Int, val masterEditionVersion: MasterEditionVersion) { companion object { fun pda(publicKey: PublicKey): ResultWithCustomError { val pdaSeeds = listOf( @@ -92,44 +51,41 @@ data class MasterEditionAccount( } } -class MasterEditionV1AccountRule( - override val clazz: Class = MasterEditionV1::class.java -) : BorshRule { - override fun read(input: BorshInput): MasterEditionV1 { - val supply = input.readU64() - val max_supply = input.readU64() - val printing_mint = PublicKeyRule().read(input) - val one_time_printing_authorization_mint = PublicKeyRule().read(input) - return MasterEditionV1(supply, max_supply, printing_mint, one_time_printing_authorization_mint) - } +@Serializable +sealed class MasterEditionVersion { + @Serializable + data class MasterEditionV1 ( + val supply: Long?, + val max_supply: Long?, + val printing_mint: PublicKey, + val one_time_printing_authorization_mint: PublicKey + ): MasterEditionVersion() - override fun write(obj: Any, output: BorshOutput): Self { - throw UnsupportedOperationException() - } + @Serializable + data class MasterEditionV2 ( + val supply: Long?, + val max_supply: Long? + ): MasterEditionVersion() } -data class MasterEditionV1 ( - val supply: Long?, - val max_supply: Long?, - val printing_mint: PublicKey, - val one_time_printing_authorization_mint: PublicKey -): BorshCodable +// have to use this for now because the experimental beet kt does not handle sealed classes yet +// this will be replaced with beet kt generated code in the near future +object MasterEditionAccountSerializer : KSerializer { -class MasterEditionV2AccountRule( - override val clazz: Class = MasterEditionV2::class.java -) : BorshRule { - override fun read(input: BorshInput): MasterEditionV2 { - val supply = input.readU64() - val max_supply = input.readU64() - return MasterEditionV2(supply, max_supply) - } + override val descriptor: SerialDescriptor = MasterEditionAccount.serializer().descriptor - override fun write(obj: Any, output: BorshOutput): Self { - TODO("Not yet implemented") + override fun deserialize(decoder: Decoder): MasterEditionAccount { + val key = decoder.decodeByte().toInt() + val masterEditionVersion = if (key == MetadataKey.MasterEditionV1.ordinal) { + decoder.decodeSerializableValue(MasterEditionVersion.MasterEditionV1.serializer()) + } else { + decoder.decodeSerializableValue(MasterEditionVersion.MasterEditionV2.serializer()) + } + return MasterEditionAccount(key, masterEditionVersion) } -} -data class MasterEditionV2 ( - val supply: Long?, - val max_supply: Long? -): BorshCodable \ No newline at end of file + override fun serialize(encoder: Encoder, value: MasterEditionAccount) { + encoder.encodeByte(value.type.toByte()) + encoder.encodeSerializableValue(MasterEditionVersion.serializer(), value.masterEditionVersion) + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MetadataAccount.kt b/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MetadataAccount.kt index 373bab4..03869a9 100644 --- a/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MetadataAccount.kt +++ b/lib/src/main/java/com/metaplex/lib/programs/token_metadata/accounts/MetadataAccount.kt @@ -1,74 +1,52 @@ +@file:UseSerializers(PublicKeyAs32ByteSerializer::class) + package com.metaplex.lib.programs.token_metadata.accounts import com.metaplex.lib.modules.nfts.models.MetaplexContstants +import com.metaplex.lib.programs.token_metadata.MetadataKey +import com.metaplex.lib.serialization.serializers.solana.ByteDiscriminatorSerializer +import com.metaplex.lib.serialization.serializers.solana.PublicKeyAs32ByteSerializer import com.metaplex.lib.shared.OperationError import com.metaplex.lib.shared.ResultWithCustomError import com.solana.core.PublicKey -import com.solana.core.PublicKeyRule -import com.solana.models.buffer.Buffer -import com.solana.networking.MoshiAdapterFactory import com.solana.vendor.borshj.* -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import org.bitcoinj.core.Base58 -import java.lang.UnsupportedOperationException -class MetadataAccountJsonAdapterFactory : MoshiAdapterFactory { - override fun create(borsh: Borsh): Object { - return MetadataAccountJsonAdapter(borsh) - } -} - -class MetadataAccountJsonAdapter(val borsh: Borsh) : Object() { - @FromJson - fun fromJson(rawData: Any): Buffer { - return Buffer.create(borsh, rawData, MetadataAccount::class.java) - } - - @ToJson - fun toJson(metadataAccount: Buffer): String { - throw UnsupportedOperationException() - } -} +// TODO: Deprecate objects in this file. +// this will be replaced with beet kt generated code in the near future enum class MetaplexTokenStandard { NonFungible, FungibleAsset, Fungible, NonFungibleEdition +} +enum class MetaplexCollectionDetails { + V1 } +@Serializable data class MetaplexCollection( val verified: Boolean, val key: PublicKey, -) : BorshCodable - -class MetaplexCollectionRule( - override val clazz: Class = MetaplexCollection::class.java -) : BorshRule { - override fun read(input: BorshInput): MetaplexCollection { - val verified = input.readBoolean() - val key = PublicKey(input.readFixedArray(32)) - return MetaplexCollection(verified, key) - } - - override fun write(obj: Any, output: BorshOutput): Self { - TODO("Not yet implemented") - } -} - -enum class MetaplexCollectionDetails { - V1 -} +) +@Serializable data class MetadataAccount( - val key: Int, + val key: Byte, val update_authority: PublicKey, val mint: PublicKey, val data: MetaplexData, val primarySaleHappened: Boolean, val isMutable: Boolean, - val editionNonce: Int?, + val editionNonce: UByte?, val tokenStandard: MetaplexTokenStandard?, val collection: MetaplexCollection?, + val uses: Uses? = null, val collectionDetails: MetaplexCollectionDetails? = null ) : BorshCodable { companion object { @@ -88,104 +66,15 @@ data class MetadataAccount( } } -class MetadataAccountRule( - override val clazz: Class = MetadataAccount::class.java -) : BorshRule { - override fun read(input: BorshInput): MetadataAccount { - val key: Int = input.read().toInt() - val updateAuthority: PublicKey = PublicKeyRule().read(input) - val mint: PublicKey = PublicKeyRule().read(input) - val data = MetaplexDataRule().read(input) - val primarySaleHappened = input.readBoolean() - val isMutable: Boolean = input.readBoolean() - val hasEditionNonce: Boolean = input.readBoolean() - var editionNonce: Int? = null - if (hasEditionNonce) { - editionNonce = 256 + input.read().toInt() // The byte is inverted we fix the inversion on the byte to int. - } - - val tokenStandard = - if (input.readBoolean()) - MetaplexTokenStandard.values().getOrNull(input.read().toInt()) - else MetaplexTokenStandard.NonFungible - - val hasCollection: Boolean = input.readBoolean() - var collection: MetaplexCollection? = null - if (hasCollection) { - collection = MetaplexCollectionRule().read(input) - } - -// print("++++++++ remaining bytes = [") -// while (true) { -// -// try { -// val byte = input.readU8() -// print("$byte,") -// } catch (error: BufferUnderflowException) { -// -//// println("+++++ remaining bytes underflow") -// break -// } -// } -// println("]") - - // read Uses object (not using this currently so just read and throw away) - val hasUses: Boolean = input.readBoolean() - if (hasUses) { - input.read() // UseMethod - input.readU64() // remaining - input.readU64() // total - } -// else { -// input.read() -// } - - val collectionDetails: MetaplexCollectionDetails? = - if (input.readBoolean()) - MetaplexCollectionDetails.values().getOrNull(input.readU8().toInt()) - else null - - return MetadataAccount( - key = key, - update_authority = updateAuthority, - mint = mint, - data = data, - primarySaleHappened = primarySaleHappened, - isMutable = isMutable, - editionNonce = editionNonce, - tokenStandard = tokenStandard, - collection = collection, - collectionDetails = collectionDetails - ) - } - - override fun write(obj: Any, output: BorshOutput): Self { - TODO("Not yet implemented") - } -} - -class MetaplexDataAdapterJsonAdapterFactory : MoshiAdapterFactory { - override fun create(borsh: Borsh): Object { - return MetaplexDataAdapter(borsh) - } -} - -class MetaplexDataAdapter(val borsh: Borsh) : Object() { - @FromJson - fun fromJson(rawData: Any): Buffer { - return Buffer.create(borsh, rawData, MetaplexData::class.java) - } -} - +@Serializable data class MetaplexData( val name: String, val symbol: String, val uri: String, - val sellerFeeBasisPoints: Int, - val hasCreators: Boolean, - val addressCount: Int, - val creators: Array -) : BorshCodable { + val sellerFeeBasisPoints: Short, + val creators: Array? +){ + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -194,8 +83,6 @@ data class MetaplexData( if (symbol != other.symbol) return false if (uri != other.uri) return false if (sellerFeeBasisPoints != other.sellerFeeBasisPoints) return false - if (hasCreators != other.hasCreators) return false - if (addressCount != other.addressCount) return false if (!creators.contentEquals(other.creators)) return false return true } @@ -205,63 +92,31 @@ data class MetaplexData( result = 31 * result + symbol.hashCode() result = 31 * result + uri.hashCode() result = 31 * result + sellerFeeBasisPoints - result = 31 * result + hasCreators.hashCode() - result = 31 * result + addressCount result = 31 * result + creators.contentHashCode() return result } } -class MetaplexDataRule( - override val clazz: Class = MetaplexData::class.java -) : BorshRule { - override fun read(input: BorshInput): MetaplexData { - val name = input.readString().replace("\u0000", "") - val symbol = input.readString().replace("\u0000", "") - val uri = input.readString().replace("\u0000", "") - val sellerFeeBasisPoints = input.readU16().toInt() - val hasCreators = input.readBoolean() - val creatorsArray = arrayListOf() - if (hasCreators) { - val addressCount = input.readU32() - for (i in 0 until addressCount) { - val creator = MetaplexCreatorRule().read(input) - creatorsArray.add(creator) - } - } - return MetaplexData( - name = name, - symbol = symbol, - uri = uri, - sellerFeeBasisPoints = sellerFeeBasisPoints, - hasCreators = hasCreators, - addressCount = creatorsArray.count(), - creators = creatorsArray.toTypedArray() - ) - } - - override fun write(obj: Any, output: BorshOutput): Self { - TODO("Not yet implemented") - } -} - +@Serializable data class MetaplexCreator( val address: PublicKey, - val verified: Int, - val share: Int, -) : BorshCodable + val verified: Byte, + val share: Byte, +) + +@Serializable +data class Uses(val method: Byte = 0, val remaining: Long = 0, val total: Long = 0) -class MetaplexCreatorRule( - override val clazz: Class = MetaplexCreator::class.java -) : BorshRule { - override fun read(input: BorshInput): MetaplexCreator { - val address = PublicKeyRule().read(input) - val verified = input.readU8().toInt() - val share = input.readU8().toInt() - return MetaplexCreator(address, verified, share) +object CollectionDetailsSerializer : KSerializer { + override val descriptor = Byte.serializer().descriptor + override fun serialize(encoder: Encoder, value: MetaplexCollectionDetails) { + encoder.encodeByte(value.ordinal.toByte()) + encoder.encodeLong(0) } - override fun write(obj: Any, output: BorshOutput): Self { - TODO("Not yet implemented") + override fun deserialize(decoder: Decoder): MetaplexCollectionDetails { + return MetaplexCollectionDetails.values().get(decoder.decodeByte().toInt()).also { + decoder.decodeLong() + } } } \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/serialization/serializers/legacy/BorshCodeableaSerializer.kt b/lib/src/main/java/com/metaplex/lib/serialization/serializers/legacy/BorshCodeableaSerializer.kt deleted file mode 100644 index 30ec2a9..0000000 --- a/lib/src/main/java/com/metaplex/lib/serialization/serializers/legacy/BorshCodeableaSerializer.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * BorshCodeableaSerializer - * metaplex-android - * - * Created by Funkatronics on 8/1/2022 - */ - -package com.metaplex.lib.serialization.serializers.legacy - -import com.metaplex.lib.serialization.format.BorshDecoder -import com.metaplex.lib.serialization.format.BorshEncoder -import com.metaplex.lib.programs.token_metadata.MasterEditionAccountRule -import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccountRule -import com.metaplex.lib.programs.token_metadata.accounts.MetaplexCollectionRule -import com.metaplex.lib.programs.token_metadata.accounts.MetaplexCreatorRule -import com.metaplex.lib.programs.token_metadata.accounts.MetaplexDataRule -import com.metaplex.lib.shared.AccountPublicKeyRule -import com.solana.vendor.borshj.BorshBuffer -import com.solana.vendor.borshj.BorshCodable -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder - -@Serializer(forClass = BorshCodable::class) -internal class BorshCodeableSerializer(val clazz: Class) : KSerializer { - override val descriptor: SerialDescriptor = buildClassSerialDescriptor(clazz.simpleName) {} - - val rule = listOf( - MetadataAccountRule(), - MetaplexDataRule(), - MetaplexCollectionRule(), - AccountPublicKeyRule(), - MasterEditionAccountRule(), - MetaplexCreatorRule() - ).find { it.clazz == clazz } - - override fun deserialize(decoder: Decoder): BorshCodable? = - rule?.read(BorshBuffer.wrap((decoder as? BorshDecoder)?.bytes)) - - override fun serialize(encoder: Encoder, value: BorshCodable?) { - value?.let { - rule?.write(value, BorshBuffer.wrap((encoder as? BorshEncoder)?.borshEncodedBytes)) - } - } -} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/shared/GmaBuilder.kt b/lib/src/main/java/com/metaplex/lib/shared/GmaBuilder.kt deleted file mode 100644 index aa126d6..0000000 --- a/lib/src/main/java/com/metaplex/lib/shared/GmaBuilder.kt +++ /dev/null @@ -1,97 +0,0 @@ -package com.metaplex.lib.shared - -import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount -import com.metaplex.lib.drivers.solana.Connection -import com.solana.core.PublicKey -import com.solana.models.buffer.BufferInfo -import java.lang.RuntimeException -import java.util.concurrent.CountDownLatch - -data class GmaBuilderOptions( - val chunkSize: Int?, -) - -data class MaybeAccountInfoWithPublicKey( - val pubkey: PublicKey, - val exists: Boolean, - val metadata: MetadataAccount?, -) - - -class GmaBuilder( - private val connection: Connection, - private var publicKeys: List, - options: GmaBuilderOptions? -) { - private val chunkSize: Int = options?.chunkSize ?: 100 - - fun setPublicKeys(publicKeys: List): GmaBuilder { - val newPublicKeys = this.publicKeys.toMutableList() - newPublicKeys.addAll(publicKeys) - this.publicKeys = newPublicKeys - return this - } - - fun get(): OperationResult, Exception> { - return this.getChunks(publicKeys) - } - - private fun getChunks(publicKeys: List): OperationResult, Exception> { - return OperationResult { cb -> - val chunks = publicKeys.chunked(this.chunkSize) - val chunkOperations = chunks.map { this.getChunk(it) } - processChuncks(chunkOperations) { result -> - result.onSuccess { - cb(ResultWithCustomError.success(it)) - }.onFailure { - cb(ResultWithCustomError.failure(RuntimeException(it))) - } - } - } - } - - private fun processChuncks( - chunkOperations: List, Exception>>, - cb: (Result>) -> Unit - ) { - val results = mutableListOf() - for (chunks in chunkOperations) { - val lock = CountDownLatch(1) - chunks.run { result -> - result.onSuccess { - results.addAll(it) - }.onFailure { - cb(Result.failure(RuntimeException(it))) - }.also { - lock.countDown() - } - } - lock.await() - } - cb(Result.success(results)) - } - - private fun getChunk(publicKeys: List): OperationResult, Exception> { - return OperationResult?>, ResultError> { cb -> - this.connection.getMultipleAccountsInfo(publicKeys, MetadataAccount::class.java) { - it.onSuccess { list -> - cb(ResultWithCustomError.success(list)) - }.onFailure { error -> - cb(ResultWithCustomError.failure(ResultError(error))) - } - } - }.flatMap { accounts -> - val maybeAccounts = mutableListOf() - publicKeys.zip(accounts).forEach { pair -> - val publicKey = pair.first - val account = pair.second - account?.data?.value?.let { - maybeAccounts.add(MaybeAccountInfoWithPublicKey(publicKey, true, it)) - } ?: run { - maybeAccounts.add(MaybeAccountInfoWithPublicKey(publicKey, false, null)) - } - } - OperationResult.success(maybeAccounts) - } - } -} \ No newline at end of file diff --git a/lib/src/main/java/com/metaplex/lib/shared/GpaBuilder.kt b/lib/src/main/java/com/metaplex/lib/shared/GpaBuilder.kt index 082ae1c..4735bce 100644 --- a/lib/src/main/java/com/metaplex/lib/shared/GpaBuilder.kt +++ b/lib/src/main/java/com/metaplex/lib/shared/GpaBuilder.kt @@ -1,20 +1,11 @@ package com.metaplex.lib.shared import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.drivers.solana.ProgramAccountRequest import com.solana.core.PublicKey -import com.solana.core.PublicKeyRule import com.solana.models.DataSlice -import com.solana.models.ProgramAccount import com.solana.models.ProgramAccountConfig import com.solana.models.RpcSendTransactionConfig -import com.solana.models.buffer.Buffer -import com.solana.models.buffer.BufferInfo -import com.solana.networking.MoshiAdapterFactory -import com.solana.vendor.borshj.* -import com.squareup.moshi.FromJson import org.bitcoinj.core.Base58 -import java.lang.RuntimeException import java.math.BigInteger class GetProgramAccountsConfig(val encoding: RpcSendTransactionConfig.Encoding = RpcSendTransactionConfig.Encoding.base64, @@ -22,41 +13,6 @@ class GetProgramAccountsConfig(val encoding: RpcSendTransactionConfig.Encoding = val dataSlice: DataSlice? = null, val filters: List>? = null) -class AccountPublicKeyJsonAdapterFactory: MoshiAdapterFactory { - override fun create(borsh: Borsh): Object { - return AccountPublicKeyJsonAdapter(borsh) - } -} - -class AccountPublicKeyJsonAdapter(val borsh: Borsh): Object() { - @FromJson - fun fromJson(rawData: Any): Buffer { - return Buffer.create(borsh, rawData, AccountPublicKey::class.java) - } -} - -class AccountPublicKeyRule( - override val clazz: Class = AccountPublicKey::class.java -) : BorshRule { - override fun read(input: BorshInput): AccountPublicKey { - val publicKey = PublicKeyRule().read(input) - return AccountPublicKey(publicKey) - } - - override fun write(obj: Any, output: BorshOutput): Self { - TODO("Not yet implemented") - } -} - -data class AccountPublicKey ( - val publicKey: PublicKey -): BorshCodable - -data class AccountInfoWithPublicKey ( - val pubkey: PublicKey, - val account: BufferInfo -) - fun GetProgramAccountsConfig.merge( other: GetProgramAccountsConfig ) = GetProgramAccountsConfig( @@ -90,8 +46,6 @@ fun GetProgramAccountsConfig.copyAndReplace( filters ?: this.filters ) - - class GpaBuilderFactory { companion object { fun from(instance: Class, builder: GpaBuilder): T { @@ -172,50 +126,15 @@ abstract class GpaBuilder(open val connection: Connection, open val programId: P return this.addFilter(mapOf("dataSize" to requestDataSize)) } - inline fun get(): OperationResult>, Exception> { - return OperationResult>, ResultError> { cb -> - this.connection.getProgramAccounts( - this.programId, - ProgramAccountConfig(this.config.encoding, this.config.filters, this.config.dataSlice, this.config.commitment), - B::class.java - ){ result -> - result.onSuccess { - cb(ResultWithCustomError.success(it)) - }.onFailure { - cb(ResultWithCustomError.failure(RuntimeException(it))) - } - } - }.map { programAccounts -> - val infoAccounts = mutableListOf>() - for (programAccount in programAccounts){ - val infoAccount = AccountInfoWithPublicKey(PublicKey(programAccount.pubkey), programAccount.account) - infoAccounts.add(infoAccount) - } - infoAccounts - } - } + suspend fun get() = connection.getProgramAccounts( + com.metaplex.lib.drivers.solana.AccountPublicKey.serializer(), + programId, + ProgramAccountConfig(config.encoding, config.filters, config.dataSlice, config.commitment) + ) - inline fun getAndMap(noinline callback: (account: List>) -> T): OperationResult { - return this.get().map(callback) - } + suspend fun getPublicKeys(): Result> = + this.get().map { account -> account.map { PublicKey(it.publicKey) } } - fun getPublicKeys(): OperationResult, Exception> { - return this.getAndMap { account: List> -> - account.map { it.pubkey } - } - } - - fun getDataAsPublicKeys(): OperationResult, Exception> { - return this.getAndMap { account: List> -> - account.map { - it.account.data!!.value!!.publicKey - } - } - } + suspend fun getDataAsPublicKeys(): Result> = + this.get().map { account -> account.map { it.account.data!!.publicKey } } } - -suspend fun GpaBuilder.getSuspend() = connection.getProgramAccounts( - com.metaplex.lib.drivers.solana.AccountPublicKey.serializer(), - programId, - ProgramAccountConfig(config.encoding, config.filters, config.dataSlice, config.commitment) -) diff --git a/lib/src/main/java/com/metaplex/lib/shared/OperationHandler.kt b/lib/src/main/java/com/metaplex/lib/shared/OperationHandler.kt index 9f1ab6c..eeaa27d 100644 --- a/lib/src/main/java/com/metaplex/lib/shared/OperationHandler.kt +++ b/lib/src/main/java/com/metaplex/lib/shared/OperationHandler.kt @@ -20,12 +20,4 @@ interface OperationHandler { val connection: Connection val dispatcher: CoroutineDispatcher suspend fun handle(input: I): Result - - @Deprecated("Dependency forwarding is obsolete and has been replaced with direct dependency injection.", - ReplaceWith("connection") - ) - var metaplex: Metaplex - - @Deprecated(ASYNC_CALLBACK_DEPRECATION_MESSAGE, ReplaceWith("handle(input)")) - fun handle(operation: OperationResult): OperationResult } diff --git a/lib/src/test/java/com/metaplex/lib/MetaplexTestUtils.kt b/lib/src/test/java/com/metaplex/lib/MetaplexTestUtils.kt index 007da9b..b190e1b 100644 --- a/lib/src/test/java/com/metaplex/lib/MetaplexTestUtils.kt +++ b/lib/src/test/java/com/metaplex/lib/MetaplexTestUtils.kt @@ -10,6 +10,7 @@ import com.metaplex.lib.drivers.solana.TransactionOptions import com.metaplex.lib.drivers.storage.MemoryStorageDriver import com.metaplex.lib.drivers.storage.StorageDriver import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import com.solana.networking.RPCEndpoint import java.net.URL @@ -27,7 +28,7 @@ fun MetaplexTestUtils.generateConnectionDriver( ) = SolanaConnectionDriver(JdkRpcDriver(rpcURL), txOptions) fun MetaplexTestUtils.generateMetaplexInstance( - account: Account = Account(), connectionDriver: Connection = generateConnectionDriver(), + account: Account = HotAccount(), connectionDriver: Connection = generateConnectionDriver(), storageDriver: StorageDriver = MemoryStorageDriver() ): Metaplex { val identityDriver = KeypairIdentityDriver(account, connectionDriver) diff --git a/lib/src/test/java/com/metaplex/lib/MetaplexTests.kt b/lib/src/test/java/com/metaplex/lib/MetaplexTests.kt index aaec337..45a90d5 100644 --- a/lib/src/test/java/com/metaplex/lib/MetaplexTests.kt +++ b/lib/src/test/java/com/metaplex/lib/MetaplexTests.kt @@ -1,55 +1,24 @@ package com.metaplex.lib -import com.metaplex.lib.MetaplexTestUtils.TEST_ACCOUNT_PUBLICKEY -import com.solana.models.buffer.AccountInfo -import com.solana.models.buffer.BufferInfo -import com.solana.networking.RPCEndpoint +import com.metaplex.lib.drivers.indenty.ReadOnlyIdentityDriver +import com.metaplex.lib.drivers.storage.MemoryStorageDriver +import com.solana.core.HotAccount import org.junit.Assert import org.junit.Test -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit class MetaplexTests { - val metaplex: Metaplex get() = MetaplexTestUtils.readOnlyMainnetMetaplexInstance @Test fun testMetaplexSetUpReturnsValidInstance() { - Assert.assertNotNull(metaplex) - } - - @Test - fun testGetAccountInfoReturnsNonNullBufferForValidAccount() { - // given - val accountKey = TEST_ACCOUNT_PUBLICKEY - var bufferInfo: BufferInfo? = null - - // when - val lock = CountDownLatch(1) - metaplex.getAccountInfo(accountKey, AccountInfo::class.java) { - bufferInfo = it.getOrNull() - lock.countDown() - } - lock.await(2000, TimeUnit.MILLISECONDS) - - // then - Assert.assertNotNull(bufferInfo) - } - - @Test - fun testGetMultipleAccountsReturnsNonNullBuffer() { - // given - val accountKeys = listOf(TEST_ACCOUNT_PUBLICKEY) - var bufferInfo: List?>? = null + // gven + val connection = MetaplexTestUtils.generateConnectionDriver() + val identityDriver = ReadOnlyIdentityDriver(HotAccount().publicKey, connection) + val storageDriver = MemoryStorageDriver() // when - val lock = CountDownLatch(1) - metaplex.getMultipleAccountsInfo(accountKeys, AccountInfo::class.java) { - bufferInfo = it.getOrNull() - lock.countDown() - } - lock.await(2000, TimeUnit.MILLISECONDS) + val metaplex = Metaplex(connection, identityDriver, storageDriver) // then - Assert.assertNotNull(bufferInfo) + Assert.assertNotNull(metaplex) } } \ No newline at end of file diff --git a/lib/src/test/java/com/metaplex/lib/drivers/identity/KeypairIdentityDriverTests.kt b/lib/src/test/java/com/metaplex/lib/drivers/identity/KeypairIdentityDriverTests.kt index 06ad5a7..98fe583 100644 --- a/lib/src/test/java/com/metaplex/lib/drivers/identity/KeypairIdentityDriverTests.kt +++ b/lib/src/test/java/com/metaplex/lib/drivers/identity/KeypairIdentityDriverTests.kt @@ -2,11 +2,9 @@ package com.metaplex.lib.drivers.identity import com.metaplex.lib.* import com.metaplex.lib.drivers.indenty.KeypairIdentityDriver -import com.metaplex.lib.drivers.solana.SolanaConnectionDriver -import com.solana.core.Account import com.solana.core.DerivationPath +import com.solana.core.HotAccount import com.solana.core.Transaction -import com.solana.networking.RPCEndpoint import com.solana.programs.SystemProgram import org.junit.Assert import org.junit.Test @@ -20,7 +18,7 @@ class KeypairIdentityDriverTests { // given val expectedPublicKey = SolanaTestData.TEST_ACCOUNT_MNEMONIC_PAIR.publicKey val solanaConnection = MetaplexTestUtils.generateConnectionDriver() - val account = Account.fromMnemonic(SolanaTestData.TEST_ACCOUNT_MNEMONIC_PAIR.mnemonic, + val account = HotAccount.fromMnemonic(SolanaTestData.TEST_ACCOUNT_MNEMONIC_PAIR.mnemonic, "", DerivationPath.BIP44_M_44H_501H_0H_OH) // when @@ -34,7 +32,7 @@ class KeypairIdentityDriverTests { fun testSignTransactionReturnsTrxHash() { // given val expectedSignedTransaction = "AaHQ/obYLnD6GUFqxDKiiNkw2NYsLt+NZHa8ALB64uM0wpADNVQ5eWhzW38FcxfthDz6zXsJao58y5/fFovSoAABAAEC1J5StK6hI4+ERBMKkBUsHeIzegza3Eb/t7dwtSG4Q9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJrdSfHQAfBFYiaNQeEg3d9YB3F537Ex4K5dG79qBe0rAQECAAAMAgAAAOgDAAAAAAAA" - val account = Account.fromMnemonic(SolanaTestData.TEST_ACCOUNT_MNEMONIC_PAIR.mnemonic, + val account = HotAccount.fromMnemonic(SolanaTestData.TEST_ACCOUNT_MNEMONIC_PAIR.mnemonic, "", DerivationPath.BIP44_M_44H_501H_0H_OH) val connectionDriver = MetaplexTestUtils.generateConnectionDriver() diff --git a/lib/src/test/java/com/metaplex/lib/drivers/solana/AccountInfoTests.kt b/lib/src/test/java/com/metaplex/lib/drivers/solana/AccountInfoTests.kt index 219fc1b..be91dd8 100644 --- a/lib/src/test/java/com/metaplex/lib/drivers/solana/AccountInfoTests.kt +++ b/lib/src/test/java/com/metaplex/lib/drivers/solana/AccountInfoTests.kt @@ -13,6 +13,7 @@ import com.metaplex.data.model.responseJson import com.metaplex.lib.serialization.serializers.base64.BorshAsBase64JsonArraySerializer import com.metaplex.lib.serialization.serializers.solana.AnchorAccountSerializer import com.metaplex.lib.modules.auctions.models.AuctionHouse +import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.serializer @@ -37,13 +38,14 @@ class AccountInfoTests { @Test fun testSolanaAccountSerializer() { // given - val serializer = SolanaAccountSerializer() - val expectedResponse = AccountInfo(TestDataProvider.auctionHouse, + @Serializable data class TestAccount(val data: String) + val serializer = SolanaAccountSerializer(TestAccount.serializer()) + val expectedResponse = AccountInfo(TestAccount("test data"), false, 123456, "the owner", 123) // when val actualResponse = Json.decodeFromString(serializer, expectedResponse.responseJson( - BorshAsBase64JsonArraySerializer(AnchorAccountSerializer()) + BorshAsBase64JsonArraySerializer(TestAccount.serializer()) )) // then diff --git a/lib/src/test/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriverTests.kt b/lib/src/test/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriverTests.kt index 114093a..bed3cc7 100644 --- a/lib/src/test/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriverTests.kt +++ b/lib/src/test/java/com/metaplex/lib/drivers/solana/SolanaConnectionDriverTests.kt @@ -10,14 +10,17 @@ package com.metaplex.lib.drivers.solana import com.metaplex.data.TestDataProvider +import com.metaplex.lib.MetaplexTestUtils import com.metaplex.data.model.publicKey import com.metaplex.lib.drivers.rpc.RpcError +import com.metaplex.lib.generateConnectionDriver import com.metaplex.mock.driver.rpc.MockRpcDriver -import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import com.solana.models.ProgramAccountConfig -import com.solana.models.SignatureStatus import com.solana.models.SignatureStatusRequestConfiguration +import com.solana.programs.SystemProgram +import com.util.airdrop import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.builtins.serializer @@ -27,6 +30,7 @@ import java.lang.Error class SolanaConnectionDriverTests { + //region UNIT //region getAccountInfo @Test fun testGetAccountInfoReturnsValidAccountInfo() = runTest { @@ -84,7 +88,7 @@ class SolanaConnectionDriverTests { @Test fun testGetMultipleAccountsInfoReturnsValidAccountInfo() = runTest { // given - val accounts = listOf(Account().publicKey) + val accounts = listOf(HotAccount().publicKey) val accountsRequest = MultipleAccountsRequest(accounts.map { it.toBase58() }) val expectedAccountInfo = listOf(AccountInfo("testAccount", false, 0, "", 0)) val solanaDriver = SolanaConnectionDriver(MockRpcDriver().apply { @@ -101,7 +105,7 @@ class SolanaConnectionDriverTests { @Test fun testGetMultipleAccountsInfoReturnsEmptyListForNullAccount() = runTest { // given - val accounts = listOf(Account().publicKey) + val accounts = listOf(HotAccount().publicKey) val expectedAccountInfo = listOf() val solanaDriver = SolanaConnectionDriver(MockRpcDriver()) @@ -245,4 +249,38 @@ class SolanaConnectionDriverTests { Assert.assertEquals(expectedErrorMessage, actualResult.exceptionOrNull()?.message) } //endregion + //endregion + + //region INTEGRATION + @Test + fun testGetAccountInfoReturnsNonNullBufferForValidAccount() = runTest { + // given + val account = HotAccount() + val connection = MetaplexTestUtils.generateConnectionDriver() + + // when + connection.airdrop(account.publicKey, 0.1f) + val accountInfo = connection.getAccountInfo(account.publicKey).getOrThrow() + + // then + Assert.assertNotNull(accountInfo) + Assert.assertEquals(SystemProgram.PROGRAM_ID.toString(), accountInfo.owner) + } + + @Test + fun testGetMultipleAccountsReturnsNonNullBuffer() = runTest { + // given + val account = HotAccount() + val accountKeys = listOf(account.publicKey) + val connection = MetaplexTestUtils.generateConnectionDriver() + + // when + connection.airdrop(account.publicKey, 0.1f) + val accountInfoList = connection.getMultipleAccountsInfo(accountKeys).getOrThrow() + + // then + Assert.assertNotNull(accountInfoList) + Assert.assertTrue(accountInfoList.isNotEmpty()) + } + //endregion } diff --git a/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionHouseClientTests.kt b/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionHouseClientTests.kt index 92cf035..5cb30f0 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionHouseClientTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionHouseClientTests.kt @@ -13,8 +13,10 @@ import com.metaplex.data.TestDataProvider import com.metaplex.lib.MetaplexTestUtils import com.metaplex.lib.drivers.indenty.IdentityDriver import com.metaplex.lib.drivers.indenty.KeypairIdentityDriver -import com.metaplex.lib.drivers.solana.* -import com.metaplex.lib.experimental.jen.jenerateAuctionHouse +import com.metaplex.lib.drivers.solana.AccountInfo +import com.metaplex.lib.drivers.solana.AccountRequest +import com.metaplex.lib.drivers.solana.Connection +import com.metaplex.lib.drivers.solana.SolanaConnectionDriver import com.metaplex.lib.generateConnectionDriver import com.metaplex.lib.modules.auctions.models.* import com.metaplex.lib.modules.nfts.NftClient @@ -23,14 +25,12 @@ import com.metaplex.lib.programs.token_metadata.MetadataKey import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount import com.metaplex.lib.programs.token_metadata.accounts.MetaplexData import com.metaplex.mock.driver.rpc.MockRpcDriver -import com.solana.core.Account +import com.solana.core.HotAccount import com.util.airdrop -import kotlinx.coroutines.* +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Assert -import org.junit.Before import org.junit.Test -import java.net.URL class AuctionHouseClientTests { @@ -43,7 +43,7 @@ class AuctionHouseClientTests { @Test fun testListingReturnsExpectedListing() = runTest { // given - val seller = Account() + val seller = HotAccount() val auctionHouse = TestDataProvider.auctionHouse val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -67,7 +67,7 @@ class AuctionHouseClientTests { @Test fun testBidReturnsExpectedBid() = runTest { // given - val buyer = Account() + val buyer = HotAccount() val auctionHouse = TestDataProvider.auctionHouse val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -91,16 +91,16 @@ class AuctionHouseClientTests { @Test fun testPurchaseReturnsExpectedPurchase() = runTest { // given - val buyer = Account() - val seller = Account() - val asset = Account() + val buyer = HotAccount() + val seller = HotAccount() + val asset = HotAccount() val auctionHouse = TestDataProvider.auctionHouse val rpcDriver = MockRpcDriver(autoConfirmTransactions = true).apply { // this mocking is annoying, need to find a cleaner way to set this up willReturn(AccountRequest(MetadataAccount.pda(asset.publicKey).getOrThrows().toBase58()), - AccountInfo(MetadataAccount(MetadataKey.MetadataV1.ordinal, + AccountInfo(MetadataAccount(MetadataKey.MetadataV1.ordinal.toByte(), seller.publicKey, asset.publicKey, - MetaplexData("", "", "", 250, false, 0, arrayOf()), + MetaplexData("", "", "", 250, arrayOf()), true, false, null, null, null) , false, 0, null, 0)) } @@ -137,7 +137,7 @@ class AuctionHouseClientTests { @Test fun testAuctioneerListingWithoutAuctioneerReturnsError() = runTest { // given - val seller = Account() + val seller = HotAccount() val auctionHouse = TestDataProvider.auctionHouseWithAuctioneer val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -156,7 +156,7 @@ class AuctionHouseClientTests { @Test fun testAuctioneerBidWithoutAuctioneerReturnsError() = runTest { // given - val buyer = Account() + val buyer = HotAccount() val auctionHouse = TestDataProvider.auctionHouseWithAuctioneer val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -175,8 +175,8 @@ class AuctionHouseClientTests { @Test fun testExecuteSaleWithoutAuctioneerReturnsError() = runTest { // given - val buyer = Account() - val seller = Account() + val buyer = HotAccount() + val seller = HotAccount() val auctionHouse = TestDataProvider.auctionHouseWithAuctioneer val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -209,8 +209,8 @@ class AuctionHouseClientTests { @Test fun testExecuteSaleWithDifferentMintsReturnsError() = runTest { // given - val buyer = Account() - val seller = Account() + val buyer = HotAccount() + val seller = HotAccount() val auctionHouse = TestDataProvider.auctionHouse val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -243,8 +243,8 @@ class AuctionHouseClientTests { @Test fun testExecuteSaleWithDifferentAuctionHousesReturnsError() = runTest { // given - val buyer = Account() - val seller = Account() + val buyer = HotAccount() + val seller = HotAccount() val auctionHouse1 = TestDataProvider.auctionHouse val rpcDriver = MockRpcDriver(autoConfirmTransactions = true) val connection = SolanaConnectionDriver(rpcDriver) @@ -252,22 +252,11 @@ class AuctionHouseClientTests { AuctionHouseClient(auctionHouse1, connection, KeypairIdentityDriver(seller, connection)) val auctionHouse2 = AuctionHouse( -// auctionHouseFeeAccount = Account().publicKey, -// auctionHouseTreasury = Account().publicKey, treasuryWithdrawalDestinationOwner = seller.publicKey, feeWithdrawalDestination = seller.publicKey, -// treasuryMint = PublicKey(WRAPPED_SOL_MINT_ADDRESS), authority = seller.publicKey, creator = seller.publicKey, -// bump = 253u, -// treasuryBump = 254u, -// feePayerBump = 252u, sellerFeeBasisPoints = 200u, -// requiresSignOff = false, -// canChangeSalePrice = false, -// escrowPaymentBump = 0u, -// hasAuctioneer = false, -// auctioneerPdaBump = 0u ) val listing = Listing(auctionHouse1, @@ -298,7 +287,7 @@ class AuctionHouseClientTests { @Test fun testCreatePublicListingOnAuctionHouse() = runTest { // given - val seller = Account() + val seller = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(seller, connection) val auctionsClient = AuctionsClient(connection, identityDriver) @@ -328,7 +317,7 @@ class AuctionHouseClientTests { @Test fun testCancelPublicListingOnAuctionHouse() = runTest { // given - val seller = Account() + val seller = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(seller, connection) val auctionsClient = AuctionsClient(connection, identityDriver) @@ -366,10 +355,9 @@ class AuctionHouseClientTests { @Test fun testCreatePublicBidOnAuctionHouse() = runTest { // given - val buyer = Account() - val seller = Account() -// val connection = MetaplexTestUtils.generateConnectionDriver() - val connection = MetaplexTestUtils.generateConnectionDriver(URL("http://127.0.0.1:8899")) + val buyer = HotAccount() + val seller = HotAccount() + val connection = MetaplexTestUtils.generateConnectionDriver() val buyerIdentityDriver = KeypairIdentityDriver(buyer, connection) val sellerIdentityDriver = KeypairIdentityDriver(seller, connection) val auctionsClient = AuctionsClient(connection, buyerIdentityDriver) @@ -401,10 +389,9 @@ class AuctionHouseClientTests { @Test fun testCancelPublicBidOnAuctionHouse() = runTest { // given - val buyer = Account() - val seller = Account() -// val connection = MetaplexTestUtils.generateConnectionDriver() - val connection = MetaplexTestUtils.generateConnectionDriver(URL("http://127.0.0.1:8899")) + val buyer = HotAccount() + val seller = HotAccount() + val connection = MetaplexTestUtils.generateConnectionDriver() val buyerIdentityDriver = KeypairIdentityDriver(buyer, connection) val sellerIdentityDriver = KeypairIdentityDriver(seller, connection) val auctionsClient = AuctionsClient(connection, buyerIdentityDriver) @@ -443,9 +430,9 @@ class AuctionHouseClientTests { @Test fun testExecuteSaleOnPublicListingBidPair() = runTest { // given - val buyer = Account() - val seller = Account() - val connection = MetaplexTestUtils.generateConnectionDriver(URL("http://127.0.0.1:8899")) + val buyer = HotAccount() + val seller = HotAccount() + val connection = MetaplexTestUtils.generateConnectionDriver() val buyerIdentityDriver = KeypairIdentityDriver(buyer, connection) val sellerIdentityDriver = KeypairIdentityDriver(seller, connection) val auctionsClient = AuctionsClient(connection, buyerIdentityDriver) diff --git a/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionsTests.kt b/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionsTests.kt index e5fdab4..d42dd1b 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionsTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/auctions/AuctionsTests.kt @@ -22,6 +22,7 @@ import com.metaplex.lib.modules.auctions.models.AuctionHouse import com.metaplex.lib.modules.auctions.models.address import com.metaplex.mock.driver.rpc.MockRpcDriver import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import com.util.airdrop import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -43,7 +44,7 @@ class AuctionsTests { }) val client = AuctionsClient(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection)) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection)) // when var result = client.findAuctionHouseByAddress(PublicKey(address)).getOrNull() @@ -58,7 +59,7 @@ class AuctionsTests { val address = TestDataProvider.badAddress val connection = SolanaConnectionDriver(MockRpcDriver()) val client = AuctionsClient(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection)) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection)) // when var result = client.findAuctionHouseByAddress(PublicKey(address)).getOrNull() @@ -80,7 +81,7 @@ class AuctionsTests { }) val client = AuctionsClient(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection)) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection)) // when var result = client.findAuctionHouseByCreatorAndMint(creator, treasuryMint).getOrNull() @@ -96,7 +97,7 @@ class AuctionsTests { val treasuryMint = PublicKey(TestDataProvider.badAddress) val connection = SolanaConnectionDriver(MockRpcDriver()) val client = AuctionsClient(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection)) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection)) // when var result = client.findAuctionHouseByCreatorAndMint(creator, treasuryMint).getOrNull() @@ -110,7 +111,7 @@ class AuctionsTests { @Test fun testCreateNewAuctionHouseMinimumConfiguration() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = AuctionsClient(connection, identityDriver) diff --git a/lib/src/test/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationTests.kt b/lib/src/test/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationTests.kt index 8d2b1ea..9ed9bd2 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/auctions/operations/FindAuctionHouseByAddressOperationTests.kt @@ -16,6 +16,7 @@ import com.metaplex.lib.drivers.solana.SolanaConnectionDriver import com.metaplex.mock.driver.rpc.MockErrorRpcDriver import com.metaplex.mock.driver.rpc.MockRpcDriver import com.solana.core.Account +import com.solana.core.HotAccount import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Assert @@ -27,7 +28,7 @@ class FindAuctionHouseByAddressOperationTests { fun testFindAuctionHouseByAddressOperationReturnsAuctionHouseModel() = runTest { // given val expectedAH = TestDataProvider.auctionHouse - val auctionHouseAddress = Account().publicKey + val auctionHouseAddress = HotAccount().publicKey val rpcDriver = MockRpcDriver().apply { willReturn(AccountRequest(auctionHouseAddress.toBase58()), AccountInfo(TestDataProvider.auctionHouseAccount, false, 0, null, 0)) @@ -46,7 +47,7 @@ class FindAuctionHouseByAddressOperationTests { @Test fun testFindAuctionHouseByAddressOperationHandlesError() = runTest { // given - val auctionHouseAddress = Account().publicKey + val auctionHouseAddress = HotAccount().publicKey val expectedErrorMessage = "An error occurred" val rpcDriver = MockErrorRpcDriver(expectedErrorMessage) val connection = SolanaConnectionDriver(rpcDriver) diff --git a/lib/src/test/java/com/metaplex/lib/modules/candymachine/CandyMachineClientTests.kt b/lib/src/test/java/com/metaplex/lib/modules/candymachine/CandyMachineClientTests.kt index 80a3fa2..2bfc817 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/candymachine/CandyMachineClientTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/candymachine/CandyMachineClientTests.kt @@ -13,11 +13,8 @@ import com.metaplex.lib.MetaplexTestUtils import com.metaplex.lib.drivers.indenty.IdentityDriver import com.metaplex.lib.drivers.indenty.KeypairIdentityDriver import com.metaplex.lib.drivers.indenty.ReadOnlyIdentityDriver -import com.metaplex.lib.drivers.rpc.JdkRpcDriver -import com.metaplex.lib.drivers.solana.Commitment import com.metaplex.lib.drivers.solana.Connection import com.metaplex.lib.drivers.solana.SolanaConnectionDriver -import com.metaplex.lib.drivers.solana.TransactionOptions import com.metaplex.lib.generateConnectionDriver import com.metaplex.lib.modules.candymachines.CandyMachineClient import com.metaplex.lib.modules.candymachines.models.CandyMachine @@ -26,7 +23,7 @@ import com.metaplex.lib.modules.candymachines.refresh import com.metaplex.lib.modules.nfts.NftClient import com.metaplex.lib.modules.nfts.models.Metadata import com.metaplex.mock.driver.rpc.MockErrorRpcDriver -import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import com.solana.networking.RPCEndpoint import com.util.airdrop @@ -34,7 +31,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Assert import org.junit.Test -import java.net.URL class CandyMachineClientTests { @@ -53,7 +49,7 @@ class CandyMachineClientTests { val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineClient(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection) ) // when @@ -67,7 +63,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineCreateHandlesAndReturnsError() = runTest { // given - val signer = Account() + val signer = HotAccount() val expectedErrorMessage = "An Error Occurred" val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineClient(connection, KeypairIdentityDriver(signer, connection)) @@ -88,7 +84,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineInsertItemsHandlesAndReturnsError() = runTest { // given - val signer = Account() + val signer = HotAccount() val expectedErrorMessage = "An Error Occurred" val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineClient(connection, KeypairIdentityDriver(signer, connection)) @@ -108,7 +104,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineMintNftHandlesAndReturnsError() = runTest { // given - val signer = Account() + val signer = HotAccount() val expectedErrorMessage = "An Error Occurred" val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineClient(connection, KeypairIdentityDriver(signer, connection)) @@ -133,7 +129,7 @@ class CandyMachineClientTests { val cmAddress = PublicKey("4Add8hdxC44H3DcGfgWTvn2GNBfofk5uu2iatEW9LCYz") val connection = MetaplexTestUtils.generateConnectionDriver(RPCEndpoint.devnetSolana.url) val client = CandyMachineClient(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection)) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection)) // when val candyMachine: CandyMachine? = client.findByAddress(cmAddress).getOrNull() @@ -146,7 +142,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineCreateCreatesValidCandyMachine() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = CandyMachineClient(connection, identityDriver) @@ -166,7 +162,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineSetCollectionUpdatesCandyMachineCollection() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = CandyMachineClient(connection, identityDriver) @@ -190,7 +186,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineInsertItemsCanAddItemsToCandyMachine() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = CandyMachineClient(connection, identityDriver) @@ -216,7 +212,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineInsertItemsSequentiallyAddsItemsToCandyMachine() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = CandyMachineClient(connection, identityDriver) @@ -246,7 +242,7 @@ class CandyMachineClientTests { @Test fun testCandyMachineMintNftMintsAndReturnsNft() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = CandyMachineClient(connection, identityDriver) diff --git a/lib/src/test/java/com/metaplex/lib/modules/candymachine/Helpers.kt b/lib/src/test/java/com/metaplex/lib/modules/candymachine/Helpers.kt deleted file mode 100644 index 19e7f6a..0000000 --- a/lib/src/test/java/com/metaplex/lib/modules/candymachine/Helpers.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Helpers - * Metaplex - * - * Created by Funkatronics on 10/4/2022 - */ - -package com.metaplex.unit.lib.modules.candymachine - -import com.metaplex.lib.drivers.solana.Commitment -import com.metaplex.lib.drivers.solana.Connection -import com.metaplex.lib.drivers.solana.TransactionOptions -import com.metaplex.lib.experimental.jen.candymachine.CandyMachineInstructions -import com.metaplex.lib.experimental.jen.candymachine.ConfigLine -import com.metaplex.lib.experimental.jen.tokenmetadata.* -import com.metaplex.lib.extensions.signSendAndConfirm -import com.metaplex.lib.modules.candymachines.CandyMachineClient -import com.metaplex.lib.modules.candymachines.models.CandyMachine -import com.metaplex.lib.modules.nfts.models.NFT -import com.metaplex.lib.modules.nfts.operations.FindNftByMintOnChainOperationHandler -import com.metaplex.lib.modules.token.builders.addMintWithTokenInstruction -import com.metaplex.lib.programs.token_metadata.MasterEditionAccount -import com.metaplex.lib.programs.token_metadata.accounts.MetadataAccount -import com.metaplex.lib.shared.builders.TransactionBuilder -import com.solana.core.Account -import com.solana.core.PublicKey -import com.solana.core.Sysvar -import com.solana.core.Transaction -import com.solana.programs.SystemProgram -import com.solana.programs.TokenProgram -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -// will eventually move these into appropriate clients, but need to keep things in scope for now - -suspend fun CandyMachineClient.createNft(transactionOptions: TransactionOptions = - TransactionOptions(commitment = Commitment.CONFIRMED, skipPreflight = true)) -: Result = runCatching { - - val newMintAccount = Account() - - CreateNftTransactionBuilder(newMintAccount.publicKey, signer.publicKey, connection)//, dispatcher) - .build().getOrThrow() - .signSendAndConfirm(connection, signer, listOf(newMintAccount), transactionOptions) - - return FindNftByMintOnChainOperationHandler(connection)//, dispatcher) - .handle(newMintAccount.publicKey) -} - -class CreateNftTransactionBuilder(val newMint: PublicKey, payer: PublicKey, - connection: Connection, - dispatcher: CoroutineDispatcher = Dispatchers.IO) - : TransactionBuilder(payer, connection, dispatcher) { - - override suspend fun build(): Result = withContext(dispatcher) { - Result.success(Transaction().apply { - - val newMetadata = MetadataAccount.pda(newMint).getOrThrows() - val newEdition = MasterEditionAccount.pda(newMint).getOrThrows() - - addMintWithTokenInstruction(payer, newMint) - - addInstruction( - TokenMetadataInstructions.CreateMetadataAccountV3( - metadata = newMetadata, - mint = newMint, - mintAuthority = payer, - payer = payer, - updateAuthority = payer, - systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, - createMetadataAccountArgsV3 = CreateMetadataAccountArgsV3( - DataV2( - name = "My NFT", - symbol = "", - uri = "https://mockstorage.example.com/NSJTGKyev6YwYyEwxgLS", - sellerFeeBasisPoints = 333.toUShort(), - creators = listOf(Creator(payer, true, 100.toUByte())), - collection = null, - uses = null),true, CollectionDetails.V1 - ) - )) - - addInstruction( - TokenMetadataInstructions.CreateMasterEditionV3( - edition = newEdition, - mint = newMint, - updateAuthority = payer, - mintAuthority = payer, - payer = payer, - metadata = newMetadata, - tokenProgram = TokenProgram.PROGRAM_ID, - systemProgram = SystemProgram.PROGRAM_ID, - rent = Sysvar.SYSVAR_RENT_ADDRESS, - createMasterEditionArgs = CreateMasterEditionArgs(0.toULong()) - )) - }) - } -} - -suspend fun CandyMachineClient.insertItems( - candyMachine: CandyMachine, transactionOptions: TransactionOptions = - TransactionOptions(commitment = Commitment.CONFIRMED, skipPreflight = true) -): Result = - AddConfigLinesTransactionBuilder(candyMachine, signer.publicKey, connection)//, dispatcher) - .build().mapCatching { - it.signSendAndConfirm(connection, signer, transactionOptions = transactionOptions) - .getOrThrow() - } - -class AddConfigLinesTransactionBuilder(val candyMachine: CandyMachine, payer: PublicKey, - connection: Connection, - dispatcher: CoroutineDispatcher = Dispatchers.IO) - : TransactionBuilder(payer, connection, dispatcher) { - - val configLines = mutableListOf() - - override suspend fun build(): Result = withContext(dispatcher) { - Result.success(Transaction().apply { - candyMachine.apply { - - addInstruction( - CandyMachineInstructions.addConfigLines( - candyMachine = address, - authority= payer, - index = 0.toUInt(), - configLines = listOf(ConfigLine("My Nft", "http://example.com/mynft")) - )) - } - }) - } -} \ No newline at end of file diff --git a/lib/src/test/java/com/metaplex/lib/modules/candymachinev2/CandyMachineV2ClientTests.kt b/lib/src/test/java/com/metaplex/lib/modules/candymachinev2/CandyMachineV2ClientTests.kt index 2c3c337..c472961 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/candymachinev2/CandyMachineV2ClientTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/candymachinev2/CandyMachineV2ClientTests.kt @@ -19,6 +19,7 @@ import com.metaplex.lib.modules.candymachinesv2.CandyMachineV2Client import com.metaplex.lib.modules.candymachinesv2.models.CandyMachineV2 import com.metaplex.mock.driver.rpc.MockErrorRpcDriver import com.solana.core.Account +import com.solana.core.HotAccount import com.solana.core.PublicKey import com.solana.networking.RPCEndpoint import com.util.airdrop @@ -43,7 +44,7 @@ class CandyMachineV2ClientTests { val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineV2Client(connection, - ReadOnlyIdentityDriver(Account().publicKey, connection) + ReadOnlyIdentityDriver(HotAccount().publicKey, connection) ) // when @@ -57,7 +58,7 @@ class CandyMachineV2ClientTests { @Test fun testCandyMachineCreateHandlesAndReturnsError() = runTest { // given - val signer = Account() + val signer = HotAccount() val expectedErrorMessage = "An Error Occurred" val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineV2Client(connection, KeypairIdentityDriver(signer, connection)) @@ -73,7 +74,7 @@ class CandyMachineV2ClientTests { @Test fun testCandyMachineMintNftHandlesAndReturnsError() = runTest { // given - val signer = Account() + val signer = HotAccount() val expectedErrorMessage = "An Error Occurred" val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = CandyMachineV2Client(connection, KeypairIdentityDriver(signer, connection)) @@ -91,7 +92,7 @@ class CandyMachineV2ClientTests { @Test fun testFindCandyMachineV2ByAddressReturnsValidCandyMachine() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val client = CandyMachineV2Client(connection, KeypairIdentityDriver(signer, connection)) @@ -110,7 +111,7 @@ class CandyMachineV2ClientTests { @Test fun testCandyMachineCreateCreatesValidCandyMachine() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val client = CandyMachineV2Client(connection, KeypairIdentityDriver(signer, connection)) @@ -128,7 +129,7 @@ class CandyMachineV2ClientTests { @Test fun testCandyMachineMintNftMintsAndReturnsNft() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val client = CandyMachineV2Client(connection, KeypairIdentityDriver(signer, connection)) diff --git a/lib/src/test/java/com/metaplex/lib/modules/nfts/NftClientTests.kt b/lib/src/test/java/com/metaplex/lib/modules/nfts/NftClientTests.kt index 96dd25a..b2aad33 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/nfts/NftClientTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/nfts/NftClientTests.kt @@ -9,21 +9,16 @@ package com.metaplex.lib.modules.nfts import com.metaplex.lib.MetaplexTestUtils import com.metaplex.lib.drivers.indenty.KeypairIdentityDriver -import com.metaplex.lib.drivers.rpc.JdkRpcDriver -import com.metaplex.lib.drivers.solana.Commitment import com.metaplex.lib.drivers.solana.SolanaConnectionDriver -import com.metaplex.lib.drivers.solana.TransactionOptions import com.metaplex.lib.generateConnectionDriver import com.metaplex.lib.modules.nfts.models.Metadata import com.metaplex.lib.programs.token_metadata.accounts.MetaplexCollectionDetails import com.metaplex.mock.driver.rpc.MockErrorRpcDriver -import com.solana.core.Account -import com.solana.networking.RPCEndpoint +import com.solana.core.HotAccount import com.util.airdrop import kotlinx.coroutines.test.runTest import org.junit.Assert import org.junit.Test -import java.net.URL class NftClientTests { @@ -31,7 +26,7 @@ class NftClientTests { @Test fun testNftCreateHandlesAndReturnsError() = runTest { // given - val signer = Account() + val signer = HotAccount() val expectedErrorMessage = "An Error Occurred" val connection = SolanaConnectionDriver(MockErrorRpcDriver(expectedErrorMessage)) val client = NftClient(connection, KeypairIdentityDriver(signer, connection)) @@ -52,7 +47,7 @@ class NftClientTests { @Test fun testNftCreateCreatesValidNft() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = NftClient(connection, identityDriver) @@ -77,12 +72,12 @@ class NftClientTests { @Test fun testNftCreateWithCollectionCreatesValidNft() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = NftClient(connection, identityDriver) - val expectedCollection = Account() + val expectedCollection = HotAccount() val expectedMetadata = Metadata("My NFT", uri = "http://example.com/sd8756fsuyvvbf37684", sellerFeeBasisPoints = 250, @@ -106,7 +101,7 @@ class NftClientTests { @Test fun testNftCreateCollectionCreatesValidCollectionNft() = runTest { // given - val signer = Account() + val signer = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val identityDriver = KeypairIdentityDriver(signer, connection) val client = NftClient(connection, identityDriver) diff --git a/lib/src/test/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandlerTests.kt b/lib/src/test/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandlerTests.kt index 26dc334..46e040e 100644 --- a/lib/src/test/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandlerTests.kt +++ b/lib/src/test/java/com/metaplex/lib/modules/nfts/operations/FindNftsByOwnerOnChainOperationHandlerTests.kt @@ -3,28 +3,21 @@ package com.metaplex.lib.modules.nfts.operations import com.metaplex.lib.* -import com.metaplex.lib.drivers.indenty.KeypairIdentityDriver -import com.metaplex.lib.drivers.rpc.JdkRpcDriver -import com.metaplex.lib.drivers.solana.Commitment -import com.metaplex.lib.drivers.solana.SolanaConnectionDriver -import com.metaplex.lib.drivers.solana.TransactionOptions -import com.metaplex.lib.drivers.storage.MemoryStorageDriver import com.metaplex.lib.modules.nfts.models.Metadata import com.metaplex.lib.modules.nfts.models.NFT -import com.solana.core.Account +import com.solana.core.HotAccount import com.util.airdrop import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Assert import org.junit.Test -import java.net.URL class FindNftsByOwnerOnChainOperationHandlerTests { @Test fun testFindNftsByOwnerOnChainOperation() = runTest { // given - val owner = Account() + val owner = HotAccount() val connection = MetaplexTestUtils.generateConnectionDriver() val metaplex = MetaplexTestUtils.generateMetaplexInstance(owner, connection) diff --git a/lib/src/test/java/com/metaplex/mock/driver/solana/SolanaConnection.kt b/lib/src/test/java/com/metaplex/mock/driver/solana/SolanaConnection.kt index 0c31efb..6952005 100644 --- a/lib/src/test/java/com/metaplex/mock/driver/solana/SolanaConnection.kt +++ b/lib/src/test/java/com/metaplex/mock/driver/solana/SolanaConnection.kt @@ -8,15 +8,9 @@ package com.metaplex.mock.driver.solana import com.metaplex.lib.drivers.solana.* -import com.metaplex.lib.serialization.serializers.legacy.BorshCodeableSerializer import com.solana.core.PublicKey import com.solana.models.ProgramAccountConfig import com.solana.models.SignatureStatusRequestConfiguration -import com.solana.models.buffer.BufferInfo -import com.solana.vendor.borshj.BorshCodable -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.serialization.KSerializer /** @@ -88,56 +82,4 @@ abstract class SolanaConnection : Connection { else result as Result> // safe cast, null case handled above } //endregion - - //region DEPRECATED METHODS - override fun getAccountInfo( - account: PublicKey, - decodeTo: Class, - onComplete: (Result>) -> Unit - ) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getAccountInfo(BorshCodeableSerializer(decodeTo), account) - .map { it.toBufferInfo() }) - } - } - - override fun getMultipleAccountsInfo( - accounts: List, - decodeTo: Class, - onComplete: (Result?>>) -> Unit - ) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getMultipleAccountsInfo(BorshCodeableSerializer(decodeTo), accounts) - .map { it.map { it?.toBufferInfo() } }) - } - } - - override fun getProgramAccounts( - account: PublicKey, - programAccountConfig: ProgramAccountConfig, - decodeTo: Class, - onComplete: (Result>>) -> Unit) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getProgramAccounts(BorshCodeableSerializer(decodeTo), account, programAccountConfig) - .map { it.map { - com.solana.models.ProgramAccount(it.account.toBufferInfo(), it.publicKey) - } }) - } - } - - override fun getSignatureStatuses(signatures: List, - configs: SignatureStatusRequestConfiguration?, - onComplete: ((Result) -> Unit)) { - CoroutineScope(Dispatchers.IO).launch { - onComplete(getSignatureStatuses(signatures, configs).map { - com.solana.models.SignatureStatus(it.map { sigStatus -> - com.solana.models.SignatureStatus.Value( - sigStatus.slot, sigStatus.confirmations, - sigStatus.err, sigStatus.confirmationStatus - ) - }) - }) - } - } - //endregion } \ No newline at end of file diff --git a/lib/src/test/java/com/util/Airdrop.kt b/lib/src/test/java/com/util/Airdrop.kt index 80df912..964d211 100644 --- a/lib/src/test/java/com/util/Airdrop.kt +++ b/lib/src/test/java/com/util/Airdrop.kt @@ -9,7 +9,10 @@ package com.util import com.metaplex.lib.drivers.rpc.RpcRequest import com.metaplex.lib.drivers.solana.Connection +import com.metaplex.lib.extensions.confirmTransaction import com.solana.core.PublicKey +import kotlinx.coroutines.isActive +import kotlinx.coroutines.withTimeout import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.* import kotlin.math.pow @@ -30,32 +33,4 @@ class AirdropRequest(wallet: PublicKey, lamports: Long, commitment: String = "co } suspend fun Connection.airdrop(wallet: PublicKey, amountSol: Float) = - get(AirdropRequest(wallet, amountSol), String.serializer()) - -//suspend fun Connection.airdropAndConfirm(wallet: PublicKey, amountSol: Float) { -// -// val startingLamports = -// get(AccountBalanceRequest(wallet), SolanaResponseSerializer(Long.serializer())).getOrNull() -// ?: throw Error("could not read starting balance of wallet") -// -// val expectedLamports = startingLamports + (amountSol*10f.pow(9)).toLong() -// -// get(AirdropRequest(wallet, amountSol), String.serializer()).apply { -// -// val result = getOrNull() -// -// // wait for confirmation (hacky) -// while (get(SignatureStatusRequest(listOf(result!!)), SignatureStatusesSerializer()) -// .getOrNull()?.first()?.confirmationStatus != "confirmed") { -// val millis = System.currentTimeMillis(); var dummy = 0 -// while (System.currentTimeMillis() - millis < 500) dummy++ -// } -// -// // wait for the balance to actually show up (super hacky) -// while (get(AccountBalanceRequest(wallet), SolanaResponseSerializer(Long.serializer())) -// .getOrNull() != expectedLamports) { -// val millis = System.currentTimeMillis(); var dummy = 0 -// while (System.currentTimeMillis() - millis < 500) dummy++ -// } -// } -//} \ No newline at end of file + get(AirdropRequest(wallet, amountSol), String.serializer()).confirmTransaction(this) \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 79f9043..50d72ca 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -36,7 +36,7 @@ android { dependencies { implementation project(path: ':lib') - implementation 'com.github.metaplex-foundation:SolanaKT:1.1.5' + implementation 'com.github.metaplex-foundation:SolanaKT:2.0.0' implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.6.0' diff --git a/sample/src/main/java/com/metaplex/sample/FirstFragment.kt b/sample/src/main/java/com/metaplex/sample/FirstFragment.kt index 87eefcd..f15d485 100644 --- a/sample/src/main/java/com/metaplex/sample/FirstFragment.kt +++ b/sample/src/main/java/com/metaplex/sample/FirstFragment.kt @@ -28,6 +28,9 @@ import com.metaplex.lib.solana.SolanaConnectionDriver import com.metaplex.sample.databinding.FragmentFirstBinding import com.solana.core.PublicKey import com.solana.networking.RPCEndpoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** @@ -62,19 +65,20 @@ class FirstFragment : Fragment() { } val solanaConnection = SolanaConnectionDriver(RPCEndpoint.mainnetBetaSolana) - val solanaIdentityDriver = ReadOnlyIdentityDriver(ownerPublicKey, solanaConnection.solanaRPC) + val solanaIdentityDriver = ReadOnlyIdentityDriver(ownerPublicKey, solanaConnection) val storageDriver = OkHttpSharedStorageDriver() metaplex = Metaplex(solanaConnection, solanaIdentityDriver, storageDriver) - metaplex.nft.findAllByOwner(ownerPublicKey) { result -> - result.onSuccess { nfts -> - val nftList = nfts.filterNotNull() - val adapter = NFTRecycleViewAdapter(requireContext(), metaplex, nftList.toTypedArray(), ownerPublicKey) - requireActivity().runOnUiThread { - binding.nftsRecyclerView.layoutManager = GridLayoutManager(context, 2) - binding.nftsRecyclerView.adapter = adapter + CoroutineScope(Dispatchers.IO).launch { + metaplex.nft.findAllByOwner(ownerPublicKey) + .onSuccess { nfts -> + val nftList = nfts.filterNotNull() + val adapter = NFTRecycleViewAdapter(requireContext(), metaplex, nftList.toTypedArray(), ownerPublicKey) + requireActivity().runOnUiThread { + binding.nftsRecyclerView.layoutManager = GridLayoutManager(context, 2) + binding.nftsRecyclerView.adapter = adapter + } } - } } } diff --git a/sample/src/main/java/com/metaplex/sample/NftDetailsActivity.kt b/sample/src/main/java/com/metaplex/sample/NftDetailsActivity.kt index 895c95a..4ad33c6 100644 --- a/sample/src/main/java/com/metaplex/sample/NftDetailsActivity.kt +++ b/sample/src/main/java/com/metaplex/sample/NftDetailsActivity.kt @@ -32,6 +32,9 @@ import com.metaplex.lib.programs.token_metadata.accounts.MetaplexCreator import com.metaplex.lib.solana.SolanaConnectionDriver import com.solana.core.PublicKey import com.solana.networking.RPCEndpoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class NftDetailsActivity : AppCompatActivity() { @@ -69,15 +72,16 @@ class NftDetailsActivity : AppCompatActivity() { setTitle(nftName) val solanaConnection = SolanaConnectionDriver(RPCEndpoint.mainnetBetaSolana) - val solanaIdentityDriver = ReadOnlyIdentityDriver(ownerPublicKey, solanaConnection.solanaRPC) + val solanaIdentityDriver = ReadOnlyIdentityDriver(ownerPublicKey, solanaConnection) val storageDriver = OkHttpSharedStorageDriver() metaplex = Metaplex(solanaConnection, solanaIdentityDriver, storageDriver) - metaplex.nft.findByMint(PublicKey(mintAccount)) { result -> - result.onSuccess { nft -> - fetchOffChainMetadata(this, nft) - } + CoroutineScope(Dispatchers.IO).launch { + metaplex.nft.findByMint(PublicKey(mintAccount)) + .onSuccess { nft -> + fetchOffChainMetadata(this@NftDetailsActivity, nft) + } } } @@ -101,12 +105,13 @@ class NftDetailsActivity : AppCompatActivity() { recyclerView.layoutManager = layoutManager recyclerView.adapter = NftAttributesRecyclerViewAdapter(it.attributes!!.toTypedArray()) - val hasCreators = nft.metadataAccount.data.hasCreators - val creators = nft.metadataAccount.data.creators - creators.sortByDescending { it.share } + val hasCreators = nft.metadataAccount.data.creators?.isNotEmpty() == true + val creators = (nft.metadataAccount.data.creators ?: arrayOf()).also { + it.sortByDescending { it.share } + } makeCreatorsViews(findViewById(R.id.nftCreators), hasCreators, creators) - val salesStatus = if (nft.metadataAccount.data.hasCreators) "Secondary" else "Primary" + val salesStatus = if (hasCreators) "Secondary" else "Primary" val royaltyFeeBasis = (nft.metadataAccount.data.sellerFeeBasisPoints.toDouble() / 100).toString() val metadataStatus = if (nft.metadataAccount.isMutable) "Mutable" else "Immutable" val nftOverviewProps = arrayOf(salesStatus, royaltyFeeBasis.plus("%"), metadataStatus) @@ -137,7 +142,7 @@ class NftDetailsActivity : AppCompatActivity() { private fun makeCreatorsViews(view: ViewGroup, hasCreators: Boolean, creators: Array) { if (hasCreators) { for (creator in creators) { - if (creator.share != 0) { + if (creator.share.toInt() != 0) { val creatorAddress = creator.address.toBase58() val creatorRoyaltySplit = creator.share.toString().plus("%")