名词解读AVSAVS是任何需要自己的分布式验证语义进行验证的系统,例如侧链、数据可用性层、新虚拟机、守护者网络、预言机网络、桥接器、阈值加密方案、可信执行环境等。每个AVS都有自己的一套合同,这些合同保存与服务功能相关的状态,例如哪些运营商正在运行该服务以及有多少权益在保护该服务。
AVS 是任何需要自己的分布式验证语义进行验证的系统,例如侧链、数据可用性层、新虚拟机、守护者网络、预言机网络、桥接器、阈值加密方案、可信执行环境等。 每个 AVS 都有自己的一套合同,这些合同保存与服务功能相关的状态,例如哪些运营商正在运行该服务以及有多少权益在保护该服务。
LSD 是流动性质押协议的意思。 你质押 ETH 给到Lido。Lido会返还给你 1:1 锚定 ETH 的 stETH 代币。此stETH你可以分享质押的收益,也可以用来兑换ETH,并且还可以参与其他的defi交易。除此之外还可以将stETH质押的EidgenLayer上赚取额外的收益,如下图所示
再质押体系,浅显的理解就是“兼职”,借助再质押体系,以太坊质押网络现在可以单独承接有安全需求的 dAPP,同时仍可以为以太坊主网提供安全保障,并且申领质押奖励,再质押奖励等。 另外再质押出售的是以太坊的安全性,以往的 L2 Rollup 只能依据以太坊区块空间大小定价,表现为 DA 和 Gas Fee,再质押将以太坊安全性标准化,并且将其 “货币化”,以更廉价的方式提供等同以太坊的安全性。
ReStaking出现之前,以 DA 等为例,要么用昂贵但安全的以太坊主网,要么用廉价但不正统的 Celestia 等服务。现在利用再质押,可以一面使用以太坊的安全性,一方面减少费用支出,同时既有的多重质押收益和 LRT 再质押代币的流通功能也不受限。
EigenLayer 是以太坊首个提供可编程信任的通用网络。从技术上讲,它是以太坊上的一组智能合约,以及一组链下节点软件,允许个人质押者、质押服务提供商和 LST 质押者选择运行新的链下分布式系统。
如今,以太坊验证者质押 ETH 是为了做出基于资本的承诺,即他们不会偏离以太坊协议。EigenLayer 扩大了这些质押者可以做出的承诺范围,包括选择积极参与和响应新用例和分布式系统的任务。质押者通过运行额外的节点软件,并授予 EigenLayer 智能合约以可编程的方式对其质押的 ETH 或 LST 施加分布式系统指定的额外削减条件的能力,来选择加入这些系统。
我们讲这些新用例和分布式系统统统称为“主动验证服务”-AVS。 EigenLayer的一个非常强大的优势是,区块提议者可以在以太坊核心共识软件之外做出承诺。无需通过协议升级的情况下尝试区块构建的新想法。
EigenLayer 提供了一种新的池化安全机制,允许模块通过重新质押的 ETH 而不是自己的代币进行保护。具体来说,以太坊验证者可以将其信标链提现凭证设置为 EigenLayer 智能合约,并选择加入基于 EigenLayer 构建的新模块。验证者下载并运行这些模块所需的任何其他节点软件。然后,这些模块能够对选择加入该模块的验证者的质押 ETH 施加额外的削减条件。我们称这种机制为重新质押。
EigenLayer 提供了一种开放市场机制,该机制控制其池化证券如何由验证者提供以及如何由 AVS 使用。EigenLayer 创建了一个市场,验证者可以在其中选择是否加入或退出基于 EigenLayer 构建的每个模块。各种模块将需要充分激励验证者将重新质押的 Eth 分配给他们的模块,并且验证者将帮助确定哪些模块值得分配这种额外的池化证券,因为可能会有额外的削减。EigenLayer 的选择加入动态有两个重要的好处:(1)核心区块链的稳定、保守治理与快速、高效的自由市场治理结构相得益彰,以推出新的辅助功能;(2)选择加入验证使新的区块链模块能够利用验证者之间的异构资源,从而更好地平衡安全性和性能。
希望开发新 AVS 的创新者必须引导新的信任网络才能获得安全性。
由于每个 AVS 都开发了自己的信任池,用户除了向以太坊支付交易费外,还必须向这些池支付费用。费用流的转移导致以太坊的价值泄漏。
质押以保护新 AVS 的验证者必须承担资本成本,这相当于在新的系统中质押的机会成本和价格风险。因此,AVS 必须提供足够高的质押回报才能覆盖这笔成本。对于当今运行的大多数 AVS 来说,质押的资本成本远远超过任何运营成本。例如,考虑一个有 100 亿美元质押的数据可用性层,并假设质押者预期的年百分比回报率 (APR) 为 5%。为了补偿资本成本,这个 AVS 每年需要向质押者偿还至少 5 亿美元。这远远高于与数据存储或网络成本相关的运营成本。
DApps 的信任模型较低。当前的 AVS 生态系统产生了一种非常不受欢迎的安全动态:一般来说,DApp 的任何一个中间件依赖项都可能成为攻击的目标。因此,DApp 的损坏成本(参见第 3.1 节的描述)通常必须被认为不超过破坏其至少一个依赖项的最低成本。参见图 3a 的说明。在一个应用程序依赖于关键模块(例如 Oracle)并用少量股份保护它的世界中,以太坊提供的强大经济安全保障可能意义不大,因为攻击 Oracle 的成本远低于攻击以太坊的成本。
如果没有构造安全池子,攻击者只需要攻击最小的那个点,即有10亿美金就可以达到目的。但是如果构造了安全池子,则攻击者必须要对整个池子的安全资金进行攻击,难度极大的增加。
EigenLayer 为 ETH 质押者提供了几个他们可以参与的额外收入来源,并且由于高度安全的 AVS 生态系统的存在
由于 ETH 质押者在多个服务中重复使用其资本,因此其资本成本得到了摊销。
因为重新质押的资金池更大,信任模型会更好
在此模型中,原生重新质押的单独质押者有两种参与 EigenLayer 的选择:(1)单独质押者可以选择加入 EigenLayer 上的 AVS,并直接为其提供验证服务;或(2)单独质押者可以将 EigenLayer 操作委托给其他实体,同时继续自己为以太坊进行验证。后一种选择允许具有不可升级/轻量级设置的家庭质押者继续为以太坊做出去中心化和抗审查贡献(并且不与任何运营商分享核心以太坊收益),同时通过委托给另一个运营商通过 EigenLayer 获得额外奖励。我们设想将有许多基于 EigenLayer 构建的服务被设计为轻量级,适合不想将其质押委托给其他运营商的家庭质押者。例如,考虑一个去中心化的价格预言机,它在计算上易于运行,但需要高度信任。
EigenLayer 中的委托模型要求将他们的权益委托给运营商。如果他们的运营商没有履行其参与的 EigenLayer 模块中的义务,那么他们存入的权益将受到削减。将他们的权益委托给该运营商的重新持有者也将被削减。因此,EigenLayer 重新持有者应该只委托给有成功履行义务记录的可信运营商。EigenLayer 中没有内置委托激励措施,但其他人可以构建创新的委托框架在 EigenLayer 之上。
一 流动性质押 验证者可以通过质押他们的 LST,来获取额外的收益 二 重新质押 通过将其提款凭证指向 EigenLayer 合约来原生重新质押其质押的 ETH。这相当于 L1 → EigenLayer 收益堆叠 (当然,EigenLayer还有超流体质押。ETH LP质押这里先不展开赘述)
第一步 用户操作质押 第二步 调用StrategyManager操作质押 第三步 系统会根据你的策略和代币,选择不同的StrategyBaseTVLLimits 第四步 StrategyBaseTVLLimits计算出可以获得的份额 第五步 将结算结果存入到StrategyManagerStorage中
function depositIntoStrategy(
IStrategy strategy,
IERC20 token,
uint256 amount
) external onlyWhenNotPaused(PAUSED_DEPOSITS) nonReentrant returns (uint256 shares) {
// 执行内部方法
shares = _depositIntoStrategy(msg.sender, strategy, token, amount);
}
function _depositIntoStrategy(
address staker,
IStrategy strategy,
IERC20 token,
uint256 amount
) internal onlyStrategiesWhitelistedForDeposit(strategy) returns (uint256 shares) {
// 转移代币到指定的策略地址
token.safeTransferFrom(msg.sender, address(strategy), amount);
// 执行deposit操作,计算份额(下面会介绍)
shares = strategy.deposit(token, amount);
// 并把增加的份额加入到质押者的名下
_addShares(staker, strategy, shares);
// 向代理增加份额
delegation.increaseDelegatedShares(staker, strategy, shares);
emit Deposit(staker, token, strategy, shares);
return shares;
}
function deposit(
IERC20 token,
uint256 amount
) external virtual override onlyWhenNotPaused(PAUSED_DEPOSITS) onlyStrategyManager returns (uint256 newShares) {
// 在执行前先调用验证流程
_beforeDeposit(token, amount);
require(token == underlyingToken, "StrategyBase.deposit: Can only deposit underlyingToken");
uint256 priorTotalShares = totalShares;
// 计算需要新增加的份额
uint256 virtualShareAmount = priorTotalShares + SHARES_OFFSET;
uint256 virtualTokenBalance = _tokenBalance() + BALANCE_OFFSET;
uint256 virtualPriorTokenBalance = virtualTokenBalance - amount;
newShares = (amount * virtualShareAmount) / virtualPriorTokenBalance;
// 校验新增份额不能为0,避免异常
require(newShares != 0, "StrategyBase.deposit: newShares cannot be zero");
// 更新total 份额
totalShares = (priorTotalShares + newShares);
return newShares;
}
第一步 用户操作解质押 第二步 调用DelegationManager执行unDelegate操作 第三步 调用StrategyManager执行removeShares(中间涉及到份额移除和资产的提取) 第四步 DelegationManagerStorage
function undelegate(address staker) external onlyWhenNotPaused(PAUSED_ENTER_WITHDRAWAL_QUEUE) returns (bytes32) {
require(isDelegated(staker), "DelegationManager.undelegate: staker must be delegated to undelegate");
address operator = delegatedTo[staker];
require(!isOperator(staker), "DelegationManager.undelegate: operators cannot be undelegated");
require(staker != address(0), "DelegationManager.undelegate: cannot undelegate zero address");
require(
msg.sender == staker ||
msg.sender == operator ||
msg.sender == _operatorDetails[operator].delegationApprover,
"DelegationManager.undelegate: caller cannot undelegate staker"
);
// 根据当前质押者,获取指定的策略和份额
(IStrategy[] memory strategies, uint256[] memory shares)
= getDelegatableShares(staker);
// 更新 StakeRegistry 的操作者
_pushOperatorStakeUpdate(operator);
// 如果操作人员是运营商,需要发送事件
if (msg.sender != staker) {
emit StakerForceUndelegated(staker, operator);
}
// 对staker 进行解代理
emit StakerUndelegated(staker, operator);
delegatedTo[staker] = address(0);
// 执行移除份额的操作
return _removeSharesAndQueueWithdrawal({
staker: staker,
operator: operator,
withdrawer: staker,
strategies: strategies,
shares: shares
});
}
function _removeSharesAndQueueWithdrawal(
address staker,
address operator,
address withdrawer,
IStrategy[] memory strategies,
uint256[] memory shares
) internal returns (bytes32) {
require(staker != address(0), "DelegationManager._removeSharesAndQueueWithdrawal: staker cannot be zero address");
// 依次遍历针对不同策略下,不同份额的逻辑
for (uint256 i = 0; i < strategies.length;) {
// Similar to `isDelegated` logic
if (operator != address(0)) {
_decreaseOperatorShares({
operator: operator,
staker: staker,
strategy: strategies[i],
shares: shares[i]
});
// 更新Operator
_pushOperatorStakeUpdate(operator);
}
// 判断走原生ETH的份额移除 / LST的份额移除
if (strategies[i] == beaconChainETHStrategy) {
/**
* 如果导致质押者的虚拟以太坊链ETH股份降低到0一下,则该笔交易会被回退
* 以防止质押者不当排队撤回过多股份给他们所委托的操作员
*/
eigenPodManager.removeShares(staker, shares[i]);
} else {
// 走LST的份额移除
strategyManager.removeShares(staker, strategies[i], shares[i]);
}
unchecked { ++i; }
}
// nonce值加1
uint256 nonce = cumulativeWithdrawalsQueued[staker];
cumulativeWithdrawalsQueued[staker]++;
Withdrawal memory withdrawal = Withdrawal({
staker: staker,
delegatedTo: operator,
withdrawer: withdrawer,
nonce: nonce,
startBlock: uint32(block.number),
strategies: strategies,
shares: shares
});
bytes32 withdrawalRoot = calculateWithdrawalRoot(withdrawal);
// 存储Withdrawals记录到storage
pendingWithdrawals[withdrawalRoot] = true;
emit WithdrawalQueued(withdrawalRoot, withdrawal);
return withdrawalRoot;
}
function _removeShares(
address staker,
IStrategy strategy,
uint256 shareAmount
) internal returns (bool) {
require(shareAmount != 0, "StrategyManager._removeShares: shareAmount should not be zero!");
//确保提取的份额要小于等于质押的份额
uint256 userShares = stakerStrategyShares[staker][strategy];
require(shareAmount <= userShares, "StrategyManager._removeShares: shareAmount too high");
unchecked {
userShares = userShares - shareAmount;
}
// 更新质押者的份额数量
stakerStrategyShares[staker][strategy] = userShares;
// 如果份额数量为0,则将对应的策略从_removeStrategyFromStakerStrategyList之中移除掉
if (userShares == 0) {
_removeStrategyFromStakerStrategyList(staker, strategy);
// 移除掉了就返回true
return true;
}
// 还有剩余份额,就返回false
return false;
}
至此LST 质押的逻辑到此结束。接下来看看restaking的逻辑
第一步 创建一个EigenPod,并与之绑定 第二步 质押32个ETH,让自己成为验证者 第三步 等待验证器在链上处于活动状态 第四步 将提款地址配置指向EigenPod地址(确认https ://beaconcha.in/validator/[validator_index]#deposits) 第五步 触发操作restaking 第六步 委托给指定的运营商
function createPod() external {
require(!hasPod(msg.sender), "EigenPodManager.createPod: Sender already has a pod");
// 创建一个新的pod实例
_deployPod();
}
function _deployPod() internal onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) returns (IEigenPod) {
// check that the limit of EigenPods has not been hit, and increment the EigenPod count
require(numPods + 1 <= maxPods, "EigenPodManager._deployPod: pod limit reached");
++numPods;
// create the pod
IEigenPod pod = IEigenPod(
Create2.deploy(
0,
bytes32(uint256(uint160(msg.sender))),
// set the beacon address to the eigenPodBeacon and initialize it
abi.encodePacked(beaconProxyBytecode, abi.encode(eigenPodBeacon, ""))
)
);
pod.initialize(msg.sender);
// 与创建完成的pod完成绑定
ownerToPod[msg.sender] = pod;
emit PodDeployed(address(pod), msg.sender);
return pod;
}
function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable {
IEigenPod pod = ownerToPod[msg.sender];
if (address(pod) == address(0)) {
// 如果还没有创建pod,则会创建一个pod
pod = _deployPod();
}
// 质押32个ETH,使验证者成为活跃状态
pod.stake{value: msg.value}(pubkey, signature, depositDataRoot);
}
激活重新质押操作
function activateRestaking()
external
onlyWhenNotPaused(PAUSED_EIGENPODS_VERIFY_CREDENTIALS)
onlyEigenPodOwner
hasNeverRestaked
{
hasRestaked = true;
// 从 pod 中提取所有的资产
_processWithdrawalBeforeRestaking(podOwner);
emit RestakingActivated(podOwner);
}
将资产委托给运营商
function delegateTo(
address operator,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external {
// 进行委托
_delegate(msg.sender, operator, approverSignatureAndExpiry, approverSalt);
}
另外,用户启动检查点的频率不应高于每两周一次(大约)。执行检查点之前等待的时间越长,用户节省的 gas 就越多。无论将证明多少共识奖励,检查点的 gas 成本都是相同的。每个用户都应确定最适合其 gas 成本和重新质押收益需求的间隔。 根据以太坊协议,共识奖励大约每 8 天从信标链转移到您的 EigenPod 一次。检查点间隔超过 8 天不会给用户带来任何好处。
第一步 触发 “队列提款” 第二步 选择需要提取的金额并继续 第三步 检查点证明已启动,使用web3钱包签署交易 第四步 等待托管期结束(为了防止有验证者作恶,然后直接退出) 第五步 完成提款
function withdrawNonBeaconChainETHBalanceWei(
address recipient,
uint256 amountToWithdraw
) external onlyEigenPodOwner {
require(
// 确保提款金额要 小于等于 除信标链外的余额
amountToWithdraw <= nonBeaconChainETHBalanceWei,
"EigenPod.withdrawnonBeaconChainETHBalanceWei: amountToWithdraw is greater than nonBeaconChainETHBalanceWei"
);
nonBeaconChainETHBalanceWei -= amountToWithdraw;
emit NonBeaconChainETHWithdrawn(recipient, amountToWithdraw);
_sendETH_AsDelayedWithdrawal(recipient, amountToWithdraw);
}
function _sendETH_AsDelayedWithdrawal(address recipient, uint256 amountWei) internal {
// 正式进入到提款队列中
delayedWithdrawalRouter.createDelayedWithdrawal{value: amountWei}(podOwner, recipient);
}
function createDelayedWithdrawal(
address podOwner,
address recipient
) external payable onlyEigenPod(podOwner) onlyWhenNotPaused(PAUSED_DELAYED_WITHDRAWAL_CLAIMS) {
require(
recipient != address(0),
"DelayedWithdrawalRouter.createDelayedWithdrawal: recipient cannot be zero address"
);
uint224 withdrawalAmount = uint224(msg.value);
// 将提款信息组合成结构体,保存到mapping中
if (withdrawalAmount != 0) {
DelayedWithdrawal memory delayedWithdrawal = DelayedWithdrawal({
amount: withdrawalAmount,
blockCreated: uint32(block.number)
});
_userWithdrawals[recipient].delayedWithdrawals.push(delayedWithdrawal);
emit DelayedWithdrawalCreated(
podOwner,
recipient,
withdrawalAmount,
_userWithdrawals[recipient].delayedWithdrawals.length - 1
);
}
}
等待提款期结束之后,完成提款操作
function claimDelayedWithdrawals(
address recipient,
uint256 maxNumberOfDelayedWithdrawalsToClaim
) external nonReentrant onlyWhenNotPaused(PAUSED_DELAYED_WITHDRAWAL_CLAIMS) {
_claimDelayedWithdrawals(recipient, maxNumberOfDelayedWithdrawalsToClaim);
}
function _claimDelayedWithdrawals(address recipient, uint256 maxNumberOfDelayedWithdrawalsToClaim) internal {
uint256 amountToSend = 0;
uint256 delayedWithdrawalsCompletedBefore = _userWithdrawals[recipient].delayedWithdrawalsCompleted;
uint256 _userWithdrawalsLength = _userWithdrawals[recipient].delayedWithdrawals.length;
uint256 i = 0;
while (
i < maxNumberOfDelayedWithdrawalsToClaim && (delayedWithdrawalsCompletedBefore + i) < _userWithdrawalsLength
) {
// 声明内存对象
DelayedWithdrawal memory delayedWithdrawal = _userWithdrawals[recipient].delayedWithdrawals[
delayedWithdrawalsCompletedBefore + i
];
// 校验是否已经解禁结束
if (block.number < delayedWithdrawal.blockCreated + withdrawalDelayBlocks) {
break;
}
// 累加需要取款的金额
amountToSend += delayedWithdrawal.amount;
// increment i to account for the delayedWithdrawal being claimed
unchecked {
++i;
}
}
// 记录当前的提款对象,标记为已完成
_userWithdrawals[recipient].delayedWithdrawalsCompleted = delayedWithdrawalsCompletedBefore + i;
// 执行ETH发送
if (amountToSend != 0) {
AddressUpgradeable.sendValue(payable(recipient), amountToSend);
}
emit DelayedWithdrawalsClaimed(recipient, amountToSend, delayedWithdrawalsCompletedBefore + i);
}
我们可以看到,Eidgen Layer 使用的设计模式是,逻辑层和数据层进行分离。这样非常 非常有利于进行后期的升级。我个人看了较多的源码,发现Eidgen Layer的源码设计的非常巧妙, 大家有时间,真的可以好好的读一下。顺便也好好了解一下,在restaking赛道上的领头羊到底是怎么玩的。
https://web3caff.com/zh/archives/85864 https://docs.eigenlayer.xyz/eigenlayer/restaking-guides/restaking-user-guide/native-restaking/ https://docs.eigenlayer.xyz/html/EIGEN_Token_Whitepaper-converted-xodo.html https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/docs/experimental/AVS-Guide.md
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!