Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
a0bbb398
Commit
a0bbb398
authored
Jun 14, 2023
by
clabby
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove PoPs `BondManager` impl
parent
33608dbe
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
0 additions
and
613 deletions
+0
-613
.storage-layout
packages/contracts-bedrock/.storage-layout
+0
-8
BondManager.sol
packages/contracts-bedrock/contracts/dispute/BondManager.sol
+0
-150
BondManager.t.sol
packages/contracts-bedrock/contracts/test/BondManager.t.sol
+0
-454
storage-snapshot.sh
packages/contracts-bedrock/scripts/storage-snapshot.sh
+0
-1
No files found.
packages/contracts-bedrock/.storage-layout
View file @
a0bbb398
...
@@ -259,11 +259,3 @@
...
@@ -259,11 +259,3 @@
| gameImpls | mapping(GameType => contract IDisputeGame) | 1 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| gameImpls | mapping(GameType => contract IDisputeGame) | 1 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGames | mapping(Hash => contract IDisputeGame) | 2 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGames | mapping(Hash => contract IDisputeGame) | 2 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGameList | contract IDisputeGame[] | 3 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGameList | contract IDisputeGame[] | 3 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
=======================
➡ contracts/dispute/BondManager.sol:BondManager
=======================
| Name | Type | Slot | Offset | Bytes | Contract |
|-------|---------------------------------------------|------|--------|-------|-----------------------------------------------|
| bonds | mapping(bytes32 => struct BondManager.Bond) | 0 | 0 | 32 | contracts/dispute/BondManager.sol:BondManager |
packages/contracts-bedrock/contracts/dispute/BondManager.sol
deleted
100644 → 0
View file @
33608dbe
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../libraries/DisputeTypes.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
/**
* @title BondManager
* @notice The Bond Manager serves as an escrow for permissionless output proposal bonds.
*/
contract BondManager is IBondManager {
/**
* @notice The Bond Type
*/
struct Bond {
address owner;
bytes32 id;
uint128 expiration;
uint128 amount;
}
/**
* @notice The permissioned dispute game factory.
* @dev Used to verify the status of bonds.
*/
IDisputeGameFactory public immutable DISPUTE_GAME_FACTORY;
/**
* @notice Amount of gas used to transfer ether when splitting the bond.
* This is a reasonable amount of gas for a transfer, even to a smart contract.
* The number of participants is bound of by the block gas limit.
*/
uint256 private constant TRANSFER_GAS = 30_000;
/**
* @notice Mapping from bondId to bond.
*/
mapping(bytes32 => Bond) public bonds;
/**
* @notice Instantiates the bond maanger with the registered dispute game factory.
* @param _disputeGameFactory is the dispute game factory.
*/
constructor(IDisputeGameFactory _disputeGameFactory) {
DISPUTE_GAME_FACTORY = _disputeGameFactory;
}
/**
* @inheritdoc IBondManager
*/
function post(
bytes32 _bondId,
address _bondOwner,
uint128 _minClaimHold
) external payable {
require(bonds[_bondId].owner == address(0), "BondManager: BondId already posted.");
require(_bondOwner != address(0), "BondManager: Owner cannot be the zero address.");
require(msg.value > 0, "BondManager: Value must be non-zero.");
uint128 expiration = uint128(_minClaimHold + block.timestamp);
bonds[_bondId] = Bond({
owner: _bondOwner,
id: _bondId,
expiration: expiration,
amount: uint128(msg.value)
});
emit BondPosted(_bondId, _bondOwner, expiration, msg.value);
}
/**
* @inheritdoc IBondManager
*/
function seize(bytes32 _bondId) external {
Bond memory b = bonds[_bondId];
require(b.owner != address(0), "BondManager: The bond does not exist.");
require(b.expiration >= block.timestamp, "BondManager: Bond expired.");
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameTypes.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
require(msg.sender == address(game), "BondManager: Unauthorized seizure.");
require(game.status() == GameStatus.CHALLENGER_WINS, "BondManager: Game incomplete.");
delete bonds[_bondId];
emit BondSeized(_bondId, b.owner, msg.sender, b.amount);
bool success = SafeCall.send(payable(msg.sender), gasleft(), b.amount);
require(success, "BondManager: Failed to send Ether.");
}
/**
* @inheritdoc IBondManager
*/
function seizeAndSplit(bytes32 _bondId, address[] calldata _claimRecipients) external {
Bond memory b = bonds[_bondId];
require(b.owner != address(0), "BondManager: The bond does not exist.");
require(b.expiration >= block.timestamp, "BondManager: Bond expired.");
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameTypes.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
require(msg.sender == address(game), "BondManager: Unauthorized seizure.");
require(game.status() == GameStatus.CHALLENGER_WINS, "BondManager: Game incomplete.");
delete bonds[_bondId];
emit BondSeized(_bondId, b.owner, msg.sender, b.amount);
uint256 len = _claimRecipients.length;
uint256 proportionalAmount = b.amount / len;
// Send the proportional amount to each recipient. Do not revert if a send fails as that
// will prevent other recipients from receiving their share.
for (uint256 i; i < len; i++) {
SafeCall.send({
_target: payable(_claimRecipients[i]),
_gas: TRANSFER_GAS,
_value: proportionalAmount
});
}
}
/**
* @inheritdoc IBondManager
*/
function reclaim(bytes32 _bondId) external {
Bond memory b = bonds[_bondId];
require(b.owner == msg.sender, "BondManager: Unauthorized claimant.");
require(b.expiration <= block.timestamp, "BondManager: Bond isn't claimable yet.");
delete bonds[_bondId];
emit BondReclaimed(_bondId, msg.sender, b.amount);
bool success = SafeCall.send(payable(msg.sender), gasleft(), b.amount);
require(success, "BondManager: Failed to send Ether.");
}
}
packages/contracts-bedrock/contracts/test/BondManager.t.sol
deleted
100644 → 0
View file @
33608dbe
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
import "../libraries/DisputeTypes.sol";
import { IDisputeGame } from "../dispute/interfaces/IDisputeGame.sol";
import { IBondManager } from "../dispute/interfaces/IBondManager.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { BondManager } from "../dispute/BondManager.sol";
contract BondManager_Test is Test {
DisputeGameFactory factory;
BondManager bm;
// DisputeGameFactory events
event DisputeGameCreated(
address indexed disputeProxy,
GameType indexed gameType,
Claim indexed rootClaim
);
// BondManager events
event BondPosted(bytes32 bondId, address owner, uint256 expiration, uint256 amount);
event BondSeized(bytes32 bondId, address owner, address seizer, uint256 amount);
event BondReclaimed(bytes32 bondId, address claiment, uint256 amount);
function setUp() public {
factory = new DisputeGameFactory(address(this));
bm = new BondManager(factory);
}
/**
* -------------------------------------------
* Test Bond Posting
* -------------------------------------------
*/
/**
* @notice Tests that posting a bond succeeds.
*/
function testFuzz_post_succeeds(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
// Create2Deployer
vm.assume(owner != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
vm.expectEmit(true, true, true, true);
uint128 expiration = uint128(block.timestamp + minClaimHold);
emit BondPosted(bondId, owner, expiration, amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
// Validate the bond
(
address newFetchedOwner,
bytes32 fetchedBondId,
uint128 fetchedExpiration,
uint128 bondAmount
) = bm.bonds(bondId);
assertEq(newFetchedOwner, owner);
assertEq(fetchedExpiration, block.timestamp + minClaimHold);
assertEq(fetchedBondId, bondId);
assertEq(bondAmount, amount);
}
/**
* @notice Tests that posting a bond with the same id twice reverts.
*/
function testFuzz_post_duplicates_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
amount = amount / 2;
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
vm.deal(address(this), amount);
vm.expectRevert("BondManager: BondId already posted.");
bm.post{ value: amount }(bondId, owner, minClaimHold);
}
/**
* @notice Posting with the zero address as the owner fails.
*/
function testFuzz_post_zeroAddress_reverts(
bytes32 bondId,
uint128 minClaimHold,
uint128 amount
) public {
address owner = address(0);
vm.deal(address(this), amount);
vm.expectRevert("BondManager: Owner cannot be the zero address.");
bm.post{ value: amount }(bondId, owner, minClaimHold);
}
/**
* @notice Posting zero value bonds should revert.
*/
function testFuzz_post_zeroAddress_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold
) public {
vm.assume(owner != address(0));
uint128 amount = 0;
vm.deal(address(this), amount);
vm.expectRevert("BondManager: Value must be non-zero.");
bm.post{ value: amount }(bondId, owner, minClaimHold);
}
/**
* -------------------------------------------
* Test Bond Seizing
* -------------------------------------------
*/
/**
* @notice Non-existing bonds shouldn't be seizable.
*/
function testFuzz_seize_missingBond_reverts(bytes32 bondId) public {
vm.expectRevert("BondManager: The bond does not exist.");
bm.seize(bondId);
}
/**
* @notice Bonds that expired cannot be seized.
*/
function testFuzz_seize_expired_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
vm.warp(block.timestamp + minClaimHold + 1);
vm.expectRevert("BondManager: Bond expired.");
bm.seize(bondId);
}
/**
* @notice Bonds cannot be seized by unauthorized parties.
*/
function testFuzz_seize_unauthorized_reverts(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(0));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
MockAttestationDisputeGame game = new MockAttestationDisputeGame();
vm.prank(address(game));
vm.expectRevert("BondManager: Unauthorized seizure.");
bm.seize(bondId);
}
/**
* @notice Seizing a bond should succeed if the game resolves.
*/
function testFuzz_seize_succeeds(
bytes32 bondId,
uint128 minClaimHold,
bytes calldata extraData
) public {
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), 1 ether);
bm.post{ value: 1 ether }(bondId, address(0xba5ed), minClaimHold);
// Create a mock dispute game in the factory
IDisputeGame proxy;
Claim rootClaim;
bytes memory ed = extraData;
{
rootClaim = Claim.wrap(bytes32(""));
MockAttestationDisputeGame implementation = new MockAttestationDisputeGame();
GameType gt = GameTypes.ATTESTATION;
factory.setImplementation(gt, IDisputeGame(address(implementation)));
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
proxy = factory.create(gt, rootClaim, extraData);
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy));
}
// Update the game fields
MockAttestationDisputeGame spawned = MockAttestationDisputeGame(payable(address(proxy)));
spawned.setBondManager(bm);
spawned.setRootClaim(rootClaim);
spawned.setGameStatus(GameStatus.CHALLENGER_WINS);
spawned.setBondId(bondId);
spawned.setExtraData(ed);
// Seize the bond by calling resolve
vm.expectEmit(true, true, true, true);
emit BondSeized(bondId, address(0xba5ed), address(spawned), 1 ether);
spawned.resolve();
assertEq(address(spawned).balance, 1 ether);
// Validate that the bond was deleted
(address newFetchedOwner, , , ) = bm.bonds(bondId);
assertEq(newFetchedOwner, address(0));
}
/**
* -------------------------------------------
* Test Bond Split and Seizing
* -------------------------------------------
*/
/**
* @notice Seizing and splitting a bond should succeed if the game resolves.
*/
function testFuzz_seizeAndSplit_succeeds(
bytes32 bondId,
uint128 minClaimHold,
bytes calldata extraData
) public {
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
vm.deal(address(this), 1 ether);
bm.post{ value: 1 ether }(bondId, address(0xba5ed), minClaimHold);
// Create a mock dispute game in the factory
IDisputeGame proxy;
Claim rootClaim;
bytes memory ed = extraData;
{
rootClaim = Claim.wrap(bytes32(""));
MockAttestationDisputeGame implementation = new MockAttestationDisputeGame();
GameType gt = GameTypes.ATTESTATION;
factory.setImplementation(gt, IDisputeGame(address(implementation)));
vm.expectEmit(false, true, true, false);
emit DisputeGameCreated(address(0), gt, rootClaim);
proxy = factory.create(gt, rootClaim, extraData);
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy));
}
// Update the game fields
MockAttestationDisputeGame spawned = MockAttestationDisputeGame(payable(address(proxy)));
spawned.setBondManager(bm);
spawned.setRootClaim(rootClaim);
spawned.setGameStatus(GameStatus.CHALLENGER_WINS);
spawned.setBondId(bondId);
spawned.setExtraData(ed);
// Seize the bond by calling resolve
vm.expectEmit(true, true, true, true);
emit BondSeized(bondId, address(0xba5ed), address(spawned), 1 ether);
spawned.splitResolve();
assertEq(address(spawned).balance, 0);
address[] memory challengers = spawned.getChallengers();
uint256 proportionalAmount = 1 ether / challengers.length;
for (uint256 i = 0; i < challengers.length; i++) {
assertEq(address(challengers[i]).balance, proportionalAmount);
}
// Validate that the bond was deleted
(address newFetchedOwner, , , ) = bm.bonds(bondId);
assertEq(newFetchedOwner, address(0));
}
/**
* -------------------------------------------
* Test Bond Reclaiming
* -------------------------------------------
*/
/**
* @notice Bonds can be reclaimed after the specified amount of time.
*/
function testFuzz_reclaim_succeeds(
bytes32 bondId,
address owner,
uint128 minClaimHold,
uint128 amount
) public {
vm.assume(owner != address(factory));
vm.assume(owner != address(bm));
vm.assume(owner != address(this));
vm.assume(owner != address(0));
vm.assume(owner.code.length == 0);
amount = uint128(bound(amount, 1, type(uint128).max));
minClaimHold = uint128(bound(minClaimHold, 0, type(uint128).max - block.timestamp));
assumeNoPrecompiles(owner);
// Post the bond
vm.deal(address(this), amount);
bm.post{ value: amount }(bondId, owner, minClaimHold);
// We can't claim if the block.timestamp is less than the bond expiration.
(, , uint256 expiration, ) = bm.bonds(bondId);
if (expiration > block.timestamp) {
vm.prank(owner);
vm.expectRevert("BondManager: Bond isn't claimable yet.");
bm.reclaim(bondId);
}
// Past expiration, the owner can reclaim
vm.warp(expiration);
vm.prank(owner);
bm.reclaim(bondId);
assertEq(owner.balance, amount);
}
}
/**
* @title MockAttestationDisputeGame
* @dev A mock dispute game for testing bond seizures.
*/
contract MockAttestationDisputeGame {
GameStatus internal gameStatus;
BondManager bm;
Claim internal rc;
bytes internal ed;
bytes32 internal bondId;
address[] internal challengers;
function getChallengers() public view returns (address[] memory) {
return challengers;
}
function setBondId(bytes32 bid) external {
bondId = bid;
}
function setBondManager(BondManager _bm) external {
bm = _bm;
}
function setGameStatus(GameStatus _gs) external {
gameStatus = _gs;
}
function setRootClaim(Claim _rc) external {
rc = _rc;
}
function setExtraData(bytes memory _ed) external {
ed = _ed;
}
receive() external payable {}
fallback() external payable {}
function splitResolve() public {
challengers = [address(1), address(2)];
bm.seizeAndSplit(bondId, challengers);
}
/**
* -------------------------------------------
* Initializable Functions
* -------------------------------------------
*/
function initialize() external {
/* noop */
}
/**
* -------------------------------------------
* IVersioned Functions
* -------------------------------------------
*/
function version() external pure returns (string memory _version) {
return "0.1.0";
}
/**
* -------------------------------------------
* IDisputeGame Functions
* -------------------------------------------
*/
function createdAt() external pure returns (Timestamp _createdAt) {
return Timestamp.wrap(uint64(0));
}
function status() external view returns (GameStatus _status) {
return gameStatus;
}
function gameType() external pure returns (GameType _gameType) {
return GameTypes.ATTESTATION;
}
function rootClaim() external view returns (Claim _rootClaim) {
return rc;
}
function extraData() external view returns (bytes memory _extraData) {
return ed;
}
function gameData()
external
pure
returns (
GameType,
Claim,
bytes memory
)
{
assembly {
revert(0, 0)
}
}
function bondManager() external view returns (IBondManager _bondManager) {
return IBondManager(address(bm));
}
function resolve() external returns (GameStatus _status) {
bm.seize(bondId);
return gameStatus;
}
}
packages/contracts-bedrock/scripts/storage-snapshot.sh
View file @
a0bbb398
...
@@ -32,7 +32,6 @@ contracts=(
...
@@ -32,7 +32,6 @@ contracts=(
contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20
contracts/universal/OptimismMintableERC20.sol:OptimismMintableERC20
contracts/universal/OptimismMintableERC20Factory.sol:OptimismMintableERC20Factory
contracts/universal/OptimismMintableERC20Factory.sol:OptimismMintableERC20Factory
contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory
contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory
contracts/dispute/BondManager.sol:BondManager
)
)
dir
=
$(
dirname
"
$0
"
)
dir
=
$(
dirname
"
$0
"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment