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

feat(protocol): allow TAIKO token bonds deposits and withdrawal #17725

Merged
merged 15 commits into from
Jul 4, 2024
8 changes: 8 additions & 0 deletions packages/protocol/contracts/L1/ITaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ interface ITaikoL1 {
/// @param _pause True if paused.
function pauseProving(bool _pause) external;

/// @notice Deposits Taiko token to be used as bonds.
/// @param _amount The amount of Taiko token to deposit.
function depositBond(uint256 _amount) external;

/// @notice Withdraws Taiko token.
/// @param _amount The amount of Taiko token to withdraw.
function withdrawBond(uint256 _amount) external;

/// @notice Gets the configuration of the TaikoL1 contract.
/// @return Config struct containing configuration parameters.
function getConfig() external pure returns (TaikoData.Config memory);
Expand Down
3 changes: 2 additions & 1 deletion packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ library TaikoData {
bytes32 __reserve1;
SlotA slotA; // slot 5
SlotB slotB; // slot 6
uint256[44] __gap;
mapping(address account => uint256 bond) bondBalance;
uint256[43] __gap;
}
}
6 changes: 6 additions & 0 deletions packages/protocol/contracts/L1/TaikoEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import "./TaikoData.sol";
/// L1 libraries.
/// @custom:security-contact [email protected]
abstract contract TaikoEvents {
/// @dev Emitted when token is credited back to a user's bond balance.
event BondCredited(address indexed user, uint256 amount);

/// @dev Emitted when token is debited from a user's bond balance.
event BondDebited(address indexed user, uint256 amount);

/// @dev Emitted when a block is proposed.
/// @param blockId The ID of the proposed block.
/// @param assignedProver The block's assigned prover.
Expand Down
31 changes: 17 additions & 14 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,11 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
returns (TaikoData.BlockMetadata memory meta_, TaikoData.EthDeposit[] memory deposits_)
{
TaikoData.Config memory config = getConfig();
TaikoToken tko = TaikoToken(resolve(LibStrings.B_TAIKO_TOKEN, false));

(meta_, deposits_) = LibProposing.proposeBlock(state, tko, config, this, _params, _txList);
(meta_, deposits_) = LibProposing.proposeBlock(state, config, this, _params, _txList);

if (LibUtils.shouldVerifyBlocks(config, meta_.id, true) && !state.slotB.provingPaused) {
LibVerifying.verifyBlocks(state, tko, config, this, config.maxBlocksToVerify);
LibVerifying.verifyBlocks(state, config, this, config.maxBlocksToVerify);
}
}

Expand All @@ -100,12 +99,10 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
emitEventForClient
{
TaikoData.Config memory config = getConfig();
TaikoToken tko = TaikoToken(resolve(LibStrings.B_TAIKO_TOKEN, false));

LibProving.proveBlock(state, tko, config, this, _blockId, _input);
LibProving.proveBlock(state, config, this, _blockId, _input);

if (LibUtils.shouldVerifyBlocks(config, _blockId, false)) {
LibVerifying.verifyBlocks(state, tko, config, this, config.maxBlocksToVerify);
LibVerifying.verifyBlocks(state, config, this, config.maxBlocksToVerify);
}
}

Expand All @@ -117,13 +114,19 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
nonReentrant
emitEventForClient
{
LibVerifying.verifyBlocks(
state,
TaikoToken(resolve(LibStrings.B_TAIKO_TOKEN, false)),
getConfig(),
this,
_maxBlocksToVerify
);
LibVerifying.verifyBlocks(state, getConfig(), this, _maxBlocksToVerify);
}

function depositBond(uint256 _amount) external {
LibBonds.depositBond(state, this, _amount);
}

function withdrawBond(uint256 _amount) external {
LibBonds.withdrawBond(state, this, _amount);
}

function bondBalanceOf(address _user) external view returns (uint256) {
return LibBonds.bondBalanceOf(state, _user);
}

/// @notice Gets the details of a block.
Expand Down
102 changes: 102 additions & 0 deletions packages/protocol/contracts/L1/libs/LibBonds.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../../common/IAddressResolver.sol";
import "../../common/LibStrings.sol";
import "../TaikoData.sol";

/// @title LibBonds
/// @notice A library that offers helper functions to handle bonds.
/// @custom:security-contact [email protected]
library LibBonds {
/// @dev Emitted when token is credited back to a user's bond balance.
event BondCredited(address indexed user, uint256 amount);

/// @dev Emitted when token is debited from a user's bond balance.
event BondDebited(address indexed user, uint256 amount);

/// @dev Deposits Taiko token to be used as bonds.
/// @param _state Current TaikoData.State.
/// @param _resolver Address resolver interface.
/// @param _amount The amount of token to deposit.
function depositBond(
TaikoData.State storage _state,
IAddressResolver _resolver,
uint256 _amount
)
internal
{
_tko(_resolver).transferFrom(msg.sender, address(this), _amount);
_state.bondBalance[msg.sender] += _amount;
}

/// @dev Withdraws Taiko token.
/// @param _state Current TaikoData.State.
/// @param _resolver Address resolver interface.
/// @param _amount The amount of token to withdraw.
function withdrawBond(
TaikoData.State storage _state,
IAddressResolver _resolver,
uint256 _amount
)
internal
{
_state.bondBalance[msg.sender] -= _amount;
_tko(_resolver).transfer(msg.sender, _amount);
}

/// @dev Debits Taiko tokens as bonds.
/// @param _state Current TaikoData.State.
/// @param _resolver Address resolver interface.
/// @param _user The user address to debit.
/// @param _amount The amount of token to debit.
function debitBond(
TaikoData.State storage _state,
IAddressResolver _resolver,
address _user,
uint256 _amount
)
internal
{
uint256 balance = _state.bondBalance[_user];

if (balance >= _amount) {
unchecked {
_state.bondBalance[_user] = balance - _amount;
}
emit BondDebited(_user, _amount);
} else {
_tko(_resolver).transferFrom(_user, address(this), _amount);
}
}

/// @dev Credits Taiko tokens to user's bond balance.
/// @param _state Current TaikoData.State.
/// @param _user The user address to credit.
/// @param _amount The amount of token to credit.
function creditBond(TaikoData.State storage _state, address _user, uint256 _amount) internal {
_state.bondBalance[_user] += _amount;
emit BondCredited(_user, _amount);
}

/// @dev Gets a user's current Taiko token bond balance.
/// @param _state Current TaikoData.State.
/// @param _user The user address to credit.
/// @return The current token balance.
function bondBalanceOf(
TaikoData.State storage _state,
address _user
)
internal
view
returns (uint256)
{
return _state.bondBalance[_user];
}

function _tko(IAddressResolver _resolver) private view returns (IERC20) {
return IERC20(_resolver.resolve(LibStrings.B_TAIKO_TOKEN, false));
}
}
5 changes: 2 additions & 3 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.24;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "../../libs/LibAddress.sol";
import "../../libs/LibNetwork.sol";
import "./LibBonds.sol";
import "./LibUtils.sol";

/// @title LibProposing
Expand Down Expand Up @@ -53,15 +54,13 @@ library LibProposing {

/// @dev Proposes a Taiko L2 block.
/// @param _state Current TaikoData.State.
/// @param _tko The taiko token.
/// @param _config Actual TaikoData.Config.
/// @param _resolver Address resolver interface.
/// @param _data Encoded data bytes containing the block params.
/// @param _txList Transaction list bytes (if not blob).
/// @return meta_ The constructed block's metadata.
function proposeBlock(
TaikoData.State storage _state,
TaikoToken _tko,
TaikoData.Config memory _config,
IAddressResolver _resolver,
bytes calldata _data,
Expand Down Expand Up @@ -188,7 +187,7 @@ library LibProposing {
++_state.slotB.numBlocks;
}

_tko.transferFrom(msg.sender, address(this), _config.livenessBond);
LibBonds.debitBond(_state, _resolver, msg.sender, _config.livenessBond);

// Bribe the block builder. Unlock 1559-tips, this tip is only made
// if this transaction succeeds.
Expand Down
23 changes: 10 additions & 13 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.24;

import "../../verifiers/IVerifier.sol";
import "./LibBonds.sol";
import "./LibUtils.sol";

/// @title LibProving
Expand All @@ -16,7 +17,6 @@ library LibProving {
TaikoData.SlotB b;
ITierProvider.Tier tier;
ITierProvider.Tier minTier;
TaikoToken tko;
bytes32 metaHash;
address assignedProver;
bytes32 stateRoot;
Expand Down Expand Up @@ -89,7 +89,6 @@ library LibProving {

/// @dev Proves or contests a block transition.
/// @param _state Current TaikoData.State.
/// @param _tko The taiko token.
/// @param _config Actual TaikoData.Config.
/// @param _resolver Address resolver interface.
/// @param _blockId The index of the block to prove. This is also used to
Expand All @@ -98,7 +97,6 @@ library LibProving {
/// TaikoData.TierProof) tuple.
function proveBlock(
TaikoData.State storage _state,
TaikoToken _tko,
TaikoData.Config memory _config,
IAddressResolver _resolver,
uint64 _blockId,
Expand All @@ -122,7 +120,6 @@ library LibProving {
}

Local memory local;
local.tko = _tko;
local.b = _state.slotB;

// Check that the block has been proposed but has not yet been verified.
Expand Down Expand Up @@ -232,7 +229,7 @@ library LibProving {
// Handles the case when an incoming tier is higher than the current transition's tier.
// Reverts when the incoming proof tries to prove the same transition
// (L1_ALREADY_PROVED).
_overrideWithHigherProof(blk, ts, tran, proof, local);
_overrideWithHigherProof(_state, _resolver, blk, ts, tran, proof, local);

emit TransitionProved({
blockId: local.blockId,
Expand Down Expand Up @@ -278,7 +275,7 @@ library LibProving {

// _checkIfContestable(/*_state,*/ tier.cooldownWindow, ts.timestamp);
// Burn the contest bond from the prover.
_tko.transferFrom(msg.sender, address(this), local.tier.contestBond);
LibBonds.debitBond(_state, _resolver, msg.sender, local.tier.contestBond);

// We retain the contest bond within the transition, just in
// case this configuration is altered to a different value
Expand Down Expand Up @@ -383,6 +380,8 @@ library LibProving {
// validity bond `V` ratio is `C/V = 21/(32*r)`, and if `r` set at 10%, the C/V ratio will be
// 6.5625.
function _overrideWithHigherProof(
TaikoData.State storage _state,
IAddressResolver _resolver,
TaikoData.Block storage _blk,
TaikoData.TransitionState memory _ts,
TaikoData.Transition memory _tran,
Expand All @@ -400,13 +399,13 @@ library LibProving {
reward = _rewardAfterFriction(_ts.contestBond);

// We return the validity bond back, but the original prover doesn't get any reward.
_local.tko.transfer(_ts.prover, _ts.validityBond);
LibBonds.creditBond(_state, _ts.prover, _ts.validityBond);
} else {
// The contested transition is proven to be invalid, contester wins the game.
// Contester gets 3/4 of reward, the new prover gets 1/4.
reward = _rewardAfterFriction(_ts.validityBond) >> 2;

_local.tko.transfer(_ts.contester, _ts.contestBond + reward * 3);
LibBonds.creditBond(_state, _ts.contester, _ts.contestBond + reward * 3);
}
} else {
if (_local.sameTransition) revert L1_ALREADY_PROVED();
Expand All @@ -425,19 +424,17 @@ library LibProving {
if (_local.assignedProver == msg.sender) {
reward += _local.livenessBond;
} else {
_local.tko.transfer(_local.assignedProver, _local.livenessBond);
LibBonds.creditBond(_state, _local.assignedProver, _local.livenessBond);
}
}
}
}

unchecked {
if (reward > _local.tier.validityBond) {
_local.tko.transfer(msg.sender, reward - _local.tier.validityBond);
LibBonds.creditBond(_state, msg.sender, reward - _local.tier.validityBond);
} else if (reward < _local.tier.validityBond) {
_local.tko.transferFrom(
msg.sender, address(this), _local.tier.validityBond - reward
);
LibBonds.debitBond(_state, _resolver, msg.sender, _local.tier.validityBond - reward);
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/protocol/contracts/L1/libs/LibUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../../common/IAddressResolver.sol";
import "../../common/LibStrings.sol";
import "../../tko/TaikoToken.sol";
import "../../libs/LibMath.sol";
import "../tiers/ITierProvider.sol";
import "../tiers/ITierRouter.sol";
Expand Down
Loading