-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
blockchain/src/main/java/com/tangem/blockchain/blockchains/aptos/models/AptosAccountInfo.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.tangem.blockchain.blockchains.aptos.models | ||
|
||
import java.math.BigDecimal | ||
|
||
internal data class AptosAccountInfo(val sequenceNumber: Long, val balance: BigDecimal) |
28 changes: 28 additions & 0 deletions
28
...hain/src/main/java/com/tangem/blockchain/blockchains/aptos/models/AptosTransactionInfo.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.tangem.blockchain.blockchains.aptos.models | ||
|
||
/** | ||
* Aptos transaction info | ||
* | ||
* @property sequenceNumber sequence number | ||
* @property publicKey wallet public key | ||
* @property sourceAddress source address | ||
* @property destinationAddress destination address | ||
* @property amount amount | ||
* @property gasUnitPrice gas unit price | ||
* @property maxGasAmount max gas amount | ||
* @property expirationTimestamp expiration timestamp in seconds | ||
* @property hash hash of transaction | ||
* | ||
* @author Andrew Khokhlov on 16/01/2024 | ||
*/ | ||
data class AptosTransactionInfo( | ||
val sequenceNumber: Long, | ||
val publicKey: String, | ||
val sourceAddress: String, | ||
val destinationAddress: String, | ||
val amount: Long, | ||
val gasUnitPrice: Long, | ||
val maxGasAmount: Long, | ||
val expirationTimestamp: Long, | ||
val hash: String? = null, | ||
) |
40 changes: 40 additions & 0 deletions
40
...ain/src/main/java/com/tangem/blockchain/blockchains/aptos/network/AptosNetworkProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.tangem.blockchain.blockchains.aptos.network | ||
|
||
import com.tangem.blockchain.blockchains.aptos.models.AptosAccountInfo | ||
import com.tangem.blockchain.blockchains.aptos.models.AptosTransactionInfo | ||
import com.tangem.blockchain.common.NetworkProvider | ||
import com.tangem.blockchain.extensions.Result | ||
|
||
/** | ||
* Aptos network provider | ||
* | ||
* @author Andrew Khokhlov on 11/01/2024 | ||
*/ | ||
internal interface AptosNetworkProvider : NetworkProvider { | ||
|
||
/** Get account information by [address] */ | ||
suspend fun getAccountInfo(address: String): Result<AptosAccountInfo> | ||
|
||
/** | ||
* Get normal gas price. | ||
* Prioritizing transactions isn't supports due to difficult scheme of fee calculating. | ||
*/ | ||
suspend fun getGasUnitPrice(): Result<Long> | ||
|
||
/** | ||
* Calculate gas price unit that required to send transaction | ||
* | ||
* @param transaction unsigned transaction | ||
*/ | ||
suspend fun calculateUsedGasPriceUnit(transaction: AptosTransactionInfo): Result<Long> | ||
|
||
/** | ||
* Encode transaction in BCS | ||
* | ||
* @param transaction unsigned transaction | ||
*/ | ||
suspend fun encodeTransaction(transaction: AptosTransactionInfo): Result<String> | ||
|
||
/** Submit signed transaction [transaction] */ | ||
suspend fun submitTransaction(transaction: AptosTransactionInfo): Result<String> | ||
} |
82 changes: 82 additions & 0 deletions
82
...hain/src/main/java/com/tangem/blockchain/blockchains/aptos/network/AptosNetworkService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.tangem.blockchain.blockchains.aptos.network | ||
|
||
import com.tangem.blockchain.blockchains.aptos.models.AptosAccountInfo | ||
import com.tangem.blockchain.blockchains.aptos.models.AptosTransactionInfo | ||
import com.tangem.blockchain.common.toBlockchainSdkError | ||
import com.tangem.blockchain.extensions.Result | ||
import com.tangem.blockchain.extensions.successOr | ||
import com.tangem.blockchain.network.MultiNetworkProvider | ||
|
||
/** | ||
* Aptos network service. | ||
* Implementation of [AptosNetworkProvider] that wrap network requests in [MultiNetworkProvider]. | ||
* | ||
* @param providers list of [AptosNetworkProvider] | ||
* | ||
* @author Andrew Khokhlov on 11/01/2024 | ||
*/ | ||
internal class AptosNetworkService(providers: List<AptosNetworkProvider>) : AptosNetworkProvider { | ||
|
||
override val baseUrl: String | ||
get() = multiJsonRpcProvider.currentProvider.baseUrl | ||
|
||
private val multiJsonRpcProvider = MultiNetworkProvider(providers) | ||
|
||
override suspend fun getAccountInfo(address: String): Result<AptosAccountInfo> { | ||
return try { | ||
val accountInfo = multiJsonRpcProvider.performRequest(AptosNetworkProvider::getAccountInfo, address) | ||
.successOr { return it } | ||
|
||
Result.Success(accountInfo) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun getGasUnitPrice(): Result<Long> { | ||
return try { | ||
val gasUnitPrice = multiJsonRpcProvider.performRequest(AptosNetworkProvider::getGasUnitPrice) | ||
.successOr { return it } | ||
|
||
Result.Success(gasUnitPrice) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun calculateUsedGasPriceUnit(transaction: AptosTransactionInfo): Result<Long> { | ||
return try { | ||
val usedGasPriceUnit = multiJsonRpcProvider.performRequest( | ||
request = AptosNetworkProvider::calculateUsedGasPriceUnit, | ||
data = transaction, | ||
) | ||
.successOr { return it } | ||
|
||
Result.Success(usedGasPriceUnit) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun encodeTransaction(transaction: AptosTransactionInfo): Result<String> { | ||
return try { | ||
val hash = multiJsonRpcProvider.performRequest(AptosNetworkProvider::encodeTransaction, transaction) | ||
.successOr { return it } | ||
|
||
Result.Success(hash) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun submitTransaction(transaction: AptosTransactionInfo): Result<String> { | ||
return try { | ||
val hash = multiJsonRpcProvider.performRequest(AptosNetworkProvider::submitTransaction, transaction) | ||
.successOr { return it } | ||
|
||
Result.Success(hash) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
...va/com/tangem/blockchain/blockchains/aptos/network/converter/AptosTransactionConverter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.tangem.blockchain.blockchains.aptos.network.converter | ||
|
||
import com.tangem.blockchain.blockchains.aptos.models.AptosTransactionInfo | ||
import com.tangem.blockchain.blockchains.aptos.network.request.AptosTransactionBody | ||
|
||
/** | ||
* Converter from [AptosTransactionInfo] to [AptosTransactionBody] | ||
* | ||
* @author Andrew Khokhlov on 16/01/2024 | ||
*/ | ||
internal object AptosTransactionConverter { | ||
|
||
private const val TRANSFER_PAYLOAD_TYPE = "entry_function_payload" | ||
private const val TRANSFER_PAYLOAD_FUNCTION = "0x1::aptos_account::transfer" | ||
private const val SIGNATURE_TYPE = "ed25519_signature" | ||
|
||
fun convert(from: AptosTransactionInfo): AptosTransactionBody { | ||
return AptosTransactionBody( | ||
sender = from.sourceAddress, | ||
sequenceNumber = from.sequenceNumber.toString(), | ||
expirationTimestamp = from.expirationTimestamp.toString(), | ||
gasUnitPrice = from.gasUnitPrice.toString(), | ||
maxGasAmount = from.maxGasAmount.toString(), | ||
payload = createTransferPayload(from.destinationAddress, from.amount), | ||
signature = from.hash?.let { createSignature(publicKey = from.publicKey, hash = it) }, | ||
) | ||
} | ||
|
||
private fun createTransferPayload(destination: String, amount: Long): AptosTransactionBody.Payload { | ||
return AptosTransactionBody.Payload( | ||
type = TRANSFER_PAYLOAD_TYPE, | ||
function = TRANSFER_PAYLOAD_FUNCTION, | ||
argumentTypes = listOf(), | ||
arguments = listOf(destination, amount.toString()), | ||
) | ||
} | ||
|
||
private fun createSignature(publicKey: String, hash: String): AptosTransactionBody.Signature { | ||
return AptosTransactionBody.Signature( | ||
type = SIGNATURE_TYPE, | ||
publicKey = publicKey, | ||
signature = hash, | ||
) | ||
} | ||
} |
107 changes: 107 additions & 0 deletions
107
...a/com/tangem/blockchain/blockchains/aptos/network/provider/AptosJsonRpcNetworkProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package com.tangem.blockchain.blockchains.aptos.network.provider | ||
|
||
import com.tangem.blockchain.blockchains.aptos.models.AptosAccountInfo | ||
import com.tangem.blockchain.blockchains.aptos.models.AptosTransactionInfo | ||
import com.tangem.blockchain.blockchains.aptos.network.AptosApi | ||
import com.tangem.blockchain.blockchains.aptos.network.AptosNetworkProvider | ||
import com.tangem.blockchain.blockchains.aptos.network.converter.AptosTransactionConverter | ||
import com.tangem.blockchain.blockchains.aptos.network.response.AptosResourceBody | ||
import com.tangem.blockchain.common.BlockchainSdkError | ||
import com.tangem.blockchain.common.toBlockchainSdkError | ||
import com.tangem.blockchain.extensions.Result | ||
import com.tangem.blockchain.network.createRetrofitInstance | ||
import okhttp3.Interceptor | ||
|
||
internal class AptosJsonRpcNetworkProvider( | ||
override val baseUrl: String, | ||
headerInterceptors: List<Interceptor>, | ||
private val decimals: Int, | ||
) : AptosNetworkProvider { | ||
|
||
private val api = createRetrofitInstance(baseUrl = baseUrl, headerInterceptors = headerInterceptors) | ||
.create(AptosApi::class.java) | ||
|
||
override suspend fun getAccountInfo(address: String): Result<AptosAccountInfo> { | ||
return try { | ||
val resources = api.getAccountResources(address) | ||
|
||
val accountResource = resources.getResource<AptosResourceBody.AccountResource>()?.account | ||
val coinResource = resources.getResource<AptosResourceBody.CoinResource>()?.coinData | ||
|
||
if (accountResource != null && coinResource != null) { | ||
Result.Success( | ||
AptosAccountInfo( | ||
sequenceNumber = accountResource.sequenceNumber.toLong(), | ||
balance = coinResource.coin.value.toBigDecimal().movePointLeft(decimals), | ||
), | ||
) | ||
} else { | ||
Result.Failure(BlockchainSdkError.AccountNotFound) | ||
} | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun getGasUnitPrice(): Result<Long> { | ||
return try { | ||
val response = api.estimateGasPrice() | ||
|
||
Result.Success(response.normalGasUnitPrice) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun calculateUsedGasPriceUnit(transaction: AptosTransactionInfo): Result<Long> { | ||
return try { | ||
val requestBody = AptosTransactionConverter.convert(transaction) | ||
val response = api.simulateTransaction(requestBody).firstOrNull() | ||
|
||
val usedGasUnit = response?.usedGasUnit?.toLongOrNull() | ||
if (response != null && usedGasUnit != null && response.isSuccess) { | ||
Result.Success(usedGasUnit) | ||
} else { | ||
Result.Failure(BlockchainSdkError.FailedToLoadFee) | ||
} | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun encodeTransaction(transaction: AptosTransactionInfo): Result<String> { | ||
return try { | ||
val requestBody = AptosTransactionConverter.convert(transaction) | ||
val hash = api.encodeSubmission(requestBody) | ||
|
||
if (hash.isNotBlank()) { | ||
Result.Success(hash) | ||
} else { | ||
Result.Failure(BlockchainSdkError.FailedToSendException) | ||
} | ||
|
||
Result.Success(hash) | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
override suspend fun submitTransaction(transaction: AptosTransactionInfo): Result<String> { | ||
return try { | ||
val requestBody = AptosTransactionConverter.convert(transaction) | ||
val response = api.submitTransaction(requestBody) | ||
|
||
if (response.hash.isNotBlank()) { | ||
Result.Success(response.hash) | ||
} else { | ||
Result.Failure(BlockchainSdkError.FailedToSendException) | ||
} | ||
} catch (e: Exception) { | ||
Result.Failure(e.toBlockchainSdkError()) | ||
} | ||
} | ||
|
||
private inline fun <reified T : AptosResourceBody> List<AptosResourceBody>.getResource(): T? { | ||
return firstNotNullOfOrNull { it as? T } | ||
} | ||
} |