Skip to content

Commit

Permalink
Contract testnet launchable version (#6)
Browse files Browse the repository at this point in the history
* Update README with a walking through of the contract

* Contract basic structure

* Update git config files

* New path constants and some name refactoring

* Create relay transactions for admin and relayer and update rates tx for relayer

* Update README.md

Co-authored-by: j pimmel <frankly.watson@gmail.com>

* Update README.md

Co-authored-by: j pimmel <frankly.watson@gmail.com>

* Update README.md

Co-authored-by: j pimmel <frankly.watson@gmail.com>

* Update README.md

Co-authored-by: j pimmel <frankly.watson@gmail.com>

* Update README.md

Co-authored-by: j pimmel <frankly.watson@gmail.com>

* Add events for updater and relayer creation

* Add some basic comments to txs

* Simplify relayer authorization method and create a revoke relater transaction

* Add force update method for relayers. Improve granting and revoking system for relayers

* updateData, forceUpdate, and deleteSymbol functions

* Compleat getReferenceData functions

* Add ReferenceData struct for returning UFix rates

* Createa ReferenceData struct for returning data

* Delete standard contracts not used so far

* Modify flow.json

* Include transaction rates mockups along with built and signed emulator transactions for manual testing

* Include emulator relayer account on flow.json

* Create get rates script

* refdataupdated event emitted after update has actually been done

* gitignore

* Fix typos

* fixed untracked files

* move manual testing md to root folder

* Tx scaffolding moved to its own folder

* gitignore

* gitignore

* fixed untracked files

* Added testnet accounts to flow.json

* Change update event name

---------

Co-authored-by: j pimmel <frankly.watson@gmail.com>
  • Loading branch information
alilloig and franklywatson authored Oct 16, 2023
1 parent 61673fa commit 1ffb02d
Show file tree
Hide file tree
Showing 18 changed files with 436 additions and 1,367 deletions.
1 change: 0 additions & 1 deletion .env

This file was deleted.

2 changes: 0 additions & 2 deletions .gitignore

This file was deleted.

208 changes: 165 additions & 43 deletions contracts/BandOracle.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,36 @@ pub contract BandOracle {
/// Paths
///
// Admin resource storage path
pub let BandOracleAdminStoragePath: StoragePath
// OracleAdmin resource paths
pub let OracleAdminStoragePath: StoragePath
pub let OracleAdminPrivatePath: PrivatePath

// Relay resource paths
pub let RelayStoragePath: StoragePath
pub let RelayPrivatePath: PrivatePath

///
/// Contract level fields
/// Fields
///
/// Set a string as base private path for data updater capabilities
access(contract) let dataUpdaterPrivateBasePath: String

// Mapping from symbol to data struct
access(contract) let symbolsRefData: {String: RefData}

///
/// Events
///
//
pub event NewRelayerCreated()

//
pub event RelayerAuthorised()

//
pub event RelayerDismissed()
//
pub event RefDataUpdated()
// Emitted by a relayer when it updates a set of symbols
pub event BandOracleSymbolsUpdated(symbols: [String], relayerID: UInt64, requestID: UInt64)

///
/// Data Structs
/// Structs
///
//
// Struct for storing market data
pub struct RefData {
// USD-rate, multiplied by 1e9.
pub var rate: UInt64
Expand All @@ -53,76 +52,168 @@ pub contract BandOracle {
}
}

///
/// Resources
///
// Struct for consuming market data
pub struct ReferenceData {
// Base / quote symbols rate
pub var rate: UInt256
// UNIX epoch when base data is last resolved.
pub var baseTimestamp: UInt64
// UNIX epoch when quote data is last resolved.
pub var quoteTimestamp: UInt64

///
///
pub resource RelayerAdministrator {
init(rate: UInt256, baseTimestamp: UInt64, quoteTimestamp: UInt64) {
self.rate = rate
self.baseTimestamp = baseTimestamp
self.quoteTimestamp = quoteTimestamp
}
}

// This would create new data updater resources and publish a capability to
// them to the account meant to create a Relay resource

// It will also provide a mechanism to unlink that capability and delete the
// associated updater resource in case a certain Relayer needs to be
// unauthorized
///
/// Resources
///
pub resource interface OracleAdmin {
pub fun getUpdaterCapabilityPathFromAddress (relayer: Address): PrivatePath
}

///
///
pub resource interface DataUpdater {
pub fun updateData (symbolsRates: {String: UInt64}, resolveTime: UInt64,
requestID: UInt64)
requestID: UInt64, relayerID: UInt64)
pub fun forceUpdateData (symbolsRates: {String: UInt64}, resolveTime: UInt64,
requestID: UInt64, relayerID: UInt64)
}

///
///
pub resource RefDataUpdater: DataUpdater {
pub resource BandOracleAdmin: OracleAdmin, DataUpdater {

///
/// Auxiliary method to ensure that the formation of the capability path that
/// identifies relayers is done in a uniform way
///
pub fun getUpdaterCapabilityPathFromAddress (relayer: Address): PrivatePath {
// Create the string that will form the private path concatenating the base
// path and the relayer identifying address
let privatePathString =
BandOracle.getUpdaterCapabilityNameFromAddress(relayer: relayer)
// Attempt to create the private path using the identifier
let dataUpdaterPrivatePath =
PrivatePath(identifier: privatePathString)
?? panic("Error while creating data updater capability private path")
return dataUpdaterPrivatePath
}

// OracleAdmin and entitled relayers can call this method to update rates
pub fun updateData (symbolsRates: {String: UInt64}, resolveTime: UInt64,
requestID: UInt64){
requestID: UInt64, relayerID: UInt64) {
BandOracle.updateRefData(symbolsRates: symbolsRates, resolveTime: resolveTime,
requestID: requestID)
requestID: requestID, relayerID: relayerID)
}

// OracleAdmin and entitled relayers can call this method to force update rates
pub fun forceUpdateData (symbolsRates: {String: UInt64}, resolveTime: UInt64,
requestID: UInt64, relayerID: UInt64) {
BandOracle.forceUpdateRefData(symbolsRates: symbolsRates, resolveTime: resolveTime,
requestID: requestID, relayerID: relayerID)
}
}

}

///
///
pub resource Relay {

// Capability linked to the assigned updater resource
// Capability linked to the OracleAdmin allowing relayers to relay rate updates
access(self) let updaterCapability: Capability<&{DataUpdater}>

pub fun relay (symbolsRates: {String: UInt64}, resolveTime: UInt64, requestID: UInt64){
pub fun relayRates (symbolsRates: {String: UInt64}, resolveTime: UInt64, requestID: UInt64) {
let updaterRef = self.updaterCapability.borrow()
?? panic ("Can't borrow reference to data updater while processing request ".concat(requestID.toString()))
updaterRef.updateData(symbolsRates: symbolsRates, resolveTime: resolveTime, requestID: requestID, relayerID: self.uuid)
}

pub fun forceRelayRates (symbolsRates: {String: UInt64}, resolveTime: UInt64, requestID: UInt64) {
let updaterRef = self.updaterCapability.borrow()
?? panic ("Can't borrow reference to data updater while processing request ".concat(requestID.toString()))
updaterRef.forceUpdateData(symbolsRates: symbolsRates, resolveTime: resolveTime, requestID: requestID, relayerID: self.uuid)
}

init(updaterCapability: Capability<&{DataUpdater}>){
init(updaterCapability: Capability<&{DataUpdater}>) {
self.updaterCapability = updaterCapability
let updaterRef = self.updaterCapability.borrow()
?? panic ("Can't borrow linked updater")
}
}

///
/// Contract functions
/// Functions
///
///
///
access(contract) fun updateRefData (symbolsRates: {String: UInt64}, resolveTime: UInt64, requestID: UInt64, relayerID: UInt64) {
let updatedSymbols: [String] = []
// For each symbol rate relayed
for symbol in symbolsRates.keys {
// If the symbol hasn't stored rates yet, or the stored records are older
// than the new relayed rates
if (BandOracle.symbolsRefData[symbol] == nil ) ||
(BandOracle.symbolsRefData[symbol]!.timestamp < resolveTime) {
// Store the relayed rate
BandOracle.symbolsRefData[symbol] =
RefData(rate: symbolsRates[symbol]!, timestamp: resolveTime, requestID: requestID)
updatedSymbols.append(symbol)
}
}
emit BandOracleSymbolsUpdated(symbols: updatedSymbols, relayerID: relayerID, requestID: requestID)
}

///
///
access(contract) fun updateRefData (symbolsRates: {String: UInt64}, resolveTime: UInt64, requestID: UInt64) {
access(contract) fun forceUpdateRefData (symbolsRates: {String: UInt64}, resolveTime: UInt64, requestID: UInt64, relayerID: UInt64) {
// For each symbol rate relayed, store it no matter what was the previous
// records for it
for symbol in symbolsRates.keys {
BandOracle.symbolsRefData[symbol] =
RefData(rate: symbolsRates[symbol]!, timestamp: resolveTime, requestID: requestID)
}
emit BandOracleSymbolsUpdated(symbols: symbolsRates.keys, relayerID: relayerID, requestID: requestID)
}

///
///
access(contract) fun removeSymbol (symbol: String) {
BandOracle.symbolsRefData[symbol] = nil
}

///
///
access(contract) fun _getRefData (symbol: String): RefData?{
return self.symbolsRefData[symbol]
access(contract) fun _getRefData (symbol: String): RefData? {
if (symbol == "USD") {
return RefData(rate: 1000000000, timestamp: UInt64(getCurrentBlock().timestamp), requestID: 0)
} else {
return self.symbolsRefData[symbol] ?? nil
}
}

///
///
pub fun getReferenceData (baseSymbol: String, quoteSymbol: String): RefData?{
return self.symbolsRefData[baseSymbol]
pub fun getReferenceData (baseSymbol: String, quoteSymbol: String): ReferenceData? {
let baseRefData = BandOracle._getRefData(symbol: baseSymbol)
let quoteRefData = BandOracle._getRefData(symbol: quoteSymbol)
let backToDecimalFactor: UInt256 = 1000000000000000000
if (baseRefData == nil || quoteRefData == nil) {
return nil
} else {
let rate = UInt256((UInt256(baseRefData!.rate) * backToDecimalFactor) / UInt256(quoteRefData!.rate))
return ReferenceData (rate: rate,
baseTimestamp: baseRefData!.timestamp,
quoteTimestamp: quoteRefData!.timestamp)
}

}

///
Expand All @@ -131,11 +222,42 @@ pub contract BandOracle {
return <- create Relay(updaterCapability: updaterCapability)
}

///
/// Auxiliary method to ensure that the formation of the capability name that
/// identifies data updater capability for relayers is done in a uniform way
/// by both admin and relayers
///
pub fun getUpdaterCapabilityNameFromAddress (relayer: Address): String {
// Create the string that will form the private path concatenating the base
// path and the relayer identifying address
let capabilityName =
BandOracle.dataUpdaterPrivateBasePath.concat(relayer.toString())
return capabilityName
}

///
///
init() {
self.BandOracleAdminStoragePath = /storage/BandOracleAdmin
self.account.save(<- create RelayerAdministrator(), to: self.BandOracleAdminStoragePath)
self.OracleAdminStoragePath = /storage/BandOracleAdmin
self.OracleAdminPrivatePath = /private/BandOracleAdmin
self.RelayStoragePath = /storage/BandOracleRelay
self.RelayPrivatePath = /private/BandOracleRelay
self.dataUpdaterPrivateBasePath = "DataUpdater"
self.account.save(<- create BandOracleAdmin(), to: self.OracleAdminStoragePath)
self.account.link<&{OracleAdmin}>(self.OracleAdminPrivatePath, target: self.OracleAdminStoragePath)
self.symbolsRefData = {}
// Create a relayer on the admin account so the relay methods are never accessed directly.
// The admin could decide to build a transaction borrowing the whole BandOracleAdmin
// resource and call updateData methods bypassing relayData methods but we are explicitly
// discouraging that by giving the admin a regular relay resource on contract deployment.
let oracleAdminRef = self.account.borrow<&{OracleAdmin}>(from: BandOracle.OracleAdminStoragePath)
?? panic("Can't borrow a reference to the Oracle Admin")
let dataUpdaterPrivatePath = oracleAdminRef.getUpdaterCapabilityPathFromAddress(relayer: self.account.address)
self.account.link<&{BandOracle.DataUpdater}>(dataUpdaterPrivatePath, target: BandOracle.OracleAdminStoragePath)
?? panic ("Data Updater capability for admin creation failed")
let updaterCapability = self.account.getCapability<&{BandOracle.DataUpdater}>(dataUpdaterPrivatePath)
let relayer <- BandOracle.createRelay(updaterCapability: updaterCapability)
self.account.save(<- relayer, to: BandOracle.RelayStoragePath)
self.account.link<&BandOracle.Relay>(BandOracle.RelayPrivatePath, target: BandOracle.RelayStoragePath)
}
}
Loading

0 comments on commit 1ffb02d

Please sign in to comment.