Skip to content

Commit

Permalink
add missing tests, make a new _borrowAccount method for internal use …
Browse files Browse the repository at this point in the history
…and give borrowAccount the Owner entitlement
  • Loading branch information
austinkline committed Apr 3, 2024
1 parent b075ab0 commit 96fbbbf
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 11 deletions.
25 changes: 15 additions & 10 deletions contracts/HybridCustody.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ access(all) contract HybridCustody {
/// An OwnedAccount shares the BorrowableAccount capability to itelf with ChildAccount resources
///
access(all) resource interface BorrowableAccount {
access(contract) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account
access(contract) view fun _borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account
access(all) view fun check(): Bool
}

Expand Down Expand Up @@ -182,7 +182,7 @@ access(all) contract HybridCustody {
access(all) view fun getParentAddresses(): [Address]

/// Borrows this OwnedAccount's AuthAccount Capability
access(contract) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account
access(Owner) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account

/// Returns the current owner of this account, if there is one
access(all) view fun getOwner(): Address?
Expand Down Expand Up @@ -245,7 +245,7 @@ access(all) contract HybridCustody {
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}?
access(Manage) fun borrowOwnedAccount(addr: Address): auth(Owner) &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}?
access(Manage) fun removeOwned(addr: Address)
access(Manage) fun setManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?, childAddress: Address) {
pre {
Expand Down Expand Up @@ -418,7 +418,7 @@ access(all) contract HybridCustody {

/// Returns a reference to an owned account
///
access(Manage) fun borrowOwnedAccount(addr: Address): &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}? {
access(Manage) fun borrowOwnedAccount(addr: Address): auth(Owner) &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}? {
if let cap = self.ownedAccounts[addr] {
return cap.borrow()
}
Expand Down Expand Up @@ -612,7 +612,7 @@ access(all) contract HybridCustody {
return nil
}

let acct = child.borrowAccount()
let acct = child._borrowAccount()
let tmp = f!.getCapability(acct: acct, controllerID: controllerID)
if tmp == nil {
return nil
Expand Down Expand Up @@ -660,7 +660,7 @@ access(all) contract HybridCustody {
return nil
}

let acct = child.borrowAccount()
let acct = child._borrowAccount()
return f!.getPublicCapability(acct: acct, path: path)
}

Expand All @@ -673,7 +673,7 @@ access(all) contract HybridCustody {
/// Sets the child account as redeemed by the given Address
///
access(contract) fun setRedeemed(_ addr: Address) {
let acct = self.childCap.borrow()!.borrowAccount()
let acct = self.childCap.borrow()!._borrowAccount()
if let o = acct.storage.borrow<&OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) {
o.setRedeemed(addr)
}
Expand All @@ -683,7 +683,7 @@ access(all) contract HybridCustody {
///
access(Owner) fun borrowCapabilityDelegator(): auth(Capabilities) &CapabilityDelegator.Delegator? {
let path = HybridCustody.getCapabilityDelegatorIdentifier(self.parent)
return self.childCap.borrow()!.borrowAccount().storage.borrow<auth(Capabilities) &CapabilityDelegator.Delegator>(
return self.childCap.borrow()!._borrowAccount().storage.borrow<auth(Capabilities) &CapabilityDelegator.Delegator>(
from: StoragePath(identifier: path)!
)
}
Expand Down Expand Up @@ -731,7 +731,7 @@ access(all) contract HybridCustody {
return
}

let acct = child.borrowAccount()
let acct = child._borrowAccount()
if let ownedAcct = acct.storage.borrow<auth(Owner) &OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) {
ownedAcct.removeParent(parent: parent)
}
Expand Down Expand Up @@ -930,10 +930,15 @@ access(all) contract HybridCustody {

/// Returns a reference to the encapsulated account object
///
access(contract) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account {
access(Owner) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account {
return self.acct.borrow() ?? panic("unable to borrow Account Capability")
}

// Used internally so that child account resources are able to borrow their underlying Account reference
access(contract) view fun _borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account {
return self.borrowAccount()
}

/// Returns the addresses of all associated parents pending and active
///
access(all) view fun getParentAddresses(): [Address] {
Expand Down
11 changes: 11 additions & 0 deletions scripts/hybrid-custody/borrow_owned_account.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import "HybridCustody"

access(all) fun main(owner: Address, child: Address): Address {
let m = getAuthAccount<auth(Storage) &Account>(owner).storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
?? panic("manager could not be borrowed")
let ownedAcct = m.borrowOwnedAccount(addr: child)
?? panic("could not borrow owned account")

let acct = ownedAcct.borrowAccount()
return acct.address
}
13 changes: 13 additions & 0 deletions scripts/hybrid-custody/get_account_public_address.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "HybridCustody"

access(all) fun main(parent: Address, child: Address): Address {
let cap = getAccount(parent).capabilities.get<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath)
?? panic("manager not found")
let manager = cap.borrow()
?? panic("unable to borrow manager")

let acct = manager.borrowAccountPublic(addr: child)
?? panic("child account not found")

return acct.getAddress()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ access(all) fun main(parent: Address, child: Address): String {
?? panic("manager not found")

let c = m.borrowAccount(addr: child) ?? panic("child not found")

let tmp = c.resolveView(Type<MetadataViews.Display>()) ?? panic("unable to resolve metadata display")

let d = c.resolveView(Type<MetadataViews.Display>())! as! MetadataViews.Display
let d = tmp as! MetadataViews.Display
return d.name
}
148 changes: 148 additions & 0 deletions test/HybridCustody_tests.cdc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Test
import "test_helpers.cdc"
import "HybridCustody"

access(all) let adminAccount = Test.getAccount(0x0000000000000007)
access(all) let accounts: {String: Test.TestAccount} = {}
Expand Down Expand Up @@ -964,6 +965,148 @@ fun testGetChildAccountCapabilityFilterAndFactory() {
scriptExecutor("test/can_get_child_factory_and_filter_caps.cdc", [child.address, parent.address])
}

access(all)
fun testSetCapabilityFactoryForParent() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)

let newFactory = Test.createAccount()
setupEmptyFactory(newFactory)

txExecutor("hybrid-custody/set_capability_factory_for_parent.cdc", [child], [parent.address, newFactory.address], nil)

expectScriptFailure(
"hybrid-custody/get_nft_provider_capability.cdc",
[parent.address, child.address],
"capability not found"
)
}

access(all)
fun testSetCapabilityFilterForParent() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)

let newFilter = Test.createAccount()
setupFilter(newFilter, FilterKindAllowList)

txExecutor("hybrid-custody/set_capability_filter_for_parent.cdc", [child], [parent.address, newFilter.address], nil)

expectScriptFailure(
"hybrid-custody/get_nft_provider_capability.cdc",
[parent.address, child.address],
"capability not found"
)
}

access(all)
fun testSetChildAccountDisplay_toNil() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)
txExecutor("hybrid-custody/metadata/set_child_account_display_to_nil.cdc", [parent], [child.address], nil)

expectScriptFailure(
"hybrid-custody/metadata/resolve_child_display_name.cdc",
[parent.address, child.address],
"unable to resolve metadata display"
)
}

access(all)
fun testRemoveChild_invalidChildAccountCapability() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)

txExecutor("hybrid-custody/unlink_child_capability.cdc", [child], [parent.address], nil)

txExecutor("hybrid-custody/remove_child_account.cdc", [parent], [child.address], nil)

let e = Test.eventsOfType(Type<HybridCustody.AccountUpdated>()).removeLast() as! HybridCustody.AccountUpdated
Test.assert(e.child == child.address, message: "unexpected AccountUpdated value")
}

