Skip to content

Commit

Permalink
IOS-7662 Refactor Cosmos stakekit builder (#808)
Browse files Browse the repository at this point in the history
  • Loading branch information
tureck1y committed Aug 16, 2024
1 parent 55be4b5 commit 2b059e4
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 107 deletions.
4 changes: 4 additions & 0 deletions BlockchainSdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@
DC667F062C401B97000842D0 /* StakeKitTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC667F052C401B97000842D0 /* StakeKitTransaction.swift */; };
DC667F082C4022CF000842D0 /* SolanaStakeKitTransactionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC667F072C4022CF000842D0 /* SolanaStakeKitTransactionHelper.swift */; };
DC667F0C2C40341C000842D0 /* SolanaStakeKitTransactionHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC667F0B2C40341C000842D0 /* SolanaStakeKitTransactionHelperTests.swift */; };
DC6CB9E82C6FB11200C21B67 /* CosmosStakeKitTransactionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6CB9E72C6FB11200C21B67 /* CosmosStakeKitTransactionHelper.swift */; };
DC87B9EF2B8F4D9700C61C5A /* BitcoreProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC87B9EC2B8F4D9700C61C5A /* BitcoreProvider.swift */; };
DC87B9F02B8F4D9700C61C5A /* BitcoreResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC87B9ED2B8F4D9700C61C5A /* BitcoreResponse.swift */; };
DC87B9F12B8F4D9700C61C5A /* BitcoreTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC87B9EE2B8F4D9700C61C5A /* BitcoreTarget.swift */; };
Expand Down Expand Up @@ -1627,6 +1628,7 @@
DC667F052C401B97000842D0 /* StakeKitTransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakeKitTransaction.swift; sourceTree = "<group>"; };
DC667F072C4022CF000842D0 /* SolanaStakeKitTransactionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolanaStakeKitTransactionHelper.swift; sourceTree = "<group>"; };
DC667F0B2C40341C000842D0 /* SolanaStakeKitTransactionHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolanaStakeKitTransactionHelperTests.swift; sourceTree = "<group>"; };
DC6CB9E72C6FB11200C21B67 /* CosmosStakeKitTransactionHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CosmosStakeKitTransactionHelper.swift; sourceTree = "<group>"; };
DC87B9EC2B8F4D9700C61C5A /* BitcoreProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoreProvider.swift; sourceTree = "<group>"; };
DC87B9ED2B8F4D9700C61C5A /* BitcoreResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoreResponse.swift; sourceTree = "<group>"; };
DC87B9EE2B8F4D9700C61C5A /* BitcoreTarget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoreTarget.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3391,6 +3393,7 @@
DA67A69529E51FEA001B6799 /* CosmosWalletManager.swift */,
DC0468FB2B87CC6C00C785D1 /* OtherChains */,
0A3BF8E92C69F5D900163492 /* CosmosProtoMessage.swift */,
DC6CB9E72C6FB11200C21B67 /* CosmosStakeKitTransactionHelper.swift */,
);
path = Cosmos;
sourceTree = "<group>";
Expand Down Expand Up @@ -4692,6 +4695,7 @@
2DDEFBE82B59B44700885675 /* AlgorandProviderTarget.swift in Sources */,
B6D7131F2AEBEF750095FE6A /* NEARNetworkResult.APIError.swift in Sources */,
B633EA222B8FC5DD00F11BFF /* UTXOTransactionHistoryProvider.swift in Sources */,
DC6CB9E82C6FB11200C21B67 /* CosmosStakeKitTransactionHelper.swift in Sources */,
EF2D9E0F2BC43DA80055C485 /* EthereumNetworkProvider.swift in Sources */,
DC5E65272B1650F400E81AA5 /* OP_0.swift in Sources */,
B6BA93732AEA0E9B00F84E36 /* NEARNetworkParams.Transaction.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// CosmosStakeKitTransactionHelper.swift
// BlockchainSdk
//
// Created by Alexander Osokin on 16.08.2024.
// Copyright © 2024 Tangem AG. All rights reserved.
//

import Foundation
import WalletCore

struct CosmosStakeKitTransactionHelper {
private let builder: CosmosTransactionBuilder

init(builder: CosmosTransactionBuilder) {
self.builder = builder
}

func prepareForSign(stakingTransaction: StakeKitTransaction) throws -> Data {
let txInputData = try makeInput(stakingTransaction: stakingTransaction)
return try builder.buildForSignRaw(txInputData: txInputData)
}

func buildForSend(
stakingTransaction: StakeKitTransaction,
signature: Data
) throws -> Data {
let txInputData = try makeInput(stakingTransaction: stakingTransaction)
return try builder.buildForSendRaw(txInputData: txInputData, signature: signature)
}

private func makeInput(
stakingTransaction: StakeKitTransaction
) throws -> Data {
let stakingProtoMessage = try CosmosProtoMessage(serializedData: Data(hex: stakingTransaction.unsignedData))

let feeMessage = stakingProtoMessage.feeAndKeyContainer.feeContainer
let feeValue = feeMessage.feeAmount

guard let message = CosmosMessage.createStakeMessage(message: stakingProtoMessage.delegateContainer.delegate) else {
throw WalletError.failedToBuildTx
}

let serializedInput = try builder.serializeInput(
gas: feeMessage.gas,
feeAmount: feeValue.amount,
feeDenomiation: feeValue.denomination,
messages: [message],
memo: nil
)

return serializedInput
}
}
163 changes: 61 additions & 102 deletions BlockchainSdk/Blockchains/Cosmos/CosmosTransactionBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,16 @@ class CosmosTransactionBuilder {
// MARK: Regular transaction

func buildForSign(transaction: Transaction) throws -> Data {
let input = try makeInput(transaction: transaction, fee: transaction.fee)
let txInputData = try input.serializedData()

return try buildForSignInternal(txInputData: txInputData)
let txInputData = try makeInput(transaction: transaction, fee: transaction.fee)
return try buildForSignRaw(txInputData: txInputData)
}

func buildForSend(transaction: Transaction, signature: Data) throws -> Data {
let input = try makeInput(transaction: transaction, fee: transaction.fee)
let txInputData = try input.serializedData()

return try buildForSendInternal(txInputData: txInputData, signature: signature)
}

// MARK: Staking

func buildForSign(stakingTransaction: StakeKitTransaction) throws -> Data {
let input = try makeInput(stakingTransaction: stakingTransaction)
let txInputData = try input.serializedData()

return try buildForSignInternal(txInputData: txInputData)
let txInputData = try makeInput(transaction: transaction, fee: transaction.fee)
return try buildForSendRaw(txInputData: txInputData, signature: signature)
}

func buildForSend(
stakingTransaction: StakeKitTransaction,
signature: Data
) throws -> Data {
let input = try makeInput(stakingTransaction: stakingTransaction)
let txInputData = try input.serializedData()

return try buildForSendInternal(txInputData: txInputData, signature: signature)
}

// MARK: Private

private func buildForSignInternal(txInputData: Data) throws -> Data {
func buildForSignRaw(txInputData: Data) throws -> Data {
let preImageHashes = TransactionCompiler.preImageHashes(coinType: cosmosChain.coin, txInputData: txInputData)
let output = try TxCompilerPreSigningOutput(serializedData: preImageHashes)

Expand All @@ -81,7 +56,7 @@ class CosmosTransactionBuilder {
return output.dataHash
}

private func buildForSendInternal(txInputData: Data, signature: Data) throws -> Data {
func buildForSendRaw(txInputData: Data, signature: Data) throws -> Data {
let publicKeys = DataVector()
publicKeys.add(data: publicKey)

Expand Down Expand Up @@ -109,7 +84,47 @@ class CosmosTransactionBuilder {
return outputData
}

private func makeInput(transaction: Transaction, fee: Fee?) throws -> CosmosSigningInput {
func serializeInput(
gas: UInt64,
feeAmount: String,
feeDenomiation: String,
messages: [WalletCore.TW_Cosmos_Proto_Message],
memo: String?
) throws -> Data {
guard let accountNumber, let sequenceNumber else {
throw WalletError.failedToBuildTx
}

let fee = CosmosFee.with { fee in
fee.gas = gas
fee.amounts = [
CosmosAmount.with { amount in
amount.amount = feeAmount
amount.denom = feeDenomiation
}
]
}

let input = CosmosSigningInput.with {
$0.mode = .sync
$0.signingMode = .protobuf
$0.accountNumber = accountNumber
$0.chainID = cosmosChain.chainID
$0.sequence = sequenceNumber
$0.publicKey = publicKey
$0.messages = messages
$0.privateKey = Data(repeating: 1, count: 32)
$0.fee = fee
$0.memo = memo ?? ""
}

let serialized = try input.serializedData()
return serialized
}

// MARK: Private

private func makeInput(transaction: Transaction, fee: Fee?) throws -> Data {
let decimalValue: Decimal
switch transaction.amount.type {
case .coin:
Expand Down Expand Up @@ -161,81 +176,26 @@ class CosmosTransactionBuilder {
$0.sendCoinsMessage = sendCoinsMessage
}
}

guard
let accountNumber = self.accountNumber,
let sequenceNumber = self.sequenceNumber
else {

guard let fee, let parameters = fee.parameters as? CosmosFeeParameters else {
throw WalletError.failedToBuildTx
}
let params = transaction.params as? CosmosTransactionParams

let feeAmountInSmallestDenomination = (fee.amount.value * decimalValue).uint64Value
let feeDenomination = try feeDenomination(for: transaction.amount)
let input = CosmosSigningInput.with {
$0.mode = .sync
$0.signingMode = .protobuf;
$0.accountNumber = accountNumber
$0.chainID = cosmosChain.chainID
$0.memo = params?.memo ?? ""
$0.sequence = sequenceNumber
$0.messages = [message]
$0.publicKey = publicKey
let params = transaction.params as? CosmosTransactionParams

if let fee, let parameters = fee.parameters as? CosmosFeeParameters {
let feeAmountInSmallestDenomination = (fee.amount.value * decimalValue).uint64Value
let serializedInput = try serializeInput(
gas: parameters.gas,
feeAmount: "\(feeAmountInSmallestDenomination)",
feeDenomiation: feeDenomination,
messages: [message],
memo: params?.memo
)

$0.fee = CosmosFee.with {
$0.gas = parameters.gas
$0.amounts = [CosmosAmount.with {
$0.amount = "\(feeAmountInSmallestDenomination)"
$0.denom = feeDenomination
}]
}
}
}

return input
return serializedInput
}

private func makeInput(
stakingTransaction: StakeKitTransaction
) throws -> CosmosSigningInput {
guard let accountNumber, let sequenceNumber else {
throw WalletError.failedToBuildTx
}

let stakingProtoMessage = try CosmosProtoMessage(serializedData: Data(hex: stakingTransaction.unsignedData))

let feeMessage = stakingProtoMessage.feeAndKeyContainer.feeContainer
let feeValue = feeMessage.feeAmount

guard let message = CosmosMessage.createStakeMessage(message: stakingProtoMessage.delegateContainer.delegate) else {
throw WalletError.failedToBuildTx
}
let fee = CosmosFee.with { fee in
fee.gas = feeMessage.gas
fee.amounts = [
CosmosAmount.with { amount in
amount.amount = feeValue.amount
amount.denom = feeValue.denomination
}
]
}

let input = CosmosSigningInput.with {
$0.mode = .sync
$0.signingMode = .protobuf
$0.accountNumber = accountNumber
$0.chainID = cosmosChain.chainID
$0.sequence = sequenceNumber
$0.publicKey = publicKey
$0.messages = [message]
$0.privateKey = Data(repeating: 1, count: 32)
$0.fee = fee
}
return input
}


private func denomination(for amount: Amount) throws -> String {
switch amount.type {
case .coin:
Expand All @@ -252,7 +212,6 @@ class CosmosTransactionBuilder {
}
}


private func feeDenomination(for amount: Amount) throws -> String {
switch amount.type {
case .coin:
Expand Down
11 changes: 6 additions & 5 deletions BlockchainSdk/Blockchains/Cosmos/CosmosWalletManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,10 @@ extension CosmosWalletManager: StakeKitTransactionSender {
transaction: StakeKitTransaction,
signer: any TransactionSigner
) -> AnyPublisher<TransactionSendResult, SendTxError> {
Result {
try txBuilder.buildForSign(stakingTransaction: transaction)
let helper = CosmosStakeKitTransactionHelper(builder: txBuilder)

return Result {
try helper.prepareForSign(stakingTransaction: transaction)
}
.publisher
.withWeakCaptureOf(self)
Expand All @@ -216,9 +218,8 @@ extension CosmosWalletManager: StakeKitTransactionSender {
return try signature.unmarshal(with: manager.wallet.publicKey.blockchainKey, hash: hash).data
}
}
.withWeakCaptureOf(self)
.tryMap { manager, signature -> Data in
try manager.txBuilder.buildForSend(stakingTransaction: transaction, signature: signature)
.tryMap { signature -> Data in
try helper.buildForSend(stakingTransaction: transaction, signature: signature)
}
.withWeakCaptureOf(self)
.flatMap { manager, transaction in
Expand Down

0 comments on commit 2b059e4

Please sign in to comment.