Skip to content

Commit

Permalink
IOS-7728 Use confirmed commitment (#813)
Browse files Browse the repository at this point in the history
  • Loading branch information
tureck1y committed Aug 27, 2024
1 parent 47caf42 commit e5053b8
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 52 deletions.
91 changes: 47 additions & 44 deletions BlockchainSdk/Blockchains/Solana/SolanaNetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,40 @@ class SolanaNetworkService {
var host: String {
hostProvider.host
}

private let solanaSdk: Solana
private let blockchain: Blockchain
private let hostProvider: HostProvider

init(solanaSdk: Solana, blockchain: Blockchain, hostProvider: HostProvider) {
self.solanaSdk = solanaSdk
self.blockchain = blockchain
self.hostProvider = hostProvider
}

func getInfo(accountId: String, tokens: [Token], transactionIDs: [String]) -> AnyPublisher<SolanaAccountInfoResponse, Error> {
Publishers.Zip4(
mainAccountInfo(accountId: accountId),
tokenAccountsInfo(accountId: accountId, programId: .tokenProgramId),
tokenAccountsInfo(accountId: accountId, programId: .token2022ProgramId),
confirmedTransactions(among: transactionIDs)
)
.tryMap { [weak self] mainAccount, splTokenAccounts, token2022Accounts, confirmedTransactionIDs in
guard let self = self else {
throw WalletError.empty
}

let tokenAccounts = splTokenAccounts + token2022Accounts
return self.mapInfo(
mainAccountInfo: mainAccount,
tokenAccountsInfo: tokenAccounts,
tokens: tokens,
confirmedTransactionIDs: confirmedTransactionIDs
)
.tryMap { [weak self] mainAccount, splTokenAccounts, token2022Accounts, confirmedTransactionIDs in
guard let self = self else {
throw WalletError.empty
}
.eraseToAnyPublisher()

let tokenAccounts = splTokenAccounts + token2022Accounts
return self.mapInfo(
mainAccountInfo: mainAccount,
tokenAccountsInfo: tokenAccounts,
tokens: tokens,
confirmedTransactionIDs: confirmedTransactionIDs
)
}
.eraseToAnyPublisher()
}

func sendSol(
amount: UInt64,
computeUnitLimit: UInt32?,
Expand All @@ -64,10 +64,12 @@ class SolanaNetworkService {
allowUnfundedRecipient: true,
signer: signer
)
.eraseToAnyPublisher()
}

func sendRaw(base64serializedTransaction: String) -> AnyPublisher<TransactionID, Error> {
solanaSdk.api.sendTransaction(serializedTransaction: base64serializedTransaction)
.eraseToAnyPublisher()
}

func sendSplToken(amount: UInt64, computeUnitLimit: UInt32?, computeUnitPrice: UInt64?, sourceTokenAddress: String, destinationAddress: String, token: Token, tokenProgramId: PublicKey, signer: SolanaTransactionSigner) -> AnyPublisher<TransactionID, Error> {
Expand All @@ -83,8 +85,9 @@ class SolanaNetworkService {
allowUnfundedRecipient: true,
signer: signer
)
.eraseToAnyPublisher()
}

func getFeeForMessage(
amount: UInt64,
computeUnitLimit: UInt32?,
Expand All @@ -100,45 +103,45 @@ class SolanaNetworkService {
allowUnfundedRecipient: true,
fromPublicKey: fromPublicKey
)
.flatMap { [solanaSdk] message -> AnyPublisher<FeeForMessageResult, Error> in
.flatMap { [solanaSdk] (message, _) -> AnyPublisher<FeeForMessageResult, Error> in
solanaSdk.api.getFeeForMessage(message)
}
.map { [blockchain] in
Decimal($0.value) / blockchain.decimalValue
}
.eraseToAnyPublisher()
}

func transactionFee(numberOfSignatures: Int) -> AnyPublisher<Decimal, Error> {
solanaSdk.api.getFees(commitment: nil)
.tryMap { [weak self] fee in
guard let self = self else {
throw WalletError.empty
}

guard let lamportsPerSignature = fee.feeCalculator?.lamportsPerSignature else {
throw BlockchainSdkError.failedToLoadFee
}

return Decimal(lamportsPerSignature) * Decimal(numberOfSignatures) / self.blockchain.decimalValue
}
.eraseToAnyPublisher()
}

// This fee is deducted from the transaction amount itself (!)
func mainAccountCreationFee() -> AnyPublisher<Decimal, Error> {
minimalBalanceForRentExemption(dataLength: 0)
}

func mainAccountCreationFee(dataLength: UInt64) -> AnyPublisher<Decimal, Error> {
minimalBalanceForRentExemption(dataLength: dataLength)
}

func accountRentFeePerEpoch() -> AnyPublisher<Decimal, Error> {
// https://docs.solana.com/developing/programming-model/accounts#calculation-of-rent
let minimumAccountSizeInBytes = Decimal(128)
let numberOfEpochs = Decimal(1)

let rentInLamportPerByteEpoch: Decimal
if blockchain.isTestnet {
// Solana Testnet uses the same value as Mainnet.
Expand All @@ -148,33 +151,33 @@ class SolanaNetworkService {
rentInLamportPerByteEpoch = Decimal(19.055441478439427)
}
let lamportsInSol = blockchain.decimalValue

let rent = minimumAccountSizeInBytes * numberOfEpochs * rentInLamportPerByteEpoch / lamportsInSol

return Just(rent).setFailureType(to: Error.self).eraseToAnyPublisher()
}

func minimalBalanceForRentExemption(dataLength: UInt64 = 0) -> AnyPublisher<Decimal, Error> {
// The accounts metadata size (128) is already factored in
solanaSdk.api.getMinimumBalanceForRentExemption(dataLength: dataLength)
.tryMap { [weak self] balanceInLamports in
guard let self = self else {
throw WalletError.empty
}

return Decimal(balanceInLamports) / self.blockchain.decimalValue
}
.eraseToAnyPublisher()
}

func tokenProgramId(contractAddress: String) -> AnyPublisher<PublicKey, Error> {
solanaSdk.api.getAccountInfo(account: contractAddress, decodedTo: AccountInfo.self)
.tryMap { accountInfo in
let tokenProgramIds: [PublicKey] = [
.tokenProgramId,
.token2022ProgramId
]

for tokenProgramId in tokenProgramIds {
if tokenProgramId.base58EncodedString == accountInfo.owner {
return tokenProgramId
Expand All @@ -184,7 +187,7 @@ class SolanaNetworkService {
}
.eraseToAnyPublisher()
}

private func mainAccountInfo(accountId: String) -> AnyPublisher<SolanaMainAccountInfoResponse, Error> {
solanaSdk.api.getAccountInfo(account: accountId, decodedTo: AccountInfo.self)
.tryMap { info in
Expand All @@ -204,23 +207,23 @@ class SolanaNetworkService {
break
}
}

throw error
}.eraseToAnyPublisher()
}

private func tokenAccountsInfo(accountId: String, programId: PublicKey) -> AnyPublisher<[TokenAccount<AccountInfoData>], Error> {
let configs = RequestConfiguration(commitment: "recent", encoding: "jsonParsed")

return solanaSdk.api.getTokenAccountsByOwner(pubkey: accountId, programId: programId.base58EncodedString, configs: configs)
.eraseToAnyPublisher()
}

private func confirmedTransactions(among transactionIDs: [String]) -> AnyPublisher<[String], Error> {
guard !transactionIDs.isEmpty else {
return .justWithError(output: [])
}

return solanaSdk.api.getSignatureStatuses(pubkeys: transactionIDs)
.map { statuses in
zip(transactionIDs, statuses)
Expand All @@ -234,7 +237,7 @@ class SolanaNetworkService {
}
.eraseToAnyPublisher()
}

private func mapInfo(
mainAccountInfo: SolanaMainAccountInfoResponse,
tokenAccountsInfo: [TokenAccount<AccountInfoData>],
Expand All @@ -243,7 +246,7 @@ class SolanaNetworkService {
) -> SolanaAccountInfoResponse {
let balance = (Decimal(mainAccountInfo.balance) / blockchain.decimalValue).rounded(blockchain: blockchain)
let accountExists = mainAccountInfo.accountExists

let tokenInfoResponses: [SolanaTokenAccountInfoResponse] = tokenAccountsInfo.compactMap {
guard
let info = $0.account.data.value?.parsed.info,
Expand All @@ -252,19 +255,19 @@ class SolanaNetworkService {
else {
return nil
}

let address = $0.pubkey
let mint = info.mint
let amount = (integerAmount / token.decimalValue).rounded(scale: token.decimalCount)

return SolanaTokenAccountInfoResponse(address: address, mint: mint, balance: amount, space: $0.account.space)
}

let tokenPairs = tokenInfoResponses.map { ($0.mint, $0) }
let tokensByMint = Dictionary(tokenPairs) { token1, _ in
return token1
}

return SolanaAccountInfoResponse(
balance: balance,
accountExists: accountExists,
Expand Down
6 changes: 3 additions & 3 deletions BlockchainSdk/Blockchains/Solana/SolanaSdk+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extension Api {

func getMinimumBalanceForRentExemption(
dataLength: UInt64,
commitment: Commitment? = "recent"
commitment: Commitment? = nil
) -> AnyPublisher<UInt64, Error> {
Deferred {
Future { [weak self] promise in
Expand Down Expand Up @@ -164,7 +164,7 @@ extension Api {
return
}

self.sendTransaction(serializedTransaction: serializedTransaction, configs: configs) {
self.sendTransaction(serializedTransaction: serializedTransaction, configs: configs, startSendingTimestamp: Date()) {
switch $0 {
case .failure(let error):
promise(.failure(error))
Expand All @@ -186,7 +186,7 @@ extension Action {
computeUnitPrice: UInt64?,
allowUnfundedRecipient: Bool = false,
fromPublicKey: PublicKey
) -> AnyPublisher<String, Error> {
) -> AnyPublisher<(String, Date), Error> {
Deferred {
Future { [weak self] promise in
guard let self else {
Expand Down
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ target 'BlockchainSdk' do
pod 'BinanceChain', :git => 'https://github.com/tangem/swiftbinancechain.git', :tag => '0.0.11'
#pod 'BinanceChain', :path => '../SwiftBinanceChain'

pod 'Solana.Swift', :git => 'https://github.com/tangem/Solana.Swift', :tag => '1.2.0-tangem10'
pod 'Solana.Swift', :git => 'https://github.com/tangem/Solana.Swift', :tag => '1.2.0-tangem11'
#pod 'Solana.Swift', :path => '../Solana.Swift'

pod 'SwiftyJSON', :git => 'https://github.com/tangem/SwiftyJSON.git', :tag => '5.0.1-tangem1'
Expand Down
8 changes: 4 additions & 4 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ DEPENDENCIES:
- BitcoinCore.swift (from `https://github.com/tangem/bitcoincore.git`, tag `0.0.20`)
- Moya (= 15.0.0)
- Sodium (= 0.9.1)
- Solana.Swift (from `https://github.com/tangem/Solana.Swift`, tag `1.2.0-tangem10`)
- Solana.Swift (from `https://github.com/tangem/Solana.Swift`, tag `1.2.0-tangem11`)
- stellar-ios-mac-sdk (= 2.5.4)
- SwiftCBOR (= 0.4.5)
- SwiftyJSON (from `https://github.com/tangem/SwiftyJSON.git`, tag `5.0.1-tangem1`)
Expand All @@ -60,7 +60,7 @@ EXTERNAL SOURCES:
:tag: 0.0.20
Solana.Swift:
:git: https://github.com/tangem/Solana.Swift
:tag: 1.2.0-tangem10
:tag: 1.2.0-tangem11
SwiftyJSON:
:git: https://github.com/tangem/SwiftyJSON.git
:tag: 5.0.1-tangem1
Expand All @@ -77,7 +77,7 @@ CHECKOUT OPTIONS:
:tag: 0.0.20
Solana.Swift:
:git: https://github.com/tangem/Solana.Swift
:tag: 1.2.0-tangem10
:tag: 1.2.0-tangem11
SwiftyJSON:
:git: https://github.com/tangem/SwiftyJSON.git
:tag: 5.0.1-tangem1
Expand All @@ -100,6 +100,6 @@ SPEC CHECKSUMS:
SwiftyJSON: c0bd75b5969785b3d9ae41d5709fe75bc5de4002
TangemSdk: f3adf8d0c18bf7a76406462fa31c0efa9f2efa93

PODFILE CHECKSUM: 52792b492481e3a4e4dcf8ac0ecb1cffe929ad2c
PODFILE CHECKSUM: c3bf95e55c682fb33df4ea9627e21a434869279d

COCOAPODS: 1.15.2

0 comments on commit e5053b8

Please sign in to comment.