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 13 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.
46 changes: 1 addition & 45 deletions docs/client-chain-contracts-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,21 +244,7 @@ Exocore validator set should be the owner of `Controller` so that it can update

```solidity
interface IController {
// @notice Balance update info for specific user indexed by token
struct TokenBalanceUpdateInfo {
address token;
uint256 lastlyUpdatedPrincipalBalance;
uint256 lastlyUpdatedRewardBalance;
uint256 unlockAmount;
}

// @notice this info is used to update specific user's owned tokens balance
struct UserBalanceUpdateInfo {
address user;
uint256 updatedAt;
TokenBalanceUpdateInfo[] tokenInfo;
}


event DepositResult(address indexed depositor, bool indexed success, uint256 amount);
MaxMustermann2 marked this conversation as resolved.
Show resolved Hide resolved
event WithdrawResult(address indexed withdrawer, bool indexed success, uint256 amount);
event DelegateResult(address indexed delegator, address indexed delegatee, bool indexed success, uint256 amount);
Expand Down Expand Up @@ -306,20 +292,6 @@ interface IController {
* @param distination - The destination address that the assets would be transfered to.
*/
function claim(address token, uint256 amount, address recipient) external;

/// *** function signatures for commands of Exocore validator set forwarded by Gateway ***

/**
* @notice This should only be called by Exocore validator set through Gateway to update user's involved
* lastly updated token balance.
* @dev Only Exocore validato set could indirectly call this function through Gateway contract.
* @dev This function could be called in two scenaries:
* 1) Exocore validator set periodically calls this to update user principal and reward balance.
* 2) Exocore validator set sends reponse for the request of withdrawPrincipalFromExocore and unlock part of
* the vault assets and update user's withdrawable balance correspondingly.
* @param info - The info needed for updating users balance.
*/
function updateUsersBalance(UserBalanceUpdateInfo[] calldata info) external;
}
```

Expand Down Expand Up @@ -377,19 +349,3 @@ This function is aimed for user claiming the unlocked amount of principal. Befor
This function should be accessible for any EOA address and contract address.

In aspect of security, we must carefully check against user’s claimable(unlocked) principal balance.

### `updateUsersBalance`

This function should only be called by Exocore validator set through `Gateway` to update user’s `principalBalance`, `rewardBalance` and `withdrawableBalance`.

This function could be called in two scenaries:

1. Exocore validator set periodically calls this to update user principal and reward balance(this should not update user's withdrawable balance).
2. Exocore validator set sends reponse for the request of withdrawPrincipalFromExocore and unlock part of
the vault assets and update user's withdrawable balance correspondingly. User's `principalBalance` and `rewardBalance` will be updated either.

This function should be only accessible for `Gateway` so that this function could only be called by Exocore validator set through `Gateway`.

This function relies on the Exocore set correctly returning the updated `principalBalance`, `rewardBalance` and `unlockAmount`.

Everytime the user is trying to withdraw principal and reward from Exocore chain, this function must be called by Exocore validator set via cross-chain message to correctly update user’s Exocore balance and especially correctly update user’s withdrawable amount on client chain.
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
Loading
Loading