Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bf9fb2f
feat: Restore AlpenFlow implementation with supporting contracts
kgrgpg Jun 4, 2025
f30f481
fix: Add MOET and TidalPoolGovernance to flow.json for deployment
kgrgpg Jun 4, 2025
50b93e3
remove DeFiBlocks interface definitions from TidalProtocol contract
sisyphusSmiling Jun 5, 2025
2c59dc3
update InternalPosition.topUpSource to value field from reference
sisyphusSmiling Jun 5, 2025
86c8507
fix PriceOracle conformance .price() calls
sisyphusSmiling Jun 5, 2025
e5ba670
Update cadence/contracts/TidalProtocol.cdc
sisyphusSmiling Jun 5, 2025
9908038
Assign TokenState.lastUpdate as current block timestamp on init
sisyphusSmiling Jun 5, 2025
0d6e43d
update error message
sisyphusSmiling Jun 5, 2025
6f0594e
remove TidalGovernance contract
sisyphusSmiling Jun 5, 2025
aa4e4f5
add select contract, tranasction, script, and test changes from #6
sisyphusSmiling Jun 6, 2025
9e475d0
update DeFiBlocks submodule to latest main
sisyphusSmiling Jun 6, 2025
d49bcc9
add new DFB dependencies to flow.json
sisyphusSmiling Jun 6, 2025
e9a5ec6
fix PositionSink/Source conflict with IdentifiableStruct.id()
sisyphusSmiling Jun 6, 2025
5880b82
update PositionSource fields from access(all) to access(self)
sisyphusSmiling Jun 6, 2025
49a8f5c
add check on FungibleToken defining contract conformance when token a…
sisyphusSmiling Jun 6, 2025
430bb5b
remove unused methods from test_helpers.cdc
sisyphusSmiling Jun 6, 2025
eb474f7
remove redundant TidalProtocol position sink/source definitions
sisyphusSmiling Jun 6, 2025
5626a48
remove comment notes
sisyphusSmiling Jun 6, 2025
ea9202f
fix insufficient funds on rebalance error by minting MOET instead of …
sisyphusSmiling Jun 6, 2025
12b8e50
remove contract debug logs
sisyphusSmiling Jun 6, 2025
214a6bd
add behavioral test cases for position creation & under/overcollatera…
sisyphusSmiling Jun 6, 2025
2c17878
remove unused methods, structs & implement TODOs
sisyphusSmiling Jun 6, 2025
3404c1a
protects against underflow on TokenState.updateCredit/DebitBalance
sisyphusSmiling Jun 6, 2025
3c61191
restrict access on Position's privileged methods
sisyphusSmiling Jun 6, 2025
a71be40
update minor contract formatting
sisyphusSmiling Jun 7, 2025
22bb4d4
update DeFiBlocks with latest changes merged to main
sisyphusSmiling Jun 8, 2025
87d7d21
update DeFiBlocks to latest
sisyphusSmiling Jun 12, 2025
953f8f3
Merge branch 'tracer-bullet' into gio/refactor-pool-creation-updated
sisyphusSmiling Jun 13, 2025
c44f9a1
fix MOET deposit logic
sisyphusSmiling Jun 16, 2025
9257899
fix import syntax
sisyphusSmiling Jun 17, 2025
f9e1bfa
add initial protocol events (#13)
sisyphusSmiling Jun 20, 2025
9030c5f
update position rebalance transaction comments
sisyphusSmiling Jun 20, 2025
f1e8e55
fix fundsAvailableAboveTargetHealthAfterDepositing when effectiveDebt…
sisyphusSmiling Jun 20, 2025
9d788ec
Update contract comments & reorganize for readability (#12)
sisyphusSmiling Jun 21, 2025
7dc24b4
Update .gitmodules
nialexsan Jun 24, 2025
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
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "DeFiBlocks"]
path = DeFiBlocks
url = https://github.com/onflow/DeFiBlocks
url = git@github.com:onflow/DeFiBlocks.git
2 changes: 1 addition & 1 deletion DeFiBlocks
5 changes: 4 additions & 1 deletion cadence/contracts/MOET.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,11 @@ access(all) contract MOET : FungibleToken {

access(all) fun deposit(from: @{FungibleToken.Vault}) {
let vault <- from as! @MOET.Vault
self.balance = self.balance + vault.balance
let amount = vault.balance
vault.balance = 0.0
destroy vault

self.balance = self.balance + amount
}

access(all) fun createEmptyVault(): @MOET.Vault {
Expand Down
2,492 changes: 1,254 additions & 1,238 deletions cadence/contracts/TidalProtocol.cdc

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions cadence/contracts/mocks/MockOracle.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import "FungibleToken"

import "DFB"

///
/// THIS CONTRACT IS A MOCK AND IS NOT INTENDED FOR USE IN PRODUCTION
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
///
access(all) contract MockOracle {

/// token price denominated in USD
access(self) let mockedPrices: {Type: UFix64}
/// the token type in which prices are denominated
access(self) let unitOfAccount: Type
/// bps up or down by which current price moves when bumpPrice is called
access(self) let bumpVariance: UInt16

access(all) struct PriceOracle : DFB.PriceOracle {

/// Returns the asset type serving as the price basis - e.g. USD in FLOW/USD
access(all) view fun unitOfAccount(): Type {
return MockOracle.unitOfAccount
}

/// Returns the latest price data for a given asset denominated in unitOfAccount()
access(all) fun price(ofToken: Type): UFix64? {
if ofToken == self.unitOfAccount() {
return 1.0
}
return MockOracle.mockedPrices[ofToken]
}
}

// resets the price of the token within 0-bumpVariance (bps) of the current price
// allows for mocked data to have variability
access(all) fun bumpPrice(forToken: Type) {
if forToken == self.unitOfAccount {
return
}
let current = self.mockedPrices[forToken]
?? panic("MockOracle does not have a price set for token \(forToken.identifier)")
let sign = revertibleRandom<UInt8>(modulo: 2) // 0 - down | 1 - up
let variance = self.convertToBPS(revertibleRandom<UInt16>(modulo: self.bumpVariance)) // bps up or down
if sign == 0 {
self.mockedPrices[forToken] = current - (current * variance)
} else {
self.mockedPrices[forToken] = current + (current * variance)
}
}

access(all) fun setPrice(forToken: Type, price: UFix64) {
self.mockedPrices[forToken] = price
}

access(self) view fun convertToBPS(_ variance: UInt16): UFix64 {
var res = UFix64(variance)
for i in InclusiveRange(0, 3) {
res = res / 10.0
}
return res
}

init(unitOfAccountIdentifier: String) {
self.mockedPrices = {}
// e.g. vault.getType().identifier == 'A.0ae53cb6e3f42a79.FlowToken.Vault'
self.unitOfAccount = CompositeType(unitOfAccountIdentifier) ?? panic("Invalid unitOfAccountIdentifier \(unitOfAccountIdentifier)")
self.bumpVariance = 100 // 0.1% variance
}
}
56 changes: 56 additions & 0 deletions cadence/contracts/mocks/MockTidalProtocolConsumer.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import "FungibleToken"

import "DFB"
import "TidalProtocol"

/// THIS CONTRACT IS NOT SAFE FOR PRODUCTION - FOR TEST USE ONLY
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
///
/// A simple contract enabling the persistent storage of a Position similar to a pattern expected for platforms
/// building on top of TidalProtocol's lending protocol
///
access(all) contract MockTidalProtocolConsumer {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need a wrapper to enable persistent storage?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I suppose we don't. I originally added it thinking we might use Capabilities on it only to later realize they weren't needed and that we can actually issue Capabilities on structs as well as resources.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll note this as part of the post-tracer refactor, so it can be removed in a subsequent PR. Leaving for now to avoid merge conflicts in #19


/// Canonical path for where the wrapper is to be stored
access(all) let WrapperStoragePath: StoragePath

/// Opens a TidalProtocol Position and returns a PositionWrapper containing that new position
///
access(all)
fun createPositionWrapper(
collateral: @{FungibleToken.Vault},
issuanceSink: {DFB.Sink},
repaymentSource: {DFB.Source}?,
pushToDrawDownSink: Bool
): @PositionWrapper {
return <- create PositionWrapper(
position: TidalProtocol.openPosition(
collateral: <-collateral,
issuanceSink: issuanceSink,
repaymentSource: repaymentSource,
pushToDrawDownSink: pushToDrawDownSink
)
)
}

/// A simple resource encapsulating a TidalProtocol Position
access(all) resource PositionWrapper {

access(self) let position: TidalProtocol.Position

init(position: TidalProtocol.Position) {
self.position = position
}

/// NOT SAFE FOR PRODUCTION
///
/// Returns a reference to the wrapped Position
access(all) fun borrowPosition(): &TidalProtocol.Position {
return &self.position
}
}

init() {
self.WrapperStoragePath = /storage/tidalProtocolPositionWrapper
}
}
13 changes: 13 additions & 0 deletions cadence/scripts/tidal-protocol/get_available_balance.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "TidalProtocol"

access(all)
fun main(pid: UInt64, vaultIdentifier: String, pullFromTopUpSource: Bool): UFix64 {
let vaultType = CompositeType(vaultIdentifier) ?? panic("Invalid vaultIdentifier \(vaultIdentifier)")

let protocolAddress= Type<@TidalProtocol.Pool>().address!

let pool = getAccount(protocolAddress).capabilities.borrow<&TidalProtocol.Pool>(TidalProtocol.PoolPublicPath)
?? panic("Could not find a configured TidalProtocol Pool in account \(protocolAddress) at path \(TidalProtocol.PoolPublicPath)")

return pool.availableBalance(pid: pid, type: vaultType, pullFromTopUpSource: pullFromTopUpSource)
}
17 changes: 17 additions & 0 deletions cadence/scripts/tidal-protocol/get_reserve_balance_for_type.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "TidalProtocol"

/// Returns the Pool's reserve balance for a given Vault type
///
/// @param vaultIdentifier: The Type identifier (e.g. vault.getType().identifier) of the related token vault
///
access(all)
fun main(vaultIdentifier: String): UFix64 {
let vaultType = CompositeType(vaultIdentifier) ?? panic("Invalid vaultIdentifier \(vaultIdentifier)")

let protocolAddress= Type<@TidalProtocol.Pool>().address!

let pool = getAccount(protocolAddress).capabilities.borrow<&TidalProtocol.Pool>(TidalProtocol.PoolPublicPath)
?? panic("Could not find a configured TidalProtocol Pool in account \(protocolAddress) at path \(TidalProtocol.PoolPublicPath)")

return pool.reserveBalance(type: vaultType)
}
9 changes: 9 additions & 0 deletions cadence/scripts/tidal-protocol/pool_exists.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import "TidalProtocol"

/// Returns whether there is a Pool stored in the provided account's address. This address would normally be the
/// TidalProtocol contract address
///
access(all)
fun main(address: Address): Bool {
return getAccount(address).storage.type(at: TidalProtocol.PoolStoragePath) == Type<@TidalProtocol.Pool>()
}
13 changes: 13 additions & 0 deletions cadence/scripts/tidal-protocol/position_health.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "TidalProtocol"

/// Returns the position health for a given position id, reverting if the position does not exist
///
/// @param pid: The Position ID
///
access(all)
fun main(pid: UInt64): UFix64 {
let protocolAddress= Type<@TidalProtocol.Pool>().address!
return getAccount(protocolAddress).capabilities.borrow<&TidalProtocol.Pool>(TidalProtocol.PoolPublicPath)
?.positionHealth(pid: pid)
?? panic("Could not find a configured TidalProtocol Pool in account \(protocolAddress) at path \(TidalProtocol.PoolPublicPath)")
}
8 changes: 8 additions & 0 deletions cadence/scripts/tokens/get_balance.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "FungibleToken"

/// Returns a account's balance of a FungibleToken Vault with public Capability published at the provided path
///
access(all)
fun main(address: Address, vaultPublicPath: PublicPath): UFix64? {
return getAccount(address).capabilities.borrow<&{FungibleToken.Vault}>(vaultPublicPath)?.balance ?? nil
}
Loading