该文档提出了一个修改版的 EIP-1559 提案,旨在将一部分原本销毁的基础费用分配给在给定区块内消耗 gas 的合约部署者,为开发者和那些能够吸引大量网络活动的参与者提供协议级别的补贴,通过 CSR NFT 来认领未来产生的费用。
CIP | 标题 | 作者 | 状态 | 类型 | 类别 | 创建时间 |
---|---|---|---|---|---|---|
001 | 合约担保收入 | Zak Cole (@0xzak), Scott Lewis (@scott_lew_is) | 草案 | 标准跟踪 | 核心 | 2022-08-29 |
EIP-1559 的修改版本,它保留了瞬时拥塞管理的好处,并提供了一种新颖的经济机制,允许合约创建者获得其合约在给定区块内消耗的 gas 费用的一部分。
本文档是一个正在进行的工作。如果你想贡献,请在相应的存储库中提交 pull request。
我们提出了一种新颖的经济机制,该机制修改了 EIP-1559,将总 base fee(否则将被销毁的金额)的一部分分配给在给定区块内消耗 gas 的合约的部署者。这为开发者和那些负责吸引大量网络活动的使用者提供了一种协议级别的补贴。我们的目标是以尽可能少的更改来实现 CSR 协议,使其与现有的 EIP-1559 规范保持一致,同时提供简单而灵活的用户体验。
会计师(Accountant)存在于协议层,应实现为 Cosmos SDK 模块。由于这些流程将负责维护网络状态的重要部分,因此必须在共识层上实现。
在应用层,CSR 程序作为一系列智能合约运行,负责生成和维护符合条件的合约地址的注册表。作为合约创建者,参与是自愿选择的。如果合约创建者选择部署启用 CSR 的合约,他们必须集成对 CSR 旋转栅门(Turnstile)的支持,如下节所述。部署启用 CSR 的合约后,合约创建者将获得一个 CSR NFT。此 NFT 充当所有未来应计费用的声明票。
Turnstile.sol
是一个智能合约,代表会计师注册其他智能合约。
为了选择加入 CSR 计划并铸造 CSR NFT,合约部署交易必须调用 Turnstile.sol
上的 register
函数,以将新部署的合约地址附加到会计师的注册表中。
注册默认将新的 NFT 作为 beneficiary
铸造,并将该 NFT 发送到 fromAddr
,但可以调用该函数来分配现有的 NFT 作为受益人或将新铸造的 NFT 发送到另一个地址。
beneficiary
是接收累计费用的 NFT。
turnstile.register(nftID = None, nftRecipient = None)
### 如果未指定现有的 `nftID` 作为受益人,则会触发将新 NFT 铸造到 `msg.sender` 的默认情况。
if nftID == None:
铸造一个具有下一个未使用的 nftID 的 NFT
如果指定了 `nftRecipient`,则将 NFT 发送到 `nftRecipient`,如果 nftRecipient 为 None,则发送到 fromAddr。
将 (合约地址: 下一个可用的 nftID) 键值对添加到注册表
else:
### 使现有的 NFT 成为新部署的合约的受益人
将 (contractAddress:nftID) 键值对添加到注册表
turnstile.deploy(智能合约, nftID = None, nftRecipient = None)
### deploy 是一个辅助函数,让开发者可以轻松地在他们的部署交易中使用
turnstile.register 。
在链上部署合约
调用 turnstile.register(nftID, nftRecipient)
当给定的交易在一个区块中被密封时,协议会解析交易数据以查找 toAddr
,然后找到注册表中存在的 nftID beneficiaries,并将 20% 的已销毁 base fee 累积到这些 nftID。NFT 的所有者可以随时声明此费用总额。当所有者从已销毁的 base_fee
中提取累积的 gas 时,他们可以指定一个小于或等于可用余额的提款金额(默认为可用余额),以及不同于他们自己的接收地址,提款金额将发送到该地址。如果在调用提款时未指定地址,则 NFT 的所有者将收到累积的费用。提款后的累积余额设置为零。
Cosmos(客户端)侧在状态中存储以下数据结构:
message CSR {
// Contracts 是注册到此 NFT 的所有 EVM 地址的列表
repeated string contracts = 1;
// The NFT id which this CSR corresponds to // 此 CSR 对应的 NFT id
uint64 id = 2;
// The total number of transactions for this CSR NFT // 此 CSR NFT 的总交易数
uint64 txs = 3;
// The cumulative revenue for this CSR NFT // 此 CSR NFT 的累计收入
string revenue = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}
x/csr
模块(客户端)设计用于处理 2 种类型的状态转换:Register
和 Assign
。这些状态转换通过 Turnstile 智能合约上的一组方法触发。这些方法中的每一种都会发出一个事件,该事件随后在 csr 模块的 PostTxProcessing Hook中进行解析。
PostTxProcessing Hook实现 EvmHooks.PostTxProcessing。EVM Hook允许用户利用 Turnstile 智能合约来注册智能合约并将其分配给 CSR NFT + 为已注册到某些 NFT 的合约分配交易费用。在每次成功的 EVM 交易之后,PostTxProcessing Hook将检查 tx 中发出的任何事件是否来自 Turnstile 地址。如果存在某些事件,则事件处理程序将处理并相应地更新状态。在Hook的最后,Hook将检查 tx 中的 To
地址是否属于当前状态中的任何 NFT。如果是,则将拆分费用并将其分配给 Turnstile 地址 / NFT。
func (h Hooks) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
// Check if the csr module has been enabled // 检查是否已启用 csr 模块
params := h.k.GetParams(ctx)
if !params.EnableCsr {
return nil
}
// Check and process turnstile events if applicable // 检查并处理旋转栅门事件(如果适用)
h.processEvents(ctx, receipt)
// Grab contract to check which NFT it belongs to // 获取合约以检查它属于哪个 NFT
contract := msg.To()
if contract == nil {
return nil
}
nftID, foundNFT := h.k.GetNFTByContract(ctx, contract.String())
if !foundNFT {
return nil
}
csr, found := h.k.GetCSR(ctx, nftID)
if !found {
return sdkerrors.Wrapf(ErrNonexistentCSR, "EVMHook::PostTxProcessing the NFT ID was found but the CSR was not.") // EVMHoo EVMHook::PostTxProcessing 找到了 NFT ID,但未找到 CSR。
}
// Calculate fees to be distributed = intFloor(GasUsed * GasPrice * csrShares) // 计算要分配的费用 = intFloor(GasUsed * GasPrice * csrShares)
fee := sdk.NewIntFromUint64(receipt.GasUsed).Mul(sdk.NewIntFromBigInt(msg.GasPrice()))
csrFee := sdk.NewDecFromInt(fee).Mul(params.CsrShares).TruncateInt()
evmDenom := h.k.evmKeeper.GetParams(ctx).EvmDenom
csrFees := sdk.Coins{{Denom: evmDenom, Amount: csrFee}}
// Send fees from fee collector to module account before distribution // 在分配之前,将费用从费用收集器发送到模块帐户
err := h.k.bankKeeper.SendCoinsFromModuleToModule(ctx, h.k.FeeCollectorName, types.ModuleName, csrFees)
if err != nil {
return sdkerrors.Wrapf(ErrFeeDistribution, "EVMHook::PostTxProcessing failed to distribute fees from fee collector to module, %d", err) // EVMHook::PostTxProcessing 无法将费用从费用收集器分配到模块,%d
}
// Get the turnstile which will receive funds for tx fees // 获取将接收 tx 费用的旋转栅门
turnstileAddress, found := h.k.GetTurnstile(ctx)
if !found {
return sdkerrors.Wrapf(ErrContractDeployments, "Keeper::ProcessEvents the turnstile contract has not been found.") // Keeper::ProcessEvents 未找到旋转栅门合约。
}
// Distribute fees to turnstile contract by NFT ID distributeFees(amount, nftID) // 通过 NFT ID distributeFees(amount, nftID) 将费用分配给旋转栅门合约
amount := csrFee.BigInt()
_, err = h.k.CallMethod(ctx, "distributeFees", contracts.TurnstileContract, types.ModuleAddress, &turnstileAddress, amount, new(big.Int).SetUint64(nftID))
if err != nil {
return sdkerrors.Wrapf(ErrFeeDistribution, "EVMHook::PostTxProcessing failed to distribute fees from module account to turnstile, %d", err) // EVMHook::PostTxProcessing 无法将费用从模块帐户分配到旋转栅门,%d
}
// Update TX count for this NFT // 更新此 NFT 的 TX 计数
csr.Txs += 1
// Update the cumulative revenue accumulated by this NFT // 更新此 NFT 积累的累计收入
csr.Revenue = csr.Revenue.Add(csrFee)
// Store updated CSR // 存储更新的 CSR
h.k.SetCSR(ctx, *csr)
return nil
}
事件按如下方式处理:
func (h Hooks) processEvents(ctx sdk.Context, receipt *ethtypes.Receipt) {
// Get the turnstile address from which state transition events are emitted // 获取从中发出状态转换事件的旋转栅门地址
turnstileAddress, found := h.k.GetTurnstile(ctx)
if !found {
panic(sdkerrors.Wrapf(ErrContractDeployments, "Keeper::ProcessEvents the turnstile contract has not been found.")) // Keeper::ProcessEvents 未找到旋转栅门合约。
}
for _, log := range receipt.Logs {
if len(log.Topics) == 0 {
continue
}
// Only process events that originate from the Turnstile contract // 仅处理来自 Turnstile 合约的事件
eventID := log.Topics[0]
if log.Address == turnstileAddress {
event, err := TurnstileContract.EventByID(eventID)
if err != nil {
h.k.Logger(ctx).Error(err.Error())
}
// switch and process based on the turnstile event type // 根据旋转栅门事件类型进行切换和处理
switch event.Name {
case types.TurnstileEventRegister:
err = h.k.RegisterEvent(ctx, log.Data)
case types.TurnstileEventUpdate:
err = h.k.UpdateEvent(ctx, log.Data)
}
if err != nil {
h.k.Logger(ctx).Error(err.Error())
}
}
}
}
以下是如何处理 Register
事件的代码主体:
func (k Keeper) RegisterEvent(ctx sdk.Context, data []byte) error {
var event types.RegisterCSREvent
// Unpack the data // 解包数据
err := TurnstileContract.UnpackIntoInterface(&event, types.TurnstileEventRegister, data)
if err != nil {
return err
}
// Validate that the contract entered can be registered // 验证输入的合约是否可以注册
err = k.ValidateContract(ctx, event.SmartContract)
if err != nil {
return err
}
// Check that the receiver account exists in the evm store // 检查接收者帐户是否存在于 evm 存储中
if acct := k.evmKeeper.GetAccount(ctx, event.Recipient); acct == nil {
return sdkerrors.Wrapf(ErrNonexistentAcct, "EventHandler::RegisterEvent: account does not exist: %s", event.Recipient) // EventHandler::RegisterEvent: 帐户不存在: %s
}
// Check if this token ID has already been registered // 检查此Token ID 是否已注册
nftID := event.TokenId.Uint64()
_, found := k.GetCSR(ctx, nftID)
if found {
return sdkerrors.Wrapf(ErrDuplicateNFTID, "EventHandler::RegisterEvent: this NFT id has already been registered") // EventHandler::RegisterEvent: 此 NFT ID 已注册
}
// Create CSR object and perform stateless validation // 创建 CSR 对象并执行无状态验证
csr := types.NewCSR(
[]string{event.SmartContract.String()},
nftID,
)
if err := csr.Validate(); err != nil {
return err
}
// Set the CSR in the store // 在存储中设置 CSR
k.SetCSR(ctx, csr)
return nil
}
在首次调用的合约与交易期间调用的底层协议之间添加拆分
- 原文链接: github.com/Canto-Improve...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!