Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cadence/contracts/FlowYieldVaultsClosedBeta.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ access(all) contract FlowYieldVaultsClosedBeta {
return cap
}

/// Clean up any previously issued controller before issuing a fresh capability.
access(contract) fun _cleanupExistingGrant(_ addr: Address) {
if let info = self.issuedCapIDs[addr] {
if let ctrl = self.account.capabilities.storage.getController(byCapabilityID: info.capID) {
ctrl.delete()
emit BetaRevoked(addr: addr, capID: info.capID)
}
}
}

/// Delete the recorded controller, revoking *all copies* of the capability
access(contract) fun _revokeByAddress(_ addr: Address) {
let info = self.issuedCapIDs[addr] ?? panic("No cap recorded for address")
Expand All @@ -83,6 +93,7 @@ access(all) contract FlowYieldVaultsClosedBeta {
// 2) A small in-account helper resource that performs privileged ops
access(all) resource AdminHandle {
access(Admin) fun grantBeta(addr: Address): Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge> {
FlowYieldVaultsClosedBeta._cleanupExistingGrant(addr)
FlowYieldVaultsClosedBeta._ensureBadge(addr)
return FlowYieldVaultsClosedBeta._issueBadgeCap(addr)
}
Expand Down
53 changes: 53 additions & 0 deletions cadence/tests/grant_beta_cleanup_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Test

import "test_helpers.cdc"

import "FlowYieldVaultsClosedBeta"

access(all) let flowYieldVaultsAccount = Test.getAccount(0x0000000000000009)

access(all)
fun setup() {
deployContracts()
}

access(all)
fun test_ReGrantBetaRevokesPreviousCapability() {
let user = Test.createAccount()
transferFlow(signer: serviceAccount, recipient: user.address, amount: 1.0)

grantBeta(flowYieldVaultsAccount, user)

let backupRes = _executeTransaction("../transactions/test/backup_beta_cap.cdc", [], user)
Test.expect(backupRes, Test.beSucceeded())

// Re-granting should revoke the previously issued controller (and thus all old capability copies).
grantBeta(flowYieldVaultsAccount, user)

// Event assertions: the re-grant should emit BetaRevoked for the *previous* capID, then a fresh BetaGranted.
let grantedAny = Test.eventsOfType(Type<FlowYieldVaultsClosedBeta.BetaGranted>())
var userGrants: [FlowYieldVaultsClosedBeta.BetaGranted] = []
for evt in grantedAny {
let g = evt as! FlowYieldVaultsClosedBeta.BetaGranted
if g.addr == user.address {
userGrants.append(g)
}
}
Test.assertEqual(2, userGrants.length)

let revokedAny = Test.eventsOfType(Type<FlowYieldVaultsClosedBeta.BetaRevoked>())
var userRevokes: [FlowYieldVaultsClosedBeta.BetaRevoked] = []
for evt in revokedAny {
let r = evt as! FlowYieldVaultsClosedBeta.BetaRevoked
if r.addr == user.address {
userRevokes.append(r)
}
}
Test.assertEqual(1, userRevokes.length)
Test.assert(userRevokes[0].capID != nil, message: "Expected revoke capID to be non-nil")
Test.assertEqual(userGrants[0].capID, userRevokes[0].capID!)
Test.assert(userGrants[0].capID != userGrants[1].capID, message: "Expected a fresh capID on re-grant")

let assertRes = _executeTransaction("../transactions/test/assert_backup_beta_cap_revoked.cdc", [], user)
Test.expect(assertRes, Test.beSucceeded())
}
16 changes: 16 additions & 0 deletions cadence/transactions/test/assert_backup_beta_cap_revoked.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "FlowYieldVaultsClosedBeta"

/// Asserts that the beta capability stored at the backup path has been revoked.
transaction {
prepare(signer: auth(Storage, Capabilities) &Account) {
let backupPath = StoragePath(identifier: "FlowYieldVaultsBetaCapBackup")!

let cap = signer.storage.load<
Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>
>(from: backupPath)
?? panic("Missing beta capability at backup path \(backupPath)")

assert(!cap.check(), message: "Expected backup beta capability to be revoked")
}
}

28 changes: 28 additions & 0 deletions cadence/transactions/test/backup_beta_cap.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import "FlowYieldVaultsClosedBeta"

/// Moves the current beta capability from the canonical path to a backup path.
/// Intended for tests that need to retain an "old" beta capability across re-grants.
transaction {
prepare(signer: auth(Storage, Capabilities) &Account) {
let sourcePath = FlowYieldVaultsClosedBeta.UserBetaCapStoragePath
let backupPath = StoragePath(identifier: "FlowYieldVaultsBetaCapBackup")!

let cap = signer.storage.load<
Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>
>(from: sourcePath)
?? panic("Missing beta capability at \(sourcePath)")

if let t = signer.storage.type(at: backupPath) {
if t == Type<Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>>() {
let _ = signer.storage.load<
Capability<auth(FlowYieldVaultsClosedBeta.Beta) &FlowYieldVaultsClosedBeta.BetaBadge>
>(from: backupPath)
} else {
panic("Unexpected type at backup path: ".concat(t.identifier))
}
}

signer.storage.save(cap, to: backupPath)
}
}