-
Notifications
You must be signed in to change notification settings - Fork 38
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 8249 bsdk krc20 #4144
IOS 8249 bsdk krc20 #4144
Changes from all commits
1833753
52e0d06
79e8832
863bf69
3391132
062735c
c9673a8
77da14a
b7bb755
bd84b94
9370db5
dfadf75
363e6da
bb6cfcc
724bb1e
8fdd52e
6568ada
604680a
fa68563
7d5c0f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// | ||
// KaspaKRC20.swift | ||
// BlockchainSdk | ||
// | ||
// Created by Sergei Iakovlev on 16.10.2024 | ||
// Copyright © 2024 Tangem AG. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import CryptoSwift | ||
|
||
struct KaspaIncompleteTokenTransactionStorageID: Hashable, Identifiable { | ||
let id: String | ||
|
||
init( | ||
walletAddress: String, | ||
contractAddress: String | ||
) { | ||
id = "KaspaTokenIncompleteTransactions_\(walletAddress.sha256())_\(contractAddress)" | ||
} | ||
} | ||
|
||
enum KaspaKRC20 { | ||
static let RevealTransactionMassConstant: Decimal = 4100 | ||
|
||
struct TransactionGroup { | ||
let kaspaCommitTransaction: KaspaTransaction | ||
let kaspaRevealTransaction: KaspaTransaction | ||
let hashesCommit: [Data] | ||
let hashesReveal: [Data] | ||
} | ||
|
||
struct TransactionMeta { | ||
let redeemScriptCommit: KaspaKRC20.RedeemScript | ||
let incompleteTransactionParams: KaspaKRC20.IncompleteTokenTransactionParams | ||
} | ||
|
||
struct CommitTransction { | ||
let transaction: KaspaTransaction | ||
let hashes: [Data] | ||
let redeemScript: KaspaKRC20.RedeemScript | ||
let sourceAddress: String | ||
let params: IncompleteTokenTransactionParams | ||
} | ||
|
||
struct RevealTransaction { | ||
let transaction: KaspaTransaction | ||
let hashes: [Data] | ||
let redeemScript: KaspaKRC20.RedeemScript | ||
} | ||
|
||
struct IncompleteTokenTransactionParams: TransactionParams, Codable { | ||
let transactionId: String | ||
let amount: UInt64 | ||
let envelope: KaspaKRC20.Envelope | ||
} | ||
|
||
struct RevealTransactionFeeParameter: FeeParameters { | ||
let amount: Amount | ||
} | ||
|
||
struct Envelope: Codable { | ||
let p: String | ||
let op: String | ||
let amt: String | ||
let to: String | ||
let tick: String | ||
|
||
init(amount: Decimal, recipient: String, ticker: String) { | ||
p = "krc-20" | ||
op = "transfer" | ||
amt = amount.description | ||
to = recipient | ||
tick = ticker | ||
} | ||
|
||
var data: Data { | ||
let kasplexId = "kasplex".data(using: .utf8)! | ||
let kasplexIdCount = UInt8(kasplexId.count & 0xff) | ||
|
||
let payload = "{\"amt\":\"\(amt)\",\"op\":\"\(op)\",\"p\":\"\(p)\",\"tick\":\"\(tick)\",\"to\":\"\(to)\"}".data(using: .utf8)! | ||
siblockchaina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let payloadCount = UInt8(payload.count & 0xff) | ||
|
||
let elements = [ | ||
OpCode.OP_FALSE.value.data, | ||
OpCode.OP_IF.value.data, | ||
kasplexIdCount.data, | ||
kasplexId, | ||
OpCode.OP_1.value.data, | ||
OpCode.OP_0.value.data, | ||
OpCode.OP_0.value.data, | ||
OpCode.OP_PUSHDATA1.value.data, | ||
payloadCount.data, | ||
payload, | ||
OpCode.OP_ENDIF.value.data, | ||
] | ||
|
||
return elements.reduce(Data(), +) | ||
} | ||
} | ||
|
||
struct RedeemScript { | ||
let publicKey: Data | ||
let envelope: KaspaKRC20.Envelope | ||
|
||
init(publicKey: Data, envelope: KaspaKRC20.Envelope) { | ||
self.publicKey = publicKey | ||
self.envelope = envelope | ||
} | ||
|
||
var data: Data { | ||
return UInt8(publicKey.count & 0xff).data + publicKey + OpCode.OP_CODESEPARATOR.value.data + envelope.data | ||
} | ||
|
||
var redeemScriptHash: Data { | ||
return OpCode.OP_HASH256.value.data + UInt8(32).data + data.hashBlake2b(outputLength: 32)! + OpCode.OP_EQUAL.value.data | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// | ||
// KaspaNetworkModelsKRC20.swift | ||
// BlockchainSdk | ||
// | ||
// Created by Sergei Iakovlev on 17.10.2024 | ||
// Copyright © 2024 Tangem AG. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
struct KaspaBalanceResponseKRC20: Codable { | ||
struct Result: Codable { | ||
let tick: String | ||
let balance: String | ||
let locked: String | ||
let dec: String | ||
let opScoreMod: String | ||
} | ||
|
||
let message: String | ||
let result: [Result] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// | ||
// KaspaNetworkProviderKRC20.swift | ||
// BlockchainSdk | ||
// | ||
// Created by Sergei Iakovlev on 17.10.2024 | ||
// Copyright © 2024 Tangem AG. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Combine | ||
|
||
class KaspaNetworkProviderKRC20: HostProvider { | ||
var host: String { | ||
url.hostOrUnknown | ||
} | ||
|
||
private let url: URL | ||
private let provider: NetworkProvider<KaspaTargetKRC20> | ||
|
||
init(url: URL, networkConfiguration: NetworkProviderConfiguration) { | ||
self.url = url | ||
provider = NetworkProvider<KaspaTargetKRC20>(configuration: networkConfiguration) | ||
} | ||
|
||
func balance(address: String, token: String) -> AnyPublisher<KaspaBalanceResponseKRC20, Error> { | ||
requestPublisher(for: .balance(address: address, token: token)) | ||
} | ||
|
||
private func requestPublisher<T: Decodable>(for request: KaspaTargetKRC20.Request) -> AnyPublisher<T, Error> { | ||
let decoder = JSONDecoder() | ||
decoder.keyDecodingStrategy = .convertFromSnakeCase | ||
|
||
return provider.requestPublisher(KaspaTargetKRC20(request: request, baseURL: url)) | ||
.filterSuccessfulStatusAndRedirectCodes() | ||
.map(T.self, using: decoder) | ||
.mapError { moyaError in | ||
if case .objectMapping = moyaError { | ||
return WalletError.failedToParseNetworkResponse() | ||
} | ||
return moyaError | ||
} | ||
.eraseToAnyPublisher() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// | ||
// KaspaNetworkServiceKRC20.swift | ||
// BlockchainSdk | ||
// | ||
// Created by Sergei Iakovlev on 17.10.2024 | ||
// Copyright © 2024 Tangem AG. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Combine | ||
|
||
class KaspaNetworkServiceKRC20: MultiNetworkProvider { | ||
let providers: [KaspaNetworkProviderKRC20] | ||
var currentProviderIndex: Int = 0 | ||
|
||
private let blockchain: Blockchain | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Как будто не используется, да и не нужен целый |
||
|
||
init(providers: [KaspaNetworkProviderKRC20], blockchain: Blockchain) { | ||
self.providers = providers | ||
self.blockchain = blockchain | ||
} | ||
|
||
func balance(address: String, tokens: [Token]) -> AnyPublisher<[Token: Result<KaspaBalanceResponseKRC20, Error>], Error> { | ||
tokens | ||
.publisher | ||
.setFailureType(to: Error.self) | ||
.withWeakCaptureOf(self) | ||
.flatMap { networkService, token in | ||
networkService.providerPublisher(for: { provider in | ||
provider.balance(address: address, token: token.contractAddress) | ||
.retry(2) | ||
.eraseToAnyPublisher() | ||
}) | ||
.mapToResult() | ||
.setFailureType(to: Error.self) | ||
Comment on lines
+26
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. как будто |
||
.map { (token, $0) } | ||
.eraseToAnyPublisher() | ||
} | ||
.collect() | ||
.map { $0.reduce(into: [Token: Result<KaspaBalanceResponseKRC20, Error>]()) { $0[$1.0] = $1.1 }} | ||
.eraseToAnyPublisher() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// | ||
// KaspaTargetKRC20.swift | ||
// BlockchainSdk | ||
// | ||
// Created by Sergei Iakovlev on 17.10.2024 | ||
// Copyright © 2024 Tangem AG. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import Moya | ||
|
||
struct KaspaTargetKRC20: TargetType { | ||
let request: Request | ||
let baseURL: URL | ||
|
||
var path: String { | ||
switch request { | ||
case .balance(let address, let token): | ||
return "krc20/address/\(address)/token/\(token)" | ||
} | ||
} | ||
|
||
var method: Moya.Method { | ||
switch request { | ||
case .balance: | ||
return .get | ||
} | ||
} | ||
|
||
var task: Moya.Task { | ||
switch request { | ||
case .balance: | ||
return .requestPlain | ||
} | ||
} | ||
|
||
var headers: [String: String]? { | ||
nil | ||
} | ||
} | ||
|
||
extension KaspaTargetKRC20 { | ||
enum Request { | ||
case balance(address: String, token: String) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
как будто лишнее