Skip to content

Commit

Permalink
feat(protocol): allow TAIKO token bonds deposits and withdrawal (#17725)
Browse files Browse the repository at this point in the history
  • Loading branch information
dantaik committed Jul 4, 2024
1 parent 919cb4c commit e505392
Show file tree
Hide file tree
Showing 19 changed files with 279 additions and 143 deletions.
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
47 changes: 27 additions & 20 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,29 @@ 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);
}

/// @inheritdoc ITaikoL1
function pauseProving(bool _pause) external {
_authorizePause(msg.sender, _pause);
LibProving.pauseProving(state, _pause);
}

/// @inheritdoc ITaikoL1
function depositBond(uint256 _amount) external whenNotPaused {
LibBonds.depositBond(state, this, _amount);
}

/// @inheritdoc ITaikoL1
function withdrawBond(uint256 _amount) external whenNotPaused {
LibBonds.withdrawBond(state, this, _amount);
}

/// @notice Gets the current bond balance of a given address.
/// @return The current bond balance.
function bondBalanceOf(address _user) external view returns (uint256) {
return LibBonds.bondBalanceOf(state, _user);
}

/// @notice Gets the details of a block.
Expand Down Expand Up @@ -201,12 +214,6 @@ contract TaikoL1 is EssentialContract, ITaikoL1, TaikoEvents, TaikoErrors {
return (state.slotA, state.slotB);
}

/// @inheritdoc ITaikoL1
function pauseProving(bool _pause) external {
_authorizePause(msg.sender, _pause);
LibProving.pauseProving(state, _pause);
}

/// @inheritdoc EssentialContract
function unpause() public override {
super.unpause(); // permission checked inside
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
{
_state.bondBalance[msg.sender] += _amount;
_tko(_resolver).transferFrom(msg.sender, address(this), _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

0 comments on commit e505392

Please sign in to comment.