Smart Contract
CapabilityCache
A.acc5081c003e24cf.CapabilityCache
1/*
2https://github.com/Flowtyio/capability-cache
3
4CapabilityCache helps manage capabilities which are issued but are not in public paths.
5Rather than looping through all capabilities under a storage path and finding one that
6matches the Capability type you want, the cache can be used to retrieve them
7*/
8access(all) contract CapabilityCache {
9
10 access(all) let basePathIdentifier: String
11
12 access(all) event CapabilityAdded(owner: Address?, cacheUuid: UInt64, namespace: String, resourceType: Type, capabilityType: Type, capabilityID: UInt64)
13 access(all) event CapabilityRemoved(owner: Address?, cacheUuid: UInt64, namespace: String, resourceType: Type, capabilityType: Type, capabilityID: UInt64)
14
15 // Add to a namespace
16 access(all) entitlement Add
17
18 // Remove from a namespace
19 access(all) entitlement Delete
20
21 // Retrieve a cap from the namespace
22 access(all) entitlement Get
23
24 // Resource that manages capabilities for a provided namespace. Only one capability is permitted per type.
25 access(all) resource Cache {
26 // A dictionary of resourceType -> CapabilityType -> Capability
27 // For example, one might store a Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}> for the @TopShot.NFT resource.
28 // Note that the resource type is not necessarily the type that the borrowed capability is an instance of. This is because some resource definitions
29 // might be reused.
30 access(self) let caps: {Type: {Type: Capability}}
31
32 // who is this capability cache maintained by? e.g. flowty, dapper, find?
33 access(all) let namespace: String
34
35 // Remove a capability, if it exists,
36 access(Delete) fun removeCapabilityByType(resourceType: Type, capabilityType: Type): Capability? {
37 if let ref = &self.caps[resourceType] as auth(Mutate) &{Type: Capability}? {
38 let cap = ref.remove(key: capabilityType)
39 if cap != nil {
40 emit CapabilityRemoved(owner: self.owner?.address, cacheUuid: self.uuid, namespace: self.namespace, resourceType: resourceType, capabilityType: capabilityType, capabilityID: cap!.id)
41 }
42 }
43
44 return nil
45 }
46
47 // Adds a capability to the cache. If there is already an entry for the given type,
48 // it will be returned
49 access(Add) fun addCapability(resourceType: Type, cap: Capability): Capability? {
50 pre {
51 cap.id != 0: "cannot add a capability with id 0"
52 }
53
54 let capType = cap.getType()
55 emit CapabilityAdded(owner: self.owner?.address, cacheUuid: self.uuid, namespace: self.namespace, resourceType: resourceType, capabilityType: capType, capabilityID: cap.id)
56 if let ref = &self.caps[resourceType] as auth(Mutate) &{Type: Capability}? {
57 return ref.insert(key: capType, cap)
58 }
59
60 self.caps[resourceType] = {
61 capType: cap
62 }
63
64 return nil
65 }
66
67 // Retrieve a capability key'd by a given type.
68 access(Get) fun getCapabilityByType(resourceType: Type, capabilityType: Type): Capability? {
69 if let tmp = self.caps[resourceType] {
70 return tmp[capabilityType]
71 }
72
73 return nil
74 }
75
76 init(namespace: String) {
77 self.caps = {}
78
79 self.namespace = namespace
80 }
81 }
82
83 // There is no uniform storage path for the Capability Cache. Instead, each platform which issues capabilities
84 // should manage their own cache, and can generate the storage path to store it in with this helper method
85 access(all) fun getPathForCache(_ namespace: String): StoragePath {
86 return StoragePath(identifier: self.basePathIdentifier.concat(namespace))
87 ?? panic("invalid namespace value")
88 }
89
90 access(all) fun createCache(namespace: String): @Cache {
91 return <- create Cache(namespace: namespace)
92 }
93
94 init() {
95 self.basePathIdentifier = "CapabilityCache_".concat(self.account.address.toString()).concat("_")
96 }
97}