Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/middleware/examples/ECDSAVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.29;

import {
OwnableBasedApp
} from "@ssv/src/middleware/modules/core+roles/OwnableBasedApp.sol";

import {
SignatureChecker
} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "forge-std/Test.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract ECDSAVerifier is OwnableBasedApp, Test {
mapping(address => bool) public hasOptedIn;
constructor(
address _basedAppManager,
address _initOwner
) OwnableBasedApp(_basedAppManager, _initOwner) {}

function optInToBApp(
uint32,
address[] calldata,
uint32[] calldata,
bytes calldata data
) external override onlySSVBasedAppManager returns (bool success) {
(address signer, bytes32 messageHash, bytes memory signature) = abi
.decode(data, (address, bytes32, bytes));
success = SignatureChecker.isValidSignatureNow(
signer,
messageHash,
signature
);

require(!hasOptedIn[signer], "Replay signature is not allowed");

// Validate signature
success = SignatureChecker.isValidSignatureNow(
signer,
messageHash,
signature
);
require(success, "Invalid signature");

// Prevent replay
hasOptedIn[signer] = true; // mark as completed
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
AccessControl
} from "@openzeppelin/contracts/access/AccessControl.sol";

import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol";
import {
BasedAppCore
} from "@ssv/src/middleware/modules/core/BasedAppCore.sol";
Expand Down
1 change: 0 additions & 1 deletion src/middleware/modules/core+roles/OwnableBasedApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity 0.8.29;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol";
import {
BasedAppCore
} from "@ssv/src/middleware/modules/core/BasedAppCore.sol";
Expand Down
7 changes: 7 additions & 0 deletions test/helpers/Setup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
import {
WhitelistExample
} from "@ssv/src/middleware/examples/WhitelistExample.sol";
import { ECDSAVerifier } from "@ssv/src/middleware/examples/ECDSAVerifier.sol";
import { IBasedApp } from "@ssv/src/middleware/interfaces/IBasedApp.sol";

