Skip to content

Commit

Permalink
AND-4561 Implement "approve" in blockchain sdk
Browse files Browse the repository at this point in the history
Signed-off-by: Maqsudjon Ismoilov <[email protected]>
  • Loading branch information
iMaks99 committed Sep 22, 2023
1 parent 65275fc commit 7ae20b2
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.kethereum.model.Address
import org.kethereum.model.PublicKey
import org.kethereum.model.SignatureData
import org.kethereum.model.createTransactionWithDefaults
import org.komputing.khex.extensions.toHexString
import java.math.BigDecimal
import java.math.BigInteger

Expand All @@ -32,6 +33,7 @@ class EthereumUtils {
private val tokenTransferFromSignature = "transferFrom(address,address,uint256)".toByteArray().toKeccak().copyOf(4)

private const val HEX_PREFIX = "0x"
private const val HEX_F = "f"

// ERC-20 standard defines balanceOf function as returning uint256. Don't accept anything else.
private const val UInt256Size = 32
Expand Down Expand Up @@ -295,7 +297,6 @@ class EthereumUtils {
return CompiledEthereumTransaction(transaction, hash)
}


fun buildSetWalletToSign(
processorContractAddress: String,
cardAddress: String,
Expand Down Expand Up @@ -333,7 +334,6 @@ class EthereumUtils {
return CompiledEthereumTransaction(transaction, hash)
}


fun buildProcessToSign(
processorContractAddress: String,
processorAddress: String,
Expand Down Expand Up @@ -428,6 +428,12 @@ class EthereumUtils {
return CompiledEthereumTransaction(transaction, hash)
}

fun createErc20ApproveDataHex(spender: String, amount: Amount?): String =
createErc20ApproveData(
spender = spender,
amount = amount?.value?.movePointRight(amount.decimals)?.toBigInteger()
).toHexString()

private fun createErc20TransferData(recipient: String, amount: BigInteger) =
tokenTransferSignature.toByteArray() +
recipient.substring(2).hexToBytes().toFixedLengthByteArray(32) +
Expand All @@ -438,16 +444,10 @@ class EthereumUtils {
recepient, amount.value!!.movePointRight(amount.decimals).toBigInteger()
)

private fun createErc20ApproveData(spender: String, amount: BigInteger) =
private fun createErc20ApproveData(spender: String, amount: BigInteger?): ByteArray =
tokenApproveSignature +
spender.substring(2).hexToBytes().toFixedLengthByteArray(32) +
amount.toBytesPadded(32)

internal fun createErc20ApproveData(spender: String, amount: Amount) =
createErc20ApproveData(
spender = spender,
amount = amount.value!!.movePointRight(amount.decimals).toBigInteger()
)
(amount?.toBytesPadded(32) ?: HEX_F.repeat(64).hexToBytes())

private fun createSetSpendLimitData(cardAddress: String, amount: BigInteger) =
setSpendLimitSignature +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import org.kethereum.extensions.toHexString
import org.kethereum.keccakshortcut.keccak
import org.komputing.khex.extensions.toHexString
import java.math.BigDecimal
import java.math.BigInteger

Expand All @@ -28,7 +29,8 @@ open class EthereumWalletManager(
TransactionSender,
SignatureCountValidator,
TokenFinder,
EthereumGasLoader {
EthereumGasLoader,
Approver {

// move to constructor later
protected val feesCalculator = EthereumFeesCalculator()
Expand Down Expand Up @@ -216,6 +218,18 @@ open class EthereumWalletManager(
return getGasLimitInternal(amount, destination, data)
}

override suspend fun getAllowance(
spenderAddress: String,
token: Token,
): Result<BigDecimal> {
return networkProvider.getAllowance(wallet.address, token, spenderAddress)
}

override fun getApproveData(
spenderAddress: String,
value: Amount?,
) = EthereumUtils.createErc20ApproveDataHex(spenderAddress, value)

private suspend fun getGasLimitInternal(
amount: Amount,
destination: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.tangem.blockchain.blockchains.ethereum.network

import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.Companion.toKeccak
import com.tangem.blockchain.common.NetworkProvider
import com.tangem.blockchain.common.toBlockchainSdkError
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.retryIO
import com.tangem.blockchain.network.createRetrofitInstance
import com.tangem.common.extensions.toHexString
import org.komputing.khex.extensions.toHexString
import java.math.BigDecimal

class EthereumJsonRpcProvider(
Expand Down Expand Up @@ -97,7 +98,12 @@ class EthereumJsonRpcProvider(
) = EthCallObject(
to = contractAddress,
//5c9b5c6313a3746a1246d07bbedc0292da99f8e2000000000000000000000000e4c4693526e4e3a26f36311d3f80a193b2bae906
data = "0xdd62ed3e000000000000000000000000" + ownerAddress.substring(2) + "000000000000000000000000" + spenderAddress.substring(2)
data = buildString {
append(tokenAllowanceSignature)
append(ownerAddress.substring(2))
append(CALL_DATA_SEPARATOR)
append(spenderAddress.substring(2))
}
)

private fun createProcessCallObject(
Expand Down Expand Up @@ -133,6 +139,11 @@ class EthereumJsonRpcProvider(
Result.Failure(exception.toBlockchainSdkError())
}
}

companion object {
private val tokenAllowanceSignature = "allowance(address,address)".toByteArray().toKeccak().copyOf(4)
private const val CALL_DATA_SEPARATOR = "000000000000000000000000"
}
}

data class EthereumTokenBalanceRequestData(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tangem.blockchain.blockchains.ethereum.network

import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.NetworkProvider
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.TransactionData
Expand All @@ -12,7 +11,7 @@ import java.math.BigInteger

interface EthereumNetworkProvider: NetworkProvider {
suspend fun getInfo(address: String, tokens: Set<Token>): Result<EthereumInfoResponse>
suspend fun getAllowance(ownerAddress: String, token: Token, spenderAddress: String): Result<Amount>
suspend fun getAllowance(ownerAddress: String, token: Token, spenderAddress: String): Result<BigDecimal>
suspend fun sendTransaction(transaction: String): SimpleResult
suspend fun getSignatureCount(address: String): Result<Int>
suspend fun findErc20Tokens(address: String): Result<List<BlockchairToken>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ class EthereumNetworkService(
}
}

override suspend fun getAllowance(ownerAddress: String, token: Token, spenderAddress: String): Result<Amount> {
override suspend fun getAllowance(ownerAddress: String, token: Token, spenderAddress: String): Result<BigDecimal> {
return try {
val requestData = EthereumTokenAllowanceRequestData(ownerAddress, token.contractAddress, spenderAddress)
val amountValue = multiJsonRpcProvider.performRequest(
request = EthereumJsonRpcProvider::getTokenAllowance,
data = requestData
).extractResult().parseAmount(token.decimals)

Result.Success(Amount(token, amountValue))
Result.Success(amountValue)
} catch (exception: Exception) {
Result.Failure(exception.toBlockchainSdkError())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,15 @@ interface SignatureCountValidator {
interface TokenFinder {
suspend fun findTokens(): Result<List<Token>>
}

interface Approver {
suspend fun getAllowance(
spenderAddress: String,
token: Token,
): Result<BigDecimal>

fun getApproveData(
spenderAddress: String,
value: Amount? = null,
): String
}

0 comments on commit 7ae20b2

Please sign in to comment.