|
| 1 | +--- |
| 2 | +sidebar_label: 'Based App Development' |
| 3 | +sidebar_position: 2 |
| 4 | +--- |
| 5 | + |
| 6 | +# Based Application Development |
| 7 | + |
| 8 | +This guide outlines the steps for based applications developers looking to build on the Based Applications platform. |
| 9 | + |
| 10 | +<!-- ## 0. Developing a Based Application Middleware smart contract |
| 11 | +
|
| 12 | +The `BAppManager` smart contract developed by SSV Labs accepts registrations of BApps that implement a specific interface. This is outlined [in this dedicated page](./smart-contracts/based-app-middleware-example.md), that also provides a simple example. --> |
| 13 | + |
| 14 | +## 1. Configuring and Registering the bApp |
| 15 | + |
| 16 | +1. **Define core attributes**: |
| 17 | +- `bApp`: a unique 20-byte EVM address that uniquely identifies the bApp. |
| 18 | +- `tokens`: A list of ERC-20 tokens to be used in the bApp's security mechanism. For the native ETH token, use the special address [`0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`](https://github.com/ssvlabs/based-applications/blob/main/src/BasedAppManager.sol#L62). |
| 19 | +- `sharedRiskLevels`: a list with $\beta$ values, one for each token, representing the bApp's tolerance for risk (token over-usage). |
| 20 | +2. **Optional Non-Slashable Validator Balance**: If the bApp uses non-slashable validator balance, it should be configured off-chain, in the bApp's network. |
| 21 | +3. **Register the bApp**: Use the [`registerBApp`](./smart-contracts/BasedAppManager#registerbappbapp-tokens-sharedrisklevels-metadatauri) function of the smart contract: |
| 22 | +```javascript |
| 23 | +function registerBApp( |
| 24 | + address bApp, |
| 25 | + address[] calldata tokens, |
| 26 | + uint32[] calldata sharedRiskLevels, |
| 27 | + string calldata metadataURI |
| 28 | +) |
| 29 | +``` |
| 30 | +- `metadataURI`: A JSON object containing additional details about your bApp, such as its name, description, logo, and website. |
| 31 | +4. **Update Configuration**: After registering, the bApp configuration can be updated only by the `owner` account. |
| 32 | + |
| 33 | +## 2. Securing the bApp |
| 34 | + |
| 35 | +Once the bApp is registered, `Strategies` can join it and allocate capital to secure it. |
| 36 | + |
| 37 | +### 2.1 Opting in |
| 38 | + |
| 39 | +A Strategy opts-in to the bApp by using the [`optInToBApp`](./smart-contracts/BasedAppManager#optintobappstrategyid-bapp-tokens-obligationpercentages-data) function of the smart contract: |
| 40 | +```javascript |
| 41 | +function optInToBApp( |
| 42 | + uint256 strategyId, |
| 43 | + address bApp, |
| 44 | + address[] calldata tokens, |
| 45 | + uint32[] calldata obligationPercentages, |
| 46 | + bytes calldata data |
| 47 | +) |
| 48 | +``` |
| 49 | +- `tokens`: List of tokens to obligate to the bApp. |
| 50 | +- `obligationPercentages`: The proportion of each token's balance to commit to the bApp. |
| 51 | +- `data`: An extra optional field for off-chain information required by the bApp for participation. |
| 52 | + |
| 53 | +For example, if `tokens = [SSV]` and `obligationPercentages = [50%]`, then 50% of the strategy's `SSV` balance will be obligated to the bApp. |
| 54 | + |
| 55 | +The strategy’s owner can later update their obligations by modifying existing ones or adding a new token obligation. |
| 56 | + |
| 57 | +### 2.2 Strategy's Funds |
| 58 | + |
| 59 | +To compose their balances, strategies: |
| 60 | +1. receive ERC20 (or ETH) via [**deposits**](https://github.com/ssvlabs/based-applications/blob/main/src/BasedAppManager.sol#L376) from accounts. |
| 61 | +2. inherit the non-slashable validator balance from the strategy's owner account. Accounts [**delegate**](https://github.com/ssvlabs/based-applications/blob/main/src/BasedAppManager.sol#L201) validator balances between themselves, and the strategy inherits its owner's non-delegated balance plus the received balances from other accounts. |
| 62 | + |
| 63 | +## 3. Participant Weight |
| 64 | + |
| 65 | +Based Application managers need to track the weight of each participant (strategy) in the bApp itself. This process entails two separate steps: |
| 66 | + |
| 67 | +1. Collecting risk-adjusted strategy weights for each token |
| 68 | +2. Combining such weights into a strategy weight, for each strategy |
| 69 | + |
| 70 | +### Risk-adjusted strategy-token weights |
| 71 | + |
| 72 | +The first step is made fairly easier thanks to the `based-apps-sdk`, which needs to be installed first: |
| 73 | + |
| 74 | +```sh |
| 75 | +npm i @ssv-labs/based-apps-sdk |
| 76 | +``` |
| 77 | + |
| 78 | +The SDK provides a function that returns all the risk-adjusted weights for each token, for all the strategies that opted in to a given bApp: |
| 79 | + |
| 80 | +```ts |
| 81 | +import { BasedAppsSDK, chains } from "@ssv-labs/based-apps-sdk"; |
| 82 | + |
| 83 | +const sdk = new BasedAppsSDK({ |
| 84 | + chain: chains.holesky.id, |
| 85 | +}); |
| 86 | + |
| 87 | +// calculate strategy-token weights via the SDK |
| 88 | +const strategyTokenWeights = await sdk.api.calculateParticipantWeights({ |
| 89 | + bAppId: "0x64714cf5db177398729e37627be0fc08f43b17a6", |
| 90 | +}); |
| 91 | + |
| 92 | +console.info( |
| 93 | + `Strategy-token weights: ${JSON.stringify( |
| 94 | + strategyTokenWeights, |
| 95 | + undefined, |
| 96 | + 2 |
| 97 | + )}` |
| 98 | +); |
| 99 | +``` |
| 100 | + |
| 101 | +This returns a matrix, mapping the risk-adjusted weight of each token to a given strategy. |
| 102 | + |
| 103 | +```sh |
| 104 | +Strategy-token weights: [ |
| 105 | + { |
| 106 | + "id": "10", |
| 107 | + "tokenWeights": [ |
| 108 | + { |
| 109 | + "token": "0x68a8ddd7a59a900e0657e9f8bbe02b70c947f25f", |
| 110 | + "weight": 0.9267840593141798 |
| 111 | + } |
| 112 | + ], |
| 113 | + "validatorBalanceWeight": 0.3 |
| 114 | + }, |
| 115 | + { |
| 116 | + "id": "2", |
| 117 | + "tokenWeights": [ |
| 118 | + { |
| 119 | + "token": "0x68a8ddd7a59a900e0657e9f8bbe02b70c947f25f", |
| 120 | + "weight": 0.07321594068582021 |
| 121 | + } |
| 122 | + ], |
| 123 | + "validatorBalanceWeight": 0.7 |
| 124 | + } |
| 125 | +] |
| 126 | +``` |
| 127 | + |
| 128 | +In this example, strategy number `2` has a risk-adjusted weight of `~0.07` for the specified token, but it has a validator balance weight of `0.7`. |
| 129 | + |
| 130 | +Strategy number `10`, has a much bigger risk-adjusted weight of `~0.93` for specified token, but it has a validator balance weight of `0.3`. |
| 131 | + |
| 132 | +For more information on how these risk-adjusted strategy-token weights are calculated, [please visit this page](../learn/based-applications/strategy-weights.md). |
| 133 | + |
| 134 | +### Final weight |
| 135 | + |
| 136 | +To combine data from the previous step into the final Weight it is necessary to use a **combination function**. Such function has to be defined by the bApp itself and can be tailored to its specific needs. Traditionally, one good combination functions include the arithmetic mean, geometric mean, and harmonic mean. |
| 137 | + |
| 138 | +Let's consider the bApp from step 1, which uses tokens **`0x68a8ddd7a59a900e0657e9f8bbe02b70c947f25f`** and **validator balance**. For the purpose of our example, this bApp considers **`0x68a8ddd7a59a900e0657e9f8bbe02b70c947f25f`** to be twice times as important as **validator balance**. |
| 139 | + |
| 140 | +:::info |
| 141 | +Note: this is a purely fictional scenario, to show the strong impact of the coefficients attributed to tokens and validator balance, with respect to the final combined weight of a strategy. |
| 142 | +::: |
| 143 | + |
| 144 | +Below it's reported a code snippet, showing how to combine weights from step 1 using a simple arithmetic weighted average in the described scenario: |
| 145 | + |
| 146 | +```ts |
| 147 | +const validatorImportance = 1; |
| 148 | +const ssvTokenImportance = 2; |
| 149 | + |
| 150 | +console.info(`Using arithmetic weighted average to calculate Strategy weights. |
| 151 | +Validator Balance is 2 times more important than 0x68a8ddd7a59a900e0657e9f8bbe02b70c947f25f`); |
| 152 | + |
| 153 | +let simpleAverageStrategyWeights = new Map(); |
| 154 | +for (const strategy of strategyTokenWeights) { |
| 155 | + // calculate the strategy weight, combining token weight and validator balance weight |
| 156 | + let strategyWeight = |
| 157 | + ((strategy.validatorBalanceWeight || 0) * validatorImportance + |
| 158 | + strategy.tokenWeights[0].weight * ssvTokenImportance) / |
| 159 | + (validatorImportance + ssvTokenImportance); |
| 160 | + // set the value in the mapping |
| 161 | + simpleAverageStrategyWeights.set(strategy.id, strategyWeight); |
| 162 | +} |
| 163 | + |
| 164 | +console.info( |
| 165 | + `Final Strategy weights: ${JSON.stringify( |
| 166 | + Object.fromEntries(simpleAverageStrategyWeights), |
| 167 | + undefined, |
| 168 | + 2 |
| 169 | + )}` |
| 170 | +); |
| 171 | +``` |
| 172 | + |
| 173 | +This returns a map with the combined risk-adjusted weight of each strategy. |
| 174 | + |
| 175 | +```json |
| 176 | +Using arithmetic weighted average to calculate Strategy weights. |
| 177 | +Validator Balance is 2 times more important than 0x68a8ddd7a59a900e0657e9f8bbe02b70c947f25f |
| 178 | +Final Strategy weights: { |
| 179 | + "2": 0.28214396045721346, |
| 180 | + "10": 0.7178560395427865 |
| 181 | +} |
| 182 | +``` |
| 183 | + |
| 184 | +### Complete example |
| 185 | + |
| 186 | +[The following page](./participant-weight-example.md), shows the full coded example of how to obtain risk-adjusted strategy-token weights, and how to combine them. The example uses a weighted simple average (as shown here), as well as a slightly more involved **combination functions** like weighted geometric average and weighted harmonic average ([explained here](../learn/based-applications/strategy-weights.md)), and shows the different outcome of the three. |
0 commit comments