access(all)
fun testBorrowAccount_nilCapability() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)
txExecutor("hybrid-custody/unlink_child_capability.cdc", [child], [parent.address], nil)

expectScriptFailure(
"hybrid-custody/metadata/resolve_child_display_name.cdc",
[parent.address, child.address],
"child not found"
)
}

access(all)
fun testBorrowAccountPublic() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)

let res = scriptExecutor("hybrid-custody/get_account_public_address.cdc", [parent.address, child.address])! as! Address
Test.assertEqual(child.address, res)

txExecutor("hybrid-custody/remove_child_account.cdc", [parent], [child.address], nil)

expectScriptFailure("hybrid-custody/get_account_public_address.cdc", [parent.address, child.address], "child account not found")
}

access(all) fun testBorrowOwnedAccount() {
let child = Test.createAccount()
let parent = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: parent)

let owner = Test.createAccount()
setupAccountManager(owner)

txExecutor("hybrid-custody/transfer_ownership.cdc", [child], [owner.address], nil)
Test.assert(getPendingOwner(child: child)! == owner.address, message: "child account pending ownership was not updated correctly")

txExecutor("hybrid-custody/accept_ownership.cdc", [owner], [child.address, nil, nil], nil)

let res = scriptExecutor("hybrid-custody/borrow_owned_account.cdc", [owner.address, child.address])! as! Address
Test.assertEqual(child.address, res)

expectScriptFailure("hybrid-custody/borrow_owned_account.cdc", [owner.address, parent.address], "could not borrow owned account")
}

