Skip to content

Commit

Permalink
IOS-7628 Move staking-related logic from txbuilder into helper
Browse files Browse the repository at this point in the history
  • Loading branch information
fedorov-d committed Aug 16, 2024
1 parent 39a211d commit 8bb8e2a
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 92 deletions.
4 changes: 4 additions & 0 deletions BlockchainSdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
0A158C022B74E44D0004DC23 /* BlockBookResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A158C002B74E44D0004DC23 /* BlockBookResponses.swift */; };
0A158C052B74E4680004DC23 /* BitcoinCashNowNodesNetworkProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A158C042B74E4680004DC23 /* BitcoinCashNowNodesNetworkProvider.swift */; };
0A3BF8EA2C69F5D900163492 /* CosmosProtoMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3BF8E92C69F5D900163492 /* CosmosProtoMessage.swift */; };
0A3FDED52C6F6A5A0023B717 /* EthereumStakeKitTransactionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3FDED42C6F6A5A0023B717 /* EthereumStakeKitTransactionHelper.swift */; };
0A54FF452BB475B9000D293D /* TangemNetworkLoggerPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A54FF442BB475B9000D293D /* TangemNetworkLoggerPlugin.swift */; };
0A6272652BD96736003B2F4D /* SS58.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6272642BD96736003B2F4D /* SS58.swift */; };
0A6CCDB62C6E54B600018D42 /* EthereumCompiledTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6CCDB52C6E54B600018D42 /* EthereumCompiledTransaction.swift */; };
Expand Down Expand Up @@ -915,6 +916,7 @@
0A158C002B74E44D0004DC23 /* BlockBookResponses.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockBookResponses.swift; sourceTree = "<group>"; };
0A158C042B74E4680004DC23 /* BitcoinCashNowNodesNetworkProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoinCashNowNodesNetworkProvider.swift; sourceTree = "<group>"; };
0A3BF8E92C69F5D900163492 /* CosmosProtoMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CosmosProtoMessage.swift; sourceTree = "<group>"; };
0A3FDED42C6F6A5A0023B717 /* EthereumStakeKitTransactionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EthereumStakeKitTransactionHelper.swift; sourceTree = "<group>"; };
0A54FF442BB475B9000D293D /* TangemNetworkLoggerPlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TangemNetworkLoggerPlugin.swift; sourceTree = "<group>"; };
0A6272642BD96736003B2F4D /* SS58.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SS58.swift; sourceTree = "<group>"; };
0A6CCDB52C6E54B600018D42 /* EthereumCompiledTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthereumCompiledTransaction.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2536,6 +2538,7 @@
2DDE5B9B29C4F8D200A5B708 /* EthereumWalletAssembly.swift */,
EF9F9085299F88D1006F638F /* EthereumWalletManager.swift */,
EFF607C62BD000D000C37210 /* EthereumAddressService.swift */,
0A3FDED42C6F6A5A0023B717 /* EthereumStakeKitTransactionHelper.swift */,
EF2D9D952BC3F6770055C485 /* Interfaces */,
EF2D9D982BC3F6770055C485 /* Models */,
EF2D9D912BC3F6770055C485 /* Network */,
Expand Down Expand Up @@ -5301,6 +5304,7 @@
DA82433D27A2B06500CFC2C0 /* PolkadotJsonRpcProvider.swift in Sources */,
EF57BEDA2A1E625400C2A493 /* DerivationConfigV2.swift in Sources */,
EF1339BA2AB4B38000B78BA3 /* ApproveERC20TokenMethod.swift in Sources */,
0A3FDED52C6F6A5A0023B717 /* EthereumStakeKitTransactionHelper.swift in Sources */,
B6DC28482B62BB580070F1D2 /* HederaRESTNetworkProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// EthereumStakeKitTransactionHelper.swift
// BlockchainSdk
//
// Created by Dmitry Fedorov on 16.08.2024.
// Copyright © 2024 Tangem AG. All rights reserved.
//

import Foundation
import WalletCore
import BigInt

struct EthereumStakeKitTransactionHelper {
func prepareForSign(
_ stakingTransaction: StakeKitTransaction,
transactionBuilder: EthereumTransactionBuilder
) throws -> Data {
let input = try buildSigningInput(
stakingTransaction: stakingTransaction,
transactionBuilder: transactionBuilder
)
let preSigningOutput = try transactionBuilder.buildTxCompilerPreSigningOutput(input: input)
return preSigningOutput.dataHash
}

func prepareForSend(
stakingTransaction: StakeKitTransaction,
transactionBuilder: EthereumTransactionBuilder,
signatureInfo: SignatureInfo
) throws -> Data {
let input = try buildSigningInput(
stakingTransaction: stakingTransaction,
transactionBuilder: transactionBuilder
)
let output = try transactionBuilder.buildSigningOutput(input: input, signatureInfo: signatureInfo)
return output.encoded
}

private func buildSigningInput(
stakingTransaction: StakeKitTransaction,
transactionBuilder: EthereumTransactionBuilder
) throws -> EthereumSigningInput {
let compiledTransactionData = Data(hex: stakingTransaction.unsignedData)
let compiledTransaction = try JSONDecoder().decode(
EthereumCompiledTransaction.self,
from: compiledTransactionData
)

guard let amountValue = stakingTransaction.amount.bigUIntValue else {
throw EthereumTransactionBuilderError.invalidAmount
}

guard let gasLimit = BigUInt(compiledTransaction.gasLimit, radix: 16),
let baseFee = BigUInt(compiledTransaction.maxFeePerGas, radix: 16),
let priorityFee = BigUInt(compiledTransaction.maxPriorityFeePerGas, radix: 16) else {
throw EthereumTransactionBuilderError.feeParametersNotFound
}

return try transactionBuilder.buildSigningInput(
// TODO: refactor, 'user' field is used only in erc20Transfer, consider moving elsewhere?
destination: .contract(user: "", contract: compiledTransaction.to, value: amountValue),
fee: Fee(
stakingTransaction.fee.amount,
parameters: EthereumEIP1559FeeParameters(gasLimit: gasLimit, baseFee: baseFee, priorityFee: priorityFee)
),
parameters: EthereumTransactionParams(
data: Data(hex: compiledTransaction.data),
nonce: compiledTransaction.nonce
)
)
}
}
125 changes: 43 additions & 82 deletions BlockchainSdk/Blockchains/Ethereum/EthereumTransactionBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,12 @@ class EthereumTransactionBuilder {
let preSigningOutput = try buildTxCompilerPreSigningOutput(input: input)
return preSigningOutput.dataHash
}

func buildForSign(stakingTransaction: StakeKitTransaction) throws -> Data {
let input = try buildSigningInput(stakingTransaction: stakingTransaction)
let preSigningOutput = try buildTxCompilerPreSigningOutput(input: input)
return preSigningOutput.dataHash
}

func buildForSend(transaction: Transaction, signatureInfo: SignatureInfo) throws -> Data {
let input = try buildSigningInput(transaction: transaction)
let output = try buildSigningOutput(input: input, signatureInfo: signatureInfo)
return output.encoded
}

func buildForSend(stakingTransaction: StakeKitTransaction, signatureInfo: SignatureInfo) throws -> Data {
let input = try buildSigningInput(stakingTransaction: stakingTransaction)
let output = try buildSigningOutput(input: input, signatureInfo: signatureInfo)
return output.encoded
}
}

func buildDummyTransactionForL1(destination: String, value: String?, data: Data?, fee: Fee) throws -> Data {
let valueData = BigUInt(Data(hex: value ?? "0x0"))
Expand Down Expand Up @@ -89,76 +77,7 @@ class EthereumTransactionBuilder {
let method = TransferERC20TokenMethod(destination: destination, amount: bigUInt)
return method.data
}
}

private extension EthereumTransactionBuilder {
func buildTxCompilerPreSigningOutput(input: EthereumSigningInput) throws -> TxCompilerPreSigningOutput {
let txInputData = try input.serializedData()
let preImageHashes = TransactionCompiler.preImageHashes(coinType: coinType, txInputData: txInputData)
let preSigningOutput = try TxCompilerPreSigningOutput(serializedData: preImageHashes)

if preSigningOutput.error != .ok {
Log.debug("EthereumPreSigningOutput has a error: \(preSigningOutput.errorMessage)")
throw EthereumTransactionBuilderError.walletCoreError(message: preSigningOutput.errorMessage)
}

return preSigningOutput
}

func buildSigningInput(transaction: Transaction) throws -> EthereumSigningInput {
guard let amountValue = transaction.amount.bigUIntValue else {
throw EthereumTransactionBuilderError.invalidAmount
}

switch transaction.amount.type {
case .coin:
return try buildSigningInput(
destination: .user(user: transaction.destinationAddress, value: amountValue),
fee: transaction.fee,
parameters: transaction.params as? EthereumTransactionParams ?? .empty
)
case .token(let token):
return try buildSigningInput(
destination: .contract(
user: transaction.destinationAddress,
contract: transaction.contractAddress ?? token.contractAddress,
value: amountValue
),
fee: transaction.fee,
parameters: transaction.params as? EthereumTransactionParams ?? .empty
)
case .reserve, .feeResource:
throw BlockchainSdkError.notImplemented
}
}

func buildSigningInput(stakingTransaction: StakeKitTransaction) throws -> EthereumSigningInput {
let compiledTransactionData = Data(hex: stakingTransaction.unsignedData)
let compiledTransaction = try JSONDecoder().decode(
EthereumCompiledTransaction.self,
from: compiledTransactionData
)

guard let amountValue = stakingTransaction.amount.bigUIntValue else {
throw EthereumTransactionBuilderError.invalidAmount
}

guard let gasLimit = BigUInt(compiledTransaction.gasLimit, radix: 16),
let baseFee = BigUInt(compiledTransaction.maxFeePerGas, radix: 16),
let priorityFee = BigUInt(compiledTransaction.maxPriorityFeePerGas, radix: 16) else {
throw EthereumTransactionBuilderError.feeParametersNotFound
}

return try buildSigningInput(
destination: .user(user: compiledTransaction.to, value: amountValue),
fee: Fee(
stakingTransaction.fee.amount,
parameters: EthereumEIP1559FeeParameters(gasLimit: gasLimit, baseFee: baseFee, priorityFee: priorityFee)
),
parameters: EthereumTransactionParams(data: nil, nonce: compiledTransaction.nonce)
)
}

func buildSigningInput(destination: DestinationType, fee: Fee, parameters: EthereumTransactionParams) throws -> EthereumSigningInput {
guard let nonce = parameters.nonce, nonce >= 0 else {
throw EthereumTransactionBuilderError.invalidNonce
Expand Down Expand Up @@ -219,7 +138,20 @@ private extension EthereumTransactionBuilder {

return input
}

func buildTxCompilerPreSigningOutput(input: EthereumSigningInput) throws -> TxCompilerPreSigningOutput {
let txInputData = try input.serializedData()
let preImageHashes = TransactionCompiler.preImageHashes(coinType: coinType, txInputData: txInputData)
let preSigningOutput = try TxCompilerPreSigningOutput(serializedData: preImageHashes)

if preSigningOutput.error != .ok {
Log.debug("EthereumPreSigningOutput has a error: \(preSigningOutput.errorMessage)")
throw EthereumTransactionBuilderError.walletCoreError(message: preSigningOutput.errorMessage)
}

return preSigningOutput
}

func buildSigningOutput(input: EthereumSigningInput, signatureInfo: SignatureInfo) throws -> EthereumSigningOutput {
guard signatureInfo.signature.count == Constants.signatureSize else {
throw EthereumTransactionBuilderError.invalidSignatureCount
Expand Down Expand Up @@ -271,6 +203,35 @@ private extension EthereumTransactionBuilder {
}
}

private extension EthereumTransactionBuilder {
func buildSigningInput(transaction: Transaction) throws -> EthereumSigningInput {
guard let amountValue = transaction.amount.bigUIntValue else {
throw EthereumTransactionBuilderError.invalidAmount
}

switch transaction.amount.type {
case .coin:
return try buildSigningInput(
destination: .user(user: transaction.destinationAddress, value: amountValue),
fee: transaction.fee,
parameters: transaction.params as? EthereumTransactionParams ?? .empty
)
case .token(let token):
return try buildSigningInput(
destination: .contract(
user: transaction.destinationAddress,
contract: transaction.contractAddress ?? token.contractAddress,
value: amountValue
),
fee: transaction.fee,
parameters: transaction.params as? EthereumTransactionParams ?? .empty
)
case .reserve, .feeResource:
throw BlockchainSdkError.notImplemented
}
}
}

extension EthereumTransactionBuilder {
enum DestinationType: Hashable {
case user(user: String, value: BigUInt)
Expand Down
21 changes: 11 additions & 10 deletions BlockchainSdk/Blockchains/Ethereum/EthereumWalletManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,9 @@ extension EthereumWalletManager: StakeKitTransactionSender {
}

func signStakeKit(_ transaction: StakeKitTransaction, signer: TransactionSigner) -> AnyPublisher<String, Error> {
Result {
try txBuilder.buildForSign(stakingTransaction: transaction)
let helper = EthereumStakeKitTransactionHelper()
return Result {
try helper.prepareForSign(transaction, transactionBuilder: txBuilder)
}
.publisher
.withWeakCaptureOf(self)
Expand All @@ -439,14 +440,14 @@ extension EthereumWalletManager: StakeKitTransactionSender {
}
.withWeakCaptureOf(self)
.tryMap { walletManager, signatureInfo -> String in
try walletManager.txBuilder
.buildForSend(
stakingTransaction: transaction,
signatureInfo: signatureInfo
)
.hexString
.lowercased()
.addHexPrefix()
try helper.prepareForSend(
stakingTransaction: transaction,
transactionBuilder: walletManager.txBuilder,
signatureInfo: signatureInfo
)
.hexString
.lowercased()
.addHexPrefix()
}
.eraseToAnyPublisher()
}
Expand Down

0 comments on commit 8bb8e2a

Please sign in to comment.