Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
simonmcl committed Nov 8, 2024
2 parents 68f450d + e7fe6e2 commit 8820421
Show file tree
Hide file tree
Showing 37 changed files with 1,604 additions and 188 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
languages: "swift"

- name: Test
run: xcodebuild -scheme KukaiCoreSwift -destination "platform=iOS Simulator,OS=17.2,name=iPhone 15"
run: xcodebuild -scheme KukaiCoreSwift -destination "platform=iOS Simulator,OS=17.2,name=iPhone 15" -resultBundlePath "~/xcode-$NOW.xcresult"

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- main
jobs:
build:
runs-on: macos-14
runs-on: macos-15

steps:
- uses: actions/[email protected]
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ on:
jobs:
deploy:
name: Running unit tests
runs-on: macos-14
runs-on: macos-15
steps:
- name: Select Xcode version
#run: sudo xcode-select -s '/Applications/Xcode_14.3.1.app/Contents/Developer'
run: sudo xcode-select -s '/Applications/Xcode_15.2.app/Contents/Developer'
run: sudo xcode-select -s '/Applications/Xcode_16.app/Contents/Developer'

- name: Checkout repository
uses: actions/[email protected]
Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:

- name: Test
#run: xcodebuild test -scheme KukaiCoreSwift -destination "platform=iOS Simulator,OS=16.4,name=iPhone 14" -enableCodeCoverage YES -resultBundlePath "~/xcode-$NOW.xcresult"
run: xcodebuild test -scheme KukaiCoreSwift -destination "platform=iOS Simulator,OS=17.2,name=iPhone 15" -enableCodeCoverage YES -resultBundlePath "~/xcode-$NOW.xcresult"
run: xcodebuild test -scheme KukaiCoreSwift -destination "platform=iOS Simulator,OS=18.0,name=iPhone 16" -enableCodeCoverage YES -resultBundlePath "~/xcode-$NOW.xcresult"



Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ let package = Package(
],
dependencies: [
.package(name: "KukaiCryptoSwift", url: "https://github.com/kukai-wallet/kukai-crypto-swift", from: "1.0.23" /*.branch("develop")*/),
.package(name: "CustomAuth", url: "https://github.com/torusresearch/customauth-swift-sdk", from: "10.0.1"),
.package(name: "CustomAuth", url: "https://github.com/torusresearch/customauth-swift-sdk", from: "10.0.2"),
.package(name: "SignalRClient", url: "https://github.com/moozzyk/SignalR-Client-Swift", from: "0.8.0"),
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.18.10")
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.19.3")
],
targets: [
.target(
Expand Down
4 changes: 2 additions & 2 deletions Sources/KukaiCoreSwift/Clients/TezosDomainsClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public class TezosDomainsClient {
dispatchGroup.leave()
}

getDomainFor(address: address, url: TezosNodeClientConfig.defaultTestnetURLs.tezosDomainsURL) { result in
getDomainFor(address: address, url: TezosNodeClientConfig.defaultGhostnetURLs.tezosDomainsURL) { result in
guard let res = try? result.get() else {
errorGhost = result.getFailure()
dispatchGroup.leave()
Expand Down Expand Up @@ -172,7 +172,7 @@ public class TezosDomainsClient {
dispatchGroup.leave()
}

getDomainsFor(addresses: addresses, url: TezosNodeClientConfig.defaultTestnetURLs.tezosDomainsURL) { result in
getDomainsFor(addresses: addresses, url: TezosNodeClientConfig.defaultGhostnetURLs.tezosDomainsURL) { result in
guard let res = try? result.get() else {
errorGhost = result.getFailure()
dispatchGroup.leave()
Expand Down
32 changes: 30 additions & 2 deletions Sources/KukaiCoreSwift/Clients/TzKTClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class TzKTClient {
/// Unique Errors that TzKTClient can throw
public enum TzKTServiceError: Error {
case invalidURL
case invalidAddress
case parseError(String)
}

Expand Down Expand Up @@ -322,7 +323,8 @@ public class TzKTClient {
var estimatedNextReward: RewardDetails? = nil

// Check if we have enough rewards to bring us up to the current cycle
guard currentDelegatorRewards.count > TzKTClient.numberOfFutureCyclesReturned,
guard TzKTClient.numberOfFutureCyclesReturned >= 0,
currentDelegatorRewards.count > TzKTClient.numberOfFutureCyclesReturned,
let currentBakerConfig = bakerConfigs[delegate.address],
let inProgresCycleBakerConfig = bakerConfigs[currentDelegatorRewards[TzKTClient.numberOfFutureCyclesReturned].baker.address] else {

Expand Down Expand Up @@ -713,6 +715,11 @@ public class TzKTClient {
- parameter completion: The completion block called with a `Result` containing the number or an error
*/
public func getBalanceCount(forAddress: String, completion: @escaping (Result<Int, KukaiError>) -> Void) {
guard forAddress != "" else {
completion(Result.failure(KukaiError.internalApplicationError(error: TzKTServiceError.invalidAddress)))
return
}

var url = config.tzktURL
url.appendPathComponent("v1/tokens/balances/count")
url.appendQueryItem(name: "account", value: forAddress)
Expand All @@ -730,6 +737,11 @@ public class TzKTClient {
- parameter completion: The completion block called with a `Result` containing an array of balances or an error
*/
public func getBalancePage(forAddress: String, offset: Int = 0, completion: @escaping ((Result<[TzKTBalance], KukaiError>) -> Void)) {
guard forAddress != "" else {
completion(Result.failure(KukaiError.internalApplicationError(error: TzKTServiceError.invalidAddress)))
return
}

var url = config.tzktURL
url.appendPathComponent("v1/tokens/balances")
url.appendQueryItem(name: "account", value: forAddress)
Expand All @@ -749,6 +761,11 @@ public class TzKTClient {
- parameter completion: The completion block called with a `Result` containing an object or an error
*/
public func getAccount(forAddress: String, fromURL: URL? = nil, completion: @escaping ((Result<TzKTAccount, KukaiError>) -> Void)) {
guard forAddress != "" else {
completion(Result.failure(KukaiError.internalApplicationError(error: TzKTServiceError.invalidAddress)))
return
}

var url = fromURL == nil ? config.tzktURL : fromURL
url?.appendPathComponent("v1/accounts/\(forAddress)")

Expand All @@ -768,6 +785,11 @@ public class TzKTClient {
- parameter completion: The completion block called with a `Result` containing an object or an error
*/
public func getAllBalances(forAddress address: String, completion: @escaping ((Result<Account, KukaiError>) -> Void)) {
guard address != "" else {
completion(Result.failure(KukaiError.internalApplicationError(error: TzKTServiceError.invalidAddress)))
return
}

getBalanceCount(forAddress: address) { [weak self] result in
guard let tokenCount = try? result.get() else {
completion(Result.failure(result.getFailure()))
Expand Down Expand Up @@ -901,7 +923,7 @@ public class TzKTClient {
continue
} else if balance.token.metadata != nil {
// Else create a Token object and put into array, if we have valid metadata (e.g. able to tell how many decimals it has)
tokens.append(Token(from: balance.token, andTokenAmount: balance.tokenAmount))
tokens.append(Token(from: balance, andTokenAmount: balance.tokenAmount))
}
}

Expand Down Expand Up @@ -958,6 +980,11 @@ public class TzKTClient {

/// Fetch all transactions, both account operations, and token transfers, and combine them into 1 response
public func fetchTransactions(forAddress address: String, limit: Int = 50, completion: @escaping (([TzKTTransaction]) -> Void)) {
guard address != "" else {
completion([])
return
}

let dispatchGroupTransactions = DispatchGroup()
dispatchGroupTransactions.enter()
dispatchGroupTransactions.enter()
Expand Down Expand Up @@ -1121,6 +1148,7 @@ public class TzKTClient {
extension TzKTClient: HubConnectionDelegate {

public func connectionDidOpen(hubConnection: HubConnection) {
if addressesToWatch.count == 0 { return }

// Request to be subscribed to events belonging to the given account
let subscription = AccountSubscription(addresses: addressesToWatch)
Expand Down
6 changes: 6 additions & 0 deletions Sources/KukaiCoreSwift/Models/BakingBad/TzKTBalance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public struct TzKTBalance: Codable {
/// Details about the Token
public let token: TzKTBalanceToken

/// The block level where the token was first seen
public let firstLevel: Decimal

/// The block level where the token was last seen
public let lastLevel: Decimal

/// Helper to convert the RPC token balance to a `TokenAmount` object
public var tokenAmount: TokenAmount {
return TokenAmount(fromRpcAmount: balance, decimalPlaces: token.metadata?.decimalsInt ?? 0) ?? .zero()
Expand Down
20 changes: 12 additions & 8 deletions Sources/KukaiCoreSwift/Models/Config/TezosNodeClientConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public struct TezosNodeClientConfig {
/// An enum indicating whether the network is mainnet or testnet
public enum NetworkType: String {
case mainnet
case testnet
case ghostnet
case protocolnet
}

/// Allow switching between local forging or remote forging+parsing
Expand Down Expand Up @@ -49,7 +50,7 @@ public struct TezosNodeClientConfig {
}

/// Preconfigured struct with all the URL's needed to work with Tezos testnet
public struct defaultTestnetURLs {
public struct defaultGhostnetURLs {

/// The default testnet URLs to use for estimating and injecting operations
public static let nodeURLs = [URL(string: "https://ghostnet.smartpy.io")!, URL(string: "https://rpc.ghostnet.tzboot.net")!]
Expand Down Expand Up @@ -141,13 +142,16 @@ public struct TezosNodeClientConfig {
tezosDomainsURL = TezosNodeClientConfig.defaultMainnetURLs.tezosDomainsURL
objktApiURL = TezosNodeClientConfig.defaultMainnetURLs.objktApiURL

case .testnet:
nodeURLs = TezosNodeClientConfig.defaultTestnetURLs.nodeURLs
case .ghostnet:
nodeURLs = TezosNodeClientConfig.defaultGhostnetURLs.nodeURLs
forgingType = .local
tzktURL = TezosNodeClientConfig.defaultTestnetURLs.tzktURL
betterCallDevURL = TezosNodeClientConfig.defaultTestnetURLs.betterCallDevURL
tezosDomainsURL = TezosNodeClientConfig.defaultTestnetURLs.tezosDomainsURL
objktApiURL = TezosNodeClientConfig.defaultTestnetURLs.objktApiURL
tzktURL = TezosNodeClientConfig.defaultGhostnetURLs.tzktURL
betterCallDevURL = TezosNodeClientConfig.defaultGhostnetURLs.betterCallDevURL
tezosDomainsURL = TezosNodeClientConfig.defaultGhostnetURLs.tezosDomainsURL
objktApiURL = TezosNodeClientConfig.defaultGhostnetURLs.objktApiURL

case .protocolnet:
fatalError("No defaults for networkType protocolnet. Must be user supplied")
}
}

Expand Down
15 changes: 8 additions & 7 deletions Sources/KukaiCoreSwift/Models/LedgerWallet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,17 @@ public class LedgerWallet: Wallet {

/**
Sign a hex string.
If the string starts with "03" and is not 32 characters long, it will be treated as a watermarked operation and Ledger will be asked to parse + display the operation details.
Else it will be treated as an unknown operation and will simply display the Blake2b hash.
Please be careful when asking the Ledger to parse (passing in an operation), Ledgers have very limited display ability. Keep it to a single operation, not invoking a smart contract
If its an operation "03" will be prefix to the start, if not the hex will be passed directly to the ledger as it now supports parsing strings directly, but requires them to be unhashed
*/
public func sign(_ hex: String, isOperation: Bool, completion: @escaping ((Result<[UInt8], KukaiError>) -> Void)) {
let isWatermarkedOperation = (String(hex.prefix(2)) == "03") && hex.count != 32
var hexToSign = hex
if isOperation {
hexToSign = "03"+hex
}

LedgerService.shared.connectTo(uuid: ledgerUUID)
.flatMap { _ -> AnyPublisher<String, KukaiError> in
return LedgerService.shared.sign(hex: hex, parse: isWatermarkedOperation)
.flatMap { [weak self] _ -> AnyPublisher<String, KukaiError> in
return LedgerService.shared.sign(hex: hexToSign, forDerivationPath: self?.derivationPath ?? HD.defaultDerivationPath, parse: true)
}
.sink(onError: { error in
completion(Result.failure(error))
Expand Down Expand Up @@ -107,6 +108,6 @@ public class LedgerWallet: Wallet {
*/
public func publicKeyBase58encoded() -> String {
let publicKeyData = Data(hexString: publicKey) ?? Data()
return Base58Check.encode(message: publicKeyData.bytes, prefix: Prefix.Keys.Ed25519.public)
return Base58Check.encode(message: publicKeyData.bytes(), prefix: Prefix.Keys.Ed25519.public)
}
}
8 changes: 8 additions & 0 deletions Sources/KukaiCoreSwift/Models/NFT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public struct NFT: Codable, Hashable {
return favouriteSortIndex != nil
}

/// The block level where the token was first seen
public var firstlevel: Decimal

/// The block level where the token was last seen
public var lastLevel: Decimal


/**
Create a more developer friednly `NFT` from a generic `TzKTBalance` object
Expand All @@ -83,6 +89,8 @@ public struct NFT: Codable, Hashable {
displayURI = URL(string: tzkt.token.metadata?.displayUri ?? "")
thumbnailURI = URL(string: tzkt.token.metadata?.thumbnailUri ?? "")
metadata = tzkt.token.metadata
firstlevel = tzkt.firstLevel
lastLevel = tzkt.lastLevel
}

/// Confomring to Equatable
Expand Down
32 changes: 22 additions & 10 deletions Sources/KukaiCoreSwift/Models/Token.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public class Token: Codable, CustomStringConvertible {
/// Recording if the position the index the user chose for the favourite token to appear
public var favouriteSortIndex: Int? = nil

/// The block level where the token was first seen
public var firstlevel: Decimal

/// The block level where the token was last seen
public var lastLevel: Decimal

/// The individual NFT's owned of this token type
public var nfts: [NFT]?

Expand Down Expand Up @@ -126,6 +132,8 @@ public class Token: Codable, CustomStringConvertible {
self.tokenId = tokenId
self.nfts = nfts
self.mintingTool = mintingTool
self.firstlevel = 0
self.lastLevel = 0

// TODO: make failable init
if let faVersion = faVersion, faVersion == .fa2 && tokenId == nil {
Expand All @@ -136,23 +144,25 @@ public class Token: Codable, CustomStringConvertible {
/**
Init a `Token` from an object returned by the TzKT API
*/
public init(from: TzKTBalanceToken, andTokenAmount: TokenAmount, stakedTokenAmount: TokenAmount? = nil, unstakedTokenAmount: TokenAmount? = nil) {
let decimalsString = from.metadata?.decimals ?? "0"
public init(from: TzKTBalance, andTokenAmount: TokenAmount, stakedTokenAmount: TokenAmount? = nil, unstakedTokenAmount: TokenAmount? = nil) {
let decimalsString = from.token.metadata?.decimals ?? "0"
let decimalsInt = Int(decimalsString) ?? 0
let isNFT = (from.metadata?.artifactUri != nil && decimalsInt == 0 && from.standard == .fa2)
let isNFT = (from.token.metadata?.artifactUri != nil && decimalsInt == 0 && from.token.standard == .fa2)

self.name = from.contract.alias
self.symbol = isNFT ? from.contract.alias ?? "" : from.displaySymbol
self.name = from.token.contract.alias
self.symbol = isNFT ? from.token.contract.alias ?? "" : from.token.displaySymbol
self.tokenType = isNFT ? .nonfungible : .fungible
self.faVersion = from.standard
self.faVersion = from.token.standard
self.balance = andTokenAmount
self.stakedBalance = stakedTokenAmount ?? .zeroBalance(decimalPlaces: andTokenAmount.decimalPlaces)
self.unstakedBalance = unstakedTokenAmount ?? .zeroBalance(decimalPlaces: andTokenAmount.decimalPlaces)
self.thumbnailURL = from.metadata?.thumbnailURL ?? TzKTClient.avatarURL(forToken: from.contract.address)
self.tokenContractAddress = from.contract.address
self.tokenId = Decimal(string: from.tokenId) ?? 0
self.thumbnailURL = from.token.metadata?.thumbnailURL ?? TzKTClient.avatarURL(forToken: from.token.contract.address)
self.tokenContractAddress = from.token.contract.address
self.tokenId = Decimal(string: from.token.tokenId) ?? 0
self.nfts = []
self.mintingTool = from.metadata?.mintingTool
self.mintingTool = from.token.metadata?.mintingTool
self.firstlevel = from.firstLevel
self.lastLevel = from.lastLevel

// TODO: make failable init
if let faVersion = faVersion, faVersion == .fa2 && tokenId == nil {
Expand Down Expand Up @@ -183,6 +193,8 @@ public class Token: Codable, CustomStringConvertible {
self.tokenId = Decimal(string: from.token.tokenId) ?? 0
self.nfts = []
self.mintingTool = from.mintingTool
self.firstlevel = from.level
self.lastLevel = from.level

// TODO: make failable init
if let faVersion = faVersion, faVersion == .fa2 && tokenId == nil {
Expand Down
1 change: 1 addition & 0 deletions Sources/KukaiCoreSwift/Models/Wallet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public protocol Wallet: Codable {
/**
Sign a hex string with the wallets private key
- parameter hex: A hex encoded string, representing a forged operation payload.
- parameter isOperation: A boolean to indicate whether its an operation or something else such as an expression to sign. So that the appropriate prefix can be added automatically
- parameter completion: A completion block to run with the resulting signature, needs to be done async in order to support usecases such as signing with an external ledger.
*/
func sign(_ hex: String, isOperation: Bool, completion: @escaping ((Result<[UInt8], KukaiError>) -> Void))
Expand Down
Loading

0 comments on commit 8820421

Please sign in to comment.