Skip to content

Commit 8ce8e16

Browse files
feat: Restore AlpenFlow implementation with supporting contracts (#8)
* feat: Restore AlpenFlow implementation with supporting contracts - TidalProtocol: 100% restoration of Dieter's AlpenFlow functionality - Oracle-based pricing and health calculations - Deposit rate limiting and position queues - Advanced health management functions - Async position updates - MOET: Mock stablecoin for multi-token testing - TidalPoolGovernance: Role-based governance system - AlpenFlow_dete_original: Reference implementation Note: Old tests removed as they're incompatible with new contracts. Updated tests coming in follow-up PR. No breaking changes. Foundation for multi-token lending protocol. * fix: Add MOET and TidalPoolGovernance to flow.json for deployment * remove DeFiBlocks interface definitions from TidalProtocol contract * update InternalPosition.topUpSource to value field from reference * fix PriceOracle conformance .price() calls * Update cadence/contracts/TidalProtocol.cdc * Assign TokenState.lastUpdate as current block timestamp on init * update error message * remove TidalGovernance contract --------- Co-authored-by: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com>
1 parent c96c25a commit 8ce8e16

16 files changed

Lines changed: 2785 additions & 2444 deletions

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,6 @@ This project is licensed under the MIT License.
209209
- [FungibleToken Standard](https://github.com/onflow/flow-ft)
210210
- [DeFi Blocks](https://github.com/onflow/defi-blocks)
211211
- [Flow Discord](https://discord.gg/flow)
212+
213+
## Note
214+
Tests are being updated for the new contract implementation and will be added in the next PR.

cadence/contracts/AlpenFlow_dete_original.cdc

Lines changed: 1451 additions & 0 deletions
Large diffs are not rendered by default.

cadence/contracts/MOET.cdc

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import "FungibleToken"
2+
import "MetadataViews"
3+
import "FungibleTokenMetadataViews"
4+
5+
///
6+
/// THIS CONTRACT IS A MOCK AND IS NOT INTENDED FOR USE IN PRODUCTION
7+
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
8+
///
9+
access(all) contract MOET : FungibleToken {
10+
11+
/// Total supply of MOET in existence
12+
access(all) var totalSupply: UFix64
13+
14+
/// Storage and Public Paths
15+
access(all) let VaultStoragePath: StoragePath
16+
access(all) let VaultPublicPath: PublicPath
17+
access(all) let ReceiverPublicPath: PublicPath
18+
access(all) let AdminStoragePath: StoragePath
19+
20+
/// The event that is emitted when new tokens are minted
21+
access(all) event Minted(type: String, amount: UFix64, toUUID: UInt64, minterUUID: UInt64)
22+
/// Emitted whenever a new Minter is created
23+
access(all) event MinterCreated(uuid: UInt64)
24+
25+
/// createEmptyVault
26+
///
27+
/// Function that creates a new Vault with a balance of zero
28+
/// and returns it to the calling context. A user must call this function
29+
/// and store the returned Vault in their storage in order to allow their
30+
/// account to be able to receive deposits of this token type.
31+
///
32+
access(all) fun createEmptyVault(vaultType: Type): @MOET.Vault {
33+
return <- create Vault(balance: 0.0)
34+
}
35+
36+
access(all) view fun getContractViews(resourceType: Type?): [Type] {
37+
return [
38+
Type<FungibleTokenMetadataViews.FTView>(),
39+
Type<FungibleTokenMetadataViews.FTDisplay>(),
40+
Type<FungibleTokenMetadataViews.FTVaultData>(),
41+
Type<FungibleTokenMetadataViews.TotalSupply>()
42+
]
43+
}
44+
45+
access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
46+
switch viewType {
47+
case Type<FungibleTokenMetadataViews.FTView>():
48+
return FungibleTokenMetadataViews.FTView(
49+
ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
50+
ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
51+
)
52+
case Type<FungibleTokenMetadataViews.FTDisplay>():
53+
let media = MetadataViews.Media(
54+
file: MetadataViews.HTTPFile(
55+
url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
56+
),
57+
mediaType: "image/svg+xml"
58+
)
59+
let medias = MetadataViews.Medias([media])
60+
return FungibleTokenMetadataViews.FTDisplay(
61+
name: "TidalProtocol USD",
62+
symbol: "MOET",
63+
description: "A mocked version of TidalProtocol stablecoin",
64+
externalURL: MetadataViews.ExternalURL("https://flow.com"),
65+
logos: medias,
66+
socials: {
67+
"twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
68+
}
69+
)
70+
case Type<FungibleTokenMetadataViews.FTVaultData>():
71+
return FungibleTokenMetadataViews.FTVaultData(
72+
storagePath: self.VaultStoragePath,
73+
receiverPath: self.ReceiverPublicPath,
74+
metadataPath: self.VaultPublicPath,
75+
receiverLinkedType: Type<&MOET.Vault>(),
76+
metadataLinkedType: Type<&MOET.Vault>(),
77+
createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
78+
return <-MOET.createEmptyVault(vaultType: Type<@MOET.Vault>())
79+
})
80+
)
81+
case Type<FungibleTokenMetadataViews.TotalSupply>():
82+
return FungibleTokenMetadataViews.TotalSupply(
83+
totalSupply: MOET.totalSupply
84+
)
85+
}
86+
return nil
87+
}
88+
89+
/* --- CONSTRUCTS --- */
90+
91+
/// Vault
92+
///
93+
/// Each user stores an instance of only the Vault in their storage
94+
/// The functions in the Vault and governed by the pre and post conditions
95+
/// in FungibleToken when they are called.
96+
/// The checks happen at runtime whenever a function is called.
97+
///
98+
/// Resources can only be created in the context of the contract that they
99+
/// are defined in, so there is no way for a malicious user to create Vaults
100+
/// out of thin air. A special Minter resource needs to be defined to mint
101+
/// new tokens.
102+
///
103+
access(all) resource Vault: FungibleToken.Vault {
104+
105+
/// The total balance of this vault
106+
access(all) var balance: UFix64
107+
108+
/// Identifies the destruction of a Vault even when destroyed outside of Buner.burn() scope
109+
access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid, balance: UFix64 = self.balance)
110+
111+
init(balance: UFix64) {
112+
self.balance = balance
113+
}
114+
115+
/// Called when a fungible token is burned via the `Burner.burn()` method
116+
access(contract) fun burnCallback() {
117+
if self.balance > 0.0 {
118+
MOET.totalSupply = MOET.totalSupply - self.balance
119+
}
120+
self.balance = 0.0
121+
}
122+
123+
access(all) view fun getViews(): [Type] {
124+
return MOET.getContractViews(resourceType: nil)
125+
}
126+
127+
access(all) fun resolveView(_ view: Type): AnyStruct? {
128+
return MOET.resolveContractView(resourceType: nil, viewType: view)
129+
}
130+
131+
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
132+
let supportedTypes: {Type: Bool} = {}
133+
supportedTypes[self.getType()] = true
134+
return supportedTypes
135+
}
136+
137+
access(all) view fun isSupportedVaultType(type: Type): Bool {
138+
return self.getSupportedVaultTypes()[type] ?? false
139+
}
140+
141+
access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
142+
return amount <= self.balance
143+
}
144+
145+
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @MOET.Vault {
146+
self.balance = self.balance - amount
147+
return <-create Vault(balance: amount)
148+
}
149+
150+
access(all) fun deposit(from: @{FungibleToken.Vault}) {
151+
let vault <- from as! @MOET.Vault
152+
self.balance = self.balance + vault.balance
153+
destroy vault
154+
}
155+
156+
access(all) fun createEmptyVault(): @MOET.Vault {
157+
return <-create Vault(balance: 0.0)
158+
}
159+
}
160+
161+
/// Minter
162+
///
163+
/// Resource object that token admin accounts can hold to mint new tokens.
164+
///
165+
access(all) resource Minter {
166+
/// Identifies when a Minter is destroyed, coupling with MinterCreated event to trace Minter UUIDs
167+
access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid)
168+
169+
init() {
170+
emit MinterCreated(uuid: self.uuid)
171+
}
172+
173+
/// mintTokens
174+
///
175+
/// Function that mints new tokens, adds them to the total supply,
176+
/// and returns them to the calling context.
177+
///
178+
access(all) fun mintTokens(amount: UFix64): @MOET.Vault {
179+
MOET.totalSupply = MOET.totalSupply + amount
180+
let vault <-create Vault(balance: amount)
181+
emit Minted(type: vault.getType().identifier, amount: amount, toUUID: vault.uuid, minterUUID: self.uuid)
182+
return <-vault
183+
}
184+
}
185+
186+
init(initialMint: UFix64) {
187+
188+
self.totalSupply = 0.0
189+
190+
let address = self.account.address
191+
self.VaultStoragePath = StoragePath(identifier: "moetTokenVault_\(address)")!
192+
self.VaultPublicPath = PublicPath(identifier: "moetTokenVault_\(address)")!
193+
self.ReceiverPublicPath = PublicPath(identifier: "moetTokenReceiver_\(address)")!
194+
self.AdminStoragePath = StoragePath(identifier: "moetTokenAdmin_\(address)")!
195+
196+
197+
// Create a public capability to the stored Vault that exposes
198+
// the `deposit` method and getAcceptedTypes method through the `Receiver` interface
199+
// and the `balance` method through the `Balance` interface
200+
//
201+
self.account.storage.save(<-create Vault(balance: self.totalSupply), to: self.VaultStoragePath)
202+
let vaultCap = self.account.capabilities.storage.issue<&MOET.Vault>(self.VaultStoragePath)
203+
self.account.capabilities.publish(vaultCap, at: self.VaultPublicPath)
204+
let receiverCap = self.account.capabilities.storage.issue<&MOET.Vault>(self.VaultStoragePath)
205+
self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
206+
207+
// Create a Minter & mint the initial supply of tokens to the contract account's Vault
208+
let admin <- create Minter()
209+
210+
self.account.capabilities.borrow<&Vault>(self.ReceiverPublicPath)!.deposit(
211+
from: <- admin.mintTokens(amount: initialMint)
212+
)
213+
214+
self.account.storage.save(<-admin, to: self.AdminStoragePath)
215+
}
216+
}

0 commit comments

Comments
 (0)