Required Interface
The Steer Protocol is possible due to a robust and flexible API which allows developers to build their own jobs and apps. Below is the interface for the v1.0.0 of the Steer Protocol.
This interface must be adhered to in order to be compatible with the Steer Protocol.
App must consist of 3 main functions:
Configuration
Most apps require configuration. JSON Schema is used to provide the proper interface between the bundle and their consumers (keeper and interfaces). The configured JSON Schema is also used to validate the configuration of the app params on the Steer Protocol when being set. The configuration object defined by the schema definition is passed into the initiliazer of the app.
Hint: App creator can check if the configuration object is proper via the backtesting toolset.
Interface (Assemblyscript)
export function config(): string
Example (Assemblyscript)
export function config(): string {
return `{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Strategy Config",
"type": "object",
"expectedDataTypes": ["OHLC"],
"properties": {
"percent": {
"type": "number",
"description": "Percent for trailing stop order",
"default": 5.0
},
"poolFee": {
"description": "Pool fee percent for desired Uniswapv3 pool",
"enum" : [10000, 3000, 500, 100],
"enumNames": ["1%", "0.3%", "0.05%", "0.01%"]
},
"binWidth": {
"type": "number",
"description": "Width for liquidity position, must be a multiple of pool tick spacing",
"default": 600
}
},
"required": ["percent", "binWidth", "poolFee"]
}`;
}
Inside the static config, there is a extra field called expectedDataTypes which define the type of data used by the app.
Initialize
The wasm bundle will be loaded by the keeper nodes and initialized with the configuration object previously created. Any necessary values needed for execution should be stored in memory to be used when execute is called. The initialize function can be optional if no configuration is needed.
Execution Context
For convenience, additional context data is joined with the config and passed into the initialize function, currently the additional data provided is:
interface ExecutionContext
{
// General Execution Context
lastExecutionTime: number = 0;
executionTimestamp: number = 0;
epochLength: number = 0;
epochTimestamp: i32 = 0;
vaultAddress: string = "";
blockTime: i32 = 0;
blockNumber: i32 = 0;
// ALM CONTEXT
positions: Array<Position> = [];
currentTick: number = 0;
totalAmount0: string = "";
totalAmount1: string = "";
}
Providing this data in this scope helps reduce additional calls since this information is typically present and can be very helpful in some processes.
Interface (Assemblyscript)
export function initialize(config: string): void
Example (Assemblyscript)
export function initialize(config: string): void {
// Parse the config object
const configJson = <JSON.Obj>JSON.parse(config);
// Get our config variables
const _width = configJson.getInteger("binWidth");
const _percent = configJson.getNum("percent");
// Handle null case
if (_width == null || _percent == null) {
throw new Error("Invalid config");
}
// Assign values to memory
width = i32(_width._num);
percent = f32(_percent._num);
}
Execute
Execution of an app is dependent on its datasets. The datasets are passed into the execute function of the app and then used to calculate the results of the app. The results are then published on the Steer Network which will facilitate on-chain execution.
The result of a given app is dependent on the vault which is used to execute the app. For instance, for a concentrated liquidity app the tend function request should be returned. Please check with the documentation of the vault to see what the proper function and types are. Any discrepencies will cause the execution to fail.
The result of the execution is passed into a class called the TransactionFactory in the node, which handles Keeper voting and on-chain execution.
The result is always stringified JSON. This allows for interpretation across execution environments.
Interface
public execute(_dataSources: string): string
Where the parameter _dataSources
will be a stringified array with each item being the resulting data from each DataConnector in order.
The string returned from the execute method will be a stringified object with the following elements:
interface transactionRequest
{
functionName: string;
typesArray: string[];
valuesArray: Type[];
}
If you wish for no action to be taken by the orchestrator (useful for cutting down on gas costs) you can return the string continue
from the execution bundle.
This interface must be adhered to in order for actions to be completed on-chain.
functionName: This should be the function selector for the function you wish to call on the associated vault. It will have the function name followed by the input parameter types. Follow this link to learn more about function selectors.
typesArray: Very similar to the types that will be in the function selector, this array of types is used to encode the valuesArray.
valuesArray: Lastly these are the values that will be passed as the encoded parameters into the desired vault function.
To learn more about encoding and types visit https://docs.soliditylang.org/en/v0.8.15/abi-spec.html#types.
Example (Assemblyscript)
export function execute(_prices: string): string {
// ...custom logic...
return
`{
functionName: "tend(uint256,(int24[],int24[],uint16[]),bytes)",
typesArray: ["uint256","tuple(int24[],int24[],uint16[])","bytes"],
valuesArray: [10000, [[22920],[23040],[1]], '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffff']
}`;
}
Take note of the tuple type here; the type is wrapped in parenthesis in the selector, but square brackets in the values array. Consult the Solidity Docs to correctly format complex types. If you are curious about the bytes placeholder in this example, read these observations on how we can inject time-sensitive data with consensus.
Version
Steer is ever evolving, and our interfaces might change. We will offer backwards compatibility for all versions we introduce.
Interface
export function version(): i32
As more versions are released the breaking changes will be listed here.
Example (Assemblyscript)
export function version(): i32 {
return 1;
}