access(all)
fun testRemoveOwned() {
let child = Test.createAccount()
let owner = Test.createAccount()

setupChildAndParent_FilterKindAll(child: child, parent: Test.createAccount())
setupAccountManager(owner)

txExecutor("hybrid-custody/transfer_ownership.cdc", [child], [owner.address], nil)
Test.assert(getPendingOwner(child: child)! == owner.address, message: "child account pending ownership was not updated correctly")

txExecutor("hybrid-custody/accept_ownership.cdc", [owner], [child.address, nil, nil], nil)

// remove the owned account
txExecutor("hybrid-custody/remove_owned_account.cdc", [owner], [child.address], nil)

let sealEvent = Test.eventsOfType(Type<HybridCustody.AccountSealed>()).removeLast() as! HybridCustody.AccountSealed
Test.assertEqual(child.address, sealEvent.address)

let ownershipEvent = Test.eventsOfType(Type<HybridCustody.OwnershipUpdated>()).removeLast() as! HybridCustody.OwnershipUpdated
Test.assertEqual(owner.address, ownershipEvent.previousOwner!)
Test.assertEqual(nil, ownershipEvent.owner)
}

// --------------- End Test Cases ---------------

Expand Down Expand Up @@ -1018,6 +1161,11 @@ fun setupFactoryManager(_ acct: Test.TestAccount) {
txExecutor("factory/setup_nft_ft_manager.cdc", [acct], [], nil)
}

access(all)
fun setupEmptyFactory(_ acct: Test.TestAccount) {
txExecutor("factory/setup_empty_factory.cdc", [acct], [], nil)
}

access(all)
fun setupNFTCollection(_ acct: Test.TestAccount) {
txExecutor("example-nft/setup_full.cdc", [acct], [], nil)
Expand Down
25 changes: 25 additions & 0 deletions transactions/factory/setup_empty_factory.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import "NonFungibleToken"
import "FungibleToken"

import "CapabilityFactory"

transaction {
prepare(acct: auth(Storage, Capabilities) &Account) {
if acct.storage.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil {
let f <- CapabilityFactory.createFactoryManager()
acct.storage.save(<-f, to: CapabilityFactory.StoragePath)
}

if acct.capabilities.get<&{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath)?.check() != true {
acct.capabilities.unpublish(CapabilityFactory.PublicPath)

let cap = acct.capabilities.storage.issue<&{CapabilityFactory.Getter}>(CapabilityFactory.StoragePath)
acct.capabilities.publish(cap, at: CapabilityFactory.PublicPath)
}

assert(
acct.capabilities.get<&{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath)!.check(),
message: "CapabilityFactory is not setup properly"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "HybridCustody"

transaction(childAddress: Address) {
prepare(acct: auth(Storage) &Account) {
let m = acct.storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
?? panic("manager not found")

m.setChildAccountDisplay(address: childAddress, nil)
}
}
10 changes: 10 additions & 0 deletions transactions/hybrid-custody/remove_owned_account.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "HybridCustody"

transaction(addr: Address) {
prepare(acct: auth(Storage) &Account) {
let m = acct.storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
?? panic("manager not found")

m.removeOwned(addr: addr)
}
}
13 changes: 13 additions & 0 deletions transactions/hybrid-custody/set_capability_factory_for_parent.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "HybridCustody"
import "CapabilityFactory"

transaction(parent: Address, factoryAddress: Address) {
prepare(acct: auth(Storage) &Account) {
let cap = getAccount(factoryAddress).capabilities.get<&{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath)
?? panic("capability factory was nil")

let ownedAccount = acct.storage.borrow<auth(HybridCustody.Owner) &{HybridCustody.OwnedAccountPrivate}>(from: HybridCustody.OwnedAccountStoragePath)
?? panic("owned account not found")
ownedAccount.setCapabilityFactoryForParent(parent: parent, cap: cap)
}
}
13 changes: 13 additions & 0 deletions transactions/hybrid-custody/set_capability_filter_for_parent.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "HybridCustody"
import "CapabilityFilter"

transaction(parent: Address, factoryAddress: Address) {
prepare(acct: auth(Storage) &Account) {
let cap = getAccount(factoryAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath)
?? panic("capability filter was nil")

let ownedAccount = acct.storage.borrow<auth(HybridCustody.Owner) &{HybridCustody.OwnedAccountPrivate}>(from: HybridCustody.OwnedAccountStoragePath)
?? panic("owned account not found")
ownedAccount.setCapabilityFilterForParent(parent: parent, cap: cap)
}
}
10 changes: 10 additions & 0 deletions transactions/hybrid-custody/unlink_child_capability.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "HybridCustody"

transaction(parent: Address) {
prepare(acct: auth(Storage, Capabilities) &Account) {
let storagePath = StoragePath(identifier: HybridCustody.getChildAccountIdentifier(parent))!
for c in acct.capabilities.storage.getControllers(forPath: storagePath) {
c.delete()
}
}
}

0 comments on commit 96fbbbf

Please sign in to comment.