Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOS-7662 Refactor Cosmos stakekit builder #808

Merged
merged 2 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading