Updated on 2026-08-14
This commit is contained in:
parent
98e7136b42
commit
61b848204f
5 changed files with 326 additions and 87 deletions
|
|
@ -2,7 +2,6 @@ package com.tangem.data.transaction
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.data.transaction.convertes.GaslessSignedTransactionResultConverter
|
||||
import com.tangem.data.transaction.convertes.GaslessTransactionRequestBuilder
|
||||
|
|
@ -27,11 +26,12 @@ class DefaultGaslessTransactionRepository(
|
|||
) : GaslessTransactionRepository {
|
||||
|
||||
private val supportedTokensState = MutableStateFlow<Map<Network.ID, Set<CryptoCurrency>>>(hashMapOf())
|
||||
|
||||
private val gaslessTransactionRequestBuilder = GaslessTransactionRequestBuilder()
|
||||
private val signedTransactionResultConverter = GaslessSignedTransactionResultConverter()
|
||||
|
||||
override fun isNetworkSupported(network: Network): Boolean {
|
||||
val blockchain = Blockchain.fromNetworkId(network.backendId) ?: return false
|
||||
val blockchain = Blockchain.fromId(network.rawId)
|
||||
return SUPPORTED_BLOCKCHAINS.contains(blockchain)
|
||||
}
|
||||
|
||||
|
|
@ -44,8 +44,7 @@ class DefaultGaslessTransactionRepository(
|
|||
|
||||
val supportedTokensData = gaslessTxServiceApi.getSupportedTokens().getOrThrow()
|
||||
if (supportedTokensData.isSuccess) {
|
||||
val networkBlockchain = Blockchain.fromNetworkId(network.backendId)
|
||||
?: error("Cannot determine blockchain for network id: ${network.backendId}")
|
||||
val networkBlockchain = Blockchain.fromId(network.rawId)
|
||||
val supportedTokens = supportedTokensData.result.tokens
|
||||
.filter {
|
||||
it.chainId == networkBlockchain.getChainId()
|
||||
|
|
@ -79,16 +78,14 @@ class DefaultGaslessTransactionRepository(
|
|||
return TOKEN_RECEIVER_ADDRESS
|
||||
}
|
||||
|
||||
override suspend fun sendGaslessTransaction(
|
||||
override suspend fun signGaslessTransaction(
|
||||
gaslessTransactionData: GaslessTransactionData,
|
||||
signature: String,
|
||||
userAddress: String,
|
||||
network: Network,
|
||||
eip7702Auth: Eip7702Authorization?,
|
||||
): GaslessSignedTransactionResult = withContext(coroutineDispatcherProvider.io) {
|
||||
val blockchain = Blockchain.fromNetworkId(network.backendId)
|
||||
?: error("Cannot determine blockchain for network id: ${network.backendId}")
|
||||
|
||||
val blockchain = Blockchain.fromId(network.rawId)
|
||||
val transactionRequest = gaslessTransactionRequestBuilder.build(
|
||||
gaslessTransaction = gaslessTransactionData,
|
||||
signature = signature,
|
||||
|
|
@ -111,9 +108,14 @@ class DefaultGaslessTransactionRepository(
|
|||
return BASE_GAS_FOR_TRANSACTION
|
||||
}
|
||||
|
||||
override fun getChainIdForNetwork(network: Network): Int {
|
||||
val networkBlockchain = Blockchain.fromId(network.rawId)
|
||||
return networkBlockchain.getChainId() ?: error("ChainId not found for blockchain ${networkBlockchain.name}")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
const val TOKEN_RECEIVER_ADDRESS = "0x"
|
||||
const val TOKEN_RECEIVER_ADDRESS = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
||||
|
||||
val BASE_GAS_FOR_TRANSACTION: BigInteger = BigInteger("100000")
|
||||
val SUPPORTED_BLOCKCHAINS = arrayOf(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.data.transaction
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
|
@ -17,7 +16,7 @@ class MockedGaslessTransactionRepository(
|
|||
) : GaslessTransactionRepository {
|
||||
|
||||
override fun isNetworkSupported(network: Network): Boolean {
|
||||
val blockchain = Blockchain.fromNetworkId(network.backendId) ?: return false
|
||||
val blockchain = Blockchain.fromId(network.rawId)
|
||||
return SUPPORTED_BLOCKCHAINS.contains(blockchain)
|
||||
}
|
||||
|
||||
|
|
@ -35,11 +34,16 @@ class MockedGaslessTransactionRepository(
|
|||
return setOf(usdcPolygon)
|
||||
}
|
||||
|
||||
override fun getChainIdForNetwork(network: Network): Int {
|
||||
val networkBlockchain = Blockchain.fromId(network.rawId)
|
||||
return networkBlockchain.getChainId() ?: error("ChainId not found for blockchain ${networkBlockchain.name}")
|
||||
}
|
||||
|
||||
override fun getTokenFeeReceiverAddress(): String {
|
||||
return TOKEN_RECEIVER_ADDRESS
|
||||
}
|
||||
|
||||
override suspend fun sendGaslessTransaction(
|
||||
override suspend fun signGaslessTransaction(
|
||||
gaslessTransactionData: GaslessTransactionData,
|
||||
signature: String,
|
||||
userAddress: String,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ interface GaslessTransactionRepository {
|
|||
* @throws IllegalStateException if network is not supported or chainId cannot be determined
|
||||
* @throws Exception if service returns error or network request fails
|
||||
*/
|
||||
suspend fun sendGaslessTransaction(
|
||||
suspend fun signGaslessTransaction(
|
||||
gaslessTransactionData: GaslessTransactionData,
|
||||
signature: String,
|
||||
userAddress: String,
|
||||
|
|
@ -63,4 +63,6 @@ interface GaslessTransactionRepository {
|
|||
* Hardcoded value as baseGas
|
||||
*/
|
||||
fun getBaseGasForTransaction(): BigInteger
|
||||
|
||||
fun getChainIdForNetwork(network: Network): Int
|
||||
}
|
||||
|
|
@ -6,15 +6,30 @@ import arrow.core.raise.either
|
|||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
|
||||
import com.tangem.blockchain.blockchains.ethereum.gasless.EthereumGaslessDataProvider
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.blockchains.ethereum.models.EIP7702AuthorizationData
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionSigner
|
||||
import com.tangem.blockchain.common.UnmarshalHelper
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.formatHex
|
||||
import com.tangem.blockchain.extensions.removeLeadingZero
|
||||
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplySendCallData
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.toDecompressedPublicKey
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.card.models.TwinKey
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.transaction.GaslessTransactionRepository
|
||||
import com.tangem.domain.transaction.error.SendTransactionError
|
||||
import com.tangem.domain.transaction.models.Eip7702Authorization
|
||||
import com.tangem.domain.transaction.models.GaslessSignedTransactionResult
|
||||
import com.tangem.domain.transaction.models.GaslessTransactionData
|
||||
import com.tangem.domain.transaction.models.TransactionFeeExtended
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
|
@ -23,95 +38,219 @@ import java.math.BigInteger
|
|||
class CreateAndSendGaslessTransactionUseCase(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val gaslessTransactionRepository: GaslessTransactionRepository,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val getHotWalletSigner: (UserWallet.Hot) -> TransactionSigner,
|
||||
) {
|
||||
|
||||
@Suppress("UnusedPrivateProperty")
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
userWallet: UserWallet,
|
||||
transactionData: TransactionData,
|
||||
fee: TransactionFeeExtended,
|
||||
): Either<SendTransactionError, Unit> {
|
||||
return either {
|
||||
catch(
|
||||
block = {
|
||||
transactionData as? TransactionData.Uncompiled ?: error("Uncompiled transaction data required")
|
||||
): Either<SendTransactionError, Unit> = either {
|
||||
catch(
|
||||
block = {
|
||||
val uncompiledTxData = validateTransactionData(transactionData)
|
||||
val context = prepareGaslessContext(userWallet, uncompiledTxData, fee)
|
||||
val signedData = signGaslessTransactionByUser(userWallet, context, uncompiledTxData)
|
||||
val remoteSignedData = signTransactionOnBackend(context, signedData, uncompiledTxData)
|
||||
broadcastSignedTransaction(remoteSignedData)
|
||||
},
|
||||
catch = {
|
||||
raise(SendTransactionError.DataError(it.message))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val tokenForFeeStatus = getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
|
||||
userWalletId,
|
||||
fee.feeTokenId,
|
||||
).getOrNull() ?: error("Token for fee not found")
|
||||
/**
|
||||
* Validates and casts transaction data to Uncompiled type.
|
||||
*/
|
||||
private fun validateTransactionData(transactionData: TransactionData): TransactionData.Uncompiled {
|
||||
return transactionData as? TransactionData.Uncompiled
|
||||
?: error("Uncompiled transaction data required")
|
||||
}
|
||||
|
||||
val blockchain = Blockchain.fromId(tokenForFeeStatus.currency.network.rawId)
|
||||
/**
|
||||
* Prepares all necessary context for gasless transaction.
|
||||
* Includes: wallet manager, gasless provider, token status, nonce, transaction data.
|
||||
*/
|
||||
private suspend fun prepareGaslessContext(
|
||||
userWallet: UserWallet,
|
||||
transactionData: TransactionData.Uncompiled,
|
||||
fee: TransactionFeeExtended,
|
||||
): GaslessContext {
|
||||
val tokenForFeeStatus = getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
|
||||
userWallet.walletId,
|
||||
fee.feeTokenId,
|
||||
).getOrNull() ?: error("Token for fee not found")
|
||||
|
||||
val gaslessTransactionData = createGaslessTransactionData(
|
||||
userWalletId = userWalletId,
|
||||
transactionData = transactionData,
|
||||
txFee = fee,
|
||||
tokenFeeStatus = tokenForFeeStatus,
|
||||
)
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(
|
||||
userWallet.walletId,
|
||||
tokenForFeeStatus.currency.network,
|
||||
) ?: error("WalletManager not found for network ${tokenForFeeStatus.currency.network.id}")
|
||||
|
||||
val chainId =
|
||||
blockchain.getChainId() ?: error("ChainId not found for blockchain ${blockchain.name}")
|
||||
val gaslessDataProvider = walletManager as? EthereumGaslessDataProvider ?: error(
|
||||
"WalletManager for network ${tokenForFeeStatus.currency.network.id} " +
|
||||
"does not support gasless transactions",
|
||||
)
|
||||
|
||||
val eip712Data = Eip712TypedDataBuilder.build(
|
||||
gaslessTransaction = gaslessTransactionData,
|
||||
chainId = chainId,
|
||||
verifyingContract = transactionData.sourceAddress,
|
||||
)
|
||||
val gaslessContractNonce = getContractNonce(gaslessDataProvider, transactionData.sourceAddress)
|
||||
|
||||
val eip712HashToSign = EthereumUtils.makeTypedDataHash(eip712Data)
|
||||
},
|
||||
catch = {
|
||||
raise(SendTransactionError.DataError(it.message))
|
||||
},
|
||||
)
|
||||
val gaslessTransactionData = createGaslessTransactionData(
|
||||
transactionData = transactionData,
|
||||
txFee = fee,
|
||||
tokenFeeStatus = tokenForFeeStatus,
|
||||
nonce = gaslessContractNonce,
|
||||
)
|
||||
|
||||
val chainId = gaslessTransactionRepository.getChainIdForNetwork(tokenForFeeStatus.currency.network)
|
||||
|
||||
return GaslessContext(
|
||||
walletManager = walletManager,
|
||||
gaslessDataProvider = gaslessDataProvider,
|
||||
tokenForFeeStatus = tokenForFeeStatus,
|
||||
gaslessTransactionData = gaslessTransactionData,
|
||||
chainId = chainId,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets contract nonce with fallback to zero on failure.
|
||||
*/
|
||||
private suspend fun getContractNonce(
|
||||
gaslessDataProvider: EthereumGaslessDataProvider,
|
||||
userAddress: String,
|
||||
): BigInteger {
|
||||
return when (val nonceResult = gaslessDataProvider.getGaslessContractNonce(userAddress)) {
|
||||
is Result.Failure -> BigInteger.ZERO
|
||||
is Result.Success -> nonceResult.data
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createGaslessTransactionData(
|
||||
userWalletId: UserWalletId,
|
||||
/**
|
||||
* Signs gasless transaction and EIP-7702 authorization.
|
||||
* Returns prepared signatures and authorization data.
|
||||
*/
|
||||
private suspend fun signGaslessTransactionByUser(
|
||||
userWallet: UserWallet,
|
||||
context: GaslessContext,
|
||||
transactionData: TransactionData.Uncompiled,
|
||||
): SignedGaslessData {
|
||||
val eip712Data = Eip712TypedDataBuilder.build(
|
||||
gaslessTransaction = context.gaslessTransactionData,
|
||||
chainId = context.chainId,
|
||||
verifyingContract = transactionData.sourceAddress,
|
||||
)
|
||||
|
||||
val eip712HashToSign = EthereumUtils.makeTypedDataHash(eip712Data)
|
||||
val eip7702Data = getEIP7702DataForGasless(context.gaslessDataProvider)
|
||||
|
||||
val signedHashes = signHashes(
|
||||
userWallet = userWallet,
|
||||
walletManager = context.walletManager,
|
||||
hashesToOperate = HashesToOperate(
|
||||
eip712Hash = eip712HashToSign,
|
||||
eip7702Hash = eip7702Data.data,
|
||||
),
|
||||
)
|
||||
|
||||
val decompressedPublicKey = context.walletManager
|
||||
.wallet.publicKey.blockchainKey
|
||||
.toDecompressedPublicKey()
|
||||
|
||||
val preparedEip712Hash = UnmarshalHelper.unmarshalSignatureExtended(
|
||||
signature = signedHashes.eip712Hash,
|
||||
hash = eip712HashToSign,
|
||||
publicKey = decompressedPublicKey,
|
||||
).asRSVLegacyEVM().toHexString().formatHex().lowercase()
|
||||
|
||||
val extendedEip7702Data = UnmarshalHelper.unmarshalSignatureExtended(
|
||||
signature = signedHashes.eip7702Hash,
|
||||
hash = eip7702Data.data,
|
||||
publicKey = decompressedPublicKey,
|
||||
)
|
||||
|
||||
val eip7702Auth = Eip7702Authorization(
|
||||
chainId = context.chainId,
|
||||
address = transactionData.sourceAddress,
|
||||
nonce = eip7702Data.nonce,
|
||||
yParity = extendedEip7702Data.recId,
|
||||
r = extendedEip7702Data.r.toFormattedHex(),
|
||||
s = extendedEip7702Data.s.toFormattedHex(),
|
||||
)
|
||||
|
||||
return SignedGaslessData(
|
||||
eip712Signature = preparedEip712Hash,
|
||||
eip7702Auth = eip7702Auth,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends gasless transaction to the service.
|
||||
*/
|
||||
private suspend fun signTransactionOnBackend(
|
||||
context: GaslessContext,
|
||||
signedData: SignedGaslessData,
|
||||
transactionData: TransactionData.Uncompiled,
|
||||
): GaslessSignedTransactionResult {
|
||||
return gaslessTransactionRepository.signGaslessTransaction(
|
||||
network = context.tokenForFeeStatus.currency.network,
|
||||
gaslessTransactionData = context.gaslessTransactionData,
|
||||
signature = signedData.eip712Signature,
|
||||
userAddress = transactionData.sourceAddress,
|
||||
eip7702Auth = signedData.eip7702Auth,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UnusedParameter")
|
||||
private fun broadcastSignedTransaction(remoteSignedData: GaslessSignedTransactionResult) {
|
||||
TODO("Will be implemented later")
|
||||
}
|
||||
|
||||
private suspend fun signHashes(
|
||||
userWallet: UserWallet,
|
||||
walletManager: WalletManager,
|
||||
hashesToOperate: HashesToOperate,
|
||||
): HashesToOperate {
|
||||
val signer = getSigner(userWallet)
|
||||
val hashesToSign = listOf(hashesToOperate.eip712Hash, hashesToOperate.eip7702Hash)
|
||||
|
||||
return when (val signerResult = signer.sign(hashesToSign, walletManager.wallet.publicKey)) {
|
||||
is CompletionResult.Failure -> error("Signing failed: ${signerResult.error.message ?: "sdk error"}")
|
||||
is CompletionResult.Success -> {
|
||||
val signatures = signerResult.data
|
||||
require(signatures.size == 2) { "Expected 2 signatures, got ${signatures.size}" }
|
||||
HashesToOperate(
|
||||
eip712Hash = signatures[0],
|
||||
eip7702Hash = signatures[1],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSigner(userWallet: UserWallet): TransactionSigner {
|
||||
return when (userWallet) {
|
||||
is UserWallet.Cold -> {
|
||||
val card = userWallet.scanResponse.card
|
||||
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins
|
||||
|
||||
cardSdkConfigRepository.getCommonSigner(
|
||||
cardId = card.cardId.takeIf { isCardNotBackedUp },
|
||||
twinKey = TwinKey.getOrNull(scanResponse = userWallet.scanResponse),
|
||||
)
|
||||
}
|
||||
is UserWallet.Hot -> getHotWalletSigner(userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createGaslessTransactionData(
|
||||
transactionData: TransactionData.Uncompiled,
|
||||
txFee: TransactionFeeExtended,
|
||||
tokenFeeStatus: CryptoCurrencyStatus,
|
||||
nonce: BigInteger,
|
||||
): GaslessTransactionData {
|
||||
val bigIntegerAmount =
|
||||
transactionData.amount.value?.movePointRight(transactionData.amount.decimals)?.toBigInteger()
|
||||
?: error("Amount value is null")
|
||||
val txData = (transactionData.extras as? EthereumTransactionExtras)?.callData ?: error("Call data required")
|
||||
|
||||
val transaction = GaslessTransactionData.Transaction(
|
||||
to = getDestinationAddress(transactionData),
|
||||
value = bigIntegerAmount,
|
||||
data = txData.data,
|
||||
)
|
||||
|
||||
val tokenForFee = tokenFeeStatus.currency as? CryptoCurrency.Token
|
||||
?: error("only CryptoCurrency.Token supported for fee")
|
||||
|
||||
val txFeeInTokenCurrency = txFee.transactionFee.normal as? Fee.Ethereum.TokenCurrency ?: error(
|
||||
"only Fee.Ethereum.TokenCurrency supported for gasless fee",
|
||||
)
|
||||
val fee = GaslessTransactionData.Fee(
|
||||
feeToken = tokenForFee.contractAddress,
|
||||
maxTokenFee = txFeeInTokenCurrency.gasLimit,
|
||||
coinPriceInToken = txFeeInTokenCurrency.coinPriceInToken,
|
||||
feeTransferGasLimit = txFeeInTokenCurrency.feeTransferGasLimit,
|
||||
baseGas = txFeeInTokenCurrency.baseGas,
|
||||
)
|
||||
|
||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(userWalletId, tokenForFee.network)
|
||||
?: error("WalletManager not found for network ${tokenForFee.network.id}")
|
||||
val gaslessDataProvider = walletManager as? EthereumGaslessDataProvider ?: error(
|
||||
"WalletManager for network ${tokenForFee.network.id} does not support gasless transactions",
|
||||
)
|
||||
|
||||
val nonceResult = gaslessDataProvider.getGaslessContractNonce(
|
||||
userAddress = transactionData.sourceAddress,
|
||||
)
|
||||
val nonce = when (nonceResult) {
|
||||
is com.tangem.blockchain.extensions.Result.Failure -> BigInteger.ZERO
|
||||
is com.tangem.blockchain.extensions.Result.Success -> nonceResult.data
|
||||
}
|
||||
val transaction = buildTransaction(transactionData)
|
||||
val fee = buildFee(txFee, tokenFeeStatus)
|
||||
|
||||
return GaslessTransactionData(
|
||||
transaction = transaction,
|
||||
|
|
@ -120,6 +259,50 @@ class CreateAndSendGaslessTransactionUseCase(
|
|||
)
|
||||
}
|
||||
|
||||
private fun buildTransaction(transactionData: TransactionData.Uncompiled): GaslessTransactionData.Transaction {
|
||||
val amount = transactionData.amount.value
|
||||
?.movePointRight(transactionData.amount.decimals)
|
||||
?.toBigInteger()
|
||||
?: error("Transaction amount value is null")
|
||||
|
||||
val callData = (transactionData.extras as? EthereumTransactionExtras)?.callData
|
||||
?: error("Ethereum call data is required")
|
||||
|
||||
return GaslessTransactionData.Transaction(
|
||||
to = getDestinationAddress(transactionData),
|
||||
value = amount,
|
||||
data = callData.data,
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildFee(
|
||||
txFee: TransactionFeeExtended,
|
||||
tokenFeeStatus: CryptoCurrencyStatus,
|
||||
): GaslessTransactionData.Fee {
|
||||
val tokenForFee = tokenFeeStatus.currency as? CryptoCurrency.Token
|
||||
?: error("Fee currency must be a token")
|
||||
|
||||
val feeInTokenCurrency = txFee.transactionFee.normal as? Fee.Ethereum.TokenCurrency
|
||||
?: error("Fee must be in token currency")
|
||||
|
||||
return GaslessTransactionData.Fee(
|
||||
feeToken = tokenForFee.contractAddress,
|
||||
maxTokenFee = feeInTokenCurrency.gasLimit,
|
||||
coinPriceInToken = feeInTokenCurrency.coinPriceInToken,
|
||||
feeTransferGasLimit = feeInTokenCurrency.feeTransferGasLimit,
|
||||
baseGas = feeInTokenCurrency.baseGas,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getEIP7702DataForGasless(
|
||||
gaslessDataProvider: EthereumGaslessDataProvider,
|
||||
): EIP7702AuthorizationData {
|
||||
return when (val dataResult = gaslessDataProvider.prepareEIP7702AuthorizationData()) {
|
||||
is Result.Failure -> throw dataResult.error
|
||||
is Result.Success -> dataResult.data
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDestinationAddress(txData: TransactionData.Uncompiled): String {
|
||||
val ethereumCallData = (txData.extras as? EthereumTransactionExtras)?.callData
|
||||
return if (ethereumCallData is EthereumYieldSupplySendCallData) {
|
||||
|
|
@ -128,4 +311,52 @@ class CreateAndSendGaslessTransactionUseCase(
|
|||
txData.destinationAddress
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Context containing all prepared data for gasless transaction.
|
||||
*/
|
||||
private data class GaslessContext(
|
||||
val walletManager: WalletManager,
|
||||
val gaslessDataProvider: EthereumGaslessDataProvider,
|
||||
val tokenForFeeStatus: CryptoCurrencyStatus,
|
||||
val gaslessTransactionData: GaslessTransactionData,
|
||||
val chainId: Int,
|
||||
)
|
||||
|
||||
/**
|
||||
* Signed data ready for submission.
|
||||
*/
|
||||
private data class SignedGaslessData(
|
||||
val eip712Signature: String,
|
||||
val eip7702Auth: Eip7702Authorization,
|
||||
)
|
||||
|
||||
private data class HashesToOperate(
|
||||
val eip712Hash: ByteArray,
|
||||
val eip7702Hash: ByteArray,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as HashesToOperate
|
||||
|
||||
if (!eip712Hash.contentEquals(other.eip712Hash)) return false
|
||||
if (!eip7702Hash.contentEquals(other.eip7702Hash)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = eip712Hash.contentHashCode()
|
||||
result = 31 * result + eip7702Hash.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
fun BigInteger.toFormattedHex(): String {
|
||||
return toByteArray().removeLeadingZero().toHexString().formatHex()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1346"
|
||||
tangemBlockchainSdk = "develop-1352"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-573"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue