Skip to content
Open
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
23 changes: 22 additions & 1 deletion Sources/Cadence/Cadence+Child.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ extension CadenceLoader.Category {
public enum Child: String, CaseIterable, CadenceLoaderProtocol {
case getChildAddress = "get_child_addresses"
case getChildAccountMeta = "get_child_account_meta"

case getAccessibleCoinInfo = "get_accessible_coin_info"
case getChildAccountAllowTypes = "get_child_account_allow_types"
var filename: String {
rawValue
}
Expand Down Expand Up @@ -43,6 +44,21 @@ public extension Flow {
).decode()
}

func getChildAccessibleToken(address: Flow.Address, parentAddress: Flow.Address) async throws -> [CadenceLoader.Category.Child.TokenInfo] {
let script = try CadenceLoader.load(CadenceLoader.Category.Child.getAccessibleCoinInfo)
return try await executeScriptAtLatestBlock(
script: .init(text: script),
arguments: [.address(parentAddress), .address(address)])
.decode()
}

func getChildAccessibleCollection(address: Flow.Address, parentAddress: Flow.Address) async throws -> [String] {
let script = try CadenceLoader.load(CadenceLoader.Category.Child.getChildAccountAllowTypes)
return try await executeScriptAtLatestBlock(
script: .init(text: script),
arguments: [.address(parentAddress), .address(address)])
.decode()
}
}

extension CadenceLoader.Category.Child {
Expand All @@ -55,4 +71,9 @@ extension CadenceLoader.Category.Child {
public struct Thumbnail: Codable {
public let url: URL?
}

public struct TokenInfo: Codable {
public let id: String?
public let balance: UInt64
}
}
92 changes: 92 additions & 0 deletions Sources/Cadence/CommonCadence/Child/get_accessible_coin_info.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import HybridCustody from 0xHybridCustody
import MetadataViews from 0xMetadataViews
import FungibleToken from 0xFungibleToken
import NonFungibleToken from 0xNonFungibleToken


access(all) struct TokenInfo {
access(all) let id: String
access(all) let balance: UFix64

init(id: String, balance: UFix64) {
self.id = id
self.balance = balance
}
}

access(all) fun main(parent: Address, childAddress: Address): [TokenInfo] {
let manager = getAuthAccount<auth(Storage) &Account>(parent).storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist")

var typeIdsWithProvider: {Address: [String]} = {}

var coinInfoList: [TokenInfo] = []
let providerType = Type<Capability<&{FungibleToken.Provider}>>()
let vaultType: Type = Type<@{FungibleToken.Vault}>()

// Iterate through child accounts

let acct = getAuthAccount<auth(Storage, Capabilities) &Account> (childAddress)
let foundTypes: [String] = []
let vaultBalances: {String: UFix64} = {}
let childAcct = manager.borrowAccount(addr: childAddress) ?? panic("child account not found")
// get all private paths
acct.storage.forEachStored(fun (path: StoragePath, type: Type): Bool {
// Check which private paths have NFT Provider AND can be borrowed
if !type.isSubtype(of: providerType){
return true
}

let controllers = acct.capabilities.storage.getControllers(forPath: path)

// let providerCap = cap as! Capability<&{FungibleToken.Provider}>

for c in controllers {
if !c.borrowType.isSubtype(of: providerType) {
continue
}

if let cap = childAcct.getCapability(controllerID: c.capabilityID, type: providerType) {
let providerCap = cap as! Capability<&{NonFungibleToken.Provider}>

if !providerCap.check(){
continue
}
foundTypes.append(cap.borrow<&AnyResource>()!.getType().identifier)
}
}
return true
})
typeIdsWithProvider[childAddress] = foundTypes


acct.storage.forEachStored(fun (path: StoragePath, type: Type): Bool {

if typeIdsWithProvider[childAddress] == nil {
return true
}

for key in typeIdsWithProvider.keys {
for idx, value in typeIdsWithProvider[key]! {
let value = typeIdsWithProvider[key]!

if value[idx] != type.identifier {
continue
} else {
if type.isInstance(vaultType) {
continue
}
if let vault = acct.storage.borrow<&{FungibleToken.Balance}>(from: path) {
// Iterate over IDs & resolve the view
coinInfoList.append(
TokenInfo(id: type.identifier, balance: vault.balance))
}
continue
}
}
}
return true
})


return coinInfoList
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import HybridCustody from 0xHybridCustody
import NonFungibleToken from 0xNonFungibleToken
import FungibleToken from 0xFungibleToken


// This script iterates through a parent's child accounts,
// identifies private paths with an accessible NonFungibleToken.Provider, and returns the corresponding typeIds
access(all) fun main(addr: Address, child: Address): [String]? {
let account = getAuthAccount<auth(Storage) &Account>(addr)
let manager = getAuthAccount<auth(Storage) &Account>(addr).storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist")



let nftProviderType = Type<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider}>()
let ftProviderType = Type<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>()

// Iterate through child accounts
let addr = getAuthAccount<auth(Storage, Capabilities) &Account>(child)
let foundTypes: [String] = []
let childAcct = manager.borrowAccount(addr: child) ?? panic("child account not found")
// get all private paths

for s in addr.storage.storagePaths {
let controllers = addr.capabilities.storage.getControllers(forPath: s)
for c in controllers {
// if !c.borrowType.isSubtype(of: providerType) {
// continue
// }

if let nftCap = childAcct.getCapability(controllerID: c.capabilityID, type: nftProviderType) {
let providerCap = nftCap as! Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider}>

if !providerCap.check(){
continue
}

foundTypes.append(nftCap.borrow<&AnyResource>()!.getType().identifier)
break
}
if let ftCap = childAcct.getCapability(controllerID: c.capabilityID, type: ftProviderType) {
let providerCap = ftCap as! Capability<&{FungibleToken.Provider}>

if !providerCap.check(){
continue
}

foundTypes.append(ftCap.borrow<&AnyResource>()!.getType().identifier)
break
}
}
}

return foundTypes
}
13 changes: 13 additions & 0 deletions Tests/AddressRegistorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ final class AddressRegistorTests: XCTestCase {

let addressA = Flow.Address(hex: "0x39416b4b085d94c7")
let addressB = Flow.Address(hex: "0x84221fe0294044d7")
let addressBChild = Flow.Address(hex: "0x16c41a2b76dee69b")

func testContract() {
let result = flow.addressRegister.contractExists("0xFlowToken", on: .mainnet)
Expand Down Expand Up @@ -47,6 +48,18 @@ final class AddressRegistorTests: XCTestCase {
XCTAssertNotNil(result[result.keys.first!]?.name)
}

func testChildAccessibleToken() async throws {
let result = try await flow.getChildAccessibleToken(address: addressBChild, parentAddress: addressB)
print(result)
XCTAssertTrue(result.count >= 0)
}

func testChildAccessibleCollection() async throws {
let result = try await flow.getChildAccessibleCollection(address: addressBChild, parentAddress: addressB)
print(result)
XCTAssertTrue(result.count >= 0)
}

func testStake() async throws {
let models = try await flow.getStakingInfo(address: addressB)
XCTAssertTrue(!models.isEmpty)
Expand Down
Loading