You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The example discussed in this page can be found at this repository:
8
+
The code referenced in this page can be found at this repository:
10
9
11
10
<ahref="https://github.com/ssvlabs/examples">
12
11
<img
@@ -16,86 +15,282 @@ The example discussed in this page can be found at this repository:
16
15
/>
17
16
</a>
18
17
19
-
It has been built to execute one task: fetch the most recent block and reach a majority vote on the slot number.
20
18
21
-
The simple example implemented [here](https://github.com/ssvlabs/examples) and used as reference in this page does not currently use the SDK as it breaks down the steps further, with verbose logging, for illustrative purposes, as this is the most important thing that BApp client developers need to familiarize with.
19
+
This page demonstrates the core mechanics of building a Based Application and integrating it with the Based Applications Framework.
20
+
21
+
The following topics are covered:
22
+
23
+
- Creating a bApp contract
24
+
- Implementing business logic for a bApp
25
+
- Registering a bApp to the protocol
26
+
- Strategy registration and opt-in process
27
+
- Client implementation for task listening and response handling
28
+
29
+
30
+
:::info
31
+
This tutorial demonstrates one specific use case of bApps, implementing logic for tasks and how the contract/client handles execution and validation of tasks. This structure can be replaced with any logic necessary for a Based Application.
32
+
33
+
A Based Application does not require following this task/response structure, [any use case listed here can be implemented.](../learn/based-applications/use-cases)
34
+
:::
35
+
36
+
## 1. Creating a bApp contract
37
+
38
+
The contract used in this tutorial is a price oracle contract for retrieving the current price of ETH.
39
+
40
+
The contract inherits from a base bApp contract, providing the necessary functionality for network operation. Additional business logic for creating and handling tasks is also implemented.
41
+
42
+
This bApp example implements two functions:
43
+
44
+
-```createNewTask()```
45
+
-```respondToTask()```
46
+
47
+
These functions handle the initial task request from users (```createNewTask()```), triggering an event. The client listens for this event to begin task execution. Upon completion, the client pushes data back on-chain (```respondToTask()```)
48
+
49
+
This example uses ECDSA for verification (any verification method can be used), where strategy owners sign messages containing the task number and ETH price for their vote on the client. Signatures are stored in an array and sent in the ```respondToTask()``` function along with public keys. The contract verifies signatures and confirms each address has opted into the bApp before saving the price.
50
+
51
+
:::info
52
+
Steps to deploy and verify the contract are included in the README of the repo.
53
+
:::
22
54
23
-
When launched, the application the first action it takes is to fetch on-chain data for the given Based Application, in order to calculate the Strategy weights.
55
+
The full contract code can be found [here](https://github.com/ssvlabs/examples/bapp-contracts/middleware/examples/ethPriceOracle.sol)
24
56
25
-
## Strategy Weights
26
57
27
-
In Based Applications, the **obligated token balance and delegated validator balance**are used to attribute a weight to each Strategy, which is then **used to vote** on whatever task the client should be accomplishing.
58
+
The create and respond functions are as follows:
28
59
29
-
For an overview of these steps as well as a thorough explanation on the calculations, [please refer to this page in the Learn section](../learn/based-applications/strategy-weights.md).
Developers should, however, not worry too much about it, as all of this can be accomplished thanks to high-level SDK functions: [`getStrategyTokenWeights()`](./BA-SDK/module-reference/api-module.md#getstrategytokenweights) and [`calcArithmeticStrategyWeights()`](./BA-SDK/module-reference/utils-module.md#calcarithmeticstrategyweights) (as well as its other variants).
65
+
// store hash of task on-chain, emit event, and increase taskNum
66
+
allTaskHashes[latestTaskNum] = taskHash;
67
+
emit NewTaskCreated(latestTaskNum, taskHash);
68
+
latestTaskNum = latestTaskNum +1;
32
69
33
-
The vote calculation follows these steps:
70
+
return taskHash;
71
+
}
34
72
35
-
1. Collect strategies opted-in to the bapp
36
-
2. Collect total validator balance delegated to all opted-in strategy owners
37
-
3. Collect total obligated token balances
38
-
4. Get "significance" of tokens and validator balance from config
39
-
5. Calculate risk-adjusted weights for each token, for each strategy
40
-
6. Normalize the obtained weights
41
-
7. Combine strategy-token weights into a final weight for each strategy
73
+
functionrespondToTask(
74
+
bytes32taskHash,
75
+
uint32taskNumber,
76
+
uint256ethPrice,
77
+
bytes[] calldatasignatures,
78
+
address[] calldatasigners
79
+
) external {
80
+
// check that the task is valid and hasn't been responded to yet
81
+
if (taskHash != allTaskHashes[taskNumber]) { revert TaskMismatch(); }
82
+
if (allTaskResponses[msg.sender][taskNumber].length!=0) { revert AlreadyResponded(); }
83
+
if (ethPrice <=0) { revert InvalidPrice(); }
84
+
if (signatures.length!=signers.length) { revert InvalidSignature(); }
After contract deployment, the register function becomes available.
117
+
118
+
The contract inherits ```OwnableBasedApp```, providing the register function.
119
+
120
+
With the contract deployed and verified, navigate to Etherscan and access the contract page. Under ```Write Contract```, locate the ```registerBapp``` function.
121
+
122
+

123
+
124
+
Sign this transaction with the required tokens, shared risk level for each token, and the [bApp metadata URL.](./smart-contracts/metadata-schema)
125
+
126
+
## 3. Strategy creation and bApp opt-in process
127
+
128
+
After on-chain deployment and network registration, strategies can be created and opt into the bApp. Once opted in, participants can deposit any supported tokens.
129
+
130
+
For detailed guidance on this process, [follow this guide.](../user-guides/create-strategy.md)
131
+
132
+
## 4. Client implementation for the bApp
133
+
134
+
Each Based Application requires a client implementation, to be run by each strategy.
135
+
136
+
In this example, the strategy client will:
137
+
138
+
**4.1** Listen for tasks to process, monitoring events emitted from ```createNewTask()```
139
+
140
+
**4.2** Execute tasks off-chain, fetching the current ETH price
141
+
142
+
**4.3** Cast votes on the correct price, signing messages containing the task number and fetched price
143
+
144
+
**4.4** After majority vote determination, the last voting strategy signs the ```respondToTask()``` function and publishes the price on-chain
145
+
146
+
147
+
### Code Snippets
148
+
149
+
All of these code snippets and the working implementation can be found [here](https://github.com/ssvlabs/examples/eth-price-oracle-client)
150
+
151
+
#### 4.1 Task listening implementation
152
+
153
+
The viem client used to instantiate the bApps SDK also handles listening for ```createNewTask()``` events.
154
+
155
+
Task listening is implemented using ```watchEvent()```, with task data passed to ```handleNewTask``` for execution:
Once the application has finalized the Strategy Weights, it then starts the execution of a simple task (providing the next block slot number). The task is effectively executed, but the interaction between two independent strategy is simulated, for simplicity.
186
+
When a user initiates a task, the client fetches the current ETH price:
60
187
61
-
1. Strategies retrieve the slot number. In real world scenario, multiple instances of a client would do this independently, whereas the example only does this once.
2. The first Strategy attempts to complete the task, signing and broadcasting the voted slot number.
197
+
returnMath.round(price);
198
+
} catch (error) {
199
+
console.error('Error fetching ETH price:', error);
200
+
throwerror;
201
+
}
202
+
}
203
+
```
204
+
205
+
#### 4.3 Task outcome voting
64
206
65
-
3. The vote is processed, but the first Strategy only has 13.54% of the total weight, the majority is not reached, so it is not enough to complete the task.
207
+
With task details and execution complete, the client casts its vote by signing a message containing the price and task hash:
66
208
67
-
4. The second Strategy attempts to complete the task, signing and broadcasting the voted slot number.
0 commit comments