-
Notifications
You must be signed in to change notification settings - Fork 0
YieldVaults EarlyAccess #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
f9eaf9b
42d8b68
63dc888
dd7d1ac
7d716e9
2b7ac28
8c9de31
4869dd6
66d8b5b
216084b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,186 @@ | ||||||
| import "FlowYieldVaultsInterfaces" | ||||||
|
|
||||||
| /// Gates yield vault creation during the early access period. | ||||||
| /// An `Admin` resource issues and manages `EarlyAccessPass` resources. | ||||||
| /// Pass holders call `createYieldVault` to create yield vaults | ||||||
| /// until their allowance is exhausted. | ||||||
| access(all) contract FlowYieldVaultsEarlyAccess { | ||||||
|
|
||||||
| /// Emitted when a new pass is issued to an address. | ||||||
| access(all) event PassIssued(passUUID: UInt64, addr: Address, allowance: UInt64) | ||||||
| /// Emitted when a pass is revoked and destroyed by the admin. | ||||||
| access(all) event PassRevoked(passUUID: UInt64) | ||||||
| /// Emitted when a pass is used to create a yield vault. | ||||||
| access(all) event PassUsed(passUUID: UInt64, remainingAllowance: UInt64) | ||||||
|
|
||||||
| /// Contract name on this account implementing `FlowYieldVaultsInterfaces`. | ||||||
| access(all) var flowYieldVaultsName: String | ||||||
| /// Storage path where the `Admin` resource is saved. | ||||||
| access(all) let adminStoragePath: StoragePath | ||||||
| /// Storage path where pass capabilities are stored for claiming. | ||||||
| access(all) let passCapabilityStoragePath: StoragePath | ||||||
| /// Tracks the UUID of the last pass issued to each address; | ||||||
| /// used by the address-based claim transaction. | ||||||
| access(all) var mostRecentIssuedPassUUID: {Address: UInt64} | ||||||
|
holyfuchs marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| /// Held in the holder's storage; gates yield vault creation during early access. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| access(all) resource EarlyAccessPass { | ||||||
| /// Number of yield vaults the holder may still create. | ||||||
| access(all) var remainingAllowance: UInt64 | ||||||
|
|
||||||
| /// Consumes one unit of allowance and creates a new yield vault. | ||||||
| /// Panics if allowance is exhausted. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `strategyID`: Identifies the vault strategy to create. | ||||||
| /// | ||||||
| /// **Returns** A new `YieldVault` to be saved in the caller's storage. | ||||||
| access(all) fun createYieldVault(strategyID: UInt64): @{FlowYieldVaultsInterfaces.YieldVault} { | ||||||
| pre { self.remainingAllowance > 0: "No remaining allowance" } | ||||||
| self.remainingAllowance = self.remainingAllowance - 1 | ||||||
| let fyv = FlowYieldVaultsEarlyAccess.getFlowYieldVaultsContract() | ||||||
| let vault <- fyv.createYieldVault(strategyID: strategyID) | ||||||
| emit PassUsed(passUUID: self.uuid, remainingAllowance: self.remainingAllowance) | ||||||
| return <- vault | ||||||
| } | ||||||
|
|
||||||
| access(contract) fun setAllowance(_ newAllowance: UInt64) { | ||||||
| self.remainingAllowance = newAllowance | ||||||
| } | ||||||
|
|
||||||
| init(allowance: UInt64) { | ||||||
| self.remainingAllowance = allowance | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| access(all) resource Admin { | ||||||
| /// Issues a pass to `addr`, publishes the capability to their inbox, | ||||||
| /// and records it as their most recently issued pass | ||||||
| /// (used by the address-based claim transaction). | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `addr`: Recipient who will claim the pass from their inbox. | ||||||
| /// - `allowance`: Number of yield vaults the pass holder may create. | ||||||
| /// | ||||||
| /// **Returns** The UUID of the newly created pass. | ||||||
| access(all) fun issuePass(to addr: Address, allowance: UInt64): UInt64 { | ||||||
| let pass <- create EarlyAccessPass(allowance: allowance) | ||||||
| let passUUID = FlowYieldVaultsEarlyAccess.storePass(pass: <- pass) | ||||||
| FlowYieldVaultsEarlyAccess.publishPassCapability(passUUID: passUUID, addr: addr) | ||||||
| FlowYieldVaultsEarlyAccess.mostRecentIssuedPassUUID[addr] = passUUID | ||||||
| emit PassIssued(passUUID: passUUID, addr: addr, allowance: allowance) | ||||||
| return passUUID | ||||||
| } | ||||||
|
|
||||||
| /// Destroys the pass and attempts to retract the inbox capability. | ||||||
| /// Panics if the pass is not found. If already claimed, the capability | ||||||
| /// stays in the recipient's storage but becomes unborrow-able, | ||||||
| /// blocking future vault creation. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `passUUID`: UUID of the target pass. | ||||||
| access(all) fun revokePass(passUUID: UInt64) { | ||||||
| let pass <- FlowYieldVaultsEarlyAccess.loadPass(passUUID: passUUID) | ||||||
| destroy pass | ||||||
| FlowYieldVaultsEarlyAccess.unpublishPassCapability(passUUID: passUUID) | ||||||
| emit PassRevoked(passUUID: passUUID) | ||||||
| } | ||||||
|
|
||||||
| /// Replaces the remaining allowance on an existing pass. | ||||||
| /// Panics if the pass is not found. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `passUUID`: UUID of the target pass. | ||||||
| /// - `newAllowance`: New vault budget; `0` immediately blocks creation. | ||||||
| access(all) fun setAllowance(passUUID: UInt64, newAllowance: UInt64) { | ||||||
| let pass = FlowYieldVaultsEarlyAccess.borrowPass(passUUID: passUUID) | ||||||
| pass.setAllowance(newAllowance) | ||||||
| } | ||||||
|
|
||||||
| /// Sets the contract name used to resolve the yield vaults | ||||||
| /// implementation. Must be called before any vault is created. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `flowYieldVaultsName`: Name of a contract on this account | ||||||
| /// that conforms to `FlowYieldVaultsInterfaces`. | ||||||
| access(all) fun setFlowYieldVaults(flowYieldVaultsName: String) { | ||||||
| FlowYieldVaultsEarlyAccess.flowYieldVaultsName = flowYieldVaultsName | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Returns whether a pass with the given UUID currently exists in storage. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `passUUID`: UUID of the target pass. | ||||||
| /// | ||||||
| /// **Returns** `true` if the pass exists, `false` otherwise. | ||||||
| view access(all) fun passExists(passUUID: UInt64): Bool { | ||||||
| return self.checkPass(passUUID: passUUID) | ||||||
| } | ||||||
|
Comment on lines
+104
to
+112
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function seems redundant. However, |
||||||
|
|
||||||
| /// Returns the remaining allowance of the pass with the given UUID. | ||||||
| /// Panics if the pass is not found. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `passUUID`: UUID of the target pass. | ||||||
| /// | ||||||
| /// **Returns** Number of yield vaults the pass holder may still create. | ||||||
| view access(all) fun remainingAllowance(passUUID: UInt64): UInt64 { | ||||||
| let pass = self.borrowPass(passUUID: passUUID) | ||||||
| return pass.remainingAllowance | ||||||
| } | ||||||
|
|
||||||
| /// Returns the inbox key used to publish and claim a pass capability. | ||||||
| /// | ||||||
| /// **Parameters** | ||||||
| /// - `passUUID`: UUID of the target pass. | ||||||
| /// | ||||||
| /// **Returns** The inbox key string for the given pass. | ||||||
| view access(all) fun inboxName(passUUID: UInt64): String { | ||||||
| return "EarlyAccessPass_\(passUUID)" | ||||||
| } | ||||||
|
|
||||||
| view access(self) fun getFlowYieldVaultsContract(): &{FlowYieldVaultsInterfaces} { | ||||||
| return self.account.contracts.borrow<&{FlowYieldVaultsInterfaces}>(name: self.flowYieldVaultsName) | ||||||
| ?? panic("FlowYieldVaults contract '\(self.flowYieldVaultsName)' not found on this account") | ||||||
| } | ||||||
|
holyfuchs marked this conversation as resolved.
|
||||||
|
|
||||||
| access(self) fun storePass(pass: @EarlyAccessPass): UInt64 { | ||||||
| let uuid = pass.uuid | ||||||
| self.account.storage.save(<- pass, to: FlowYieldVaultsEarlyAccess.passStoragePath(passUUID: uuid)) | ||||||
| return uuid | ||||||
| } | ||||||
|
|
||||||
| access(self) fun loadPass(passUUID: UInt64): @EarlyAccessPass { | ||||||
| return <- (self.account.storage.load<@EarlyAccessPass>(from: self.passStoragePath(passUUID: passUUID)) ?? panic("Pass not found")) | ||||||
| } | ||||||
|
|
||||||
| view access(self) fun checkPass(passUUID: UInt64): Bool { | ||||||
| return self.account.storage.check<@EarlyAccessPass>(from: self.passStoragePath(passUUID: passUUID)) | ||||||
| } | ||||||
|
|
||||||
| view access(self) fun borrowPass(passUUID: UInt64): &EarlyAccessPass { | ||||||
| return self.account.storage.borrow<&EarlyAccessPass>(from: self.passStoragePath(passUUID: passUUID)) ?? panic("Pass not found") | ||||||
| } | ||||||
|
|
||||||
| access(self) fun publishPassCapability(passUUID: UInt64, addr: Address) { | ||||||
| let capability = self.account.capabilities.storage.issue<&EarlyAccessPass>(self.passStoragePath(passUUID: passUUID)) | ||||||
| self.account.inbox.publish(capability, name: self.inboxName(passUUID: passUUID), recipient: addr) | ||||||
| } | ||||||
|
|
||||||
| access(self) fun unpublishPassCapability(passUUID: UInt64) { | ||||||
| let _ = self.account.inbox.unpublish<&EarlyAccessPass>(self.inboxName(passUUID: passUUID)) | ||||||
| } | ||||||
|
|
||||||
| view access(self) fun passStoragePath(passUUID: UInt64): StoragePath { | ||||||
| return StoragePath(identifier: "FlowYieldVaultsEarlyAccessPass_\(passUUID)")! | ||||||
| } | ||||||
|
|
||||||
| init() { | ||||||
| self.flowYieldVaultsName = "" | ||||||
| self.adminStoragePath = StoragePath(identifier: "FlowYieldVaultsEarlyAccessAdmin")! | ||||||
| self.passCapabilityStoragePath = StoragePath(identifier: "FlowYieldVaultsEarlyAccessPassCapability")! | ||||||
| self.mostRecentIssuedPassUUID = {} | ||||||
| self.account.storage.save(<- create Admin(), to: self.adminStoragePath) | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import "FungibleToken" | ||
|
|
||
| access(all) contract interface FlowYieldVaultsInterfaces { | ||
|
holyfuchs marked this conversation as resolved.
Outdated
|
||
|
|
||
| access(all) struct interface Strategy { | ||
| access(all) fun createYieldVault(strategyID: UInt64): @{YieldVault} | ||
| } | ||
|
|
||
| access(all) resource interface YieldVault: FungibleToken.Provider, FungibleToken.Receiver {} | ||
|
holyfuchs marked this conversation as resolved.
Outdated
|
||
|
|
||
| access(account) fun createYieldVault(strategyID: UInt64): @{YieldVault} | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import "FlowYieldVaultsEarlyAccess" | ||
|
|
||
| access(all) fun main(passUUID: UInt64): Bool { | ||
| return FlowYieldVaultsEarlyAccess.passExists(passUUID: passUUID) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import "FlowYieldVaultsEarlyAccess" | ||
|
|
||
| access(all) fun main(passUUID: UInt64): UInt64 { | ||
| return FlowYieldVaultsEarlyAccess.remainingAllowance(passUUID: passUUID) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.