Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add natspec documentation #58

Merged
merged 20 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ EXOCORE_GENESIS_PRIVATE_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3f
USE_ENDPOINT_MOCK=true
USE_EXOCORE_PRECOMPILE_MOCK=true

OPERATOR_KEYS=
VALIDATOR_KEYS=
EXO_ADDRESSES=
NAMES=
CONS_KEYS=
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/slither.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: crytic/[email protected]
with:
fail-on: medium
2 changes: 1 addition & 1 deletion docs/architecture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
382 changes: 143 additions & 239 deletions docs/client-chain-contracts-design.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/native_deposit_workflow.wsd
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fork again
:Store the request details in registeredRequests and registeredRequestActions;

:Encode the request action arguments;
:Send the request action to ExoCore using _sendMsgToExocore();
:Send the request action to Exocore using _sendMsgToExocore();

stop

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ pragma solidity ^0.8.19;

import {Bootstrap} from "../src/core/Bootstrap.sol";
import {Vault} from "../src/core/Vault.sol";
import {IOperatorRegistry} from "../src/interfaces/IOperatorRegistry.sol";
import {IValidatorRegistry} from "../src/interfaces/IValidatorRegistry.sol";

import {ERC20PresetFixedSupply} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol";

import "forge-std/Script.sol";

// This script does not intentionally inherit from BaseScript, since
// that script has boilerplate that is not needed here.
contract RegisterOperatorsAndDelegate is Script {
contract RegisterValidatorsAndDelegate is Script {

uint256 primaryKey;
// registration data for operators
uint256[] operatorKeys;
// registration data for validators
uint256[] validatorKeys;
string[] exoAddresses;
string[] names;
bytes32[] consKeys;
Expand All @@ -34,7 +34,7 @@ contract RegisterOperatorsAndDelegate is Script {

function setUp() public {
primaryKey = vm.envUint("TEST_ACCOUNT_THREE_PRIVATE_KEY");
operatorKeys = vm.envUint("OPERATOR_KEYS", ",");
validatorKeys = vm.envUint("VALIDATOR_KEYS", ",");
exoAddresses = vm.envString("EXO_ADDRESSES", ",");
names = vm.envString("NAMES", ",");
consKeys = vm.envBytes32("CONS_KEYS", ",");
Expand All @@ -43,9 +43,9 @@ contract RegisterOperatorsAndDelegate is Script {
clientChain = vm.createSelectFork(clientChainRPCURL);

require(
operatorKeys.length == exoAddresses.length && operatorKeys.length == names.length
&& operatorKeys.length == consKeys.length,
"Operator registration data length mismatch"
validatorKeys.length == exoAddresses.length && validatorKeys.length == names.length
&& validatorKeys.length == consKeys.length,
"Validator registration data length mismatch"
);

string memory deployedContracts = vm.readFile("script/deployedBootstrapOnly.json");
Expand All @@ -57,20 +57,20 @@ contract RegisterOperatorsAndDelegate is Script {

function run() public {
vm.selectFork(clientChain);
IOperatorRegistry.Commission memory commission = IOperatorRegistry.Commission(0, 1e18, 1e18);
IValidatorRegistry.Commission memory commission = IValidatorRegistry.Commission(0, 1e18, 1e18);
Bootstrap bootstrap = Bootstrap(bootstrapAddr);
ERC20PresetFixedSupply token = ERC20PresetFixedSupply(tokenAddr);
address vaultAddr = address(bootstrap.tokenToVault(tokenAddr));
for (uint256 i = 0; i < operatorKeys.length; i++) {
uint256 pk = operatorKeys[i];
for (uint256 i = 0; i < validatorKeys.length; i++) {
uint256 pk = validatorKeys[i];
address addr = vm.addr(pk);
console.log(i, addr);
string memory exoAddr = exoAddresses[i];
string memory name = names[i];
bytes32 consKey = consKeys[i];
vm.startBroadcast(pk);
// register operator
bootstrap.registerOperator(exoAddr, name, commission, consKey);
// register validator
bootstrap.registerValidator(exoAddr, name, commission, consKey);
vm.stopBroadcast();
// give them the balance
vm.startBroadcast(primaryKey);
Expand All @@ -89,15 +89,15 @@ contract RegisterOperatorsAndDelegate is Script {
bootstrap.deposit(tokenAddr, depositAmount);
vm.stopBroadcast();
}
for (uint256 i = 0; i < operatorKeys.length; i++) {
uint256 pk = operatorKeys[i];
for (uint256 i = 0; i < validatorKeys.length; i++) {
uint256 pk = validatorKeys[i];
vm.startBroadcast(pk);
for (uint256 j = 0; j < operatorKeys.length; j++) {
for (uint256 j = 0; j < validatorKeys.length; j++) {
uint256 amount = amounts[i][j];
if (amount == 0) {
continue;
}
// i is the transaction sender and j is the operator
// i is the transaction sender and j is the validator
string memory exoAddr = exoAddresses[j];
bootstrap.delegateTo(exoAddr, tokenAddr, amount);
}
Expand Down
88 changes: 44 additions & 44 deletions script/integration/1_DeployBootstrap.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import "../../src/core/BeaconProxyBytecode.sol";
import {Bootstrap} from "../../src/core/Bootstrap.sol";
import {CustomProxyAdmin} from "../../src/core/CustomProxyAdmin.sol";
import {Vault} from "../../src/core/Vault.sol";
import {IOperatorRegistry} from "../../src/interfaces/IOperatorRegistry.sol";
import {IValidatorRegistry} from "../../src/interfaces/IValidatorRegistry.sol";
import {IVault} from "../../src/interfaces/IVault.sol";
import {MyToken} from "../../test/foundry/unit/MyToken.sol";

Expand All @@ -31,8 +31,8 @@ contract DeployContracts is Script {
uint16 exocoreChainId = 1;
uint16 clientChainId = 2;
address exocoreValidatorSet = vm.addr(uint256(0x8));
// assumes 3 operators, to add more - change registerOperators and delegate.
uint256[] operators;
// assumes 3 validators, to add more - change registerValidators and delegate.
uint256[] validators;
uint256[] stakers;
uint256 contractDeployer;
Bootstrap bootstrap;
Expand All @@ -55,10 +55,10 @@ contract DeployContracts is Script {

function setUp() private {
// these are default values for Anvil's usual mnemonic.
uint256[] memory ANVIL_OPERATORS = new uint256[](3);
ANVIL_OPERATORS[0] = uint256(0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80);
ANVIL_OPERATORS[1] = uint256(0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d);
ANVIL_OPERATORS[2] = uint256(0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a);
uint256[] memory ANVIL_VALIDATORS = new uint256[](3);
ANVIL_VALIDATORS[0] = uint256(0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80);
ANVIL_VALIDATORS[1] = uint256(0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d);
ANVIL_VALIDATORS[2] = uint256(0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a);

uint256[] memory ANVIL_STAKERS = new uint256[](7);
ANVIL_STAKERS[0] = uint256(0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6);
Expand All @@ -75,22 +75,22 @@ contract DeployContracts is Script {

uint256 CONTRACT_DEPLOYER = uint256(0xf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897);

operators = vm.envOr("KEY_OPERATORS", ",", ANVIL_OPERATORS);
stakers = vm.envOr("KEY_STAKERS", ",", ANVIL_STAKERS);
tokenDeployers = vm.envOr("KEY_TOKEN_DEPLOYERS", ",", ANVIL_TOKEN_DEPLOYERS);
contractDeployer = vm.envOr("KEY_DEPLOYER", CONTRACT_DEPLOYER);
validators = vm.envOr("ANVIL_VALIDATORS", ",", ANVIL_VALIDATORS);
stakers = vm.envOr("ANVIL_STAKERS", ",", ANVIL_STAKERS);
tokenDeployers = vm.envOr("ANVIL_TOKEN_DEPLOYERS", ",", ANVIL_TOKEN_DEPLOYERS);
contractDeployer = vm.envOr("CONTRACT_DEPLOYER", CONTRACT_DEPLOYER);
}

function deployTokens() private {
string[2] memory names = ["MyToken1", "MyToken2"];
string[2] memory symbols = ["MT1", "MT2"];
uint256[2] memory initialBalances = [2000 * 10 ** decimals[0], 5000 * 10 ** decimals[1]];
address[] memory initialAddresses = new address[](operators.length + stakers.length);
for (uint256 i = 0; i < operators.length; i++) {
initialAddresses[i] = vm.addr(operators[i]);
address[] memory initialAddresses = new address[](validators.length + stakers.length);
for (uint256 i = 0; i < validators.length; i++) {
initialAddresses[i] = vm.addr(validators[i]);
}
for (uint256 i = 0; i < stakers.length; i++) {
initialAddresses[operators.length + i] = vm.addr(stakers[i]);
initialAddresses[validators.length + i] = vm.addr(stakers[i]);
}
for (uint256 i = 0; i < tokenDeployers.length; i++) {
vm.startBroadcast(tokenDeployers[i]);
Expand Down Expand Up @@ -142,15 +142,15 @@ contract DeployContracts is Script {
}

function approveAndDeposit() private {
// amounts deposited by each operator, for the tokens 1 and 2.
uint256[2] memory operatorAmounts = [1500 * 10 ** decimals[0], 2000 * 10 ** decimals[1]];
// amounts deposited by each validators, for the tokens 1 and 2.
uint256[2] memory validatorAmounts = [1500 * 10 ** decimals[0], 2000 * 10 ** decimals[1]];
// stakerAmounts - keep divisible by 3 for delegate
uint256[2] memory stakerAmounts = [300 * 10 ** decimals[0], 600 * 10 ** decimals[1]];
for (uint256 i = 0; i < whitelistTokens.length; i++) {
for (uint256 j = 0; j < operators.length; j++) {
vm.startBroadcast(operators[j]);
for (uint256 j = 0; j < validators.length; j++) {
vm.startBroadcast(validators[j]);
MyToken(whitelistTokens[i]).approve(address(vaults[i]), type(uint256).max);
bootstrap.deposit(whitelistTokens[i], operatorAmounts[i]);
bootstrap.deposit(whitelistTokens[i], validatorAmounts[i]);
vm.stopBroadcast();
}
}
Expand All @@ -164,7 +164,7 @@ contract DeployContracts is Script {
}
}

function registerOperators() private {
function registerValidators() private {
// the mnemonics corresponding to the consensus public keys are given here. to recover,
// echo "${MNEMONIC}" | exocored init localnet --chain-id exocorelocal_233-1 --recover
// the value in this script is this one
Expand All @@ -176,7 +176,7 @@ contract DeployContracts is Script {
"exo1wnw7zcl9fy04ax69uffumwkdxftfqsjyj37wt2",
"exo1rtg0cgw94ep744epyvanc0wdd5kedwql73vlmr"
];
string[3] memory names = ["operator1", "operator2", "operator3"];
string[3] memory names = ["validator1", "validator2", "validator3"];
bytes32[3] memory pubKeys = [
// wonder quality resource ketchup occur stadium vicious output situate plug second
// monkey harbor vanish then myself primary feed earth story real soccer shove like
Expand All @@ -188,20 +188,20 @@ contract DeployContracts is Script {
// wise sister language work muscle parade dad angry across emerge trade
bytes32(0x4C9DE94E1F3225906602AE812E30F1BE56427126D60F2F6CB661B7F4FDA638DC)
];
IOperatorRegistry.Commission memory commission = IOperatorRegistry.Commission(0, 1e18, 1e18);
for (uint256 i = 0; i < operators.length; i++) {
vm.startBroadcast(operators[i]);
bootstrap.registerOperator(exos[i], names[i], commission, pubKeys[i]);
IValidatorRegistry.Commission memory commission = IValidatorRegistry.Commission(0, 1e18, 1e18);
for (uint256 i = 0; i < validators.length; i++) {
vm.startBroadcast(validators[i]);
bootstrap.registerValidator(exos[i], names[i], commission, pubKeys[i]);
vm.stopBroadcast();
}
}

function delegate() private {
// operator delegations. i used these values so that we have a mix of operators
// delegating amongst themselves and to other operators. i also set it up such that
// validator delegations. i used these values so that we have a mix of validators
// delegating amongst themselves and to other validators. i also set it up such that
// the amount for each self delegation is non zero, although that is not validated
// in the contract.
uint256[3][3][2] memory operatorDelegations = [
uint256[3][3][2] memory validatorDelegations = [
[
[200 * 10 ** decimals[0], 50 * 10 ** decimals[0], 50 * 10 ** decimals[0]],
[0 * 10 ** decimals[0], 300 * 10 ** decimals[0], 0 * 10 ** decimals[0]],
Expand All @@ -214,40 +214,40 @@ contract DeployContracts is Script {
]
];
for (uint256 i = 0; i < whitelistTokens.length; i++) {
for (uint256 j = 0; j < operators.length; j++) {
uint256 delegator = operators[j];
for (uint256 k = 0; k < operators.length; k++) {
uint256 amount = operatorDelegations[i][j][k];
address operator = vm.addr(operators[k]);
string memory operatorExo = bootstrap.ethToExocoreAddress(operator);
for (uint256 j = 0; j < validators.length; j++) {
uint256 delegator = validators[j];
for (uint256 k = 0; k < validators.length; k++) {
uint256 amount = validatorDelegations[i][j][k];
address validator = vm.addr(validators[k]);
string memory validatorExo = bootstrap.ethToExocoreAddress(validator);
vm.startBroadcast(delegator);
if (amount != 0) {
bootstrap.delegateTo(operatorExo, whitelistTokens[i], amount);
bootstrap.delegateTo(validatorExo, whitelistTokens[i], amount);
}
vm.stopBroadcast();
}
}
}
// now i have N stakers, with N operators and 2 tokens.
// now i have N stakers, with N validators and 2 tokens.
// i will take 1/3 and 2/3 of the deposit amounts for each token for each staker
// respectively
// find a random number for those amounts for each operators
// find a random number for those amounts for each validators
// op1 = random1, op2 = random2, op3 = 1/3 - random1 - random2
for (uint256 i = 0; i < whitelistTokens.length; i++) {
for (uint256 j = 0; j < stakers.length; j++) {
uint256 delegator = stakers[j];
address delegatorAddress = vm.addr(delegator);
uint256 deposit = bootstrap.totalDepositAmounts(delegatorAddress, whitelistTokens[i]);
uint256 stakerDelegationToDo = (deposit * (i + 1)) / 3;
for (uint256 k = 0; k < operators.length; k++) {
for (uint256 k = 0; k < validators.length; k++) {
uint256 amount;
if (k == operators.length - 1) {
if (k == validators.length - 1) {
amount = stakerDelegationToDo;
} else {
amount = random(stakerDelegationToDo);
}
address operator = vm.addr(operators[k]);
string memory exo = bootstrap.ethToExocoreAddress(operator);
address validator = vm.addr(validators[k]);
string memory exo = bootstrap.ethToExocoreAddress(validator);
vm.startBroadcast(delegator);
bootstrap.delegateTo(exo, whitelistTokens[i], amount);
stakerDelegationToDo -= amount;
Expand All @@ -267,8 +267,8 @@ contract DeployContracts is Script {
console.log("Contract deployed");
approveAndDeposit();
console.log("Approved and deposited");
registerOperators();
console.log("Operators registered");
registerValidators();
console.log("Validators registered");
delegate();
console.log("[Delegated]; done!");

Expand Down
16 changes: 16 additions & 0 deletions src/core/BaseRestakingController.sol
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {IBaseRestakingController} from "../interfaces/IBaseRestakingController.sol";
Expand All @@ -10,6 +11,11 @@ import {OptionsBuilder} from "@layerzero-v2/oapp/contracts/oapp/libs/OptionsBuil
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

/// @title BaseRestakingController
/// @author ExocoreNetwork
/// @notice The base contract for the restaking controller. It only controls ERC20 tokens.
/// @dev This contract is abstract because it does not call the base contract's constructor. It is not used by
/// Bootstrap.
abstract contract BaseRestakingController is
PausableUpgradeable,
ReentrancyGuardUpgradeable,
Expand All @@ -22,6 +28,7 @@ abstract contract BaseRestakingController is

receive() external payable {}

/// @inheritdoc IBaseRestakingController
function claim(address token, uint256 amount, address recipient)
external
isTokenWhitelisted(token)
Expand All @@ -41,6 +48,7 @@ abstract contract BaseRestakingController is
emit ClaimSucceeded(token, recipient, amount);
}

/// @inheritdoc IBaseRestakingController
function delegateTo(string calldata operator, address token, uint256 amount)
external
payable
Expand All @@ -56,6 +64,7 @@ abstract contract BaseRestakingController is
_processRequest(Action.REQUEST_DELEGATE_TO, actionArgs, encodedRequest);
}

/// @inheritdoc IBaseRestakingController
function undelegateFrom(string calldata operator, address token, uint256 amount)
external
payable
Expand All @@ -71,6 +80,10 @@ abstract contract BaseRestakingController is
_processRequest(Action.REQUEST_UNDELEGATE_FROM, actionArgs, encodedRequest);
}

/// @dev Processes the request by sending it to Exocore.
/// @param action The action to be performed.
/// @param actionArgs The encodePacked arguments for the action.
/// @param encodedRequest The encoded request.
function _processRequest(Action action, bytes memory actionArgs, bytes memory encodedRequest) internal {
outboundNonce++;
_registeredRequests[outboundNonce] = encodedRequest;
Expand All @@ -79,6 +92,9 @@ abstract contract BaseRestakingController is
_sendMsgToExocore(action, actionArgs);
}

/// @dev Sends a message to Exocore.
/// @param action The action to be performed.
/// @param actionArgs The encodePacked arguments for the action.
function _sendMsgToExocore(Action action, bytes memory actionArgs) internal {
bytes memory payload = abi.encodePacked(action, actionArgs);
bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(
Expand Down
Loading
Loading