Skip to content
This repository has been archived by the owner on Dec 2, 2024. It is now read-only.

Commit

Permalink
IOS-6549: Mantle: error when enter max amount (#760)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseiMuraveinik1 authored Jul 22, 2024
1 parent 6eadbf4 commit fd12511
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 7 deletions.
28 changes: 28 additions & 0 deletions BlockchainSdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,8 @@
DA198B6329E41AD800DB6EEA /* CosmosNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA198B6229E41AD800DB6EEA /* CosmosNetworkService.swift */; };
DA198B6529E44D2600DB6EEA /* CosmosChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA198B6429E44D2600DB6EEA /* CosmosChain.swift */; };
DA19F5E129DFE32600FD57A3 /* StellarNetworkProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA19F5E029DFE32600FD57A3 /* StellarNetworkProvider.swift */; };
DA1BA9A72C454AFB006F6839 /* MantleWalletManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BA9A62C454AFB006F6839 /* MantleWalletManager.swift */; };
DA1BA9A92C454C53006F6839 /* MantleWalletAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BA9A82C454C53006F6839 /* MantleWalletAssembly.swift */; };
DA1D3B7B2B57B8FB00247393 /* BigUInt+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1D3B7A2B57B8FB00247393 /* BigUInt+.swift */; };
DA2309D52C123510002A930C /* FeeResourceRestrictable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2309D42C123510002A930C /* FeeResourceRestrictable.swift */; };
DA2E770029769BF6001FF957 /* TransactionCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2E76FF29769BF5001FF957 /* TransactionCreator.swift */; };
Expand Down Expand Up @@ -542,6 +544,7 @@
DAD555292BFB4110000030E5 /* KoinosTransactionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD555282BFB4110000030E5 /* KoinosTransactionBuilder.swift */; };
DAD555382BFB463C000030E5 /* KoinosAccountNonce.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD555372BFB463C000030E5 /* KoinosAccountNonce.swift */; };
DAD5CDF72C0F3A8900DC4909 /* KoinosWalletManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5CDF62C0F3A8900DC4909 /* KoinosWalletManagerTests.swift */; };
DAD62DE62C467718008509BE /* MantleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD62DE52C467718008509BE /* MantleTests.swift */; };
DAD65BB02955F201003B3E8D /* EthereumApiKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD65BAF2955F201003B3E8D /* EthereumApiKeys.swift */; };
DAD746742C16E43800436A59 /* FeeResourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD746732C16E43800436A59 /* FeeResourceType.swift */; };
DADF5F932A1B7C6B00D58DC3 /* TWPublicKey+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF5F922A1B7C6B00D58DC3 /* TWPublicKey+.swift */; };
Expand Down Expand Up @@ -1363,6 +1366,8 @@
DA198B6229E41AD800DB6EEA /* CosmosNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CosmosNetworkService.swift; sourceTree = "<group>"; };
DA198B6429E44D2600DB6EEA /* CosmosChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CosmosChain.swift; sourceTree = "<group>"; };
DA19F5E029DFE32600FD57A3 /* StellarNetworkProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StellarNetworkProvider.swift; sourceTree = "<group>"; };
DA1BA9A62C454AFB006F6839 /* MantleWalletManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MantleWalletManager.swift; sourceTree = "<group>"; };
DA1BA9A82C454C53006F6839 /* MantleWalletAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MantleWalletAssembly.swift; sourceTree = "<group>"; };
DA1D3B7A2B57B8FB00247393 /* BigUInt+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BigUInt+.swift"; sourceTree = "<group>"; };
DA2309D42C123510002A930C /* FeeResourceRestrictable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeResourceRestrictable.swift; sourceTree = "<group>"; };
DA2E76FF29769BF5001FF957 /* TransactionCreator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionCreator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1439,6 +1444,7 @@
DAD555282BFB4110000030E5 /* KoinosTransactionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinosTransactionBuilder.swift; sourceTree = "<group>"; };
DAD555372BFB463C000030E5 /* KoinosAccountNonce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinosAccountNonce.swift; sourceTree = "<group>"; };
DAD5CDF62C0F3A8900DC4909 /* KoinosWalletManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinosWalletManagerTests.swift; sourceTree = "<group>"; };
DAD62DE52C467718008509BE /* MantleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MantleTests.swift; sourceTree = "<group>"; };
DAD65BAF2955F201003B3E8D /* EthereumApiKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumApiKeys.swift; sourceTree = "<group>"; };
DAD746732C16E43800436A59 /* FeeResourceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeResourceType.swift; sourceTree = "<group>"; };
DADB5450298BB10000491102 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2134,6 +2140,7 @@
B67C5A262B8288F600A33A68 /* Hedera */,
2DB613CE2B912CFB007CFA9B /* Radiant */,
DAE343C42BF3630A001B3F38 /* Koinos */,
DAD62DE42C4676E8008509BE /* Mantle */,
5D14E47E2397B80F00C15FC8 /* Info.plist */,
);
path = BlockchainSdkTests;
Expand Down Expand Up @@ -2173,6 +2180,7 @@
DC3550F22B584C5100A93DBA /* XDC */,
5DC7DD5C243F8375008F83F2 /* XRP */,
DACAAB4F2BE8BA8A0039ED3D /* Koinos */,
DA1BA9A52C454AEE006F6839 /* Mantle */,
);
path = Blockchains;
sourceTree = "<group>";
Expand Down Expand Up @@ -3306,6 +3314,15 @@
path = Cosmos;
sourceTree = "<group>";
};
DA1BA9A52C454AEE006F6839 /* Mantle */ = {
isa = PBXGroup;
children = (
DA1BA9A82C454C53006F6839 /* MantleWalletAssembly.swift */,
DA1BA9A62C454AFB006F6839 /* MantleWalletManager.swift */,
);
path = Mantle;
sourceTree = "<group>";
};
DA3081222817AEB800DE41F1 /* protobuf */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3467,6 +3484,14 @@
path = Models;
sourceTree = "<group>";
};
DAD62DE42C4676E8008509BE /* Mantle */ = {
isa = PBXGroup;
children = (
DAD62DE52C467718008509BE /* MantleTests.swift */,
);
path = Mantle;
sourceTree = "<group>";
};
DAE30DF7279164500056C5A3 /* Solana */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4469,6 +4494,7 @@
2D24F57B2B6A425500D502A0 /* AptosWalletAssembly.swift in Sources */,
2DDE5BB029C4F8D200A5B708 /* WalletManagerAssembly.swift in Sources */,
5D304390239A58C90066B8D9 /* BitcoinWalletManager.swift in Sources */,
DA1BA9A92C454C53006F6839 /* MantleWalletAssembly.swift in Sources */,
DC5E650F2B1650F400E81AA5 /* OP_NOPN.swift in Sources */,
B63B792D2AD850DF0055BD06 /* NEARWalletAssembly.swift in Sources */,
DC5E65232B1650F400E81AA5 /* OP_CHECKLOCKTIMEVERIFY.swift in Sources */,
Expand Down Expand Up @@ -4786,6 +4812,7 @@
EF57BEDC2A1E626700C2A493 /* DerivationConfigV3.swift in Sources */,
B62344992AF26C0F00B82306 /* BigUInt+.swift in Sources */,
DC5E652B2B1650F400E81AA5 /* OP_NOTIF.swift in Sources */,
DA1BA9A72C454AFB006F6839 /* MantleWalletManager.swift in Sources */,
5D54FE1E23E4313A009FAC2F /* LitecoinWalletManager.swift in Sources */,
2DDE5BA629C4F8D200A5B708 /* CardanoWalletAssembly.swift in Sources */,
B6F89E942BB20C600009A453 /* SubscanAPITarget.swift in Sources */,
Expand Down Expand Up @@ -5211,6 +5238,7 @@
0A0C37EA2BDBAC7F00C122F7 /* SS58Tests.swift in Sources */,
DA1D3B7B2B57B8FB00247393 /* BigUInt+.swift in Sources */,
2D535E872A0CC5FA0081EB76 /* AddressesValidationTests.swift in Sources */,
DAD62DE62C467718008509BE /* MantleTests.swift in Sources */,
B62BCBAC2B3D978F007494CF /* VeChainTests.swift in Sources */,
EF0DA78928523FAC0081092A /* LitecoinTests.swift in Sources */,
EF0DA78828523FAC0081092A /* BitcoinTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Combine
import TangemSdk
import Moya

class EthereumWalletManager: BaseManager, WalletManager {
class EthereumWalletManager: BaseManager, WalletManager, EthereumTransactionSigner {
let txBuilder: EthereumTransactionBuilder
let networkService: EthereumNetworkService
let addressConverter: EthereumAddressConverter
Expand Down Expand Up @@ -71,11 +71,8 @@ class EthereumWalletManager: BaseManager, WalletManager {
}
.eraseToAnyPublisher()
}
}

// MARK: - EthereumTransactionSigner

extension EthereumWalletManager: EthereumTransactionSigner {

// It can't be into extension because it will be overridden in the `MantleWalletManager`
/// Build and sign transaction
/// - Parameters:
/// - Returns: The hex of the raw transaction ready to be sent over the network
Expand Down
35 changes: 35 additions & 0 deletions BlockchainSdk/Blockchains/Mantle/MantleWalletAssembly.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// MantleWalletAssembly.swift
// BlockchainSdk
//
// Created by Aleksei Muraveinik on 15.07.24.
// Copyright © 2024 Tangem AG. All rights reserved.
//

import Foundation

struct MantleWalletAssembly: WalletManagerAssembly {
func make(with input: WalletManagerAssemblyInput) throws -> WalletManager {
guard let chainId = input.blockchain.chainId else {
throw EthereumWalletAssemblyError.chainIdNotFound
}

let providers = networkProviderAssembly.makeEthereumJsonRpcProviders(with: input)
let txBuilder = EthereumTransactionBuilder(chainId: chainId)
let networkService = EthereumNetworkService(
decimals: input.blockchain.decimalCount,
providers: providers,
blockcypherProvider: nil,
abiEncoder: WalletCoreABIEncoder()
)

let addressConverter = EthereumAddressConverterFactory().makeConverter(for: input.blockchain)

return MantleWalletManager(
wallet: input.wallet,
addressConverter: addressConverter,
txBuilder: txBuilder,
networkService: networkService,
allowsFeeSelection: input.blockchain.allowsFeeSelection)
}
}
80 changes: 80 additions & 0 deletions BlockchainSdk/Blockchains/Mantle/MantleWalletManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// MantleWalletManager.swift
// BlockchainSdk
//
// Created by Aleksei Muraveinik on 15.07.24.
// Copyright © 2024 Tangem AG. All rights reserved.
//

import BigInt
import Combine
import Foundation

// This is a workaround for sending a Mantle transaction.
// Unfortunately, Mantle's current implementation does not conform to our existing fee calculation rules.
// https://tangem.slack.com/archives/GMXC6PP71/p1719591856597299?thread_ts=1714215815.690169&cid=GMXC6PP71
final class MantleWalletManager: EthereumWalletManager {
override func getFee(destination: String, value: String?, data: Data?) -> AnyPublisher<[Fee], any Error> {
let blockchain = wallet.blockchain

let adjustedValue = value
.flatMap { value in
EthereumUtils.parseEthereumDecimal(value, decimalsCount: blockchain.decimalCount)
}
.flatMap { parsedValue in
Amount(
with: blockchain,
type: .coin,
value: parsedValue - (1 / blockchain.decimalValue)
)
.encodedForSend
}

return super.getFee(destination: destination, value: adjustedValue, data: data)
.withWeakCaptureOf(self)
.tryMap { walletManager, fees in
try fees.map { fee in
try walletManager.mapMantleFee(fee, gasLimitMultiplier: 1.6)
}
}
.eraseToAnyPublisher()
}

override func sign(_ transaction: Transaction, signer: any TransactionSigner) -> AnyPublisher<String, any Error> {
var transaction = transaction
do {
transaction.fee = try mapMantleFee(transaction.fee, gasLimitMultiplier: 0.7)
} catch {
return Fail(error: error).eraseToAnyPublisher()
}
return super.sign(transaction, signer: signer)
}
}

// MARK: - Private

private extension MantleWalletManager {
func mapMantleFee(_ fee: Fee, gasLimitMultiplier: Double) throws -> Fee {
let parameters: any EthereumFeeParameters = switch fee.parameters {
case let parameters as EthereumEIP1559FeeParameters:
EthereumEIP1559FeeParameters(
gasLimit: BigUInt(ceil(Double(parameters.gasLimit) * gasLimitMultiplier)),
maxFeePerGas: parameters.maxFeePerGas,
priorityFee: parameters.priorityFee
)
case let parameters as EthereumLegacyFeeParameters:
EthereumLegacyFeeParameters(
gasLimit: BigUInt(ceil(Double(parameters.gasLimit) * gasLimitMultiplier)),
gasPrice: parameters.gasPrice
)
default:
throw WalletError.failedToGetFee
}

let blockchain = wallet.blockchain
let feeValue = parameters.calculateFee(decimalValue: blockchain.decimalValue)
let amount = Amount(with: blockchain, value: feeValue)

return Fee(amount, parameters: parameters)
}
}
3 changes: 2 additions & 1 deletion BlockchainSdk/Common/Blockchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1176,7 +1176,6 @@ extension Blockchain {
.moonbeam,
.polygonZkEVM,
.moonriver,
.mantle,
.flare,
.taraxa,
.decimal,
Expand Down Expand Up @@ -1231,6 +1230,8 @@ extension Blockchain {
return BittensorWalletAssembly()
case .koinos:
return KoinosWalletAssembly()
case .mantle:
return MantleWalletAssembly()
}
}
}
37 changes: 37 additions & 0 deletions BlockchainSdkTests/Mantle/MantleTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// MantleTests.swift
// BlockchainSdkTests
//
// Created by Aleksei Muraveinik on 16.07.24.
// Copyright © 2024 Tangem AG. All rights reserved.
//

import XCTest
@testable import BlockchainSdk

final class MantleTests: XCTestCase {
private let blockchain = Blockchain.mantle(testnet: false)

func testDecodeEncodedForSend() throws {
let testCases: [Decimal] = [
try XCTUnwrap(Decimal(stringValue: "29.618123086712134")),
try XCTUnwrap(Decimal(stringValue: "0.000000000003194")),
try XCTUnwrap(Decimal(stringValue: "84.329847293749302")),
try XCTUnwrap(Decimal(stringValue: "19.287394872934987")),
try XCTUnwrap(Decimal(stringValue: "73.928374982374892")),
try XCTUnwrap(Decimal(stringValue: "1.847392874932748")),
try XCTUnwrap(Decimal(stringValue: "47.832984723984723")),
try XCTUnwrap(Decimal(stringValue: "0.0000000001234567")),
try XCTUnwrap(Decimal(stringValue: "56.392847293847298"))
]

try testCases.forEach { decimal in
let amount = Amount(with: blockchain, type: .coin, value: decimal)

let encodedForSend = try XCTUnwrap(amount.encodedForSend)
let decodedValue = EthereumUtils.parseEthereumDecimal(encodedForSend, decimalsCount: blockchain.decimalCount)

XCTAssertEqual(decodedValue, decimal)
}
}
}

0 comments on commit fd12511

Please sign in to comment.