diff --git a/Sources/DAB/Token/DidFiles/ICRC1Oracle.did b/Sources/DAB/Token/DidFiles/ICRC1Oracle.did index 194655b..74ee36b 100644 --- a/Sources/DAB/Token/DidFiles/ICRC1Oracle.did +++ b/Sources/DAB/Token/DidFiles/ICRC1Oracle.did @@ -32,10 +32,14 @@ type ICRC1Request = record { symbol : text; decimals : nat8; fee : nat; -};service : (opt Conf) -> { - get_all_icrc1_canisters : () -> (vec ICRC1) query; - replace_icrc1_canisters : (vec ICRC1) -> (); - store_new_icrc1_canisters : (vec ICRC1) -> (); - store_icrc1_canister : (ICRC1Request) -> (); - sync_controllers: () -> (vec text); -} \ No newline at end of file +}; + +service : { + count_icrc1_canisters : () -> (nat64) query; + get_all_icrc1_canisters : () -> (vec ICRC1) query; + get_icrc1_paginated : (nat64, nat64) -> (vec ICRC1) query; + replace_icrc1_canisters : (vec ICRC1) -> (); + set_operator : (principal) -> (); + store_icrc1_canister : (ICRC1Request) -> (); + store_new_icrc1_canisters : (vec ICRC1) -> (); +} diff --git a/Sources/DAB/Token/Generated/ICRC1Oracle.did.swift b/Sources/DAB/Token/Generated/ICRC1Oracle.did.swift index 0a61c84..c5f92b5 100644 --- a/Sources/DAB/Token/Generated/ICRC1Oracle.did.swift +++ b/Sources/DAB/Token/Generated/ICRC1Oracle.did.swift @@ -87,14 +87,23 @@ enum ICRC1Oracle { } - /// service : (opt Conf) -> { - /// get_all_icrc1_canisters : () -> (vec ICRC1) query; - /// replace_icrc1_canisters : (vec ICRC1) -> (); - /// store_new_icrc1_canisters : (vec ICRC1) -> (); - /// store_icrc1_canister : (ICRC1Request) -> (); - /// sync_controllers: () -> (vec text); + /// service : { + /// count_icrc1_canisters : () -> (nat64) query; + /// get_all_icrc1_canisters : () -> (vec ICRC1) query; + /// get_icrc1_paginated : (nat64, nat64) -> (vec ICRC1) query; + /// replace_icrc1_canisters : (vec ICRC1) -> (); + /// set_operator : (principal) -> (); + /// store_icrc1_canister : (ICRC1Request) -> (); + /// store_new_icrc1_canisters : (vec ICRC1) -> (); /// } class Service: ICPService { + /// count_icrc1_canisters : () -> (nat64) query; + func count_icrc1_canisters(sender: ICPSigningPrincipal? = nil) async throws -> UInt64 { + let caller = ICPQueryNoArgs(canister, "count_icrc1_canisters") + let response = try await caller.callMethod(client, sender: sender) + return response + } + /// get_all_icrc1_canisters : () -> (vec ICRC1) query; func get_all_icrc1_canisters(sender: ICPSigningPrincipal? = nil) async throws -> [ICRC1] { let caller = ICPQueryNoArgs<[ICRC1]>(canister, "get_all_icrc1_canisters") @@ -102,15 +111,22 @@ enum ICRC1Oracle { return response } + /// get_icrc1_paginated : (nat64, nat64) -> (vec ICRC1) query; + func get_icrc1_paginated(_ arg0: UInt64, _ arg1: UInt64, sender: ICPSigningPrincipal? = nil) async throws -> [ICRC1] { + let caller = ICPQuery, [ICRC1]>(canister, "get_icrc1_paginated") + let response = try await caller.callMethod(.init(arg0, arg1), client, sender: sender) + return response + } + /// replace_icrc1_canisters : (vec ICRC1) -> (); func replace_icrc1_canisters(_ arg0: [ICRC1], sender: ICPSigningPrincipal? = nil) async throws { let caller = ICPCallNoResult<[ICRC1]>(canister, "replace_icrc1_canisters") let _ = try await caller.callMethod(arg0, client, sender: sender) } - /// store_new_icrc1_canisters : (vec ICRC1) -> (); - func store_new_icrc1_canisters(_ arg0: [ICRC1], sender: ICPSigningPrincipal? = nil) async throws { - let caller = ICPCallNoResult<[ICRC1]>(canister, "store_new_icrc1_canisters") + /// set_operator : (principal) -> (); + func set_operator(_ arg0: ICPPrincipal, sender: ICPSigningPrincipal? = nil) async throws { + let caller = ICPCallNoResult(canister, "set_operator") let _ = try await caller.callMethod(arg0, client, sender: sender) } @@ -120,11 +136,10 @@ enum ICRC1Oracle { let _ = try await caller.callMethod(arg0, client, sender: sender) } - /// sync_controllers: () -> (vec text); - func sync_controllers(sender: ICPSigningPrincipal? = nil) async throws -> [String] { - let caller = ICPCallNoArgs<[String]>(canister, "sync_controllers") - let response = try await caller.callMethod(client, sender: sender) - return response + /// store_new_icrc1_canisters : (vec ICRC1) -> (); + func store_new_icrc1_canisters(_ arg0: [ICRC1], sender: ICPSigningPrincipal? = nil) async throws { + let caller = ICPCallNoResult<[ICRC1]>(canister, "store_new_icrc1_canisters") + let _ = try await caller.callMethod(arg0, client, sender: sender) } } diff --git a/Sources/DAB/TokenOracle.swift b/Sources/DAB/TokenOracle.swift index e820cf7..6979435 100644 --- a/Sources/DAB/TokenOracle.swift +++ b/Sources/DAB/TokenOracle.swift @@ -46,7 +46,7 @@ public class TokenOracle { if let cachedCanisters = cachedCanisters { return cachedCanisters.compactMap { try? ICPToken($0) } } - cachedCanisters = try await service.get_all_icrc1_canisters() + cachedCanisters = try await fetchAllIcrc1Canisters() return cachedCanisters!.compactMap { try? ICPToken($0) } } @@ -100,6 +100,29 @@ public class TokenOracle { } } +private extension TokenOracle { + private static let pageSize: UInt64 = 50 + func fetchAllIcrc1Canisters() async throws -> [ICRC1Oracle.ICRC1] { + let canisterCount = try await service.count_icrc1_canisters() + let nPages = canisterCount / Self.pageSize + 1 + let canisters = await withTaskGroup(of: [ICRC1Oracle.ICRC1].self) { [weak self] group in + for i in 0.. where Provider: PaginatorPageProvider { - private let provider: Provider - private var nextPage: NextPage = .first - - private var stack = Stack() -// private let logger = BTLogger(module: "Paginator") - - init(provider: Provider) { - self.provider = provider - } - - func stream() -> AsyncThrowingStream { -// logger.info("Begin paginator") - return AsyncThrowingStream { try await self.fetchNext() } // we keep strong reference to self by design - // consumer must simply keep a reference to the stream to keep this alive - } - - private func fetchNext() async throws -> Provider.Object? { - while stack.isEmpty { -// logger.debug("No objects in stack") - guard let objects = try await fetchNextPage() else { - return nil - } - objects.forEach { stack.push($0) } - } -// logger.debug("Popping object from stack") - return stack.pop() - } - - private func fetchNextPage() async throws -> [Provider.Object]? { - guard let nextPageToFetch = nextPage.page else { -// logger.info("Fetched last page. Stopping") - return nil - } -// logger.info("Fetching \(nextPageToFetch)") - - let (objects, nextPage) = try await provider.fetch(nextPageToFetch) - if let nextPage = nextPage { - self.nextPage = .page(nextPage) - } else { - self.nextPage = .none - } - return objects - } -} - -protocol PaginatorPage: CustomStringConvertible, Equatable { - static func first() -> Self -} - -enum NextPage: CustomStringConvertible, Equatable { - case first - case page(Page) - case none - - var page: Page? { - switch self { - case .first: return Page.first() - case .page(let page): return page - case .none: return nil - } - } - - var description: String { - switch self { - case .first: return "first page" - case .page(let page): return "page \(page.description)" - case .none: return "no page" - } - } -} - -protocol PaginatorPageProvider { - associatedtype Object - associatedtype Page: PaginatorPage - - func fetch(_ page: Page) async throws -> ([Object], nextPage: Page?) -} diff --git a/Sources/IcpKit/Utils/Stack.swift b/Sources/IcpKit/Utils/Stack.swift deleted file mode 100644 index 854f2ea..0000000 --- a/Sources/IcpKit/Utils/Stack.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// Stack.swift -// -// Created by Konstantinos Gaitanis on 15.04.23. -// - -import Foundation - -protocol Stackable { - associatedtype Element - func peek() -> Element? - mutating func push(_ element: Element) - @discardableResult mutating func pop() -> Element? -} - -extension Stackable { - var isEmpty: Bool { peek() == nil } -} - -struct Stack: Stackable { - private var storage = [Element]() - func peek() -> Element? { storage.last } - mutating func push(_ element: Element) { storage.insert(element, at: 0) } - mutating func pop() -> Element? { storage.popLast() } -} - -extension Stack: Equatable where Element: Equatable { - static func == (lhs: Stack, rhs: Stack) -> Bool { lhs.storage == rhs.storage } -} - -extension Stack: CustomStringConvertible { - var description: String { "\(storage)" } -} - -extension Stack: ExpressibleByArrayLiteral { - init(arrayLiteral elements: Self.Element...) { storage = elements } -} diff --git a/Tests/DABTests/DABTests.swift b/Tests/DABTests/DABTests.swift index 8c072e0..541e146 100644 --- a/Tests/DABTests/DABTests.swift +++ b/Tests/DABTests/DABTests.swift @@ -56,7 +56,8 @@ final class DABTests: XCTestCase { } func testIcrc1Oracle() async throws { - let _ = try await oracle.allTokens() + let tokens = try await oracle.allTokens() + print(tokens.count) } func testAllTokens() async throws {