Skip to content

Commit b742440

Browse files
feat: <- remove bapp struct for mapping and save cycles
1 parent 0b70feb commit b742440

8 files changed

+3472
-226
lines changed

src/BasedAppManager.sol

+58-76
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ import {OwnableUpgradeable, Initializable} from "@openzeppelin/contracts-upgrade
66
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
77
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
88

9+
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
10+
911
import {ICore} from "./interfaces/ICore.sol";
1012
import {IBasedAppManager} from "./interfaces/IBasedAppManager.sol";
1113

14+
// TODO assumption that sharedRiskLevel can not be 0 when it's set
15+
// todo should check that is not 0, otherwise use a fixed value to identify the "0 value".
16+
// if max cap is 10000, then "0 value" could be 10001
17+
1218
/**
1319
* @title BasedAppManager
1420
* @notice The Core Contract to manage Based Applications, Delegations & Strategies for SSV Based Applications Platform.
@@ -51,7 +57,7 @@ import {IBasedAppManager} from "./interfaces/IBasedAppManager.sol";
5157
* Marco Tabasco
5258
* Riccardo Persiani
5359
*/
54-
contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable, IBasedAppManager {
60+
contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable, ReentrancyGuard, IBasedAppManager {
5561
using SafeERC20 for IERC20;
5662

5763
uint32 public constant MAX_PERCENTAGE = 1e4;
@@ -70,10 +76,15 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
7076
uint256 private _strategyCounter;
7177

7278
/**
73-
* @notice Tracks the bApps created
79+
* @notice Tracks the owners of the bApps
7480
* @dev The bApp is identified with its address
7581
*/
76-
mapping(address bApp => ICore.BApp) public bApps;
82+
mapping(address bApp => address owner) public bAppOwners;
83+
/**
84+
* @notice Tracks the tokens supported by the bApps
85+
* @dev The bApp is identified with its address
86+
*/
87+
mapping(address bApp => mapping(address token => uint32 sharedRiskLevel)) public bAppTokens;
7788
/**
7889
* @notice Tracks the strategies created
7990
* @dev The strategy ID is incremental and unique
@@ -127,26 +138,20 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
127138
*/
128139
mapping(uint256 strategyId => mapping(address token => mapping(address bApp => ICore.ObligationRequest))) public
129140
obligationRequests;
130-
/**
131-
* @notice Tracks all the obligation created in a strategy.
132-
* @dev This value is never decremented. It is used to avoid a new opt in for an obligation that was created before and set to zero.
133-
*/
134-
mapping(uint256 strategyId => mapping(address bApp => uint32 numberOfObligations)) public obligationsCounter;
135141

136142
/// @notice Prevents the initialization of the implementation contract itself during deployment
137143
constructor() {
138144
_disableInitializers();
139145
}
140146

141147
/// @notice Initialize the contract
148+
/// @param owner The owner of the contract
142149
/// @param _maxFeeIncrement The maximum fee increment
143-
function initialize(
144-
uint32 _maxFeeIncrement
145-
) public initializer {
150+
function initialize(address owner, uint32 _maxFeeIncrement) public initializer {
146151
if (_maxFeeIncrement == 0 || _maxFeeIncrement > MAX_PERCENTAGE) {
147152
revert ICore.InvalidMaxFeeIncrement();
148153
}
149-
__Ownable_init(msg.sender);
154+
__Ownable_init(owner);
150155
__UUPSUpgradeable_init();
151156
maxFeeIncrement = _maxFeeIncrement;
152157
emit MaxFeeIncrementSet(maxFeeIncrement);
@@ -168,7 +173,7 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
168173
modifier onlyBAppOwner(
169174
address bApp
170175
) {
171-
if (bApps[bApp].owner != msg.sender) revert ICore.InvalidBAppOwner(msg.sender, bApps[bApp].owner);
176+
if (bAppOwners[bApp] != msg.sender) revert ICore.InvalidBAppOwner(msg.sender, bAppOwners[bApp]);
172177
_;
173178
}
174179

@@ -238,28 +243,24 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
238243
// ********************
239244

240245
/// @notice Function to register a bApp
241-
/// @param owner The address of the owner
242246
/// @param bAppAddress The address of the bApp
243247
/// @param tokens The list of tokens the bApp accepts, can also be empty.
244-
/// @param sharedRiskLevel The shared risk level of the bApp
248+
/// @param sharedRiskLevels The shared risk level of the bApp
249+
/// @param metadataURI The metadata URI of the bApp
245250
function registerBApp(
246-
address owner,
247251
address bAppAddress,
248252
address[] calldata tokens,
249-
uint32 sharedRiskLevel,
253+
uint32[] calldata sharedRiskLevels,
250254
string calldata metadataURI
251255
) external {
252-
ICore.BApp storage bApp = bApps[bAppAddress];
253-
if (bApp.owner != address(0)) revert ICore.BAppAlreadyRegistered();
254-
255-
bApp.owner = owner;
256-
bApp.sharedRiskLevel = sharedRiskLevel;
256+
if (bAppOwners[bAppAddress] != address(0)) revert ICore.BAppAlreadyRegistered();
257+
bAppOwners[bAppAddress] = msg.sender;
257258

258259
for (uint256 i = 0; i < tokens.length; i++) {
259-
bApp.tokens.push(tokens[i]);
260+
bAppTokens[bAppAddress][tokens[i]] = sharedRiskLevels[i];
260261
}
261262

262-
emit BAppRegistered(bAppAddress, owner, msg.sender, metadataURI, tokens);
263+
emit BAppRegistered(bAppAddress, msg.sender, tokens, sharedRiskLevels, metadataURI);
263264
}
264265

265266
/// @notice Function to update the metadata URI of the Based Application
@@ -272,25 +273,20 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
272273
/// @notice Function to add tokens to a bApp
273274
/// @param bAppAddress The address of the bApp
274275
/// @param tokens The list of tokens to add
275-
function addTokensToBApp(address bAppAddress, address[] calldata tokens) external {
276-
ICore.BApp storage bApp = bApps[bAppAddress];
276+
function addTokensToBApp(
277+
address bAppAddress,
278+
address[] calldata tokens,
279+
uint32[] calldata sharedRiskLevels
280+
) external onlyBAppOwner(bAppAddress) {
281+
if (tokens.length != sharedRiskLevels.length) revert ICore.TokensLengthNotMatchingRiskLevels();
277282
for (uint256 i = 0; i < tokens.length; i++) {
278-
for (uint256 j = 0; j < bApp.tokens.length; j++) {
279-
if (bApp.tokens[j] == tokens[i]) revert ICore.TokenAlreadyAddedToBApp(tokens[i]);
280-
}
281-
bApp.tokens.push(tokens[i]);
283+
// todo if (sharedRiskLevels[i] == 0 || sharedRiskLevels[i] > MAX_RISK_VALUE) revert ICore.InvalidPercentage();
284+
if (bAppTokens[bAppAddress][tokens[i]] != 0) revert ICore.TokenAlreadyAddedToBApp(tokens[i]);
285+
bAppTokens[bAppAddress][tokens[i]] = sharedRiskLevels[i];
282286
}
283287
emit BAppTokensUpdated(bAppAddress, tokens);
284288
}
285289

286-
/// @notice Function to get the tokens for a bApp
287-
/// @param bAppAddress The address of the bApp
288-
function getBAppTokens(
289-
address bAppAddress
290-
) external view returns (address[] memory) {
291-
return bApps[bAppAddress].tokens;
292-
}
293-
294290
// ***********************
295291
// ** Section: Strategy **
296292
// ***********************
@@ -312,6 +308,7 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
312308
}
313309

314310
/// @notice Opt-in to a bApp with a list of tokens and obligation percentages
311+
/// @dev checks that each token is supported by the bApp, but not that the obligation is > 0
315312
/// @param strategyId The ID of the strategy
316313
/// @param bApp The address of the bApp
317314
/// @param tokens The list of tokens to opt-in with
@@ -326,14 +323,14 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
326323
) external onlyStrategyOwner(strategyId) {
327324
if (tokens.length != obligationPercentages.length) revert ICore.TokensLengthNotMatchingPercentages();
328325

329-
ICore.BApp storage existingBApp = bApps[bApp];
330-
_matchTokens(tokens, existingBApp.tokens);
326+
//ICore.BApp storage existingBApp = bApps[bApp];
327+
_matchTokens(tokens, bApp);
331328

332329
// Check if a strategy exists for the given bApp.
333330
// It is not possible opt-in to the same bApp twice with the same strategy owner.
334331
if (accountBAppStrategy[msg.sender][bApp] != 0) revert ICore.BAppAlreadyOptedIn();
335332

336-
emit BAppOptedInByStrategy(strategyId, bApp, data);
333+
emit BAppOptedInByStrategy(strategyId, bApp, data, tokens, obligationPercentages);
337334

338335
_setObligations(strategyId, bApp, tokens, obligationPercentages);
339336

@@ -370,7 +367,7 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
370367
/// @param strategyId The ID of the strategy
371368
/// @param token The ERC20 token address
372369
/// @param amount The amount to withdraw
373-
function fastWithdrawERC20(uint256 strategyId, IERC20 token, uint256 amount) external {
370+
function fastWithdrawERC20(uint256 strategyId, IERC20 token, uint256 amount) external nonReentrant {
374371
if (amount == 0) revert ICore.InvalidAmount();
375372
if (usedTokens[strategyId][address(token)] != 0) revert ICore.TokenIsUsedByTheBApp();
376373
if (strategyTokenBalances[strategyId][msg.sender][address(token)] < amount) revert ICore.InsufficientBalance();
@@ -386,7 +383,7 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
386383
/// @notice Withdraw ETH from the strategy
387384
/// @param strategyId The ID of the strategy
388385
/// @param amount The amount to withdraw
389-
function fastWithdrawETH(uint256 strategyId, uint256 amount) external {
386+
function fastWithdrawETH(uint256 strategyId, uint256 amount) external nonReentrant {
390387
if (amount == 0) revert ICore.InvalidAmount();
391388
if (usedTokens[strategyId][ETH_ADDRESS] != 0) revert ICore.TokenIsUsedByTheBApp();
392389
if (strategyTokenBalances[strategyId][msg.sender][ETH_ADDRESS] < amount) revert ICore.InsufficientBalance();
@@ -492,19 +489,15 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
492489
) external onlyStrategyOwner(strategyId) {
493490
if (obligationPercentage > MAX_PERCENTAGE) revert ICore.InvalidPercentage();
494491
if (obligations[strategyId][bApp][token] != 0) revert ICore.ObligationAlreadySet();
495-
if (obligationsCounter[strategyId][bApp] == 0) revert ICore.BAppNotOptedIn();
492+
if (accountBAppStrategy[msg.sender][bApp] != strategyId) revert ICore.BAppNotOptedIn();
496493

497-
address[] storage bAppTokens = bApps[bApp].tokens;
498-
_matchToken(token, bAppTokens);
494+
if (bAppTokens[bApp][token] == 0) revert ICore.TokenNoTSupportedByBApp(token);
499495

500496
if (obligationPercentage != 0) {
501497
usedTokens[strategyId][token] += 1;
502498
obligations[strategyId][bApp][token] = obligationPercentage;
503499
}
504500

505-
accountBAppStrategy[msg.sender][bApp] = strategyId;
506-
obligationsCounter[strategyId][bApp] += 1;
507-
508501
emit ObligationCreated(strategyId, bApp, token, obligationPercentage);
509502
}
510503

@@ -519,10 +512,14 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
519512
address token,
520513
uint32 obligationPercentage
521514
) external onlyStrategyOwner(strategyId) {
522-
if (obligationsCounter[strategyId][bApp] == 0) revert ICore.BAppNotOptedIn();
515+
if (accountBAppStrategy[msg.sender][bApp] != strategyId) revert ICore.BAppNotOptedIn();
523516
if (obligationPercentage > MAX_PERCENTAGE) revert ICore.InvalidPercentage();
524517
if (obligationPercentage <= obligations[strategyId][bApp][token]) revert ICore.InvalidPercentage();
525518

519+
if (obligations[strategyId][bApp][token] == 0 && obligationPercentage > 0) {
520+
usedTokens[strategyId][token] += 1;
521+
}
522+
526523
obligations[strategyId][bApp][token] = obligationPercentage;
527524

528525
emit ObligationUpdated(strategyId, bApp, token, obligationPercentage, true);
@@ -538,8 +535,9 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
538535
address token,
539536
uint32 obligationPercentage
540537
) external onlyStrategyOwner(strategyId) {
541-
if (obligationsCounter[strategyId][bApp] == 0) revert ICore.BAppNotOptedIn();
538+
if (accountBAppStrategy[msg.sender][bApp] != strategyId) revert ICore.BAppNotOptedIn();
542539
if (obligationPercentage > MAX_PERCENTAGE) revert ICore.InvalidPercentage();
540+
if (obligationPercentage == obligations[strategyId][bApp][token]) revert ICore.PercentageAlreadySet();
543541

544542
ICore.ObligationRequest storage request = obligationRequests[strategyId][bApp][token];
545543

@@ -577,13 +575,11 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
577575
revert ICore.UpdateObligationExpired();
578576
}
579577

580-
// Remove the usedToken from the counter, but not the obligation counter.
581-
if (percentage == 0) {
578+
if (percentage == 0 && obligations[strategyId][bApp][address(token)] > 0) {
582579
usedTokens[strategyId][address(token)] -= 1;
583580
}
584581

585-
// If updating an obligation from 0 to greater then increase the usedToken counter.
586-
if (obligations[strategyId][bApp][address(token)] == 0) {
582+
if (obligations[strategyId][bApp][address(token)] == 0 && percentage > 0) {
587583
usedTokens[strategyId][address(token)] += 1;
588584
}
589585

@@ -654,42 +650,28 @@ contract BasedAppManager is Initializable, OwnableUpgradeable, UUPSUpgradeable,
654650
address token = tokens[i];
655651
uint32 obligationPercentage = obligationPercentages[i];
656652

653+
// todo check the bapp mapping and reject if the token is not available
654+
657655
if (obligationPercentage > MAX_PERCENTAGE) revert ICore.InvalidPercentage();
658656

659657
if (obligationPercentage != 0) {
660658
usedTokens[strategyId][token] += 1;
661659
obligations[strategyId][bApp][token] = obligationPercentage;
662660
}
663661

664-
obligationsCounter[strategyId][bApp] += 1;
662+
// obligationsCounter[strategyId][bApp] += 1;
665663

666664
emit ObligationCreated(strategyId, bApp, token, obligationPercentage);
667665
}
668666
}
669667

670668
/// @notice Match the tokens of strategy with the bApp
671-
/// Complexity: O(n * m)
669+
/// Complexity: O(n)
672670
/// @param tokens The list of strategy tokens
673-
/// @param bAppTokens The list of bApp tokens
674-
function _matchTokens(address[] calldata tokens, address[] memory bAppTokens) private pure {
671+
/// @param bApp The bApp address
672+
function _matchTokens(address[] calldata tokens, address bApp) private view {
675673
for (uint256 i = 0; i < tokens.length; i++) {
676-
address token = tokens[i];
677-
_matchToken(token, bAppTokens);
678-
}
679-
}
680-
681-
/// @notice Match the single token of strategy with the bApp token list
682-
/// @param token The strategy token
683-
/// @param bAppTokens The list of bApp tokens
684-
function _matchToken(address token, address[] memory bAppTokens) private pure {
685-
bool matched = false;
686-
for (uint256 i = 0; i < bAppTokens.length; ++i) {
687-
address bAppToken = bAppTokens[i];
688-
if (bAppToken == token) {
689-
matched = true;
690-
break;
691-
}
674+
if (bAppTokens[bApp][tokens[i]] == 0) revert ICore.TokenNoTSupportedByBApp(tokens[i]);
692675
}
693-
if (!matched) revert ICore.TokenNoTSupportedByBApp(token);
694676
}
695677
}

src/interfaces/IBasedAppManager.sol

+14-9
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
55

66
interface IBasedAppManager {
77
event BAppMetadataURIUpdated(address indexed bAppAddress, string metadataURI);
8-
event BAppOptedInByStrategy(uint256 indexed strategyId, address indexed bApp, bytes data);
8+
event BAppOptedInByStrategy(
9+
uint256 indexed strategyId, address indexed bApp, bytes data, address[] tokens, uint32[] obligationPercentages
10+
);
911
event BAppRegistered(
10-
address indexed bAppAddress, address indexed owner, address from, string metadataURI, address[] tokens
12+
address indexed bAppAddress,
13+
address indexed owner,
14+
address[] tokens,
15+
uint32[] sharedRiskLevel,
16+
string metadataURI
1117
);
1218
event BAppTokensUpdated(address indexed bAppAddress, address[] tokens);
1319
event DelegationCreated(address indexed delegator, address indexed receiver, uint32 percentage);
@@ -55,20 +61,19 @@ interface IBasedAppManager {
5561
) external;
5662

5763
function registerBApp(
58-
address owner,
5964
address bAppAddress,
6065
address[] calldata tokens,
61-
uint32 sharedRiskLevel,
66+
uint32[] calldata sharedRiskLevels,
6267
string calldata metadataURI
6368
) external;
6469

6570
function updateMetadataURI(address bAppAddress, string calldata metadataURI) external;
6671

67-
function addTokensToBApp(address bAppAddress, address[] calldata tokens) external;
68-
69-
function getBAppTokens(
70-
address bAppAddress
71-
) external view returns (address[] memory);
72+
function addTokensToBApp(
73+
address bAppAddress,
74+
address[] calldata tokens,
75+
uint32[] calldata sharedRiskLevels
76+
) external;
7277

7378
function createStrategy(
7479
uint32 fee

src/interfaces/ICore.sol

+2-10
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,6 @@
22
pragma solidity 0.8.28;
33

44
interface ICore {
5-
/// @notice Represents an AVS
6-
struct BApp {
7-
/// @dev The owner of the bApp
8-
address owner;
9-
/// @dev The erc20 tokens the bApp accepts (can accept multiple)
10-
address[] tokens;
11-
/// @dev beta parameter
12-
uint32 sharedRiskLevel;
13-
}
14-
155
/// @notice Represents a Strategy
166
struct Strategy {
177
/// @dev The owner of the strategy
@@ -65,10 +55,12 @@ interface ICore {
6555
error NoPendingWithdrawalETH();
6656
error ObligationAlreadySet();
6757
error ObligationTimelockNotElapsed();
58+
error PercentageAlreadySet();
6859
error TokenAlreadyAddedToBApp(address token);
6960
error TokenIsUsedByTheBApp();
7061
error TokenNoTSupportedByBApp(address token);
7162
error TokensLengthNotMatchingPercentages();
63+
error TokensLengthNotMatchingRiskLevels();
7264
error UpdateObligationExpired();
7365
error WithdrawalExpired();
7466
error WithdrawalTimelockNotElapsed();

0 commit comments

Comments
 (0)