Symbiotic Protocol is an extremely flexible and permissionless shared security system.
This repository contains the core Symbiotic smart contracts responsible for managing deposits, stake allocations and slashing.
Symbiotic core consists of:
-
Collateral: a new type of asset that allows stakeholders to hold onto their funds and earn yield from them without needing to lock these funds in a direct manner or convert them to another type of asset.
-
Vaults: the delegation and restaking management layer of Symbiotic that handles three crucial parts of the Symbiotic economy: accounting, delegation strategies, and reward distribution.
-
Operators: entities running infrastructure for decentralized networks within and outside of the Symbiotic ecosystem.
-
Resolvers: contracts or entities that are able to veto slashing incidents forwarded from networks and can be shared across networks.
-
Networks: any protocols that require a decentralized infrastructure network to deliver a service in the crypto economy, e.g., enabling developers to launch decentralized applications by taking care of validating and ordering transactions, providing off-chain data to applications in the crypto economy, or providing users with guarantees about cross-network interactions, etc.
- Git (installation)
- Foundry (installation)
Clone the repository
git clone --recurse-submodules https://github.com/symbioticfi/core.git
-
If you need a common (non-tokenized) Vault deployment
Open
DeployVault.s.sol, you will see config like this:// Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic address OWNER = 0x0000000000000000000000000000000000000000; // Address of the collateral token address COLLATERAL = 0x0000000000000000000000000000000000000000; // Vault's burner to send slashed funds to (e.g., 0xdEaD or some unwrapper contract; not used in case of no slasher) address BURNER = 0x000000000000000000000000000000000000dEaD; // Duration of the vault epoch (the withdrawal delay for staker varies from EPOCH_DURATION to 2 * EPOCH_DURATION depending on when the withdrawal is requested) uint48 EPOCH_DURATION = 7 days; // Type of the delegator: // 0. NetworkRestakeDelegator (allows restaking across multiple networks and having multiple operators per network) // 1. FullRestakeDelegator (do not use without knowing what you are doing) // 2. OperatorSpecificDelegator (allows restaking across multiple networks with only a single operator) // 3. OperatorNetworkSpecificDelegator (allocates the stake to a specific operator and network) uint64 DELEGATOR_INDEX = 0; // Setting depending on the delegator type: // 0. NetworkLimitSetRoleHolders (adjust allocations for networks) // 1. NetworkLimitSetRoleHolders (adjust allocations for networks) // 2. NetworkLimitSetRoleHolders (adjust allocations for networks) // 3. network (the only network that will receive the stake; should be an array with a single element) address[] NETWORK_ALLOCATION_SETTERS_OR_NETWORK = [0x0000000000000000000000000000000000000000]; // Setting depending on the delegator type: // 0. OperatorNetworkSharesSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares) // 1. OperatorNetworkLimitSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares) // 2. operator (the only operator that will receive the stake; should be an array with a single element) // 3. operator (the only operator that will receive the stake; should be an array with a single element) address[] OPERATOR_ALLOCATION_SETTERS_OR_OPERATOR = [0x0000000000000000000000000000000000000000]; // Whether to deploy a slasher bool WITH_SLASHER = true; // Type of the slasher: // 0. Slasher (allows instant slashing) // 1. VetoSlasher (allows having a veto period if the resolver is set) uint64 SLASHER_INDEX = 1; // Duration of a veto period (should be less than EPOCH_DURATION) uint48 VETO_DURATION = 1 days; // Optional // Deposit limit (maximum amount of the active stake allowed in the vault) uint256 DEPOSIT_LIMIT = 0; // Addresses of the whitelisted depositors address[] WHITELISTED_DEPOSITORS = new address[](0); // Address of the hook contract which, e.g., can automatically adjust the allocations on slashing events (not used in case of no slasher) address HOOK = 0x0000000000000000000000000000000000000000; // Delay in epochs for a network to update a resolver uint48 RESOLVER_SET_EPOCHS_DELAY = 3;
Edit needed fields, and execute the script via:
forge script script/DeployVault.s.sol --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcast -
If you need a tokenized (active deposits' shares are represented as ERC-20) Vault deployment
Open
DeployVaultTokenized.s.sol, you will see config like this:// Name of the ERC20 representing shares of the active stake in the vault string NAME = "SymVault"; // Symbol of the ERC20 representing shares of the active stake in the vault string SYMBOL = "SV"; // Address of the owner of the vault who can migrate the vault to new versions whitelisted by Symbiotic address OWNER = 0x0000000000000000000000000000000000000000; // Address of the collateral token address COLLATERAL = 0x0000000000000000000000000000000000000000; // Vault's burner to send slashed funds to (e.g., 0xdEaD or some unwrapper contract; not used in case of no slasher) address BURNER = 0x000000000000000000000000000000000000dEaD; // Duration of the vault epoch (the withdrawal delay for staker varies from EPOCH_DURATION to 2 * EPOCH_DURATION depending on when the withdrawal is requested) uint48 EPOCH_DURATION = 7 days; // Type of the delegator: // 0. NetworkRestakeDelegator (allows restaking across multiple networks and having multiple operators per network) // 1. FullRestakeDelegator (do not use without knowing what you are doing) // 2. OperatorSpecificDelegator (allows restaking across multiple networks with only a single operator) // 3. OperatorNetworkSpecificDelegator (allocates the stake to a specific operator and network) uint64 DELEGATOR_INDEX = 0; // Setting depending on the delegator type: // 0. NetworkLimitSetRoleHolders (adjust allocations for networks) // 1. NetworkLimitSetRoleHolders (adjust allocations for networks) // 2. NetworkLimitSetRoleHolders (adjust allocations for networks) // 3. network (the only network that will receive the stake; should be an array with a single element) address[] NETWORK_ALLOCATION_SETTERS_OR_NETWORK = [0x0000000000000000000000000000000000000000]; // Setting depending on the delegator type: // 0. OperatorNetworkSharesSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares) // 1. OperatorNetworkLimitSetRoleHolders (adjust allocations for operators inside networks; in shares, resulting percentage is operatorShares / totalOperatorShares) // 2. operator (the only operator that will receive the stake; should be an array with a single element) // 3. operator (the only operator that will receive the stake; should be an array with a single element) address[] OPERATOR_ALLOCATION_SETTERS_OR_OPERATOR = [0x0000000000000000000000000000000000000000]; // Whether to deploy a slasher bool WITH_SLASHER = true; // Type of the slasher: // 0. Slasher (allows instant slashing) // 1. VetoSlasher (allows having a veto period if the resolver is set) uint64 SLASHER_INDEX = 1; // Duration of a veto period (should be less than EPOCH_DURATION) uint48 VETO_DURATION = 1 days; // Optional // Deposit limit (maximum amount of the active stake allowed in the vault) uint256 DEPOSIT_LIMIT = 0; // Addresses of the whitelisted depositors address[] WHITELISTED_DEPOSITORS = new address[](0); // Address of the hook contract which, e.g., can automatically adjust the allocations on slashing events (not used in case of no slasher) address HOOK = 0x0000000000000000000000000000000000000000; // Delay in epochs for a network to update a resolver uint48 RESOLVER_SET_EPOCHS_DELAY = 3;
Edit needed fields, and execute the script via:
forge script script/DeployVaultTokenized.s.sol --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcast
In the console, you will see logs like these:
Deployed vault
vault:0x9c9e536A269ec83a0628404e35b2F940d7226c8C
delegator:0xb7a105A294f7E2d399C9692c12ba4cAba90F5AAB
slasher:0x8D2E18628F28660cF75Ca51C85a354d7c8508B59There are 9 predefined action-scripts, that you can use from the start:
- RegisterOperator – register an operator in the
OperatorRegistry - OptInVault – opt-in operator to the vault
- OptInNetwork – opt-in operator to the network
- SetMaxNetworkLimit – set new maximum network limit for the vault
- SetNetworkLimit – set a network limit (how much stake the vault curator is ready to give to the subnetwork)
- SetOperatorNetworkShares – set an operator's shares for a subnetwork (what percentage, which is equal to the shares divided by the total operators' shares, of the subnetwork's stake the vault curator is ready to give to the operator)
- SetHook – configure automation hooks that react to slashing events
- SetResolver – set a new resolver for the vault (only if the vault uses VetoSlasher)
- VetoSlash – veto a pending slash request during the veto period
Interaction with different actions is similar; let's consider SetNetworkLimit as an example:
-
Open SetNetworkLimit.s.sol, you will see config like this:
// Address of the Vault address constant VAULT = address(0); // Address of the Network to set the network limit for address constant NETWORK = address(0); // Subnetwork Identifier uint96 constant IDENTIFIER = 0; // Network limit value to set uint256 constant LIMIT = 0;
-
Edit needed fields, and execute the operation:
-
If you use an EOA and want to execute the script:
forge script script/actions/SetNetworkLimit.s.sol --rpc-url <RPC_URL> --private-key <PRIVATE_KEY> --broadcast
-
If you use a Safe multisig and want to get a transaction calldata:
forge script script/actions/SetMaxNetworkLimit.s.sol --rpc-url <RPC_URL> --sender <MULTISIG_ADDRESS> --unlocked
In the logs, you will see
dataandtargetfields like this:SetNetworkLimit data: data:0x02145348759d4335cb712aa188935c2bd3aa6d205ac613050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 target:0xd6c4b4267BFB908BBdf8C9BDa7d0Ae517aA145b0
In Safe->TransactionBuilder, you should:
- enable "Custom data"
- enter
targetas a target address - use the
data(e.g.,0x02145348759d4335cb712aa188935c2bd3aa6d205ac61305...) received earlier as aData (Hex encoded)
-
Moreover, a Tenderly simulation link is provided as an additional safeguard, e.g.:
Simulation link:
https://dashboard.tenderly.co/TENDERLY_USERNAME/TENDERLY_PROJECT/simulator/new?network=1&contractAddress=0xd6c4b4267BFB908BBdf8C9BDa7d0Ae517aA145b0&from=0x2aCA71020De61bb532008049e1Bd41E451aE8AdC&rawFunctionInput=0x02145348759d4335cb712aa188935c2bd3aa6d205ac613050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000forge build
forge test
forge fmt
Configure environment
Create .env based on the template:
ETH_RPC_URL=
ETHERSCAN_API_KEY=
Security audits are aggregated in ./audits.