@flowtyio/flow-contracts 0.0.7 → 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.
- package/README.md +13 -1
- package/add.js +2 -2
- package/contracts/hybrid-custody/CapabilityDelegator.cdc +174 -0
- package/contracts/hybrid-custody/CapabilityFactory.cdc +105 -0
- package/contracts/hybrid-custody/CapabilityFilter.cdc +199 -0
- package/contracts/hybrid-custody/HybridCustody.cdc +1202 -0
- package/contracts/hybrid-custody/factories/FTAllFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +10 -0
- package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +10 -0
- package/find.js +26 -0
- package/flow.json +77 -0
- package/index.js +0 -2
- package/package.json +2 -2
- package/utils.js +2 -1
|
@@ -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
|
+
}
|