Nialexsan/non public pool#55
Conversation
remove logs
sisyphusSmiling
left a comment
There was a problem hiding this comment.
Biggest callout not captured here is that this setup only restricts access via our application. Without an offchain allowlist/blocklist, anyone with access to the URL can open a Tide. We'll need clarity on whether that's correct behavior or if the closed beta behavior should only enable known addresses. If the latter, we'll also need to consider where that list is stored and how that access is enforced.
| // 1) Define an entitlement only the admin can issue | ||
| access(all) entitlement Admin | ||
|
|
||
| access(all) resource interface IBeta {} |
There was a problem hiding this comment.
We should remove this interface and interact only with the concrete type
| access(all) entitlement Admin | ||
|
|
||
| access(all) resource interface IBeta {} | ||
| access(all) resource BetaBadge: IBeta {} |
There was a problem hiding this comment.
We should add the address of the user this was issued for
|
|
||
| // 2) A small in-account helper resource that performs privileged ops | ||
| access(all) resource AdminHandle { | ||
| access(Admin) fun grantBeta(addr: Address): Capability<&{TidalYieldClosedBeta.IBeta}> { |
There was a problem hiding this comment.
This should return an authorized capability on the concrete type
| let ctrl = self.account.capabilities.storage.getController(byCapabilityID: id) | ||
| ?? panic("Missing controller for recorded cap ID") | ||
| ctrl.delete() | ||
| self.issuedCapIDs.remove(key: addr) |
There was a problem hiding this comment.
We'll also want to load & destroy the BetaBadge in storage as well.
I'm also noticing that there is not retained history of an address having been revoked. Without an allowlist, this could result in a previously revoked address gaining access again. If we want to capture that information, we may want to either keep a revoked: {Address: Bool} mapping or update issuedCapIDs to include this information. Maybe something like {Address: AccessInfo} where AccessInfo is
access(all) struct AccessInfo {
access(all) let capID: UInt64
access(all) let isRevoked: Bool
}| } | ||
| /// Creates a new Tide executing the specified Strategy with the provided funds | ||
| access(all) fun createTide(strategyType: Type, withVault: @{FungibleToken.Vault}) { | ||
| access(all) fun createTide(betaRef: &{TidalYieldClosedBeta.IBeta}, strategyType: Type, withVault: @{FungibleToken.Vault}) { |
There was a problem hiding this comment.
This will need to be updated to use an authorized reference on the concrete type. We'll also want to accept the same reference type on deposit to ensure only valid beta participants can add funds to their Tide.
We may also want to require a reference for addTide, withdrawTide and createManager.
Also, since we're adding address to the beta resource, we should check that against self.owner!.address so restrict third-party access via copied Capabilities.
| let capExists = getAccount(addr).capabilities.exists(TidalYieldClosedBeta.BetaBadgePublicPath) | ||
| if !capExists { | ||
| return false | ||
| } | ||
| let betaCapID = TidalYieldClosedBeta.getBetaCapID(addr) | ||
| let existingCap = getAccount(addr).capabilities.get<&{TidalYieldClosedBeta.IBeta}>(TidalYieldClosedBeta.BetaBadgePublicPath) | ||
|
|
||
| return existingCap.id != nil && betaCapID != nil |
There was a problem hiding this comment.
| let capExists = getAccount(addr).capabilities.exists(TidalYieldClosedBeta.BetaBadgePublicPath) | |
| if !capExists { | |
| return false | |
| } | |
| let betaCapID = TidalYieldClosedBeta.getBetaCapID(addr) | |
| let existingCap = getAccount(addr).capabilities.get<&{TidalYieldClosedBeta.IBeta}>(TidalYieldClosedBeta.BetaBadgePublicPath) | |
| return existingCap.id != nil && betaCapID != nil | |
| let acct = getAuthAccount<auth(Storage) &Account>(addr) | |
| let betaCapID = TidalYieldClosedBeta.getBetaCapID(addr) | |
| let existingCap = acct.storage.borrow<Capability<auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge>>(TidalYieldClosedBeta.UserBetaCapStoragePath) | |
| return betaCapID != nil && existingCap?.id == betaCapID |
| let copyCap = user.capabilities.storage.issue<&{TidalYieldClosedBeta.IBeta}>(p) | ||
|
|
||
| let pubPath = TidalYieldClosedBeta.BetaBadgePublicPath | ||
|
|
||
| // If anything is already published there, remove it first | ||
| let existingAny = user.capabilities.exists(pubPath) | ||
| if existingAny { | ||
| user.capabilities.unpublish(pubPath) | ||
| } | ||
|
|
||
| user.capabilities.publish( | ||
| copyCap, | ||
| at: TidalYieldClosedBeta.BetaBadgePublicPath | ||
| ) |
There was a problem hiding this comment.
We shouldn't need this after the rest of the changes
| prepare( | ||
| admin: auth(Capabilities) &Account, | ||
| ) { | ||
| let adminCap: Capability<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle> = | ||
| admin.capabilities.storage.issue<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | ||
| TidalYieldClosedBeta.AdminHandleStoragePath | ||
| ) | ||
| let handle: auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle = | ||
| adminCap.borrow() ?? panic("Missing AdminHandle") | ||
|
|
||
| handle.revokeByAddress(addr: addr) |
There was a problem hiding this comment.
No need to issue a capability
| prepare( | |
| admin: auth(Capabilities) &Account, | |
| ) { | |
| let adminCap: Capability<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle> = | |
| admin.capabilities.storage.issue<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | |
| TidalYieldClosedBeta.AdminHandleStoragePath | |
| ) | |
| let handle: auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle = | |
| adminCap.borrow() ?? panic("Missing AdminHandle") | |
| handle.revokeByAddress(addr: addr) | |
| prepare(admin: auth(BorrowValue) &Account) { | |
| let handle = admin.storage.borrow<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | |
| from: TidalYieldClosedBeta.AdminHandleStoragePath | |
| )?.revokeByAddress(addr: addr) | |
| ?? panic("Missing AdminHandle") |
| let manager: &TidalYield.TideManager | ||
| let strategy: Type | ||
| let depositVault: @{FungibleToken.Vault} | ||
| let betaRef: &{TidalYieldClosedBeta.IBeta} |
There was a problem hiding this comment.
Update to use concrete type
| access(all) resource BetaBadge { | ||
| access(all) let _owner: Address | ||
| init(_ o: Address) { | ||
| self._owner = o | ||
| } | ||
| access(all) view fun getOwner(): Address { | ||
| return self._owner | ||
| } | ||
| } |
There was a problem hiding this comment.
np: preference for assignedOwner or assignedTo to avoid confusion with self.owner
| let adminCap: Capability<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle> = | ||
| admin.capabilities.storage.issue<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | ||
| TidalYieldClosedBeta.AdminHandleStoragePath | ||
| ) | ||
| admin.capabilities.storage.issue<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | ||
| TidalYieldClosedBeta.AdminHandleStoragePath | ||
| ) | ||
| let handle: auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle = | ||
| adminCap.borrow() ?? panic("Missing AdminHandle") | ||
| adminCap.borrow() ?? panic("Missing AdminHandle") |
There was a problem hiding this comment.
| let adminCap: Capability<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle> = | |
| admin.capabilities.storage.issue<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | |
| TidalYieldClosedBeta.AdminHandleStoragePath | |
| ) | |
| admin.capabilities.storage.issue<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | |
| TidalYieldClosedBeta.AdminHandleStoragePath | |
| ) | |
| let handle: auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle = | |
| adminCap.borrow() ?? panic("Missing AdminHandle") | |
| adminCap.borrow() ?? panic("Missing AdminHandle") | |
| let handle = admin.capabilities.storage.borrow<auth(TidalYieldClosedBeta.Admin) &TidalYieldClosedBeta.AdminHandle>( | |
| from: TidalYieldClosedBeta.AdminHandleStoragePath | |
| ) ?? panic("Missing AdminHandle") |
| access(all) fun createTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, strategyType: Type, withVault: @{FungibleToken.Vault}) { | ||
| let balance = withVault.balance |
There was a problem hiding this comment.
| access(all) fun createTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, strategyType: Type, withVault: @{FungibleToken.Vault}) { | |
| let balance = withVault.balance | |
| access(all) fun createTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, strategyType: Type, withVault: @{FungibleToken.Vault}) { | |
| pre { | |
| betaRef.getOwner() == self.owner?.address: | |
| "BetaBadge may only be used by its assigned owner \(betaRef.getOwner()) to create a Tide" | |
| TidalYieldClosedBeta.issuedCapIDs[betaRef.getOwner()]?.isRevoked ?? true == false: | |
| "Beta access for \(betaRef.getOwner()) has been revoked - may only withdraw from existing Tides" | |
| } | |
| let balance = withVault.balance |
| access(all) fun depositToTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, _ id: UInt64, from: @{FungibleToken.Vault}) { | ||
| pre { | ||
| self.tides[id] != nil: | ||
| "No Tide with ID \(id) found" | ||
| } |
There was a problem hiding this comment.
| access(all) fun depositToTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, _ id: UInt64, from: @{FungibleToken.Vault}) { | |
| pre { | |
| self.tides[id] != nil: | |
| "No Tide with ID \(id) found" | |
| } | |
| access(all) fun depositToTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, _ id: UInt64, from: @{FungibleToken.Vault}) { | |
| pre { | |
| self.tides[id] != nil: | |
| "No Tide with ID \(id) found" | |
| betaRef.getOwner() == self.owner?.address: | |
| "BetaBadge may only be used by its assigned owner \(betaRef.getOwner()) to deposit to a Tide" | |
| TidalYieldClosedBeta.issuedCapIDs[betaRef.getOwner()]?.isRevoked ?? true == false: | |
| "Beta access for \(betaRef.getOwner()) has been revoked - may only withdraw from existing Tides" | |
| } |
| access(all) fun addTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, _ tide: @Tide) { | ||
| pre { | ||
| self.tides[tide.uniqueID.id] == nil: | ||
| "Collision with Tide ID \(tide.uniqueID.id) - a Tide with this ID already exists" |
There was a problem hiding this comment.
| access(all) fun addTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, _ tide: @Tide) { | |
| pre { | |
| self.tides[tide.uniqueID.id] == nil: | |
| "Collision with Tide ID \(tide.uniqueID.id) - a Tide with this ID already exists" | |
| access(all) fun addTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, _ tide: @Tide) { | |
| pre { | |
| self.tides[tide.uniqueID.id] == nil: | |
| "Collision with Tide ID \(tide.uniqueID.id) - a Tide with this ID already exists" | |
| TidalYieldClosedBeta.issuedCapIDs[betaRef.getOwner()]?.isRevoked ?? true == false: | |
| "Beta access for \(betaRef.getOwner()) has been revoked - may only withdraw from existing Tides" | |
| } |
| access(all) fun createTideManager(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge): @TideManager { | ||
| return <-create TideManager() | ||
| } |
There was a problem hiding this comment.
| access(all) fun createTideManager(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge): @TideManager { | |
| return <-create TideManager() | |
| } | |
| access(all) fun createTideManager(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge): @TideManager { | |
| pre { | |
| TidalYieldClosedBeta.issuedCapIDs[betaRef.getOwner()]?.isRevoked ?? true == false: | |
| "Beta access for \(betaRef.getOwner()) has been revoked - may only withdraw from existing Tides" | |
| } | |
| return <-create TideManager() | |
| } |
| pre { | ||
| self.tides[id] != nil: | ||
| "No Tide with ID \(id) found" | ||
| } |
There was a problem hiding this comment.
From our offline convo yesterday, I think we still want users to be able to withdraw after being revoked, but we should include the same check as other method if not.
| pre { | |
| self.tides[id] != nil: | |
| "No Tide with ID \(id) found" | |
| } | |
| pre { | |
| self.tides[id] != nil: | |
| "No Tide with ID \(id) found" | |
| TidalYieldClosedBeta.issuedCapIDs[betaRef.getOwner()]?.isRevoked ?? true == false: | |
| "Beta access for \(betaRef.getOwner()) has been revoked - may only withdraw from existing Tides" | |
| } |
| if (addr == nil) { | ||
| return false | ||
| } | ||
| // Must have a live, non-revoked record for this address | ||
| let recordedID: UInt64? = self.getBetaCapID(addr!); | ||
| if recordedID == nil { | ||
| return false | ||
| } | ||
|
|
||
| if self.issuedCapIDs[addr!]?.isRevoked == true { | ||
| return false | ||
| } | ||
|
|
||
| if betaRef.getOwner() != addr { | ||
| return false | ||
| } | ||
|
|
||
| return true | ||
| } |
There was a problem hiding this comment.
I think making assertions with relevant messages would be more informative about the reason for failure, but functionally this seems to cover all the bases. I don't think it's a blocker since this is only temporarily necessary.
No description provided.