@flowtyio/flow-contracts 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1202 @@
1
+ // Third-party imports
2
+ import "MetadataViews"
3
+
4
+ // HC-owned imports
5
+ import "CapabilityFactory"
6
+ import "CapabilityDelegator"
7
+ import "CapabilityFilter"
8
+
9
+ /// HybridCustody defines a framework for sharing accounts via account linking.
10
+ /// In the contract, there are three main resources:
11
+ ///
12
+ /// 1. OwnedAccount - A resource which maintains an AuthAccount Capability, and handles publishing and revoking access
13
+ /// of that account via another resource called a ChildAccount
14
+ /// 2. ChildAccount - A second resource which exists on the same account as the OwnedAccount and contains the filters
15
+ /// and retrieval patterns governing the scope of parent account access. A Capability on this resource is shared to
16
+ /// the parent account, enabling Hybrid Custody access to the underlying account.
17
+ /// 3. Manager - A resource setup by the parent which manages all child accounts shared with it. The Manager resource
18
+ /// also maintains a set of accounts that it "owns", meaning it has a capability to the full OwnedAccount resource
19
+ /// and would then also be able to manage the child account's links as it sees fit.
20
+ ///
21
+ /// Contributors (please add to this list if you contribute!):
22
+ /// - Austin Kline - https://twitter.com/austin_flowty
23
+ /// - Deniz Edincik - https://twitter.com/bluesign
24
+ /// - Giovanni Sanchez - https://twitter.com/gio_incognito
25
+ /// - Ashley Daffin - https://twitter.com/web3ashlee
26
+ /// - Felipe Ribeiro - https://twitter.com/Frlabs33
27
+ ///
28
+ /// Repo reference: https://github.com/onflow/hybrid-custody
29
+ ///
30
+ pub contract HybridCustody {
31
+
32
+ /* --- Canonical Paths --- */
33
+ //
34
+ // Note: Paths for ChildAccount & Delegator are derived from the parent's address
35
+ //
36
+ pub let OwnedAccountStoragePath: StoragePath
37
+ pub let OwnedAccountPublicPath: PublicPath
38
+ pub let OwnedAccountPrivatePath: PrivatePath
39
+
40
+ pub let ManagerStoragePath: StoragePath
41
+ pub let ManagerPublicPath: PublicPath
42
+ pub let ManagerPrivatePath: PrivatePath
43
+
44
+ pub let LinkedAccountPrivatePath: PrivatePath
45
+ pub let BorrowableAccountPrivatePath: PrivatePath
46
+
47
+ /* --- Events --- */
48
+ //
49
+ /// Manager creation event
50
+ pub event CreatedManager(id: UInt64)
51
+ /// OwnedAccount creation event
52
+ pub event CreatedOwnedAccount(id: UInt64, child: Address)
53
+ /// ChildAccount added/removed from Manager
54
+ /// active : added to Manager
55
+ /// !active : removed from Manager
56
+ pub event AccountUpdated(id: UInt64?, child: Address, parent: Address, active: Bool)
57
+ /// OwnedAccount added/removed or sealed
58
+ /// active && owner != nil : added to Manager
59
+ /// !active && owner == nil : removed from Manager
60
+ pub event OwnershipUpdated(id: UInt64, child: Address, previousOwner: Address?, owner: Address?, active: Bool)
61
+ /// ChildAccount ready to be redeemed by emitted pendingParent
62
+ pub event ChildAccountPublished(
63
+ ownedAcctID: UInt64,
64
+ childAcctID: UInt64,
65
+ capDelegatorID: UInt64,
66
+ factoryID: UInt64,
67
+ filterID: UInt64,
68
+ filterType: Type,
69
+ child: Address,
70
+ pendingParent: Address
71
+ )
72
+ /// OwnedAccount granted ownership to a new address, publishing a Capability for the pendingOwner
73
+ pub event OwnershipGranted(ownedAcctID: UInt64, child: Address, previousOwner: Address?, pendingOwner: Address)
74
+ /// Account has been sealed - keys revoked, new AuthAccount Capability generated
75
+ pub event AccountSealed(id: UInt64, address: Address, parents: [Address])
76
+
77
+ /// An OwnedAccount shares the BorrowableAccount capability to itelf with ChildAccount resources
78
+ ///
79
+ pub resource interface BorrowableAccount {
80
+ access(contract) fun borrowAccount(): &AuthAccount
81
+ pub fun check(): Bool
82
+ }
83
+
84
+ /// Public methods anyone can call on an OwnedAccount
85
+ ///
86
+ pub resource interface OwnedAccountPublic {
87
+ /// Returns the addresses of all parent accounts
88
+ pub fun getParentAddresses(): [Address]
89
+
90
+ /// Returns associated parent addresses and their redeemed status - true if redeemed, false if pending
91
+ pub fun getParentStatuses(): {Address: Bool}
92
+
93
+ /// Returns true if the given address is a parent of this child and has redeemed it. Returns false if the given
94
+ /// address is a parent of this child and has NOT redeemed it. Returns nil if the given address it not a parent
95
+ /// of this child account.
96
+ pub fun getRedeemedStatus(addr: Address): Bool?
97
+
98
+ /// A callback function to mark a parent as redeemed on the child account.
99
+ access(contract) fun setRedeemed(_ addr: Address)
100
+ }
101
+
102
+ /// Private interface accessible to the owner of the OwnedAccount
103
+ ///
104
+ pub resource interface OwnedAccountPrivate {
105
+ /// Deletes the ChildAccount resource being used to share access to this OwnedAccount with the supplied parent
106
+ /// address, and unlinks the paths it was using to reach the underlying account.
107
+ pub fun removeParent(parent: Address): Bool
108
+
109
+ /// Sets up a new ChildAccount resource for the given parentAddress to redeem. This child account uses the
110
+ /// supplied factory and filter to manage what can be obtained from the child account, and a new
111
+ /// CapabilityDelegator resource is created for the sharing of one-off capabilities. Each of these pieces of
112
+ /// access control are managed through the child account.
113
+ pub fun publishToParent(
114
+ parentAddress: Address,
115
+ factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>,
116
+ filter: Capability<&{CapabilityFilter.Filter}>
117
+ ) {
118
+ pre {
119
+ factory.check(): "Invalid CapabilityFactory.Getter Capability provided"
120
+ filter.check(): "Invalid CapabilityFilter Capability provided"
121
+ }
122
+ }
123
+
124
+ /// Passes ownership of this child account to the given address. Once executed, all active keys on the child
125
+ /// account will be revoked, and the active AuthAccount Capability being used by to obtain capabilities will be
126
+ /// rotated, preventing anyone without the newly generated Capability from gaining access to the account.
127
+ pub fun giveOwnership(to: Address)
128
+
129
+ /// Revokes all keys on an account, unlinks all currently active AuthAccount capabilities, then makes a new one
130
+ /// and replaces the OwnedAccount's underlying AuthAccount Capability with the new one to ensure that all
131
+ /// parent accounts can still operate normally.
132
+ /// Unless this method is executed via the giveOwnership function, this will leave an account **without** an
133
+ /// owner.
134
+ /// USE WITH EXTREME CAUTION.
135
+ pub fun seal()
136
+
137
+ // setCapabilityFactoryForParent
138
+ // Override the existing CapabilityFactory Capability for a given parent. This will allow the owner of the
139
+ // account to start managing their own factory of capabilities to be able to retrieve
140
+ pub fun setCapabilityFactoryForParent(parent: Address, cap: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>) {
141
+ pre {
142
+ cap.check(): "Invalid CapabilityFactory.Getter Capability provided"
143
+ }
144
+ }
145
+
146
+ /// Override the existing CapabilityFilter Capability for a given parent. This will allow the owner of the
147
+ /// account to start managing their own filter for retrieving Capabilities on Private Paths
148
+ pub fun setCapabilityFilterForParent(parent: Address, cap: Capability<&{CapabilityFilter.Filter}>) {
149
+ pre {
150
+ cap.check(): "Invalid CapabilityFilter Capability provided"
151
+ }
152
+ }
153
+
154
+ /// Adds a capability to a parent's managed @ChildAccount resource. The Capability can be made public,
155
+ /// permitting anyone to borrow it.
156
+ pub fun addCapabilityToDelegator(parent: Address, cap: Capability, isPublic: Bool) {
157
+ pre {
158
+ cap.check<&AnyResource>(): "Invalid Capability provided"
159
+ }
160
+ }
161
+
162
+ /// Removes a Capability from the CapabilityDelegator used by the specified parent address
163
+ pub fun removeCapabilityFromDelegator(parent: Address, cap: Capability)
164
+
165
+ /// Returns the address of this OwnedAccount
166
+ pub fun getAddress(): Address
167
+
168
+ /// Checks if this OwnedAccount is a child of the specified address
169
+ pub fun isChildOf(_ addr: Address): Bool
170
+
171
+ /// Returns all addresses which are parents of this OwnedAccount
172
+ pub fun getParentAddresses(): [Address]
173
+
174
+ /// Borrows this OwnedAccount's AuthAccount Capability
175
+ pub fun borrowAccount(): &AuthAccount?
176
+
177
+ /// Returns the current owner of this account, if there is one
178
+ pub fun getOwner(): Address?
179
+
180
+ /// Returns the pending owner of this account, if there is one
181
+ pub fun getPendingOwner(): Address?
182
+
183
+ /// A callback which is invoked when a parent redeems an owned account
184
+ access(contract) fun setOwnerCallback(_ addr: Address)
185
+
186
+ /// Destroys all outstanding AuthAccount capabilities on this owned account, and creates a new one for the
187
+ /// OwnedAccount to use
188
+ pub fun rotateAuthAccount()
189
+
190
+ /// Revokes all keys on this account
191
+ pub fun revokeAllKeys()
192
+ }
193
+
194
+ /// Public methods exposed on a ChildAccount resource. OwnedAccountPublic will share some methods here, but isn't
195
+ /// necessarily the same.
196
+ ///
197
+ pub resource interface AccountPublic {
198
+ pub fun getPublicCapability(path: PublicPath, type: Type): Capability?
199
+ pub fun getPublicCapFromDelegator(type: Type): Capability?
200
+ pub fun getAddress(): Address
201
+ }
202
+
203
+ /// Methods accessible to the designated parent of a ChildAccount
204
+ ///
205
+ pub resource interface AccountPrivate {
206
+ pub fun getCapability(path: CapabilityPath, type: Type): Capability? {
207
+ post {
208
+ result == nil || [true, nil].contains(self.getManagerCapabilityFilter()?.allowed(cap: result!)):
209
+ "Capability is not allowed by this account's Parent"
210
+ }
211
+ }
212
+ pub fun getPublicCapability(path: PublicPath, type: Type): Capability?
213
+ pub fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}?
214
+ pub fun getPublicCapFromDelegator(type: Type): Capability?
215
+ pub fun getPrivateCapFromDelegator(type: Type): Capability? {
216
+ post {
217
+ result == nil || [true, nil].contains(self.getManagerCapabilityFilter()?.allowed(cap: result!)):
218
+ "Capability is not allowed by this account's Parent"
219
+ }
220
+ }
221
+ access(contract) fun redeemedCallback(_ addr: Address)
222
+ access(contract) fun setManagerCapabilityFilter(_ managerCapabilityFilter: Capability<&{CapabilityFilter.Filter}>?) {
223
+ pre {
224
+ managerCapabilityFilter == nil || managerCapabilityFilter!.check(): "Invalid Manager Capability Filter"
225
+ }
226
+ }
227
+ access(contract) fun parentRemoveChildCallback(parent: Address)
228
+ }
229
+
230
+ /// Entry point for a parent to obtain, maintain and access Capabilities or perform other actions on child accounts
231
+ ///
232
+ pub resource interface ManagerPrivate {
233
+ pub fun addAccount(cap: Capability<&{AccountPrivate, AccountPublic, MetadataViews.Resolver}>)
234
+ pub fun borrowAccount(addr: Address): &{AccountPrivate, AccountPublic, MetadataViews.Resolver}?
235
+ pub fun removeChild(addr: Address)
236
+ pub fun addOwnedAccount(cap: Capability<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>)
237
+ pub fun borrowOwnedAccount(addr: Address): &{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}?
238
+ pub fun removeOwned(addr: Address)
239
+ pub fun setManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?, childAddress: Address) {
240
+ pre {
241
+ cap == nil || cap!.check(): "Invalid Manager Capability Filter"
242
+ }
243
+ }
244
+ }
245
+
246
+ /// Functions anyone can call on a manager to get information about an account such as What child accounts it has
247
+ /// Functions anyone can call on a manager to get information about an account such as what child accounts it has
248
+ pub resource interface ManagerPublic {
249
+ pub fun borrowAccountPublic(addr: Address): &{AccountPublic, MetadataViews.Resolver}?
250
+ pub fun getChildAddresses(): [Address]
251
+ pub fun getOwnedAddresses(): [Address]
252
+ pub fun getChildAccountDisplay(address: Address): MetadataViews.Display?
253
+ access(contract) fun removeParentCallback(child: Address)
254
+ }
255
+
256
+ /// A resource for an account which fills the Parent role of the Child-Parent account management Model. A Manager
257
+ /// can redeem or remove child accounts, and obtain any capabilities exposed by the child account to them.
258
+ ///
259
+ pub resource Manager: ManagerPrivate, ManagerPublic, MetadataViews.Resolver {
260
+
261
+ /// Mapping of restricted access child account Capabilities indexed by their address
262
+ pub let childAccounts: {Address: Capability<&{AccountPrivate, AccountPublic, MetadataViews.Resolver}>}
263
+ /// Mapping of unrestricted owned account Capabilities indexed by their address
264
+ pub let ownedAccounts: {Address: Capability<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>}
265
+
266
+ /// A bucket of structs so that the Manager resource can be easily extended with new functionality.
267
+ pub let data: {String: AnyStruct}
268
+ /// A bucket of resources so that the Manager resource can be easily extended with new functionality.
269
+ pub let resources: @{String: AnyResource}
270
+
271
+ /// An optional filter to gate what capabilities are permitted to be returned from a child account For example,
272
+ /// Dapper Wallet parent account's should not be able to retrieve any FungibleToken Provider capabilities.
273
+ pub var filter: Capability<&{CapabilityFilter.Filter}>?
274
+
275
+ // display metadata for a child account exists on its parent
276
+ pub let childAccountDisplays: {Address: MetadataViews.Display}
277
+
278
+ /// Sets the Display on the ChildAccount. If nil, the display is removed.
279
+ ///
280
+ pub fun setChildAccountDisplay(address: Address, _ d: MetadataViews.Display?) {
281
+ pre {
282
+ self.childAccounts[address] != nil: "There is no child account with this address"
283
+ }
284
+
285
+ if d == nil {
286
+ self.childAccountDisplays.remove(key: address)
287
+ return
288
+ }
289
+
290
+ self.childAccountDisplays[address] = d
291
+ }
292
+
293
+ /// Adds a ChildAccount Capability to this Manager. If a default Filter is set in the manager, it will also be
294
+ /// added to the ChildAccount
295
+ ///
296
+ pub fun addAccount(cap: Capability<&{AccountPrivate, AccountPublic, MetadataViews.Resolver}>) {
297
+ pre {
298
+ self.childAccounts[cap.address] == nil: "There is already a child account with this address"
299
+ }
300
+
301
+ let acct = cap.borrow()
302
+ ?? panic("child account capability could not be borrowed")
303
+
304
+ self.childAccounts[cap.address] = cap
305
+
306
+ emit AccountUpdated(id: acct.uuid, child: cap.address, parent: self.owner!.address, active: true)
307
+
308
+ acct.redeemedCallback(self.owner!.address)
309
+ acct.setManagerCapabilityFilter(self.filter)
310
+ }
311
+
312
+ /// Sets the default Filter Capability for this Manager. Does not propagate to child accounts.
313
+ ///
314
+ pub fun setDefaultManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?) {
315
+ pre {
316
+ cap == nil || cap!.check(): "supplied capability must be nil or check must pass"
317
+ }
318
+
319
+ self.filter = cap
320
+ }
321
+
322
+ /// Sets the Filter Capability for this Manager, propagating to the specified child account
323
+ ///
324
+ pub fun setManagerCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>?, childAddress: Address) {
325
+ let acct = self.borrowAccount(addr: childAddress)
326
+ ?? panic("child account not found")
327
+
328
+ acct.setManagerCapabilityFilter(cap)
329
+ }
330
+
331
+ /// Removes specified child account from the Manager's child accounts. Callbacks to the child account remove
332
+ /// any associated resources and Capabilities
333
+ ///
334
+ pub fun removeChild(addr: Address) {
335
+ let cap = self.childAccounts.remove(key: addr)
336
+ ?? panic("child account not found")
337
+
338
+ self.childAccountDisplays.remove(key: addr)
339
+
340
+ if !cap.check() {
341
+ // Emit event if invalid capability
342
+ emit AccountUpdated(id: nil, child: cap.address, parent: self.owner!.address, active: false)
343
+ return
344
+ }
345
+
346
+ let acct = cap.borrow()!
347
+ // Get the child account id before removing capability
348
+ let id: UInt64 = acct.uuid
349
+
350
+ acct.parentRemoveChildCallback(parent: self.owner!.address)
351
+
352
+ emit AccountUpdated(id: id, child: cap.address, parent: self.owner!.address, active: false)
353
+ }
354
+
355
+ /// Contract callback that removes a child account from the Manager's child accounts in the event a child
356
+ /// account initiates unlinking parent from child
357
+ ///
358
+ access(contract) fun removeParentCallback(child: Address) {
359
+ self.childAccounts.remove(key: child)
360
+ self.childAccountDisplays.remove(key: child)
361
+ }
362
+
363
+ /// Adds an owned account to the Manager's list of owned accounts, setting the Manager account as the owner of
364
+ /// the given account
365
+ ///
366
+ pub fun addOwnedAccount(cap: Capability<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>) {
367
+ pre {
368
+ self.ownedAccounts[cap.address] == nil: "There is already an owned account with this address"
369
+ }
370
+
371
+ let acct = cap.borrow()
372
+ ?? panic("owned account capability could not be borrowed")
373
+
374
+ // for safety, rotate the auth account capability to prevent any outstanding capabilities from the previous owner
375
+ // and revoke all outstanding keys.
376
+ acct.rotateAuthAccount()
377
+ acct.revokeAllKeys()
378
+
379
+ self.ownedAccounts[cap.address] = cap
380
+
381
+ emit OwnershipUpdated(id: acct.uuid, child: cap.address, previousOwner: acct.getOwner(), owner: self.owner!.address, active: true)
382
+ acct.setOwnerCallback(self.owner!.address)
383
+ }
384
+
385
+ /// Returns a reference to a child account
386
+ ///
387
+ pub fun borrowAccount(addr: Address): &{AccountPrivate, AccountPublic, MetadataViews.Resolver}? {
388
+ let cap = self.childAccounts[addr]
389
+ if cap == nil {
390
+ return nil
391
+ }
392
+
393
+ return cap!.borrow()
394
+ }
395
+
396
+ /// Returns a reference to a child account's public AccountPublic interface
397
+ ///
398
+ pub fun borrowAccountPublic(addr: Address): &{AccountPublic, MetadataViews.Resolver}? {
399
+ let cap = self.childAccounts[addr]
400
+ if cap == nil {
401
+ return nil
402
+ }
403
+
404
+ return cap!.borrow()
405
+ }
406
+
407
+ /// Returns a reference to an owned account
408
+ ///
409
+ pub fun borrowOwnedAccount(addr: Address): &{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}? {
410
+ if let cap = self.ownedAccounts[addr] {
411
+ return cap.borrow()
412
+ }
413
+
414
+ return nil
415
+ }
416
+
417
+ /// Removes specified child account from the Manager's child accounts. Callbacks to the child account remove
418
+ /// any associated resources and Capabilities
419
+ ///
420
+ pub fun removeOwned(addr: Address) {
421
+ if let acct = self.ownedAccounts.remove(key: addr) {
422
+ if acct.check() {
423
+ acct.borrow()!.seal()
424
+ }
425
+ let id: UInt64? = acct.borrow()?.uuid ?? nil
426
+
427
+ emit OwnershipUpdated(id: id!, child: addr, previousOwner: self.owner!.address, owner: nil, active: false)
428
+ }
429
+ // Don't emit an event if nothing was removed
430
+ }
431
+
432
+ /// Removes the owned Capabilty on the specified account, relinquishing access to the account and publishes a
433
+ /// Capability for the specified account. See `OwnedAccount.giveOwnership()` for more details on this method.
434
+ ///
435
+ /// **NOTE:** The existence of this method does not imply that it is the only way to receive access to a
436
+ /// OwnedAccount Capability or that only the labeled `to` account has said access. Rather, this is a convenient
437
+ /// mechanism intended to easily transfer 'root' access on this account to another account and an attempt to
438
+ /// minimize access vectors.
439
+ ///
440
+ pub fun giveOwnership(addr: Address, to: Address) {
441
+ let acct = self.ownedAccounts.remove(key: addr)
442
+ ?? panic("account not found")
443
+
444
+ acct.borrow()!.giveOwnership(to: to)
445
+ }
446
+
447
+ /// Returns an array of child account addresses
448
+ ///
449
+ pub fun getChildAddresses(): [Address] {
450
+ return self.childAccounts.keys
451
+ }
452
+
453
+ /// Returns an array of owned account addresses
454
+ ///
455
+ pub fun getOwnedAddresses(): [Address] {
456
+ return self.ownedAccounts.keys
457
+ }
458
+
459
+ /// Retrieves the parent-defined display for the given child account
460
+ ///
461
+ pub fun getChildAccountDisplay(address: Address): MetadataViews.Display? {
462
+ return self.childAccountDisplays[address]
463
+ }
464
+
465
+ /// Returns the types of supported views - none at this time
466
+ ///
467
+ pub fun getViews(): [Type] {
468
+ return []
469
+ }
470
+
471
+ /// Resolves the given view if supported - none at this time
472
+ ///
473
+ pub fun resolveView(_ view: Type): AnyStruct? {
474
+ return nil
475
+ }
476
+
477
+ init(filter: Capability<&{CapabilityFilter.Filter}>?) {
478
+ pre {
479
+ filter == nil || filter!.check(): "Invalid CapabilityFilter Filter capability provided"
480
+ }
481
+ self.childAccounts = {}
482
+ self.ownedAccounts = {}
483
+ self.childAccountDisplays = {}
484
+ self.filter = filter
485
+
486
+ self.data = {}
487
+ self.resources <- {}
488
+ }
489
+
490
+ destroy () {
491
+ destroy self.resources
492
+ }
493
+ }
494
+
495
+ /// The ChildAccount resource sits between a child account and a parent and is stored on the same account as the
496
+ /// child account. Once created, a private capability to the child account is shared with the intended parent. The
497
+ /// parent account will accept this child capability into its own manager resource and use it to interact with the
498
+ /// child account.
499
+ ///
500
+ /// Because the ChildAccount resource exists on the child account itself, whoever owns the child account will be
501
+ /// able to manage all ChildAccount resources it shares, without worrying about whether the upstream parent can do
502
+ /// anything to prevent it.
503
+ ///
504
+ pub resource ChildAccount: AccountPrivate, AccountPublic, MetadataViews.Resolver {
505
+ /// A Capability providing access to the underlying child account
506
+ access(self) let childCap: Capability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>
507
+
508
+ /// The CapabilityFactory Manager is a ChildAccount's way of limiting what types can be asked for by its parent
509
+ /// account. The CapabilityFactory returns Capabilities which can be casted to their appropriate types once
510
+ /// obtained, but only if the child account has configured their factory to allow it. For instance, a
511
+ /// ChildAccount might choose to expose NonFungibleToken.Provider, but not FungibleToken.Provider
512
+ pub var factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>
513
+
514
+ /// The CapabilityFilter is a restriction put at the front of obtaining any non-public Capability. Some wallets
515
+ /// might want to give access to NonFungibleToken.Provider, but only to **some** of the collections it manages,
516
+ /// not all of them.
517
+ pub var filter: Capability<&{CapabilityFilter.Filter}>
518
+
519
+ /// The CapabilityDelegator is a way to share one-off capabilities from the child account. These capabilities
520
+ /// can be public OR private and are separate from the factory which returns a capability at a given path as a
521
+ /// certain type. When using the CapabilityDelegator, you do not have the ability to specify which path a
522
+ /// capability came from. For instance, Dapper Wallet might choose to expose a Capability to their Full TopShot
523
+ /// collection, but only to the path that the collection exists in.
524
+ pub let delegator: Capability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>
525
+
526
+ /// managerCapabilityFilter is a component optionally given to a child account when a manager redeems it. If
527
+ /// this filter is not nil, any Capability returned through the `getCapability` function checks that the
528
+ /// manager allows access first.
529
+ access(self) var managerCapabilityFilter: Capability<&{CapabilityFilter.Filter}>?
530
+
531
+ /// A bucket of structs so that the ChildAccount resource can be easily extended with new functionality.
532
+ access(self) let data: {String: AnyStruct}
533
+
534
+ /// A bucket of resources so that the ChildAccount resource can be easily extended with new functionality.
535
+ access(self) let resources: @{String: AnyResource}
536
+
537
+ /// ChildAccount resources have a 1:1 association with parent accounts, the named parent Address here is the
538
+ /// one with a Capability on this resource.
539
+ pub let parent: Address
540
+
541
+ /// Returns the Address of the underlying child account
542
+ ///
543
+ pub fun getAddress(): Address {
544
+ return self.childCap.address
545
+ }
546
+
547
+ /// Callback setting the child account as redeemed by the provided parent Address
548
+ ///
549
+ access(contract) fun redeemedCallback(_ addr: Address) {
550
+ self.childCap.borrow()!.setRedeemed(addr)
551
+ }
552
+
553
+ /// Sets the given filter as the managerCapabilityFilter for this ChildAccount
554
+ ///
555
+ access(contract) fun setManagerCapabilityFilter(
556
+ _ managerCapabilityFilter: Capability<&{CapabilityFilter.Filter}>?
557
+ ) {
558
+ self.managerCapabilityFilter = managerCapabilityFilter
559
+ }
560
+
561
+ /// Sets the CapabiltyFactory.Manager Capability
562
+ ///
563
+ pub fun setCapabilityFactory(cap: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>) {
564
+ self.factory = cap
565
+ }
566
+
567
+ /// Sets the Filter Capability as the one provided
568
+ ///
569
+ pub fun setCapabilityFilter(cap: Capability<&{CapabilityFilter.Filter}>) {
570
+ self.filter = cap
571
+ }
572
+
573
+ /// The main function to a child account's capabilities from a parent account. When a PrivatePath type is used,
574
+ /// the CapabilityFilter will be borrowed and the Capability being returned will be checked against it to
575
+ /// ensure that borrowing is permitted.
576
+ /// Also know that this method retrieves Capabilities via the CapabilityFactory path. To retrieve arbitrary
577
+ /// Capabilities, see `getPrivateCapFromDelegator()` and `getPublicCapFromDelegator()` which use the
578
+ /// `Delegator` retrieval path.
579
+ ///
580
+ pub fun getCapability(path: CapabilityPath, type: Type): Capability? {
581
+ let child = self.childCap.borrow() ?? panic("failed to borrow child account")
582
+
583
+ let f = self.factory.borrow()!.getFactory(type)
584
+ if f == nil {
585
+ return nil
586
+ }
587
+
588
+ let acct = child.borrowAccount()
589
+
590
+ let cap = f!.getCapability(acct: acct, path: path)
591
+
592
+ if path.getType() == Type<PrivatePath>() {
593
+ assert(self.filter.borrow()!.allowed(cap: cap), message: "requested capability is not allowed")
594
+ }
595
+
596
+ return cap
597
+ }
598
+
599
+ /// Retrieves a private Capability from the Delegator or nil none is found of the given type. Useful for
600
+ /// arbitrary Capability retrieval
601
+ ///
602
+ pub fun getPrivateCapFromDelegator(type: Type): Capability? {
603
+ if let d = self.delegator.borrow() {
604
+ return d.getPrivateCapability(type)
605
+ }
606
+
607
+ return nil
608
+ }
609
+
610
+ /// Retrieves a public Capability from the Delegator or nil none is found of the given type. Useful for
611
+ /// arbitrary Capability retrieval
612
+ ///
613
+ pub fun getPublicCapFromDelegator(type: Type): Capability? {
614
+ if let d = self.delegator.borrow() {
615
+ return d.getPublicCapability(type)
616
+ }
617
+ return nil
618
+ }
619
+
620
+ /// Enables retrieval of public Capabilities of the given type from the specified path or nil if none is found.
621
+ /// Callers should be aware this method uses the `CapabilityFactory` retrieval path.
622
+ ///
623
+ pub fun getPublicCapability(path: PublicPath, type: Type): Capability? {
624
+ return self.getCapability(path: path, type: type)
625
+ }
626
+
627
+ /// Returns a reference to the stored managerCapabilityFilter if one exists
628
+ ///
629
+ pub fun getManagerCapabilityFilter(): &{CapabilityFilter.Filter}? {
630
+ return self.managerCapabilityFilter != nil ? self.managerCapabilityFilter!.borrow() : nil
631
+ }
632
+
633
+ /// Sets the child account as redeemed by the given Address
634
+ ///
635
+ access(contract) fun setRedeemed(_ addr: Address) {
636
+ let acct = self.childCap.borrow()!.borrowAccount()
637
+ if let o = acct.borrow<&OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) {
638
+ o.setRedeemed(addr)
639
+ }
640
+ }
641
+
642
+ /// Returns a reference to the stored delegator, generally used for arbitrary Capability retrieval
643
+ ///
644
+ pub fun borrowCapabilityDelegator(): &CapabilityDelegator.Delegator? {
645
+ let path = HybridCustody.getCapabilityDelegatorIdentifier(self.parent)
646
+ return self.childCap.borrow()!.borrowAccount().borrow<&CapabilityDelegator.Delegator>(
647
+ from: StoragePath(identifier: path)!
648
+ )
649
+ }
650
+
651
+ /// Returns a list of supported metadata views
652
+ ///
653
+ pub fun getViews(): [Type] {
654
+ return [
655
+ Type<MetadataViews.Display>()
656
+ ]
657
+ }
658
+
659
+ /// Resolves a view of the given type if supported
660
+ ///
661
+ pub fun resolveView(_ view: Type): AnyStruct? {
662
+ switch view {
663
+ case Type<MetadataViews.Display>():
664
+ let childAddress = self.getAddress()
665
+ let manager = getAccount(self.parent).getCapability<&HybridCustody.Manager{HybridCustody.ManagerPublic}>(HybridCustody.ManagerPublicPath)
666
+
667
+ if !manager.check() {
668
+ return nil
669
+ }
670
+
671
+ return manager!.borrow()!.getChildAccountDisplay(address: childAddress)
672
+ }
673
+ return nil
674
+ }
675
+
676
+ /// Callback to enable parent-initiated removal all the child account and its associated resources &
677
+ /// Capabilities
678
+ ///
679
+ access(contract) fun parentRemoveChildCallback(parent: Address) {
680
+ if !self.childCap.check() {
681
+ return
682
+ }
683
+
684
+ let child: &AnyResource{HybridCustody.BorrowableAccount} = self.childCap.borrow()!
685
+ if !child.check() {
686
+ return
687
+ }
688
+
689
+ let acct = child.borrowAccount()
690
+ if let ownedAcct = acct.borrow<&OwnedAccount>(from: HybridCustody.OwnedAccountStoragePath) {
691
+ ownedAcct.removeParent(parent: parent)
692
+ }
693
+ }
694
+
695
+ init(
696
+ _ childCap: Capability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>,
697
+ _ factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>,
698
+ _ filter: Capability<&{CapabilityFilter.Filter}>,
699
+ _ delegator: Capability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>,
700
+ _ parent: Address
701
+ ) {
702
+ pre {
703
+ childCap.check(): "Provided childCap Capability is invalid"
704
+ factory.check(): "Provided factory Capability is invalid"
705
+ filter.check(): "Provided filter Capability is invalid"
706
+ delegator.check(): "Provided delegator Capability is invalid"
707
+ }
708
+ self.childCap = childCap
709
+ self.factory = factory
710
+ self.filter = filter
711
+ self.delegator = delegator
712
+ self.managerCapabilityFilter = nil // this will get set when a parent account redeems
713
+ self.parent = parent
714
+
715
+ self.data = {}
716
+ self.resources <- {}
717
+ }
718
+
719
+ destroy () {
720
+ destroy <- self.resources
721
+ }
722
+ }
723
+
724
+ /// A resource which sits on the account it manages to make it easier for apps to configure the behavior they want
725
+ /// to permit. An OwnedAccount can be used to create ChildAccount resources and share them, publishing them to
726
+ /// other addresses.
727
+ ///
728
+ /// The OwnedAccount can also be used to pass ownership of an account off to another address, or to relinquish
729
+ /// ownership entirely, marking the account as owned by no one. Note that even if there isn't an owner, the parent
730
+ /// accounts would still exist, allowing a form of Hybrid Custody which has no true owner over an account, but
731
+ /// shared partial ownership.
732
+ ///
733
+ pub resource OwnedAccount: OwnedAccountPrivate, BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver {
734
+ /// Capability on the underlying account object
735
+ access(self) var acct: Capability<&AuthAccount>
736
+
737
+ /// Mapping of current and pending parents, true and false respectively
738
+ pub let parents: {Address: Bool}
739
+ /// Address of the pending owner, if one exists
740
+ pub var pendingOwner: Address?
741
+ /// Address of the current owner, if one exists
742
+ pub var acctOwner: Address?
743
+ /// Owned status of this account
744
+ pub var currentlyOwned: Bool
745
+
746
+ /// A bucket of structs so that the OwnedAccount resource can be easily extended with new functionality.
747
+ access(self) let data: {String: AnyStruct}
748
+
749
+ /// A bucket of resources so that the OwnedAccount resource can be easily extended with new functionality.
750
+ access(self) let resources: @{String: AnyResource}
751
+
752
+ /// display is its own field on the OwnedAccount resource because only the owner of the child account should be
753
+ /// able to set this field.
754
+ access(self) var display: MetadataViews.Display?
755
+
756
+ /// Callback that sets this OwnedAccount as redeemed by the parent
757
+ ///
758
+ access(contract) fun setRedeemed(_ addr: Address) {
759
+ pre {
760
+ self.parents[addr] != nil: "address is not waiting to be redeemed"
761
+ }
762
+
763
+ self.parents[addr] = true
764
+ }
765
+
766
+ /// Callback that sets the owner once redeemed
767
+ ///
768
+ access(contract) fun setOwnerCallback(_ addr: Address) {
769
+ pre {
770
+ self.pendingOwner == addr: "Address does not match pending owner!"
771
+ }
772
+ self.pendingOwner = nil
773
+ self.acctOwner = addr
774
+ }
775
+
776
+
777
+ /// A helper method to make it easier to manage what parents an account has configured. The steps to sharing this
778
+ /// OwnedAccount with a new parent are:
779
+ ///
780
+ /// 1. Create a new CapabilityDelegator for the ChildAccount resource being created. We make a new one here because
781
+ /// CapabilityDelegator types are meant to be shared explicitly. Making one shared base-line of capabilities might
782
+ /// introduce unforseen behavior where an app accidentally shared something to all accounts when it only meant
783
+ /// to go to one of them. It is better for parent accounts to have less access than they might have anticipated,
784
+ /// than for a child to have given out access it did not intend to.
785
+ /// 2. Create a new Capability<&{BorrowableAccount}> which has its own unique path for the parent to share this
786
+ /// child account with. We make new ones each time so that you can revoke access from one parent, without
787
+ /// destroying them all. A new link is made each time based on the address being shared to allow this
788
+ /// fine-grained control, but it is all managed by the OwnedAccount resource itself.
789
+ /// 3. A new @ChildAccount resource is created and saved, using the CapabilityDelegator made in step one, and our
790
+ /// CapabilityFactory and CapabilityFilter Capabilities. Once saved, public and private links are configured for
791
+ /// the ChildAccount.
792
+ /// 4. Publish the newly made private link to the designated parent's inbox for them to claim on their @Manager
793
+ /// resource.
794
+ ///
795
+ pub fun publishToParent(
796
+ parentAddress: Address,
797
+ factory: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>,
798
+ filter: Capability<&{CapabilityFilter.Filter}>
799
+ ) {
800
+ pre{
801
+ self.parents[parentAddress] == nil: "Address pending or already redeemed as parent"
802
+ }
803
+ let capDelegatorIdentifier = HybridCustody.getCapabilityDelegatorIdentifier(parentAddress)
804
+
805
+ let identifier = HybridCustody.getChildAccountIdentifier(parentAddress)
806
+ let childAccountStorage = StoragePath(identifier: identifier)!
807
+
808
+ let capDelegatorStorage = StoragePath(identifier: capDelegatorIdentifier)!
809
+ let acct = self.borrowAccount()
810
+
811
+ assert(acct.borrow<&AnyResource>(from: capDelegatorStorage) == nil, message: "conflicting resource found in capability delegator storage slot for parentAddress")
812
+ assert(acct.borrow<&AnyResource>(from: childAccountStorage) == nil, message: "conflicting resource found in child account storage slot for parentAddress")
813
+
814
+ if acct.borrow<&CapabilityDelegator.Delegator>(from: capDelegatorStorage) == nil {
815
+ let delegator <- CapabilityDelegator.createDelegator()
816
+ acct.save(<-delegator, to: capDelegatorStorage)
817
+ }
818
+
819
+ let capDelegatorPublic = PublicPath(identifier: capDelegatorIdentifier)!
820
+ let capDelegatorPrivate = PrivatePath(identifier: capDelegatorIdentifier)!
821
+
822
+ acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic}>(
823
+ capDelegatorPublic,
824
+ target: capDelegatorStorage
825
+ )
826
+ acct.link<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>(
827
+ capDelegatorPrivate,
828
+ target: capDelegatorStorage
829
+ )
830
+ let delegator = acct.getCapability<&CapabilityDelegator.Delegator{CapabilityDelegator.GetterPublic, CapabilityDelegator.GetterPrivate}>(
831
+ capDelegatorPrivate
832
+ )
833
+ assert(delegator.check(), message: "failed to setup capability delegator for parent address")
834
+
835
+ let borrowableCap = self.borrowAccount().getCapability<&{BorrowableAccount, OwnedAccountPublic, MetadataViews.Resolver}>(
836
+ HybridCustody.OwnedAccountPrivatePath
837
+ )
838
+ let childAcct <- create ChildAccount(borrowableCap, factory, filter, delegator, parentAddress)
839
+
840
+ let childAccountPrivatePath = PrivatePath(identifier: identifier)!
841
+
842
+ acct.save(<-childAcct, to: childAccountStorage)
843
+ acct.link<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountPrivatePath, target: childAccountStorage)
844
+
845
+ let delegatorCap = acct.getCapability<&ChildAccount{AccountPrivate, AccountPublic, MetadataViews.Resolver}>(childAccountPrivatePath)
846
+ assert(delegatorCap.check(), message: "Delegator capability check failed")
847
+
848
+ acct.inbox.publish(delegatorCap, name: identifier, recipient: parentAddress)
849
+ self.parents[parentAddress] = false
850
+
851
+ emit ChildAccountPublished(
852
+ ownedAcctID: self.uuid,
853
+ childAcctID: delegatorCap.borrow()!.uuid,
854
+ capDelegatorID: delegator.borrow()!.uuid,
855
+ factoryID: factory.borrow()!.uuid,
856
+ filterID: filter.borrow()!.uuid,
857
+ filterType: filter.borrow()!.getType(),
858
+ child: self.getAddress(),
859
+ pendingParent: parentAddress
860
+ )
861
+ }
862
+
863
+ /// Checks the validity of the encapsulated account Capability
864
+ ///
865
+ pub fun check(): Bool {
866
+ return self.acct.check()
867
+ }
868
+
869
+ /// Returns a reference to the encapsulated account object
870
+ ///
871
+ pub fun borrowAccount(): &AuthAccount {
872
+ return self.acct.borrow()!
873
+ }
874
+
875
+ /// Returns the addresses of all associated parents pending and active
876
+ ///
877
+ pub fun getParentAddresses(): [Address] {
878
+ return self.parents.keys
879
+ }
880
+
881
+ /// Returns whether the given address is a parent of this account
882
+ ///
883
+ pub fun isChildOf(_ addr: Address): Bool {
884
+ return self.parents[addr] != nil
885
+ }
886
+
887
+ /// Returns nil if the given address is not a parent, false if the parent has not redeemed the child account
888
+ /// yet, and true if they have
889
+ ///
890
+ pub fun getRedeemedStatus(addr: Address): Bool? {
891
+ return self.parents[addr]
892
+ }
893
+
894
+ /// Returns associated parent addresses and their redeemed status
895
+ ///
896
+ pub fun getParentStatuses(): {Address: Bool} {
897
+ return self.parents
898
+ }
899
+
900
+ /// Unlinks all paths configured when publishing an account, and destroy's the @ChildAccount resource
901
+ /// configured for the provided parent address. Once done, the parent will not have any valid capabilities with
902
+ /// which to access the child account.
903
+ ///
904
+ pub fun removeParent(parent: Address): Bool {
905
+ if self.parents[parent] == nil {
906
+ return false
907
+ }
908
+ let identifier = HybridCustody.getChildAccountIdentifier(parent)
909
+ let capDelegatorIdentifier = HybridCustody.getCapabilityDelegatorIdentifier(parent)
910
+
911
+ let acct = self.borrowAccount()
912
+ acct.unlink(PrivatePath(identifier: identifier)!)
913
+ acct.unlink(PublicPath(identifier: identifier)!)
914
+
915
+ acct.unlink(PrivatePath(identifier: capDelegatorIdentifier)!)
916
+ acct.unlink(PublicPath(identifier: capDelegatorIdentifier)!)
917
+
918
+ destroy <- acct.load<@AnyResource>(from: StoragePath(identifier: identifier)!)
919
+ destroy <- acct.load<@AnyResource>(from: StoragePath(identifier: capDelegatorIdentifier)!)
920
+
921
+ self.parents.remove(key: parent)
922
+ emit AccountUpdated(id: self.uuid, child: self.acct.address, parent: parent, active: false)
923
+
924
+ let parentManager = getAccount(parent).getCapability<&Manager{ManagerPublic}>(HybridCustody.ManagerPublicPath)
925
+ if parentManager.check() {
926
+ parentManager.borrow()?.removeParentCallback(child: self.owner!.address)
927
+ }
928
+
929
+ return true
930
+ }
931
+
932
+ /// Returns the address of the encapsulated account
933
+ ///
934
+ pub fun getAddress(): Address {
935
+ return self.acct.address
936
+ }
937
+
938
+ /// Returns the address of the pending owner if one is assigned. Pending owners are assigned when ownership has
939
+ /// been granted, but has not yet been redeemed.
940
+ ///
941
+ pub fun getPendingOwner(): Address? {
942
+ return self.pendingOwner
943
+ }
944
+
945
+ /// Returns the address of the current owner if one is assigned. Current owners are assigned when ownership has
946
+ /// been redeemed.
947
+ ///
948
+ pub fun getOwner(): Address? {
949
+ if !self.currentlyOwned {
950
+ return nil
951
+ }
952
+ return self.acctOwner != nil ? self.acctOwner! : self.owner!.address
953
+ }
954
+
955
+ /// This method is used to transfer ownership of the child account to a new address.
956
+ /// Ownership here means that one has unrestricted access on this OwnedAccount resource, giving them full
957
+ /// access to the account.
958
+ ///
959
+ /// **NOTE:** The existence of this method does not imply that it is the only way to receive access to a
960
+ /// OwnedAccount Capability or that only the labeled 'acctOwner' has said access. Rather, this is a convenient
961
+ /// mechanism intended to easily transfer 'root' access on this account to another account and an attempt to
962
+ /// minimize access vectors.
963
+ ///
964
+ pub fun giveOwnership(to: Address) {
965
+ self.seal()
966
+
967
+ let acct = self.borrowAccount()
968
+ // Unlink existing owner's Capability if owner exists
969
+ if self.acctOwner != nil {
970
+ acct.unlink(
971
+ PrivatePath(identifier: HybridCustody.getOwnerIdentifier(self.acctOwner!))!
972
+ )
973
+ }
974
+ // Link a Capability for the new owner, retrieve & publish
975
+ let identifier = HybridCustody.getOwnerIdentifier(to)
976
+ let cap = acct.link<&{OwnedAccountPrivate, OwnedAccountPublic, MetadataViews.Resolver}>(
977
+ PrivatePath(identifier: identifier)!,
978
+ target: HybridCustody.OwnedAccountStoragePath
979
+ ) ?? panic("failed to link child account capability")
980
+
981
+ acct.inbox.publish(cap, name: identifier, recipient: to)
982
+
983
+ self.pendingOwner = to
984
+ self.currentlyOwned = true
985
+
986
+ emit OwnershipGranted(ownedAcctID: self.uuid, child: self.acct.address, previousOwner: self.getOwner(), pendingOwner: to)
987
+ }
988
+
989
+ /// Revokes all keys on the underlying account
990
+ ///
991
+ pub fun revokeAllKeys() {
992
+ let acct = self.borrowAccount()
993
+
994
+ // Revoke all keys
995
+ acct.keys.forEach(fun (key: AccountKey): Bool {
996
+ if !key.isRevoked {
997
+ acct.keys.revoke(keyIndex: key.keyIndex)
998
+ }
999
+ return true
1000
+ })
1001
+ }
1002
+
1003
+ /// Cancels all existing AuthAccount capabilities, and creates a new one. The newly created capability will
1004
+ /// then be used by the child account for accessing its AuthAccount going forward.
1005
+ ///
1006
+ /// This is used when altering ownership of an account, and can also be used as a safeguard for anyone who
1007
+ /// assumes ownership of an account to guarantee that the previous owner doesn't maintain admin access to the
1008
+ /// account via other AuthAccount Capabilities.
1009
+ ///
1010
+ pub fun rotateAuthAccount() {
1011
+ let acct = self.borrowAccount()
1012
+
1013
+ // Find all active AuthAccount capabilities so they can be removed after we make the new auth account cap
1014
+ let pathsToUnlink: [PrivatePath] = []
1015
+ acct.forEachPrivate(fun (path: PrivatePath, type: Type): Bool {
1016
+ if type.identifier == "Capability<&AuthAccount>" {
1017
+ pathsToUnlink.append(path)
1018
+ }
1019
+ return true
1020
+ })
1021
+
1022
+ // Link a new AuthAccount Capability
1023
+ // NOTE: This path cannot be sufficiently randomly generated, an app calling this function could build a
1024
+ // capability to this path before it is made, thus maintaining ownership despite making it look like they
1025
+ // gave it away. Until capability controllers, this method should not be fully trusted.
1026
+ let authAcctPath = "HybridCustodyRelinquished".concat(HybridCustody.account.address.toString()).concat(getCurrentBlock().height.toString())
1027
+ let acctCap = acct.linkAccount(PrivatePath(identifier: authAcctPath)!)!
1028
+
1029
+ self.acct = acctCap
1030
+ let newAcct = self.acct.borrow()!
1031
+
1032
+ // cleanup, remove all previously found paths. We had to do it in this order because we will be unlinking
1033
+ // the existing path which will cause a deference issue with the originally borrowed auth account
1034
+ for p in pathsToUnlink {
1035
+ newAcct.unlink(p)
1036
+ }
1037
+ }
1038
+
1039
+ /// Revokes all keys on an account, unlinks all currently active AuthAccount capabilities, then makes a new one
1040
+ /// and replaces the @OwnedAccount's underlying AuthAccount Capability with the new one to ensure that all parent
1041
+ /// accounts can still operate normally.
1042
+ /// Unless this method is executed via the giveOwnership function, this will leave an account **without** an owner.
1043
+ ///
1044
+ /// USE WITH EXTREME CAUTION.
1045
+ ///
1046
+ pub fun seal() {
1047
+ self.rotateAuthAccount()
1048
+ self.revokeAllKeys() // There needs to be a path to giving ownership that doesn't revoke keys
1049
+ emit AccountSealed(id: self.uuid, address: self.acct.address, parents: self.parents.keys)
1050
+ self.currentlyOwned = false
1051
+ }
1052
+
1053
+ /// Retrieves a reference to the ChildAccount associated with the given parent account if one exists.
1054
+ ///
1055
+ pub fun borrowChildAccount(parent: Address): &ChildAccount? {
1056
+ let identifier = HybridCustody.getChildAccountIdentifier(parent)
1057
+ return self.borrowAccount().borrow<&ChildAccount>(from: StoragePath(identifier: identifier)!)
1058
+ }
1059
+
1060
+ /// Sets the CapabilityFactory Manager for the specified parent in the associated ChildAccount.
1061
+ ///
1062
+ pub fun setCapabilityFactoryForParent(
1063
+ parent: Address,
1064
+ cap: Capability<&CapabilityFactory.Manager{CapabilityFactory.Getter}>
1065
+ ) {
1066
+ let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address")
1067
+ p.setCapabilityFactory(cap: cap)
1068
+ }
1069
+
1070
+ /// Sets the Filter for the specified parent in the associated ChildAccount.
1071
+ ///
1072
+ pub fun setCapabilityFilterForParent(parent: Address, cap: Capability<&{CapabilityFilter.Filter}>) {
1073
+ let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address")
1074
+ p.setCapabilityFilter(cap: cap)
1075
+ }
1076
+
1077
+ /// Retrieves a reference to the Delegator associated with the given parent account if one exists.
1078
+ ///
1079
+ pub fun borrowCapabilityDelegatorForParent(parent: Address): &CapabilityDelegator.Delegator? {
1080
+ let identifier = HybridCustody.getCapabilityDelegatorIdentifier(parent)
1081
+ return self.borrowAccount().borrow<&CapabilityDelegator.Delegator>(from: StoragePath(identifier: identifier)!)
1082
+ }
1083
+
1084
+ /// Adds the provided Capability to the Delegator associated with the given parent account.
1085
+ ///
1086
+ pub fun addCapabilityToDelegator(parent: Address, cap: Capability, isPublic: Bool) {
1087
+ let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address")
1088
+ let delegator = self.borrowCapabilityDelegatorForParent(parent: parent)
1089
+ ?? panic("could not borrow capability delegator resource for parent address")
1090
+ delegator.addCapability(cap: cap, isPublic: isPublic)
1091
+ }
1092
+
1093
+ /// Removes the provided Capability from the Delegator associated with the given parent account.
1094
+ ///
1095
+ pub fun removeCapabilityFromDelegator(parent: Address, cap: Capability) {
1096
+ let p = self.borrowChildAccount(parent: parent) ?? panic("could not find parent address")
1097
+ let delegator = self.borrowCapabilityDelegatorForParent(parent: parent)
1098
+ ?? panic("could not borrow capability delegator resource for parent address")
1099
+ delegator.removeCapability(cap: cap)
1100
+ }
1101
+
1102
+ pub fun getViews(): [Type] {
1103
+ return [
1104
+ Type<MetadataViews.Display>()
1105
+ ]
1106
+ }
1107
+
1108
+ pub fun resolveView(_ view: Type): AnyStruct? {
1109
+ switch view {
1110
+ case Type<MetadataViews.Display>():
1111
+ return self.display
1112
+ }
1113
+ return nil
1114
+ }
1115
+
1116
+ /// Sets this OwnedAccount's display to the one provided
1117
+ ///
1118
+ pub fun setDisplay(_ d: MetadataViews.Display) {
1119
+ self.display = d
1120
+ }
1121
+
1122
+ init(
1123
+ _ acct: Capability<&AuthAccount>
1124
+ ) {
1125
+ self.acct = acct
1126
+
1127
+ self.parents = {}
1128
+ self.pendingOwner = nil
1129
+ self.acctOwner = nil
1130
+ self.currentlyOwned = true
1131
+
1132
+ self.data = {}
1133
+ self.resources <- {}
1134
+ self.display = nil
1135
+ }
1136
+
1137
+ destroy () {
1138
+ destroy <- self.resources
1139
+ }
1140
+ }
1141
+
1142
+ /// Utility function to get the path identifier for a parent address when interacting with a ChildAccount and its
1143
+ /// parents
1144
+ ///
1145
+ pub fun getChildAccountIdentifier(_ addr: Address): String {
1146
+ return "ChildAccount_".concat(addr.toString())
1147
+ }
1148
+
1149
+ /// Utility function to get the path identifier for a parent address when interacting with a Delegator and its
1150
+ /// parents
1151
+ ///
1152
+ pub fun getCapabilityDelegatorIdentifier(_ addr: Address): String {
1153
+ return "ChildCapabilityDelegator_".concat(addr.toString())
1154
+ }
1155
+
1156
+ /// Utility function to get the path identifier for a parent address when interacting with an OwnedAccount and its
1157
+ /// owners
1158
+ ///
1159
+ pub fun getOwnerIdentifier(_ addr: Address): String {
1160
+ return "HybridCustodyOwnedAccount_".concat(HybridCustody.account.address.toString()).concat(addr.toString())
1161
+ }
1162
+
1163
+ /// Returns an OwnedAccount wrapping the provided AuthAccount Capability.
1164
+ ///
1165
+ pub fun createOwnedAccount(
1166
+ acct: Capability<&AuthAccount>
1167
+ ): @OwnedAccount {
1168
+ pre {
1169
+ acct.check(): "invalid auth account capability"
1170
+ }
1171
+
1172
+ let ownedAcct <- create OwnedAccount(acct)
1173
+ emit CreatedOwnedAccount(id: ownedAcct.uuid, child: acct.borrow()!.address)
1174
+ return <- ownedAcct
1175
+ }
1176
+
1177
+ /// Returns a new Manager with the provided Filter as default (if not nil).
1178
+ ///
1179
+ pub fun createManager(filter: Capability<&{CapabilityFilter.Filter}>?): @Manager {
1180
+ pre {
1181
+ filter == nil || filter!.check(): "Invalid CapabilityFilter Filter capability provided"
1182
+ }
1183
+ let manager <- create Manager(filter: filter)
1184
+ emit CreatedManager(id: manager.uuid)
1185
+ return <- manager
1186
+ }
1187
+
1188
+ init() {
1189
+ let identifier = "HybridCustodyChild_".concat(self.account.address.toString())
1190
+ self.OwnedAccountStoragePath = StoragePath(identifier: identifier)!
1191
+ self.OwnedAccountPrivatePath = PrivatePath(identifier: identifier)!
1192
+ self.OwnedAccountPublicPath = PublicPath(identifier: identifier)!
1193
+
1194
+ self.LinkedAccountPrivatePath = PrivatePath(identifier: "LinkedAccountPrivatePath_".concat(identifier))!
1195
+ self.BorrowableAccountPrivatePath = PrivatePath(identifier: "BorrowableAccountPrivatePath_".concat(identifier))!
1196
+
1197
+ let managerIdentifier = "HybridCustodyManager_".concat(self.account.address.toString())
1198
+ self.ManagerStoragePath = StoragePath(identifier: managerIdentifier)!
1199
+ self.ManagerPublicPath = PublicPath(identifier: managerIdentifier)!
1200
+ self.ManagerPrivatePath = PrivatePath(identifier: managerIdentifier)!
1201
+ }
1202
+ }