Skip to content

Nialexsan/non public pool#55

Merged
nialexsan merged 42 commits into
mainfrom
nialexsan/non-public-pool
Sep 19, 2025
Merged

Nialexsan/non public pool#55
nialexsan merged 42 commits into
mainfrom
nialexsan/non-public-pool

Conversation

@nialexsan

Copy link
Copy Markdown
Contributor

No description provided.

@nialexsan nialexsan marked this pull request as ready for review September 18, 2025 16:40
Comment thread cadence/transactions/tidal-yield/admin/grant_beta.cdc Outdated
Comment thread cadence/transactions/tidal-yield/admin/grant_beta.cdc Outdated
Comment thread cadence/transactions/tidal-yield/admin/grant_beta.cdc Outdated

@sisyphusSmiling sisyphusSmiling left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add the address of the user this was issued for

Comment thread cadence/contracts/TidalYieldClosedBeta.cdc

// 2) A small in-account helper resource that performs privileged ops
access(all) resource AdminHandle {
access(Admin) fun grantBeta(addr: Address): Capability<&{TidalYieldClosedBeta.IBeta}> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
}

Comment thread cadence/contracts/TidalYield.cdc Outdated
}
/// 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}) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +4 to +11
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines +40 to +53
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
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need this after the rest of the changes

Comment on lines +7 to +17
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to issue a capability

Suggested change
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}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to use concrete type

@sisyphusSmiling sisyphusSmiling self-requested a review September 19, 2025 18:03
Comment on lines +6 to +14
access(all) resource BetaBadge {
access(all) let _owner: Address
init(_ o: Address) {
self._owner = o
}
access(all) view fun getOwner(): Address {
return self._owner
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np: preference for assignedOwner or assignedTo to avoid confusion with self.owner

Comment on lines +11 to +16
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")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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")

Comment on lines +322 to 323
access(all) fun createTide(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge, strategyType: Type, withVault: @{FungibleToken.Vault}) {
let balance = withVault.balance

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines +349 to 353
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"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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"
}

Comment on lines +340 to 343
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"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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"
}

Comment on lines +417 to 419
access(all) fun createTideManager(betaRef: auth(TidalYieldClosedBeta.Beta) &TidalYieldClosedBeta.BetaBadge): @TideManager {
return <-create TideManager()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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()
}

Comment on lines +366 to +369
pre {
self.tides[id] != nil:
"No Tide with ID \(id) found"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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"
}

Comment on lines +107 to +125
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
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@nialexsan nialexsan merged commit 16af405 into main Sep 19, 2025
4 checks passed
@nialexsan nialexsan deleted the nialexsan/non-public-pool branch September 19, 2025 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants