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

Commit fd12511

Browse files
IOS-6549: Mantle: error when enter max amount (#760)
1 parent 6eadbf4 commit fd12511

File tree

6 files changed

+185
-7
lines changed

6 files changed

+185
-7
lines changed

BlockchainSdk.xcodeproj/project.pbxproj

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@
469469
DA198B6329E41AD800DB6EEA /* CosmosNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA198B6229E41AD800DB6EEA /* CosmosNetworkService.swift */; };
470470
DA198B6529E44D2600DB6EEA /* CosmosChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA198B6429E44D2600DB6EEA /* CosmosChain.swift */; };
471471
DA19F5E129DFE32600FD57A3 /* StellarNetworkProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA19F5E029DFE32600FD57A3 /* StellarNetworkProvider.swift */; };
472+
DA1BA9A72C454AFB006F6839 /* MantleWalletManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BA9A62C454AFB006F6839 /* MantleWalletManager.swift */; };
473+
DA1BA9A92C454C53006F6839 /* MantleWalletAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BA9A82C454C53006F6839 /* MantleWalletAssembly.swift */; };
472474
DA1D3B7B2B57B8FB00247393 /* BigUInt+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1D3B7A2B57B8FB00247393 /* BigUInt+.swift */; };
473475
DA2309D52C123510002A930C /* FeeResourceRestrictable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2309D42C123510002A930C /* FeeResourceRestrictable.swift */; };
474476
DA2E770029769BF6001FF957 /* TransactionCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2E76FF29769BF5001FF957 /* TransactionCreator.swift */; };
@@ -542,6 +544,7 @@
542544
DAD555292BFB4110000030E5 /* KoinosTransactionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD555282BFB4110000030E5 /* KoinosTransactionBuilder.swift */; };
543545
DAD555382BFB463C000030E5 /* KoinosAccountNonce.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD555372BFB463C000030E5 /* KoinosAccountNonce.swift */; };
544546
DAD5CDF72C0F3A8900DC4909 /* KoinosWalletManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5CDF62C0F3A8900DC4909 /* KoinosWalletManagerTests.swift */; };
547+
DAD62DE62C467718008509BE /* MantleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD62DE52C467718008509BE /* MantleTests.swift */; };
545548
DAD65BB02955F201003B3E8D /* EthereumApiKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD65BAF2955F201003B3E8D /* EthereumApiKeys.swift */; };
546549
DAD746742C16E43800436A59 /* FeeResourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD746732C16E43800436A59 /* FeeResourceType.swift */; };
547550
DADF5F932A1B7C6B00D58DC3 /* TWPublicKey+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADF5F922A1B7C6B00D58DC3 /* TWPublicKey+.swift */; };
@@ -1363,6 +1366,8 @@
13631366
DA198B6229E41AD800DB6EEA /* CosmosNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CosmosNetworkService.swift; sourceTree = "<group>"; };
13641367
DA198B6429E44D2600DB6EEA /* CosmosChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CosmosChain.swift; sourceTree = "<group>"; };
13651368
DA19F5E029DFE32600FD57A3 /* StellarNetworkProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StellarNetworkProvider.swift; sourceTree = "<group>"; };
1369+
DA1BA9A62C454AFB006F6839 /* MantleWalletManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MantleWalletManager.swift; sourceTree = "<group>"; };
1370+
DA1BA9A82C454C53006F6839 /* MantleWalletAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MantleWalletAssembly.swift; sourceTree = "<group>"; };
13661371
DA1D3B7A2B57B8FB00247393 /* BigUInt+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BigUInt+.swift"; sourceTree = "<group>"; };
13671372
DA2309D42C123510002A930C /* FeeResourceRestrictable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeResourceRestrictable.swift; sourceTree = "<group>"; };
13681373
DA2E76FF29769BF5001FF957 /* TransactionCreator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionCreator.swift; sourceTree = "<group>"; };
@@ -1439,6 +1444,7 @@
14391444
DAD555282BFB4110000030E5 /* KoinosTransactionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinosTransactionBuilder.swift; sourceTree = "<group>"; };
14401445
DAD555372BFB463C000030E5 /* KoinosAccountNonce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinosAccountNonce.swift; sourceTree = "<group>"; };
14411446
DAD5CDF62C0F3A8900DC4909 /* KoinosWalletManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoinosWalletManagerTests.swift; sourceTree = "<group>"; };
1447+
DAD62DE52C467718008509BE /* MantleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MantleTests.swift; sourceTree = "<group>"; };
14421448
DAD65BAF2955F201003B3E8D /* EthereumApiKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumApiKeys.swift; sourceTree = "<group>"; };
14431449
DAD746732C16E43800436A59 /* FeeResourceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeeResourceType.swift; sourceTree = "<group>"; };
14441450
DADB5450298BB10000491102 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -2134,6 +2140,7 @@
21342140
B67C5A262B8288F600A33A68 /* Hedera */,
21352141
2DB613CE2B912CFB007CFA9B /* Radiant */,
21362142
DAE343C42BF3630A001B3F38 /* Koinos */,
2143+
DAD62DE42C4676E8008509BE /* Mantle */,
21372144
5D14E47E2397B80F00C15FC8 /* Info.plist */,
21382145
);
21392146
path = BlockchainSdkTests;
@@ -2173,6 +2180,7 @@
21732180
DC3550F22B584C5100A93DBA /* XDC */,
21742181
5DC7DD5C243F8375008F83F2 /* XRP */,
21752182
DACAAB4F2BE8BA8A0039ED3D /* Koinos */,
2183+
DA1BA9A52C454AEE006F6839 /* Mantle */,
21762184
);
21772185
path = Blockchains;
21782186
sourceTree = "<group>";
@@ -3306,6 +3314,15 @@
33063314
path = Cosmos;
33073315
sourceTree = "<group>";
33083316
};
3317+
DA1BA9A52C454AEE006F6839 /* Mantle */ = {
3318+
isa = PBXGroup;
3319+
children = (
3320+
DA1BA9A82C454C53006F6839 /* MantleWalletAssembly.swift */,
3321+
DA1BA9A62C454AFB006F6839 /* MantleWalletManager.swift */,
3322+
);
3323+
path = Mantle;
3324+
sourceTree = "<group>";
3325+
};
33093326
DA3081222817AEB800DE41F1 /* protobuf */ = {
33103327
isa = PBXGroup;
33113328
children = (
@@ -3467,6 +3484,14 @@
34673484
path = Models;
34683485
sourceTree = "<group>";
34693486
};
3487+
DAD62DE42C4676E8008509BE /* Mantle */ = {
3488+
isa = PBXGroup;
3489+
children = (
3490+
DAD62DE52C467718008509BE /* MantleTests.swift */,
3491+
);
3492+
path = Mantle;
3493+
sourceTree = "<group>";
3494+
};
34703495
DAE30DF7279164500056C5A3 /* Solana */ = {
34713496
isa = PBXGroup;
34723497
children = (
@@ -4469,6 +4494,7 @@
44694494
2D24F57B2B6A425500D502A0 /* AptosWalletAssembly.swift in Sources */,
44704495
2DDE5BB029C4F8D200A5B708 /* WalletManagerAssembly.swift in Sources */,
44714496
5D304390239A58C90066B8D9 /* BitcoinWalletManager.swift in Sources */,
4497+
DA1BA9A92C454C53006F6839 /* MantleWalletAssembly.swift in Sources */,
44724498
DC5E650F2B1650F400E81AA5 /* OP_NOPN.swift in Sources */,
44734499
B63B792D2AD850DF0055BD06 /* NEARWalletAssembly.swift in Sources */,
44744500
DC5E65232B1650F400E81AA5 /* OP_CHECKLOCKTIMEVERIFY.swift in Sources */,
@@ -4786,6 +4812,7 @@
47864812
EF57BEDC2A1E626700C2A493 /* DerivationConfigV3.swift in Sources */,
47874813
B62344992AF26C0F00B82306 /* BigUInt+.swift in Sources */,
47884814
DC5E652B2B1650F400E81AA5 /* OP_NOTIF.swift in Sources */,
4815+
DA1BA9A72C454AFB006F6839 /* MantleWalletManager.swift in Sources */,
47894816
5D54FE1E23E4313A009FAC2F /* LitecoinWalletManager.swift in Sources */,
47904817
2DDE5BA629C4F8D200A5B708 /* CardanoWalletAssembly.swift in Sources */,
47914818
B6F89E942BB20C600009A453 /* SubscanAPITarget.swift in Sources */,
@@ -5211,6 +5238,7 @@
52115238
0A0C37EA2BDBAC7F00C122F7 /* SS58Tests.swift in Sources */,
52125239
DA1D3B7B2B57B8FB00247393 /* BigUInt+.swift in Sources */,
52135240
2D535E872A0CC5FA0081EB76 /* AddressesValidationTests.swift in Sources */,
5241+
DAD62DE62C467718008509BE /* MantleTests.swift in Sources */,
52145242
B62BCBAC2B3D978F007494CF /* VeChainTests.swift in Sources */,
52155243
EF0DA78928523FAC0081092A /* LitecoinTests.swift in Sources */,
52165244
EF0DA78828523FAC0081092A /* BitcoinTests.swift in Sources */,

BlockchainSdk/Blockchains/Ethereum/EthereumWalletManager.swift

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Combine
1212
import TangemSdk
1313
import Moya
1414

15-
class EthereumWalletManager: BaseManager, WalletManager {
15+
class EthereumWalletManager: BaseManager, WalletManager, EthereumTransactionSigner {
1616
let txBuilder: EthereumTransactionBuilder
1717
let networkService: EthereumNetworkService
1818
let addressConverter: EthereumAddressConverter
@@ -71,11 +71,8 @@ class EthereumWalletManager: BaseManager, WalletManager {
7171
}
7272
.eraseToAnyPublisher()
7373
}
74-
}
75-
76-
// MARK: - EthereumTransactionSigner
77-
78-
extension EthereumWalletManager: EthereumTransactionSigner {
74+
75+
// It can't be into extension because it will be overridden in the `MantleWalletManager`
7976
/// Build and sign transaction
8077
/// - Parameters:
8178
/// - Returns: The hex of the raw transaction ready to be sent over the network
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// MantleWalletAssembly.swift
3+
// BlockchainSdk
4+
//
5+
// Created by Aleksei Muraveinik on 15.07.24.
6+
// Copyright © 2024 Tangem AG. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
struct MantleWalletAssembly: WalletManagerAssembly {
12+
func make(with input: WalletManagerAssemblyInput) throws -> WalletManager {
13+
guard let chainId = input.blockchain.chainId else {
14+
throw EthereumWalletAssemblyError.chainIdNotFound
15+
}
16+
17+
let providers = networkProviderAssembly.makeEthereumJsonRpcProviders(with: input)
18+
let txBuilder = EthereumTransactionBuilder(chainId: chainId)
19+
let networkService = EthereumNetworkService(
20+
decimals: input.blockchain.decimalCount,
21+
providers: providers,
22+
blockcypherProvider: nil,
23+
abiEncoder: WalletCoreABIEncoder()
24+
)
25+
26+
let addressConverter = EthereumAddressConverterFactory().makeConverter(for: input.blockchain)
27+
28+
return MantleWalletManager(
29+
wallet: input.wallet,
30+
addressConverter: addressConverter,
31+
txBuilder: txBuilder,
32+
networkService: networkService,
33+
allowsFeeSelection: input.blockchain.allowsFeeSelection)
34+
}
35+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// MantleWalletManager.swift
3+
// BlockchainSdk
4+
//
5+
// Created by Aleksei Muraveinik on 15.07.24.
6+
// Copyright © 2024 Tangem AG. All rights reserved.
7+
//
8+
9+
import BigInt
10+
import Combine
11+
import Foundation
12+
13+
// This is a workaround for sending a Mantle transaction.
14+
// Unfortunately, Mantle's current implementation does not conform to our existing fee calculation rules.
15+
// https://tangem.slack.com/archives/GMXC6PP71/p1719591856597299?thread_ts=1714215815.690169&cid=GMXC6PP71
16+
final class MantleWalletManager: EthereumWalletManager {
17+
override func getFee(destination: String, value: String?, data: Data?) -> AnyPublisher<[Fee], any Error> {
18+
let blockchain = wallet.blockchain
19+
20+
let adjustedValue = value
21+
.flatMap { value in
22+
EthereumUtils.parseEthereumDecimal(value, decimalsCount: blockchain.decimalCount)
23+
}
24+
.flatMap { parsedValue in
25+
Amount(
26+
with: blockchain,
27+
type: .coin,
28+
value: parsedValue - (1 / blockchain.decimalValue)
29+
)
30+
.encodedForSend
31+
}
32+
33+
return super.getFee(destination: destination, value: adjustedValue, data: data)
34+
.withWeakCaptureOf(self)
35+
.tryMap { walletManager, fees in
36+
try fees.map { fee in
37+
try walletManager.mapMantleFee(fee, gasLimitMultiplier: 1.6)
38+
}
39+
}
40+
.eraseToAnyPublisher()
41+
}
42+
43+
override func sign(_ transaction: Transaction, signer: any TransactionSigner) -> AnyPublisher<String, any Error> {
44+
var transaction = transaction
45+
do {
46+
transaction.fee = try mapMantleFee(transaction.fee, gasLimitMultiplier: 0.7)
47+
} catch {
48+
return Fail(error: error).eraseToAnyPublisher()
49+
}
50+
return super.sign(transaction, signer: signer)
51+
}
52+
}
53+
54+
// MARK: - Private
55+
56+
private extension MantleWalletManager {
57+
func mapMantleFee(_ fee: Fee, gasLimitMultiplier: Double) throws -> Fee {
58+
let parameters: any EthereumFeeParameters = switch fee.parameters {
59+
case let parameters as EthereumEIP1559FeeParameters:
60+
EthereumEIP1559FeeParameters(
61+
gasLimit: BigUInt(ceil(Double(parameters.gasLimit) * gasLimitMultiplier)),
62+
maxFeePerGas: parameters.maxFeePerGas,
63+
priorityFee: parameters.priorityFee
64+
)
65+
case let parameters as EthereumLegacyFeeParameters:
66+
EthereumLegacyFeeParameters(
67+
gasLimit: BigUInt(ceil(Double(parameters.gasLimit) * gasLimitMultiplier)),
68+
gasPrice: parameters.gasPrice
69+
)
70+
default:
71+
throw WalletError.failedToGetFee
72+
}
73+
74+
let blockchain = wallet.blockchain
75+
let feeValue = parameters.calculateFee(decimalValue: blockchain.decimalValue)
76+
let amount = Amount(with: blockchain, value: feeValue)
77+
78+
return Fee(amount, parameters: parameters)
79+
}
80+
}

BlockchainSdk/Common/Blockchain.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,6 @@ extension Blockchain {
11761176
.moonbeam,
11771177
.polygonZkEVM,
11781178
.moonriver,
1179-
.mantle,
11801179
.flare,
11811180
.taraxa,
11821181
.decimal,
@@ -1231,6 +1230,8 @@ extension Blockchain {
12311230
return BittensorWalletAssembly()
12321231
case .koinos:
12331232
return KoinosWalletAssembly()
1233+
case .mantle:
1234+
return MantleWalletAssembly()
12341235
}
12351236
}
12361237
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// MantleTests.swift
3+
// BlockchainSdkTests
4+
//
5+
// Created by Aleksei Muraveinik on 16.07.24.
6+
// Copyright © 2024 Tangem AG. All rights reserved.
7+
//
8+
9+
import XCTest
10+
@testable import BlockchainSdk
11+
12+
final class MantleTests: XCTestCase {
13+
private let blockchain = Blockchain.mantle(testnet: false)
14+
15+
func testDecodeEncodedForSend() throws {
16+
let testCases: [Decimal] = [
17+
try XCTUnwrap(Decimal(stringValue: "29.618123086712134")),
18+
try XCTUnwrap(Decimal(stringValue: "0.000000000003194")),
19+
try XCTUnwrap(Decimal(stringValue: "84.329847293749302")),
20+
try XCTUnwrap(Decimal(stringValue: "19.287394872934987")),
21+
try XCTUnwrap(Decimal(stringValue: "73.928374982374892")),
22+
try XCTUnwrap(Decimal(stringValue: "1.847392874932748")),
23+
try XCTUnwrap(Decimal(stringValue: "47.832984723984723")),
24+
try XCTUnwrap(Decimal(stringValue: "0.0000000001234567")),
25+
try XCTUnwrap(Decimal(stringValue: "56.392847293847298"))
26+
]
27+
28+
try testCases.forEach { decimal in
29+
let amount = Amount(with: blockchain, type: .coin, value: decimal)
30+
31+
let encodedForSend = try XCTUnwrap(amount.encodedForSend)
32+
let decodedValue = EthereumUtils.parseEthereumDecimal(encodedForSend, decimalsCount: blockchain.decimalCount)
33+
34+
XCTAssertEqual(decodedValue, decimal)
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)