contract Setup is Test {
Expand All @@ -53,6 +54,7 @@ contract Setup is Test {
BasedAppMock4 public bApp4;
NonCompliantBApp public nonCompliantBApp;
WhitelistExample public whitelistExample;
ECDSAVerifier public ecdsaVerifierExample;
// Tokens
IERC20 public erc20mock;
IERC20 public erc20mock2;
Expand Down Expand Up @@ -157,6 +159,10 @@ contract Setup is Test {

nonCompliantBApp = new NonCompliantBApp(address(proxiedManager));
whitelistExample = new WhitelistExample(address(proxiedManager), USER1);
ecdsaVerifierExample = new ECDSAVerifier(
address(proxiedManager),
USER1
);

bApps.push(bApp1);
bApps.push(bApp2);
Expand All @@ -170,6 +176,7 @@ contract Setup is Test {
vm.label(address(bApp4), "BasedApp4");
vm.label(address(nonCompliantBApp), "NonCompliantBApp");
vm.label(address(whitelistExample), "WhitelistExample");
vm.label(address(ecdsaVerifierExample), "ECDSAVerifierExample");
vm.label(address(proxiedManager), "BasedAppManagerProxy");

vm.deal(USER1, INITIAL_USER1_BALANCE_ETH);
Expand Down
118 changes: 118 additions & 0 deletions test/middleware/examples/ECDSAVerifier.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.29;

import {
IBasedAppWhitelisted
} from "@ssv/src/middleware/interfaces/IBasedAppWhitelisted.sol";
import { IBasedApp } from "@ssv/test/helpers/Setup.t.sol";
import { UtilsTest } from "@ssv/test/helpers/Utils.t.sol";
import { ICore } from "@ssv/src/core/interfaces/ICore.sol";
import { console, Script } from "forge-std/Script.sol";

contract WhitelistExampleTest is UtilsTest, Script {
function testCreateStrategies() public {
vm.startPrank(USER1);
erc20mock.approve(address(proxiedManager), INITIAL_USER1_BALANCE_ERC20);
erc20mock2.approve(
address(proxiedManager),
INITIAL_USER1_BALANCE_ERC20
);
uint32 strategyId1 = proxiedManager.createStrategy(
STRATEGY1_INITIAL_FEE,
""
);
assertEq(strategyId1, STRATEGY1, "Should set the correct strategy ID");
(address owner, uint32 delegationFeeOnRewards) = proxiedManager
.strategies(strategyId1);
assertEq(owner, USER1, "Should set the correct strategy owner");
assertEq(
delegationFeeOnRewards,
STRATEGY1_INITIAL_FEE,
"Should set the correct strategy fee"
);
vm.stopPrank();
vm.prank(USER2);
uint32 strategyId2 = proxiedManager.createStrategy(
STRATEGY1_INITIAL_FEE,
""
);
}

function testRegisterECDSAVerifierExampleBApp() public {
vm.startPrank(USER1);
ICore.TokenConfig[] memory tokenConfigsInput = createSingleTokenConfig(
address(erc20mock),
102
);
ecdsaVerifierExample.registerBApp(tokenConfigsInput, "");
checkBAppInfo(
tokenConfigsInput,
address(ecdsaVerifierExample),
proxiedManager
);
vm.stopPrank();
}

function testRevertOptInToBAppWithUnauthorizedCaller() public {
vm.prank(USER1);
(
address[] memory tokensInput,
uint32[] memory riskLevelInput
) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000);
vm.expectRevert(
abi.encodeWithSelector(IBasedApp.UnauthorizedCaller.selector)
);
ecdsaVerifierExample.optInToBApp(
STRATEGY1,
tokensInput,
riskLevelInput,
""
);
}

function testOptInToBApp() public {
testCreateStrategies();
testRegisterECDSAVerifierExampleBApp();
(
address[] memory tokensInput,
uint32[] memory riskLevelInput
) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000);
address signer = 0x763569566a3CE4f8D73f96c4996aBdf297f74ADE;
bytes32 messageHash = 0x5b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed86;
bytes
memory signature = hex"ddc30e871857b9a4d2dce47f49aec426404e69c98e05abc345df2f096e47fcb33d3e061da2435584d92df8731522d35ae485447311eb4a3c0bfdb09150cbad081b";

bytes memory data = abi.encode(signer, messageHash, signature);
vm.prank(USER1);
proxiedManager.optInToBApp(
STRATEGY1,
address(ecdsaVerifierExample),
tokensInput,
riskLevelInput,
data
);
}

function testReplayAttack() public {
testOptInToBApp();
(
address[] memory tokensInput,
uint32[] memory riskLevelInput
) = createSingleTokenAndSingleRiskLevel(address(erc20mock), 10_000);
address signer = 0x763569566a3CE4f8D73f96c4996aBdf297f74ADE;
bytes32 messageHash = 0x5b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed86;
bytes
memory signature = hex"ddc30e871857b9a4d2dce47f49aec426404e69c98e05abc345df2f096e47fcb33d3e061da2435584d92df8731522d35ae485447311eb4a3c0bfdb09150cbad081b";

bytes memory data = abi.encode(signer, messageHash, signature);
vm.prank(USER2);
vm.expectRevert();
proxiedManager.optInToBApp(
STRATEGY2,
address(ecdsaVerifierExample),
tokensInput,
riskLevelInput,
data
);
}
}
4 changes: 2 additions & 2 deletions test/middleware/examples/WhitelistExample.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ contract WhitelistExampleTest is UtilsTest {
assertEq(whitelistExample.isWhitelisted(STRATEGY1), false);
}

function testRevertOddWhitelistedAccount() public {
function testRevertAddWhitelistedAccount() public {
testAddWhitelistedAccount();
vm.prank(USER1);
vm.expectRevert(
Expand All @@ -136,7 +136,7 @@ contract WhitelistExampleTest is UtilsTest {
whitelistExample.removeWhitelisted(STRATEGY1);
}

function testRevertOddWhitelistedZeroID() public {
function testRevertAddWhitelistedZeroID() public {
vm.prank(USER1);
vm.expectRevert(
abi.encodeWithSelector(IBasedAppWhitelisted.ZeroID.selector)
Expand Down
7 changes: 7 additions & 0 deletions test/middleware/examples/client/a.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
✅ Generated test input:
Signer Address : 0x86266729219486B9Fb9181ade660d2B4B286Daa8
Message : Hello, Ethereum!
Message Hash : 0x5b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed86
Signature : 0xad99db4ff3eea3e20e0d23a3c63eda2fbe6d05f60aee19e1f8855dd8cd6e06bc0ca32d083d9f9b8f49910a48bb006edb5f44355006a33ecae1a6d1b5035706a31c
ABI Encoded Payload: 0x00000000000000000000000086266729219486b9fb9181ade660d2b4b286daa85b001f2ad81fe86899545b51f8ecd1ca08674437d5c4748e1b70ba5dcf85ed8600000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041ad99db4ff3eea3e20e0d23a3c63eda2fbe6d05f60aee19e1f8855dd8cd6e06bc0ca32d083d9f9b8f49910a48bb006edb5f44355006a33ecae1a6d1b5035706a31c00000000000000000000000000000000000000000000000000000000000000
res: 0x0A823667Ea010859244c74e7915a32C2BFb59c08
122 changes: 122 additions & 0 deletions test/middleware/examples/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions test/middleware/examples/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "client",
"version": "1.0.0",
"main": "sig.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"ethers": "^6.14.3"
}
}
Loading