-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from onflow/add-hybrid-custody
Add HybridCustody dependencies & cross-account listing txn
- Loading branch information
Showing
23 changed files
with
2,753 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
lib/js/test/node_modules/* | ||
lib/js/test/node_modules/* | ||
.idea | ||
*.pkey | ||
*.pub | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/// CapabilityDelegator is a contract used to share Capabiltities to other accounts. It is used by the | ||
/// HybridCustody contract to allow more flexible sharing of Capabilities when an app wants to share things | ||
/// that aren't the NFT-standard interface types. | ||
/// | ||
/// Inside of CapabilityDelegator is a resource called `Delegator` which maintains a mapping of public and private | ||
/// Capabilities. They cannot and should not be mixed. A public `Delegator` is able to be borrowed by anyone, whereas a | ||
/// private `Delegator` can only be borrowed from the child account when you have access to the full `ChildAccount` | ||
/// resource. | ||
/// | ||
pub contract CapabilityDelegator { | ||
|
||
/* --- Canonical Paths --- */ | ||
// | ||
pub let StoragePath: StoragePath | ||
pub let PrivatePath: PrivatePath | ||
pub let PublicPath: PublicPath | ||
|
||
/* --- Events --- */ | ||
// | ||
pub event DelegatorCreated(id: UInt64) | ||
pub 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? { | ||
post { | ||
result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type" | ||
} | ||
} | ||
pub fun findFirstPrivateType(_ type: Type): Type? | ||
pub fun getAllPrivate(): [Capability] | ||
} | ||
|
||
/// Exposes public Capability retrieval | ||
/// | ||
pub resource interface GetterPublic { | ||
pub fun getPublicCapability(_ type: Type): Capability? { | ||
post { | ||
result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type " | ||
} | ||
} | ||
|
||
pub fun findFirstPublicType(_ type: Type): Type? | ||
pub 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(self) let privateCapabilities: {Type: Capability} | ||
access(self) let publicCapabilities: {Type: Capability} | ||
|
||
// ------ Begin Getter methods | ||
// | ||
/// Returns the public Capability of the given Type if it exists | ||
/// | ||
pub fun getPublicCapability(_ type: Type): Capability? { | ||
return self.publicCapabilities[type] | ||
} | ||
|
||
/// Returns the private Capability of the given Type if it exists | ||
/// | ||
/// | ||
/// @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? { | ||
return self.privateCapabilities[type] | ||
} | ||
|
||
/// Returns all public Capabilities | ||
/// | ||
/// @return List of all public Capabilities | ||
/// | ||
pub fun getAllPublic(): [Capability] { | ||
return self.publicCapabilities.values | ||
} | ||
|
||
/// Returns all private Capabilities | ||
/// | ||
/// @return List of all private Capabilities | ||
/// | ||
pub fun getAllPrivate(): [Capability] { | ||
return self.privateCapabilities.values | ||
} | ||
|
||
/// Returns the first public Type that is a subtype of the given Type | ||
/// | ||
/// @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? { | ||
for t in self.publicCapabilities.keys { | ||
if t.isSubtype(of: type) { | ||
return t | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
/// Returns the first private Type that is a subtype of the given Type | ||
/// | ||
/// @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? { | ||
for t in self.privateCapabilities.keys { | ||
if t.isSubtype(of: type) { | ||
return t | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
// ------- End Getter methods | ||
/// Adds a Capability to the Delegator | ||
/// | ||
/// @param cap: Capability to add | ||
/// @param isPublic: Whether the Capability should be public or private | ||
/// | ||
pub fun addCapability(cap: Capability, isPublic: Bool) { | ||
pre { | ||
cap.check<&AnyResource>(): "Invalid Capability provided" | ||
} | ||
if isPublic { | ||
self.publicCapabilities.insert(key: cap.getType(), cap) | ||
} else { | ||
self.privateCapabilities.insert(key: cap.getType(), cap) | ||
} | ||
emit DelegatorUpdated(id: self.uuid, capabilityType: cap.getType(), isPublic: isPublic, active: true) | ||
} | ||
|
||
/// Removes a Capability from the Delegator | ||
/// | ||
/// @param cap: Capability to remove | ||
/// | ||
pub 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) | ||
} | ||
|
||
if let removedPrivate = self.privateCapabilities.remove(key: cap.getType()) { | ||
emit DelegatorUpdated(id: self.uuid, capabilityType: cap.getType(), isPublic: false, active: false) | ||
} | ||
} | ||
|
||
init() { | ||
self.privateCapabilities = {} | ||
self.publicCapabilities = {} | ||
} | ||
} | ||
|
||
/// Creates a new Delegator and returns it | ||
/// | ||
/// @return Newly created Delegator | ||
/// | ||
pub fun createDelegator(): @Delegator { | ||
let delegator <- create Delegator() | ||
emit DelegatorCreated(id: delegator.uuid) | ||
return <- delegator | ||
} | ||
|
||
init() { | ||
let identifier = "CapabilityDelegator_".concat(self.account.address.toString()) | ||
self.StoragePath = StoragePath(identifier: identifier)! | ||
self.PrivatePath = PrivatePath(identifier: identifier)! | ||
self.PublicPath = PublicPath(identifier: identifier)! | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/// # Capability Factory | ||
/// | ||
/// This contract defines a Factory interface and a Manager resource to contain Factory implementations, as well as a | ||
/// Getter interface for retrieval of contained Factories. | ||
/// | ||
/// A Factory is defines a method getCapability() which defines the retrieval pattern of a Capability from a given | ||
/// account at the specified path. This pattern arose out of a need to retrieve arbitrary & castable Capabilities from | ||
/// an account under the static typing constraints inherent to Cadence. | ||
/// | ||
/// The Manager resource is a container for Factories, and implements the Getter interface. | ||
/// | ||
/// **Note:** It's generally an anti-pattern to pass around AuthAccount references; however, the need for castable | ||
/// 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 { | ||
|
||
pub let StoragePath: StoragePath | ||
pub let PrivatePath: PrivatePath | ||
pub let PublicPath: PublicPath | ||
|
||
/// 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 | ||
} | ||
|
||
/// 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}? | ||
} | ||
|
||
/// Manager is a resource that contains Factories and implements the Getter interface for retrieval of contained | ||
/// Factories | ||
/// | ||
pub resource Manager: Getter { | ||
/// Mapping of Factories indexed on Type of Capability they retrieve | ||
pub 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] { | ||
return self.factories.keys | ||
} | ||
|
||
/// Retrieves a Factory from the Manager, returning it or nil if it doesn't exist | ||
/// | ||
/// @param t: Type the Factory is indexed on | ||
/// | ||
pub fun getFactory(_ t: Type): {CapabilityFactory.Factory}? { | ||
return self.factories[t] | ||
} | ||
|
||
/// Adds a Factory to the Manager, conditioned on the Factory not already existing | ||
/// | ||
/// @param t: Type of Capability the Factory retrieves | ||
/// @param f: Factory to add | ||
/// | ||
pub fun addFactory(_ t: Type, _ f: {CapabilityFactory.Factory}) { | ||
pre { | ||
!self.factories.containsKey(t): "Factory of given type already exists" | ||
} | ||
self.factories[t] = f | ||
} | ||
|
||
/// Updates a Factory in the Manager, adding if it didn't already exist | ||
/// | ||
/// @param t: Type of Capability the Factory retrieves | ||
/// @param f: Factory to replace existing Factory | ||
/// | ||
pub fun updateFactory(_ t: Type, _ f: {CapabilityFactory.Factory}) { | ||
self.factories[t] = f | ||
} | ||
|
||
/// Removes a Factory from the Manager, returning it or nil if it didn't exist | ||
/// | ||
/// @param t: Type the Factory is indexed on | ||
/// | ||
pub fun removeFactory(_ t: Type): {CapabilityFactory.Factory}? { | ||
return self.factories.remove(key: t) | ||
} | ||
|
||
init () { | ||
self.factories = {} | ||
} | ||
} | ||
|
||
/// Creates a Manager resource | ||
/// | ||
/// @return Manager resource | ||
pub 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)! | ||
} | ||
} |
Oops, something went wrong.