Skip to content

Commit 32a86d1

Browse files
authored
Merge branch 'main' into renovate/major-react-monorepo
2 parents 08d0e8a + 884413e commit 32a86d1

File tree

5 files changed

+2600
-4083
lines changed

5 files changed

+2600
-4083
lines changed

β€Ž.github/workflows/main.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
run: npm run build
2626

2727
- name: Configure AWS credentials
28-
uses: aws-actions/configure-aws-credentials@50ac8dd1e1b10d09dac7b8727528b91bed831ac0 # v3
28+
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4
2929
with:
3030
role-to-assume: ${{ secrets.DOCS_AWS_IAM_ROLE }}
3131
aws-region: ${{ secrets.DOCS_AWS_REGION }}

β€Ždocs/based-applications/developers/bapp-example.md

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,14 @@ This bApp example implements two functions:
4444
- ```createNewTask()```
4545
- ```respondToTask()```
4646

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()```)
47+
These functions handle the initial task request from users (```createNewTask()```), triggering an event.
48+
The client listens for this event to begin task execution.
49+
Upon completion, the client pushes data back on-chain (```respondToTask()```)
4850

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.
51+
This example uses ECDSA for verification (any verification method can be used), where strategy owners or a signer they have delegated sign
52+
messages containing the task number and ETH price for their vote on the client.
53+
Signatures are stored in an array and sent in the ```respondToTask()``` function along with public keys.
54+
The contract verifies signatures and confirms each address has opted into the bApp or is a delegated signer before saving the price.
5055

5156
:::info
5257
Steps to deploy and verify the contract are included in the README of the repo.
@@ -75,7 +80,8 @@ function respondToTask(
7580
uint32 taskNumber,
7681
uint256 ethPrice,
7782
bytes[] calldata signatures,
78-
address[] calldata signers
83+
address[] calldata signers,
84+
uint32 strategyId
7985
) external {
8086
// check that the task is valid and hasn't been responded to yet
8187
if (taskHash != allTaskHashes[taskNumber]) { revert TaskMismatch(); }
@@ -85,32 +91,60 @@ function respondToTask(
8591

8692
// Create the message that was signed (task num + price)
8793
bytes32 messageHash = keccak256(abi.encodePacked(taskNumber, ethPrice));
94+
95+
// Get the strategy signer
96+
address strategySignerAddress = strategySigner[strategyId];
8897

8998
// Verify each signature
9099
for (uint i = 0; i < signatures.length; i++) {
100+
91101
// Recover the signer address from the signature
92102
address recoveredSigner = messageHash.recover(signatures[i]);
93-
94-
// Verify the recovered signer matches the expected signer
95-
if (recoveredSigner != signers[i]) {
96-
revert InvalidSigner();
97-
}
98103

99-
// Check if the signer has opted into this bApp
100-
uint32 strategyId = IAccountBAppStrategy(address(ssvBasedApps)).accountBAppStrategy(recoveredSigner, address(this));
101-
if (strategyId == 0) {
102-
revert NotOptedIn();
104+
if (strategySignerAddress != address(0)) {
105+
// if strategy has a signer set, verify this signer is the correct one
106+
if (strategySignerAddress != signers[i]) {
107+
revert InvalidSigner();
108+
}
109+
} else {
110+
// if strategy has no signer set, check the signer is the owner of the strategy
111+
uint32 derivedStrategyId = IAccountBAppStrategy(address(ssvBasedApps)).accountBAppStrategy(recoveredSigner, address(this));
112+
if (derivedStrategyId != strategyId) {
113+
revert NotOptedIn();
114+
}
103115
}
104116
}
105117

106118
// Store the response
107119
allTaskResponses[msg.sender][taskNumber] = abi.encode(ethPrice);
120+
mostRecentPrice = ethPrice;
108121

109122
// Emit event with the ETH price
110123
emit TaskResponded(taskNumber, taskHash, msg.sender, ethPrice);
111124
}
112125
```
113126

127+
In this example the ```optInToBapp``` function is also overridden, this is what is called when a strategy opts in to a bApp.
128+
Here in the data field an encoded public address is passed in (e.g 0x000000000000000000000000ac5a7ce31843e737cd38938a8efdec0be5e728b4).
129+
This address can then be used as the signer address which is run on the client.
130+
131+
```typescript
132+
function optInToBApp(
133+
uint32 strategyId,
134+
address[] calldata,
135+
uint32[] calldata,
136+
bytes calldata data
137+
) external override onlySSVBasedAppManager returns (bool success) {
138+
// Decode the padded address correctly from bytes32
139+
address signer = address(uint160(uint256(abi.decode(data, (bytes32)))));
140+
// Store the address in the mapping
141+
strategySigner[strategyId] = signer;
142+
143+
emit DebugOptIn(strategyId, signer, testOne[1], testTwo[2]);
144+
145+
return true;
146+
}
147+
```
114148
## 2. Registering a bApp to the network
115149

