Smart Contract

CapabilityFilter

A.d8a7e05a7ac670c0.CapabilityFilter

Deployed

1w ago
Feb 17, 2026, 02:27:56 PM UTC

Dependents

7821 imports
1/// CapabilityFilter defines `Filter`, an interface to sit on top of a ChildAccount's capabilities. Requested
2/// capabilities will only return if the filter's `allowed` method returns true.
3///
4/// Along with the `Filter` interface are three implementations:
5/// - `DenylistFilter`  - A filter which contains a mapping of denied Types
6/// - `AllowlistFilter` - A filter which contains a mapping of allowed Types
7/// - `AllowAllFilter`  - A passthrough, all requested capabilities are allowed
8/// 
9access(all) contract CapabilityFilter {
10    
11    /* --- Canonical Paths --- */
12    //
13    access(all) let StoragePath: StoragePath
14    access(all) let PublicPath: PublicPath
15
16    access(all) entitlement Add
17    access(all) entitlement Delete
18
19    /* --- Events --- */
20    //
21    access(all) event FilterUpdated(id: UInt64, filterType: Type, type: Type, active: Bool)
22
23    /// `Filter` is a simple interface with methods to determine if a Capability is allowed and retrieve details about
24    /// the Filter itself
25    ///
26    access(all) resource interface Filter {
27        access(all) view fun allowed(cap: Capability): Bool
28        access(all) view fun getDetails(): AnyStruct
29    }
30
31    /// `DenylistFilter` is a `Filter` which contains a mapping of denied Types
32    ///
33    access(all) resource DenylistFilter: Filter {
34
35        /// Represents the underlying types which should not ever be returned by a RestrictedChildAccount. The filter
36        /// will borrow a requested capability, and make sure that the type it gets back is not in the list of denied
37        /// types
38        access(self) let deniedTypes: {Type: Bool}
39
40        /// Adds a type to the mapping of denied types with a value of true
41        /// 
42        /// @param type: The type to add to the denied types mapping
43        ///
44        access(Add) fun addType(_ type: Type) {
45            self.deniedTypes.insert(key: type, true)
46            emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: true)
47        }
48
49        /// Removes a type from the mapping of denied types
50        ///
51        /// @param type: The type to remove from the denied types mapping
52        ///
53        access(Delete) fun removeType(_ type: Type) {
54            if let removed = self.deniedTypes.remove(key: type) {
55                emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: false)
56            }
57        }
58
59        /// Removes all types from the mapping of denied types
60        ///
61        access(Delete) fun removeAllTypes() {
62            for type in self.deniedTypes.keys {
63                self.removeType(type)
64            }
65        }
66
67        /// Determines if a requested capability is allowed by this `Filter`
68        ///
69        /// @param cap: The capability to check
70        /// @return: true if the capability is allowed, false otherwise
71        ///
72        access(all) view fun allowed(cap: Capability): Bool {
73            if let item = cap.borrow<&AnyResource>() {
74                return !self.deniedTypes.containsKey(item.getType())
75            }
76
77            return false
78        }
79
80        /// Returns details about this filter
81        ///
82        /// @return A struct containing details about this filter including this Filter's Type indexed on the `type`
83        ///         key as well as types denied indexed on the `deniedTypes` key
84        ///
85        access(all) view fun getDetails(): AnyStruct {
86            return {
87                "type": self.getType(),
88                "deniedTypes": self.deniedTypes.keys
89            }
90        }
91
92        init() {
93            self.deniedTypes = {}
94        }
95    }
96
97    /// `AllowlistFilter` is a `Filter` which contains a mapping of allowed Types
98    ///
99    access(all) resource AllowlistFilter: Filter {
100        // allowedTypes
101        // Represents the set of underlying types which are allowed to be 
102        // returned by a RestrictedChildAccount. The filter will borrow
103        // a requested capability, and make sure that the type it gets back is
104        // in the list of allowed types
105        access(self) let allowedTypes: {Type: Bool}
106
107        /// Adds a type to the mapping of allowed types with a value of true
108        /// 
109        /// @param type: The type to add to the allowed types mapping
110        ///
111        access(Add) fun addType(_ type: Type) {
112            self.allowedTypes.insert(key: type, true)
113            emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: true)
114        }
115
116        /// Removes a type from the mapping of allowed types
117        ///
118        /// @param type: The type to remove from the denied types mapping
119        ///
120        access(Delete) fun removeType(_ type: Type) {
121            if let removed = self.allowedTypes.remove(key: type) {
122                emit FilterUpdated(id: self.uuid, filterType: self.getType(), type: type, active: false)
123            }
124        }
125
126        /// Removes all types from the mapping of denied types
127        ///
128        access(Delete) fun removeAllTypes() {
129            for type in self.allowedTypes.keys {
130                self.removeType(type)
131            }
132        }
133        
134        /// Determines if a requested capability is allowed by this `Filter`
135        ///
136        /// @param cap: The capability to check
137        /// @return: true if the capability is allowed, false otherwise
138        ///
139        access(all) view fun allowed(cap: Capability): Bool {
140            if let item = cap.borrow<&AnyResource>() {
141                return self.allowedTypes.containsKey(item.getType())
142            }
143
144            return false
145        }
146
147        /// Returns details about this filter
148        ///
149        /// @return A struct containing details about this filter including this Filter's Type indexed on the `type`
150        ///         key as well as types allowed indexed on the `allowedTypes` key
151        ///
152        access(all) view fun getDetails(): AnyStruct {
153            return {
154                "type": self.getType(),
155                "allowedTypes": self.allowedTypes.keys
156            }
157        }
158
159        init() {
160            self.allowedTypes = {}
161        }
162    }
163
164    /// AllowAllFilter is a passthrough, all requested capabilities are allowed
165    ///
166    access(all) resource AllowAllFilter: Filter {
167        /// Determines if a requested capability is allowed by this `Filter`
168        ///
169        /// @param cap: The capability to check
170        /// @return: true since this filter is a passthrough
171        ///
172        access(all) view fun allowed(cap: Capability): Bool {
173            return true
174        }
175        
176        /// Returns details about this filter
177        ///
178        /// @return A struct containing details about this filter including this Filter's Type indexed on the `type`
179        ///         key
180        ///
181        access(all) view fun getDetails(): AnyStruct {
182            return {
183                "type": self.getType()
184            }
185        }
186    }
187
188    /// Creates a new `Filter` of the given type
189    ///
190    /// @param t: The type of `Filter` to create
191    /// @return: A new instance of the given `Filter` type
192    ///
193    access(all) fun createFilter(_ t: Type): @{Filter} {
194        post {
195            result.getType() == t
196        }
197
198        switch t {
199            case Type<@AllowAllFilter>():
200                return <- create AllowAllFilter()
201            case Type<@AllowlistFilter>():
202                return <- create AllowlistFilter()
203            case Type<@DenylistFilter>():
204                return <- create DenylistFilter()
205        }
206
207        panic("unsupported type requested: ".concat(t.identifier))
208    }
209
210    init() {
211        let identifier = "CapabilityFilter_".concat(self.account.address.toString())
212        
213        self.StoragePath = StoragePath(identifier: identifier)!
214        self.PublicPath = PublicPath(identifier: identifier)!
215    }
216}
217