diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 1af3875..7fec93b 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -20,11 +20,9 @@ jobs: with: go-version: 1.18 - name: Install Flow CLI - run: bash -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v1.5.0 + run: bash -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/feature/stable-cadence/install.sh)" - name: Run tests run: sh ./test.sh - - name: Normalize coverage report filepaths - run : sh ./normalize_coverage_report.sh - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 env: diff --git a/.gitignore b/.gitignore index 84dd629..3c54981 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ coverage.lcov .idea *.pkey *.private -*.pem \ No newline at end of file +*.pem +node_modules/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index ee884a8..0f2df34 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "modules/flow-nft"] path = modules/flow-nft - url = https://github.com/Flowtyio/flow-nft.git -[submodule "flow-ft"] - path = flow-ft - url = https://github.com/Flowtyio/flow-ft.git + url = https://github.com/onflow/flow-nft.git [submodule "modules/flow-utils"] path = modules/flow-utils - url = https://github.com/Flowtyio/flow-utils.git + url = https://github.com/green-goo-dao/flow-utils.git +[submodule "modules/flow-ft"] + path = modules/flow-ft + url = https://github.com/onflow/flow-ft.git diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..5ffe41a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,11 @@ +ignore: + - "contracts/standard/ExampleNFT.cdc" + - "contracts/standard/ExampleNFT2.cdc" + - "contracts/standard/ExampleToken.cdc" + - "contracts/factories/FTBalanceFactory.cdc" + - "contracts/factories/FTProviderFactory.cdc" + - "contracts/factories/FTReceiverFactory.cdc" + - "contracts/factories/NFTCollectionPublicFactory.cdc" + - "contracts/factories/NFTProviderFactory.cdc" + - "contracts/factories/FTVaultFactory.cdc" + - "contracts/factories/NFTCollectionFactory.cdc" \ No newline at end of file diff --git a/contracts/CapabilityDelegator.cdc b/contracts/CapabilityDelegator.cdc index f342aaf..796002b 100644 --- a/contracts/CapabilityDelegator.cdc +++ b/contracts/CapabilityDelegator.cdc @@ -7,48 +7,51 @@ /// private `Delegator` can only be borrowed from the child account when you have access to the full `ChildAccount` /// resource. /// -pub contract CapabilityDelegator { +access(all) contract CapabilityDelegator { /* --- Canonical Paths --- */ // - pub let StoragePath: StoragePath - pub let PrivatePath: PrivatePath - pub let PublicPath: PublicPath + access(all) let StoragePath: StoragePath + access(all) let PublicPath: PublicPath + + access(all) entitlement Get + access(all) entitlement Add + access(all) entitlement Delete /* --- Events --- */ // - pub event DelegatorCreated(id: UInt64) - pub event DelegatorUpdated(id: UInt64, capabilityType: Type, isPublic: Bool, active: Bool) + access(all) event DelegatorCreated(id: UInt64) + access(all) event DelegatorUpdated(id: UInt64, capabilityType: Type, isPublic: Bool, active: Bool) /// Private interface for Capability retrieval /// - pub resource interface GetterPrivate { - pub fun getPrivateCapability(_ type: Type): Capability? { + access(all) resource interface GetterPrivate { + access(Get) view fun getPrivateCapability(_ type: Type): Capability? { post { result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type" } } - pub fun findFirstPrivateType(_ type: Type): Type? - pub fun getAllPrivate(): [Capability] + access(all) view fun findFirstPrivateType(_ type: Type): Type? + access(Get) fun getAllPrivate(): [Capability] } /// Exposes public Capability retrieval /// - pub resource interface GetterPublic { - pub fun getPublicCapability(_ type: Type): Capability? { + access(all) resource interface GetterPublic { + access(all) view fun getPublicCapability(_ type: Type): Capability? { post { - result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type " + result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type" } } - pub fun findFirstPublicType(_ type: Type): Type? - pub fun getAllPublic(): [Capability] + access(all) view fun findFirstPublicType(_ type: Type): Type? + access(all) view fun getAllPublic(): [Capability] } /// This Delegator is used to store Capabilities, partitioned by public and private access with corresponding /// GetterPublic and GetterPrivate conformances.AccountCapabilityController /// - pub resource Delegator: GetterPublic, GetterPrivate { + access(all) resource Delegator: GetterPublic, GetterPrivate { access(self) let privateCapabilities: {Type: Capability} access(self) let publicCapabilities: {Type: Capability} @@ -56,7 +59,7 @@ pub contract CapabilityDelegator { // /// Returns the public Capability of the given Type if it exists /// - pub fun getPublicCapability(_ type: Type): Capability? { + access(all) view fun getPublicCapability(_ type: Type): Capability? { return self.publicCapabilities[type] } @@ -66,7 +69,7 @@ pub contract CapabilityDelegator { /// @param type: Type of the Capability to retrieve /// @return Capability of the given Type if it exists, nil otherwise /// - pub fun getPrivateCapability(_ type: Type): Capability? { + access(Get) view fun getPrivateCapability(_ type: Type): Capability? { return self.privateCapabilities[type] } @@ -74,7 +77,7 @@ pub contract CapabilityDelegator { /// /// @return List of all public Capabilities /// - pub fun getAllPublic(): [Capability] { + access(all) view fun getAllPublic(): [Capability] { return self.publicCapabilities.values } @@ -82,7 +85,7 @@ pub contract CapabilityDelegator { /// /// @return List of all private Capabilities /// - pub fun getAllPrivate(): [Capability] { + access(Get) fun getAllPrivate(): [Capability] { return self.privateCapabilities.values } @@ -91,7 +94,7 @@ pub contract CapabilityDelegator { /// @param type: Type to check for subtypes /// @return First public Type that is a subtype of the given Type, nil otherwise /// - pub fun findFirstPublicType(_ type: Type): Type? { + access(all) view fun findFirstPublicType(_ type: Type): Type? { for t in self.publicCapabilities.keys { if t.isSubtype(of: type) { return t @@ -106,7 +109,7 @@ pub contract CapabilityDelegator { /// @param type: Type to check for subtypes /// @return First private Type that is a subtype of the given Type, nil otherwise /// - pub fun findFirstPrivateType(_ type: Type): Type? { + access(all) view fun findFirstPrivateType(_ type: Type): Type? { for t in self.privateCapabilities.keys { if t.isSubtype(of: type) { return t @@ -122,7 +125,7 @@ pub contract CapabilityDelegator { /// @param cap: Capability to add /// @param isPublic: Whether the Capability should be public or private /// - pub fun addCapability(cap: Capability, isPublic: Bool) { + access(Add) fun addCapability(cap: Capability, isPublic: Bool) { pre { cap.check<&AnyResource>(): "Invalid Capability provided" } @@ -138,7 +141,7 @@ pub contract CapabilityDelegator { /// /// @param cap: Capability to remove /// - pub fun removeCapability(cap: Capability) { + access(Delete) fun removeCapability(cap: Capability) { if let removedPublic = self.publicCapabilities.remove(key: cap.getType()) { emit DelegatorUpdated(id: self.uuid, capabilityType: cap.getType(), isPublic: true, active: false) } @@ -158,7 +161,7 @@ pub contract CapabilityDelegator { /// /// @return Newly created Delegator /// - pub fun createDelegator(): @Delegator { + access(all) fun createDelegator(): @Delegator { let delegator <- create Delegator() emit DelegatorCreated(id: delegator.uuid) return <- delegator @@ -167,7 +170,6 @@ pub contract CapabilityDelegator { init() { let identifier = "CapabilityDelegator_".concat(self.account.address.toString()) self.StoragePath = StoragePath(identifier: identifier)! - self.PrivatePath = PrivatePath(identifier: identifier)! self.PublicPath = PublicPath(identifier: identifier)! } } diff --git a/contracts/CapabilityFactory.cdc b/contracts/CapabilityFactory.cdc index ee777f4..f4b62ac 100644 --- a/contracts/CapabilityFactory.cdc +++ b/contracts/CapabilityFactory.cdc @@ -13,37 +13,40 @@ /// Capabilities is critical to the use case of Hybrid Custody. It's advised to use Factories sparingly and only for /// cases where Capabilities must be castable by the caller. /// -pub contract CapabilityFactory { +access(all) contract CapabilityFactory { - pub let StoragePath: StoragePath - pub let PrivatePath: PrivatePath - pub let PublicPath: PublicPath + access(all) let StoragePath: StoragePath + access(all) let PublicPath: PublicPath + + access(all) entitlement Add + access(all) entitlement Delete /// Factory structures a common interface for Capability retrieval from a given account at a specified path /// - pub struct interface Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability + access(all) struct interface Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? } /// Getter defines an interface for retrieval of a Factory if contained within the implementing resource /// - pub resource interface Getter { - pub fun getSupportedTypes(): [Type] - pub fun getFactory(_ t: Type): {CapabilityFactory.Factory}? + access(all) resource interface Getter { + access(all) view fun getSupportedTypes(): [Type] + access(all) view fun getFactory(_ t: Type): {CapabilityFactory.Factory}? } /// Manager is a resource that contains Factories and implements the Getter interface for retrieval of contained /// Factories /// - pub resource Manager: Getter { + access(all) resource Manager: Getter { /// Mapping of Factories indexed on Type of Capability they retrieve - pub let factories: {Type: {CapabilityFactory.Factory}} + access(all) let factories: {Type: {CapabilityFactory.Factory}} /// Retrieves a list of Types supported by contained Factories /// /// @return List of Types supported by the Manager /// - pub fun getSupportedTypes(): [Type] { + access(all) view fun getSupportedTypes(): [Type] { return self.factories.keys } @@ -51,7 +54,7 @@ pub contract CapabilityFactory { /// /// @param t: Type the Factory is indexed on /// - pub fun getFactory(_ t: Type): {CapabilityFactory.Factory}? { + access(all) view fun getFactory(_ t: Type): {CapabilityFactory.Factory}? { return self.factories[t] } @@ -60,7 +63,7 @@ pub contract CapabilityFactory { /// @param t: Type of Capability the Factory retrieves /// @param f: Factory to add /// - pub fun addFactory(_ t: Type, _ f: {CapabilityFactory.Factory}) { + access(Add) fun addFactory(_ t: Type, _ f: {CapabilityFactory.Factory}) { pre { !self.factories.containsKey(t): "Factory of given type already exists" } @@ -72,7 +75,7 @@ pub contract CapabilityFactory { /// @param t: Type of Capability the Factory retrieves /// @param f: Factory to replace existing Factory /// - pub fun updateFactory(_ t: Type, _ f: {CapabilityFactory.Factory}) { + access(Add) fun updateFactory(_ t: Type, _ f: {CapabilityFactory.Factory}) { self.factories[t] = f } @@ -80,7 +83,7 @@ pub contract CapabilityFactory { /// /// @param t: Type the Factory is indexed on /// - pub fun removeFactory(_ t: Type): {CapabilityFactory.Factory}? { + access(Delete) fun removeFactory(_ t: Type): {CapabilityFactory.Factory}? { return self.factories.remove(key: t) } @@ -92,14 +95,13 @@ pub contract CapabilityFactory { /// Creates a Manager resource /// /// @return Manager resource - pub fun createFactoryManager(): @Manager { + access(all) fun createFactoryManager(): @Manager { return <- create Manager() } init() { let identifier = "CapabilityFactory_".concat(self.account.address.toString()) self.StoragePath = StoragePath(identifier: identifier)! - self.PrivatePath = PrivatePath(identifier: identifier)! self.PublicPath = PublicPath(identifier: identifier)! } } \ No newline at end of file diff --git a/contracts/CapabilityFilter.cdc b/contracts/CapabilityFilter.cdc index 7a76509..efd8487 100644 --- a/contracts/CapabilityFilter.cdc +++ b/contracts/CapabilityFilter.cdc @@ -6,29 +6,31 @@ /// - `AllowlistFilter` - A filter which contains a mapping of allowed Types /// - `AllowAllFilter` - A passthrough, all requested capabilities are allowed /// -pub contract CapabilityFilter { +access(all) contract CapabilityFilter { /* --- Canonical Paths --- */ // - pub let StoragePath: StoragePath - pub let PublicPath: PublicPath - pub let PrivatePath: PrivatePath + access(all) let StoragePath: StoragePath + access(all) let PublicPath: PublicPath + + access(all) entitlement Add + access(all) entitlement Delete /* --- Events --- */ // - pub event FilterUpdated(id: UInt64, filterType: Type, type: Type, active: Bool) + access(all) event FilterUpdated(id: UInt64, filterType: Type, type: Type, active: Bool) /// `Filter` is a simple interface with methods to determine if a Capability is allowed and retrieve details about /// the Filter itself /// - pub resource interface Filter { - pub fun allowed(cap: Capability): Bool - pub fun getDetails(): AnyStruct + access(all) resource interface Filter { + access(all) view fun allowed(cap: Capability): Bool + access(all) view fun getDetails(): AnyStruct } /// `DenylistFilter` is a `Filter` which contains a mapping of denied Types /// - pub resource DenylistFilter: Filter { + access(all) resource DenylistFilter: Filter { /// Represents the underlying types which should not ever be returned by a RestrictedChildAccount. The filter /// will borrow a requested capability, and make sure that the type it gets back is not in the list of denied @@ -39,7 +41,7 @@ pub contract CapabilityFilter { /// /// @param type: The type to add to the denied types mapping /// - pub fun addType(_ type: Type) { + access(Add) fun addType(_ type: Type) { self.deniedTypes.insert(key: type, true) emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: true) } @@ -48,7 +50,7 @@ pub contract CapabilityFilter { /// /// @param type: The type to remove from the denied types mapping /// - pub fun removeType(_ type: Type) { + access(Delete) fun removeType(_ type: Type) { if let removed = self.deniedTypes.remove(key: type) { emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: false) } @@ -56,7 +58,7 @@ pub contract CapabilityFilter { /// Removes all types from the mapping of denied types /// - pub fun removeAllTypes() { + access(Delete) fun removeAllTypes() { for type in self.deniedTypes.keys { self.removeType(type) } @@ -67,7 +69,7 @@ pub contract CapabilityFilter { /// @param cap: The capability to check /// @return: true if the capability is allowed, false otherwise /// - pub fun allowed(cap: Capability): Bool { + access(all) view fun allowed(cap: Capability): Bool { if let item = cap.borrow<&AnyResource>() { return !self.deniedTypes.containsKey(item.getType()) } @@ -80,7 +82,7 @@ pub contract CapabilityFilter { /// @return A struct containing details about this filter including this Filter's Type indexed on the `type` /// key as well as types denied indexed on the `deniedTypes` key /// - pub fun getDetails(): AnyStruct { + access(all) view fun getDetails(): AnyStruct { return { "type": self.getType(), "deniedTypes": self.deniedTypes.keys @@ -94,7 +96,7 @@ pub contract CapabilityFilter { /// `AllowlistFilter` is a `Filter` which contains a mapping of allowed Types /// - pub resource AllowlistFilter: Filter { + access(all) resource AllowlistFilter: Filter { // allowedTypes // Represents the set of underlying types which are allowed to be // returned by a RestrictedChildAccount. The filter will borrow @@ -106,7 +108,7 @@ pub contract CapabilityFilter { /// /// @param type: The type to add to the allowed types mapping /// - pub fun addType(_ type: Type) { + access(Add) fun addType(_ type: Type) { self.allowedTypes.insert(key: type, true) emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: true) } @@ -115,7 +117,7 @@ pub contract CapabilityFilter { /// /// @param type: The type to remove from the denied types mapping /// - pub fun removeType(_ type: Type) { + access(Delete) fun removeType(_ type: Type) { if let removed = self.allowedTypes.remove(key: type) { emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: false) } @@ -123,7 +125,7 @@ pub contract CapabilityFilter { /// Removes all types from the mapping of denied types /// - pub fun removeAllTypes() { + access(Delete) fun removeAllTypes() { for type in self.allowedTypes.keys { self.removeType(type) } @@ -134,7 +136,7 @@ pub contract CapabilityFilter { /// @param cap: The capability to check /// @return: true if the capability is allowed, false otherwise /// - pub fun allowed(cap: Capability): Bool { + access(all) view fun allowed(cap: Capability): Bool { if let item = cap.borrow<&AnyResource>() { return self.allowedTypes.containsKey(item.getType()) } @@ -147,7 +149,7 @@ pub contract CapabilityFilter { /// @return A struct containing details about this filter including this Filter's Type indexed on the `type` /// key as well as types allowed indexed on the `allowedTypes` key /// - pub fun getDetails(): AnyStruct { + access(all) view fun getDetails(): AnyStruct { return { "type": self.getType(), "allowedTypes": self.allowedTypes.keys @@ -161,13 +163,13 @@ pub contract CapabilityFilter { /// AllowAllFilter is a passthrough, all requested capabilities are allowed /// - pub resource AllowAllFilter: Filter { + access(all) resource AllowAllFilter: Filter { /// Determines if a requested capability is allowed by this `Filter` /// /// @param cap: The capability to check /// @return: true since this filter is a passthrough /// - pub fun allowed(cap: Capability): Bool { + access(all) view fun allowed(cap: Capability): Bool { return true } @@ -176,7 +178,7 @@ pub contract CapabilityFilter { /// @return A struct containing details about this filter including this Filter's Type indexed on the `type` /// key /// - pub fun getDetails(): AnyStruct { + access(all) view fun getDetails(): AnyStruct { return { "type": self.getType() } @@ -188,7 +190,7 @@ pub contract CapabilityFilter { /// @param t: The type of `Filter` to create /// @return: A new instance of the given `Filter` type /// - pub fun create(_ t: Type): @AnyResource{Filter} { + access(all) fun createFilter(_ t: Type): @{Filter} { post { result.getType() == t } @@ -210,6 +212,5 @@ pub contract CapabilityFilter { self.StoragePath = StoragePath(identifier: identifier)! self.PublicPath = PublicPath(identifier: identifier)! - self.PrivatePath = PrivatePath(identifier: identifier)! } } diff --git a/contracts/HybridCustody.cdc b/contracts/HybridCustody.cdc index 1c5795d..7602830 100644 --- a/contracts/HybridCustody.cdc +++ b/contracts/HybridCustody.cdc @@ -1,5 +1,7 @@ // Third-party imports import "MetadataViews" +import "ViewResolver" +import "Burner" // HC-owned imports import "CapabilityFactory" @@ -27,39 +29,37 @@ import "CapabilityFilter" /// /// Repo reference: https://github.com/onflow/hybrid-custody /// -pub contract HybridCustody { +access(all) contract HybridCustody { + access(all) entitlement Owner + access(all) entitlement Child + access(all) entitlement Manage /* --- Canonical Paths --- */ // // Note: Paths for ChildAccount & Delegator are derived from the parent's address // - pub let OwnedAccountStoragePath: StoragePath - pub let OwnedAccountPublicPath: PublicPath - pub let OwnedAccountPrivatePath: PrivatePath + access(all) let OwnedAccountStoragePath: StoragePath + access(all) let OwnedAccountPublicPath: PublicPath - pub let ManagerStoragePath: StoragePath - pub let ManagerPublicPath: PublicPath - pub let ManagerPrivatePath: PrivatePath - - pub let LinkedAccountPrivatePath: PrivatePath - pub let BorrowableAccountPrivatePath: PrivatePath + access(all) let ManagerStoragePath: StoragePath + access(all) let ManagerPublicPath: PublicPath /* --- Events --- */ // /// Manager creation event - pub event CreatedManager(id: UInt64) + access(all) event CreatedManager(id: UInt64) /// OwnedAccount creation event - pub event CreatedOwnedAccount(id: UInt64, child: Address) + access(all) event CreatedOwnedAccount(id: UInt64, child: Address) /// ChildAccount added/removed from Manager /// active : added to Manager /// !active : removed from Manager - pub event AccountUpdated(id: UInt64?, child: Address, parent: Address, active: Bool) + access(all) event AccountUpdated(id: UInt64?, child: Address, parent: Address?, active: Bool) /// OwnedAccount added/removed or sealed /// active && owner != nil : added to Manager /// !active && owner == nil : removed from Manager - pub event OwnershipUpdated(id: UInt64, child: Address, previousOwner: Address?, owner: Address?, active: Bool) + access(all) event OwnershipUpdated(id: UInt64, child: Address, previousOwner: Address?, owner: Address?, active: Bool) /// ChildAccount ready to be redeemed by emitted pendingParent - pub event ChildAccountPublished( + access(all) event ChildAccountPublished( ownedAcctID: UInt64, childAcctID: UInt64, capDelegatorID: UInt64, @@ -70,49 +70,52 @@ pub contract HybridCustody { pendingParent: Address ) /// OwnedAccount granted ownership to a new address, publishing a Capability for the pendingOwner - pub event OwnershipGranted(ownedAcctID: UInt64, child: Address, previousOwner: Address?, pendingOwner: Address) + access(all) event OwnershipGranted(ownedAcctID: UInt64, child: Address, previousOwner: Address?, pendingOwner: Address) /// Account has been sealed - keys revoked, new AuthAccount Capability generated - pub event AccountSealed(id: UInt64, address: Address, parents: [Address]) + access(all) event AccountSealed(id: UInt64, address: Address, parents: [Address]) /// An OwnedAccount shares the BorrowableAccount capability to itelf with ChildAccount resources /// - pub resource interface BorrowableAccount { - access(contract) fun borrowAccount(): &AuthAccount - pub fun check(): Bool + access(all) resource interface BorrowableAccount { + access(contract) view fun _borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account + access(all) view fun check(): Bool } /// Public methods anyone can call on an OwnedAccount /// - pub resource interface OwnedAccountPublic { + access(all) resource interface OwnedAccountPublic { /// Returns the addresses of all parent accounts - pub fun getParentAddresses(): [Address] + access(all) view fun getParentAddresses(): [Address] /// Returns associated parent addresses and their redeemed status - true if redeemed, false if pending - pub fun getParentStatuses(): {Address: Bool} + access(all) view fun getParentStatuses(): {Address: Bool} /// Returns true if the given address is a parent of this child and has redeemed it. Returns false if the given /// address is a parent of this child and has NOT redeemed it. Returns nil if the given address it not a parent /// of this child account. - pub fun getRedeemedStatus(addr: Address): Bool? + access(all) view fun getRedeemedStatus(addr: Address): Bool? /// 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 /// - pub resource interface OwnedAccountPrivate { + access(all) resource interface OwnedAccountPrivate { /// Deletes the ChildAccount resource being used to share access to this OwnedAccount with the supplied parent /// address, and unlinks the paths it was using to reach the underlying account. - pub fun removeParent(parent: Address): Bool + access(Owner) fun removeParent(parent: Address): Bool /// Sets up a new ChildAccount resource for the given parentAddress to redeem. This child account uses the /// supplied factory and filter to manage what can be obtained from the child account, and a new /// CapabilityDelegator resource is created for the sharing of one-off capabilities. Each of these pieces of /// access control are managed through the child account. - pub fun publishToParent( + access(Owner) fun publishToParent( parentAddress: Address, - factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>, + factory: Capability<&CapabilityFactory.Manager>, filter: Capability<&{CapabilityFilter.Filter}> ) { pre { @@ -124,7 +127,7 @@ pub contract HybridCustody { /// Passes ownership of this child account to the given address. Once executed, all active keys on the child /// account will be revoked, and the active AuthAccount Capability being used by to obtain capabilities will be /// rotated, preventing anyone without the newly generated Capability from gaining access to the account. - pub fun giveOwnership(to: Address) + access(Owner) fun giveOwnership(to: Address) /// Revokes all keys on an account, unlinks all currently active AuthAccount capabilities, then makes a new one /// and replaces the OwnedAccount's underlying AuthAccount Capability with the new one to ensure that all @@ -132,20 +135,20 @@ pub contract HybridCustody { /// Unless this method is executed via the giveOwnership function, this will leave an account **without** an /// owner. /// USE WITH EXTREME CAUTION. - pub fun seal() + access(Owner) fun seal() // setCapabilityFactoryForParent // Override the existing CapabilityFactory Capability for a given parent. This will allow the owner of the // account to start managing their own factory of capabilities to be able to retrieve - pub fun setCapabilityFactoryForParent(parent: Address, cap: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>) { + access(Owner) fun setCapabilityFactoryForParent(parent: Address, cap: Capability<&CapabilityFactory.Manager>) { pre { cap.check(): "Invalid CapabilityFactory.Getter Capability provided" } } /// Override the existing CapabilityFilter Capability for a given parent. This will allow the owner of the - /// account to start managing their own filter for retrieving Capabilities on Private Paths - pub fun setCapabilityFilterForParent(parent: Address, cap: Capability<&{CapabilityFilter.Filter}>) { + /// account to start managing their own filter for retrieving Capabilities + access(Owner) fun setCapabilityFilterForParent(parent: Address, cap: Capability<&{CapabilityFilter.Filter}>) { pre { cap.check(): "Invalid CapabilityFilter Capability provided" } @@ -153,68 +156,67 @@ pub contract HybridCustody { /// Adds a capability to a parent's managed @ChildAccount resource. The Capability can be made public, /// permitting anyone to borrow it. - pub fun addCapabilityToDelegator(parent: Address, cap: Capability, isPublic: Bool) { + access(Owner) fun addCapabilityToDelegator(parent: Address, cap: Capability, isPublic: Bool) { pre { cap.check<&AnyResource>(): "Invalid Capability provided" } } /// Removes a Capability from the CapabilityDelegator used by the specified parent address - pub fun removeCapabilityFromDelegator(parent: Address, cap: Capability) + access(Owner) fun removeCapabilityFromDelegator(parent: Address, cap: Capability) /// Returns the address of this OwnedAccount - pub fun getAddress(): Address + access(all) view fun getAddress(): Address /// Checks if this OwnedAccount is a child of the specified address - pub fun isChildOf(_ addr: Address): Bool + access(all) view fun isChildOf(_ addr: Address): Bool /// Returns all addresses which are parents of this OwnedAccount - pub fun getParentAddresses(): [Address] + access(all) view fun getParentAddresses(): [Address] /// Borrows this OwnedAccount's AuthAccount Capability - pub fun borrowAccount(): &AuthAccount? + access(Owner) view fun borrowAccount(): auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account /// Returns the current owner of this account, if there is one - pub fun getOwner(): Address? + access(all) view fun getOwner(): Address? /// Returns the pending owner of this account, if there is one - pub fun getPendingOwner(): Address? + access(all) view fun getPendingOwner(): Address? /// A callback which is invoked when a parent redeems an owned account access(contract) fun setOwnerCallback(_ addr: Address) /// Destroys all outstanding AuthAccount capabilities on this owned account, and creates a new one for the /// OwnedAccount to use - pub fun rotateAuthAccount() + access(Owner) fun rotateAuthAccount() /// Revokes all keys on this account - pub fun revokeAllKeys() + access(Owner) fun revokeAllKeys() } /// Public methods exposed on a ChildAccount resource. OwnedAccountPublic will share some methods here, but isn't /// necessarily the same. /// - pub resource interface AccountPublic { - pub fun getPublicCapability(path: PublicPath, type: Type): Capability? - pub fun getPublicCapFromDelegator(type: Type): Capability? - pub fun getAddress(): Address - pub fun getCapabilityFactoryManager(): &{CapabilityFactory.Getter}? - pub fun getCapabilityFilter(): &{CapabilityFilter.Filter}? + access(all) resource interface AccountPublic { + access(all) view fun getPublicCapability(path: PublicPath, type: Type): Capability? + access(all) view fun getPublicCapFromDelegator(type: Type): Capability? + 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 /// - pub resource interface AccountPrivate { - pub fun getCapability(path: CapabilityPath, type: Type): Capability? { + access(all) resource interface AccountPrivate { + 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" } } - pub fun getPublicCapability(path: PublicPath, type: Type): Capability? - pub fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}? - pub fun getPublicCapFromDelegator(type: Type): Capability? - pub fun getPrivateCapFromDelegator(type: Type): Capability? { + access(all) view fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}? + 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" @@ -231,14 +233,14 @@ pub contract HybridCustody { /// Entry point for a parent to obtain, maintain and access Capabilities or perform other actions on child accounts /// - pub resource interface ManagerPrivate { - pub fun addAccount(cap: Capability<&{AccountPrivate, AccountPublic, MetadataViews.Resolver}>) - pub fun borrowAccount(addr: Address): &{AccountPrivate, AccountPublic, MetadataViews.Resolver}? - pub fun removeChild(addr: Address) - pub fun addOwnedAccount(cap: Capability<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>) - pub fun borrowOwnedAccount(addr: Address): &{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}? - pub fun removeOwned(addr: Address) - pub fun setManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?, childAddress: Address) { + access(all) resource interface ManagerPrivate { + access(Manage) fun addAccount(cap: Capability) + access(Manage) fun borrowAccount(addr: Address): auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}? + access(Manage) fun removeChild(addr: Address) + access(Manage) fun addOwnedAccount(cap: Capability) + 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 { cap == nil || cap!.check(): "Invalid Manager Capability Filter" } @@ -247,39 +249,40 @@ pub contract HybridCustody { /// Functions anyone can call on a manager to get information about an account such as What child accounts it has /// Functions anyone can call on a manager to get information about an account such as what child accounts it has - pub resource interface ManagerPublic { - pub fun borrowAccountPublic(addr: Address): &{AccountPublic, MetadataViews.Resolver}? - pub fun getChildAddresses(): [Address] - pub fun getOwnedAddresses(): [Address] - pub fun getChildAccountDisplay(address: Address): MetadataViews.Display? + access(all) resource interface ManagerPublic { + access(all) view fun borrowAccountPublic(addr: Address): &{AccountPublic, ViewResolver.Resolver}? + access(all) view fun getChildAddresses(): [Address] + access(all) view fun getOwnedAddresses(): [Address] + access(all) view fun getChildAccountDisplay(address: Address): MetadataViews.Display? access(contract) fun removeParentCallback(child: Address) } /// A resource for an account which fills the Parent role of the Child-Parent account management Model. A Manager /// can redeem or remove child accounts, and obtain any capabilities exposed by the child account to them. /// - pub resource Manager: ManagerPrivate, ManagerPublic, MetadataViews.Resolver { + access(all) resource Manager: ManagerPrivate, ManagerPublic, ViewResolver.Resolver, Burner.Burnable { + access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid) /// Mapping of restricted access child account Capabilities indexed by their address - pub let childAccounts: {Address: Capability<&{AccountPrivate, AccountPublic, MetadataViews.Resolver}>} + access(self) let childAccounts: {Address: Capability} /// Mapping of unrestricted owned account Capabilities indexed by their address - pub let ownedAccounts: {Address: Capability<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>} + access(self) let ownedAccounts: {Address: Capability} /// A bucket of structs so that the Manager resource can be easily extended with new functionality. - pub let data: {String: AnyStruct} + access(self) let data: {String: AnyStruct} /// A bucket of resources so that the Manager resource can be easily extended with new functionality. - pub let resources: @{String: AnyResource} + access(self) let resources: @{String: AnyResource} /// An optional filter to gate what capabilities are permitted to be returned from a child account For example, /// Dapper Wallet parent account's should not be able to retrieve any FungibleToken Provider capabilities. - pub var filter: Capability<&{CapabilityFilter.Filter}>? + access(self) var filter: Capability<&{CapabilityFilter.Filter}>? // display metadata for a child account exists on its parent - pub let childAccountDisplays: {Address: MetadataViews.Display} + access(self) let childAccountDisplays: {Address: MetadataViews.Display} /// Sets the Display on the ChildAccount. If nil, the display is removed. /// - pub fun setChildAccountDisplay(address: Address, _ d: MetadataViews.Display?) { + access(Manage) fun setChildAccountDisplay(address: Address, _ d: MetadataViews.Display?) { pre { self.childAccounts[address] != nil: "There is no child account with this address" } @@ -295,7 +298,7 @@ pub 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 /// - pub fun addAccount(cap: Capability<&{AccountPrivate, AccountPublic, MetadataViews.Resolver}>) { + access(Manage) fun addAccount(cap: Capability) { pre { self.childAccounts[cap.address] == nil: "There is already a child account with this address" } @@ -313,17 +316,17 @@ pub contract HybridCustody { /// Sets the default Filter Capability for this Manager. Does not propagate to child accounts. /// - pub fun setDefaultManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?) { + access(Manage) fun setDefaultManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?) { pre { cap == nil || cap!.check(): "supplied capability must be nil or check must pass" } self.filter = cap } - + /// Sets the Filter Capability for this Manager, propagating to the specified child account /// - pub fun setManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?, childAddress: Address) { + access(Manage) fun setManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?, childAddress: Address) { let acct = self.borrowAccount(addr: childAddress) ?? panic("child account not found") @@ -333,7 +336,7 @@ pub contract HybridCustody { /// Removes specified child account from the Manager's child accounts. Callbacks to the child account remove /// any associated resources and Capabilities /// - pub fun removeChild(addr: Address) { + access(Manage) fun removeChild(addr: Address) { let cap = self.childAccounts.remove(key: addr) ?? panic("child account not found") @@ -349,9 +352,11 @@ pub contract HybridCustody { // Get the child account id before removing capability let id: UInt64 = acct.uuid - acct.parentRemoveChildCallback(parent: self.owner!.address) + if self.owner != nil { + acct.parentRemoveChildCallback(parent: self.owner!.address) + } - emit AccountUpdated(id: id, child: cap.address, parent: self.owner!.address, active: false) + emit AccountUpdated(id: id, child: cap.address, parent: self.owner?.address, active: false) } /// Contract callback that removes a child account from the Manager's child accounts in the event a child @@ -365,7 +370,7 @@ pub contract HybridCustody { /// Adds an owned account to the Manager's list of owned accounts, setting the Manager account as the owner of /// the given account /// - pub fun addOwnedAccount(cap: Capability<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>) { + access(Manage) fun addOwnedAccount(cap: Capability) { pre { self.ownedAccounts[cap.address] == nil: "There is already an owned account with this address" } @@ -386,7 +391,7 @@ pub contract HybridCustody { /// Returns a reference to a child account /// - pub fun borrowAccount(addr: Address): &{AccountPrivate, AccountPublic, MetadataViews.Resolver}? { + access(Manage) fun borrowAccount(addr: Address): auth(Child) &{AccountPrivate, AccountPublic, ViewResolver.Resolver}? { let cap = self.childAccounts[addr] if cap == nil { return nil @@ -397,7 +402,7 @@ pub contract HybridCustody { /// Returns a reference to a child account's public AccountPublic interface /// - pub fun borrowAccountPublic(addr: Address): &{AccountPublic, MetadataViews.Resolver}? { + access(all) view fun borrowAccountPublic(addr: Address): &{AccountPublic, ViewResolver.Resolver}? { let cap = self.childAccounts[addr] if cap == nil { return nil @@ -408,7 +413,7 @@ pub contract HybridCustody { /// Returns a reference to an owned account /// - pub fun borrowOwnedAccount(addr: Address): &{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}? { + access(Manage) view fun borrowOwnedAccount(addr: Address): auth(Owner) &{OwnedAccountPrivate, OwnedAccountPublic, ViewResolver.Resolver}? { if let cap = self.ownedAccounts[addr] { return cap.borrow() } @@ -419,7 +424,7 @@ pub contract HybridCustody { /// Removes specified child account from the Manager's child accounts. Callbacks to the child account remove /// any associated resources and Capabilities /// - pub fun removeOwned(addr: Address) { + access(Manage) fun removeOwned(addr: Address) { if let acct = self.ownedAccounts.remove(key: addr) { if acct.check() { acct.borrow()!.seal() @@ -439,7 +444,7 @@ pub contract HybridCustody { /// mechanism intended to easily transfer 'root' access on this account to another account and an attempt to /// minimize access vectors. /// - pub 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") @@ -448,31 +453,31 @@ pub contract HybridCustody { /// Returns an array of child account addresses /// - pub fun getChildAddresses(): [Address] { + access(all) view fun getChildAddresses(): [Address] { return self.childAccounts.keys } /// Returns an array of owned account addresses /// - pub fun getOwnedAddresses(): [Address] { + access(all) view fun getOwnedAddresses(): [Address] { return self.ownedAccounts.keys } /// Retrieves the parent-defined display for the given child account /// - pub fun getChildAccountDisplay(address: Address): MetadataViews.Display? { + access(all) view fun getChildAccountDisplay(address: Address): MetadataViews.Display? { return self.childAccountDisplays[address] } /// Returns the types of supported views - none at this time /// - pub fun getViews(): [Type] { + access(all) view fun getViews(): [Type] { return [] } /// Resolves the given view if supported - none at this time /// - pub fun resolveView(_ view: Type): AnyStruct? { + access(all) view fun resolveView(_ view: Type): AnyStruct? { return nil } @@ -489,8 +494,19 @@ pub contract HybridCustody { self.resources <- {} } - destroy () { - destroy self.resources + // When a manager resource is destroyed, attempt to remove this parent from every + // child account it currently has + // + // Destruction will fail if there are any owned account to prevent loss of access to an account + access(contract) fun burnCallback() { + pre { + // Prevent accidental burning of a resource that has ownership of other accounts + self.ownedAccounts.length == 0: "cannot destroy a manager with owned accounts" + } + + for c in self.childAccounts.keys { + self.removeChild(addr: c) + } } } @@ -503,27 +519,29 @@ pub contract HybridCustody { /// able to manage all ChildAccount resources it shares, without worrying about whether the upstream parent can do /// anything to prevent it. /// - pub resource ChildAccount: AccountPrivate, AccountPublic, MetadataViews.Resolver { + access(all) resource ChildAccount: AccountPrivate, AccountPublic, ViewResolver.Resolver, Burner.Burnable { + access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid, address: Address = self.childCap.address, parent: Address = self.parent) + /// A Capability providing access to the underlying child account - access(self) let childCap: Capability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}> + access(self) let childCap: Capability<&{BorrowableAccount, OwnedAccountPublic, ViewResolver.Resolver}> /// The CapabilityFactory Manager is a ChildAccount's way of limiting what types can be asked for by its parent /// account. The CapabilityFactory returns Capabilities which can be casted to their appropriate types once /// obtained, but only if the child account has configured their factory to allow it. For instance, a /// ChildAccount might choose to expose NonFungibleToken.Provider, but not FungibleToken.Provider - pub var factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}> + access(self) var factory: Capability<&CapabilityFactory.Manager> /// The CapabilityFilter is a restriction put at the front of obtaining any non-public Capability. Some wallets /// might want to give access to NonFungibleToken.Provider, but only to **some** of the collections it manages, /// not all of them. - pub var filter: Capability<&{CapabilityFilter.Filter}> + access(self) var filter: Capability<&{CapabilityFilter.Filter}> /// The CapabilityDelegator is a way to share one-off capabilities from the child account. These capabilities /// can be public OR private and are separate from the factory which returns a capability at a given path as a /// certain type. When using the CapabilityDelegator, you do not have the ability to specify which path a /// capability came from. For instance, Dapper Wallet might choose to expose a Capability to their Full TopShot /// collection, but only to the path that the collection exists in. - pub let delegator: Capability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}> + access(self) let delegator: Capability /// managerCapabilityFilter is a component optionally given to a child account when a manager redeems it. If /// this filter is not nil, any Capability returned through the `getCapability` function checks that the @@ -538,11 +556,11 @@ pub contract HybridCustody { /// ChildAccount resources have a 1:1 association with parent accounts, the named parent Address here is the /// one with a Capability on this resource. - pub let parent: Address + access(all) let parent: Address /// Returns the Address of the underlying child account /// - pub fun getAddress(): Address { + access(all) view fun getAddress(): Address { return self.childCap.address } @@ -562,24 +580,24 @@ pub contract HybridCustody { /// Sets the CapabiltyFactory.Manager Capability /// - pub fun setCapabilityFactory(cap: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>) { + access(contract) fun setCapabilityFactory(cap: Capability<&CapabilityFactory.Manager>) { self.factory = cap } /// Sets the Filter Capability as the one provided /// - pub fun setCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>) { + access(contract) fun setCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>) { self.filter = cap } - /// The main function to a child account's capabilities from a parent account. When a PrivatePath type is used, - /// the CapabilityFilter will be borrowed and the Capability being returned will be checked against it to + /// The main function to a child account's capabilities from a parent account. When getting a capability, the CapabilityFilter will be borrowed and + /// the Capability being returned will be checked against it to /// ensure that borrowing is permitted. If not allowed, nil is returned. /// Also know that this method retrieves Capabilities via the CapabilityFactory path. To retrieve arbitrary /// Capabilities, see `getPrivateCapFromDelegator()` and `getPublicCapFromDelegator()` which use the /// `Delegator` retrieval path. /// - pub fun getCapability(path: CapabilityPath, 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) @@ -587,15 +605,16 @@ pub contract HybridCustody { return nil } - let acct = child.borrowAccount() - let cap = f!.getCapability(acct: acct, path: path) + let acct = child._borrowAccount() + let tmp = f!.getCapability(acct: acct, controllerID: controllerID) + if tmp == nil { + return nil + } + let cap = tmp! // Check that private capabilities are allowed by either internal or manager filter (if assigned) // If not allowed, return nil - if path.getType() == Type() && ( - self.filter.borrow()!.allowed(cap: cap) == false || - (self.getManagerCapabilityFilter()?.allowed(cap: cap) ?? true) == false - ) { + if self.filter.borrow()!.allowed(cap: cap) == false || (self.getManagerCapabilityFilter()?.allowed(cap: cap) ?? true) == false { return nil } @@ -605,7 +624,7 @@ pub contract HybridCustody { /// Retrieves a private Capability from the Delegator or nil none is found of the given type. Useful for /// arbitrary Capability retrieval /// - pub fun getPrivateCapFromDelegator(type: Type): Capability? { + access(Child) view fun getPrivateCapFromDelegator(type: Type): Capability? { if let d = self.delegator.borrow() { return d.getPrivateCapability(type) } @@ -616,7 +635,7 @@ pub contract HybridCustody { /// Retrieves a public Capability from the Delegator or nil none is found of the given type. Useful for /// arbitrary Capability retrieval /// - pub fun getPublicCapFromDelegator(type: Type): Capability? { + access(all) view fun getPublicCapFromDelegator(type: Type): Capability? { if let d = self.delegator.borrow() { return d.getPublicCapability(type) } @@ -626,37 +645,43 @@ pub contract HybridCustody { /// Enables retrieval of public Capabilities of the given type from the specified path or nil if none is found. /// Callers should be aware this method uses the `CapabilityFactory` retrieval path. /// - pub fun getPublicCapability(path: PublicPath, type: Type): Capability? { - return self.getCapability(path: path, type: type) + access(all) view fun getPublicCapability(path: PublicPath, type: Type): Capability? { + let child = self.childCap.borrow() ?? panic("failed to borrow child account") + + let f = self.factory.borrow()!.getFactory(type) + if f == nil { + return nil + } + + let acct = child._borrowAccount() + return f!.getPublicCapability(acct: acct, path: path) } /// Returns a reference to the stored managerCapabilityFilter if one exists /// - pub fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}? { + access(all) view fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}? { return self.managerCapabilityFilter != nil ? self.managerCapabilityFilter!.borrow() : nil } /// Sets the child account as redeemed by the given Address /// access(contract) fun setRedeemed(_ addr: Address) { - let acct = self.childCap.borrow()!.borrowAccount() - if let o = acct.borrow<&OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) { - o.setRedeemed(addr) - } + let acct = self.childCap.borrow()!._borrowAccount() + acct.storage.borrow<&OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath)?.setRedeemed(addr) } /// Returns a reference to the stored delegator, generally used for arbitrary Capability retrieval /// - pub fun borrowCapabilityDelegator(): &CapabilityDelegator.Delegator? { + access(Owner) fun borrowCapabilityDelegator(): auth(CapabilityDelegator.Get) &CapabilityDelegator.Delegator? { let path = HybridCustody.getCapabilityDelegatorIdentifier(self.parent) - return self.childCap.borrow()!.borrowAccount().borrow<&CapabilityDelegator.Delegator>( + return self.childCap.borrow()!._borrowAccount().storage.borrow( from: StoragePath(identifier: path)! ) } /// Returns a list of supported metadata views /// - pub fun getViews(): [Type] { + access(all) view fun getViews(): [Type] { return [ Type() ] @@ -664,17 +689,22 @@ pub contract HybridCustody { /// Resolves a view of the given type if supported /// - pub fun resolveView(_ view: Type): AnyStruct? { + access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): let childAddress = self.getAddress() - let manager = getAccount(self.parent).getCapability<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath) + let tmp = getAccount(self.parent).capabilities.get<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath) + if tmp == nil { + return nil + } + + let manager = tmp! if !manager.check() { return nil } - return manager!.borrow()!.getChildAccountDisplay(address: childAddress) + return manager.borrow()!.getChildAccountDisplay(address: childAddress) } return nil } @@ -687,22 +717,22 @@ pub contract HybridCustody { return } - let child: &AnyResource{HybridCustody.BorrowableAccount} = self.childCap.borrow()! + let child: &{HybridCustody.BorrowableAccount} = self.childCap.borrow()! if !child.check() { return } - let acct = child.borrowAccount() - if let ownedAcct = acct.borrow<&OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) { + let acct = child._borrowAccount() + if let ownedAcct = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) { ownedAcct.removeParent(parent: parent) } } init( - _ childCap: Capability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>, - _ factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>, + _ childCap: Capability<&{BorrowableAccount, OwnedAccountPublic, ViewResolver.Resolver}>, + _ factory: Capability<&CapabilityFactory.Manager>, _ filter: Capability<&{CapabilityFilter.Filter}>, - _ delegator: Capability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>, + _ delegator: Capability, _ parent: Address ) { pre { @@ -724,18 +754,28 @@ pub contract HybridCustody { /// Returns a capability to this child account's CapabilityFilter /// - pub fun getCapabilityFilter(): &{CapabilityFilter.Filter}? { + access(all) view fun getCapabilityFilter(): &{CapabilityFilter.Filter}? { return self.filter.check() ? self.filter.borrow() : nil } /// Returns a capability to this child account's CapabilityFactory /// - pub fun getCapabilityFactoryManager(): &{CapabilityFactory.Getter}? { + access(all) view fun getCapabilityFactoryManager(): &{CapabilityFactory.Getter}? { return self.factory.check() ? self.factory.borrow() : nil } - destroy () { - destroy <- self.resources + 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) + } + + // When a ChildAccount is destroyed, attempt to remove it from the parent account as well + access(contract) fun burnCallback() { + self.parentRemoveChildCallback(parent: self.parent) } } @@ -748,18 +788,19 @@ pub contract HybridCustody { /// accounts would still exist, allowing a form of Hybrid Custody which has no true owner over an account, but /// shared partial ownership. /// - pub resource OwnedAccount: OwnedAccountPrivate, BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver { + access(all) resource OwnedAccount: OwnedAccountPrivate, BorrowableAccount, OwnedAccountPublic, ViewResolver.Resolver, Burner.Burnable { + access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid, addr: Address = self.acct.address) /// Capability on the underlying account object - access(self) var acct: Capability<&AuthAccount> + access(self) var acct: Capability /// Mapping of current and pending parents, true and false respectively - pub let parents: {Address: Bool} + access(all) let parents: {Address: Bool} /// Address of the pending owner, if one exists - pub var pendingOwner: Address? + access(all) var pendingOwner: Address? /// Address of the current owner, if one exists - pub var acctOwner: Address? + access(all) var acctOwner: Address? /// Owned status of this account - pub var currentlyOwned: Bool + access(all) var currentlyOwned: Bool /// A bucket of structs so that the OwnedAccount resource can be easily extended with new functionality. access(self) let data: {String: AnyStruct} @@ -810,12 +851,12 @@ pub contract HybridCustody { /// 4. Publish the newly made private link to the designated parent's inbox for them to claim on their @Manager /// resource. /// - pub fun publishToParent( + access(Owner) fun publishToParent( parentAddress: Address, - factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>, + factory: Capability<&CapabilityFactory.Manager>, filter: Capability<&{CapabilityFilter.Filter}> ) { - pre{ + pre { self.parents[parentAddress] == nil: "Address pending or already redeemed as parent" } let capDelegatorIdentifier = HybridCustody.getCapabilityDelegatorIdentifier(parentAddress) @@ -826,41 +867,30 @@ pub contract HybridCustody { let capDelegatorStorage = StoragePath(identifier: capDelegatorIdentifier)! let acct = self.borrowAccount() - assert(acct.borrow<&AnyResource>(from: capDelegatorStorage) == nil, message: "conflicting resource found in capability delegator storage slot for parentAddress") - assert(acct.borrow<&AnyResource>(from: childAccountStorage) == nil, message: "conflicting resource found in child account storage slot for parentAddress") + assert(acct.storage.borrow<&AnyResource>(from: capDelegatorStorage) == nil, message: "conflicting resource found in capability delegator storage slot for parentAddress") + assert(acct.storage.borrow<&AnyResource>(from: childAccountStorage) == nil, message: "conflicting resource found in child account storage slot for parentAddress") - if acct.borrow<&CapabilityDelegator.Delegator>(from: capDelegatorStorage) == nil { + if acct.storage.borrow<&CapabilityDelegator.Delegator>(from: capDelegatorStorage) == nil { let delegator <- CapabilityDelegator.createDelegator() - acct.save(<-delegator, to: capDelegatorStorage) + acct.storage.save(<-delegator, to: capDelegatorStorage) } let capDelegatorPublic = PublicPath(identifier: capDelegatorIdentifier)! - let capDelegatorPrivate = PrivatePath(identifier: capDelegatorIdentifier)! - acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>( - capDelegatorPublic, - target: capDelegatorStorage - ) - acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>( - capDelegatorPrivate, - target: capDelegatorStorage - ) - let delegator = acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>( - capDelegatorPrivate - ) + let pubCap = acct.capabilities.storage.issue<&{CapabilityDelegator.GetterPublic}>(capDelegatorStorage) + acct.capabilities.publish(pubCap, at: capDelegatorPublic) + + let delegator = acct.capabilities.storage.issue(capDelegatorStorage) assert(delegator.check(), message: "failed to setup capability delegator for parent address") - let borrowableCap = self.borrowAccount().getCapability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>( - HybridCustody.OwnedAccountPrivatePath + let borrowableCap = self.borrowAccount().capabilities.storage.issue<&{BorrowableAccount, OwnedAccountPublic, ViewResolver.Resolver}>( + HybridCustody.OwnedAccountStoragePath ) - let childAcct <- create ChildAccount(borrowableCap, factory, filter, delegator, parentAddress) - let childAccountPrivatePath = PrivatePath(identifier: identifier)! + let childAcct <- create ChildAccount(borrowableCap, factory, filter, delegator, parentAddress) - acct.save(<-childAcct, to: childAccountStorage) - acct.link<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountPrivatePath, target: childAccountStorage) - - let delegatorCap = acct.getCapability<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountPrivatePath) + acct.storage.save(<-childAcct, to: childAccountStorage) + let delegatorCap = acct.capabilities.storage.issue(childAccountStorage) assert(delegatorCap.check(), message: "Delegator capability check failed") acct.inbox.publish(delegatorCap, name: identifier, recipient: parentAddress) @@ -880,38 +910,43 @@ pub contract HybridCustody { /// Checks the validity of the encapsulated account Capability /// - pub fun check(): Bool { + access(all) view fun check(): Bool { return self.acct.check() } /// Returns a reference to the encapsulated account object /// - pub fun borrowAccount(): &AuthAccount { - return self.acct.borrow()! + 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 /// - pub fun getParentAddresses(): [Address] { + access(all) view fun getParentAddresses(): [Address] { return self.parents.keys } /// Returns whether the given address is a parent of this account /// - pub fun isChildOf(_ addr: Address): Bool { + access(all) view fun isChildOf(_ addr: Address): Bool { return self.parents[addr] != nil } /// Returns nil if the given address is not a parent, false if the parent has not redeemed the child account /// yet, and true if they have /// - pub fun getRedeemedStatus(addr: Address): Bool? { + access(all) view fun getRedeemedStatus(addr: Address): Bool? { return self.parents[addr] } /// Returns associated parent addresses and their redeemed status /// - pub fun getParentStatuses(): {Address: Bool} { + access(all) view fun getParentStatuses(): {Address: Bool} { return self.parents } @@ -919,29 +954,37 @@ pub contract HybridCustody { /// configured for the provided parent address. Once done, the parent will not have any valid capabilities with /// which to access the child account. /// - pub fun removeParent(parent: Address): Bool { + access(Owner) fun removeParent(parent: Address): Bool { if self.parents[parent] == nil { return false } + let identifier = HybridCustody.getChildAccountIdentifier(parent) let capDelegatorIdentifier = HybridCustody.getCapabilityDelegatorIdentifier(parent) let acct = self.borrowAccount() - acct.unlink(PrivatePath(identifier: identifier)!) - acct.unlink(PublicPath(identifier: identifier)!) - acct.unlink(PrivatePath(identifier: capDelegatorIdentifier)!) - acct.unlink(PublicPath(identifier: capDelegatorIdentifier)!) + // get all controllers which target this storage path + let storagePath = StoragePath(identifier: identifier)! + let childAccountControllers = acct.capabilities.storage.getControllers(forPath: storagePath) + for c in childAccountControllers { + c.delete() + } + Burner.burn(<- acct.storage.load<@AnyResource>(from: storagePath)) - destroy <- acct.load<@AnyResource>(from: StoragePath(identifier: identifier)!) - destroy <- acct.load<@AnyResource>(from: StoragePath(identifier: capDelegatorIdentifier)!) + let delegatorStoragePath = StoragePath(identifier: capDelegatorIdentifier)! + let delegatorControllers = acct.capabilities.storage.getControllers(forPath: delegatorStoragePath) + for c in delegatorControllers { + c.delete() + } + Burner.burn(<- acct.storage.load<@AnyResource>(from: delegatorStoragePath)) self.parents.remove(key: parent) emit AccountUpdated(id: self.uuid, child: self.acct.address, parent: parent, active: false) - let parentManager = getAccount(parent).getCapability<&Manager{ManagerPublic}>(HybridCustody.ManagerPublicPath) + let parentManager = getAccount(parent).capabilities.get<&{ManagerPublic}>(HybridCustody.ManagerPublicPath) if parentManager.check() { - parentManager.borrow()?.removeParentCallback(child: self.owner!.address) + parentManager.borrow()?.removeParentCallback(child: acct.address) } return true @@ -949,21 +992,21 @@ pub contract HybridCustody { /// Returns the address of the encapsulated account /// - pub fun getAddress(): Address { + access(all) view fun getAddress(): Address { return self.acct.address } /// Returns the address of the pending owner if one is assigned. Pending owners are assigned when ownership has /// been granted, but has not yet been redeemed. /// - pub fun getPendingOwner(): Address? { + access(all) view fun getPendingOwner(): Address? { return self.pendingOwner } /// Returns the address of the current owner if one is assigned. Current owners are assigned when ownership has /// been redeemed. /// - pub fun getOwner(): Address? { + access(all) view fun getOwner(): Address? { if !self.currentlyOwned { return nil } @@ -979,22 +1022,17 @@ pub contract HybridCustody { /// mechanism intended to easily transfer 'root' access on this account to another account and an attempt to /// minimize access vectors. /// - pub fun giveOwnership(to: Address) { + access(Owner) fun giveOwnership(to: Address) { self.seal() let acct = self.borrowAccount() - // Unlink existing owner's Capability if owner exists - if self.acctOwner != nil { - acct.unlink( - PrivatePath(identifier: HybridCustody.getOwnerIdentifier(self.acctOwner!))! - ) - } + // Link a Capability for the new owner, retrieve & publish let identifier = HybridCustody.getOwnerIdentifier(to) - let cap = acct.link<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>( - PrivatePath(identifier: identifier)!, - target: HybridCustody.OwnedAccountStoragePath - ) ?? panic("failed to link child account capability") + let cap = acct.capabilities.storage.issue(HybridCustody.OwnedAccountStoragePath) + + // make sure we can borrow the newly issued owned account + cap.borrow()?.borrowAccount() ?? panic("can not borrow the Hybrid Custody Owned Account") acct.inbox.publish(cap, name: identifier, recipient: to) @@ -1006,7 +1044,7 @@ pub contract HybridCustody { /// Revokes all keys on the underlying account /// - pub fun revokeAllKeys() { + access(Owner) fun revokeAllKeys() { let acct = self.borrowAccount() // Revoke all keys @@ -1025,36 +1063,25 @@ pub contract HybridCustody { /// assumes ownership of an account to guarantee that the previous owner doesn't maintain admin access to the /// account via other AuthAccount Capabilities. /// - pub fun rotateAuthAccount() { + access(Owner) fun rotateAuthAccount() { let acct = self.borrowAccount() // Find all active AuthAccount capabilities so they can be removed after we make the new auth account cap - let pathsToUnlink: [PrivatePath] = [] - acct.forEachPrivate(fun (path: PrivatePath, type: Type): Bool { - if type.identifier == "Capability<&AuthAccount>" { - pathsToUnlink.append(path) - } - return true - }) + let controllersToDestroy = acct.capabilities.account.getControllers() // Link a new AuthAccount Capability - // NOTE: This path cannot be sufficiently randomly generated, an app calling this function could build a - // capability to this path before it is made, thus maintaining ownership despite making it look like they - // gave it away. Until capability controllers, this method should not be fully trusted. - let authAcctPath = "HybridCustodyRelinquished_" - .concat(HybridCustody.account.address.toString()) - .concat(getCurrentBlock().height.toString()) - .concat(unsafeRandom().toString()) // ensure that the path is different from the previous one - let acctCap = acct.linkAccount(PrivatePath(identifier: authAcctPath)!)! + let acctCap = acct.capabilities.account.issue() self.acct = acctCap let newAcct = self.acct.borrow()! // 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 p in pathsToUnlink { - newAcct.unlink(p) + 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 @@ -1064,7 +1091,7 @@ pub contract HybridCustody { /// /// USE WITH EXTREME CAUTION. /// - pub fun seal() { + access(Owner) fun seal() { self.rotateAuthAccount() self.revokeAllKeys() // There needs to be a path to giving ownership that doesn't revoke keys emit AccountSealed(id: self.uuid, address: self.acct.address, parents: self.parents.keys) @@ -1073,16 +1100,16 @@ pub contract HybridCustody { /// Retrieves a reference to the ChildAccount associated with the given parent account if one exists. /// - pub fun borrowChildAccount(parent: Address): &ChildAccount? { + access(Owner) fun borrowChildAccount(parent: Address): auth(Child) &ChildAccount? { let identifier = HybridCustody.getChildAccountIdentifier(parent) - return self.borrowAccount().borrow<&ChildAccount>(from: StoragePath(identifier: identifier)!) + return self.borrowAccount().storage.borrow(from: StoragePath(identifier: identifier)!) } /// Sets the CapabilityFactory Manager for the specified parent in the associated ChildAccount. /// - pub fun setCapabilityFactoryForParent( + access(Owner) fun setCapabilityFactoryForParent( parent: Address, - cap: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}> + cap: Capability<&CapabilityFactory.Manager> ) { let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address") p.setCapabilityFactory(cap: cap) @@ -1090,21 +1117,21 @@ pub contract HybridCustody { /// Sets the Filter for the specified parent in the associated ChildAccount. /// - pub fun setCapabilityFilterForParent(parent: Address, cap: Capability<&{CapabilityFilter.Filter}>) { + access(Owner) fun setCapabilityFilterForParent(parent: Address, cap: Capability<&{CapabilityFilter.Filter}>) { let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address") p.setCapabilityFilter(cap: cap) } /// Retrieves a reference to the Delegator associated with the given parent account if one exists. /// - pub fun borrowCapabilityDelegatorForParent(parent: Address): &CapabilityDelegator.Delegator? { + access(Owner) fun borrowCapabilityDelegatorForParent(parent: Address): auth(CapabilityDelegator.Get, CapabilityDelegator.Add, CapabilityDelegator.Delete) &CapabilityDelegator.Delegator? { let identifier = HybridCustody.getCapabilityDelegatorIdentifier(parent) - return self.borrowAccount().borrow<&CapabilityDelegator.Delegator>(from: StoragePath(identifier: identifier)!) + return self.borrowAccount().storage.borrow(from: StoragePath(identifier: identifier)!) } /// Adds the provided Capability to the Delegator associated with the given parent account. /// - pub fun addCapabilityToDelegator(parent: Address, cap: Capability, isPublic: Bool) { + access(Owner) fun addCapabilityToDelegator(parent: Address, cap: Capability, isPublic: Bool) { let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address") let delegator = self.borrowCapabilityDelegatorForParent(parent: parent) ?? panic("could not borrow capability delegator resource for parent address") @@ -1113,20 +1140,20 @@ pub contract HybridCustody { /// Removes the provided Capability from the Delegator associated with the given parent account. /// - pub fun removeCapabilityFromDelegator(parent: Address, cap: Capability) { + access(Owner) fun removeCapabilityFromDelegator(parent: Address, cap: Capability) { let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address") let delegator = self.borrowCapabilityDelegatorForParent(parent: parent) ?? panic("could not borrow capability delegator resource for parent address") delegator.removeCapability(cap: cap) } - pub fun getViews(): [Type] { + access(all) view fun getViews(): [Type] { return [ Type() ] } - pub fun resolveView(_ view: Type): AnyStruct? { + access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return self.display @@ -1136,12 +1163,27 @@ pub contract HybridCustody { /// Sets this OwnedAccount's display to the one provided /// - pub fun setDisplay(_ d: MetadataViews.Display) { + access(Owner) fun setDisplay(_ d: MetadataViews.Display) { 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<&AuthAccount> + _ acct: Capability ) { self.acct = acct @@ -1155,36 +1197,39 @@ pub contract HybridCustody { self.display = nil } - destroy () { - destroy <- self.resources + // When an OwnedAccount is destroyed, remove it from every configured parent account + access(contract) fun burnCallback() { + for p in self.parents.keys { + self.removeParent(parent: p) + } } } /// Utility function to get the path identifier for a parent address when interacting with a ChildAccount and its /// parents /// - pub fun getChildAccountIdentifier(_ addr: Address): String { + access(all) view fun getChildAccountIdentifier(_ addr: Address): String { return "ChildAccount_".concat(addr.toString()) } /// Utility function to get the path identifier for a parent address when interacting with a Delegator and its /// parents /// - pub fun getCapabilityDelegatorIdentifier(_ addr: Address): String { + access(all) view fun getCapabilityDelegatorIdentifier(_ addr: Address): String { return "ChildCapabilityDelegator_".concat(addr.toString()) } /// Utility function to get the path identifier for a parent address when interacting with an OwnedAccount and its /// owners /// - pub fun getOwnerIdentifier(_ addr: Address): String { + access(all) view fun getOwnerIdentifier(_ addr: Address): String { return "HybridCustodyOwnedAccount_".concat(HybridCustody.account.address.toString()).concat(addr.toString()) } /// Returns an OwnedAccount wrapping the provided AuthAccount Capability. /// - pub fun createOwnedAccount( - acct: Capability<&AuthAccount> + access(all) fun createOwnedAccount( + acct: Capability ): @OwnedAccount { pre { acct.check(): "invalid auth account capability" @@ -1197,7 +1242,7 @@ pub contract HybridCustody { /// Returns a new Manager with the provided Filter as default (if not nil). /// - pub fun createManager(filter: Capability<&{CapabilityFilter.Filter}>?): @Manager { + access(all) fun createManager(filter: Capability<&{CapabilityFilter.Filter}>?): @Manager { pre { filter == nil || filter!.check(): "Invalid CapabilityFilter Filter capability provided" } @@ -1209,15 +1254,10 @@ pub contract HybridCustody { init() { let identifier = "HybridCustodyChild_".concat(self.account.address.toString()) self.OwnedAccountStoragePath = StoragePath(identifier: identifier)! - self.OwnedAccountPrivatePath = PrivatePath(identifier: identifier)! self.OwnedAccountPublicPath = PublicPath(identifier: identifier)! - self.LinkedAccountPrivatePath = PrivatePath(identifier: "LinkedAccountPrivatePath_".concat(identifier))! - self.BorrowableAccountPrivatePath = PrivatePath(identifier: "BorrowableAccountPrivatePath_".concat(identifier))! - let managerIdentifier = "HybridCustodyManager_".concat(self.account.address.toString()) self.ManagerStoragePath = StoragePath(identifier: managerIdentifier)! self.ManagerPublicPath = PublicPath(identifier: managerIdentifier)! - self.ManagerPrivatePath = PrivatePath(identifier: managerIdentifier)! } } diff --git a/contracts/factories/FTAllFactory.cdc b/contracts/factories/FTAllFactory.cdc index 46f506b..046991e 100644 --- a/contracts/factories/FTAllFactory.cdc +++ b/contracts/factories/FTAllFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "FungibleToken" -pub contract FTAllFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(path) +access(all) contract FTAllFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check() { + return nil + } + + return con.capability as! Capability + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return nil } } } \ No newline at end of file diff --git a/contracts/factories/FTBalanceFactory.cdc b/contracts/factories/FTBalanceFactory.cdc index bd9d097..f4b70c1 100644 --- a/contracts/factories/FTBalanceFactory.cdc +++ b/contracts/factories/FTBalanceFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "FungibleToken" -pub contract FTBalanceFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{FungibleToken.Balance}>(path) +access(all) contract FTBalanceFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check<&{FungibleToken.Balance}>() { + return nil + } + + return con.capability as! Capability<&{FungibleToken.Balance}> + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return acct.capabilities.get<&{FungibleToken.Balance}>(path) } } } \ No newline at end of file diff --git a/contracts/factories/FTProviderFactory.cdc b/contracts/factories/FTProviderFactory.cdc index e27dd0a..bff3175 100644 --- a/contracts/factories/FTProviderFactory.cdc +++ b/contracts/factories/FTProviderFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "FungibleToken" -pub contract FTProviderFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{FungibleToken.Provider}>(path) +access(all) contract FTProviderFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check() { + return nil + } + + return con.capability as! Capability + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return nil } } } \ No newline at end of file diff --git a/contracts/factories/FTReceiverBalanceFactory.cdc b/contracts/factories/FTReceiverBalanceFactory.cdc index 49673d5..dd87c1c 100644 --- a/contracts/factories/FTReceiverBalanceFactory.cdc +++ b/contracts/factories/FTReceiverBalanceFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "FungibleToken" -pub contract FTReceiverBalanceFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{FungibleToken.Receiver, FungibleToken.Balance}>(path) +access(all) contract FTReceiverBalanceFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check<&{FungibleToken.Receiver, FungibleToken.Balance}>() { + return nil + } + + return con.capability as! Capability<&{FungibleToken.Receiver, FungibleToken.Balance}> + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return acct.capabilities.get<&{FungibleToken.Receiver, FungibleToken.Balance}>(path) } } } \ No newline at end of file diff --git a/contracts/factories/FTReceiverFactory.cdc b/contracts/factories/FTReceiverFactory.cdc index 03ade5c..815a244 100644 --- a/contracts/factories/FTReceiverFactory.cdc +++ b/contracts/factories/FTReceiverFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "FungibleToken" -pub contract FTReceiverFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{FungibleToken.Receiver}>(path) +access(all) contract FTReceiverFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check<&{FungibleToken.Receiver}>() { + return nil + } + + return con.capability as! Capability<&{FungibleToken.Receiver}> + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return acct.capabilities.get<&{FungibleToken.Receiver}>(path) } } } \ No newline at end of file diff --git a/contracts/factories/FTVaultFactory.cdc b/contracts/factories/FTVaultFactory.cdc new file mode 100644 index 0000000..c279af4 --- /dev/null +++ b/contracts/factories/FTVaultFactory.cdc @@ -0,0 +1,46 @@ +import "CapabilityFactory" +import "FungibleToken" + +access(all) contract FTVaultFactory { + access(all) struct WithdrawFactory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check() { + return nil + } + + return con.capability as! Capability + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return nil + } + } + + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check<&{FungibleToken.Vault}>() { + return nil + } + + return con.capability as! Capability<&{FungibleToken.Vault}> + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + let cap = acct.capabilities.get<&{FungibleToken.Vault}>(path) + if !cap.check() { + return nil + } + + return cap + + } + } +} \ No newline at end of file diff --git a/contracts/factories/NFTCollectionFactory.cdc b/contracts/factories/NFTCollectionFactory.cdc new file mode 100644 index 0000000..caf4dec --- /dev/null +++ b/contracts/factories/NFTCollectionFactory.cdc @@ -0,0 +1,45 @@ +import "CapabilityFactory" +import "NonFungibleToken" + +access(all) contract NFTProviderAndCollectionFactory { + access(all) struct WithdrawFactory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check() { + return nil + } + + return con.capability as! Capability + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return nil + } + } + + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check<&{NonFungibleToken.Collection}>() { + return nil + } + + return con.capability as! Capability<&{NonFungibleToken.Collection}> + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + let cap = acct.capabilities.get<&{NonFungibleToken.Collection}>(path) + if !cap.check() { + return nil + } + + return cap + } + } +} \ No newline at end of file diff --git a/contracts/factories/NFTCollectionPublicFactory.cdc b/contracts/factories/NFTCollectionPublicFactory.cdc index 0e9df83..16edaad 100644 --- a/contracts/factories/NFTCollectionPublicFactory.cdc +++ b/contracts/factories/NFTCollectionPublicFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "NonFungibleToken" -pub contract NFTCollectionPublicFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{NonFungibleToken.CollectionPublic}>(path) +access(all) contract NFTCollectionPublicFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check<&{NonFungibleToken.CollectionPublic}>() { + return nil + } + + return con.capability as! Capability<&{NonFungibleToken.CollectionPublic}> + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return acct.capabilities.get<&{NonFungibleToken.CollectionPublic}>(path) } } } \ No newline at end of file diff --git a/contracts/factories/NFTProviderAndCollectionFactory.cdc b/contracts/factories/NFTProviderAndCollectionFactory.cdc index a79755e..ebeb4e4 100644 --- a/contracts/factories/NFTProviderAndCollectionFactory.cdc +++ b/contracts/factories/NFTProviderAndCollectionFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "NonFungibleToken" -pub contract NFTProviderAndCollectionFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(path) +access(all) contract NFTProviderAndCollectionFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check() { + return nil + } + + return con.capability as! Capability + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return nil } } } \ No newline at end of file diff --git a/contracts/factories/NFTProviderFactory.cdc b/contracts/factories/NFTProviderFactory.cdc index 7bf547e..a2214dc 100644 --- a/contracts/factories/NFTProviderFactory.cdc +++ b/contracts/factories/NFTProviderFactory.cdc @@ -1,10 +1,22 @@ import "CapabilityFactory" import "NonFungibleToken" -pub contract NFTProviderFactory { - pub struct Factory: CapabilityFactory.Factory { - pub fun getCapability(acct: &AuthAccount, path: CapabilityPath): Capability { - return acct.getCapability<&{NonFungibleToken.Provider}>(path) +access(all) contract NFTProviderFactory { + access(all) struct Factory: CapabilityFactory.Factory { + access(all) view fun getCapability(acct: auth(Capabilities) &Account, controllerID: UInt64): Capability? { + if let con = acct.capabilities.storage.getController(byCapabilityID: controllerID) { + if !con.capability.check() { + return nil + } + + return con.capability as! Capability + } + + return nil + } + + access(all) view fun getPublicCapability(acct: &Account, path: PublicPath): Capability? { + return nil } } } \ No newline at end of file diff --git a/contracts/standard/ExampleNFT.cdc b/contracts/standard/ExampleNFT.cdc new file mode 100644 index 0000000..b80e993 --- /dev/null +++ b/contracts/standard/ExampleNFT.cdc @@ -0,0 +1,376 @@ +/* +* +* This is an example implementation of a Flow Non-Fungible Token +* It is not part of the official standard but it assumed to be +* similar to how many NFTs would implement the core functionality. +* +* This contract does not implement any sophisticated classification +* system for its NFTs. It defines a simple NFT with minimal metadata. +* +*/ + +import "NonFungibleToken" +import "MetadataViews" +import "ViewResolver" +import "FungibleToken" + +access(all) contract ExampleNFT: ViewResolver { + + access(all) var totalSupply: UInt64 + + access(all) event ContractInitialized() + access(all) event Withdraw(id: UInt64, from: Address?) + access(all) event Deposit(id: UInt64, to: Address?) + + access(all) event CollectionCreated(id: UInt64) + access(all) event CollectionDestroyed(id: UInt64) + + access(all) let CollectionStoragePath: StoragePath + access(all) let CollectionPublicPath: PublicPath + access(all) let MinterStoragePath: StoragePath + access(all) let MinterPublicPath: PublicPath + + access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver { + access(all) let id: UInt64 + + access(all) let name: String + access(all) let description: String + access(all) let thumbnail: String + access(self) let royalties: [MetadataViews.Royalty] + + init( + id: UInt64, + name: String, + description: String, + thumbnail: String, + royalties: [MetadataViews.Royalty] + ) { + self.id = id + self.name = name + self.description = description + self.thumbnail = thumbnail + self.royalties = royalties + } + + access(all) view fun getViews(): [Type] { + return [ + Type(), + Type(), + Type(), + Type(), + Type(), + Type(), + Type() + ] + } + + access(all) view fun resolveView(_ view: Type): AnyStruct? { + switch view { + case Type(): + return MetadataViews.Display( + name: self.name, + description: self.description, + thumbnail: MetadataViews.HTTPFile( + url: self.thumbnail + ) + ) + case Type(): + // There is no max number of NFTs that can be minted from this contract + // so the max edition field value is set to nil + let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil) + let editionList: [MetadataViews.Edition] = [editionInfo] + return MetadataViews.Editions( + editionList + ) + case Type(): + return MetadataViews.Serial( + self.id + ) + case Type(): + return MetadataViews.Royalties( + self.royalties + ) + case Type(): + return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString())) + case Type(): + return MetadataViews.NFTCollectionData( + storagePath: ExampleNFT.CollectionStoragePath, + publicPath: ExampleNFT.CollectionPublicPath, + publicCollection: Type<&ExampleNFT.Collection>(), + publicLinkedType: Type<&ExampleNFT.Collection>(), + createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} { + return <-ExampleNFT.createEmptyCollection() + }) + ) + case Type(): + let media = MetadataViews.Media( + file: MetadataViews.HTTPFile( + url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" + ), + mediaType: "image/svg+xml" + ) + return MetadataViews.NFTCollectionDisplay( + name: "The Example Collection", + description: "This collection is used as an example to help you develop your next Flow NFT.", + externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"), + squareImage: media, + bannerImage: media, + socials: { + "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") + } + ) + } + return nil + } + + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- ExampleNFT.createEmptyCollection() + } + } + + access(all) resource interface ExampleNFTCollectionPublic: NonFungibleToken.Collection { + access(all) fun deposit(token: @{NonFungibleToken.NFT}) + access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? { + post { + (result == nil) || (result?.id == id): + "Cannot borrow ExampleNFT reference: the ID of the returned reference is incorrect" + } + } + } + + access(all) resource Collection: ExampleNFTCollectionPublic { + access(all) event ResourceDestroyed(id: UInt64 = self.uuid) + + // dictionary of NFT conforming tokens + // NFT is a resource type with an `UInt64` ID field + access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} + + init () { + self.ownedNFTs <- {} + emit CollectionCreated(id: self.uuid) + } + + access(all) view fun getLength(): Int { + return self.ownedNFTs.length + } + + // withdraw removes an NFT from the collection and moves it to the caller + access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { + let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") + + emit Withdraw(id: token.id, from: self.owner?.address) + + return <-token + } + + // deposit takes a NFT and adds it to the collections dictionary + // and adds the ID to the id array + access(all) fun deposit(token: @{NonFungibleToken.NFT}) { + let token <- token as! @ExampleNFT.NFT + + let id: UInt64 = token.id + + // add the new token to the dictionary which removes the old one + let oldToken <- self.ownedNFTs[id] <- token + + emit Deposit(id: id, to: self.owner?.address) + + destroy oldToken + } + + // getIDs returns an array of the IDs that are in the collection + access(all) view fun getIDs(): [UInt64] { + return self.ownedNFTs.keys + } + + // borrowNFT gets a reference to an NFT in the collection + // so that the caller can read its metadata and call its methods + access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { + return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! + } + + access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? { + if self.ownedNFTs[id] != nil { + // Create an authorized reference to allow downcasting + let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! + return ref as! &ExampleNFT.NFT + } + + return nil + } + + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- create Collection() + } + + access(all) view fun getSupportedNFTTypes(): {Type: Bool} { + return { + Type<@ExampleNFT.NFT>(): true + } + } + + access(all) view fun isSupportedNFTType(type: Type): Bool { + return type == Type<@ExampleNFT.NFT>() + } + } + + // public function that anyone can call to create a new empty collection + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- create Collection() + } + + // Resource that an admin or something similar would own to be + // able to mint new NFTs + // + access(all) resource NFTMinter { + + // mintNFT mints a new NFT with a new ID + // and deposit it in the recipients collection using their collection reference + access(all) fun mintNFT( + recipient: &{NonFungibleToken.CollectionPublic}, + name: String, + description: String, + thumbnail: String, + royaltyReceipient: Address, + ) { + ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1 + self.mintNFTWithId(recipient: recipient, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: royaltyReceipient, id: ExampleNFT.totalSupply) + } + + access(all) fun mintNFTWithId( + recipient: &{NonFungibleToken.CollectionPublic}, + name: String, + description: String, + thumbnail: String, + royaltyReceipient: Address, + id: UInt64 + ) { + let royaltyRecipient = getAccount(royaltyReceipient).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)! + let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: 0.05, description: "") + // create a new NFT + var newNFT <- create NFT( + id: id, + name: name, + description: description, + thumbnail: thumbnail, + royalties: [cutInfo] + ) + + // deposit it in the recipient's account using their reference + recipient.deposit(token: <-newNFT) + } + + // mintNFT mints a new NFT with a new ID + // and deposit it in the recipients collection using their collection reference + access(all) fun mintNFTWithRoyaltyCuts( + recipient: &{NonFungibleToken.CollectionPublic}, + name: String, + description: String, + thumbnail: String, + royaltyReceipients: [Address], + royaltyCuts: [UFix64] + ) { + assert(royaltyReceipients.length == royaltyCuts.length, message: "mismatched royalty recipients and cuts") + let royalties: [MetadataViews.Royalty] = [] + + var index = 0 + while index < royaltyReceipients.length { + let royaltyRecipient = getAccount(royaltyReceipients[index]).capabilities.get<&{FungibleToken.Receiver}>(/public/placeholder)! + let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: royaltyCuts[index], description: "") + royalties.append(cutInfo) + index = index + 1 + } + + ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1 + + // create a new NFT + var newNFT <- create NFT( + id: ExampleNFT.totalSupply, + name: name, + description: description, + thumbnail: thumbnail, + royalties: royalties + ) + + // deposit it in the recipient's account using their reference + recipient.deposit(token: <-newNFT) + } + } + + /// Function that resolves a metadata view for this contract. + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { + case Type(): + return MetadataViews.NFTCollectionData( + storagePath: ExampleNFT.CollectionStoragePath, + publicPath: ExampleNFT.CollectionPublicPath, + publicCollection: Type<&ExampleNFT.Collection>(), + publicLinkedType: Type<&ExampleNFT.Collection>(), + createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} { + return <-ExampleNFT.createEmptyCollection() + }) + ) + case Type(): + let media = MetadataViews.Media( + file: MetadataViews.HTTPFile( + url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" + ), + mediaType: "image/svg+xml" + ) + return MetadataViews.NFTCollectionDisplay( + name: "The Example Collection", + description: "This collection is used as an example to help you develop your next Flow NFT.", + externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"), + squareImage: media, + bannerImage: media, + socials: { + "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") + } + ) + } + return nil + } + + /// Function that returns all the Metadata Views implemented by a Non Fungible Token + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getContractViews(resourceType: Type?): [Type] { + return [ + Type(), + Type() + ] + } + + init() { + // Initialize the total supply + self.totalSupply = 0 + + // Set the named paths + self.CollectionStoragePath = /storage/exampleNFTCollection + self.CollectionPublicPath = /public/exampleNFTCollection + self.MinterStoragePath = /storage/exampleNFTMinter + self.MinterPublicPath = /public/exampleNFTMinter + + // Create a Collection resource and save it to storage + let collection <- create Collection() + self.account.storage.save(<-collection, to: self.CollectionStoragePath) + let cap = self.account.capabilities.storage.issue<&ExampleNFT.Collection>(self.CollectionStoragePath) + self.account.capabilities.publish(cap, at: self.CollectionPublicPath) + + + // Create a Minter resource and save it to storage + let minter <- create NFTMinter() + self.account.storage.save(<-minter, to: self.MinterStoragePath) + + let minterCap = self.account.capabilities.storage.issue<&ExampleNFT.NFTMinter>(self.MinterStoragePath) + + emit ContractInitialized() + } +} + \ No newline at end of file diff --git a/contracts/standard/ExampleNFT2.cdc b/contracts/standard/ExampleNFT2.cdc index 0af7730..cc65ad7 100644 --- a/contracts/standard/ExampleNFT2.cdc +++ b/contracts/standard/ExampleNFT2.cdc @@ -12,64 +12,47 @@ import "NonFungibleToken" import "MetadataViews" import "ViewResolver" +import "FungibleToken" -pub contract ExampleNFT2: NonFungibleToken, ViewResolver { +access(all) contract ExampleNFT2: ViewResolver { - /// Total supply of ExampleNFTs in existence - pub var totalSupply: UInt64 + access(all) var totalSupply: UInt64 - /// The event that is emitted when the contract is created - pub event ContractInitialized() + access(all) event ContractInitialized() + access(all) event Withdraw(id: UInt64, from: Address?) + access(all) event Deposit(id: UInt64, to: Address?) - /// The event that is emitted when an NFT is withdrawn from a Collection - pub event Withdraw(id: UInt64, from: Address?) + access(all) event CollectionCreated(id: UInt64) + access(all) event CollectionDestroyed(id: UInt64) - /// The event that is emitted when an NFT is deposited to a Collection - pub event Deposit(id: UInt64, to: Address?) + access(all) let CollectionStoragePath: StoragePath + access(all) let CollectionPublicPath: PublicPath + access(all) let MinterStoragePath: StoragePath + access(all) let MinterPublicPath: PublicPath - /// Storage and Public Paths - pub let CollectionStoragePath: StoragePath - pub let CollectionPublicPath: PublicPath - pub let MinterStoragePath: StoragePath + access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver { + access(all) let id: UInt64 - /// The core resource that represents a Non Fungible Token. - /// New instances will be created using the NFTMinter resource - /// and stored in the Collection resource - /// - pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver { - - /// The unique ID that each NFT has - pub let id: UInt64 - - /// Metadata fields - pub let name: String - pub let description: String - pub let thumbnail: String + access(all) let name: String + access(all) let description: String + access(all) let thumbnail: String access(self) let royalties: [MetadataViews.Royalty] - access(self) let metadata: {String: AnyStruct} - + init( id: UInt64, name: String, description: String, thumbnail: String, - royalties: [MetadataViews.Royalty], - metadata: {String: AnyStruct}, + royalties: [MetadataViews.Royalty] ) { self.id = id self.name = name self.description = description self.thumbnail = thumbnail self.royalties = royalties - self.metadata = metadata } - - /// Function that returns all the Metadata Views implemented by a Non Fungible Token - /// - /// @return An array of Types defining the implemented views. This value will be used by - /// developers to know which parameter to pass to the resolveView() method. - /// - pub fun getViews(): [Type] { + + access(all) view fun getViews(): [Type] { return [ Type(), Type(), @@ -77,17 +60,11 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { Type(), Type(), Type(), - Type(), - Type() + Type() ] } - /// Function that resolves a metadata view for this token. - /// - /// @param view: The Type of the desired view. - /// @return A structure representing the requested view. - /// - pub fun resolveView(_ view: Type): AnyStruct? { + access(all) view fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return MetadataViews.Display( @@ -119,11 +96,9 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { return MetadataViews.NFTCollectionData( storagePath: ExampleNFT2.CollectionStoragePath, publicPath: ExampleNFT2.CollectionPublicPath, - providerPath: /private/exampleNFT2Collection, - publicCollection: Type<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic}>(), - publicLinkedType: Type<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(), - providerLinkedType: Type<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(), - createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection { + publicCollection: Type<&ExampleNFT2.Collection>(), + publicLinkedType: Type<&ExampleNFT2.Collection>(), + createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} { return <-ExampleNFT2.createEmptyCollection() }) ) @@ -144,34 +119,18 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") } ) - case Type(): - // exclude mintedTime and foo to show other uses of Traits - let excludedTraits = ["mintedTime", "foo"] - let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits) - - // mintedTime is a unix timestamp, we should mark it with a displayType so platforms know how to show it. - let mintedTimeTrait = MetadataViews.Trait(name: "mintedTime", value: self.metadata["mintedTime"]!, displayType: "Date", rarity: nil) - traitsView.addTrait(mintedTimeTrait) - - // foo is a trait with its own rarity - let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common") - let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity) - traitsView.addTrait(fooTrait) - - return traitsView - } return nil } + + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- ExampleNFT2.createEmptyCollection() + } } - /// Defines the methods that are particular to this NFT contract collection - /// - pub resource interface ExampleNFT2CollectionPublic { - pub fun deposit(token: @NonFungibleToken.NFT) - pub fun getIDs(): [UInt64] - pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT - pub fun borrowExampleNFT(id: UInt64): &ExampleNFT2.NFT? { + access(all) resource interface ExampleNFT2CollectionPublic: NonFungibleToken.Collection { + access(all) fun deposit(token: @{NonFungibleToken.NFT}) + access(all) fun borrowExampleNFT2(id: UInt64): &ExampleNFT2.NFT? { post { (result == nil) || (result?.id == id): "Cannot borrow ExampleNFT2 reference: the ID of the returned reference is incorrect" @@ -179,25 +138,24 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { } } - /// The resource that will be holding the NFTs inside any account. - /// In order to be able to manage NFTs any account will need to create - /// an empty collection first - /// - pub resource Collection: ExampleNFT2CollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection { + access(all) resource Collection: ExampleNFT2CollectionPublic { + access(all) event ResourceDestroyed(id: UInt64 = self.uuid) + // dictionary of NFT conforming tokens // NFT is a resource type with an `UInt64` ID field - pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} + access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} init () { self.ownedNFTs <- {} + emit CollectionCreated(id: self.uuid) + } + + access(all) view fun getLength(): Int { + return self.ownedNFTs.length } - /// Removes an NFT from the collection and moves it to the caller - /// - /// @param withdrawID: The ID of the NFT that wants to be withdrawn - /// @return The NFT resource that has been taken out of the collection - /// - pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { + // withdraw removes an NFT from the collection and moves it to the caller + access(NonFungibleToken.Withdraw ) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") emit Withdraw(id: token.id, from: self.owner?.address) @@ -205,11 +163,9 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { return <-token } - /// Adds an NFT to the collections dictionary and adds the ID to the id array - /// - /// @param token: The NFT resource to be included in the collection - /// - pub fun deposit(token: @NonFungibleToken.NFT) { + // deposit takes a NFT and adds it to the collections dictionary + // and adds the ID to the id array + access(all) fun deposit(token: @{NonFungibleToken.NFT}) { let token <- token as! @ExampleNFT2.NFT let id: UInt64 = token.id @@ -222,95 +178,110 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { destroy oldToken } - /// Helper method for getting the collection IDs - /// - /// @return An array containing the IDs of the NFTs in the collection - /// - pub fun getIDs(): [UInt64] { + // getIDs returns an array of the IDs that are in the collection + access(all) view fun getIDs(): [UInt64] { return self.ownedNFTs.keys } - /// Gets a reference to an NFT in the collection so that - /// the caller can read its metadata and call its methods - /// - /// @param id: The ID of the wanted NFT - /// @return A reference to the wanted NFT resource - /// - pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT { - return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)! + // borrowNFT gets a reference to an NFT in the collection + // so that the caller can read its metadata and call its methods + access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { + return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! } - /// Gets a reference to an NFT in the collection so that - /// the caller can read its metadata and call its methods - /// - /// @param id: The ID of the wanted NFT - /// @return A reference to the wanted NFT resource - /// - pub fun borrowExampleNFT(id: UInt64): &ExampleNFT2.NFT? { + access(all) fun borrowExampleNFT2(id: UInt64): &ExampleNFT2.NFT? { if self.ownedNFTs[id] != nil { // Create an authorized reference to allow downcasting - let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)! + let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! return ref as! &ExampleNFT2.NFT } return nil } - /// Gets a reference to the NFT only conforming to the `{MetadataViews.Resolver}` - /// interface so that the caller can retrieve the views that the NFT - /// is implementing and resolve them - /// - /// @param id: The ID of the wanted NFT - /// @return The resource reference conforming to the Resolver interface - /// - pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} { - let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)! - let exampleNFT = nft as! &ExampleNFT2.NFT - return exampleNFT as &AnyResource{MetadataViews.Resolver} + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- create Collection() } - destroy() { - destroy self.ownedNFTs + access(all) view fun getSupportedNFTTypes(): {Type: Bool} { + return { + Type<@ExampleNFT2.NFT>(): true + } + } + + access(all) view fun isSupportedNFTType(type: Type): Bool { + return type == Type<@ExampleNFT2.NFT>() } } - /// Allows anyone to create a new empty collection - /// - /// @return The new Collection resource - /// - pub fun createEmptyCollection(): @NonFungibleToken.Collection { + // public function that anyone can call to create a new empty collection + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <- create Collection() } - /// Resource that an admin or something similar would own to be - /// able to mint new NFTs - /// - pub resource NFTMinter { - - /// Mints a new NFT with a new ID and deposit it in the - /// recipients collection using their collection reference - /// - /// @param recipient: A capability to the collection where the new NFT will be deposited - /// @param name: The name for the NFT metadata - /// @param description: The description for the NFT metadata - /// @param thumbnail: The thumbnail for the NFT metadata - /// @param royalties: An array of Royalty structs, see MetadataViews docs - /// - pub fun mintNFT( + // Resource that an admin or something similar would own to be + // able to mint new NFTs + // + access(all) resource NFTMinter { + + // mintNFT mints a new NFT with a new ID + // and deposit it in the recipients collection using their collection reference + access(all) fun mintNFT( recipient: &{NonFungibleToken.CollectionPublic}, name: String, description: String, thumbnail: String, - royalties: [MetadataViews.Royalty] + royaltyReceipient: Address, ) { - let metadata: {String: AnyStruct} = {} - let currentBlock = getCurrentBlock() - metadata["mintedBlock"] = currentBlock.height - metadata["mintedTime"] = currentBlock.timestamp - metadata["minter"] = recipient.owner!.address + ExampleNFT2.totalSupply = ExampleNFT2.totalSupply + 1 + self.mintNFTWithId(recipient: recipient, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: royaltyReceipient, id: ExampleNFT2.totalSupply) + } - // this piece of metadata will be used to show embedding rarity into a trait - metadata["foo"] = "bar" + access(all) fun mintNFTWithId( + recipient: &{NonFungibleToken.CollectionPublic}, + name: String, + description: String, + thumbnail: String, + royaltyReceipient: Address, + id: UInt64 + ) { + let royaltyRecipient = getAccount(royaltyReceipient).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)! + let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: 0.05, description: "") + // create a new NFT + var newNFT <- create NFT( + id: id, + name: name, + description: description, + thumbnail: thumbnail, + royalties: [cutInfo] + ) + + // deposit it in the recipient's account using their reference + recipient.deposit(token: <-newNFT) + } + + // mintNFT mints a new NFT with a new ID + // and deposit it in the recipients collection using their collection reference + access(all) fun mintNFTWithRoyaltyCuts( + recipient: &{NonFungibleToken.CollectionPublic}, + name: String, + description: String, + thumbnail: String, + royaltyReceipients: [Address], + royaltyCuts: [UFix64] + ) { + assert(royaltyReceipients.length == royaltyCuts.length, message: "mismatched royalty recipients and cuts") + let royalties: [MetadataViews.Royalty] = [] + + var index = 0 + while index < royaltyReceipients.length { + let royaltyRecipient = getAccount(royaltyReceipients[index]).capabilities.get<&{FungibleToken.Receiver}>(/public/placeholder)! + let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: royaltyCuts[index], description: "") + royalties.append(cutInfo) + index = index + 1 + } + + ExampleNFT2.totalSupply = ExampleNFT2.totalSupply + 1 // create a new NFT var newNFT <- create NFT( @@ -318,14 +289,11 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { name: name, description: description, thumbnail: thumbnail, - royalties: royalties, - metadata: metadata, + royalties: royalties ) // deposit it in the recipient's account using their reference recipient.deposit(token: <-newNFT) - - ExampleNFT2.totalSupply = ExampleNFT2.totalSupply + UInt64(1) } } @@ -334,19 +302,17 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { /// @param view: The Type of the desired view. /// @return A structure representing the requested view. /// - pub fun resolveView(_ view: Type): AnyStruct? { - switch view { + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { case Type(): return MetadataViews.NFTCollectionData( - storagePath: ExampleNFT2.CollectionStoragePath, - publicPath: ExampleNFT2.CollectionPublicPath, - providerPath: /private/exampleNFT2Collection, - publicCollection: Type<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic}>(), - publicLinkedType: Type<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(), - providerLinkedType: Type<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(), - createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection { - return <-ExampleNFT2.createEmptyCollection() - }) + storagePath: ExampleNFT2.CollectionStoragePath, + publicPath: ExampleNFT2.CollectionPublicPath, + publicCollection: Type<&ExampleNFT2.Collection>(), + publicLinkedType: Type<&ExampleNFT2.Collection>(), + createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} { + return <-ExampleNFT2.createEmptyCollection() + }) ) case Type(): let media = MetadataViews.Media( @@ -355,6 +321,16 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { ), mediaType: "image/svg+xml" ) + return MetadataViews.NFTCollectionDisplay( + name: "The Example Collection", + description: "This collection is used as an example to help you develop your next Flow NFT.", + externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"), + squareImage: media, + bannerImage: media, + socials: { + "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") + } + ) } return nil } @@ -364,7 +340,7 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { /// @return An array of Types defining the implemented views. This value will be used by /// developers to know which parameter to pass to the resolveView() method. /// - pub fun getViews(): [Type] { + access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type(), Type() @@ -379,20 +355,20 @@ pub contract ExampleNFT2: NonFungibleToken, ViewResolver { self.CollectionStoragePath = /storage/exampleNFT2Collection self.CollectionPublicPath = /public/exampleNFT2Collection self.MinterStoragePath = /storage/exampleNFT2Minter + self.MinterPublicPath = /public/exampleNFT2Minter // Create a Collection resource and save it to storage let collection <- create Collection() - self.account.save(<-collection, to: self.CollectionStoragePath) + self.account.storage.save(<-collection, to: self.CollectionStoragePath) + let cap = self.account.capabilities.storage.issue<&ExampleNFT2.Collection>(self.CollectionStoragePath) + self.account.capabilities.publish(cap, at: self.CollectionPublicPath) - // create a public capability for the collection - self.account.link<&ExampleNFT2.Collection{NonFungibleToken.CollectionPublic, ExampleNFT2.ExampleNFT2CollectionPublic, MetadataViews.ResolverCollection}>( - self.CollectionPublicPath, - target: self.CollectionStoragePath - ) // Create a Minter resource and save it to storage let minter <- create NFTMinter() - self.account.save(<-minter, to: self.MinterStoragePath) + self.account.storage.save(<-minter, to: self.MinterStoragePath) + + let minterCap = self.account.capabilities.storage.issue<&ExampleNFT2.NFTMinter>(self.MinterStoragePath) emit ContractInitialized() } diff --git a/contracts/standard/ExampleToken.cdc b/contracts/standard/ExampleToken.cdc index 13873a4..18b2c7c 100644 --- a/contracts/standard/ExampleToken.cdc +++ b/contracts/standard/ExampleToken.cdc @@ -1,38 +1,49 @@ import "FungibleToken" import "MetadataViews" +import "FungibleTokenMetadataViews" -pub contract ExampleToken: FungibleToken { +access(all) contract ExampleToken: FungibleToken { /// Total supply of ExampleTokens in existence - pub var totalSupply: UFix64 - - /// Storage and Public Paths - pub let VaultStoragePath: StoragePath - pub let VaultPublicPath: PublicPath - pub let ReceiverPublicPath: PublicPath - pub let AdminStoragePath: StoragePath + access(all) var totalSupply: UFix64 + /// TokensInitialized + /// /// The event that is emitted when the contract is created - pub event TokensInitialized(initialSupply: UFix64) + access(all) event TokensInitialized(initialSupply: UFix64) + /// TokensWithdrawn + /// /// The event that is emitted when tokens are withdrawn from a Vault - pub event TokensWithdrawn(amount: UFix64, from: Address?) + access(all) event TokensWithdrawn(amount: UFix64, from: Address?) + /// TokensDeposited + /// /// The event that is emitted when tokens are deposited to a Vault - pub event TokensDeposited(amount: UFix64, to: Address?) + access(all) event TokensDeposited(amount: UFix64, to: Address?) + /// TokensMinted + /// /// The event that is emitted when new tokens are minted - pub event TokensMinted(amount: UFix64) + access(all) event TokensMinted(amount: UFix64) + /// TokensBurned + /// /// The event that is emitted when tokens are destroyed - pub event TokensBurned(amount: UFix64) + access(all) event TokensBurned(amount: UFix64) + /// MinterCreated + /// /// The event that is emitted when a new minter resource is created - pub event MinterCreated(allowedAmount: UFix64) + access(all) event MinterCreated(allowedAmount: UFix64) + /// BurnerCreated + /// /// The event that is emitted when a new burner resource is created - pub event BurnerCreated() + access(all) event BurnerCreated() + /// Vault + /// /// Each user stores an instance of only the Vault in their storage /// The functions in the Vault and governed by the pre and post conditions /// in FungibleToken when they are called. @@ -43,41 +54,76 @@ pub contract ExampleToken: FungibleToken { /// out of thin air. A special Minter resource needs to be defined to mint /// new tokens. /// - pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance { + access(all) resource Vault: FungibleToken.Vault { /// The total balance of this vault - pub var balance: UFix64 + access(all) var balance: UFix64 - /// Initialize the balance at resource creation time + // initialize the balance at resource creation time init(balance: UFix64) { self.balance = balance } + access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool { + return amount <= self.balance + } + + access(all) view fun getSupportedVaultTypes(): {Type: Bool} { + return {self.getType(): true} + } + + access(all) view fun isSupportedVaultType(type: Type): Bool { + if (type == self.getType()) { return true } else { return false } + } + + access(all) fun createEmptyVault(): @{FungibleToken.Vault} { + return <-create Vault(balance: 0.0) + } + + /// Get all the Metadata Views implemented by ExampleToken + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getViews(): [Type]{ + return ExampleToken.getContractViews(resourceType: nil) + } + + /// Get a Metadata View from ExampleToken + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveView(_ view: Type): AnyStruct? { + return ExampleToken.resolveContractView(resourceType: nil, viewType: view) + } + + /// withdraw + /// /// Function that takes an amount as an argument /// and withdraws that amount from the Vault. + /// /// It creates a new temporary Vault that is used to hold /// the money that is being transferred. It returns the newly /// created Vault to the context that called so it can be deposited /// elsewhere. /// - /// @param amount: The amount of tokens to be withdrawn from the vault - /// @return The Vault resource containing the withdrawn funds - /// - pub fun withdraw(amount: UFix64): @FungibleToken.Vault { + access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} { self.balance = self.balance - amount emit TokensWithdrawn(amount: amount, from: self.owner?.address) return <-create Vault(balance: amount) } + /// deposit + /// /// Function that takes a Vault object as an argument and adds /// its balance to the balance of the owners Vault. + /// /// It is allowed to destroy the sent Vault because the Vault /// was a temporary holder of the tokens. The Vault's balance has /// been consumed and therefore can be destroyed. /// - /// @param from: The Vault resource containing the funds that will be deposited - /// - pub fun deposit(from: @FungibleToken.Vault) { + access(all) fun deposit(from: @{FungibleToken.Vault}) { let vault <- from as! @ExampleToken.Vault self.balance = self.balance + vault.balance emit TokensDeposited(amount: vault.balance, to: self.owner?.address) @@ -85,64 +131,58 @@ pub contract ExampleToken: FungibleToken { destroy vault } - destroy() { - if self.balance > 0.0 { - ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance - } + access(contract) fun burnCallback() { + ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance } - } + /// createEmptyVault + /// /// Function that creates a new Vault with a balance of zero /// and returns it to the calling context. A user must call this function /// and store the returned Vault in their storage in order to allow their /// account to be able to receive deposits of this token type. /// - /// @return The new Vault resource - /// - pub fun createEmptyVault(): @Vault { + access(all) fun createEmptyVault(vaultType: Type): @Vault { return <-create Vault(balance: 0.0) } - pub resource Administrator { + access(all) resource Administrator { - /// Function that creates and returns a new minter resource + /// createNewMinter /// - /// @param allowedAmount: The maximum quantity of tokens that the minter could create - /// @return The Minter resource that would allow to mint tokens + /// Function that creates and returns a new minter resource /// - + access(all) fun createNewMinter(allowedAmount: UFix64): @Minter { + emit MinterCreated(allowedAmount: allowedAmount) + return <-create Minter(allowedAmount: allowedAmount) + } - /// Function that creates and returns a new burner resource + /// createNewBurner /// - /// @return The Burner resource + /// Function that creates and returns a new burner resource /// - pub fun createNewBurner(): @Burner { + access(all) fun createNewBurner(): @Burner { emit BurnerCreated() return <-create Burner() } } - // EMULATOR ONLY, anyone can mint tokens - pub fun createNewMinter(allowedAmount: UFix64): @Minter { - emit MinterCreated(allowedAmount: allowedAmount) - return <-create Minter(allowedAmount: allowedAmount) - } - + /// Minter + /// /// Resource object that token admin accounts can hold to mint new tokens. /// - pub resource Minter { + access(all) resource Minter { /// The amount of tokens that the minter is allowed to mint - pub var allowedAmount: UFix64 + access(all) var allowedAmount: UFix64 + /// mintTokens + /// /// Function that mints new tokens, adds them to the total supply, /// and returns them to the calling context. /// - /// @param amount: The quantity of tokens to mint - /// @return The Vault resource containing the minted tokens - /// - pub fun mintTokens(amount: UFix64): @ExampleToken.Vault { + access(all) fun mintTokens(amount: UFix64): @ExampleToken.Vault { pre { amount > 0.0: "Amount minted must be greater than zero" amount <= self.allowedAmount: "Amount minted must be less than the allowed amount" @@ -158,18 +198,20 @@ pub contract ExampleToken: FungibleToken { } } + /// Burner + /// /// Resource object that token admin accounts can hold to burn tokens. /// - pub resource Burner { + access(all) resource Burner { + /// burnTokens + /// /// Function that destroys a Vault instance, effectively burning the tokens. /// /// Note: the burned tokens are automatically subtracted from the /// total supply in the Vault destructor. /// - /// @param from: The Vault resource containing the tokens to burn - /// - pub fun burnTokens(from: @FungibleToken.Vault) { + access(all) fun burnTokens(from: @{FungibleToken.Vault}) { let vault <- from as! @ExampleToken.Vault let amount = vault.balance destroy vault @@ -177,35 +219,87 @@ pub contract ExampleToken: FungibleToken { } } + /// Gets a list of the metadata views that this contract supports + access(all) view fun getContractViews(resourceType: Type?): [Type] { + return [Type(), + Type(), + Type(), + Type()] + } + + /// Get a Metadata View from ExampleToken + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { + case Type(): + return FungibleTokenMetadataViews.FTView( + ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTDisplay?, + ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ) + case Type(): + let media = MetadataViews.Media( + file: MetadataViews.HTTPFile( + url: "example.com" + ), + mediaType: "image/svg+xml" + ) + let medias = MetadataViews.Medias([media]) + return FungibleTokenMetadataViews.FTDisplay( + name: "EXAMPLE Token", + symbol: "EXAMPLE", + description: "This is an example token", + externalURL: MetadataViews.ExternalURL("https://flow.com"), + logos: medias, + socials: { + "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") + } + ) + case Type(): + let vaultRef = ExampleToken.account.storage.borrow(from: /storage/exampleTokenVault) + ?? panic("Could not borrow reference to the contract's Vault!") + return FungibleTokenMetadataViews.FTVaultData( + storagePath: /storage/exampleTokenVault, + receiverPath: /public/exampleTokenReceiver, + metadataPath: /public/exampleTokenBalance, + receiverLinkedType: Type<&{FungibleToken.Receiver, FungibleToken.Vault}>(), + metadataLinkedType: Type<&{FungibleToken.Balance, FungibleToken.Vault}>(), + createEmptyVaultFunction: (fun (): @{FungibleToken.Vault} { + return <-vaultRef.createEmptyVault() + }) + ) + case Type(): + return FungibleTokenMetadataViews.TotalSupply(totalSupply: ExampleToken.totalSupply) + } + 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 - self.VaultStoragePath = /storage/exampleTokenVault - self.VaultPublicPath = /public/exampleTokenMetadata - self.ReceiverPublicPath = /public/exampleTokenReceiver - self.AdminStoragePath = /storage/exampleTokenAdmin - // Create the Vault with the total supply of tokens and save it in storage. + // Create the Vault with the total supply of tokens and save it in storage + // let vault <- create Vault(balance: self.totalSupply) - self.account.save(<-vault, to: self.VaultStoragePath) - - // Create a public capability to the stored Vault that exposes - // the `deposit` method through the `Receiver` interface. - self.account.link<&{FungibleToken.Receiver}>( - self.ReceiverPublicPath, - target: self.VaultStoragePath - ) - - // Create a public capability to the stored Vault that only exposes - // the `balance` field and the `resolveView` method through the `Balance` interface - self.account.link<&ExampleToken.Vault{FungibleToken.Balance}>( - self.VaultPublicPath, - target: self.VaultStoragePath - ) + self.account.storage.save(<-vault, to: /storage/exampleTokenVault) + + let cap = self.account.capabilities.storage.issue<&{FungibleToken.Vault}>(/storage/exampleTokenVault) + self.account.capabilities.publish(cap, at: /public/exampleTokenReceiver) + self.account.capabilities.publish(cap, at: /public/exampleTokenBalance) + let admin <- create Administrator() - self.account.save(<-admin, to: self.AdminStoragePath) + self.account.storage.save(<-admin, to: /storage/exampleTokenAdmin) // Emit an event that shows that the contract was initialized + // emit TokensInitialized(initialSupply: self.totalSupply) } -} +} \ No newline at end of file diff --git a/flow.json b/flow.json index 967a5f2..6f36e87 100644 --- a/flow.json +++ b/flow.json @@ -1,21 +1,5 @@ { "contracts": { - "AddressUtils": { - "source": "./modules/flow-utils/cadence/contracts/AddressUtils.cdc", - "aliases": { - "emulator": "f8d6e0586b0a20c7", - "testnet": "31ad40c07a2a9788", - "mainnet": "a340dc0a4ec828ab" - } - }, - "ArrayUtils": { - "source": "./modules/flow-utils/cadence/contracts/ArrayUtils.cdc", - "aliases": { - "emulator": "f8d6e0586b0a20c7", - "testnet": "31ad40c07a2a9788", - "mainnet": "a340dc0a4ec828ab" - } - }, "CapabilityDelegator": { "source": "./contracts/CapabilityDelegator.cdc", "aliases": { @@ -44,7 +28,7 @@ } }, "ExampleNFT": { - "source": "./modules/flow-nft/contracts/ExampleNFT.cdc", + "source": "./contracts/standard/ExampleNFT.cdc", "aliases": { "testing": "0000000000000007" } @@ -70,6 +54,15 @@ "mainnet": "d8a7e05a7ac670c0" } }, + "FTVaultFactory": { + "source": "./contracts/factories/FTVaultFactory.cdc", + "aliases": { + "emulator": "f8d6e0586b0a20c7", + "testing": "0000000000000007", + "testnet": "294e44e1ec6993c6", + "mainnet": "d8a7e05a7ac670c0" + } + }, "FTBalanceFactory": { "source": "./contracts/factories/FTBalanceFactory.cdc", "aliases": { @@ -106,22 +99,6 @@ "mainnet": "d8a7e05a7ac670c0" } }, - "FlowToken": { - "source": "", - "aliases": { - "emulator": "0ae53cb6e3f42a79", - "testnet": "7e60df042a9c0868", - "mainnet": "1654653399040a61" - } - }, - "FungibleToken": { - "source": "./modules/flow-nft/contracts/utility/FungibleToken.cdc", - "aliases": { - "emulator": "ee82856bf20e2aa6", - "testnet": "9a0766d93b6608b7", - "mainnet": "f233dcee88fe0abe" - } - }, "HybridCustody": { "source": "./contracts/HybridCustody.cdc", "aliases": { @@ -131,14 +108,6 @@ "mainnet": "d8a7e05a7ac670c0" } }, - "MetadataViews": { - "source": "./modules/flow-nft/contracts/MetadataViews.cdc", - "aliases": { - "emulator": "f8d6e0586b0a20c7", - "testnet": "631e88ae7f1d7c20", - "mainnet": "1d7e57aa55817448" - } - }, "NFTCollectionPublicFactory": { "source": "./contracts/factories/NFTCollectionPublicFactory.cdc", "aliases": { @@ -165,28 +134,92 @@ "testnet": "294e44e1ec6993c6" } }, - "NonFungibleToken": { - "source": "./modules/flow-nft/contracts/NonFungibleToken.cdc", + "NFTCollectionFactory": { + "source": "./contracts/factories/NFTCollectionFactory.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", - "testnet": "631e88ae7f1d7c20", - "mainnet": "1d7e57aa55817448" + "testing": "0000000000000007", + "testnet": "294e44e1ec6993c6" + } + }, + "FungibleToken": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/FungibleToken.cdc", + "aliases": { + "emulator": "0xee82856bf20e2aa6", + "testnet": "0x9a0766d93b6608b7", + "mainnet": "0xf233dcee88fe0abe" + } + }, + "FungibleTokenMetadataViews": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/FungibleTokenMetadataViews.cdc", + "aliases": { + "emulator": "0xee82856bf20e2aa6", + "testnet": "0x9a0766d93b6608b7", + "mainnet": "0xf233dcee88fe0abe" + } + }, + "Burner": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/Burner.cdc", + "aliases": { + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x9a0766d93b6608b7", + "mainnet": "0xf233dcee88fe0abe" + } + }, + "FlowToken": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/FlowToken.cdc", + "aliases": { + "emulator": "0x0ae53cb6e3f42a79", + "testnet": "0x7e60df042a9c0868", + "mainnet": "0x1654653399040a61" + } + }, + "NonFungibleToken": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/NonFungibleToken.cdc", + "aliases": { + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x631e88ae7f1d7c20", + "mainnet": "0x1d7e57aa55817448" } }, "StringUtils": { - "source": "./modules/flow-utils/cadence/contracts/StringUtils.cdc", + "source": "./node_modules/@flowtyio/flow-contracts/contracts/flow-utils/StringUtils.cdc", "aliases": { - "emulator": "f8d6e0586b0a20c7", - "testnet": "31ad40c07a2a9788", - "mainnet": "a340dc0a4ec828ab" + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x31ad40c07a2a9788", + "mainnet": "0xa340dc0a4ec828ab" + } + }, + "ArrayUtils": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/flow-utils/ArrayUtils.cdc", + "aliases": { + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x31ad40c07a2a9788", + "mainnet": "0xa340dc0a4ec828ab" + } + }, + "AddressUtils": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/flow-utils/AddressUtils.cdc", + "aliases": { + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x31ad40c07a2a9788", + "mainnet": "0xa340dc0a4ec828ab" + } + }, + "MetadataViews": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/MetadataViews.cdc", + "aliases": { + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x631e88ae7f1d7c20", + "mainnet": "0x1d7e57aa55817448" } }, "ViewResolver": { - "source": "./modules/flow-nft/contracts/ViewResolver.cdc", + "source": "./node_modules/@flowtyio/flow-contracts/contracts/ViewResolver.cdc", "aliases": { - "emulator": "f8d6e0586b0a20c7", - "testnet": "631e88ae7f1d7c20", - "mainnet": "1d7e57aa55817448" + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x631e88ae7f1d7c20", + "mainnet": "0x1d7e57aa55817448" } } }, @@ -285,6 +318,10 @@ "hashAlgorithm": "SHA2_256", "resourceID": "projects/dl-flow-admin/locations/global/keyRings/hybrid-custody-testnet/cryptoKeys/hybrid-custody-testnet-key/cryptoKeyVersions/1" } + }, + "emulator-flowtoken": { + "address": "0ae53cb6e3f42a79", + "key": "686779d775e5fcbf8d2f4a85cb4c53525d02b7ef53230d180fc16f35d9b7d025" } }, "deployments": { @@ -308,12 +345,19 @@ "NFTProviderFactory", "NFTProviderAndCollectionFactory", "NFTCollectionPublicFactory", + "FTVaultFactory", + "NFTCollectionFactory", "ExampleNFT", "ExampleNFT2", - "ExampleToken" + "ExampleToken", + "Burner" ], "emulator-ft": [ - "FungibleToken" + "FungibleToken", + "FungibleTokenMetadataViews" + ], + "emulator-flowtoken": [ + "FlowToken" ] }, "testnet": { @@ -329,7 +373,9 @@ "FTAllFactory", "FTBalanceFactory", "FTReceiverBalanceFactory", - "FTReceiverFactory" + "FTReceiverFactory", + "FTVaultFactory", + "NFTCollectionFactory" ] }, "mainnet": { @@ -345,7 +391,9 @@ "FTAllFactory", "FTBalanceFactory", "FTReceiverBalanceFactory", - "FTReceiverFactory" + "FTReceiverFactory", + "FTVaultFactory", + "NFTCollectionFactory" ] } } diff --git a/modules/flow-ft b/modules/flow-ft new file mode 160000 index 0000000..a31032a --- /dev/null +++ b/modules/flow-ft @@ -0,0 +1 @@ +Subproject commit a31032af9b68d92b2c01eb85cc2103fae698bb19 diff --git a/modules/flow-nft b/modules/flow-nft index 9b6dc02..4c0c684 160000 --- a/modules/flow-nft +++ b/modules/flow-nft @@ -1 +1 @@ -Subproject commit 9b6dc02853d49d2dadf13d08b1922c58f879add4 +Subproject commit 4c0c684baed98e2ffd7a8f9d27973c1db5dd1239 diff --git a/modules/flow-utils b/modules/flow-utils index 23100ed..c10ce02 160000 --- a/modules/flow-utils +++ b/modules/flow-utils @@ -1 +1 @@ -Subproject commit 23100ed21d0f05c7874af158b4a05648ac707011 +Subproject commit c10ce024fad1abc67cde1e551ddc1e5f5771226c diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d8f1faa --- /dev/null +++ b/package-lock.json @@ -0,0 +1,35 @@ +{ + "name": "hybrid-custody", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hybrid-custody", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@flowtyio/flow-contracts": "^0.1.0-beta.19" + } + }, + "node_modules/@flowtyio/flow-contracts": { + "version": "0.1.0-beta.19", + "resolved": "https://registry.npmjs.org/@flowtyio/flow-contracts/-/flow-contracts-0.1.0-beta.19.tgz", + "integrity": "sha512-s88ttcc+BYwgR+eXyIAtL8bXPaSj2lVQgbpD+7huadfst8N8gri3g4uaS8KxXkHZw4WQojJmaoc16Fkvlp93gQ==", + "dependencies": { + "commander": "^11.0.0" + }, + "bin": { + "flow-contracts": "index.js" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "engines": { + "node": ">=16" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..350d7f7 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "hybrid-custody", + "version": "1.0.0", + "description": "![Tests](https://github.com/onflow/hybrid-custody/actions/workflows/integration-tests.yml/badge.svg) [![codecov](https://codecov.io/gh/onflow/hybrid-custody/branch/main/graph/badge.svg?token=5GWD5NHEKF)](https://codecov.io/gh/onflow/hybrid-custody)", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "./test.sh" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@flowtyio/flow-contracts": "^0.1.0-beta.19" + } +} diff --git a/scripts/delegator/find_nft_collection_cap.cdc b/scripts/delegator/find_nft_collection_cap.cdc index f5ef04f..d184236 100644 --- a/scripts/delegator/find_nft_collection_cap.cdc +++ b/scripts/delegator/find_nft_collection_cap.cdc @@ -3,14 +3,14 @@ import "CapabilityDelegator" import "NonFungibleToken" import "ExampleNFT" -pub fun main(addr: Address): Bool { +access(all) fun main(addr: Address): Bool { let acct = getAccount(addr) let delegator = - acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath).borrow() + acct.capabilities.get<&{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath)!.borrow() ?? panic("could not borrow delegator") - let desiredType = Type>() + let desiredType = Type>() let foundType = delegator.findFirstPublicType(desiredType) ?? panic("no type found") let nakedCap = delegator.getPublicCapability(foundType) ?? panic("requested capability type was not found") diff --git a/scripts/delegator/find_nft_provider_cap.cdc b/scripts/delegator/find_nft_provider_cap.cdc index 564b14b..2916183 100644 --- a/scripts/delegator/find_nft_provider_cap.cdc +++ b/scripts/delegator/find_nft_provider_cap.cdc @@ -3,14 +3,14 @@ import "CapabilityDelegator" import "NonFungibleToken" import "ExampleNFT" -pub fun main(addr: Address): Bool { - let acct = getAuthAccount(addr) +access(all) fun main(addr: Address): Bool { + let acct = getAuthAccount(addr) let delegator = - acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPrivate}>(CapabilityDelegator.PrivatePath).borrow() + acct.capabilities.storage.issue(CapabilityDelegator.StoragePath).borrow() ?? panic("could not borrow delegator") - let desiredType = Type>() + let desiredType = Type>() let foundType = delegator.findFirstPrivateType(desiredType) ?? panic("no type found") let nakedCap = delegator.getPrivateCapability(foundType) ?? panic("requested capability type was not found") diff --git a/scripts/delegator/get_all_private_caps.cdc b/scripts/delegator/get_all_private_caps.cdc index 8596f27..8c0b48c 100644 --- a/scripts/delegator/get_all_private_caps.cdc +++ b/scripts/delegator/get_all_private_caps.cdc @@ -3,13 +3,13 @@ import "CapabilityDelegator" import "NonFungibleToken" import "ExampleNFT" -pub fun main(address: Address): Bool { - let privateCaps: [Capability] = getAuthAccount(address).getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPrivate}>(CapabilityDelegator.PrivatePath) +access(all) fun main(address: Address): Bool { + let privateCaps: [Capability] = getAuthAccount(address).capabilities.storage.issue(CapabilityDelegator.StoragePath) .borrow() ?.getAllPrivate() ?? panic("could not borrow delegator") - let desiredType: Type = Type>() + let desiredType: Type = Type>() return privateCaps.length == 1 && privateCaps[0].getType() == desiredType } \ No newline at end of file diff --git a/scripts/delegator/get_all_public_caps.cdc b/scripts/delegator/get_all_public_caps.cdc index cc562ff..696f441 100644 --- a/scripts/delegator/get_all_public_caps.cdc +++ b/scripts/delegator/get_all_public_caps.cdc @@ -3,12 +3,12 @@ import "CapabilityDelegator" import "NonFungibleToken" import "ExampleNFT" -pub fun main(address: Address): Bool { - let delegator = getAccount(address).getCapability<&{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath).borrow() +access(all) fun main(address: Address): Bool { + let delegator = getAccount(address).capabilities.get<&{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath)!.borrow() ?? panic("delegator not found") let publicCaps: [Capability] = delegator.getAllPublic() assert(publicCaps.length > 0, message: "no public capabilities found") - let desiredType: Type = Type>() + let desiredType: Type = Type>() return publicCaps[0].getType() == desiredType } \ No newline at end of file diff --git a/scripts/delegator/get_nft_collection.cdc b/scripts/delegator/get_nft_collection.cdc index 1da6458..28b693f 100644 --- a/scripts/delegator/get_nft_collection.cdc +++ b/scripts/delegator/get_nft_collection.cdc @@ -3,19 +3,19 @@ import "CapabilityDelegator" import "NonFungibleToken" import "ExampleNFT" -pub fun main(addr: Address): Bool { +access(all) fun main(addr: Address): Bool { let acct = getAccount(addr) let delegator = - acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath).borrow() + acct.capabilities.get<&{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath)!.borrow() ?? panic("could not borrow delegator") - let capType = Type>() + let capType = Type>() let nakedCap = delegator.getPublicCapability(capType) ?? panic("requested capability type was not found") // we don't need to do anything with this cap, being able to cast here is enough to know // that this works - let cap = nakedCap as! Capability<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}> + let cap = nakedCap as! Capability<&{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}> return true } \ No newline at end of file diff --git a/scripts/delegator/get_nft_provider.cdc b/scripts/delegator/get_nft_provider.cdc index aee81eb..0753b65 100644 --- a/scripts/delegator/get_nft_provider.cdc +++ b/scripts/delegator/get_nft_provider.cdc @@ -3,19 +3,19 @@ import "CapabilityDelegator" import "NonFungibleToken" import "ExampleNFT" -pub fun main(addr: Address): Bool { - let acct = getAuthAccount(addr) +access(all) fun main(addr: Address): Bool { + let acct = getAuthAccount(addr) let delegator = - acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPrivate}>(CapabilityDelegator.PrivatePath).borrow() + acct.capabilities.storage.issue(CapabilityDelegator.StoragePath).borrow() ?? panic("could not borrow delegator") - let capType = Type>() + let capType = Type>() let nakedCap = delegator.getPrivateCapability(capType) ?? panic("requested capability type was not found") // we don't need to do anything with this cap, being able to cast here is enough to know // that this works - let cap = nakedCap as! Capability<&ExampleNFT.Collection{NonFungibleToken.Provider}> + let cap = nakedCap as! Capability return true } \ No newline at end of file diff --git a/scripts/example-nft-2/get_ids.cdc b/scripts/example-nft-2/get_ids.cdc index dc15c95..0bc27cc 100644 --- a/scripts/example-nft-2/get_ids.cdc +++ b/scripts/example-nft-2/get_ids.cdc @@ -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(addr) + let collection = acct.storage.borrow<&ExampleNFT2.Collection>(from: ExampleNFT2.CollectionStoragePath) ?? panic("collection not found") return collection.getIDs() } \ No newline at end of file diff --git a/scripts/example-nft/get_ids.cdc b/scripts/example-nft/get_ids.cdc index dfb4bed..e42c150 100644 --- a/scripts/example-nft/get_ids.cdc +++ b/scripts/example-nft/get_ids.cdc @@ -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(addr) + let collection = acct.storage.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath) ?? panic("collection not found") return collection.getIDs() } \ No newline at end of file diff --git a/scripts/example-nft/mint_to_account.cdc b/scripts/example-nft/mint_to_account.cdc index d25f9a2..dd0b73d 100644 --- a/scripts/example-nft/mint_to_account.cdc +++ b/scripts/example-nft/mint_to_account.cdc @@ -6,15 +6,16 @@ import "ExampleNFT" transaction(receiver: Address, name: String, description: String, thumbnail: String) { let minter: &ExampleNFT.NFTMinter - prepare(acct: AuthAccount) { - self.minter = acct.borrow<&ExampleNFT.NFTMinter>(from: ExampleNFT.MinterStoragePath) ?? panic("minter not found") + prepare(acct: auth(BorrowValue) &Account) { + self.minter = acct.storage.borrow<&ExampleNFT.NFTMinter>(from: ExampleNFT.MinterStoragePath) ?? panic("minter not found") } execute { - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let c = getAccount(receiver).getCapability<&{NonFungibleToken.CollectionPublic}>(d.publicPath) - let r = c.borrow() ?? panic("no receiver collection") - self.minter.mintNFT(recipient: r, name: name, description: description, thumbnail: thumbnail, royalties: []) + let c = getAccount(receiver).capabilities.get<&{NonFungibleToken.CollectionPublic}>(d.publicPath) + ?? panic("no receiver capability found") + let r = c.borrow() ?? panic("could not borrow collection") + self.minter.mintNFT(recipient: r, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: self.minter.owner!.address) } } diff --git a/scripts/example-token/get_balance.cdc b/scripts/example-token/get_balance.cdc index 120cf61..f665ac5 100644 --- a/scripts/example-token/get_balance.cdc +++ b/scripts/example-token/get_balance.cdc @@ -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()) 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 diff --git a/scripts/factory/get_ft_balance_from_factory.cdc b/scripts/factory/get_ft_balance_from_factory.cdc index f6b713b..829a665 100644 --- a/scripts/factory/get_ft_balance_from_factory.cdc +++ b/scripts/factory/get_ft_balance_from_factory.cdc @@ -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(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()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + factory.getPublicCapability(acct: acct, path: vaultData.metadataPath)! as! Capability<&{FungibleToken.Balance}> } \ No newline at end of file diff --git a/scripts/factory/get_ft_provider_from_factory.cdc b/scripts/factory/get_ft_provider_from_factory.cdc index 510c277..c3d7f31 100644 --- a/scripts/factory/get_ft_provider_from_factory.cdc +++ b/scripts/factory/get_ft_provider_from_factory.cdc @@ -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(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()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + + acct.capabilities.storage.issue(vaultData.storagePath) + + let controllers = acct.capabilities.storage.getControllers(forPath: vaultData.storagePath) + for c in controllers { + if c.borrowType.isSubtype(of: Type()) { + factory.getCapability(acct: acct, controllerID: c.capabilityID)! as! Capability + return + } + } + + panic("should not reach this point") } \ No newline at end of file diff --git a/scripts/factory/get_ft_receiver_from_factory.cdc b/scripts/factory/get_ft_receiver_from_factory.cdc index b477940..23c0afe 100644 --- a/scripts/factory/get_ft_receiver_from_factory.cdc +++ b/scripts/factory/get_ft_receiver_from_factory.cdc @@ -1,13 +1,24 @@ import "FungibleToken" import "ExampleToken" +import "FungibleTokenMetadataViews" import "FTReceiverFactory" -pub fun main(addr: Address) { - let acct = getAuthAccount(addr) - let ref = &acct as &AuthAccount +access(all) fun main(addr: Address) { + let acct = getAuthAccount(addr) let factory = FTReceiverFactory.Factory() - let receiver = factory.getCapability(acct: ref, path: ExampleToken.ReceiverPublicPath) as! Capability<&{FungibleToken.Receiver}> + let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + let controllers = acct.capabilities.storage.getControllers(forPath: vaultData.storagePath) + + for c in controllers { + if c.borrowType.isSubtype(of: Type<&{FungibleToken.Receiver}>()) { + factory.getCapability(acct: acct, controllerID: c.capabilityID)! as! Capability<&{FungibleToken.Receiver}> + return + } + } + + panic("should not reach this point") } \ No newline at end of file diff --git a/scripts/factory/get_nft_provider_from_factory.cdc b/scripts/factory/get_nft_provider_from_factory.cdc index e04a09c..c49019c 100644 --- a/scripts/factory/get_nft_provider_from_factory.cdc +++ b/scripts/factory/get_nft_provider_from_factory.cdc @@ -4,13 +4,20 @@ import "NonFungibleToken" import "NFTProviderFactory" -pub fun main(addr: Address) { - let acct = getAuthAccount(addr) - let ref = &acct as &AuthAccount +access(all) fun main(addr: Address) { + let acct = getAuthAccount(addr) let factory = NFTProviderFactory.Factory() + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let controllers = acct.capabilities.storage.getControllers(forPath: d.storagePath) - let provider = factory.getCapability(acct: ref, path: d.providerPath) as! Capability<&{NonFungibleToken.Provider}> + for c in controllers { + if c.borrowType.isSubtype(of: Type()) { + factory.getCapability(acct: acct, controllerID: c.capabilityID)! as! Capability + return + } + } + + panic("should not reach this point") } \ No newline at end of file diff --git a/scripts/factory/get_nft_provider_from_factory_allowed.cdc b/scripts/factory/get_nft_provider_from_factory_allowed.cdc index cc8e960..4c963da 100644 --- a/scripts/factory/get_nft_provider_from_factory_allowed.cdc +++ b/scripts/factory/get_nft_provider_from_factory_allowed.cdc @@ -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(filterFactoryAddr) + let providerAcct = getAuthAccount(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()) ?? panic("No factory for NFT Provider found") - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! 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()) { + 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 + + let filter = ruleAcct.storage.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath) ?? panic("Problem borrowing CapabilityFilter AllowlistFilter") return filter.allowed(cap: provider) diff --git a/scripts/factory/get_supported_types_from_manager.cdc b/scripts/factory/get_supported_types_from_manager.cdc index c8e736e..ecf5987 100644 --- a/scripts/factory/get_supported_types_from_manager.cdc +++ b/scripts/factory/get_supported_types_from_manager.cdc @@ -2,9 +2,8 @@ import "CapabilityFactory" import "NonFungibleToken" -pub fun main(address: Address): [Type] { - let getterRef = getAccount(address).getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath) - .borrow() - ?? panic("CapabilityFactory Getter not found") +access(all) fun main(address: Address): [Type] { + let getterRef = getAccount(address).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) + .borrow() ?? panic("CapabilityFactory Getter not found") return getterRef.getSupportedTypes() } \ No newline at end of file diff --git a/scripts/hybrid-custody/borrow_owned_account.cdc b/scripts/hybrid-custody/borrow_owned_account.cdc new file mode 100644 index 0000000..0fde2bf --- /dev/null +++ b/scripts/hybrid-custody/borrow_owned_account.cdc @@ -0,0 +1,11 @@ +import "HybridCustody" + +access(all) fun main(owner: Address, child: Address): Address { + let m = getAuthAccount(owner).storage.borrow(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 +} \ No newline at end of file diff --git a/scripts/hybrid-custody/check_default_auth_acct_linked_path.cdc b/scripts/hybrid-custody/check_default_auth_acct_linked_path.cdc deleted file mode 100644 index ed57ad1..0000000 --- a/scripts/hybrid-custody/check_default_auth_acct_linked_path.cdc +++ /dev/null @@ -1,6 +0,0 @@ -import "HybridCustody" - -pub fun main(addr: Address): Bool { - let acct = getAuthAccount(addr) - return acct.getCapability<&AuthAccount>(HybridCustody.LinkedAccountPrivatePath).check() -} \ No newline at end of file diff --git a/scripts/hybrid-custody/get_account_cap_con_id.cdc b/scripts/hybrid-custody/get_account_cap_con_id.cdc new file mode 100644 index 0000000..ed25c89 --- /dev/null +++ b/scripts/hybrid-custody/get_account_cap_con_id.cdc @@ -0,0 +1,11 @@ +import "HybridCustody" + +access(all) fun main(addr: Address): UInt64? { + let acct: auth(Capabilities) &Account = getAuthAccount(addr) + let controllers = acct.capabilities.account.getControllers() + if controllers.length == 0 { + return nil + } + + return controllers[0].capabilityID +} \ No newline at end of file diff --git a/scripts/hybrid-custody/get_account_public_address.cdc b/scripts/hybrid-custody/get_account_public_address.cdc new file mode 100644 index 0000000..330e1d0 --- /dev/null +++ b/scripts/hybrid-custody/get_account_public_address.cdc @@ -0,0 +1,12 @@ +import "HybridCustody" + +access(all) fun main(parent: Address, child: Address): Address { + let cap = getAccount(parent).capabilities.get<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath) + let manager = cap.borrow() + ?? panic("unable to borrow manager") + + let acct = manager.borrowAccountPublic(addr: child) + ?? panic("child account not found") + + return acct.getAddress() +} \ No newline at end of file diff --git a/scripts/hybrid-custody/get_all_flow_balances.cdc b/scripts/hybrid-custody/get_all_flow_balances.cdc index fb93613..d1efc56 100644 --- a/scripts/hybrid-custody/get_all_flow_balances.cdc +++ b/scripts/hybrid-custody/get_all_flow_balances.cdc @@ -3,7 +3,7 @@ import "HybridCustody" /// Queries for $FLOW balance of a given Address and all its associated accounts /// -pub fun main(address: Address): {Address: UFix64} { +access(all) fun main(address: Address): {Address: UFix64} { // Get the balance for the given address let balances: {Address: UFix64} = { address: getAccount(address).balance } @@ -12,7 +12,7 @@ pub fun main(address: Address): {Address: UFix64} { /* Iterate over any associated accounts */ // - if let managerRef = getAuthAccount(address).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { + if let managerRef = getAuthAccount(address).storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { for childAccount in managerRef.getChildAddresses() { balances.insert(key: childAccount, getAccount(childAccount).balance) diff --git a/scripts/hybrid-custody/get_child_account_ft_capabilities.cdc b/scripts/hybrid-custody/get_child_account_ft_capabilities.cdc index b102d64..761ed49 100644 --- a/scripts/hybrid-custody/get_child_account_ft_capabilities.cdc +++ b/scripts/hybrid-custody/get_child_account_ft_capabilities.cdc @@ -3,38 +3,41 @@ import "FungibleToken" // This script iterates through a parent's child accounts, // identifies private paths with an accessible FungibleToken.Provider, and returns the corresponding typeIds -pub fun main(addr: Address):AnyStruct { - let account = getAuthAccount(addr) - let manager = getAuthAccount(addr).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist") +access(all) fun main(addr: Address):AnyStruct { + let account = getAuthAccount(addr) + let manager = getAuthAccount(addr).storage.borrow(from: HybridCustody.ManagerStoragePath) + ?? panic ("manager does not exist") - var typeIdsWithProvider = {} as {Address: [String]} + var typeIdsWithProvider: {Address: [String]} = {} - let providerType = Type>() + let providerType = Type() // Iterate through child accounts for address in manager.getChildAddresses() { - let addr = getAuthAccount(address) + let addr = getAuthAccount(address) let foundTypes: [String] = [] let childAcct = manager.borrowAccount(addr: address) ?? panic("child account not found") // get all private paths - addr.forEachPrivate(fun (path: PrivatePath, type: Type): Bool { - // Check which private paths have FT Provider AND can be borrowed - if !type.isSubtype(of: providerType){ - return true - } - if let cap = childAcct.getCapability(path: path, type: Type<&{FungibleToken.Provider}>()) { - let providerCap = cap as! Capability<&{FungibleToken.Provider}> - if !providerCap.check(){ - return true + for s in addr.storage.storagePaths { + for c in addr.capabilities.storage.getControllers(forPath: s) { + if !c.borrowType.isSubtype(of: providerType){ + continue } + if let cap = childAcct.getCapability(controllerID: c.capabilityID, type: providerType) { + let providerCap = cap as! Capability<&{FungibleToken.Provider}> + + if !providerCap.check(){ + continue + } + foundTypes.append(cap.borrow<&AnyResource>()!.getType().identifier) + typeIdsWithProvider[address] = foundTypes + break } - return true - }) - - typeIdsWithProvider[address] = foundTypes + } + } } return typeIdsWithProvider diff --git a/scripts/hybrid-custody/get_child_account_nft_capabilities.cdc b/scripts/hybrid-custody/get_child_account_nft_capabilities.cdc index 3d3a64a..85978f4 100644 --- a/scripts/hybrid-custody/get_child_account_nft_capabilities.cdc +++ b/scripts/hybrid-custody/get_child_account_nft_capabilities.cdc @@ -3,38 +3,41 @@ import "NonFungibleToken" // This script iterates through a parent's child accounts, // identifies private paths with an accessible NonFungibleToken.Provider, and returns the corresponding typeIds -pub fun main(addr: Address):AnyStruct { - let account = getAuthAccount(addr) - let manager = getAuthAccount(addr).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist") +access(all) fun main(addr: Address): AnyStruct { + let account = getAuthAccount(addr) + let manager = getAuthAccount(addr).storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist") - var typeIdsWithProvider = {} as {Address: [String]} + var typeIdsWithProvider: {Address: [String]} = {} - let providerType = Type>() + let providerType = Type() // Iterate through child accounts for address in manager.getChildAddresses() { - let addr = getAuthAccount(address) + let addr = getAuthAccount(address) let foundTypes: [String] = [] let childAcct = manager.borrowAccount(addr: address) ?? panic("child account not found") // get all private paths - addr.forEachPrivate(fun (path: PrivatePath, type: Type): Bool { - // Check which private paths have NFT Provider AND can be borrowed - if !type.isSubtype(of: providerType){ - return true - } - if let cap = childAcct.getCapability(path: path, type: Type<&{NonFungibleToken.Provider}>()) { - let providerCap = cap as! Capability<&{NonFungibleToken.Provider}> - if !providerCap.check(){ - return true + 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 cap = childAcct.getCapability(controllerID: c.capabilityID, type: providerType) { + let providerCap = cap as! Capability + + if !providerCap.check(){ + continue + } + foundTypes.append(cap.borrow<&AnyResource>()!.getType().identifier) + typeIdsWithProvider[address] = foundTypes + break } - return true - }) - - typeIdsWithProvider[address] = foundTypes + } + } } return typeIdsWithProvider diff --git a/scripts/hybrid-custody/get_child_addresses.cdc b/scripts/hybrid-custody/get_child_addresses.cdc index daea862..29f4876 100644 --- a/scripts/hybrid-custody/get_child_addresses.cdc +++ b/scripts/hybrid-custody/get_child_addresses.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(parent: Address): [Address] { - let acct = getAuthAccount(parent) - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address): [Address] { + let acct = getAuthAccount(parent) + let manager = acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") return manager.getChildAddresses() } \ No newline at end of file diff --git a/scripts/hybrid-custody/get_collection_from_inbox.cdc b/scripts/hybrid-custody/get_collection_from_inbox.cdc index 2a173fd..bb83cb0 100644 --- a/scripts/hybrid-custody/get_collection_from_inbox.cdc +++ b/scripts/hybrid-custody/get_collection_from_inbox.cdc @@ -2,13 +2,20 @@ import "HybridCustody" import "NonFungibleToken" import "ExampleNFT" -pub fun main(parent: Address, child: Address) { - let acct = getAuthAccount(parent) +access(all) fun main(parent: Address, child: Address) { + let acct = getAuthAccount(parent) + let childAcct: auth(Storage, Capabilities, Inbox) &Account = getAuthAccount(child) let inboxIdentifier = HybridCustody.getChildAccountIdentifier(parent) - let cap = acct.inbox.claim<&HybridCustody.ChildAccount{HybridCustody.AccountPrivate}>(inboxIdentifier, provider: child) + let cap = acct.inbox.claim(inboxIdentifier, provider: child) ?? panic("no inbox entry found") - cap.borrow()!.getCapability(path: ExampleNFT.CollectionPublicPath, type: Type<&{NonFungibleToken.CollectionPublic}>()) - ?? panic("capability not found") + for c in childAcct.capabilities.storage.getControllers(forPath: ExampleNFT.CollectionStoragePath) { + if c.borrowType.isSubtype(of: Type<&{NonFungibleToken.CollectionPublic}>()) { + cap.borrow()!.getCapability(controllerID: c.capabilityID, type: Type<&{NonFungibleToken.CollectionPublic}>()) + return + } + } + + panic("this should not be reached") } \ No newline at end of file diff --git a/scripts/hybrid-custody/get_collection_public_from_child.cdc b/scripts/hybrid-custody/get_collection_public_from_child.cdc new file mode 100644 index 0000000..ab9d113 --- /dev/null +++ b/scripts/hybrid-custody/get_collection_public_from_child.cdc @@ -0,0 +1,14 @@ +import "HybridCustody" +import "NonFungibleToken" + +access(all) fun main(parent: Address, child: Address, path: PublicPath, type: Type): [UInt64] { + let parentAcct = getAuthAccount(parent) + let manager = parentAcct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + ?? panic("manager not found") + let child = manager.borrowAccountPublic(addr: child) ?? panic("child account not found") + let cap = child.getPublicCapability(path: path, type: type) + ?? panic("could not get capability") + let collection = cap.borrow<&{NonFungibleToken.CollectionPublic}>() + ?? panic("failed to borrow collection") + return collection.getIDs() +} \ No newline at end of file diff --git a/scripts/hybrid-custody/get_examplenft_collection_from_delegator.cdc b/scripts/hybrid-custody/get_examplenft_collection_from_delegator.cdc index c655bba..5f47d34 100644 --- a/scripts/hybrid-custody/get_examplenft_collection_from_delegator.cdc +++ b/scripts/hybrid-custody/get_examplenft_collection_from_delegator.cdc @@ -1,8 +1,9 @@ import "HybridCustody" import "ExampleNFT" +import "NonFungibleToken" -pub fun main(parent: Address, child: Address, isPublic: Bool) { - let m = getAuthAccount(parent).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address, isPublic: Bool) { + let m = getAuthAccount(parent).storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") let acct = m.borrowAccount(addr: child) ?? panic("child account not found in manager") diff --git a/scripts/hybrid-custody/get_ft_provider_capability.cdc b/scripts/hybrid-custody/get_ft_provider_capability.cdc index 19cd9e7..119c123 100644 --- a/scripts/hybrid-custody/get_ft_provider_capability.cdc +++ b/scripts/hybrid-custody/get_ft_provider_capability.cdc @@ -2,16 +2,35 @@ import "HybridCustody" import "FungibleToken" import "ExampleToken" +import "FungibleTokenMetadataViews" // Verify that a child address borrowed as a child will let the parent borrow an FT provider capability -pub fun main(parent: Address, child: Address) { - let acct = getAuthAccount(parent) - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address) { + let acct = getAuthAccount(parent) + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager does not exist") let childAcct = m.borrowAccount(addr: child) ?? panic("child account not found") - let nakedCap = childAcct.getCapability(path: /private/exampleTokenProvider, type: Type<&{FungibleToken.Provider}>()) - ?? panic("Could not borrow reference to the owner's Vault!") + + let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + + // find the et provider + var controllerID: UInt64? = nil + let desiredType = Type() + let childAuthAcct = getAuthAccount(child) + for c in childAuthAcct.capabilities.storage.getControllers(forPath: vaultData.storagePath) { + if c.borrowType.isSubtype(of: desiredType) { + controllerID = c.capabilityID + break + } + } + + assert(controllerID != nil, message: "could not find controller id for FungibleToken Provider") + + + let nakedCap = childAcct.getCapability(controllerID: controllerID!, type: Type()) + ?? panic("Could not borrow reference to the owner's Vault!") let providerCap = nakedCap as! Capability<&{FungibleToken.Provider}> assert(providerCap.check(), message: "invalid provider capability") providerCap.borrow()! diff --git a/scripts/hybrid-custody/get_nft_collection_public_capability.cdc b/scripts/hybrid-custody/get_nft_collection_public_capability.cdc index 18da81f..8ce0b40 100644 --- a/scripts/hybrid-custody/get_nft_collection_public_capability.cdc +++ b/scripts/hybrid-custody/get_nft_collection_public_capability.cdc @@ -5,16 +5,19 @@ import "MetadataViews" import "ExampleNFT" // Verify that a child address borrowed as a child will let the parent borrow an NFT provider capability -pub fun main(parent: Address, child: Address) { - let acct = getAuthAccount(parent) - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address) { + let acct = getAuthAccount(parent) + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager does not exist") let childAcct = m.borrowAccount(addr: child) ?? panic("child account not found") - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData + let type = Type<&{NonFungibleToken.CollectionPublic}>() + let controllerId = childAcct.getControllerIDForType(type: type, forPath: d.storagePath) + ?? panic("no controller ID found for desired type") - let nakedCap = childAcct.getCapability(path: d.providerPath, type: Type<&{NonFungibleToken.CollectionPublic}>()) + let nakedCap = childAcct.getCapability(controllerID: controllerId, type: type) ?? panic("capability not found") let cap = nakedCap as! Capability<&{NonFungibleToken.CollectionPublic}> diff --git a/scripts/hybrid-custody/get_nft_provider_capability.cdc b/scripts/hybrid-custody/get_nft_provider_capability.cdc index f2401fb..d80c390 100644 --- a/scripts/hybrid-custody/get_nft_provider_capability.cdc +++ b/scripts/hybrid-custody/get_nft_provider_capability.cdc @@ -5,16 +5,20 @@ import "MetadataViews" import "ExampleNFT" // Verify that a child address borrowed as a child will let the parent borrow an NFT provider capability -pub fun main(parent: Address, child: Address) { - let acct = getAuthAccount(parent) - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address) { + let acct = getAuthAccount(parent) + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager does not exist") let childAcct = m.borrowAccount(addr: child) ?? panic("child account not found") - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let nakedCap = childAcct.getCapability(path: d.providerPath, type: Type<&{NonFungibleToken.Provider}>()) + let desiredType = Type() + let controllerID = childAcct.getControllerIDForType(type: desiredType, forPath: d.storagePath) + ?? panic("no capability found for desired type") + + let nakedCap = childAcct.getCapability(controllerID: controllerID, type: desiredType) ?? panic("capability not found") let cap = nakedCap as! Capability<&{NonFungibleToken.Provider}> diff --git a/scripts/hybrid-custody/get_num_valid_keys.cdc b/scripts/hybrid-custody/get_num_valid_keys.cdc index 2d87364..5242da3 100644 --- a/scripts/hybrid-custody/get_num_valid_keys.cdc +++ b/scripts/hybrid-custody/get_num_valid_keys.cdc @@ -1,5 +1,5 @@ -pub fun main(addr: Address): Int { - let acct = getAuthAccount(addr) +access(all) fun main(addr: Address): Int { + let acct = getAuthAccount(addr) var count = 0 acct.keys.forEach(fun (key: AccountKey): Bool { diff --git a/scripts/hybrid-custody/get_owner_of_child.cdc b/scripts/hybrid-custody/get_owner_of_child.cdc index 9d05416..05fbaa7 100644 --- a/scripts/hybrid-custody/get_owner_of_child.cdc +++ b/scripts/hybrid-custody/get_owner_of_child.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(addr: Address): Address? { - let acct = getAuthAccount(addr) - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(addr: Address): Address? { + let acct = getAuthAccount(addr) + let o = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account missing") return o.getOwner() diff --git a/scripts/hybrid-custody/get_parent_addresses.cdc b/scripts/hybrid-custody/get_parent_addresses.cdc index b4a1c94..46fa77c 100644 --- a/scripts/hybrid-custody/get_parent_addresses.cdc +++ b/scripts/hybrid-custody/get_parent_addresses.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(child: Address): [Address] { - let acct = getAuthAccount(child) - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(child: Address): [Address] { + let acct = getAuthAccount(child) + let o = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") return o.getParentAddresses() } \ No newline at end of file diff --git a/scripts/hybrid-custody/get_pending_owner_of_child.cdc b/scripts/hybrid-custody/get_pending_owner_of_child.cdc index e733bec..1cf9f63 100644 --- a/scripts/hybrid-custody/get_pending_owner_of_child.cdc +++ b/scripts/hybrid-custody/get_pending_owner_of_child.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(addr: Address): Address? { - let acct = getAuthAccount(addr) - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(addr: Address): Address? { + let acct = getAuthAccount(addr) + let o = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account missing") return o.getPendingOwner() diff --git a/scripts/hybrid-custody/get_spec_balance_from_public.cdc b/scripts/hybrid-custody/get_spec_balance_from_public.cdc index a44db15..0d044bd 100644 --- a/scripts/hybrid-custody/get_spec_balance_from_public.cdc +++ b/scripts/hybrid-custody/get_spec_balance_from_public.cdc @@ -3,13 +3,18 @@ import "HybridCustody" /// Returns the balance of the object (presumably a FungibleToken Vault) at the given path in the specified account /// -pub fun getVaultBalance(_ address: Address, _ balancePath: PublicPath): UFix64 { - return getAccount(address).getCapability<&{FungibleToken.Balance}>(balancePath).borrow()?.balance ?? 0.0 +access(all) fun getVaultBalance(_ address: Address, _ balancePath: PublicPath): UFix64 { + let cap = getAccount(address).capabilities.get<&{FungibleToken.Balance}>(balancePath) + if cap == nil { + return 0.0 + } + + return cap!.borrow()?.balance ?? 0.0 } /// Queries for FT.Vault balance of all FT.Vaults at given path in the specified account and all of its associated accounts /// -pub fun main(address: Address, balancePath: PublicPath): {Address: UFix64} { +access(all) fun main(address: Address, balancePath: PublicPath): {Address: UFix64} { // Get the balance for the given address let balances: {Address: UFix64} = { address: getVaultBalance(address, balancePath) } @@ -18,7 +23,7 @@ pub fun main(address: Address, balancePath: PublicPath): {Address: UFix64} { /* Iterate over any associated accounts */ // - if let managerRef = getAuthAccount(address).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { + if let managerRef = getAuthAccount(address).storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { for childAccount in managerRef.getChildAddresses() { balances.insert(key: childAccount, getVaultBalance(address, balancePath)) diff --git a/scripts/hybrid-custody/has_address_as_child.cdc b/scripts/hybrid-custody/has_address_as_child.cdc index 5bc5fe8..7f54937 100644 --- a/scripts/hybrid-custody/has_address_as_child.cdc +++ b/scripts/hybrid-custody/has_address_as_child.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(parent: Address, child: Address): Bool { - let acct = getAuthAccount(parent) - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address): Bool { + let acct = getAuthAccount(parent) + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") let childAccount = m.borrowAccount(addr: child) diff --git a/scripts/hybrid-custody/has_child_accounts.cdc b/scripts/hybrid-custody/has_child_accounts.cdc index 63f87cb..818a0b9 100644 --- a/scripts/hybrid-custody/has_child_accounts.cdc +++ b/scripts/hybrid-custody/has_child_accounts.cdc @@ -2,9 +2,9 @@ import "HybridCustody" /// Returns whether the given Address has a HybridCustod.Manager with child accounts or not /// -pub fun main(parent: Address): Bool { - let acct = getAuthAccount(parent) - if let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { +access(all) fun main(parent: Address): Bool { + let acct = getAuthAccount(parent) + if let manager = acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { return manager.getChildAddresses().length > 0 } return false diff --git a/scripts/hybrid-custody/has_owned_accounts.cdc b/scripts/hybrid-custody/has_owned_accounts.cdc index 4f6d9f3..c0ad0dc 100644 --- a/scripts/hybrid-custody/has_owned_accounts.cdc +++ b/scripts/hybrid-custody/has_owned_accounts.cdc @@ -2,9 +2,9 @@ import "HybridCustody" /// Returns whether the given Address has a HybridCustod.Manager with owned accounts or not /// -pub fun main(parent: Address): Bool { - let acct = getAuthAccount(parent) - if let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { +access(all) fun main(parent: Address): Bool { + let acct = getAuthAccount(parent) + if let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) { return manager.getOwnedAddresses().length > 0 } return false diff --git a/scripts/hybrid-custody/is_parent.cdc b/scripts/hybrid-custody/is_parent.cdc index ae82d71..d277f5b 100644 --- a/scripts/hybrid-custody/is_parent.cdc +++ b/scripts/hybrid-custody/is_parent.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(child: Address, parent: Address): Bool { - let acct = getAuthAccount(child) - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(child: Address, parent: Address): Bool { + let acct = getAuthAccount(child) + let owned = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") return owned.isChildOf(parent) diff --git a/scripts/hybrid-custody/is_redeemed.cdc b/scripts/hybrid-custody/is_redeemed.cdc index aecdedb..dfda220 100644 --- a/scripts/hybrid-custody/is_redeemed.cdc +++ b/scripts/hybrid-custody/is_redeemed.cdc @@ -1,8 +1,8 @@ import "HybridCustody" -pub fun main(child: Address, parent: Address): Bool { - let acct = getAuthAccount(child) - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(child: Address, parent: Address): Bool { + let acct = getAuthAccount(child) + let owned = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") return owned.getRedeemedStatus(addr: parent) ?? panic("no status found") diff --git a/scripts/hybrid-custody/metadata/assert_owned_account_display.cdc b/scripts/hybrid-custody/metadata/assert_owned_account_display.cdc index be49386..4dbe2bc 100644 --- a/scripts/hybrid-custody/metadata/assert_owned_account_display.cdc +++ b/scripts/hybrid-custody/metadata/assert_owned_account_display.cdc @@ -1,9 +1,9 @@ import "HybridCustody" import "MetadataViews" -pub fun main(addr: Address, name: String, desc: String, thumbnail: String): Bool { - let acct = getAuthAccount(addr) - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(addr: Address, name: String, desc: String, thumbnail: String): Bool { + let acct = getAuthAccount(addr) + let owned = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("no owned account found") let display = owned.resolveView(Type())! as! MetadataViews.Display diff --git a/scripts/hybrid-custody/metadata/resolve_child_display_name.cdc b/scripts/hybrid-custody/metadata/resolve_child_display_name.cdc index 24baaa0..f67dadd 100644 --- a/scripts/hybrid-custody/metadata/resolve_child_display_name.cdc +++ b/scripts/hybrid-custody/metadata/resolve_child_display_name.cdc @@ -1,13 +1,15 @@ import "HybridCustody" import "MetadataViews" -pub fun main(parent: Address, child: Address): String { - let acct = getAuthAccount(parent) - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address): String { + let acct = getAuthAccount(parent) + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") let c = m.borrowAccount(addr: child) ?? panic("child not found") + + let tmp = c.resolveView(Type()) ?? panic("unable to resolve metadata display") - let d = c.resolveView(Type())! as! MetadataViews.Display + let d = tmp as! MetadataViews.Display return d.name } \ No newline at end of file diff --git a/scripts/hybrid-custody/metadata/resolve_owned_display_name.cdc b/scripts/hybrid-custody/metadata/resolve_owned_display_name.cdc index 4e056bd..5164f1d 100644 --- a/scripts/hybrid-custody/metadata/resolve_owned_display_name.cdc +++ b/scripts/hybrid-custody/metadata/resolve_owned_display_name.cdc @@ -1,9 +1,9 @@ import "HybridCustody" import "MetadataViews" -pub fun main(child: Address): String { - let acct = getAuthAccount(child) - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) +access(all) fun main(child: Address): String { + let acct = getAuthAccount(child) + let o = acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") let d = o.resolveView(Type())! as! MetadataViews.Display diff --git a/scripts/hybrid-custody/verify_child_address.cdc b/scripts/hybrid-custody/verify_child_address.cdc index 0105895..607bfac 100644 --- a/scripts/hybrid-custody/verify_child_address.cdc +++ b/scripts/hybrid-custody/verify_child_address.cdc @@ -4,9 +4,9 @@ import "HybridCustody" Verify that a owned address borrowed as a child matches the address it is mapped to in the account manager */ -pub fun main(parent: Address, child: Address) { - let acct = getAuthAccount(parent) - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address) { + let acct = getAuthAccount(parent) + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager does not exist") let childAcct = m.borrowAccount(addr: child) ?? panic("child account not found") diff --git a/scripts/test/add_type_for_nft_provider_factory.cdc b/scripts/test/add_type_for_nft_provider_factory.cdc index 639d6d4..e87dc62 100644 --- a/scripts/test/add_type_for_nft_provider_factory.cdc +++ b/scripts/test/add_type_for_nft_provider_factory.cdc @@ -3,8 +3,8 @@ import "NFTProviderFactory" import "NonFungibleToken" -pub fun main(address: Address, type: Type): Bool { - let managerRef = getAuthAccount(address).borrow<&CapabilityFactory.Manager>( +access(all) fun main(address: Address, type: Type): Bool { + let managerRef = getAuthAccount(address).storage.borrow( from: CapabilityFactory.StoragePath ) ?? panic("CapabilityFactory Manager not found") diff --git a/scripts/test/can_get_child_factory_and_filter_caps.cdc b/scripts/test/can_get_child_factory_and_filter_caps.cdc index f4b28c4..03c9f7d 100644 --- a/scripts/test/can_get_child_factory_and_filter_caps.cdc +++ b/scripts/test/can_get_child_factory_and_filter_caps.cdc @@ -2,12 +2,25 @@ import "HybridCustody" // @addr - The address of the child account // @parent - The parent account that this child is assigned to -pub fun main(addr: Address, parent: Address): Bool { +access(all) fun main(addr: Address, parent: Address): Bool { let identifier = HybridCustody.getChildAccountIdentifier(parent) let path = PrivatePath(identifier: identifier) ?? panic("invalid public path identifier for parent address") - let acctPublic = getAuthAccount(addr).getCapability<&HybridCustody.ChildAccount{HybridCustody.AccountPublic}>(path) - .borrow() ?? panic("account public not found") + let acct = getAuthAccount(addr) + var controllerID: UInt64? = nil + for c in acct.capabilities.storage.getControllers(forPath: StoragePath(identifier: identifier)!) { + if c.borrowType.isSubtype(of: Type<&{HybridCustody.AccountPublic}>()) { + controllerID = c.capabilityID + break + } + } + + assert(controllerID != nil, message: "could not find controller id for parent identifier") + + let controller = getAuthAccount(addr).capabilities.storage.getController(byCapabilityID: controllerID!) + ?? panic("controller not found") + let cap = controller.capability as! Capability<&{HybridCustody.AccountPublic}> + let acctPublic = cap.borrow()! let factory = acctPublic.getCapabilityFactoryManager() assert(factory != nil, message: "capability factory is not valid") diff --git a/scripts/test/create_manager_with_invalid_filter.cdc b/scripts/test/create_manager_with_invalid_filter.cdc index fc8e90e..e40688b 100644 --- a/scripts/test/create_manager_with_invalid_filter.cdc +++ b/scripts/test/create_manager_with_invalid_filter.cdc @@ -1,9 +1,13 @@ import "CapabilityFilter" import "HybridCustody" -pub fun main(address: Address): Bool { +access(all) fun main(address: Address): Bool { + let acct = getAuthAccount(address) // Retrieving invalid Filter capability - let invalidFilterCap = getAuthAccount(address).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PrivatePath) + let invalidFilterCap = acct.capabilities.storage.issue<&{CapabilityFilter.Filter}>(CapabilityFilter.StoragePath) + + acct.capabilities.storage.getController(byCapabilityID: invalidFilterCap.id)!.delete() + // This step should fail due to CapabilityFilter.Filter Capability check on Manager init let manager <- HybridCustody.createManager(filter: invalidFilterCap) // Destroy and return diff --git a/scripts/test/get_all_vault_bal_from_storage.cdc b/scripts/test/get_all_vault_bal_from_storage.cdc index 2218904..368b3bc 100644 --- a/scripts/test/get_all_vault_bal_from_storage.cdc +++ b/scripts/test/get_all_vault_bal_from_storage.cdc @@ -4,9 +4,9 @@ import "HybridCustody" /// Returns a mapping of balances indexed on the Type of resource containing the balance /// -pub fun getAllBalancesInStorage(_ address: Address): {String: UFix64} { +access(all) fun getAllBalancesInStorage(_ address: Address): {String: UFix64} { // Get the account - let account: AuthAccount = getAuthAccount(address) + let account: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account = getAuthAccount(address) // Init for return value let balances: {String: UFix64} = {} // Track seen Types in array @@ -14,10 +14,10 @@ pub fun getAllBalancesInStorage(_ address: Address): {String: UFix64} { // Assign the type we'll need let balanceType: Type = Type<@{FungibleToken.Balance}>() // Iterate over all stored items & get the path if the type is what we're looking for - account.forEachStored(fun (path: StoragePath, type: Type): Bool { + account.storage.forEachStored(fun (path: StoragePath, type: Type): Bool { if type.isInstance(balanceType) || type.isSubtype(of: balanceType) { // Get a reference to the resource & its balance - let vaultRef = account.borrow<&{FungibleToken.Balance}>(from: path)! + let vaultRef = account.storage.borrow<&{FungibleToken.Balance}>(from: path)! // Insert a new values if it's the first time we've seen the type if !seen.contains(type) { balances.insert(key: type.identifier, vaultRef.balance) @@ -34,7 +34,7 @@ pub fun getAllBalancesInStorage(_ address: Address): {String: UFix64} { /// Queries for FT.Vault balance of all FT.Vaults in the specified account and all of its associated accounts /// -pub fun main(address: Address): {Address: {String: UFix64}} { +access(all) fun main(address: Address): {Address: {String: UFix64}} { // Get the balance for the given address let balances: {Address: {String: UFix64}} = { address: getAllBalancesInStorage(address) } @@ -43,9 +43,7 @@ pub fun main(address: Address): {Address: {String: UFix64}} { /* Iterate over any associated accounts */ // - if let managerRef = getAuthAccount(address) - .borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { - + if let managerRef = getAuthAccount(address).storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { for childAccount in managerRef.getChildAddresses() { balances.insert(key: childAccount, getAllBalancesInStorage(address)) seen.append(childAccount) diff --git a/scripts/test/get_flow_balance.cdc b/scripts/test/get_flow_balance.cdc index d2c5aef..497f649 100644 --- a/scripts/test/get_flow_balance.cdc +++ b/scripts/test/get_flow_balance.cdc @@ -1,3 +1,3 @@ -pub fun main(address: Address): UFix64 { +access(all) fun main(address: Address): UFix64 { return getAccount(address).balance } \ No newline at end of file diff --git a/scripts/test/get_nft_provider_capability_optional.cdc b/scripts/test/get_nft_provider_capability_optional.cdc index 85b8b06..635cb9d 100644 --- a/scripts/test/get_nft_provider_capability_optional.cdc +++ b/scripts/test/get_nft_provider_capability_optional.cdc @@ -5,16 +5,20 @@ import "MetadataViews" import "ExampleNFT" // Verify that a child address borrowed as a child will let the parent borrow an NFT provider capability -pub fun main(parent: Address, child: Address, returnsNil: Bool): Bool { - let acct = getAuthAccount(parent) - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) +access(all) fun main(parent: Address, child: Address, returnsNil: Bool): Bool { + let acct = getAuthAccount(parent) + let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager does not exist") let childAcct = manager.borrowAccount(addr: child) ?? panic("child account not found") - let collectionData = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let nakedCap = childAcct.getCapability(path: collectionData.providerPath, type: Type<&{NonFungibleToken.Provider}>()) + let type = Type() + let controllerID = childAcct.getControllerIDForType(type: type, forPath: collectionData.storagePath) + ?? panic("could not find controller for desired type") + + let nakedCap = childAcct.getCapability(controllerID: controllerID, type: type) return returnsNil ? nakedCap == nil : nakedCap?.borrow<&{NonFungibleToken.Provider}>() != nil } \ No newline at end of file diff --git a/scripts/test/get_type_from_factory.cdc b/scripts/test/get_type_from_factory.cdc index b266a46..8fcac58 100644 --- a/scripts/test/get_type_from_factory.cdc +++ b/scripts/test/get_type_from_factory.cdc @@ -2,8 +2,8 @@ import "NonFungibleToken" import "CapabilityFactory" -pub fun main(address: Address, type: Type): Bool { - let managerRef = getAuthAccount(address).borrow<&CapabilityFactory.Manager>( +access(all) fun main(address: Address, type: Type): Bool { + let managerRef = getAuthAccount(address).storage.borrow<&CapabilityFactory.Manager>( from: CapabilityFactory.StoragePath ) ?? panic("CapabilityFactory Manager not found") diff --git a/scripts/test/remove_nft_provider_factory.cdc b/scripts/test/remove_nft_provider_factory.cdc index 1462644..3dd408d 100644 --- a/scripts/test/remove_nft_provider_factory.cdc +++ b/scripts/test/remove_nft_provider_factory.cdc @@ -3,14 +3,14 @@ import "NFTProviderFactory" import "NonFungibleToken" -pub fun main(address: Address): Bool { +access(all) fun main(address: Address): Bool { - let managerRef = getAuthAccount(address).borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath) + let managerRef = getAuthAccount(address).storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("CapabilityFactory Manager not found") let expectedType = Type() - if let removed = managerRef.removeFactory(Type<&{NonFungibleToken.Provider}>()) { + if let removed = managerRef.removeFactory(Type()) { return removed.getType() == expectedType } diff --git a/scripts/test/test_get_accessible_child_nfts.cdc b/scripts/test/test_get_accessible_child_nfts.cdc index 43b1375..587dc6b 100644 --- a/scripts/test/test_get_accessible_child_nfts.cdc +++ b/scripts/test/test_get_accessible_child_nfts.cdc @@ -10,7 +10,7 @@ import "MetadataViews" /// Assertion method to ensure passing test /// -pub fun assertPassing(result: {Address: {UInt64: MetadataViews.Display}}, expectedAddressToIDs: {Address: [UInt64]}) { +access(all) fun assertPassing(result: {Address: {UInt64: MetadataViews.Display}}, expectedAddressToIDs: {Address: [UInt64]}) { for address in expectedAddressToIDs.keys { let expectedIDs: [UInt64] = expectedAddressToIDs[address]! @@ -22,42 +22,44 @@ pub fun assertPassing(result: {Address: {UInt64: MetadataViews.Display}}, expect } } -pub fun main(addr: Address, expectedAddressToIDs: {Address: [UInt64]}){ - let manager = getAuthAccount(addr).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) ?? panic ("manager does not exist") +access(all) fun main(addr: Address, expectedAddressToIDs: {Address: [UInt64]}){ + let manager = getAuthAccount(addr).storage.borrow(from: HybridCustody.ManagerStoragePath) + ?? panic ("manager does not exist") - var typeIdsWithProvider = {} as {Address: [String]} - var nftViews = {} as {Address: {UInt64: MetadataViews.Display}} + var typeIdsWithProvider: {Address: [String]} = {} + var nftViews: {Address: {UInt64: MetadataViews.Display}} = {} - let providerType = Type>() + let providerType = Type() let collectionType: Type = Type<@{NonFungibleToken.CollectionPublic}>() for address in manager.getChildAddresses() { - let acct = getAuthAccount(address) + let acct = getAuthAccount(address) let foundTypes: [String] = [] let views: {UInt64: MetadataViews.Display} = {} let childAcct = manager.borrowAccount(addr: address) ?? panic("child account not found") - // get all private paths - acct.forEachPrivate(fun (path: PrivatePath, type: Type): Bool { - // Check which private paths have NFT Provider AND can be borrowed - if !type.isSubtype(of: providerType){ - return true - } - - if let cap: Capability = childAcct.getCapability(path: path, type: Type<&{NonFungibleToken.Provider}>()) { - let providerCap = cap as! Capability<&{NonFungibleToken.Provider}> - if !providerCap.check(){ - return true + for s in acct.storage.storagePaths { + for c in acct.capabilities.storage.getControllers(forPath: s) { + if !c.borrowType.isSubtype(of: providerType){ + continue } - foundTypes.append(cap.borrow<&AnyResource>()!.getType().identifier) + if let cap: Capability = 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) + typeIdsWithProvider[address] = foundTypes + break + } } - return true - }) - typeIdsWithProvider[address] = foundTypes + } // iterate storage, check if typeIdsWithProvider contains the typeId, if so, add to views - acct.forEachStored(fun (path: StoragePath, type: Type): Bool { + acct.storage.forEachStored(fun (path: StoragePath, type: Type): Bool { if typeIdsWithProvider[address] == nil { return true @@ -73,10 +75,10 @@ pub fun main(addr: Address, expectedAddressToIDs: {Address: [UInt64]}){ if type.isInstance(collectionType) { continue } - if let collection = acct.borrow<&{NonFungibleToken.CollectionPublic}>(from: path) { + if let collection = acct.storage.borrow<&{NonFungibleToken.CollectionPublic}>(from: path) { // Iterate over IDs & resolve the view for id in collection.getIDs() { - let nft = collection.borrowNFT(id: id) + let nft = collection.borrowNFT(id)! if let display = nft.resolveView(Type())! as? MetadataViews.Display { views.insert(key: id, display) } diff --git a/scripts/test/test_get_all_collection_data_from_storage.cdc b/scripts/test/test_get_all_collection_data_from_storage.cdc index 4838084..78cd3a4 100644 --- a/scripts/test/test_get_all_collection_data_from_storage.cdc +++ b/scripts/test/test_get_all_collection_data_from_storage.cdc @@ -1,5 +1,6 @@ import "NonFungibleToken" import "MetadataViews" +import "ViewResolver" import "HybridCustody" /* @@ -10,7 +11,7 @@ import "HybridCustody" /// Assertion method to ensure passing test /// -pub fun assertPassing(result: {Address: [MetadataViews.NFTCollectionData]}, expectedAddressToCollectionLength: {Address: Int}) { +access(all) fun assertPassing(result: {Address: [MetadataViews.NFTCollectionData]}, expectedAddressToCollectionLength: {Address: Int}) { for address in result.keys { if expectedAddressToCollectionLength[address] == nil { panic("Address ".concat(address.toString()).concat(" found but not expected!")) @@ -23,29 +24,31 @@ pub fun assertPassing(result: {Address: [MetadataViews.NFTCollectionData]}, expe /// Helper function that retrieves data about all publicly accessible NFTs in an account /// -pub fun getAllViewsFromAddress(_ address: Address): [MetadataViews.NFTCollectionData] { +access(all) fun getAllViewsFromAddress(_ address: Address): [MetadataViews.NFTCollectionData] { - let account: AuthAccount = getAuthAccount(address) + let account: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account = getAuthAccount(address) let data: [MetadataViews.NFTCollectionData] = [] - let collectionType: Type = Type<@{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>() + let collectionType: Type = Type<@{NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection}>() let viewType: Type = Type() // Iterate over each public path - account.forEachStored(fun (path: StoragePath, type: Type): Bool { + account.storage.forEachStored(fun (path: StoragePath, type: Type): Bool { // Return if not the type we're looking for if !type.isInstance(collectionType) && !type.isSubtype(of: collectionType) { return true } - if let collectionRef = account - .borrow<&{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(from: path) { + if let collectionRef = account.storage + .borrow<&{NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection}>(from: path) { // Return early if no Resolver found in the Collection let ids: [UInt64]= collectionRef.getIDs() if ids.length == 0 { return true } + // Otherwise, attempt to get the NFTCollectionData & append if exists - if let dataView = collectionRef.borrowViewResolver(id: ids[0]).resolveView(viewType) as! MetadataViews.NFTCollectionData? { + let nft = collectionRef.borrowNFT(ids[0]) ?? panic("nft not found") + if let dataView = nft.resolveView(viewType) as! MetadataViews.NFTCollectionData? { data.append(dataView) } } @@ -57,16 +60,16 @@ pub fun getAllViewsFromAddress(_ address: Address): [MetadataViews.NFTCollection /// Script that retrieve data about all NFT Collections in the storage of an account and any of its child accounts /// // pub fun main(address: Address): {Address: [MetadataViews.NFTCollectionData]} { -pub fun main(address: Address, expectedAddressToCollectionLength: {Address: Int}) { +access(all) fun main(address: Address, expectedAddressToCollectionLength: {Address: Int}) { let allNFTData: {Address: [MetadataViews.NFTCollectionData]} = {address: getAllViewsFromAddress(address)} let seen: [Address] = [address] /* Iterate over any child accounts */ // - if let managerRef = getAccount(address).getCapability<&HybridCustody.Manager{HybridCustody.ManagerPublic}>( + if let managerRef = getAccount(address).capabilities.get<&{HybridCustody.ManagerPublic}>( HybridCustody.ManagerPublicPath - ).borrow() { + )!.borrow() { for childAddress in managerRef.getChildAddresses() { allNFTData.insert(key: childAddress, getAllViewsFromAddress(childAddress)) diff --git a/scripts/test/test_get_nft_display_view_from_public.cdc b/scripts/test/test_get_nft_display_view_from_public.cdc index 7ceca66..4ecd874 100644 --- a/scripts/test/test_get_nft_display_view_from_public.cdc +++ b/scripts/test/test_get_nft_display_view_from_public.cdc @@ -1,5 +1,8 @@ import "NonFungibleToken" import "MetadataViews" +import "ViewResolver" +import "ExampleNFT" + import "HybridCustody" /* @@ -10,34 +13,29 @@ import "HybridCustody" /// Assertion method to ensure passing test /// -pub fun assertPassing(result: {Address: {UInt64: MetadataViews.Display}}, expectedAddressToIDs: {Address: [UInt64]}) { - +access(all) fun assertPassing(result: {Address: {UInt64: MetadataViews.Display}}, expectedAddressToIDs: {Address: [UInt64]}) { for address in result.keys { - if expectedAddressToIDs[address] == nil { - panic("Address ".concat(address.toString()).concat(" found but not expected!")) - } - let expectedIDs: [UInt64] = expectedAddressToIDs[address]! - for i, id in result[address]!.keys { - if expectedIDs[i] != id { - panic("Resulting ID does not match expected ID!") - } + let expectedIDs: [UInt64] = expectedAddressToIDs[address] ?? panic("address expected but not found") + + for id in result[address]!.keys { + assert(expectedIDs.contains(id), message: "id expected but was not found") } } } /// Returns resolved Display from given address at specified path for each ID or nil if ResolverCollection is not found /// -pub fun getViews(_ address: Address, _ resolverCollectionPath: PublicPath): {UInt64: MetadataViews.Display} { - - let account: PublicAccount = getAccount(address) +access(all) fun getViews(_ address: Address, _ resolverCollectionPath: PublicPath): {UInt64: MetadataViews.Display} { + let account: &Account = getAccount(address) let views: {UInt64: MetadataViews.Display} = {} // Borrow the Collection if let collection = account - .getCapability<&{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(resolverCollectionPath).borrow() { + .capabilities.get<&{NonFungibleToken.CollectionPublic}>(resolverCollectionPath)!.borrow() { // Iterate over IDs & resolve the view for id in collection.getIDs() { - let display = collection.borrowViewResolver(id: id).resolveView(Type()) as! MetadataViews.Display + let nft = collection.borrowNFT(id) ?? panic("could not borrow NFT") + let display = nft.resolveView(Type())! as! MetadataViews.Display views.insert(key: id, display) } } @@ -48,18 +46,17 @@ pub fun getViews(_ address: Address, _ resolverCollectionPath: PublicPath): {UIn /// Queries for MetadataViews.Display each NFT across all associated accounts from Collections at the provided /// PublicPath /// -// pub fun main(address: Address, resolverCollectionPath: PublicPath): {Address: {UInt64: MetadataViews.Display}} { -pub fun main(address: Address, resolverCollectionPath: PublicPath, expectedAddressToIDs: {Address: [UInt64]}) { - +access(all) fun main(address: Address, resolverCollectionPath: PublicPath, expectedAddressToIDs: {Address: [UInt64]}) { let allViews: {Address: {UInt64: MetadataViews.Display}} = {address: getViews(address, resolverCollectionPath)} let seen: [Address] = [address] /* Iterate over any associated accounts */ // - if let managerRef = getAuthAccount(address).borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { + if let managerRef = getAuthAccount(address).storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) { for childAccount in managerRef.getChildAddresses() { - allViews.insert(key: childAccount, getViews(address, resolverCollectionPath)) + let views = getViews(childAccount, resolverCollectionPath) + allViews.insert(key: childAccount, views) seen.append(childAccount) } diff --git a/scripts/test/update_nft_provider_factory.cdc b/scripts/test/update_nft_provider_factory.cdc index 7f95c78..e2acdbe 100644 --- a/scripts/test/update_nft_provider_factory.cdc +++ b/scripts/test/update_nft_provider_factory.cdc @@ -3,9 +3,8 @@ import "NFTProviderFactory" import "NonFungibleToken" -pub fun main(address: Address) { - - let managerRef = getAuthAccount(address).borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath) +access(all) fun main(address: Address) { + let managerRef = getAuthAccount(address).storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("CapabilityFactory Manager not found") let nftProviderFactory = NFTProviderFactory.Factory() diff --git a/scripts/test_imports.cdc b/scripts/test_imports.cdc index f3c32a6..7fd5e30 100644 --- a/scripts/test_imports.cdc +++ b/scripts/test_imports.cdc @@ -1,5 +1,5 @@ import "HybridCustody" -pub fun main(): Bool { +access(all) fun main(): Bool { return true } \ No newline at end of file diff --git a/test.sh b/test.sh index acc7741..03faee0 100755 --- a/test.sh +++ b/test.sh @@ -2,4 +2,4 @@ set -e -flow test --cover --covercode="contracts" --coverprofile="coverage.lcov" test/*_tests.cdc \ No newline at end of file +flow-c1 test --cover --covercode="contracts" --coverprofile="coverage.lcov" test/*_tests.cdc \ No newline at end of file diff --git a/test/CapabilityDelegator_tests.cdc b/test/CapabilityDelegator_tests.cdc index 431eeed..0fd099e 100644 --- a/test/CapabilityDelegator_tests.cdc +++ b/test/CapabilityDelegator_tests.cdc @@ -37,12 +37,12 @@ fun testShareExampleNFTCollectionPublic() { let events = Test.eventsOfType(typ) Test.assertEqual(1, events.length) - let event = events[0]! as! CapabilityDelegator.DelegatorUpdated - Test.assert(event.isPublic) - Test.assert(event.active) + let e = events[0] as! CapabilityDelegator.DelegatorUpdated + Test.assert(e.isPublic) + Test.assert(e.active) - let capabilityType = Type>() - Test.assertEqual(capabilityType, event.capabilityType) + let capabilityType = Type>() + Test.assertEqual(capabilityType, e.capabilityType) } access(all) @@ -56,12 +56,12 @@ fun testShareExampleNFTCollectionPrivate() { let events = Test.eventsOfType(typ) Test.assertEqual(2, events.length) - let event = events[1]! as! CapabilityDelegator.DelegatorUpdated - Test.assert(event.isPublic == false) - Test.assert(event.active) + let e = events[1] as! CapabilityDelegator.DelegatorUpdated + Test.assert(e.isPublic == false) + Test.assert(e.active) - let capabilityType = Type>() - Test.assertEqual(capabilityType, event.capabilityType) + let capabilityType = Type>() + Test.assertEqual(capabilityType, e.capabilityType) } access(all) @@ -72,12 +72,12 @@ fun testRemoveExampleNFTCollectionPublic() { let events = Test.eventsOfType(typ) Test.assertEqual(3, events.length) - let event = events[2]! as! CapabilityDelegator.DelegatorUpdated - Test.assert(event.isPublic) - Test.assert(event.active == false) + let e = events[2] as! CapabilityDelegator.DelegatorUpdated + Test.assert(e.isPublic) + Test.assert(e.active == false) - let capabilityType = Type>() - Test.assertEqual(capabilityType, event.capabilityType) + let capabilityType = Type>() + Test.assertEqual(capabilityType, e.capabilityType) let scriptCode = loadCode("delegator/find_nft_collection_cap.cdc", "scripts") let scriptResult = Test.executeScript(scriptCode, [creator.address]) @@ -97,12 +97,12 @@ fun testRemoveExampleNFTCollectionPrivate() { let events = Test.eventsOfType(typ) Test.assertEqual(4, events.length) - let event = events[3]! as! CapabilityDelegator.DelegatorUpdated - Test.assert(event.isPublic == false) - Test.assert(event.active == false) + let e = events[3] as! CapabilityDelegator.DelegatorUpdated + Test.assert(e.isPublic == false) + Test.assert(e.active == false) - let capabilityType = Type>() - Test.assertEqual(capabilityType, event.capabilityType) + let capabilityType = Type>() + Test.assertEqual(capabilityType, e.capabilityType) let scriptCode = loadCode("delegator/find_nft_provider_cap.cdc", "scripts") let scriptResult = Test.executeScript(scriptCode, [creator.address]) @@ -119,7 +119,7 @@ fun testRemoveExampleNFTCollectionPrivate() { access(all) fun setup() { // helper nft contract so we can actually talk to nfts with tests - deploy("ExampleNFT", "../modules/flow-nft/contracts/ExampleNFT.cdc") + deploy("ExampleNFT", "../contracts/standard/ExampleNFT.cdc") // our main contract is last deploy("CapabilityDelegator", "../contracts/CapabilityDelegator.cdc") @@ -129,42 +129,42 @@ fun setup() { // BEGIN SECTION - transactions used in tests access(all) -fun setupDelegator(_ acct: Test.Account) { +fun setupDelegator(_ acct: Test.TestAccount) { txExecutor("delegator/setup.cdc", [acct], [], nil) } access(all) -fun sharePublicExampleNFT(_ acct: Test.Account) { +fun sharePublicExampleNFT(_ acct: Test.TestAccount) { txExecutor("delegator/add_public_nft_collection.cdc", [acct], [], nil) } access(all) -fun sharePrivateExampleNFT(_ acct: Test.Account) { +fun sharePrivateExampleNFT(_ acct: Test.TestAccount) { txExecutor("delegator/add_private_nft_collection.cdc", [acct], [], nil) } access(all) -fun removePublicExampleNFT(_ acct: Test.Account) { +fun removePublicExampleNFT(_ acct: Test.TestAccount) { txExecutor("delegator/remove_public_nft_collection.cdc", [acct], [], nil) } access(all) -fun removePrivateExampleNFT(_ acct: Test.Account) { +fun removePrivateExampleNFT(_ acct: Test.TestAccount) { txExecutor("delegator/remove_private_nft_collection.cdc", [acct], [], nil) } access(all) -fun setupNFTCollection(_ acct: Test.Account) { +fun setupNFTCollection(_ acct: Test.TestAccount) { txExecutor("example-nft/setup_full.cdc", [acct], [], nil) } access(all) -fun mintNFT(_ minter: Test.Account, receiver: Test.Account, name: String, description: String, thumbnail: String) { +fun mintNFT(_ minter: Test.TestAccount, receiver: Test.TestAccount, name: String, description: String, thumbnail: String) { txExecutor("example-nft/mint_to_account.cdc", [minter], [receiver.address, name, description, thumbnail], nil) } access(all) -fun mintNFTDefault(_ minter: Test.Account, receiver: Test.Account) { +fun mintNFTDefault(_ minter: Test.TestAccount, receiver: Test.TestAccount) { return mintNFT(minter, receiver: receiver, name: "example nft", description: "lorem ipsum", thumbnail: flowtyThumbnail) } @@ -173,37 +173,37 @@ fun mintNFTDefault(_ minter: Test.Account, receiver: Test.Account) { // BEGIN SECTION - scripts used in tests access(all) -fun getExampleNFTCollectionFromDelegator(_ owner: Test.Account) { +fun getExampleNFTCollectionFromDelegator(_ owner: Test.TestAccount) { let borrowed = scriptExecutor("delegator/get_nft_collection.cdc", [owner.address])! as! Bool Test.assert(borrowed, message: "failed to borrow delegator") } access(all) -fun getExampleNFTProviderFromDelegator(_ owner: Test.Account) { +fun getExampleNFTProviderFromDelegator(_ owner: Test.TestAccount) { let borrowed = scriptExecutor("delegator/get_nft_provider.cdc", [owner.address])! as! Bool Test.assert(borrowed, message: "failed to borrow delegator") } access(all) -fun getAllPublicContainsCollection(_ owner: Test.Account) { +fun getAllPublicContainsCollection(_ owner: Test.TestAccount) { let success = scriptExecutor("delegator/get_all_public_caps.cdc", [owner.address])! as! Bool Test.assert(success, message: "failed to borrow delegator") } access(all) -fun getAllPrivateContainsProvider(_ owner: Test.Account) { +fun getAllPrivateContainsProvider(_ owner: Test.TestAccount) { let success = scriptExecutor("delegator/get_all_private_caps.cdc", [owner.address])! as! Bool Test.assert(success, message: "failed to borrow delegator") } access(all) -fun findExampleNFTCollectionType(_ owner: Test.Account) { +fun findExampleNFTCollectionType(_ owner: Test.TestAccount) { let borrowed = scriptExecutor("delegator/find_nft_collection_cap.cdc", [owner.address])! as! Bool Test.assert(borrowed, message: "failed to borrow delegator") } access(all) -fun findExampleNFTProviderType(_ owner: Test.Account) { +fun findExampleNFTProviderType(_ owner: Test.TestAccount) { let borrowed = scriptExecutor("delegator/find_nft_provider_cap.cdc", [owner.address])! as! Bool Test.assert(borrowed, message: "failed to borrow delegator") } diff --git a/test/CapabilityFactory_tests.cdc b/test/CapabilityFactory_tests.cdc index cf2d7f2..2180c30 100644 --- a/test/CapabilityFactory_tests.cdc +++ b/test/CapabilityFactory_tests.cdc @@ -33,23 +33,25 @@ fun testGetSupportedTypesFromManager() { )! as! [Type] let expectedTypes = [ - Type<&{FungibleToken.Provider}>(), + Type(), Type<&{FungibleToken.Balance}>(), Type<&{FungibleToken.Receiver}>(), Type<&{FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), + Type(), + Type(), Type<&{NonFungibleToken.CollectionPublic}>(), - Type<&{NonFungibleToken.Provider}>() + Type() ] - Test.assertEqual(expectedTypes, supportedTypes) + for e in expectedTypes { + Test.assert(supportedTypes.contains(e), message: "missing expected type in supported types") + } } access(all) fun testAddFactoryFails() { expectScriptFailure( "test/add_type_for_nft_provider_factory.cdc", - [creator.address, Type<&{NonFungibleToken.Provider}>()], + [creator.address, Type()], "Factory of given type already exists" ) } @@ -64,17 +66,20 @@ fun testAddFactorySucceeds() { )! as! [Type] let expectedTypes = [ - Type<&{FungibleToken.Provider}>(), + Type(), Type<&{FungibleToken.Balance}>(), Type<&{FungibleToken.Receiver}>(), Type<&{FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), + Type(), + Type(), Type<&{NonFungibleToken.CollectionPublic}>(), - Type<&{NonFungibleToken.Provider}>(), + Type(), Type<&{NonFungibleToken.Receiver}>() ] - Test.assertEqual(expectedTypes, supportedTypes) + + for e in expectedTypes { + Test.assert(supportedTypes.contains(e), message: "missing expected type in supportedTypes") + } for type in supportedTypes { let factorySuccess = scriptExecutor( @@ -116,11 +121,14 @@ fun testSetupNFTManager() { let supportedTypes = scriptExecutor("factory/get_supported_types_from_manager.cdc", [tmp.address])! as! [Type] let expectedTypes = [ - Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), + Type(), Type<&{NonFungibleToken.CollectionPublic}>(), - Type<&{NonFungibleToken.Provider}>() + Type() ] - Test.assertEqual(expectedTypes, supportedTypes) + + for e in expectedTypes { + Test.assert(supportedTypes.contains(e), message: "missing type in supportedTypes: ".concat(e.identifier)) + } for type in supportedTypes { let factorySuccess = scriptExecutor( @@ -139,13 +147,16 @@ fun testSetupFTManager() { let supportedTypes = scriptExecutor("factory/get_supported_types_from_manager.cdc", [tmp.address])! as! [Type] let expectedTypes = [ - Type<&{FungibleToken.Provider}>(), + Type(), Type<&{FungibleToken.Balance}>(), Type<&{FungibleToken.Receiver}>(), Type<&{FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>() + Type() ] - Test.assertEqual(expectedTypes, supportedTypes) + + for e in expectedTypes { + Test.assert(supportedTypes.contains(e), message: "missing type in supportedTypes: ".concat(e.identifier)) + } for type in supportedTypes { let factorySuccess = scriptExecutor( @@ -164,16 +175,19 @@ fun testSetupNFTFTManager() { let supportedTypes = scriptExecutor("factory/get_supported_types_from_manager.cdc", [tmp.address])! as! [Type] let expectedTypes = [ - Type<&{FungibleToken.Provider}>(), + Type(), Type<&{FungibleToken.Balance}>(), Type<&{FungibleToken.Receiver}>(), Type<&{FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(), - Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), + Type(), + Type(), Type<&{NonFungibleToken.CollectionPublic}>(), - Type<&{NonFungibleToken.Provider}>() + Type() ] - Test.assertEqual(expectedTypes, supportedTypes) + + for e in expectedTypes { + Test.assert(supportedTypes.contains(e), message: "missing type in supportedTypes: ".concat(e.identifier)) + } for type in supportedTypes { let factorySuccess = scriptExecutor( @@ -189,7 +203,7 @@ fun testSetupNFTFTManager() { access(all) fun setup() { // helper nft & ft contract so we can actually talk to nfts & fts with tests - deploy("ExampleNFT", "../modules/flow-nft/contracts/ExampleNFT.cdc") + deploy("ExampleNFT", "../contracts/standard/ExampleNFT.cdc") deploy("ExampleToken", "../contracts/standard/ExampleToken.cdc") // our main contract is last @@ -207,12 +221,12 @@ fun setup() { // BEGIN SECTION - transactions used in tests access(all) -fun setupNFTCollection(_ acct: Test.Account) { +fun setupNFTCollection(_ acct: Test.TestAccount) { txExecutor("example-nft/setup_full.cdc", [acct], [], nil) } access(all) -fun setupCapabilityFactoryManager(_ acct: Test.Account) { +fun setupCapabilityFactoryManager(_ acct: Test.TestAccount) { txExecutor("factory/setup_nft_ft_manager.cdc", [acct], [], nil) } diff --git a/test/HybridCustody_tests.cdc b/test/HybridCustody_tests.cdc index dba70dc..c9b1584 100644 --- a/test/HybridCustody_tests.cdc +++ b/test/HybridCustody_tests.cdc @@ -1,8 +1,11 @@ import Test import "test_helpers.cdc" +import "HybridCustody" +import "NonFungibleToken" +import "ExampleNFT" access(all) let adminAccount = Test.getAccount(0x0000000000000007) -access(all) let accounts: {String: Test.Account} = {} +access(all) let accounts: {String: Test.TestAccount} = {} access(all) let app = "app" access(all) let child = "child" @@ -17,8 +20,8 @@ access(all) let FilterKindAll = "all" access(all) let FilterKindAllowList = "allowlist" access(all) let FilterKindDenyList = "denylist" -access(all) let exampleNFTPublicIdentifier = "ExampleNFTCollection" -access(all) let exampleNFT2PublicIdentifier = "ExampleNFT2Collection" +access(all) let exampleNFTPublicIdentifier = "exampleNFTCollection" +access(all) let exampleNFT2PublicIdentifier = "exampleNFT2Collection" // --------------- Test cases --------------- @@ -52,6 +55,8 @@ fun testSetupFactoryWithFT() { let tmp = Test.createAccount() setupFactoryManager(tmp) + txExecutor("example-token/setup.cdc", [tmp], [], nil) + scriptExecutor("factory/get_ft_provider_from_factory.cdc", [tmp.address]) scriptExecutor("factory/get_ft_balance_from_factory.cdc", [tmp.address]) scriptExecutor("factory/get_ft_receiver_from_factory.cdc", [tmp.address]) @@ -65,17 +70,18 @@ fun testSetupChildAccount() { access(all) fun testPublishAccount() { - let tmp = Test.createAccount() - setupOwnedAccount(tmp, FilterKindAll) + let child = Test.createAccount() + let parent = Test.createAccount() + + setupOwnedAccount(child, FilterKindAll) + setupNFTCollection(child) let factory = getTestAccount(nftFactory) let filter = getTestAccount(FilterKindAll) - let parent = Test.createAccount() - - txExecutor("hybrid-custody/publish_to_parent.cdc", [tmp], [parent.address, factory.address, filter.address], nil) + txExecutor("hybrid-custody/publish_to_parent.cdc", [child], [parent.address, factory.address, filter.address], nil) - scriptExecutor("hybrid-custody/get_collection_from_inbox.cdc", [parent.address, tmp.address]) + scriptExecutor("hybrid-custody/get_collection_from_inbox.cdc", [parent.address, child.address]) } access(all) @@ -103,6 +109,9 @@ fun testSetupOwnedAccountAndPublishRedeemed() { let factory = getTestAccount(nftFactory) let filter = getTestAccount(FilterKindAll) + setupFilter(filter, FilterKindAll) + setupFactoryManager(factory) + let parent = Test.createAccount() txExecutor("hybrid-custody/setup_owned_account_and_publish_to_parent.cdc", [child], [parent.address, factory.address, filter.address, nil, nil, nil], nil) @@ -119,6 +128,9 @@ fun testSetupOwnedAccountWithDisplayPublishRedeemed() { let factory = getTestAccount(nftFactory) let filter = getTestAccount(FilterKindAll) + setupFilter(filter, FilterKindAll) + setupFactoryManager(factory) + let parent = Test.createAccount() let name = "Test" @@ -270,7 +282,10 @@ fun testSeal() { let numKeysBefore = getNumValidKeys(child) Test.assert(numKeysBefore > 0, message: "no keys to revoke") - Test.assert(checkAuthAccountDefaultCap(account: child), message: "Missing Auth Account Capability at default path") + + // Get the controller ID for the current Account capability + let controllerID = getAccountCapControllerID(account: child) + ?? panic("controller ID should not be nil") let owner = getOwner(child: child) Test.assert(owner! == child.address, message: "mismatched owner") @@ -278,7 +293,9 @@ fun testSeal() { txExecutor("hybrid-custody/relinquish_ownership.cdc", [child], [], nil) let numKeysAfter = getNumValidKeys(child) Test.assert(numKeysAfter == 0, message: "not all keys were revoked") - Test.assert(!checkAuthAccountDefaultCap(account: child), message: "Found Auth Account Capability at default path") + let newControllerID = getAccountCapControllerID(account: child) + ?? panic("new controller ID should not be nil") + Test.assert(newControllerID != controllerID, message: "Found Auth Account Capability at default path") let ownerAfter = getOwner(child: child) Test.assert(ownerAfter == nil, message: "should not have an owner anymore") } @@ -767,6 +784,8 @@ fun testGetChildAccountNFTCapabilities(){ let isPublic = true setupNFT2Collection(child) + setupNFTCollection(child) + addNFTCollectionToDelegator(child: child, parent: parent, isPublic: isPublic) addNFT2CollectionToDelegator(child: child, parent: parent, isPublic: isPublic) @@ -793,6 +812,9 @@ fun testGetNFTsAccessibleFromChildAccount(){ setupNFTCollection(child) setupNFT2Collection(child) + setupNFTCollection(parent) + setupNFT2Collection(parent) + mintNFTDefault(accounts[exampleNFT]!, receiver: child) let expectedChildIDs = (scriptExecutor("example-nft/get_ids.cdc", [child.address]) as! [UInt64]?)! @@ -810,7 +832,7 @@ fun testGetNFTsAccessibleFromChildAccount(){ {child.address: expectedChildIDs} ]) - // Mint new nfts from ExampleNFT2 and assert that get_accessible_child_nfts.cdc does not return these nfts. + // // Mint new nfts from ExampleNFT2 and assert that get_accessible_child_nfts.cdc does not return these nfts. mintExampleNFT2Default(accounts[exampleNFT2]!, receiver: child) let expectedChildIDs2 = (scriptExecutor("example-nft-2/get_ids.cdc", [child.address]) as! [UInt64]?)! let expectedAddressToIDs2: {Address: [UInt64]} = {child.address: expectedChildIDs2, parent.address: expectedParentIDs} @@ -821,11 +843,11 @@ fun testGetNFTsAccessibleFromChildAccount(){ ) // revoke the ExampleNFT2 provider capability, preventing it from being returned. - let paths: [CapabilityPath] = [ - /private/exampleNFT2Collection, - /private/exampleNFTCollection + let paths: [StoragePath] = [ + /storage/exampleNFT2Collection, + /storage/exampleNFTCollection ] - txExecutor("misc/unlink_paths.cdc", [child], [paths], nil) + txExecutor("misc/unlink_from_storage_paths.cdc", [child], [paths], nil) let expectedAddressToIDsFails: {Address: [UInt64]} = {child.address: expectedChildIDs2} expectScriptFailure( @@ -846,7 +868,6 @@ fun testGetChildAccountFTCapabilities(){ let ftTypeIds = scriptExecutor("hybrid-custody/get_child_account_ft_capabilities.cdc", [parent.address])! as! {Address: [String]} Test.assert(ftTypeIds[child.address]![0] == nftIdentifier, message: "typeId should be: ".concat(nftIdentifier)) - } access(all) @@ -870,7 +891,7 @@ fun testBlockchainNativeOnboarding() { let childAddresses = scriptExecutor("hybrid-custody/get_child_addresses.cdc", [parent.address]) as! [Address]? ?? panic("problem adding blockchain native child account to signing parent") - let child = Test.Account(address: childAddresses[0], publicKey: expectedPubKey) + let child = Test.TestAccount(address: childAddresses[0], publicKey: expectedPubKey) Test.assert(checkForAddresses(child: child, parent: parent), message: "child account not linked to parent") } @@ -946,13 +967,226 @@ 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()).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()).removeLast() as! HybridCustody.AccountSealed + Test.assertEqual(child.address, sealEvent.address) + + let ownershipEvent = Test.eventsOfType(Type()).removeLast() as! HybridCustody.OwnershipUpdated + Test.assertEqual(owner.address, ownershipEvent.previousOwner!) + Test.assertEqual(nil, ownershipEvent.owner) +} + +access(all) +fun testManager_burnCallback() { + let child = Test.createAccount() + let parent = Test.createAccount() + + setupChildAndParent_FilterKindAll(child: child, parent: parent) + + txExecutor("hybrid-custody/destroy_manager.cdc", [parent], [], nil) + + let e = Test.eventsOfType(Type()).removeLast() as! HybridCustody.AccountUpdated + Test.assertEqual(e.child, child.address) +} + +access(all) +fun testChildAccount_burnCallback() { + let child = Test.createAccount() + let parent = Test.createAccount() + + setupChildAndParent_FilterKindAll(child: child, parent: parent) + + let beforeChildren = (scriptExecutor("hybrid-custody/get_child_addresses.cdc", [parent.address]))! as! [Address] + Test.assert(beforeChildren.contains(child.address), message: "missing child address") + + txExecutor("hybrid-custody/destroy_child.cdc", [child], [parent.address], nil) + + let e = Test.eventsOfType(Type()).removeLast() as! HybridCustody.ChildAccount.ResourceDestroyed + Test.assertEqual(child.address, e.address) + Test.assertEqual(parent.address, e.parent) + + // make sure that the parent no longer has the child account (burn callback should have removed it) + let afterChildren: [Address] = (scriptExecutor("hybrid-custody/get_child_addresses.cdc", [parent.address]))! as! [Address] + Test.assert(!afterChildren.contains(child.address), message: "child address found but should not have been") +} + +access(all) +fun testOwnedAccount_burnCallback() { + let child = Test.createAccount() + let parent = Test.createAccount() + + setupChildAndParent_FilterKindAll(child: child, parent: parent) + txExecutor("hybrid-custody/destroy_owned_account.cdc", [child], [], nil) + + let children = (scriptExecutor("hybrid-custody/get_child_addresses.cdc", [parent.address]))! as! [Address] + Test.assert(!children.contains(child.address), message: "child account found when it should not have been") +} + +access(all) +fun testGetPublicCapability() { + let child = Test.createAccount() + let parent = Test.createAccount() + + setupChildAndParent_FilterKindAll(child: child, parent: parent) + + setupNFTCollection(child) + mintNFTDefault(accounts[exampleNFT]!, receiver: child) + + let ids = scriptExecutor( + "hybrid-custody/get_collection_public_from_child.cdc", + [parent.address, child.address, PublicPath(identifier: exampleNFTPublicIdentifier)!, Type<&{NonFungibleToken.CollectionPublic}>()] + )! as! [UInt64] + Test.assert(ids.length > 0, message: "unexpected number of nfts in collection") + + // now try with a type that doesn't have a factory configured for it + Test.expectFailure(fun() { + scriptExecutor( + "hybrid-custody/get_collection_public_from_child.cdc", + [parent.address, child.address, PublicPath(identifier: exampleNFTPublicIdentifier)!, Type<&ExampleNFT.Collection>()] + )! as! [UInt64] + }, errorMessageSubstring: "could not get capability") +} + // --------------- End Test Cases --------------- // --------------- Transaction wrapper functions --------------- access(all) -fun setupChildAndParent_FilterKindAll(child: Test.Account, parent: Test.Account) { +fun setupChildAndParent_FilterKindAll(child: Test.TestAccount, parent: Test.TestAccount) { let factory = getTestAccount(nftFactory) let filter = getTestAccount(FilterKindAll) @@ -966,22 +1200,22 @@ fun setupChildAndParent_FilterKindAll(child: Test.Account, parent: Test.Account) } access(all) -fun setupAccountManager(_ acct: Test.Account) { +fun setupAccountManager(_ acct: Test.TestAccount) { txExecutor("hybrid-custody/setup_manager.cdc", [acct], [nil, nil], nil) } access(all) -fun setAccountManagerWithFilter(_ acct: Test.Account, _ filterAccount: Test.Account) { +fun setAccountManagerWithFilter(_ acct: Test.TestAccount, _ filterAccount: Test.TestAccount) { txExecutor("hybrid-custody/setup_manager.cdc", [acct], [nil, nil], nil) } access(all) -fun setManagerFilterOnChild(child: Test.Account, parent: Test.Account, filterAddress: Address) { +fun setManagerFilterOnChild(child: Test.TestAccount, parent: Test.TestAccount, filterAddress: Address) { txExecutor("hybrid-custody/set_manager_filter_cap.cdc", [parent], [filterAddress, child.address], nil) } access(all) -fun setupOwnedAccount(_ acct: Test.Account, _ filterKind: String) { +fun setupOwnedAccount(_ acct: Test.TestAccount, _ filterKind: String) { let factory = getTestAccount(nftFactory) let filter = getTestAccount(filterKind) @@ -996,54 +1230,59 @@ fun setupOwnedAccount(_ acct: Test.Account, _ filterKind: String) { } access(all) -fun setupFactoryManager(_ acct: Test.Account) { +fun setupFactoryManager(_ acct: Test.TestAccount) { txExecutor("factory/setup_nft_ft_manager.cdc", [acct], [], nil) } access(all) -fun setupNFTCollection(_ acct: Test.Account) { +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) } access(all) -fun setupNFT2Collection(_ acct: Test.Account) { +fun setupNFT2Collection(_ acct: Test.TestAccount) { txExecutor("example-nft-2/setup_full.cdc", [acct], [], nil) } access(all) -fun mintNFT(_ minter: Test.Account, receiver: Test.Account, name: String, description: String, thumbnail: String) { +fun mintNFT(_ minter: Test.TestAccount, receiver: Test.TestAccount, name: String, description: String, thumbnail: String) { let filepath: String = "example-nft/mint_to_account.cdc" txExecutor(filepath, [minter], [receiver.address, name, description, thumbnail], nil) } access(all) -fun mintNFTDefault(_ minter: Test.Account, receiver: Test.Account) { +fun mintNFTDefault(_ minter: Test.TestAccount, receiver: Test.TestAccount) { return mintNFT(minter, receiver: receiver, name: "example nft", description: "lorem ipsum", thumbnail: "http://example.com/image.png") } access(all) -fun mintExampleNFT2(_ minter: Test.Account, receiver: Test.Account, name: String, description: String, thumbnail: String) { +fun mintExampleNFT2(_ minter: Test.TestAccount, receiver: Test.TestAccount, name: String, description: String, thumbnail: String) { let filepath: String = "example-nft-2/mint_to_account.cdc" txExecutor(filepath, [minter], [receiver.address, name, description, thumbnail], nil) } access(all) -fun mintExampleNFT2Default(_ minter: Test.Account, receiver: Test.Account) { +fun mintExampleNFT2Default(_ minter: Test.TestAccount, receiver: Test.TestAccount) { return mintExampleNFT2(minter, receiver: receiver, name: "example nft 2", description: "lorem ipsum", thumbnail: "http://example.com/image.png") } access(all) -fun setupFT(_ acct: Test.Account) { +fun setupFT(_ acct: Test.TestAccount) { txExecutor("example-token/setup.cdc", [acct], [], nil) } access(all) -fun setupFTProvider(_ acct: Test.Account) { +fun setupFTProvider(_ acct: Test.TestAccount) { txExecutor("example-token/setup_provider.cdc", [acct], [], nil) } access(all) -fun setupFilter(_ acct: Test.Account, _ kind: String) { +fun setupFilter(_ acct: Test.TestAccount, _ kind: String) { var filePath = "" switch kind { case FilterKindAll: @@ -1063,7 +1302,7 @@ fun setupFilter(_ acct: Test.Account, _ kind: String) { } access(all) -fun addTypeToFilter(_ acct: Test.Account, _ kind: String, _ identifier: String) { +fun addTypeToFilter(_ acct: Test.TestAccount, _ kind: String, _ identifier: String) { var filePath = "" switch kind { case FilterKindAllowList: @@ -1080,7 +1319,7 @@ fun addTypeToFilter(_ acct: Test.Account, _ kind: String, _ identifier: String) } access(all) -fun removeAllFilterTypes(_ acct: Test.Account, _ kind: String) { +fun removeAllFilterTypes(_ acct: Test.TestAccount, _ kind: String) { var filePath = "" switch kind { case FilterKindAllowList: @@ -1097,12 +1336,12 @@ fun removeAllFilterTypes(_ acct: Test.Account, _ kind: String) { } access(all) -fun addNFTCollectionToDelegator(child: Test.Account, parent: Test.Account, isPublic: Bool) { +fun addNFTCollectionToDelegator(child: Test.TestAccount, parent: Test.TestAccount, isPublic: Bool) { txExecutor("hybrid-custody/add_example_nft_collection_to_delegator.cdc", [child], [parent.address, isPublic], nil) } access(all) -fun addNFT2CollectionToDelegator(child: Test.Account, parent: Test.Account, isPublic: Bool) { +fun addNFT2CollectionToDelegator(child: Test.TestAccount, parent: Test.TestAccount, isPublic: Bool) { txExecutor("hybrid-custody/add_example_nft2_collection_to_delegator.cdc", [child], [parent.address, isPublic], nil) } // ---------------- End Transaction wrapper functions @@ -1110,32 +1349,32 @@ fun addNFT2CollectionToDelegator(child: Test.Account, parent: Test.Account, isPu // ---------------- Begin script wrapper functions access(all) -fun getParentStatusesForChild(_ child: Test.Account): {Address: Bool} { +fun getParentStatusesForChild(_ child: Test.TestAccount): {Address: Bool} { return scriptExecutor("hybrid-custody/get_parents_from_child.cdc", [child.address])! as! {Address: Bool} } access(all) -fun isParent(child: Test.Account, parent: Test.Account): Bool { +fun isParent(child: Test.TestAccount, parent: Test.TestAccount): Bool { return scriptExecutor("hybrid-custody/is_parent.cdc", [child.address, parent.address])! as! Bool } access(all) -fun checkIsRedeemed(child: Test.Account, parent: Test.Account): Bool { +fun checkIsRedeemed(child: Test.TestAccount, parent: Test.TestAccount): Bool { return scriptExecutor("hybrid-custody/is_redeemed.cdc", [child.address, parent.address])! as! Bool } access(all) -fun getNumValidKeys(_ child: Test.Account): Int { +fun getNumValidKeys(_ child: Test.TestAccount): Int { return scriptExecutor("hybrid-custody/get_num_valid_keys.cdc", [child.address])! as! Int } access(all) -fun checkAuthAccountDefaultCap(account: Test.Account): Bool { - return scriptExecutor("hybrid-custody/check_default_auth_acct_linked_path.cdc", [account.address])! as! Bool +fun getAccountCapControllerID(account: Test.TestAccount): UInt64? { + return scriptExecutor("hybrid-custody/get_account_cap_con_id.cdc", [account.address]) as! UInt64? } access(all) -fun getOwner(child: Test.Account): Address? { +fun getOwner(child: Test.TestAccount): Address? { let res = scriptExecutor("hybrid-custody/get_owner_of_child.cdc", [child.address]) if res == nil { return nil @@ -1145,14 +1384,14 @@ fun getOwner(child: Test.Account): Address? { } access(all) -fun getPendingOwner(child: Test.Account): Address? { +fun getPendingOwner(child: Test.TestAccount): Address? { let res = scriptExecutor("hybrid-custody/get_pending_owner_of_child.cdc", [child.address]) return res as! Address? } access(all) -fun checkForAddresses(child: Test.Account, parent: Test.Account): Bool { +fun checkForAddresses(child: Test.TestAccount, parent: Test.TestAccount): Bool { let childAddressResult: [Address]? = (scriptExecutor("hybrid-custody/get_child_addresses.cdc", [parent.address])) as! [Address]? Test.assert(childAddressResult?.contains(child.address) == true, message: "child address not found") @@ -1162,7 +1401,7 @@ fun checkForAddresses(child: Test.Account, parent: Test.Account): Bool { } access(all) -fun getBalance(_ acct: Test.Account): UFix64 { +fun getBalance(_ acct: Test.TestAccount): UFix64 { let balance: UFix64? = (scriptExecutor("example-token/get_balance.cdc", [acct.address])! as! UFix64) return balance! } @@ -1172,7 +1411,7 @@ fun getBalance(_ acct: Test.Account): UFix64 { // ---------------- BEGIN General-purpose helper functions access(all) -fun buildTypeIdentifier(_ acct: Test.Account, _ contractName: String, _ suffix: String): String { +fun buildTypeIdentifier(_ acct: Test.TestAccount, _ contractName: String, _ suffix: String): String { let addrString = acct.address.toString() return "A.".concat(addrString.slice(from: 2, upTo: addrString.length)).concat(".").concat(contractName).concat(".").concat(suffix) } @@ -1187,7 +1426,7 @@ fun getCapabilityFilterPath(): String { // ---------------- END General-purpose helper functions access(all) -fun getTestAccount(_ name: String): Test.Account { +fun getTestAccount(_ name: String): Test.TestAccount { if accounts[name] == nil { accounts[name] = Test.createAccount() } @@ -1223,7 +1462,7 @@ fun setup() { accounts["nftCapFactory"] = adminAccount // helper nft contract so we can actually talk to nfts with tests - deploy("ExampleNFT", "../modules/flow-nft/contracts/ExampleNFT.cdc") + deploy("ExampleNFT", "../contracts/standard/ExampleNFT.cdc") deploy("ExampleNFT2", "../contracts/standard/ExampleNFT2.cdc") deploy("ExampleToken", "../contracts/standard/ExampleToken.cdc") diff --git a/test/test_helpers.cdc b/test/test_helpers.cdc index fd75998..1ae7ea2 100644 --- a/test/test_helpers.cdc +++ b/test/test_helpers.cdc @@ -49,7 +49,7 @@ fun expectScriptFailure( access(all) fun txExecutor( _ txName: String, - _ signers: [Test.Account], + _ signers: [Test.TestAccount], _ arguments: [AnyStruct], _ expectedError: String? ): Bool { diff --git a/transactions/delegator/add_private_nft_collection.cdc b/transactions/delegator/add_private_nft_collection.cdc index 29843ce..bcb09c5 100644 --- a/transactions/delegator/add_private_nft_collection.cdc +++ b/transactions/delegator/add_private_nft_collection.cdc @@ -5,13 +5,13 @@ import "MetadataViews" import "ExampleNFT" transaction { - prepare(acct: AuthAccount) { - let delegator = acct.borrow<&CapabilityDelegator.Delegator>(from: CapabilityDelegator.StoragePath) + prepare(acct: auth(BorrowValue, Capabilities) &Account) { + let delegator = acct.storage.borrow(from: CapabilityDelegator.StoragePath) ?? panic("delegator not found") - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let sharedCap = acct.getCapability<&ExampleNFT.Collection{NonFungibleToken.Provider}>(d.providerPath) + let sharedCap = acct.capabilities.storage.issue(d.storagePath) delegator.addCapability(cap: sharedCap, isPublic: false) } diff --git a/transactions/delegator/add_public_nft_collection.cdc b/transactions/delegator/add_public_nft_collection.cdc index 478239f..8dfda2f 100644 --- a/transactions/delegator/add_public_nft_collection.cdc +++ b/transactions/delegator/add_public_nft_collection.cdc @@ -4,12 +4,12 @@ import "NonFungibleToken" import "ExampleNFT" transaction { - prepare(acct: AuthAccount) { - let delegator = acct.borrow<&CapabilityDelegator.Delegator>(from: CapabilityDelegator.StoragePath) + prepare(acct: auth(BorrowValue) &Account) { + let delegator = acct.storage.borrow(from: CapabilityDelegator.StoragePath) ?? panic("delegator not found") let sharedCap - = acct.getCapability<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}>(ExampleNFT.CollectionPublicPath) + = acct.capabilities.get<&{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}>(ExampleNFT.CollectionPublicPath) delegator.addCapability(cap: sharedCap, isPublic: true) } } \ No newline at end of file diff --git a/transactions/delegator/remove_private_nft_collection.cdc b/transactions/delegator/remove_private_nft_collection.cdc index e87afa1..1a8008f 100644 --- a/transactions/delegator/remove_private_nft_collection.cdc +++ b/transactions/delegator/remove_private_nft_collection.cdc @@ -5,14 +5,16 @@ import "MetadataViews" import "ExampleNFT" transaction { - prepare(acct: AuthAccount) { - let delegator = acct.borrow<&CapabilityDelegator.Delegator>(from: CapabilityDelegator.StoragePath) + prepare(acct: auth(BorrowValue, Capabilities) &Account) { + let delegator = acct.storage.borrow(from: CapabilityDelegator.StoragePath) ?? panic("delegator not found") - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let sharedCap = acct.getCapability<&ExampleNFT.Collection{NonFungibleToken.Provider}>(d.providerPath) + let sharedCap = acct.capabilities.storage.issue(d.storagePath) delegator.removeCapability(cap: sharedCap) + + acct.capabilities.storage.getController(byCapabilityID: sharedCap.id)!.delete() } } \ No newline at end of file diff --git a/transactions/delegator/remove_public_nft_collection.cdc b/transactions/delegator/remove_public_nft_collection.cdc index a1566ca..faac917 100644 --- a/transactions/delegator/remove_public_nft_collection.cdc +++ b/transactions/delegator/remove_public_nft_collection.cdc @@ -4,12 +4,12 @@ import "NonFungibleToken" import "ExampleNFT" transaction { - prepare(acct: AuthAccount) { - let delegator = acct.borrow<&CapabilityDelegator.Delegator>(from: CapabilityDelegator.StoragePath) + prepare(acct: auth(BorrowValue, Capabilities) &Account) { + let delegator = acct.storage.borrow(from: CapabilityDelegator.StoragePath) ?? panic("delegator not found") let sharedCap - = acct.getCapability<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}>(ExampleNFT.CollectionPublicPath) + = acct.capabilities.get<&{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}>(ExampleNFT.CollectionPublicPath) delegator.removeCapability(cap: sharedCap) } } \ No newline at end of file diff --git a/transactions/delegator/setup.cdc b/transactions/delegator/setup.cdc index b1111c6..4664ebf 100644 --- a/transactions/delegator/setup.cdc +++ b/transactions/delegator/setup.cdc @@ -1,16 +1,15 @@ import "CapabilityDelegator" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&CapabilityDelegator.Delegator>(from: CapabilityDelegator.StoragePath) == nil { + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&CapabilityDelegator.Delegator>(from: CapabilityDelegator.StoragePath) == nil { let delegator <- CapabilityDelegator.createDelegator() - acct.save(<-delegator, to: CapabilityDelegator.StoragePath) + acct.storage.save(<-delegator, to: CapabilityDelegator.StoragePath) } - acct.unlink(CapabilityDelegator.PublicPath) - acct.unlink(CapabilityDelegator.PrivatePath) + acct.capabilities.unpublish(CapabilityDelegator.PublicPath) - acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.PublicPath, target: CapabilityDelegator.StoragePath) - acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>(CapabilityDelegator.PrivatePath, target: CapabilityDelegator.StoragePath) + let cap = acct.capabilities.storage.issue<&{CapabilityDelegator.GetterPublic}>(CapabilityDelegator.StoragePath) + acct.capabilities.publish(cap, at: CapabilityDelegator.PublicPath) } } \ No newline at end of file diff --git a/transactions/dev-setup/setup_nft_filter_and_factory_manager.cdc b/transactions/dev-setup/setup_nft_filter_and_factory_manager.cdc index 8510adf..4851fb6 100644 --- a/transactions/dev-setup/setup_nft_filter_and_factory_manager.cdc +++ b/transactions/dev-setup/setup_nft_filter_and_factory_manager.cdc @@ -45,50 +45,57 @@ access(all) fun withoutPrefix(_ input: String): String{ /// For more info, see docs at https://developers.onflow.org/docs/hybrid-custody/ //// transaction(nftContractAddress: Address, nftContractName: String) { - prepare(acct: AuthAccount) { + prepare(acct: auth(Storage, Capabilities) &Account) { /* --- CapabilityFactory Manager configuration --- */ // - if acct.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { + if acct.storage.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { let f <- CapabilityFactory.createFactoryManager() - acct.save(<-f, to: CapabilityFactory.StoragePath) + acct.storage.save(<-f, to: CapabilityFactory.StoragePath) } - if !acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check() { - acct.unlink(CapabilityFactory.PublicPath) - acct.link<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath, target: CapabilityFactory.StoragePath) + if !acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check() { + acct.capabilities.unpublish(CapabilityFactory.PublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&CapabilityFactory.Manager>(CapabilityFactory.StoragePath), + at: CapabilityFactory.PublicPath + ) } assert( - acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check(), + acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check() == true, message: "CapabilityFactory is not setup properly" ) - let factoryManager = acct.borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath) + let factoryManager = acct.storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("CapabilityFactory Manager not found") // Add NFT-related Factories to the Manager factoryManager.updateFactory(Type<&{NonFungibleToken.CollectionPublic}>(), NFTCollectionPublicFactory.Factory()) - factoryManager.updateFactory(Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), NFTProviderAndCollectionFactory.Factory()) - factoryManager.updateFactory(Type<&{NonFungibleToken.Provider}>(), NFTProviderFactory.Factory()) + factoryManager.updateFactory(Type(), NFTProviderAndCollectionFactory.Factory()) + factoryManager.updateFactory(Type(), NFTProviderFactory.Factory()) /* --- AllowlistFilter configuration --- */ // - if acct.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath) == nil { - acct.save(<-CapabilityFilter.create(Type<@CapabilityFilter.AllowlistFilter>()), to: CapabilityFilter.StoragePath) + if acct.storage.borrow<&AnyResource>(from: CapabilityFilter.StoragePath) == nil { + acct.storage.save(<-CapabilityFilter.createFilter(Type<@CapabilityFilter.AllowlistFilter>()), to: CapabilityFilter.StoragePath) } - if !acct.getCapability<&CapabilityFilter.AllowlistFilter{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check() { - acct.unlink(CapabilityFilter.PublicPath) - acct.link<&CapabilityFilter.AllowlistFilter{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath, target: CapabilityFilter.StoragePath) + if !acct.capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check(){ + acct.capabilities.unpublish(CapabilityFilter.PublicPath) + + acct.capabilities.publish( + acct.capabilities.storage.issue<&{CapabilityFilter.Filter}>(CapabilityFilter.StoragePath), + at: CapabilityFilter.PublicPath + ) } assert( - acct.getCapability<&CapabilityFilter.AllowlistFilter{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check(), + acct.capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check(), message: "AllowlistFilter is not setup properly" ) - let filter = acct.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath) + let filter = acct.storage.borrow(from: CapabilityFilter.StoragePath) ?? panic("AllowlistFilter does not exist") // Construct an NFT Collection Type from the provided args & add to the AllowlistFilter diff --git a/transactions/example-nft-2/mint_to_account.cdc b/transactions/example-nft-2/mint_to_account.cdc index d199519..5d5e61a 100644 --- a/transactions/example-nft-2/mint_to_account.cdc +++ b/transactions/example-nft-2/mint_to_account.cdc @@ -6,15 +6,15 @@ import "ExampleNFT2" transaction(receiver: Address, name: String, description: String, thumbnail: String) { let minter: &ExampleNFT2.NFTMinter - prepare(acct: AuthAccount) { - self.minter = acct.borrow<&ExampleNFT2.NFTMinter>(from: ExampleNFT2.MinterStoragePath) ?? panic("minter not found") + prepare(acct: auth(BorrowValue) &Account) { + self.minter = acct.storage.borrow<&ExampleNFT2.NFTMinter>(from: ExampleNFT2.MinterStoragePath) ?? panic("minter not found") } execute { - let d = ExampleNFT2.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT2.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let c = getAccount(receiver).getCapability<&{NonFungibleToken.CollectionPublic}>(d.publicPath) + let c = getAccount(receiver).capabilities.get<&{NonFungibleToken.CollectionPublic}>(d.publicPath) let r = c.borrow() ?? panic("no receiver collection") - self.minter.mintNFT(recipient: r, name: name, description: description, thumbnail: thumbnail, royalties: []) + self.minter.mintNFT(recipient: r, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: self.minter.owner!.address) } } diff --git a/transactions/example-nft-2/setup_full.cdc b/transactions/example-nft-2/setup_full.cdc index f9358e5..b72b7a1 100644 --- a/transactions/example-nft-2/setup_full.cdc +++ b/transactions/example-nft-2/setup_full.cdc @@ -4,17 +4,19 @@ import "MetadataViews" import "ExampleNFT2" transaction { - prepare(acct: AuthAccount) { - let d = ExampleNFT2.resolveView(Type())! as! MetadataViews.NFTCollectionData + prepare(acct: auth(Storage, Capabilities) &Account) { + let d = ExampleNFT2.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - if acct.borrow<&ExampleNFT2.Collection>(from: d.storagePath) == nil { - acct.save(<- ExampleNFT2.createEmptyCollection(), to: ExampleNFT2.CollectionStoragePath) + if acct.storage.borrow<&ExampleNFT2.Collection>(from: d.storagePath) == nil { + acct.storage.save(<- ExampleNFT2.createEmptyCollection(), to: ExampleNFT2.CollectionStoragePath) } - acct.unlink(d.publicPath) - acct.link<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic, NonFungibleToken.CollectionPublic}>(d.publicPath, target: d.storagePath) + acct.capabilities.unpublish(d.publicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{ExampleNFT2.ExampleNFT2CollectionPublic, NonFungibleToken.CollectionPublic}>(d.storagePath), + at: d.publicPath + ) - acct.unlink(d.providerPath) - acct.link<&ExampleNFT2.Collection{ExampleNFT2.ExampleNFT2CollectionPublic, NonFungibleToken.CollectionPublic, NonFungibleToken.Provider}>(d.providerPath, target: d.storagePath) + acct.capabilities.storage.issue(d.storagePath) } } diff --git a/transactions/example-nft/mint_to_account.cdc b/transactions/example-nft/mint_to_account.cdc index d25f9a2..ac7bd44 100644 --- a/transactions/example-nft/mint_to_account.cdc +++ b/transactions/example-nft/mint_to_account.cdc @@ -6,15 +6,15 @@ import "ExampleNFT" transaction(receiver: Address, name: String, description: String, thumbnail: String) { let minter: &ExampleNFT.NFTMinter - prepare(acct: AuthAccount) { - self.minter = acct.borrow<&ExampleNFT.NFTMinter>(from: ExampleNFT.MinterStoragePath) ?? panic("minter not found") + prepare(acct: auth(BorrowValue) &Account) { + self.minter = acct.storage.borrow<&ExampleNFT.NFTMinter>(from: ExampleNFT.MinterStoragePath) ?? panic("minter not found") } execute { - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - let c = getAccount(receiver).getCapability<&{NonFungibleToken.CollectionPublic}>(d.publicPath) + let c = getAccount(receiver).capabilities.get<&{NonFungibleToken.CollectionPublic}>(d.publicPath) let r = c.borrow() ?? panic("no receiver collection") - self.minter.mintNFT(recipient: r, name: name, description: description, thumbnail: thumbnail, royalties: []) + self.minter.mintNFT(recipient: r, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: self.minter.owner!.address) } } diff --git a/transactions/example-nft/setup_full.cdc b/transactions/example-nft/setup_full.cdc index e8b9648..c20ad67 100644 --- a/transactions/example-nft/setup_full.cdc +++ b/transactions/example-nft/setup_full.cdc @@ -1,20 +1,20 @@ import "NonFungibleToken" import "MetadataViews" -import ExampleNFT from "ExampleNFT" +import "ExampleNFT" transaction { - prepare(acct: AuthAccount) { - let d = ExampleNFT.resolveView(Type())! as! MetadataViews.NFTCollectionData + prepare(acct: auth(Storage, Capabilities) &Account) { + let d = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData - if acct.borrow<&ExampleNFT.Collection>(from: d.storagePath) == nil { - acct.save(<- ExampleNFT.createEmptyCollection(), to: ExampleNFT.CollectionStoragePath) + if acct.storage.borrow<&ExampleNFT.Collection>(from: d.storagePath) == nil { + acct.storage.save(<- ExampleNFT.createEmptyCollection(), to: ExampleNFT.CollectionStoragePath) } - acct.unlink(d.publicPath) - acct.link<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}>(d.publicPath, target: d.storagePath) + acct.capabilities.unpublish(d.publicPath) + let cap = acct.capabilities.storage.issue<&{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic}>(d.storagePath) + acct.capabilities.publish(cap, at: d.publicPath) - acct.unlink(d.providerPath) - acct.link<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic, NonFungibleToken.CollectionPublic, NonFungibleToken.Provider}>(d.providerPath, target: d.storagePath) + acct.capabilities.storage.issue(d.storagePath) } } diff --git a/transactions/example-token/mint_tokens.cdc b/transactions/example-token/mint_tokens.cdc index 4e90306..49600ec 100644 --- a/transactions/example-token/mint_tokens.cdc +++ b/transactions/example-token/mint_tokens.cdc @@ -1,4 +1,5 @@ import "FungibleToken" +import "FungibleTokenMetadataViews" import "ExampleToken" //EMULATOR transaction only! Emulator contract does not require admin resource @@ -8,21 +9,20 @@ import "ExampleToken" /// are transferred to the address after minting transaction(recipient: Address, amount: UFix64) { - - /// Reference to the Fungible Token Receiver of the recipient let tokenReceiver: &{FungibleToken.Receiver} /// The total supply of tokens before the burn let supplyBefore: UFix64 - prepare(signer: AuthAccount) { + prepare(signer: auth(Storage, Capabilities) &Account) { self.supplyBefore = ExampleToken.totalSupply + let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + // Get the account of the recipient and borrow a reference to their receiver - self.tokenReceiver = getAccount(recipient) - .getCapability(ExampleToken.ReceiverPublicPath) - .borrow<&{FungibleToken.Receiver}>() + self.tokenReceiver = getAccount(recipient).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath)!.borrow() ?? panic("Unable to borrow receiver reference") } diff --git a/transactions/example-token/setup.cdc b/transactions/example-token/setup.cdc index 3de7cc4..a94b346 100644 --- a/transactions/example-token/setup.cdc +++ b/transactions/example-token/setup.cdc @@ -1,28 +1,33 @@ import "FungibleToken" import "ExampleToken" +import "FungibleTokenMetadataViews" transaction { - prepare(acct: AuthAccount) { + prepare(acct: auth(Storage, Capabilities) &Account) { + let md = ExampleToken.resolveContractView(resourceType: nil, viewType: Type())! as! FungibleTokenMetadataViews.FTVaultData // Create a new ExampleToken Vault and put it in storage if one doesn't exist - if acct.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath) == nil { - acct.save( - <-ExampleToken.createEmptyVault(), - to: ExampleToken.VaultStoragePath - ) + if acct.storage.borrow<&ExampleToken.Vault>(from: md.storagePath) == nil { + acct.storage.save( + <-ExampleToken.createEmptyVault(vaultType: Type<@ExampleToken.Vault>()), + to: md.storagePath + ) } // Create a public capability to the Vault that only exposes // the deposit function through the Receiver interface - acct.link<&ExampleToken.Vault{FungibleToken.Receiver, FungibleToken.Balance}>( - ExampleToken.ReceiverPublicPath, - target: ExampleToken.VaultStoragePath + acct.capabilities.unpublish(md.receiverPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{FungibleToken.Receiver, FungibleToken.Balance}>(md.storagePath), + at: md.receiverPath ) // Create a public capability to the Vault that exposes the Balance and Resolver interfaces - acct.link<&ExampleToken.Vault{FungibleToken.Balance}>( - ExampleToken.VaultPublicPath, - target: ExampleToken.VaultStoragePath + acct.capabilities.unpublish(md.metadataPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{FungibleToken.Balance}>(md.storagePath), + at: md.metadataPath ) + } } \ No newline at end of file diff --git a/transactions/example-token/setup_provider.cdc b/transactions/example-token/setup_provider.cdc index 764cf4f..e8b7185 100644 --- a/transactions/example-token/setup_provider.cdc +++ b/transactions/example-token/setup_provider.cdc @@ -1,13 +1,13 @@ import "FungibleToken" import "ExampleToken" +import "FungibleTokenMetadataViews" transaction { - prepare(acct: AuthAccount) { - // Create Provider capability that can be used by parent to access the child's vault and transfer tokens - let providerPath = /private/exampleTokenProvider - - acct.unlink(providerPath) - acct.link<&{FungibleToken.Provider}>(providerPath, target: ExampleToken.VaultStoragePath) + prepare(acct: auth(Capabilities) &Account) { + let vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + + acct.capabilities.storage.issue(vaultData.storagePath) } } \ No newline at end of file diff --git a/transactions/factory/setup_empty_factory.cdc b/transactions/factory/setup_empty_factory.cdc new file mode 100644 index 0000000..f61edee --- /dev/null +++ b/transactions/factory/setup_empty_factory.cdc @@ -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.Manager>(CapabilityFactory.PublicPath).check() { + acct.capabilities.unpublish(CapabilityFactory.PublicPath) + + let cap = acct.capabilities.storage.issue<&CapabilityFactory.Manager>(CapabilityFactory.StoragePath) + acct.capabilities.publish(cap, at: CapabilityFactory.PublicPath) + } + + assert( + acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check(), + message: "CapabilityFactory is not setup properly" + ) + } +} diff --git a/transactions/factory/setup_ft_manager.cdc b/transactions/factory/setup_ft_manager.cdc index fcd4631..5685e32 100644 --- a/transactions/factory/setup_ft_manager.cdc +++ b/transactions/factory/setup_ft_manager.cdc @@ -8,29 +8,32 @@ import "FTReceiverFactory" import "FTAllFactory" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { let f <- CapabilityFactory.createFactoryManager() - acct.save(<-f, to: CapabilityFactory.StoragePath) + acct.storage.save(<-f, to: CapabilityFactory.StoragePath) } - if !acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check() { - acct.unlink(CapabilityFactory.PublicPath) - acct.link<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath, target: CapabilityFactory.StoragePath) + if !acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check() { + acct.capabilities.unpublish(CapabilityFactory.PublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&CapabilityFactory.Manager>(CapabilityFactory.StoragePath), + at: CapabilityFactory.PublicPath + ) } assert( - acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check(), + acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check(), message: "CapabilityFactory is not setup properly" ) - let manager = acct.borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath) + let manager = acct.storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("manager not found") - manager.updateFactory(Type<&{FungibleToken.Provider}>(), FTProviderFactory.Factory()) + manager.updateFactory(Type(), FTProviderFactory.Factory()) manager.updateFactory(Type<&{FungibleToken.Balance}>(), FTBalanceFactory.Factory()) manager.updateFactory(Type<&{FungibleToken.Receiver}>(), FTReceiverFactory.Factory()) manager.updateFactory(Type<&{FungibleToken.Receiver, FungibleToken.Balance}>(), FTReceiverBalanceFactory.Factory()) - manager.updateFactory(Type<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(), FTAllFactory.Factory()) + manager.updateFactory(Type(), FTAllFactory.Factory()) } } diff --git a/transactions/factory/setup_nft_ft_manager.cdc b/transactions/factory/setup_nft_ft_manager.cdc index 3c47e49..a2083f2 100644 --- a/transactions/factory/setup_nft_ft_manager.cdc +++ b/transactions/factory/setup_nft_ft_manager.cdc @@ -12,32 +12,33 @@ import "FTReceiverFactory" import "FTAllFactory" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { let f <- CapabilityFactory.createFactoryManager() - acct.save(<-f, to: CapabilityFactory.StoragePath) + acct.storage.save(<-f, to: CapabilityFactory.StoragePath) } - if !acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check() { - acct.unlink(CapabilityFactory.PublicPath) - acct.link<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath, target: CapabilityFactory.StoragePath) + if !acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check() { + acct.capabilities.unpublish(CapabilityFactory.PublicPath) + + let cap = acct.capabilities.storage.issue<&CapabilityFactory.Manager>(CapabilityFactory.StoragePath) + acct.capabilities.publish(cap, at: CapabilityFactory.PublicPath) } assert( - acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check(), + acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check(), message: "CapabilityFactory is not setup properly" ) - let manager = acct.borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath) - ?? panic("manager not found") + let manager = acct.storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("manager not found") manager.updateFactory(Type<&{NonFungibleToken.CollectionPublic}>(), NFTCollectionPublicFactory.Factory()) - manager.updateFactory(Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), NFTProviderAndCollectionFactory.Factory()) - manager.updateFactory(Type<&{NonFungibleToken.Provider}>(), NFTProviderFactory.Factory()) - manager.updateFactory(Type<&{FungibleToken.Provider}>(), FTProviderFactory.Factory()) + manager.updateFactory(Type(), NFTProviderAndCollectionFactory.Factory()) + manager.updateFactory(Type(), NFTProviderFactory.Factory()) + manager.updateFactory(Type(), FTProviderFactory.Factory()) manager.updateFactory(Type<&{FungibleToken.Balance}>(), FTBalanceFactory.Factory()) manager.updateFactory(Type<&{FungibleToken.Receiver}>(), FTReceiverFactory.Factory()) manager.updateFactory(Type<&{FungibleToken.Receiver, FungibleToken.Balance}>(), FTReceiverBalanceFactory.Factory()) - manager.updateFactory(Type<&{FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance}>(), FTAllFactory.Factory()) + manager.updateFactory(Type(), FTAllFactory.Factory()) } } diff --git a/transactions/factory/setup_nft_manager.cdc b/transactions/factory/setup_nft_manager.cdc index 7ca5590..b35cf77 100644 --- a/transactions/factory/setup_nft_manager.cdc +++ b/transactions/factory/setup_nft_manager.cdc @@ -6,27 +6,29 @@ import "NFTProviderAndCollectionFactory" import "NFTProviderFactory" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: CapabilityFactory.StoragePath) == nil { let f <- CapabilityFactory.createFactoryManager() - acct.save(<-f, to: CapabilityFactory.StoragePath) + acct.storage.save(<-f, to: CapabilityFactory.StoragePath) } - if !acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check() { - acct.unlink(CapabilityFactory.PublicPath) - acct.link<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath, target: CapabilityFactory.StoragePath) - } + + acct.capabilities.unpublish(CapabilityFactory.PublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&CapabilityFactory.Manager>(CapabilityFactory.StoragePath), + at: CapabilityFactory.PublicPath + ) assert( - acct.getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath).check(), + acct.capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath).check(), message: "CapabilityFactory is not setup properly" ) - let manager = acct.borrow<&CapabilityFactory.Manager>(from: CapabilityFactory.StoragePath) + let manager = acct.storage.borrow(from: CapabilityFactory.StoragePath) ?? panic("manager not found") manager.updateFactory(Type<&{NonFungibleToken.CollectionPublic}>(), NFTCollectionPublicFactory.Factory()) - manager.updateFactory(Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(), NFTProviderAndCollectionFactory.Factory()) - manager.updateFactory(Type<&{NonFungibleToken.Provider}>(), NFTProviderFactory.Factory()) + manager.updateFactory(Type(), NFTProviderAndCollectionFactory.Factory()) + manager.updateFactory(Type(), NFTProviderFactory.Factory()) } } diff --git a/transactions/filter/allow/add_type_to_list.cdc b/transactions/filter/allow/add_type_to_list.cdc index 34a846e..1acb657 100644 --- a/transactions/filter/allow/add_type_to_list.cdc +++ b/transactions/filter/allow/add_type_to_list.cdc @@ -1,8 +1,8 @@ import "CapabilityFilter" transaction(identifier: String) { - prepare(acct: AuthAccount) { - let filter = acct.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage) &Account) { + let filter = acct.storage.borrow(from: CapabilityFilter.StoragePath) ?? panic("filter does not exist") let c = CompositeType(identifier)! diff --git a/transactions/filter/allow/remove_all_types.cdc b/transactions/filter/allow/remove_all_types.cdc index eebce22..71df876 100644 --- a/transactions/filter/allow/remove_all_types.cdc +++ b/transactions/filter/allow/remove_all_types.cdc @@ -1,8 +1,8 @@ import "CapabilityFilter" transaction() { - prepare(acct: AuthAccount) { - let filter = acct.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage) &Account) { + let filter = acct.storage.borrow(from: CapabilityFilter.StoragePath) ?? panic("filter does not exist") filter.removeAllTypes() diff --git a/transactions/filter/allow/setup.cdc b/transactions/filter/allow/setup.cdc index 9c575dd..d3765f6 100644 --- a/transactions/filter/allow/setup.cdc +++ b/transactions/filter/allow/setup.cdc @@ -1,12 +1,17 @@ import "CapabilityFilter" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&CapabilityFilter.AllowlistFilter>(from: CapabilityFilter.StoragePath) == nil { - acct.save(<-CapabilityFilter.create(Type<@CapabilityFilter.AllowlistFilter>()), to: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: CapabilityFilter.StoragePath) == nil { + acct.storage.save(<-CapabilityFilter.createFilter(Type<@CapabilityFilter.AllowlistFilter>()), to: CapabilityFilter.StoragePath) } - acct.unlink(CapabilityFilter.PublicPath) - acct.link<&CapabilityFilter.AllowlistFilter{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath, target: CapabilityFilter.StoragePath) + acct.capabilities.unpublish(CapabilityFilter.PublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{CapabilityFilter.Filter}>(CapabilityFilter.StoragePath), + at: CapabilityFilter.PublicPath + ) + + assert(acct.capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check(), message: "failed to setup filter") } } \ No newline at end of file diff --git a/transactions/filter/deny/add_type_to_list.cdc b/transactions/filter/deny/add_type_to_list.cdc index 77ba5b7..71974f1 100644 --- a/transactions/filter/deny/add_type_to_list.cdc +++ b/transactions/filter/deny/add_type_to_list.cdc @@ -1,8 +1,8 @@ import "CapabilityFilter" transaction(identifier: String) { - prepare(acct: AuthAccount) { - let filter = acct.borrow<&CapabilityFilter.DenylistFilter>(from: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage) &Account) { + let filter = acct.storage.borrow(from: CapabilityFilter.StoragePath) ?? panic("filter does not exist") let c = CompositeType(identifier)! diff --git a/transactions/filter/deny/remove_all_types.cdc b/transactions/filter/deny/remove_all_types.cdc index eb273f3..d24cfa8 100644 --- a/transactions/filter/deny/remove_all_types.cdc +++ b/transactions/filter/deny/remove_all_types.cdc @@ -1,8 +1,8 @@ import "CapabilityFilter" transaction() { - prepare(acct: AuthAccount) { - let filter = acct.borrow<&CapabilityFilter.DenylistFilter>(from: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage) &Account) { + let filter = acct.storage.borrow(from: CapabilityFilter.StoragePath) ?? panic("filter does not exist") filter.removeAllTypes() diff --git a/transactions/filter/deny/setup.cdc b/transactions/filter/deny/setup.cdc index 1e48b79..d5a78b4 100644 --- a/transactions/filter/deny/setup.cdc +++ b/transactions/filter/deny/setup.cdc @@ -1,12 +1,17 @@ import "CapabilityFilter" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&CapabilityFilter.DenylistFilter>(from: CapabilityFilter.StoragePath) == nil { - acct.save(<- CapabilityFilter.create(Type<@CapabilityFilter.DenylistFilter>()), to: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: CapabilityFilter.StoragePath) == nil { + acct.storage.save(<- CapabilityFilter.createFilter(Type<@CapabilityFilter.DenylistFilter>()), to: CapabilityFilter.StoragePath) } - acct.unlink(CapabilityFilter.PublicPath) - acct.link<&CapabilityFilter.DenylistFilter{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath, target: CapabilityFilter.StoragePath) + acct.capabilities.unpublish(CapabilityFilter.PublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{CapabilityFilter.Filter}>(CapabilityFilter.StoragePath), + at: CapabilityFilter.PublicPath + ) + + assert(acct.capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check(), message: "failed to setup filter") } } \ No newline at end of file diff --git a/transactions/filter/setup_allow_all.cdc b/transactions/filter/setup_allow_all.cdc index 1761e82..dfe1e00 100644 --- a/transactions/filter/setup_allow_all.cdc +++ b/transactions/filter/setup_allow_all.cdc @@ -1,14 +1,17 @@ import "CapabilityFilter" transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&CapabilityFilter.AllowAllFilter>(from: CapabilityFilter.StoragePath) == nil { - acct.save(<- CapabilityFilter.create(Type<@CapabilityFilter.AllowAllFilter>()), to: CapabilityFilter.StoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&CapabilityFilter.AllowAllFilter>(from: CapabilityFilter.StoragePath) == nil { + acct.storage.save(<- CapabilityFilter.createFilter(Type<@CapabilityFilter.AllowAllFilter>()), to: CapabilityFilter.StoragePath) } - acct.unlink(CapabilityFilter.PublicPath) - let linkRes = acct.link<&CapabilityFilter.AllowAllFilter{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath, target: CapabilityFilter.StoragePath) - ?? panic("link failed") - assert(linkRes.check(), message: "failed to setup filter") + acct.capabilities.unpublish(CapabilityFilter.PublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{CapabilityFilter.Filter}>(CapabilityFilter.StoragePath), + at: CapabilityFilter.PublicPath + ) + + assert(acct.capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath).check(), message: "failed to setup filter") } } \ No newline at end of file diff --git a/transactions/hybrid-custody/accept_ownership.cdc b/transactions/hybrid-custody/accept_ownership.cdc index 0d3026d..0ef08c0 100644 --- a/transactions/hybrid-custody/accept_ownership.cdc +++ b/transactions/hybrid-custody/accept_ownership.cdc @@ -2,33 +2,41 @@ import "HybridCustody" import "CapabilityFilter" -import "MetadataViews" +import "ViewResolver" transaction(childAddress: Address, filterAddress: Address?, filterPath: PublicPath?) { - prepare(acct: AuthAccount) { + prepare(acct: auth(Storage, Capabilities, Inbox) &Account) { var filter: Capability<&{CapabilityFilter.Filter}>? = nil if filterAddress != nil && filterPath != nil { - filter = getAccount(filterAddress!).getCapability<&{CapabilityFilter.Filter}>(filterPath!) + filter = getAccount(filterAddress!).capabilities.get<&{CapabilityFilter.Filter}>(filterPath!) } - if acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { + if acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) - acct.save(<- m, to: HybridCustody.ManagerStoragePath) + acct.storage.save(<- m, to: HybridCustody.ManagerStoragePath) - acct.unlink(HybridCustody.ManagerPublicPath) - acct.unlink(HybridCustody.ManagerPrivatePath) + for c in acct.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { + c.delete() + } - acct.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerPrivatePath, target: HybridCustody.ManagerStoragePath) - acct.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath, target: HybridCustody.ManagerStoragePath) + acct.capabilities.unpublish(HybridCustody.ManagerPublicPath) + + acct.capabilities.storage.issue(HybridCustody.ManagerStoragePath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), + at: HybridCustody.ManagerPublicPath + ) } let inboxName = HybridCustody.getOwnerIdentifier(acct.address) - let cap = acct.inbox.claim<&AnyResource{HybridCustody.OwnedAccountPrivate, HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(inboxName, provider: childAddress) + let cap = acct.inbox.claim(inboxName, provider: childAddress) ?? panic("owned account cap not found") - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager no found") + let ownedAcct = cap.borrow() ?? panic("could not borrow owned account capability") + manager.addOwnedAccount(cap: cap) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/add_example_nft2_collection_to_delegator.cdc b/transactions/hybrid-custody/add_example_nft2_collection_to_delegator.cdc index 56592c0..558091c 100644 --- a/transactions/hybrid-custody/add_example_nft2_collection_to_delegator.cdc +++ b/transactions/hybrid-custody/add_example_nft2_collection_to_delegator.cdc @@ -5,16 +5,13 @@ import "NonFungibleToken" import "ExampleNFT2" transaction(parent: Address, isPublic: Bool) { - prepare(acct: AuthAccount) { - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + let o = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") let child = o.borrowChildAccount(parent: parent) ?? panic("child account not found") - let path = /private/exampleNFT2FullCollection - acct.link<&ExampleNFT2.Collection>(path, target: ExampleNFT2.CollectionStoragePath) - let cap = acct.getCapability<&ExampleNFT2.Collection>(path) - + let cap = acct.capabilities.storage.issue(ExampleNFT2.CollectionStoragePath) o.addCapabilityToDelegator(parent: parent, cap: cap, isPublic: isPublic) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/add_example_nft_collection_to_delegator.cdc b/transactions/hybrid-custody/add_example_nft_collection_to_delegator.cdc index c4ca454..da1e78f 100644 --- a/transactions/hybrid-custody/add_example_nft_collection_to_delegator.cdc +++ b/transactions/hybrid-custody/add_example_nft_collection_to_delegator.cdc @@ -5,16 +5,13 @@ import "NonFungibleToken" import "ExampleNFT" transaction(parent: Address, isPublic: Bool) { - prepare(acct: AuthAccount) { - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + let o = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") let child: &HybridCustody.ChildAccount = o.borrowChildAccount(parent: parent) ?? panic("child account not found") - let path = /private/exampleNFTFullCollection - acct.link<&ExampleNFT.Collection>(path, target: ExampleNFT.CollectionStoragePath) - let cap = acct.getCapability<&ExampleNFT.Collection>(path) - + let cap = acct.capabilities.storage.issue<&ExampleNFT.Collection>(ExampleNFT.CollectionStoragePath) o.addCapabilityToDelegator(parent: parent, cap: cap, isPublic: isPublic) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/destroy_child.cdc b/transactions/hybrid-custody/destroy_child.cdc new file mode 100644 index 0000000..a5e8ba6 --- /dev/null +++ b/transactions/hybrid-custody/destroy_child.cdc @@ -0,0 +1,11 @@ +import "HybridCustody" +import "Burner" + +transaction(parent: Address) { + prepare(acct: auth(Storage) &Account) { + let s = StoragePath(identifier: HybridCustody.getChildAccountIdentifier(parent))! + let m <- acct.storage.load<@AnyResource>(from: s) + ?? panic("no resource found in child account storage path") + Burner.burn(<- m) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/destroy_manager.cdc b/transactions/hybrid-custody/destroy_manager.cdc new file mode 100644 index 0000000..9749e73 --- /dev/null +++ b/transactions/hybrid-custody/destroy_manager.cdc @@ -0,0 +1,10 @@ +import "HybridCustody" +import "Burner" + +transaction { + prepare(acct: auth(Storage) &Account) { + let m <- acct.storage.load<@AnyResource>(from: HybridCustody.ManagerStoragePath) + ?? panic("no resource found in manager storage path") + Burner.burn(<- m) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/destroy_owned_account.cdc b/transactions/hybrid-custody/destroy_owned_account.cdc new file mode 100644 index 0000000..14d2a81 --- /dev/null +++ b/transactions/hybrid-custody/destroy_owned_account.cdc @@ -0,0 +1,10 @@ +import "HybridCustody" +import "Burner" + +transaction { + prepare(acct: auth(Storage) &Account) { + let m <- acct.storage.load<@AnyResource>(from: HybridCustody.OwnedAccountStoragePath) + ?? panic("no resource found in owned account storage path") + Burner.burn(<- m) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/metadata/set_child_account_display.cdc b/transactions/hybrid-custody/metadata/set_child_account_display.cdc index ef66cc7..0f74afa 100644 --- a/transactions/hybrid-custody/metadata/set_child_account_display.cdc +++ b/transactions/hybrid-custody/metadata/set_child_account_display.cdc @@ -2,8 +2,8 @@ import "HybridCustody" import "MetadataViews" transaction(childAddress: Address, name: String, description: String, thumbnail: String) { - prepare(acct: AuthAccount) { - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + prepare(acct: auth(Storage) &Account) { + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") let d = MetadataViews.Display( diff --git a/transactions/hybrid-custody/metadata/set_child_account_display_to_nil.cdc b/transactions/hybrid-custody/metadata/set_child_account_display_to_nil.cdc new file mode 100644 index 0000000..e9168b7 --- /dev/null +++ b/transactions/hybrid-custody/metadata/set_child_account_display_to_nil.cdc @@ -0,0 +1,10 @@ +import "HybridCustody" + +transaction(childAddress: Address) { + prepare(acct: auth(Storage) &Account) { + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) + ?? panic("manager not found") + + m.setChildAccountDisplay(address: childAddress, nil) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/metadata/set_owned_account_display.cdc b/transactions/hybrid-custody/metadata/set_owned_account_display.cdc index 67d1dc8..7f264ed 100644 --- a/transactions/hybrid-custody/metadata/set_owned_account_display.cdc +++ b/transactions/hybrid-custody/metadata/set_owned_account_display.cdc @@ -2,8 +2,8 @@ import "HybridCustody" import "MetadataViews" transaction(name: String, description: String, thumbnail: String) { - prepare(acct: AuthAccount) { - let o = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage) &Account) { + let o = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("account not found") let d = MetadataViews.Display( diff --git a/transactions/hybrid-custody/misc/save_resource_to_parent_child_storage_slot.cdc b/transactions/hybrid-custody/misc/save_resource_to_parent_child_storage_slot.cdc index 35c1f87..81063ce 100644 --- a/transactions/hybrid-custody/misc/save_resource_to_parent_child_storage_slot.cdc +++ b/transactions/hybrid-custody/misc/save_resource_to_parent_child_storage_slot.cdc @@ -2,10 +2,10 @@ import "ExampleToken" import "HybridCustody" transaction(parent: Address) { - prepare(acct: AuthAccount) { - let v <- ExampleToken.createEmptyVault() + prepare(acct: auth(Storage) &Account) { + let v <- ExampleToken.createEmptyVault(vaultType: Type<@ExampleToken.Vault>()) let identifier = HybridCustody.getChildAccountIdentifier(parent) let storagePath = StoragePath(identifier: identifier)! - acct.save(<-v, to: storagePath) + acct.storage.save(<-v, to: storagePath) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/onboarding/blockchain_native.cdc b/transactions/hybrid-custody/onboarding/blockchain_native.cdc index 422a45b..3d36102 100644 --- a/transactions/hybrid-custody/onboarding/blockchain_native.cdc +++ b/transactions/hybrid-custody/onboarding/blockchain_native.cdc @@ -3,6 +3,7 @@ import "FungibleToken" import "FlowToken" import "MetadataViews" +import "ViewResolver" import "HybridCustody" import "CapabilityFactory" @@ -16,11 +17,11 @@ transaction( filterAddress: Address ) { - prepare(parent: AuthAccount, app: AuthAccount) { + prepare(parent: auth(Storage, Capabilities, Inbox) &Account, app: auth(Storage, Capabilities) &Account) { /* --- Account Creation --- */ // // Create the child account, funding via the signing app account - let newAccount = AuthAccount(payer: app) + let newAccount = Account(payer: app) // Create a public key for the child account from string value in the provided arg // **NOTE:** You may want to specify a different signature algo for your use case let key = PublicKey( @@ -40,11 +41,9 @@ transaction( // Fund the new account if specified if initialFundingAmt > 0.0 { // Get a vault to fund the new account - let fundingProvider = app.borrow<&FlowToken.Vault{FungibleToken.Provider}>( - from: /storage/flowTokenVault - )! + let fundingProvider = app.storage.borrow(from: /storage/flowTokenVault)! // Fund the new account with the initialFundingAmount specified - newAccount.getCapability<&FlowToken.Vault{FungibleToken.Receiver}>(/public/flowTokenReceiver) + newAccount.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)! .borrow()! .deposit( from: <-fundingProvider.withdraw( @@ -61,35 +60,27 @@ transaction( /* --- Link the AuthAccount Capability --- */ // - var acctCap = newAccount.linkAccount(HybridCustody.LinkedAccountPrivatePath) - ?? panic("problem linking account Capability for new account") + let acctCap = newAccount.capabilities.account.issue() // Create a OwnedAccount & link Capabilities let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap) - newAccount.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) - newAccount - .link<&HybridCustody.OwnedAccount{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>( - HybridCustody.OwnedAccountPrivatePath, - target: HybridCustody.OwnedAccountStoragePath - ) - newAccount - .link<&HybridCustody.OwnedAccount{HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>( - HybridCustody.OwnedAccountPublicPath, - target: HybridCustody.OwnedAccountStoragePath - ) + newAccount.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) + + newAccount.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath) + newAccount.capabilities.publish( + newAccount.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath), + at: HybridCustody.OwnedAccountPublicPath + ) // Get a reference to the OwnedAccount resource - let owned = newAccount.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath)! + let owned = newAccount.storage.borrow(from: HybridCustody.OwnedAccountStoragePath)! // Get the CapabilityFactory.Manager Capability - let factory = getAccount(factoryAddress) - .getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>( - CapabilityFactory.PublicPath - ) + let factory = getAccount(factoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) assert(factory.check(), message: "factory address is not configured properly") // Get the CapabilityFilter.Filter Capability - let filter = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + let filter = getAccount(filterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(filter.check(), message: "capability filter is not configured properly") // Configure access for the delegatee parent account @@ -98,34 +89,34 @@ transaction( /* --- Add delegation to parent account --- */ // // Configure HybridCustody.Manager if needed - if parent.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { + if parent.storage.borrow<&AnyResource>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) - parent.save(<- m, to: HybridCustody.ManagerStoragePath) + parent.storage.save(<- m, to: HybridCustody.ManagerStoragePath) + + for c in parent.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { + c.delete() + } + + // configure Capabilities + parent.capabilities.storage.issue<&{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath) + parent.capabilities.publish( + parent.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), + at: HybridCustody.ManagerPublicPath + ) } - // Link Capabilities - parent.unlink(HybridCustody.ManagerPublicPath) - parent.unlink(HybridCustody.ManagerPrivatePath) - parent.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>( - HybridCustody.ManagerPrivatePath, - target: HybridCustody.ManagerStoragePath - ) - parent.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>( - HybridCustody.ManagerPublicPath, - target: HybridCustody.ManagerStoragePath - ) // Claim the ChildAccount Capability let inboxName = HybridCustody.getChildAccountIdentifier(parent.address) let cap = parent .inbox - .claim<&HybridCustody.ChildAccount{HybridCustody.AccountPrivate, HybridCustody.AccountPublic, MetadataViews.Resolver}>( + .claim( inboxName, provider: newAccount.address ) ?? panic("child account cap not found") // Get a reference to the Manager and add the account - let managerRef = parent.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + let managerRef = parent.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager no found") managerRef.addAccount(cap: cap) } diff --git a/transactions/hybrid-custody/publish_to_parent.cdc b/transactions/hybrid-custody/publish_to_parent.cdc index d9948c3..d7e80b1 100644 --- a/transactions/hybrid-custody/publish_to_parent.cdc +++ b/transactions/hybrid-custody/publish_to_parent.cdc @@ -4,14 +4,14 @@ import "CapabilityFilter" import "CapabilityDelegator" transaction(parent: Address, factoryAddress: Address, filterAddress: Address) { - prepare(acct: AuthAccount) { - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage) &Account) { + let owned = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") - let factory = getAccount(factoryAddress).getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath) + let factory = getAccount(factoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) assert(factory.check(), message: "factory address is not configured properly") - let filter = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + let filter = getAccount(filterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(filter.check(), message: "capability filter is not configured properly") owned.publishToParent(parentAddress: parent, factory: factory, filter: filter) diff --git a/transactions/hybrid-custody/redeem_account.cdc b/transactions/hybrid-custody/redeem_account.cdc index 70016db..82a757e 100644 --- a/transactions/hybrid-custody/redeem_account.cdc +++ b/transactions/hybrid-custody/redeem_account.cdc @@ -1,31 +1,39 @@ import "MetadataViews" +import "ViewResolver" import "HybridCustody" import "CapabilityFilter" transaction(childAddress: Address, filterAddress: Address?, filterPath: PublicPath?) { - prepare(acct: AuthAccount) { + prepare(acct: auth(Storage, Capabilities, Inbox) &Account) { var filter: Capability<&{CapabilityFilter.Filter}>? = nil if filterAddress != nil && filterPath != nil { - filter = getAccount(filterAddress!).getCapability<&{CapabilityFilter.Filter}>(filterPath!) + filter = getAccount(filterAddress!).capabilities.get<&{CapabilityFilter.Filter}>(filterPath!) } - if acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { + if acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) - acct.save(<- m, to: HybridCustody.ManagerStoragePath) + acct.storage.save(<- m, to: HybridCustody.ManagerStoragePath) - acct.unlink(HybridCustody.ManagerPublicPath) - acct.unlink(HybridCustody.ManagerPrivatePath) + for c in acct.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { + c.delete() + } - acct.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerPrivatePath, target: HybridCustody.ManagerStoragePath) - acct.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath, target: HybridCustody.ManagerStoragePath) + acct.capabilities.unpublish(HybridCustody.ManagerPublicPath) + + acct.capabilities.publish( + acct.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), + at: HybridCustody.ManagerPublicPath + ) + + acct.capabilities.storage.issue(HybridCustody.ManagerStoragePath) } let inboxName = HybridCustody.getChildAccountIdentifier(acct.address) - let cap = acct.inbox.claim<&HybridCustody.ChildAccount{HybridCustody.AccountPrivate, HybridCustody.AccountPublic, MetadataViews.Resolver}>(inboxName, provider: childAddress) + let cap = acct.inbox.claim(inboxName, provider: childAddress) ?? panic("child account cap not found") - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager no found") manager.addAccount(cap: cap) diff --git a/transactions/hybrid-custody/relinquish_ownership.cdc b/transactions/hybrid-custody/relinquish_ownership.cdc index fcbba96..289304c 100644 --- a/transactions/hybrid-custody/relinquish_ownership.cdc +++ b/transactions/hybrid-custody/relinquish_ownership.cdc @@ -3,8 +3,8 @@ import "HybridCustody" transaction { - prepare(acct: AuthAccount) { - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage) &Account) { + let owned = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned not found") owned.seal() } diff --git a/transactions/hybrid-custody/remove_child_account.cdc b/transactions/hybrid-custody/remove_child_account.cdc index 872b84a..36de1c7 100644 --- a/transactions/hybrid-custody/remove_child_account.cdc +++ b/transactions/hybrid-custody/remove_child_account.cdc @@ -1,8 +1,8 @@ import "HybridCustody" transaction(child: Address) { - prepare (acct: AuthAccount) { - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + prepare (acct: auth(Storage) &Account) { + let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") manager.removeChild(addr: child) } diff --git a/transactions/hybrid-custody/remove_owned_account.cdc b/transactions/hybrid-custody/remove_owned_account.cdc new file mode 100644 index 0000000..ced74ee --- /dev/null +++ b/transactions/hybrid-custody/remove_owned_account.cdc @@ -0,0 +1,10 @@ +import "HybridCustody" + +transaction(addr: Address) { + prepare(acct: auth(Storage) &Account) { + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) + ?? panic("manager not found") + + m.removeOwned(addr: addr) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/remove_parent_from_child.cdc b/transactions/hybrid-custody/remove_parent_from_child.cdc index fcbe875..5b57b7c 100644 --- a/transactions/hybrid-custody/remove_parent_from_child.cdc +++ b/transactions/hybrid-custody/remove_parent_from_child.cdc @@ -1,13 +1,13 @@ import "HybridCustody" transaction(parent: Address) { - prepare(acct: AuthAccount) { - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage) &Account) { + let owned = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned not found") owned.removeParent(parent: parent) - let manager = getAccount(parent).getCapability<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath) + let manager = getAccount(parent).capabilities.get<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath) .borrow() ?? panic("manager not found") let children = manager.getChildAddresses() assert(!children.contains(acct.address), message: "removed child is still in manager resource") diff --git a/transactions/hybrid-custody/send_child_ft_with_parent.cdc b/transactions/hybrid-custody/send_child_ft_with_parent.cdc index b2a5626..6a20b6e 100644 --- a/transactions/hybrid-custody/send_child_ft_with_parent.cdc +++ b/transactions/hybrid-custody/send_child_ft_with_parent.cdc @@ -2,22 +2,31 @@ import "FungibleToken" import "ExampleToken" import "HybridCustody" +import "FungibleTokenMetadataViews" transaction(amount: UFix64, to: Address, child: Address) { // The Vault resource that holds the tokens that are being transferred - let paymentVault: @FungibleToken.Vault + let paymentVault: @{FungibleToken.Vault} + let vaultData: FungibleTokenMetadataViews.FTVaultData - prepare(signer: AuthAccount) { + prepare(signer: auth(Storage) &Account) { // signer is the parent account // get the manager resource and borrow childAccount - let m = signer.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + let m = signer.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager does not exist") let childAcct = m.borrowAccount(addr: child) ?? panic("child account not found") + self.vaultData = ExampleToken.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not get the vault data view for ExampleToken") + //get Ft cap from child account - let cap = childAcct.getCapability(path: /private/exampleTokenProvider, type: Type<&{FungibleToken.Provider}>()) ?? panic("no cap found") - let providerCap = cap as! Capability<&{FungibleToken.Provider}> + let capType = Type() + let controllerID = childAcct.getControllerIDForType(type: capType, forPath: self.vaultData.storagePath) + ?? panic("no controller found for capType") + + let cap = childAcct.getCapability(controllerID: controllerID, type: capType) ?? panic("no cap found") + let providerCap = cap as! Capability assert(providerCap.check(), message: "invalid provider capability") // Get a reference to the child's stored vault @@ -33,8 +42,7 @@ transaction(amount: UFix64, to: Address, child: Address) { let recipient = getAccount(to) // Get a reference to the recipient's Receiver - let receiverRef = recipient.getCapability(/public/exampleTokenReceiver) - .borrow<&{FungibleToken.Receiver}>() + let receiverRef = recipient.capabilities.get<&{FungibleToken.Receiver}>(self.vaultData.receiverPath)!.borrow() ?? panic("Could not borrow receiver reference to the recipient's Vault") // Deposit the withdrawn tokens in the recipient's receiver diff --git a/transactions/hybrid-custody/set_capability_factory_for_parent.cdc b/transactions/hybrid-custody/set_capability_factory_for_parent.cdc new file mode 100644 index 0000000..b7af73d --- /dev/null +++ b/transactions/hybrid-custody/set_capability_factory_for_parent.cdc @@ -0,0 +1,12 @@ +import "HybridCustody" +import "CapabilityFactory" + +transaction(parent: Address, factoryAddress: Address) { + prepare(acct: auth(Storage) &Account) { + let cap = getAccount(factoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) + + let ownedAccount = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) + ?? panic("owned account not found") + ownedAccount.setCapabilityFactoryForParent(parent: parent, cap: cap) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/set_capability_filter_for_parent.cdc b/transactions/hybrid-custody/set_capability_filter_for_parent.cdc new file mode 100644 index 0000000..7446af6 --- /dev/null +++ b/transactions/hybrid-custody/set_capability_filter_for_parent.cdc @@ -0,0 +1,12 @@ +import "HybridCustody" +import "CapabilityFilter" + +transaction(parent: Address, factoryAddress: Address) { + prepare(acct: auth(Storage) &Account) { + let cap = getAccount(factoryAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + + let ownedAccount = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) + ?? panic("owned account not found") + ownedAccount.setCapabilityFilterForParent(parent: parent, cap: cap) + } +} \ No newline at end of file diff --git a/transactions/hybrid-custody/set_default_manager_cap.cdc b/transactions/hybrid-custody/set_default_manager_cap.cdc index 360557b..732692d 100644 --- a/transactions/hybrid-custody/set_default_manager_cap.cdc +++ b/transactions/hybrid-custody/set_default_manager_cap.cdc @@ -4,11 +4,11 @@ import "CapabilityFilter" // Sets the signing account's HybridCustody.Manager.filter capability to // the filter which exists at the given address's public path transaction(addr: Address) { - prepare(acct: AuthAccount) { - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + prepare(acct: auth(Storage) &Account) { + let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") - let cap = getAccount(addr).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + let cap = getAccount(addr).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) manager.setDefaultManagerCapabilityFilter(cap: cap) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/set_manager_filter_cap.cdc b/transactions/hybrid-custody/set_manager_filter_cap.cdc index 401fd92..abadc0b 100644 --- a/transactions/hybrid-custody/set_manager_filter_cap.cdc +++ b/transactions/hybrid-custody/set_manager_filter_cap.cdc @@ -2,11 +2,11 @@ import "HybridCustody" import "CapabilityFilter" transaction(filterAddress: Address, childAddress: Address) { - prepare(acct: AuthAccount) { - let m = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + prepare(acct: auth(Storage) &Account) { + let m = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") - let cap = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + let cap = getAccount(filterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(cap.check(), message: "capability filter is not valid") m.setManagerCapabilityFilter(cap: cap, childAddress: childAddress) diff --git a/transactions/hybrid-custody/setup_manager.cdc b/transactions/hybrid-custody/setup_manager.cdc index 746d2a9..4771ec2 100644 --- a/transactions/hybrid-custody/setup_manager.cdc +++ b/transactions/hybrid-custody/setup_manager.cdc @@ -2,22 +2,29 @@ import "HybridCustody" import "CapabilityFilter" transaction(filterAddress: Address?, filterPath: PublicPath?) { - prepare(acct: AuthAccount) { + prepare(acct: auth(Storage, Capabilities) &Account) { var filter: Capability<&{CapabilityFilter.Filter}>? = nil if filterAddress != nil && filterPath != nil { - filter = getAccount(filterAddress!).getCapability<&{CapabilityFilter.Filter}>(filterPath!) + filter = getAccount(filterAddress!).capabilities.get<&{CapabilityFilter.Filter}>(filterPath!) } - if acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { + if acct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) - acct.save(<- m, to: HybridCustody.ManagerStoragePath) + acct.storage.save(<- m, to: HybridCustody.ManagerStoragePath) } + + for c in acct.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { + c.delete() + } + + acct.capabilities.unpublish(HybridCustody.ManagerPublicPath) - acct.unlink(HybridCustody.ManagerPublicPath) - acct.unlink(HybridCustody.ManagerPrivatePath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), + at: HybridCustody.ManagerPublicPath + ) - acct.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.ManagerPrivatePath, target: HybridCustody.ManagerStoragePath) - acct.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath, target: HybridCustody.ManagerStoragePath) + acct.capabilities.storage.issue(HybridCustody.ManagerStoragePath) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/setup_multi_sig.cdc b/transactions/hybrid-custody/setup_multi_sig.cdc index 32937fb..9289e74 100644 --- a/transactions/hybrid-custody/setup_multi_sig.cdc +++ b/transactions/hybrid-custody/setup_multi_sig.cdc @@ -7,64 +7,84 @@ import "CapabilityDelegator" import "CapabilityFilter" import "MetadataViews" +import "ViewResolver" transaction(parentFilterAddress: Address?, childAccountFactoryAddress: Address, childAccountFilterAddress: Address) { - prepare(childAcct: AuthAccount, parentAcct: AuthAccount) { + prepare(childAcct: auth(Storage, Capabilities) &Account, parentAcct: auth(Storage, Capabilities, Inbox) &Account) { // --------------------- Begin setup of child account --------------------- - var acctCap = childAcct.getCapability<&AuthAccount>(HybridCustody.LinkedAccountPrivatePath) - if !acctCap.check() { - acctCap = childAcct.linkAccount(HybridCustody.LinkedAccountPrivatePath)! + var optCap: Capability? = nil + let t = Type() + for c in childAcct.capabilities.account.getControllers() { + if c.borrowType.isSubtype(of: t) { + optCap = c.capability as! Capability + break + } } - if childAcct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { + if optCap == nil { + optCap = childAcct.capabilities.account.issue() + } + let acctCap = optCap ?? panic("failed to get account capability") + + if childAcct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap) - childAcct.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) + childAcct.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) + } + + for c in childAcct.capabilities.storage.getControllers(forPath: HybridCustody.OwnedAccountStoragePath) { + c.delete() } - // check that paths are all configured properly - childAcct.unlink(HybridCustody.OwnedAccountPrivatePath) - childAcct.link<&HybridCustody.OwnedAccount{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountPrivatePath, target: HybridCustody.OwnedAccountStoragePath) + // configure capabilities + childAcct.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath) + childAcct.capabilities.publish( + childAcct.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath), + at: HybridCustody.OwnedAccountPublicPath + ) - childAcct.unlink(HybridCustody.OwnedAccountPublicPath) - childAcct.link<&HybridCustody.OwnedAccount{HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountPublicPath, target: HybridCustody.OwnedAccountStoragePath) // --------------------- End setup of child account --------------------- // --------------------- Begin setup of parent account --------------------- var filter: Capability<&{CapabilityFilter.Filter}>? = nil if parentFilterAddress != nil { - filter = getAccount(parentFilterAddress!).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + filter = getAccount(parentFilterAddress!).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) } - if parentAcct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { + if parentAcct.storage.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) == nil { let m <- HybridCustody.createManager(filter: filter) - parentAcct.save(<- m, to: HybridCustody.ManagerStoragePath) + parentAcct.storage.save(<- m, to: HybridCustody.ManagerStoragePath) + } + + for c in parentAcct.capabilities.storage.getControllers(forPath: HybridCustody.ManagerStoragePath) { + c.delete() } - parentAcct.unlink(HybridCustody.ManagerPublicPath) - parentAcct.unlink(HybridCustody.ManagerPrivatePath) + parentAcct.capabilities.publish( + parentAcct.capabilities.storage.issue<&{HybridCustody.ManagerPublic}>(HybridCustody.ManagerStoragePath), + at: HybridCustody.ManagerPublicPath + ) + parentAcct.capabilities.storage.issue(HybridCustody.ManagerStoragePath) - parentAcct.link<&HybridCustody.Manager{HybridCustody.ManagerPrivate, HybridCustody.ManagerPublic}>(HybridCustody.OwnedAccountPrivatePath, target: HybridCustody.ManagerStoragePath) - parentAcct.link<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath, target: HybridCustody.ManagerStoragePath) // --------------------- End setup of parent account --------------------- // Publish account to parent - let owned = childAcct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + let owned = childAcct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") - let factory = getAccount(childAccountFactoryAddress).getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath) + let factory = getAccount(childAccountFactoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) assert(factory.check(), message: "factory address is not configured properly") - let filterForChild = getAccount(childAccountFilterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + let filterForChild = getAccount(childAccountFilterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(filterForChild.check(), message: "capability filter is not configured properly") owned.publishToParent(parentAddress: parentAcct.address, factory: factory, filter: filterForChild) // claim the account on the parent let inboxName = HybridCustody.getChildAccountIdentifier(parentAcct.address) - let cap = parentAcct.inbox.claim<&HybridCustody.ChildAccount{HybridCustody.AccountPrivate, HybridCustody.AccountPublic, MetadataViews.Resolver}>(inboxName, provider: childAcct.address) + let cap = parentAcct.inbox.claim(inboxName, provider: childAcct.address) ?? panic("child account cap not found") - let manager = parentAcct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + let manager = parentAcct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager no found") manager.addAccount(cap: cap) diff --git a/transactions/hybrid-custody/setup_owned_account.cdc b/transactions/hybrid-custody/setup_owned_account.cdc index cab6708..63815b5 100644 --- a/transactions/hybrid-custody/setup_owned_account.cdc +++ b/transactions/hybrid-custody/setup_owned_account.cdc @@ -1,7 +1,7 @@ #allowAccountLinking +import "ViewResolver" import "MetadataViews" - import "HybridCustody" /// This transaction configures an OwnedAccount in the signer if needed and configures its Capabilities per @@ -9,33 +9,34 @@ import "HybridCustody" /// signer's OwnedAccount. /// transaction(name: String?, desc: String?, thumbnailURL: String?) { - prepare(acct: AuthAccount) { - var acctCap = acct.getCapability<&AuthAccount>(HybridCustody.LinkedAccountPrivatePath) - if !acctCap.check() { - acctCap = acct.linkAccount(HybridCustody.LinkedAccountPrivatePath)! - } + prepare(acct: auth(Storage, Capabilities) &Account) { + let acctCap = acct.capabilities.account.issue() - if acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { + if acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap) - acct.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) + acct.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) } - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + let owned = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") // Set the display metadata for the OwnedAccount if name != nil && desc != nil && thumbnailURL != nil { let thumbnail = MetadataViews.HTTPFile(url: thumbnailURL!) - let display = MetadataViews.Display(name: name!, description: desc!, thumbnail: thumbnail!) + let display = MetadataViews.Display(name: name!, description: desc!, thumbnail: thumbnail) owned.setDisplay(display) } // check that paths are all configured properly - acct.unlink(HybridCustody.OwnedAccountPrivatePath) - acct.link<&HybridCustody.OwnedAccount{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountPrivatePath, target: HybridCustody.OwnedAccountStoragePath) + for c in acct.capabilities.storage.getControllers(forPath: HybridCustody.OwnedAccountStoragePath) { + c.delete() + } - acct.unlink(HybridCustody.OwnedAccountPublicPath) - acct.link<&HybridCustody.OwnedAccount{HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountPublicPath, target: HybridCustody.OwnedAccountStoragePath) + acct.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath), + at: HybridCustody.OwnedAccountPublicPath + ) } } \ No newline at end of file diff --git a/transactions/hybrid-custody/setup_owned_account_and_publish_to_parent.cdc b/transactions/hybrid-custody/setup_owned_account_and_publish_to_parent.cdc index 2349962..6b9f380 100644 --- a/transactions/hybrid-custody/setup_owned_account_and_publish_to_parent.cdc +++ b/transactions/hybrid-custody/setup_owned_account_and_publish_to_parent.cdc @@ -1,6 +1,7 @@ #allowAccountLinking import "MetadataViews" +import "ViewResolver" import "HybridCustody" import "CapabilityFactory" @@ -12,47 +13,47 @@ import "CapabilityDelegator" /// Capability on the ChildAccount is then published to the specified parent account. /// transaction( - parent: Address, - factoryAddress: Address, - filterAddress: Address, - name: String?, - desc: String?, - thumbnailURL: String? - ) { - - prepare(acct: AuthAccount) { + parent: Address, + factoryAddress: Address, + filterAddress: Address, + name: String?, + desc: String?, + thumbnailURL: String? +) { + prepare(acct: auth(Storage, Capabilities) &Account) { // Configure OwnedAccount if it doesn't exist - if acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { - var acctCap = acct.getCapability<&AuthAccount>(HybridCustody.LinkedAccountPrivatePath) - if !acctCap.check() { - acctCap = acct.linkAccount(HybridCustody.LinkedAccountPrivatePath)! - } + if acct.storage.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) == nil { + var acctCap = acct.capabilities.account.issue() let ownedAccount <- HybridCustody.createOwnedAccount(acct: acctCap) - acct.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) + acct.storage.save(<-ownedAccount, to: HybridCustody.OwnedAccountStoragePath) + } + + for c in acct.capabilities.storage.getControllers(forPath: HybridCustody.OwnedAccountStoragePath) { + c.delete() } - // check that paths are all configured properly - acct.unlink(HybridCustody.OwnedAccountPrivatePath) - acct.link<&HybridCustody.OwnedAccount{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountPrivatePath, target: HybridCustody.OwnedAccountStoragePath) - acct.unlink(HybridCustody.OwnedAccountPublicPath) - acct.link<&HybridCustody.OwnedAccount{HybridCustody.OwnedAccountPublic, MetadataViews.Resolver}>(HybridCustody.OwnedAccountPublicPath, target: HybridCustody.OwnedAccountStoragePath) + acct.capabilities.storage.issue<&{HybridCustody.BorrowableAccount, HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{HybridCustody.OwnedAccountPublic, ViewResolver.Resolver}>(HybridCustody.OwnedAccountStoragePath), + at: HybridCustody.OwnedAccountPublicPath + ) - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + let owned = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned account not found") // Set the display metadata for the OwnedAccount if name != nil && desc != nil && thumbnailURL != nil { let thumbnail = MetadataViews.HTTPFile(url: thumbnailURL!) - let display = MetadataViews.Display(name: name!, description: desc!, thumbnail: thumbnail!) + let display = MetadataViews.Display(name: name!, description: desc!, thumbnail: thumbnail) owned.setDisplay(display) } // Get CapabilityFactory & CapabilityFilter Capabilities - let factory = getAccount(factoryAddress).getCapability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>(CapabilityFactory.PublicPath) + let factory = getAccount(factoryAddress).capabilities.get<&CapabilityFactory.Manager>(CapabilityFactory.PublicPath) assert(factory.check(), message: "factory address is not configured properly") - let filter = getAccount(filterAddress).getCapability<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) + let filter = getAccount(filterAddress).capabilities.get<&{CapabilityFilter.Filter}>(CapabilityFilter.PublicPath) assert(filter.check(), message: "capability filter is not configured properly") // Finally publish a ChildAccount capability on the signing account to the specified parent diff --git a/transactions/hybrid-custody/transfer_ownership.cdc b/transactions/hybrid-custody/transfer_ownership.cdc index 2be0ee1..34f1a30 100644 --- a/transactions/hybrid-custody/transfer_ownership.cdc +++ b/transactions/hybrid-custody/transfer_ownership.cdc @@ -3,8 +3,8 @@ import "HybridCustody" transaction(owner: Address) { - prepare(acct: AuthAccount) { - let owned = acct.borrow<&HybridCustody.OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) + prepare(acct: auth(Storage) &Account) { + let owned = acct.storage.borrow(from: HybridCustody.OwnedAccountStoragePath) ?? panic("owned not found") owned.giveOwnership(to: owner) } diff --git a/transactions/hybrid-custody/transfer_ownership_from_manager.cdc b/transactions/hybrid-custody/transfer_ownership_from_manager.cdc index 45e35c6..45a0e3d 100644 --- a/transactions/hybrid-custody/transfer_ownership_from_manager.cdc +++ b/transactions/hybrid-custody/transfer_ownership_from_manager.cdc @@ -3,8 +3,8 @@ import "HybridCustody" transaction(ownedAddress: Address, owner: Address) { - prepare(acct: AuthAccount) { - let manager = acct.borrow<&HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath) + prepare(acct: auth(Storage) &Account) { + let manager = acct.storage.borrow(from: HybridCustody.ManagerStoragePath) ?? panic("manager not found") manager.giveOwnership(addr: ownedAddress, to: owner) } diff --git a/transactions/hybrid-custody/unlink_child_capability.cdc b/transactions/hybrid-custody/unlink_child_capability.cdc new file mode 100644 index 0000000..67bd93b --- /dev/null +++ b/transactions/hybrid-custody/unlink_child_capability.cdc @@ -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() + } + } +} \ No newline at end of file diff --git a/transactions/misc/unlink_from_storage_paths.cdc b/transactions/misc/unlink_from_storage_paths.cdc new file mode 100644 index 0000000..ff8c533 --- /dev/null +++ b/transactions/misc/unlink_from_storage_paths.cdc @@ -0,0 +1,10 @@ +transaction(storagePaths: [StoragePath]) { + prepare(acct: auth(Capabilities) &Account) { + for storagePath in storagePaths { + let controllers = acct.capabilities.storage.getControllers(forPath: storagePath) + for con in controllers { + acct.capabilities.storage.getController(byCapabilityID: con.capabilityID)?.delete() + } + } + } +} \ No newline at end of file diff --git a/transactions/misc/unlink_paths.cdc b/transactions/misc/unlink_paths.cdc deleted file mode 100644 index dbe5bc8..0000000 --- a/transactions/misc/unlink_paths.cdc +++ /dev/null @@ -1,7 +0,0 @@ -transaction(paths: [CapabilityPath]) { - prepare(acct: AuthAccount) { - for p in paths { - acct.unlink(p) - } - } -} \ No newline at end of file diff --git a/transactions/test/add_type_for_nft_provider_factory.cdc b/transactions/test/add_type_for_nft_provider_factory.cdc index 4c847f9..8bb0166 100644 --- a/transactions/test/add_type_for_nft_provider_factory.cdc +++ b/transactions/test/add_type_for_nft_provider_factory.cdc @@ -4,8 +4,8 @@ import "NFTProviderFactory" import "NonFungibleToken" transaction(type: Type) { - prepare(account: AuthAccount) { - let managerRef = account.borrow<&CapabilityFactory.Manager>( + prepare(account: auth(Storage) &Account) { + let managerRef = account.storage.borrow( from: CapabilityFactory.StoragePath ) ?? panic("CapabilityFactory Manager not found")