116150
After contract deployment, the register function becomes available.
@@ -135,13 +169,13 @@ Each Based Application requires a client implementation, to be run by each strat
135169

136170
In this example, the strategy client will:
137171

138-
**4.1** Listen for tasks to process, monitoring events emitted from ```createNewTask()```
172+
* Listen for tasks to process, monitoring events emitted from ```createNewTask()``` ([**4.1**](#41-task-listening-implementation))
139173

140-
**4.2** Execute tasks off-chain, fetching the current ETH price
174+
* Execute tasks off-chain, fetching the current ETH price ([**4.2**](#42-task-execution))
141175

142-
**4.3** Cast votes on the correct price, signing messages containing the task number and fetched price
176+
* Cast votes on the correct price, signing messages containing the task number and fetched price ([**4.3**](#43-task-outcome-voting))
143177

144-
**4.4** After majority vote determination, the last voting strategy signs the ```respondToTask()``` function and publishes the price on-chain
178+
* After majority vote determination, the last voting strategy signs the ```respondToTask()``` function and publishes the price on-chain ([**4.4**](#44-majority-vote-submission))
145179

146180

147181
### Code Snippets
@@ -287,10 +321,51 @@ const { request } = await publicClient.simulateContract({
287321
BigInt(task.ethPrice),
288322
signatures as `0x${string}`[],
289323
signers as `0x${string}`[],
324+
strategyId
290325
],
291326
account: walletClient.account,
292327
});
293328

294329
// Send the transaction
295330
const hash = await walletClient.writeContract(request);
331+
```
332+
333+
The client will wait for tasks to be created, and then process the votes based on each strategies weight.
334+
335+
The output is as follows:
336+
337+
```bash
338+
[Heartbeat] Client is running and waiting for new tasks...
339+
[Heartbeat] Client is running and waiting for new tasks...
340+
[Heartbeat] Client is running and waiting for new tasks...
341+
[Heartbeat] Client is running and waiting for new tasks...
342+
[11:57:18] πŸ“‘ Message Hash: 0x2333df8c374a71c2e843dfd787ab8b96f6431d43858758b5b49bed88b1cb6ff7
343+
[11:57:18] πŸ“‘ Task Number: 10
344+
[11:57:18] πŸ“‘ ETH Price: 2483
345+
[11:57:18] πŸ“‘ Signature: 0x5d4a762890e1b7fa8fe6affddd3c6f2a897b566bbce524f4e4932f31dd6a183c455405c1a4e5fb0438daaa60df17e354de2e7188492e6ad6776716e008eb1bf11b
346+
[11:57:18] πŸ“‘ Signer Address: 0xac5a7Ce31843e737CD38938A8EfDEc0BE5e728b4
347+
[11:57:18] πŸ“‘ ════════════════════════════════════════════════════════════════════════════════
348+
[11:57:18] πŸ“Š New on-chain task detected:
349+
[11:57:18] πŸ“‘ Task Index: 10
350+
[11:57:18] πŸ“‘ Task Hash: 0xfa311eaab35462b5895f378e86e51f16d9d8e0c8dbe5b6ea9990ea3dd1b6fe5c
351+
[11:57:18] πŸ“‘ Current ETH Price: $2483
352+
[11:57:18] πŸ“‘ Status: Waiting for votes...
353+
[11:57:18] πŸ“‘ ════════════════════════════════════════════════════════════════════════════════
354+
355+
Current votes for this task:
356+
Strategy 13: 33.3%
357+
Strategy 15: 66.7%
358+
Total: 100.0%
359+
360+
361+
Majority reached! Strategy 15 will send the transaction.
362+
363+
Strategy ID: 15
364+
Number of Signatures: 1
365+
ETH Price: $2483
366+
Task Number: 10
367+
════════════════════════════════════════════════════════════════════════════════
368+
Transaction submitted: 0x7c11297736ef700635e971a6729f5ec319ac6780da02617a62c22ef566e85008
369+
370+
════════════════════════════════════════════════════════════════════════════════
296371
```

0 commit comments

Comments
Β (0)