Skip to content

Commit

Permalink
fix HybridCustody tests
Browse files Browse the repository at this point in the history
  • Loading branch information
austinkline committed Apr 3, 2024
1 parent 42746d4 commit 1301040
Show file tree
Hide file tree
Showing 78 changed files with 735 additions and 508 deletions.
80 changes: 56 additions & 24 deletions contracts/HybridCustody.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import "CapabilityFilter"
access(all) contract HybridCustody {

access(all) entitlement Owner
access(all) entitlement Child
access(all) entitlement Publish
access(all) entitlement Manage

Expand Down Expand Up @@ -102,6 +103,9 @@ access(all) contract HybridCustody {

/// A callback function to mark a parent as redeemed on the child account.
access(contract) fun setRedeemed(_ addr: Address)

/// A helper function to find what controller Id to ask for if you are looking for a specific type of capability
access(all) view fun getControllerIDForType(type: Type, forPath: StoragePath): UInt64?
}

/// Private interface accessible to the owner of the OwnedAccount
Expand Down Expand Up @@ -206,19 +210,20 @@ access(all) contract HybridCustody {
access(all) view fun getAddress(): Address
access(all) view fun getCapabilityFactoryManager(): &{CapabilityFactory.Getter}?
access(all) view fun getCapabilityFilter(): &{CapabilityFilter.Filter}?
access(all) view fun getControllerIDForType(type: Type, forPath: StoragePath): UInt64?
}

/// Methods accessible to the designated parent of a ChildAccount
///
access(all) resource interface AccountPrivate {
access(Capabilities) view fun getCapability(controllerID: UInt64, type: Type): Capability? {
access(Child) view fun getCapability(controllerID: UInt64, type: Type): Capability? {
post {
result == nil || [true, nil].contains(self.getManagerCapabilityFilter()?.allowed(cap: result!)):
"Capability is not allowed by this account's Parent"
}
}
access(all) view fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}?
access(Capabilities) view fun getPrivateCapFromDelegator(type: Type): Capability? {
access(Child) view fun getPrivateCapFromDelegator(type: Type): Capability? {
post {
result == nil || [true, nil].contains(self.getManagerCapabilityFilter()?.allowed(cap: result!)):
"Capability is not allowed by this account's Parent"
Expand All @@ -236,8 +241,8 @@ access(all) contract HybridCustody {
/// Entry point for a parent to obtain, maintain and access Capabilities or perform other actions on child accounts
///
access(all) resource interface ManagerPrivate {
access(Manage) fun addAccount(cap: Capability<&{AccountPrivate, AccountPublic, ViewResolver.Resolver}>)
access(Manage) fun borrowAccount(addr: Address): &{AccountPrivate, AccountPublic, ViewResolver.Resolver}?
access(Manage) fun addAccount(cap: Capability<auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}>)
access(Manage) fun borrowAccount(addr: Address): auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}?
access(Manage) fun removeChild(addr: Address)
access(Manage) fun addOwnedAccount(cap: Capability<auth(Owner) &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}>)
access(Manage) fun borrowOwnedAccount(addr: Address): &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}?
Expand Down Expand Up @@ -265,7 +270,7 @@ access(all) contract HybridCustody {
access(all) resource Manager: ManagerPrivate, ManagerPublic, ViewResolver.Resolver {

/// Mapping of restricted access child account Capabilities indexed by their address
access(self) let childAccounts: {Address: Capability<&{AccountPrivate, AccountPublic, ViewResolver.Resolver}>}
access(self) let childAccounts: {Address: Capability<auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}>}
/// Mapping of unrestricted owned account Capabilities indexed by their address
access(self) let ownedAccounts: {Address: Capability<auth(Owner) &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}>}

Expand Down Expand Up @@ -299,7 +304,7 @@ access(all) contract HybridCustody {
/// Adds a ChildAccount Capability to this Manager. If a default Filter is set in the manager, it will also be
/// added to the ChildAccount
///
access(Manage) fun addAccount(cap: Capability<&{AccountPrivate, AccountPublic, ViewResolver.Resolver}>) {
access(Manage) fun addAccount(cap: Capability<auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}>) {
pre {
self.childAccounts[cap.address] == nil: "There is already a child account with this address"
}
Expand Down Expand Up @@ -390,7 +395,7 @@ access(all) contract HybridCustody {

/// Returns a reference to a child account
///
access(Manage) fun borrowAccount(addr: Address): &{AccountPrivate, AccountPublic, ViewResolver.Resolver}? {
access(Manage) fun borrowAccount(addr: Address): auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}? {
let cap = self.childAccounts[addr]
if cap == nil {
return nil
Expand Down Expand Up @@ -443,7 +448,7 @@ access(all) contract HybridCustody {
/// mechanism intended to easily transfer 'root' access on this account to another account and an attempt to
/// minimize access vectors.
///
access(Owner) fun giveOwnership(addr: Address, to: Address) {
access(Manage) fun giveOwnership(addr: Address, to: Address) {
let acct = self.ownedAccounts.remove(key: addr)
?? panic("account not found")

Expand Down Expand Up @@ -584,7 +589,7 @@ access(all) contract HybridCustody {
/// Capabilities, see `getPrivateCapFromDelegator()` and `getPublicCapFromDelegator()` which use the
/// `Delegator` retrieval path.
///
access(Capabilities) view fun getCapability(controllerID: UInt64, type: Type): Capability? {
access(Child) view fun getCapability(controllerID: UInt64, type: Type): Capability? {
let child = self.childCap.borrow() ?? panic("failed to borrow child account")

let f = self.factory.borrow()!.getFactory(type)
Expand All @@ -611,7 +616,7 @@ access(all) contract HybridCustody {
/// Retrieves a private Capability from the Delegator or nil none is found of the given type. Useful for
/// arbitrary Capability retrieval
///
access(Capabilities) view fun getPrivateCapFromDelegator(type: Type): Capability? {
access(Child) view fun getPrivateCapFromDelegator(type: Type): Capability? {
if let d = self.delegator.borrow() {
return d.getPrivateCapability(type)
}
Expand Down Expand Up @@ -753,6 +758,15 @@ access(all) contract HybridCustody {
return self.factory.check() ? self.factory.borrow() : nil
}

access(all) view fun getControllerIDForType(type: Type, forPath: StoragePath): UInt64? {
let child = self.childCap.borrow()
if child == nil {
return nil
}

return child!.getControllerIDForType(type: type, forPath: forPath)
}

// TODO: burnCallback and ResourceDestoryed event
// destroy () {
// destroy <- self.resources
Expand Down Expand Up @@ -871,7 +885,7 @@ access(all) contract HybridCustody {
let childAccountPrivatePath = PrivatePath(identifier: identifier)!

acct.storage.save(<-childAcct, to: childAccountStorage)
let delegatorCap = acct.capabilities.storage.issue<&{AccountPrivate, AccountPublic, ViewResolver.Resolver}>(childAccountStorage)
let delegatorCap = acct.capabilities.storage.issue<auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}>(childAccountStorage)
assert(delegatorCap.check(), message: "Delegator capability check failed")

acct.inbox.publish(delegatorCap, name: identifier, recipient: parentAddress)
Expand All @@ -898,7 +912,7 @@ access(all) contract HybridCustody {
/// Returns a reference to the encapsulated account object
///
access(contract) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account {
return self.acct.borrow()!
return self.acct.borrow() ?? panic("unable to borrow Account Capability")
}

/// Returns the addresses of all associated parents pending and active
Expand Down Expand Up @@ -1002,14 +1016,19 @@ access(all) contract HybridCustody {

let acct = self.borrowAccount()
// Unlink existing owner's Capability if owner exists
if self.acctOwner != nil {
acct.capabilities.account.getController(byCapabilityID: self.acct.id)?.delete()
}

// TODO: is this still needed? Sealing the account should revoke and rotate all
// existing account links which should mean this one is already gone.
// if self.acctOwner != nil {
// acct.capabilities.account.getController(byCapabilityID: self.acct.id)?.delete()
// }
// Link a Capability for the new owner, retrieve & publish
let identifier = HybridCustody.getOwnerIdentifier(to)
let cap = acct.capabilities.storage.issue<&{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath)
let cap = acct.capabilities.storage.issue<auth(Owner) &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath)

// make sure we can borrow the newly issued owned account
cap.borrow()!.borrowAccount()

acct.inbox.publish(cap, name: identifier, recipient: to)

self.pendingOwner = to
Expand Down Expand Up @@ -1043,11 +1062,7 @@ access(all) contract HybridCustody {
let acct = self.borrowAccount()

// Find all active AuthAccount capabilities so they can be removed after we make the new auth account cap
let idsToDestroy: [UInt64] = []
acct.capabilities.account.forEachController(fun(con: &AccountCapabilityController): Bool {
idsToDestroy.append(con.capabilityID)
return true
})
let controllersToDestroy = acct.capabilities.account.getControllers()

// Link a new AuthAccount Capability
let acctCap = acct.capabilities.account.issue<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>()
Expand All @@ -1057,9 +1072,11 @@ access(all) contract HybridCustody {

// cleanup, remove all previously found paths. We had to do it in this order because we will be unlinking
// the existing path which will cause a deference issue with the originally borrowed auth account
for id in idsToDestroy {
newAcct.capabilities.account.getController(byCapabilityID: id)?.delete()
for con in controllersToDestroy {
newAcct.capabilities.account.getController(byCapabilityID: con.capabilityID)?.delete()
}

assert(self.acct.check(), message: "new auth account capability is not valid")
}

/// Revokes all keys on an account, unlinks all currently active AuthAccount capabilities, then makes a new one
Expand Down Expand Up @@ -1145,6 +1162,21 @@ access(all) contract HybridCustody {
self.display = d
}

access(all) view fun getControllerIDForType(type: Type, forPath: StoragePath): UInt64? {
let acct = self.acct.borrow()
if acct == nil {
return nil
}

for c in acct!.capabilities.storage.getControllers(forPath: forPath) {
if c.borrowType.isSubtype(of: type) {
return c.capabilityID
}
}

return nil
}

init(
_ acct: Capability<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>
) {
Expand Down
2 changes: 1 addition & 1 deletion contracts/factories/FTProviderFactory.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ access(all) contract FTProviderFactory {
return nil
}

return con.capability as! Capability<&auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>
return con.capability as! Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>
}

return nil
Expand Down
6 changes: 6 additions & 0 deletions contracts/standard/ExampleToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,12 @@ access(all) contract ExampleToken: FungibleToken {
return nil
}

// EMULATOR ONLY, anyone can mint tokens
access(all) fun createNewMinter(allowedAmount: UFix64): @Minter {
emit MinterCreated(allowedAmount: allowedAmount)
return <-create Minter(allowedAmount: allowedAmount)
}

init() {
self.totalSupply = 1000.0

Expand Down
6 changes: 3 additions & 3 deletions scripts/example-nft-2/get_ids.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "ExampleNFT2"

pub fun main(addr: Address): [UInt64] {
let acct = getAuthAccount(addr)
let collection = acct.borrow<&ExampleNFT2.Collection>(from: ExampleNFT2.CollectionStoragePath)
access(all) fun main(addr: Address): [UInt64] {
let acct = getAuthAccount<auth(Storage) &Account>(addr)
let collection = acct.storage.borrow<&ExampleNFT2.Collection>(from: ExampleNFT2.CollectionStoragePath)
?? panic("collection not found")
return collection.getIDs()
}
6 changes: 3 additions & 3 deletions scripts/example-nft/get_ids.cdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import "ExampleNFT"

pub fun main(addr: Address): [UInt64] {
let acct = getAuthAccount(addr)
let collection = acct.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath)
access(all) fun main(addr: Address): [UInt64] {
let acct = getAuthAccount<auth(Storage) &Account>(addr)
let collection = acct.storage.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath)
?? panic("collection not found")
return collection.getIDs()
}
10 changes: 7 additions & 3 deletions scripts/example-token/get_balance.cdc
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import "FungibleToken"
import "ExampleToken"
import "FungibleTokenMetadataViews"

pub fun main(account: Address): UFix64 {
access(all) fun main(account: Address): UFix64 {
let acct = getAccount(account)
let vaultRef = acct.getCapability(ExampleToken.VaultPublicPath)
.borrow<&ExampleToken.Vault{FungibleToken.Balance}>()

let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
?? panic("Could not get the vault data view for ExampleToken")

let vaultRef = acct.capabilities.get<&{FungibleToken.Balance}>(vaultData.metadataPath)!.borrow()
?? panic("Could not borrow Balance reference to the Vault")

return vaultRef.balance
Expand Down
12 changes: 6 additions & 6 deletions scripts/factory/get_ft_balance_from_factory.cdc
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import "FungibleToken"
import "ExampleToken"
import "FungibleTokenMetadataViews"

import "FTBalanceFactory"

pub fun main(addr: Address) {
let acct = getAuthAccount(addr)
let ref = &acct as &AuthAccount

access(all) fun main(addr: Address) {
let acct = getAuthAccount<auth(Capabilities) &Account>(addr)
let factory = FTBalanceFactory.Factory()

let provider = factory.getCapability(acct: ref, path: ExampleToken.VaultPublicPath) as! Capability<&{FungibleToken.Balance}>
let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
?? panic("Could not get the vault data view for ExampleToken")
factory.getPublicCapability(acct: acct, path: vaultData.metadataPath)! as! Capability<&{FungibleToken.Balance}>
}
24 changes: 18 additions & 6 deletions scripts/factory/get_ft_provider_from_factory.cdc
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import "FungibleToken"
import "ExampleToken"
import "FungibleTokenMetadataViews"

import "FTProviderFactory"

pub fun main(addr: Address) {
let acct = getAuthAccount(addr)
let ref = &acct as &AuthAccount

access(all) fun main(addr: Address) {
let acct = getAuthAccount<auth(Capabilities) &Account>(addr)
let factory = FTProviderFactory.Factory()
let providerPath = /private/exampleTokenProvider

let provider = factory.getCapability(acct: ref, path: providerPath) as! Capability<&{FungibleToken.Provider}>
let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
?? panic("Could not get the vault data view for ExampleToken")

acct.capabilities.storage.issue<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>(vaultData.storagePath)

let controllers = acct.capabilities.storage.getControllers(forPath: vaultData.storagePath)
for c in controllers {
if c.borrowType.isSubtype(of: Type<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>()) {
factory.getCapability(acct: acct, controllerID: c.capabilityID)! as! Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>
return
}
}

panic("should not reach this point")
}
26 changes: 18 additions & 8 deletions scripts/factory/get_nft_provider_from_factory_allowed.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,30 @@ import "NFTProviderFactory"
/// Determines if ExampleNFT Provider both has a Factory at the ruleAddr and is allowed by the AllowlistFilter found in
/// the ruleAddr account.
///
pub fun main(filterFactoryAddr: Address, providerAddr: Address): Bool {
let ruleAcct = getAuthAccount(filterFactoryAddr)
let providerAcct = &getAuthAccount(providerAddr) as! &AuthAccount
access(all) fun main(filterFactoryAddr: Address, providerAddr: Address): Bool {
let ruleAcct = getAuthAccount<auth(Storage, Capabilities) &Account>(filterFactoryAddr)
let providerAcct = getAuthAccount<auth(Storage, Capabilities) &Account>(providerAddr)

let factoryManager = ruleAcct.borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath)
let factoryManager = ruleAcct.storage.borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath)
?? panic("Problem borrowing CapabilityFactory Manager")
let factory = factoryManager.getFactory(Type<&{NonFungibleToken.Provider}>())
let factory = factoryManager.getFactory(Type<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider}>())
?? panic("No factory for NFT Provider found")

let d = ExampleNFT.resolveView(Type<MetadataViews.NFTCollectionData>())! as! MetadataViews.NFTCollectionData
let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>())! as! MetadataViews.NFTCollectionData

let provider = factory.getCapability(acct: providerAcct, path: d.providerPath) as! Capability<&{NonFungibleToken.Provider}>
var controllerID: UInt64? = nil
for c in providerAcct.capabilities.storage.getControllers(forPath: d.storagePath) {
if c.borrowType.isSubtype(of: Type<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider}>()) {
controllerID = c.capabilityID
break
}
}

let filter = ruleAcct.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath)
assert(controllerID != nil, message: "could not find existing provider capcon")

let provider = factory.getCapability(acct: providerAcct, controllerID: controllerID!)! as! Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider}>

let filter = ruleAcct.storage.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath)
?? panic("Problem borrowing CapabilityFilter AllowlistFilter")

return filter.allowed(cap: provider)
Expand Down

This file was deleted.

11 changes: 11 additions & 0 deletions scripts/hybrid-custody/get_account_cap_con_id.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import "HybridCustody"

access(all) fun main(addr: Address): UInt64? {
let acct: auth(Capabilities) &Account = getAuthAccount<auth(Capabilities) &Account>(addr)
let controllers = acct.capabilities.account.getControllers()
if controllers.length == 0 {
return nil
}

return controllers[0].capabilityID
}
Loading

0 comments on commit 1301040

Please sign in to comment.