diff --git a/app/build.gradle b/app/build.gradle index 9ce6914c60..71afea879a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,11 +21,15 @@ android { buildConfigField javaTypes.BOOLEAN, "VERBOSE_EXCEPTIONS", getKey("VERBOSE_EXCEPTIONS", "false") buildConfigField javaTypes.BOOLEAN, "ALLOW_RESTRICTED_TX", getKey("ALLOW_RESTRICTED_TX", "false") // Contracts - buildConfigField javaTypes.STRING, "SAFE_MASTER_COPY_0_0_2", asString(getKey("SAFE_MASTER_COPY_0_0_2", "0xAC6072986E985aaBE7804695EC2d8970Cf7541A2")) // Version 0.0.2-alpha (Mainnet) - buildConfigField javaTypes.STRING, "SAFE_MASTER_COPY_0_1_0", asString(getKey("SAFE_MASTER_COPY_0_1_0", "0x8942595A2dC5181Df0465AF0D7be08c8f23C93af")) // Version 0.1.0 (All networks) - buildConfigField javaTypes.STRING, "SAFE_MASTER_COPY_1_0_0", asString(getKey("SAFE_MASTER_COPY_1_0_0", "0xb6029EA3B2c51D09a50B53CA8012FeEB05bDa35A")) // Version 1.0.0 (All networks) + buildConfigField javaTypes.STRING, "SAFE_MASTER_COPY_0_0_2", asString(getKey("SAFE_MASTER_COPY_0_0_2", "0xAC6072986E985aaBE7804695EC2d8970Cf7541A2")) + // Version 0.0.2-alpha (Mainnet) + buildConfigField javaTypes.STRING, "SAFE_MASTER_COPY_0_1_0", asString(getKey("SAFE_MASTER_COPY_0_1_0", "0x8942595A2dC5181Df0465AF0D7be08c8f23C93af")) + // Version 0.1.0 (All networks) + buildConfigField javaTypes.STRING, "SAFE_MASTER_COPY_1_0_0", asString(getKey("SAFE_MASTER_COPY_1_0_0", "0xb6029EA3B2c51D09a50B53CA8012FeEB05bDa35A")) + // Version 1.0.0 (All networks) buildConfigField javaTypes.STRING, "PROXY_FACTORY_ADDRESS", asString(getKey("PROXY_FACTORY_ADDRESS", "0x12302fE9c02ff50939BaAaaf415fc226C078613C")) - buildConfigField javaTypes.STRING, "MULTI_SEND_ADDRESS", asString(getKey("MULTI_SEND_ADDRESS", "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e")) + buildConfigField javaTypes.STRING, "MULTI_SEND_OLD_ADDRESS", asString(getKey("MULTI_SEND_ADDRESS", "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e")) + buildConfigField javaTypes.STRING, "MULTI_SEND_ADDRESS", asString(getKey("MULTI_SEND_ADDRESS", "0x8D29bE29923b68abfDD21e541b9374737B49cdAD")) buildConfigField javaTypes.STRING, "ENS_REGISTRY", asString(getKey("ENS_REGISTRY", "0xe7410170f87102df0055eb195163a03b7f2bff4a")) // Safe creation params buildConfigField javaTypes.INT, "PROXY_INIT_DATA_LENGTH", getKey("PROXY_INIT_DATA_LENGTH", "3006") @@ -117,7 +121,7 @@ android { buildConfigField javaTypes.STRING, "BLOCKCHAIN_NET_URL", asString(getKey("BLOCKCHAIN_NET_URL", "https://mainnet.infura.io/v3/")) buildConfigField javaTypes.STRING, "RELAY_SERVICE_URL", asString(getKey("RELAY_SERVICE_URL", "https://safe-relay.gnosis.pm/api/")) buildConfigField javaTypes.STRING, "SAFE_CREATION_FUNDER", asString(getKey("SAFE_CREATION_FUNDER", "0x07F455F30e862E13E3E3D960762cB11c4F744d52")) - buildConfigField javaTypes.STRING, "ENS_REGISTRY", asString(getKey("MULTI_SEND_ADDRESS", "0x314159265dd8dbb310642f98f50c066173c1259b")) + buildConfigField javaTypes.STRING, "ENS_REGISTRY", asString(getKey("ENS_REGISTRY", "0x314159265dd8dbb310642f98f50c066173c1259b")) } } @@ -129,6 +133,7 @@ android { packagingOptions { exclude 'META-INF/rxjava.properties' + exclude 'META-INF/extensions.kotlin_module' exclude 'META-INF/LICENSE' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2036c9c475..0035e9ee45 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -41,6 +41,8 @@ # Okio #################################################################################################### -dontwarn okio.** +# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. +-dontwarn org.codehaus.mojo.animal_sniffer.* #################################################################################################### # Okhttp diff --git a/app/src/main/java/pm/gnosis/heimdall/data/repositories/TransactionInfoRepository.kt b/app/src/main/java/pm/gnosis/heimdall/data/repositories/TransactionInfoRepository.kt index 773ca09913..812dca1323 100644 --- a/app/src/main/java/pm/gnosis/heimdall/data/repositories/TransactionInfoRepository.kt +++ b/app/src/main/java/pm/gnosis/heimdall/data/repositories/TransactionInfoRepository.kt @@ -56,7 +56,7 @@ sealed class TransactionData : Parcelable { @Parcelize @TypeParceler - data class MultiSend(val transactions: List) : TransactionData() + data class MultiSend(val transactions: List, val contract: Solidity.Address) : TransactionData() fun addToBundle(bundle: Bundle) = bundle.let { diff --git a/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepository.kt b/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepository.kt index f210f1e2cc..cb8d24b4ce 100644 --- a/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepository.kt +++ b/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepository.kt @@ -8,8 +8,11 @@ import pm.gnosis.heimdall.GnosisSafe import pm.gnosis.heimdall.MultiSend import pm.gnosis.heimdall.data.db.ApplicationDb import pm.gnosis.heimdall.data.db.models.TransactionDescriptionDb -import pm.gnosis.heimdall.data.repositories.* +import pm.gnosis.heimdall.data.repositories.RestrictedTransactionException +import pm.gnosis.heimdall.data.repositories.TransactionData import pm.gnosis.heimdall.data.repositories.TransactionExecutionRepository.Operation +import pm.gnosis.heimdall.data.repositories.TransactionInfo +import pm.gnosis.heimdall.data.repositories.TransactionInfoRepository import pm.gnosis.heimdall.data.repositories.models.ERC20Token import pm.gnosis.heimdall.data.repositories.models.SafeTransaction import pm.gnosis.model.Solidity @@ -63,17 +66,19 @@ class DefaultTransactionInfoRepository @Inject constructor( override fun parseTransactionData(transaction: SafeTransaction): Single = Single.fromCallable { - val tx = transaction.wrapped when { - transaction.isCall() -> tx.parseCall() - isMultiSend(transaction) && isReplaceRecoveryPhrase(transaction) -> TransactionData.ReplaceRecoveryPhrase(transaction) + transaction.isCall() -> transaction.wrapped.parseCall() isMultiSend(transaction) -> parseMultiSend(transaction) - else -> - TransactionData.Generic(tx.address, tx.value?.value ?: BigInteger.ZERO, tx.data, transaction.operation) + else -> transaction.toGenericTransactionData() } } .subscribeOn(Schedulers.io()) + private fun SafeTransaction.toGenericTransactionData() = + wrapped.let { tx -> + TransactionData.Generic(tx.address, tx.value?.value ?: BigInteger.ZERO, tx.data, operation) + } + private fun SafeTransaction.isCall() = this.operation == Operation.CALL private fun Transaction.parseCall() = @@ -88,18 +93,41 @@ class DefaultTransactionInfoRepository @Inject constructor( TransactionData.Generic(address, value?.value ?: BigInteger.ZERO, data, Operation.CALL) } - // TODO: This need to be adjusted for the new MultiSend private fun isMultiSend(safeTransaction: SafeTransaction) = safeTransaction.operation == Operation.DELEGATE_CALL && - safeTransaction.wrapped.address == MULTI_SEND_LIB && safeTransaction.wrapped.data != null && safeTransaction.wrapped.data!!.isSolidityMethod(MultiSend.MultiSend.METHOD_ID) - // TODO: This need to be adjusted for the new MultiSend - private fun parseMultiSend(transaction: SafeTransaction): TransactionData.MultiSend { + private fun parseMultiSend(transaction: SafeTransaction): TransactionData { val payload = - transaction.wrapped.data?.removeSolidityMethodPrefix(MultiSend.MultiSend.METHOD_ID) ?: return TransactionData.MultiSend(emptyList()) + transaction.wrapped.data?.removeSolidityMethodPrefix(MultiSend.MultiSend.METHOD_ID) + ?: return TransactionData.MultiSend(emptyList(), transaction.wrapped.address) + + val multiSend = when (transaction.wrapped.address) { + MULTI_SEND_LIB -> nullOnThrow { parseMultiSendNew(payload) } + MULTI_SEND_OLD_LIB -> nullOnThrow { parseMultiSendOld(payload) } + else -> null + } ?: return transaction.toGenericTransactionData() + return processMultiSend(transaction, multiSend) + } + + private fun parseMultiSendNew(payload: String): TransactionData.MultiSend { + val transactions = mutableListOf() + val data = MultiSend.MultiSend.decodeArguments(payload).transactions.items.toHexString() + val reader = PayloadReader(data) + while (reader.hasAdditional(85)) { + val operation = Operation.fromInt(reader.readAsHexInt(1)) + val to = nullOnThrow { reader.read(20).asEthereumAddress() } ?: throw IllegalArgumentException("Illegal to") + val value = nullOnThrow { Wei(reader.readAsHexBigInteger(32)) } ?: throw IllegalArgumentException("Illegal value") + val dataSize = nullOnThrow { reader.readAsHexBigInteger(32) } ?: throw IllegalArgumentException("Missing data size") + val data = nullOnThrow { reader.read(dataSize.toInt()).hexToByteArray() } + transactions.add(SafeTransaction(Transaction(to, value = value, data = data?.toHex()?.addHexPrefix()), operation)) + } + + return TransactionData.MultiSend(transactions, MULTI_SEND_LIB) + } + private fun parseMultiSendOld(payload: String): TransactionData.MultiSend { val transactions = mutableListOf() val partitions = SolidityBase.PartitionData.of(payload) nullOnThrow { partitions.consume() } ?: throw IllegalArgumentException("Missing multisend data position") @@ -115,21 +143,29 @@ class DefaultTransactionInfoRepository @Inject constructor( current = nullOnThrow { partitions.consume() } } - return TransactionData.MultiSend(transactions) + return TransactionData.MultiSend(transactions, MULTI_SEND_OLD_LIB) } - private fun isReplaceRecoveryPhrase(transaction: SafeTransaction): Boolean { - val payload = transaction.wrapped.data?.removeSolidityMethodPrefix(MultiSend.MultiSend.METHOD_ID) ?: return false + private fun processMultiSend(transaction: SafeTransaction, multiSend: TransactionData.MultiSend) = + parseReplaceRecoveryPhrase(transaction, multiSend) + ?: multiSend - val noPrefix = payload.removeHexPrefix() - if (noPrefix.length.rem(PADDED_HEX_LENGTH) != 0) throw IllegalArgumentException("Data is not a multiple of $PADDED_HEX_LENGTH") - val partitions = noPrefix.chunked(PADDED_HEX_LENGTH) - if (partitions.size != 20) return false - if (partitions[3] != partitions[12]) return false + private fun parseReplaceRecoveryPhrase(transaction: SafeTransaction, multiSend: TransactionData.MultiSend): TransactionData? { + if (multiSend.transactions.size != 2) return null - if (!partitions[7].startsWith(GnosisSafe.SwapOwner.METHOD_ID)) return false - if (!partitions[16].startsWith(GnosisSafe.SwapOwner.METHOD_ID)) return false - return true + // Needs to be a valid owner swap tx + val firstOwnerSwap = multiSend.transactions[0] + if (firstOwnerSwap.operation != Operation.CALL || firstOwnerSwap.wrapped.data?.isSolidityMethod(GnosisSafe.SwapOwner.METHOD_ID) != true) + return null + + // Needs to be a valid owner swap tx + val secondOwnerSwap = multiSend.transactions[1] + if (secondOwnerSwap.operation != Operation.CALL || secondOwnerSwap.wrapped.data?.isSolidityMethod(GnosisSafe.SwapOwner.METHOD_ID) != true) + return null + + // We need to swap owners at the same Safe + if (firstOwnerSwap.wrapped.address != secondOwnerSwap.wrapped.address) return null + return TransactionData.ReplaceRecoveryPhrase(transaction) } private fun parseTokenTransfer(transaction: Transaction): TransactionData.AssetTransfer { @@ -168,8 +204,23 @@ class DefaultTransactionInfoRepository @Inject constructor( Operation.values()[operation.toInt()] ) + private class PayloadReader(private val payload: String) { + private var index = 0 + + fun read(bytes: Int) = payload.substring(index, index + bytes * 2).apply { + index += bytes * 2 + } + + fun readAsHexBigInteger(bytes: Int) = read(bytes).hexAsBigInteger() + + fun readAsHexInt(bytes: Int) = read(bytes).toInt(16) + + fun hasAdditional(bytes: Int) = (index + bytes * 2) <= payload.length + } + companion object { private val MULTI_SEND_LIB = BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!! + private val MULTI_SEND_OLD_LIB = BuildConfig.MULTI_SEND_OLD_ADDRESS.asEthereumAddress()!! // These additional costs are hardcoded in the smart contract private val SAFE_TX_BASE_COSTS = BigInteger.valueOf(32000) } diff --git a/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/WalletConnectBridgeRepository.kt b/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/WalletConnectBridgeRepository.kt index 3cc93c1d07..5cc1436af4 100644 --- a/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/WalletConnectBridgeRepository.kt +++ b/app/src/main/java/pm/gnosis/heimdall/data/repositories/impls/WalletConnectBridgeRepository.kt @@ -269,7 +269,7 @@ class WalletConnectBridgeRepository @Inject constructor( .toList() .subscribeBy( onSuccess = { - showSendTransactionNotification(peerMeta, safe, TransactionData.MultiSend(txs), call.id, sessionId) + showSendTransactionNotification(peerMeta, safe, TransactionData.MultiSend(txs, MULTI_SEND_LIB), call.id, sessionId) }, onError = { t -> rejectRequest(call.id, RejectionReason.AppError(t, MULTI_SEND_RPC)).subscribe() }) } @@ -453,6 +453,7 @@ class WalletConnectBridgeRepository @Inject constructor( companion object { private const val CHANNEL_WALLET_CONNECT_REQUESTS = "channel_wallet_connect_requests" + private val MULTI_SEND_LIB = BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!! } } diff --git a/app/src/main/java/pm/gnosis/heimdall/helpers/AddressHelper.kt b/app/src/main/java/pm/gnosis/heimdall/helpers/AddressHelper.kt index dda1acee66..4820039733 100644 --- a/app/src/main/java/pm/gnosis/heimdall/helpers/AddressHelper.kt +++ b/app/src/main/java/pm/gnosis/heimdall/helpers/AddressHelper.kt @@ -41,7 +41,7 @@ class AddressHelper @Inject constructor( } } .flatMap { - if (address == MULTI_SEND_LIB) + if (address == MULTI_SEND_LIB || address == MULTI_SEND_OLD_LIB) Single.just(addressView.context.getString(R.string.multi_send_contract)) else addressBookRepository.loadAddressBookEntry(address).map { it.name } @@ -64,5 +64,6 @@ class AddressHelper @Inject constructor( companion object { private val MULTI_SEND_LIB = BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!! + private val MULTI_SEND_OLD_LIB = BuildConfig.MULTI_SEND_OLD_ADDRESS.asEthereumAddress()!! } } diff --git a/app/src/main/java/pm/gnosis/heimdall/ui/exceptions/SimpleLocalizedException.kt b/app/src/main/java/pm/gnosis/heimdall/ui/exceptions/SimpleLocalizedException.kt index 64a394720f..83e85fa4fc 100644 --- a/app/src/main/java/pm/gnosis/heimdall/ui/exceptions/SimpleLocalizedException.kt +++ b/app/src/main/java/pm/gnosis/heimdall/ui/exceptions/SimpleLocalizedException.kt @@ -87,7 +87,7 @@ data class SimpleLocalizedException(override val message: String) : Exception(me (throwable as HttpException).let { @Suppress("ConstantConditionIf") if (BuildConfig.VERBOSE_EXCEPTIONS) { - return@add "${throwable.code()} (${throwable.message()}): ${throwable.response().errorBody()?.string()}" + return@add "${throwable.code()} (${throwable.message()}): ${throwable.response()?.errorBody()?.string()}" } when (throwable.code()) { HttpCodes.FORBIDDEN, HttpCodes.UNAUTHORIZED -> c.getString(R.string.error_not_authorized_for_action) diff --git a/app/src/main/java/pm/gnosis/heimdall/ui/safe/details/transactions/BaseTransactionViewHolder.kt b/app/src/main/java/pm/gnosis/heimdall/ui/safe/details/transactions/BaseTransactionViewHolder.kt index 15c999f9e3..db9d7c08f4 100644 --- a/app/src/main/java/pm/gnosis/heimdall/ui/safe/details/transactions/BaseTransactionViewHolder.kt +++ b/app/src/main/java/pm/gnosis/heimdall/ui/safe/details/transactions/BaseTransactionViewHolder.kt @@ -96,7 +96,7 @@ abstract class BaseTransactionViewHolder( } is TransactionData.MultiSend -> { updateViews( - address = BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, + address = data.contract, infoText = "${data.transactions.size} batched transaction", valueText = null, valueColor = R.color.blue, diff --git a/app/src/main/java/pm/gnosis/heimdall/ui/safe/helpers/RecoverSafeOwnersHelper.kt b/app/src/main/java/pm/gnosis/heimdall/ui/safe/helpers/RecoverSafeOwnersHelper.kt index f8669e020c..869efed5f2 100644 --- a/app/src/main/java/pm/gnosis/heimdall/ui/safe/helpers/RecoverSafeOwnersHelper.kt +++ b/app/src/main/java/pm/gnosis/heimdall/ui/safe/helpers/RecoverSafeOwnersHelper.kt @@ -17,6 +17,7 @@ import pm.gnosis.heimdall.data.repositories.models.SafeTransaction import pm.gnosis.heimdall.di.ApplicationContext import pm.gnosis.heimdall.ui.exceptions.SimpleLocalizedException import pm.gnosis.heimdall.ui.safe.mnemonic.InputRecoveryPhraseContract +import pm.gnosis.heimdall.ui.transactions.builder.MultiSendTransactionBuilder import pm.gnosis.mnemonic.Bip39ValidationResult import pm.gnosis.model.Solidity import pm.gnosis.model.SolidityBase @@ -223,21 +224,8 @@ class DefaultRecoverSafeOwnersHelper @Inject constructor( TransactionExecutionRepository.Operation.CALL ) else -> - SafeTransaction( - Transaction( - BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, data = MultiSend.MultiSend.encode( - Solidity.Bytes( - payloads.joinToString(separator = "") { - SolidityBase.encodeFunctionArguments( - Solidity.UInt8(BigInteger.ZERO), - safeInfo.address, - Solidity.UInt256(BigInteger.ZERO), - Solidity.Bytes(it.hexStringToByteArray()) - ) - }.hexStringToByteArray() - ) - ) - ), TransactionExecutionRepository.Operation.DELEGATE_CALL + MultiSendTransactionBuilder.build( + payloads.map { SafeTransaction(Transaction(safeInfo.address, data=it), TransactionExecutionRepository.Operation.CALL) } ) } diff --git a/app/src/main/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilders.kt b/app/src/main/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilders.kt index 34559646ad..e02bdb0a88 100644 --- a/app/src/main/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilders.kt +++ b/app/src/main/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilders.kt @@ -15,6 +15,7 @@ import pm.gnosis.models.Transaction import pm.gnosis.models.Wei import pm.gnosis.utils.asEthereumAddress import pm.gnosis.utils.hexStringToByteArray +import pm.gnosis.utils.toHex import java.math.BigInteger @@ -64,17 +65,20 @@ object MultiSendTransactionBuilder { private val MULTI_SEND_LIB = BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!! fun build(data: TransactionData.MultiSend): SafeTransaction = + build(data.transactions) + + fun build(transactions: List) = SafeTransaction( Transaction( MULTI_SEND_LIB, data = MultiSend.MultiSend.encode( Solidity.Bytes( - data.transactions.joinToString(separator = "") { - SolidityBase.encodeFunctionArguments( - Solidity.UInt8(it.operation.toInt().toBigInteger()), // Operation - it.wrapped.address, // To - Solidity.UInt256(it.wrapped.value?.value ?: BigInteger.ZERO), // Value - Solidity.Bytes(it.wrapped.data?.hexStringToByteArray() ?: byteArrayOf()) // Data - ) + transactions.joinToString(separator = "") { + val data = (it.wrapped.data?.hexStringToByteArray() ?: byteArrayOf()) + Solidity.UInt8(it.operation.toInt().toBigInteger()).encodePacked() + // Operation + it.wrapped.address.encodePacked() + // To + Solidity.UInt256(it.wrapped.value?.value ?: BigInteger.ZERO).encodePacked() + // Value + Solidity.UInt256(data.size.toBigInteger()).encodePacked() + // Data length + Solidity.Bytes(data).encodePacked() // Data }.hexStringToByteArray() ) ) diff --git a/app/src/test/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepositoryTest.kt b/app/src/test/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepositoryTest.kt index 39b3583319..4fded76cd5 100644 --- a/app/src/test/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepositoryTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/data/repositories/impls/DefaultTransactionInfoRepositoryTest.kt @@ -246,7 +246,7 @@ class DefaultTransactionInfoRepositoryTest { TEST_DATA_TRANSACTION_INFO[klass]?.let { tests -> if (tests.isEmpty()) throw IllegalStateException("Missing tests for ${klass.simpleName}") tests.forEach { - val testId = UUID.randomUUID().toString() + val testId = "$klass ${UUID.randomUUID()}" val testSubmitDate = System.currentTimeMillis() given(descriptionsDaoMock.loadDescription(testId)).willReturn( Single.just( @@ -307,12 +307,13 @@ class DefaultTransactionInfoRepositoryTest { private val TEST_ETH_AMOUNT = Wei.ether("23") private val TEST_TOKEN_ADDRESS = "0xa7e15e2e76ab469f8681b576cff168f37aa246ec".asEthereumAddress()!! private val TEST_TOKEN_AMOUNT = BigInteger("230000000000") - private val MULTI_SEND_LIB = "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e".asEthereumAddress()!! + private val MULTI_SEND_LIB = "0x8D29bE29923b68abfDD21e541b9374737B49cdAD".asEthereumAddress()!! + private val MULTI_SEND_OLD_LIB = "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e".asEthereumAddress()!! private val TOKEN_TRANSFER_DATA = ERC20Contract.Transfer.encode(TEST_ADDRESS, Solidity.UInt256(TEST_TOKEN_AMOUNT)) - private const val REPLACE_RECOVERY_PHRASE_DATA = + private const val REPLACE_RECOVERY_PHRASE_DATA_OLD_MULTISEND = "0x8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000240" + @@ -337,13 +338,35 @@ class DefaultTransactionInfoRepositoryTest { "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner "00000000000000000000000000000000000000000000000000000000" // Padding + private const val REPLACE_RECOVERY_PHRASE_DATA_NEW_MULTISEND = + "0x8d80ff0a" + // Multi send method + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000172" + + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000064" + + "e318b52b" + // Swap owner method + "000000000000000000000000000000000000000000000000000000000000000c" + // Previous Owner + "000000000000000000000000000000000000000000000000000000000000000d" + // Old Owner + "000000000000000000000000000000000000000000000000000000000000000f" + // New Owner + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000064" + + "e318b52b" + // Swap owner method + "0000000000000000000000000000000000000000000000000000000000000001" + // Previous Owner + "000000000000000000000000000000000000000000000000000000000000000a" + // Old Owner + "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner + "0000000000000000000000000000" // Padding + private const val MULTI_SEND_SWAP_OWNER_DATA = "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000c" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000d" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000f" // New Owner - private const val MULTI_SEND_1_DATA = + private const val MULTI_SEND_1_DATA_OLD = "8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000240" + @@ -355,7 +378,7 @@ class DefaultTransactionInfoRepositoryTest { MULTI_SEND_SWAP_OWNER_DATA + "00000000000000000000000000000000000000000000000000000000" // Padding - private const val MULTI_SEND_2_DATA = + private const val MULTI_SEND_2_DATA_OLD = "8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000240" + @@ -364,20 +387,56 @@ class DefaultTransactionInfoRepositoryTest { "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000164" + - MULTI_SEND_1_DATA + - "00000000000000000000000000000000000000000000000000000000"+ // Padding + MULTI_SEND_1_DATA_OLD + + "00000000000000000000000000000000000000000000000000000000" + // Padding "0000000000000000000000000000000000000000000000000000000000000000" + // Operation "000000000000000000000000c257274276a4e539741ca11b590b9447b26a8051" + // Safe address "0000000000000000000000000000000000000000000000000000000000000010" + "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000000" - private val REPLACE_RECOVERY_PHRASE_TX = + private const val MULTI_SEND_1_DATA_NEW = + "8d80ff0a" + // Multi send method + "0000000000000000000000000000000000000000000000000000000000000020" + + "00000000000000000000000000000000000000000000000000000000000000b9" + + "00" + // Operation + "A7e15e2e76Ab469F8681b576cFF168F37Aa246EC" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000064" + + MULTI_SEND_SWAP_OWNER_DATA + + "00000000000000" // Multi send padding + + private const val MULTI_SEND_2_DATA_NEW = + "8d80ff0a" + // Multi send method + "0000000000000000000000000000000000000000000000000000000000000020" + + "00000000000000000000000000000000000000000000000000000000000001ae" + + "01" + // Operation + "8D29bE29923b68abfDD21e541b9374737B49cdAD" + // MultiSend address + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000104" + + MULTI_SEND_1_DATA_NEW + + "00" + // Operation + "c257274276a4e539741ca11b590b9447b26a8051" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000010" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "000000000000000000000000000000000000" // Multi send padding + + private val REPLACE_RECOVERY_PHRASE_TX_NEW_MULTISEND = SafeTransaction( Transaction( address = MULTI_SEND_LIB, value = Wei.ZERO, - data = REPLACE_RECOVERY_PHRASE_DATA, + data = REPLACE_RECOVERY_PHRASE_DATA_NEW_MULTISEND, + nonce = BigInteger.ZERO + ), DELEGATE_CALL + ) + + private val REPLACE_RECOVERY_PHRASE_TX_OLD_MULTISEND = + SafeTransaction( + Transaction( + address = MULTI_SEND_OLD_LIB, + value = Wei.ZERO, + data = REPLACE_RECOVERY_PHRASE_DATA_OLD_MULTISEND, nonce = BigInteger.ZERO ), DELEGATE_CALL ) @@ -450,13 +509,32 @@ class DefaultTransactionInfoRepositoryTest { ), DELEGATE_CALL ), TransactionData.Generic(TEST_TOKEN_ADDRESS, BigInteger.ZERO, TOKEN_TRANSFER_DATA, DELEGATE_CALL) + ), + // Unknown multisend contracts + TestData( + transaction = SafeTransaction( + Transaction( + TEST_ADDRESS, data = "0x$MULTI_SEND_1_DATA_NEW" + ), operation = DELEGATE_CALL + ), expected = TransactionData.Generic(TEST_ADDRESS, BigInteger.ZERO, "0x$MULTI_SEND_1_DATA_NEW", DELEGATE_CALL) + ), + TestData( + transaction = SafeTransaction( + Transaction( + TEST_ADDRESS, data = "0x$MULTI_SEND_1_DATA_OLD" + ), operation = DELEGATE_CALL + ), expected = TransactionData.Generic(TEST_ADDRESS, BigInteger.ZERO, "0x$MULTI_SEND_1_DATA_OLD", DELEGATE_CALL) ) ), TransactionData.ReplaceRecoveryPhrase::class to listOf( TestData( - REPLACE_RECOVERY_PHRASE_TX, - TransactionData.ReplaceRecoveryPhrase(REPLACE_RECOVERY_PHRASE_TX) + REPLACE_RECOVERY_PHRASE_TX_OLD_MULTISEND, + TransactionData.ReplaceRecoveryPhrase(REPLACE_RECOVERY_PHRASE_TX_OLD_MULTISEND) + ), + TestData( + REPLACE_RECOVERY_PHRASE_TX_NEW_MULTISEND, + TransactionData.ReplaceRecoveryPhrase(REPLACE_RECOVERY_PHRASE_TX_NEW_MULTISEND) ) ), TransactionData.ConnectAuthenticator::class to @@ -480,18 +558,62 @@ class DefaultTransactionInfoRepositoryTest { ) ), TransactionData.MultiSend::class to + // Old multisend contract listOf( + TestData( + transaction = SafeTransaction( + Transaction( + MULTI_SEND_OLD_LIB, data = MultiSend.MultiSend.encode(Solidity.Bytes(byteArrayOf())) + ), operation = DELEGATE_CALL + ), expected = TransactionData.MultiSend(emptyList(), MULTI_SEND_OLD_LIB) + ), + TestData( + transaction = SafeTransaction( + Transaction( + MULTI_SEND_OLD_LIB, data = "0x$MULTI_SEND_1_DATA_OLD" + ), operation = DELEGATE_CALL + ), expected = TransactionData.MultiSend( + listOf( + SafeTransaction( + Transaction(TEST_SAFE, value = Wei.ZERO, data = "0x$MULTI_SEND_SWAP_OWNER_DATA"), + CALL + ) + ), + MULTI_SEND_OLD_LIB + ) + ), + TestData( + transaction = SafeTransaction( + Transaction( + MULTI_SEND_OLD_LIB, data = "0x$MULTI_SEND_2_DATA_OLD" + ), operation = DELEGATE_CALL + ), expected = TransactionData.MultiSend( + listOf( + SafeTransaction( + Transaction(MULTI_SEND_OLD_LIB, value = Wei.ZERO, data = "0x$MULTI_SEND_1_DATA_OLD".toLowerCase()), + DELEGATE_CALL + ), + + SafeTransaction( + Transaction(TEST_ADDRESS, value = Wei(BigInteger.valueOf(16)), data = "0x"), + CALL + ) + ), + MULTI_SEND_OLD_LIB + ) + ), + // New multisend contract TestData( transaction = SafeTransaction( Transaction( MULTI_SEND_LIB, data = MultiSend.MultiSend.encode(Solidity.Bytes(byteArrayOf())) ), operation = DELEGATE_CALL - ), expected = TransactionData.MultiSend(emptyList()) + ), expected = TransactionData.MultiSend(emptyList(), MULTI_SEND_LIB) ), TestData( transaction = SafeTransaction( Transaction( - MULTI_SEND_LIB, data = "0x$MULTI_SEND_1_DATA" + MULTI_SEND_LIB, data = "0x$MULTI_SEND_1_DATA_NEW" ), operation = DELEGATE_CALL ), expected = TransactionData.MultiSend( listOf( @@ -499,18 +621,19 @@ class DefaultTransactionInfoRepositoryTest { Transaction(TEST_SAFE, value = Wei.ZERO, data = "0x$MULTI_SEND_SWAP_OWNER_DATA"), CALL ) - ) + ), + MULTI_SEND_LIB ) ), TestData( transaction = SafeTransaction( Transaction( - MULTI_SEND_LIB, data = "0x$MULTI_SEND_2_DATA" + MULTI_SEND_LIB, data = "0x$MULTI_SEND_2_DATA_NEW" ), operation = DELEGATE_CALL ), expected = TransactionData.MultiSend( listOf( SafeTransaction( - Transaction(MULTI_SEND_LIB, value = Wei.ZERO, data = "0x$MULTI_SEND_1_DATA".toLowerCase()), + Transaction(MULTI_SEND_LIB, value = Wei.ZERO, data = "0x$MULTI_SEND_1_DATA_NEW".toLowerCase()), DELEGATE_CALL ), @@ -518,7 +641,8 @@ class DefaultTransactionInfoRepositoryTest { Transaction(TEST_ADDRESS, value = Wei(BigInteger.valueOf(16)), data = "0x"), CALL ) - ) + ), + MULTI_SEND_LIB ) ) ) diff --git a/app/src/test/java/pm/gnosis/heimdall/helpers/AddressHelperTest.kt b/app/src/test/java/pm/gnosis/heimdall/helpers/AddressHelperTest.kt index f23f932e4f..ae480dd6ca 100644 --- a/app/src/test/java/pm/gnosis/heimdall/helpers/AddressHelperTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/helpers/AddressHelperTest.kt @@ -92,6 +92,26 @@ class AddressHelperTest { then(addressBookRepository).shouldHaveZeroInteractions() } + @Test + fun testMultiSendOldAddress() { + given(addressView.context).willReturn(contextMock) + contextMock.mockGetString() + val testAddress = BuildConfig.MULTI_SEND_OLD_ADDRESS.asEthereumAddress()!! + + helper.populateAddressInfo(addressView, nameView, null, testAddress) + + then(imageView).shouldHaveNoMoreInteractions() + then(addressView).should().text = testAddress.asEthereumAddressChecksumString().asMiddleEllipsized(4) + then(addressView).should().setOnClickListener(MockUtils.any()) + then(addressView).should().context + then(addressView).shouldHaveNoMoreInteractions() + then(nameView).should().text = "${R.string.multi_send_contract}" + then(nameView).should().visibility = View.VISIBLE + then(nameView).shouldHaveNoMoreInteractions() + + then(addressBookRepository).shouldHaveZeroInteractions() + } + @Test fun testAddressBookAddress() { val testAddress = "0xA7e15e2e76Ab469F8681b576cFF168F37Aa246EC".asEthereumAddress()!! diff --git a/app/src/test/java/pm/gnosis/heimdall/helpers/DefaultTransactionTriggerManagerTest.kt b/app/src/test/java/pm/gnosis/heimdall/helpers/DefaultTransactionTriggerManagerTest.kt index 48c7d0c57a..cebcc8245a 100644 --- a/app/src/test/java/pm/gnosis/heimdall/helpers/DefaultTransactionTriggerManagerTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/helpers/DefaultTransactionTriggerManagerTest.kt @@ -114,7 +114,7 @@ class DefaultTransactionTriggerManagerTest { TransactionData.UpdateMasterCopy(TEST_ADDRESS) ), TransactionData.MultiSend::class to SubmittedTestData( - TransactionData.MultiSend(emptyList()) + TransactionData.MultiSend(emptyList(), TEST_ADDRESS) ) ) } diff --git a/app/src/test/java/pm/gnosis/heimdall/ui/safe/helpers/DefaultRecoverSafeOwnersHelperTest.kt b/app/src/test/java/pm/gnosis/heimdall/ui/safe/helpers/DefaultRecoverSafeOwnersHelperTest.kt index 683b270eb9..47d0118033 100644 --- a/app/src/test/java/pm/gnosis/heimdall/ui/safe/helpers/DefaultRecoverSafeOwnersHelperTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/ui/safe/helpers/DefaultRecoverSafeOwnersHelperTest.kt @@ -360,6 +360,7 @@ class DefaultRecoverSafeOwnersHelperTest { .assertValueAt(0, ViewUpdate.InputMnemonic) .assertValueAt(1, ViewUpdate.ValidMnemonic) .assertValueAt(2) { + println(it) it is ViewUpdate.RecoverData && it.safeOwner == safeOwner && it.signatures.size == 2 && @@ -429,27 +430,24 @@ class DefaultRecoverSafeOwnersHelperTest { testRecoverPayload( AccountsRepository.SafeOwner(TEST_NEW_OWNER, TEST_NEW_OWNER_KEY), TEST_NEW_EXTENSION, - "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e".asEthereumAddress()!!, // MultiSend address + "0x8D29bE29923b68abfDD21e541b9374737B49cdAD".asEthereumAddress()!!, // MultiSend address TransactionExecutionRepository.Operation.DELEGATE_CALL, "0x8d80ff0a" + "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000240" + + "0000000000000000000000000000000000000000000000000000000000000172" + // First replace transaction + "00" + + "1f81fff89bd57811983a35650296681f99c65c7e" + "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b00000000000000000000000071de9579cd3857ce70058a1ce19e3d8894f65ab9000000000000000000000000c2ac20b3bb950c087f18a458db68271325a481320000000000000000000000001e6534e09b2b0dc5ea965d0ce84ab07a4bd56b38" + - "00000000000000000000000000000000000000000000000000000000" + // Padding // Second replace transaction + "00" + + "1f81fff89bd57811983a35650296681f99c65c7e" + "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000071de9579cd3857ce70058a1ce19e3d8894f65ab900000000000000000000000031b98d14007bdee637298086988a0bbd31184523" + - "00000000000000000000000000000000000000000000000000000000" // Padding + "0000000000000000000000000000" // Multis send data padding ) } @@ -495,27 +493,24 @@ class DefaultRecoverSafeOwnersHelperTest { testRecoverPayload( AccountsRepository.SafeOwner(TEST_NEW_OWNER, TEST_NEW_OWNER_KEY), TEST_NEW_EXTENSION, - "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e".asEthereumAddress()!!, // MultiSend address + "0x8D29bE29923b68abfDD21e541b9374737B49cdAD".asEthereumAddress()!!, // MultiSend address TransactionExecutionRepository.Operation.DELEGATE_CALL, "0x8d80ff0a" + "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000240" + + "0000000000000000000000000000000000000000000000000000000000000172" + // First replace transaction + "00" + + "1f81fff89bd57811983a35650296681f99c65c7e" + "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b00000000000000000000000071de9579cd3857ce70058a1ce19e3d8894f65ab9000000000000000000000000c2ac20b3bb950c087f18a458db68271325a481320000000000000000000000001e6534e09b2b0dc5ea965d0ce84ab07a4bd56b38" + - "00000000000000000000000000000000000000000000000000000000" + // Padding // Second replace transaction + "00" + + "1f81fff89bd57811983a35650296681f99c65c7e" + "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000071de9579cd3857ce70058a1ce19e3d8894f65ab900000000000000000000000031b98d14007bdee637298086988a0bbd31184523" + - "00000000000000000000000000000000000000000000000000000000", // Padding + "0000000000000000000000000000", // Multis send data padding existingSafe = false ) } @@ -735,27 +730,24 @@ class DefaultRecoverSafeOwnersHelperTest { BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, data = "0x8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000240" + - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000172" + + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000c" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000d" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000f" + // New Owner - "00000000000000000000000000000000000000000000000000000000" + // Padding - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000b" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000c" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner - "00000000000000000000000000000000000000000000000000000000" // Padding + "0000000000000000000000000000" // Padding ), operation = TransactionExecutionRepository.Operation.DELEGATE_CALL ) assertEquals( @@ -780,27 +772,24 @@ class DefaultRecoverSafeOwnersHelperTest { BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, data = "0x8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000240" + - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000172" + + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000a" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000b" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000f" + // New Owner - "00000000000000000000000000000000000000000000000000000000" + // Padding - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "0000000000000000000000000000000000000000000000000000000000000001" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000a" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner - "00000000000000000000000000000000000000000000000000000000" // Padding + "0000000000000000000000000000" // Padding ), operation = TransactionExecutionRepository.Operation.DELEGATE_CALL ) assertEquals( @@ -825,27 +814,24 @@ class DefaultRecoverSafeOwnersHelperTest { BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, data = "0x8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000240" + - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000172" + + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000c" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000d" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000f" + // New Owner - "00000000000000000000000000000000000000000000000000000000" + // Padding - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "0000000000000000000000000000000000000000000000000000000000000001" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000a" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner - "00000000000000000000000000000000000000000000000000000000" // Padding + "0000000000000000000000000000" // Padding ), operation = TransactionExecutionRepository.Operation.DELEGATE_CALL ) assertEquals( @@ -872,26 +858,23 @@ class DefaultRecoverSafeOwnersHelperTest { BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, data = "0x8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000220" + - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000152" + + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000c" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000d" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000f" + // New Owner - "00000000000000000000000000000000000000000000000000000000" + // Padding - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000044" + "0d582f13" + // Add owner method "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner "0000000000000000000000000000000000000000000000000000000000000002" + // New threshold - "00000000000000000000000000000000000000000000000000000000" // Padding + "0000000000000000000000000000" // Padding ), operation = TransactionExecutionRepository.Operation.DELEGATE_CALL ) assertEquals( @@ -922,27 +905,24 @@ class DefaultRecoverSafeOwnersHelperTest { BuildConfig.MULTI_SEND_ADDRESS.asEthereumAddress()!!, data = "0x8d80ff0a" + // Multi send method "0000000000000000000000000000000000000000000000000000000000000020" + - "0000000000000000000000000000000000000000000000000000000000000240" + - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "0000000000000000000000000000000000000000000000000000000000000172" + + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "e318b52b" + // Swap owner method "000000000000000000000000000000000000000000000000000000000000000a" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000b" + // Old Owner "000000000000000000000000000000000000000000000000000000000000000e" + // New Owner - "00000000000000000000000000000000000000000000000000000000" + // Padding - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "0000000000000000000000001f81fff89bd57811983a35650296681f99c65c7e" + // Safe address + "00" + // Operation + "1f81fff89bd57811983a35650296681f99c65c7e" + // Safe address "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000080" + "0000000000000000000000000000000000000000000000000000000000000064" + "f8dc5dd9" + // Remove owner method "0000000000000000000000000000000000000000000000000000000000000001" + // Previous Owner "000000000000000000000000000000000000000000000000000000000000000a" + // Old Owner "0000000000000000000000000000000000000000000000000000000000000001" + // New threshold - "00000000000000000000000000000000000000000000000000000000" // Padding + "0000000000000000000000000000" // Padding ), operation = TransactionExecutionRepository.Operation.DELEGATE_CALL ) assertEquals( diff --git a/app/src/test/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilderTest.kt b/app/src/test/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilderTest.kt index c9f168fd23..aa1fb4a238 100644 --- a/app/src/test/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilderTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/ui/transactions/builder/TransactionBuilderTest.kt @@ -62,7 +62,7 @@ class TransactionBuilderTest { Transaction(MULTI_SEND_ADDRESS, data = MultiSend.MultiSend.encode(Solidity.Bytes(byteArrayOf()))), TransactionExecutionRepository.Operation.DELEGATE_CALL ), - MultiSendTransactionBuilder.build(TransactionData.MultiSend(emptyList())) + MultiSendTransactionBuilder.build(TransactionData.MultiSend(emptyList(), MULTI_SEND_ADDRESS)) ) assertEquals( @@ -72,28 +72,23 @@ class TransactionBuilderTest { Solidity.Bytes( ("" + // Tx 1 - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "000000000000000000000000a7e15e2e76ab469f8681b576cff168f37aa246ec" + // To + "00" + // Operation + "a7e15e2e76ab469f8681b576cff168f37aa246ec" + // To "0000000000000000000000000000000000000000000000000000000000000000" + // Value - "0000000000000000000000000000000000000000000000000000000000000080" + // Data position "0000000000000000000000000000000000000000000000000000000000000024" + // Data length "7de7edef" + "000000000000000000000000c257274276a4e539741ca11b590b9447b26a8051" + - "00000000000000000000000000000000000000000000000000000000" + // Data padding // Tx 2 - "0000000000000000000000000000000000000000000000000000000000000000" + // Operation - "000000000000000000000000c257274276a4e539741ca11b590b9447b26a8051" + // To + "00" + // Operation + "c257274276a4e539741ca11b590b9447b26a8051" + // To "0000000000000000000000000000000000000000000000000000000000000010" + // Value - "0000000000000000000000000000000000000000000000000000000000000080" + // Data position "0000000000000000000000000000000000000000000000000000000000000000" + // Data length // Tx 3 - "0000000000000000000000000000000000000000000000000000000000000001" + // Operation - "000000000000000000000000e74d6af1670fb6560dd61ee29eb57c7bc027ce4e" + // To + "01" + // Operation + "8D29bE29923b68abfDD21e541b9374737B49cdAD" + // To "0000000000000000000000000000000000000000000000000000000000000000" + // Value - "0000000000000000000000000000000000000000000000000000000000000080" + // Data position "0000000000000000000000000000000000000000000000000000000000000004" + // Data length - "deadbeef" + - "00000000000000000000000000000000000000000000000000000000" // Data padding + "deadbeef" ).hexStringToByteArray() ) ) @@ -112,14 +107,15 @@ class TransactionBuilderTest { Transaction(MULTI_SEND_ADDRESS, data = "0xdeadbeef"), TransactionExecutionRepository.Operation.DELEGATE_CALL ) - ) + ), + MULTI_SEND_ADDRESS ) ) ) } companion object { - private val MULTI_SEND_ADDRESS = "0xe74d6af1670fb6560dd61ee29eb57c7bc027ce4e".asEthereumAddress()!! + private val MULTI_SEND_ADDRESS = "0x8D29bE29923b68abfDD21e541b9374737B49cdAD".asEthereumAddress()!! private val TEST_ADDRESS = "0xc257274276a4e539741ca11b590b9447b26a8051".asEthereumAddress()!! private val ETHER_TOKEN = Solidity.Address(BigInteger.ZERO) private val TEST_TOKEN = "0xa7e15e2e76ab469f8681b576cff168f37aa246ec".asEthereumAddress()!! diff --git a/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/helpers/DefaultTransactionViewHolderBuilderTest.kt b/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/helpers/DefaultTransactionViewHolderBuilderTest.kt index c47d392cd1..067c3a3d7a 100644 --- a/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/helpers/DefaultTransactionViewHolderBuilderTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/helpers/DefaultTransactionViewHolderBuilderTest.kt @@ -116,7 +116,7 @@ class DefaultTransactionViewHolderBuilderTest { TransactionData.UpdateMasterCopy::class to TestData(TransactionData.UpdateMasterCopy(TEST_ADDRESS)) { it is UpdateMasterCopyViewHolder }, TransactionData.MultiSend::class to - TestData(TransactionData.MultiSend(emptyList())) { it is MultiSendViewHolder } + TestData(TransactionData.MultiSend(emptyList(), TEST_ADDRESS)) { it is MultiSendViewHolder } ) } } diff --git a/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/viewholders/MultiSendViewHolderTest.kt b/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/viewholders/MultiSendViewHolderTest.kt index 5c871a5b68..d1d1753ad0 100644 --- a/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/viewholders/MultiSendViewHolderTest.kt +++ b/app/src/test/java/pm/gnosis/heimdall/ui/transactions/view/viewholders/MultiSendViewHolderTest.kt @@ -78,7 +78,7 @@ class MultiSendViewHolderTest { @Test fun loadTransaction() { - viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList()), TEST_SAFE) + viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList(), MULTI_SEND), TEST_SAFE) val testObserver = TestObserver.create() val expectedTransaction = SafeTransaction( Transaction( @@ -94,7 +94,7 @@ class MultiSendViewHolderTest { @Test fun loadAssetChange() { - viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList()), TEST_SAFE) + viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList(), MULTI_SEND), TEST_SAFE) val testObserver = TestObserver.create>() viewHolder.loadAssetChange().subscribe(testObserver) testObserver.assertResult(None) @@ -102,7 +102,7 @@ class MultiSendViewHolderTest { @Test fun testNoInteractionWithoutView() { - viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList()), TEST_SAFE) + viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList(), MULTI_SEND), TEST_SAFE) viewHolder.start() then(addressHelperMock).shouldHaveZeroInteractions() } @@ -118,7 +118,7 @@ class MultiSendViewHolderTest { Transaction(TEST_SAFE), TransactionExecutionRepository.Operation.CALL ) - }) + }, MULTI_SEND) viewHolder = MultiSendViewHolder(addressHelperMock, data, TEST_SAFE) given(layoutInflaterMock.inflate(anyInt(), MockUtils.any(), anyBoolean())).willReturn(viewHolderViewMock) viewHolder.inflate(layoutInflaterMock, containerViewMock) @@ -156,7 +156,7 @@ class MultiSendViewHolderTest { @Test fun detach() { - viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList()), TEST_SAFE) + viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList(), MULTI_SEND), TEST_SAFE) given(viewHolderViewMock.context).willReturn(contextMock) // Set view given(layoutInflaterMock.inflate(anyInt(), MockUtils.any(), anyBoolean())).willReturn(viewHolderViewMock) @@ -191,7 +191,7 @@ class MultiSendViewHolderTest { @Test fun stop() { - viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList()), TEST_SAFE) + viewHolder = MultiSendViewHolder(addressHelperMock, TransactionData.MultiSend(emptyList(), MULTI_SEND), TEST_SAFE) given(viewHolderViewMock.context).willReturn(contextMock) // Set view given(layoutInflaterMock.inflate(anyInt(), MockUtils.any(), anyBoolean())).willReturn(viewHolderViewMock) diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 2582512e6e..84bd2e5512 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -11,12 +11,12 @@ ext { androidx_app_compat : '1.1.0', androidx_card_view : '1.0.0', androidx_constraint_layout: '1.1.3', - androidx_lifecycle : '2.2.0-rc02', + androidx_lifecycle : '2.2.0-rc03', androidx_test_ext : '1.1.0', - androidx_recycler_view : '1.0.0', - androidx_room : '2.2.1', + androidx_recycler_view : '1.1.0', + androidx_room : '2.2.2', billing : '1.0', - bivrost : 'v0.7.1', + bivrost : '0.8.0', bouncycastle : '1.61', crashlytics : '2.10.1', dagger : '2.17', @@ -24,26 +24,26 @@ ext { firebase_messaging : '20.0.1', floating_action_button : '1.6.4', geth : '1.7.1', - kethereum : '0.33', + kethereum : '0.79.5', koptional : '1.2.0', material : '1.0.0', material_progressbar : '1.4.2', moshi : '1.8.0', multidex : '2.0.1', - okhttp : '3.11.0', - okio : '1.15.0', + okhttp : '3.14.4', + okio : '2.4.1', phrase : '1.1.0', picasso : '2.71828', play_services_auth : '17.0.0', - retrofit : '2.4.0', + retrofit : '2.6.2', rxandroid : '2.1.1', rxbinding : '2.1.1', rxjava : '2.2.1', rxkotlin : '2.3.0', status_keycard : '3.0.1', - svalinn : 'v0.9.0', + svalinn : 'v0.10.0', timber : '4.7.1', - wallet_connect : '0.9.5', + wallet_connect : '0.9.6', zxing : '3.3.1', kotlinx_coroutines : '1.3.0-M2',