Smart Contract
CapabilityDelegator
A.d8a7e05a7ac670c0.CapabilityDelegator
1/// CapabilityDelegator is a contract used to share Capabiltities to other accounts. It is used by the
2/// HybridCustody contract to allow more flexible sharing of Capabilities when an app wants to share things
3/// that aren't the NFT-standard interface types.
4///
5/// Inside of CapabilityDelegator is a resource called `Delegator` which maintains a mapping of public and private
6/// Capabilities. They cannot and should not be mixed. A public `Delegator` is able to be borrowed by anyone, whereas a
7/// private `Delegator` can only be borrowed from the child account when you have access to the full `ChildAccount`
8/// resource.
9///
10access(all) contract CapabilityDelegator {
11
12 /* --- Canonical Paths --- */
13 //
14 access(all) let StoragePath: StoragePath
15 access(all) let PublicPath: PublicPath
16
17 access(all) entitlement Get
18 access(all) entitlement Add
19 access(all) entitlement Delete
20
21 /* --- Events --- */
22 //
23 access(all) event DelegatorCreated(id: UInt64)
24 access(all) event DelegatorUpdated(id: UInt64, capabilityType: Type, isPublic: Bool, active: Bool)
25
26 /// Private interface for Capability retrieval
27 ///
28 access(all) resource interface GetterPrivate {
29 access(Get) view fun getPrivateCapability(_ type: Type): Capability? {
30 post {
31 result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type"
32 }
33 }
34 access(all) view fun findFirstPrivateType(_ type: Type): Type?
35 access(Get) fun getAllPrivate(): [Capability]
36 }
37
38 /// Exposes public Capability retrieval
39 ///
40 access(all) resource interface GetterPublic {
41 access(all) view fun getPublicCapability(_ type: Type): Capability? {
42 post {
43 result == nil || type.isSubtype(of: result.getType()): "incorrect returned capability type"
44 }
45 }
46
47 access(all) view fun findFirstPublicType(_ type: Type): Type?
48 access(all) view fun getAllPublic(): [Capability]
49 }
50
51 /// This Delegator is used to store Capabilities, partitioned by public and private access with corresponding
52 /// GetterPublic and GetterPrivate conformances.AccountCapabilityController
53 ///
54 access(all) resource Delegator: GetterPublic, GetterPrivate {
55 access(self) let privateCapabilities: {Type: Capability}
56 access(self) let publicCapabilities: {Type: Capability}
57
58 // ------ Begin Getter methods
59 //
60 /// Returns the public Capability of the given Type if it exists
61 ///
62 access(all) view fun getPublicCapability(_ type: Type): Capability? {
63 return self.publicCapabilities[type]
64 }
65
66 /// Returns the private Capability of the given Type if it exists
67 ///
68 ///
69 /// @param type: Type of the Capability to retrieve
70 /// @return Capability of the given Type if it exists, nil otherwise
71 ///
72 access(Get) view fun getPrivateCapability(_ type: Type): Capability? {
73 return self.privateCapabilities[type]
74 }
75
76 /// Returns all public Capabilities
77 ///
78 /// @return List of all public Capabilities
79 ///
80 access(all) view fun getAllPublic(): [Capability] {
81 return self.publicCapabilities.values
82 }
83
84 /// Returns all private Capabilities
85 ///
86 /// @return List of all private Capabilities
87 ///
88 access(Get) fun getAllPrivate(): [Capability] {
89 return self.privateCapabilities.values
90 }
91
92 /// Returns the first public Type that is a subtype of the given Type
93 ///
94 /// @param type: Type to check for subtypes
95 /// @return First public Type that is a subtype of the given Type, nil otherwise
96 ///
97 access(all) view fun findFirstPublicType(_ type: Type): Type? {
98 for t in self.publicCapabilities.keys {
99 if t.isSubtype(of: type) {
100 return t
101 }
102 }
103
104 return nil
105 }
106
107 /// Returns the first private Type that is a subtype of the given Type
108 ///
109 /// @param type: Type to check for subtypes
110 /// @return First private Type that is a subtype of the given Type, nil otherwise
111 ///
112 access(all) view fun findFirstPrivateType(_ type: Type): Type? {
113 for t in self.privateCapabilities.keys {
114 if t.isSubtype(of: type) {
115 return t
116 }
117 }
118
119 return nil
120 }
121 // ------- End Getter methods
122
123 /// Adds a Capability to the Delegator
124 ///
125 /// @param cap: Capability to add
126 /// @param isPublic: Whether the Capability should be public or private
127 ///
128 access(Add) fun addCapability(cap: Capability, isPublic: Bool) {
129 pre {
130 cap.check<&AnyResource>(): "Invalid Capability provided"
131 }
132 if isPublic {
133 self.publicCapabilities.insert(key: cap.getType(), cap)
134 } else {
135 self.privateCapabilities.insert(key: cap.getType(), cap)
136 }
137 emit DelegatorUpdated(id: self.uuid, capabilityType: cap.getType(), isPublic: isPublic, active: true)
138 }
139
140 /// Removes a Capability from the Delegator
141 ///
142 /// @param cap: Capability to remove
143 ///
144 access(Delete) fun removeCapability(cap: Capability) {
145 if let removedPublic = self.publicCapabilities.remove(key: cap.getType()) {
146 emit DelegatorUpdated(id: self.uuid, capabilityType: cap.getType(), isPublic: true, active: false)
147 }
148
149 if let removedPrivate = self.privateCapabilities.remove(key: cap.getType()) {
150 emit DelegatorUpdated(id: self.uuid, capabilityType: cap.getType(), isPublic: false, active: false)
151 }
152 }
153
154 init() {
155 self.privateCapabilities = {}
156 self.publicCapabilities = {}
157 }
158 }
159
160 /// Creates a new Delegator and returns it
161 ///
162 /// @return Newly created Delegator
163 ///
164 access(all) fun createDelegator(): @Delegator {
165 let delegator <- create Delegator()
166 emit DelegatorCreated(id: delegator.uuid)
167 return <- delegator
168 }
169
170 init() {
171 let identifier = "CapabilityDelegator_".concat(self.account.address.toString())
172 self.StoragePath = StoragePath(identifier: identifier)!
173 self.PublicPath = PublicPath(identifier: identifier)!
174 }
175}
176