Skip to content

Commit

Permalink
Merge pull request #242 from OffchainLabs/express-lane-auction-burn
Browse files Browse the repository at this point in the history
Express lane auction burn
  • Loading branch information
yahgwai authored Aug 27, 2024
2 parents a40ba2e + e3b24b7 commit e4dd36a
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 2 deletions.
25 changes: 25 additions & 0 deletions src/express-lane-auction/Burner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {
ERC20BurnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "./Errors.sol";

/// @notice A simple contract that can burn any tokens that are transferred to it
/// Token must support the ERC20BurnableUpgradeable.burn(uint256) interface
contract Burner {
ERC20BurnableUpgradeable public immutable token;

constructor(address _token) {
if (_token == address(0)) {
revert ZeroAddress();
}
token = ERC20BurnableUpgradeable(_token);
}

/// @notice Can be called at any time by anyone to burn any tokens held by this burner
function burn() external {
token.burn(token.balanceOf(address(this)));
}
}
1 change: 1 addition & 0 deletions src/express-lane-auction/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ error RoundTooLong(uint64 roundDurationSeconds);
error ZeroAuctionClosingSeconds();
error NegativeOffset();
error NegativeRoundStart(int64 roundStart);
error ZeroAddress();
89 changes: 87 additions & 2 deletions test/foundry/ExpressLaneAuction.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../../src/express-lane-auction/ExpressLaneAuction.sol";
import {ERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
ERC20Burnable,
IERC20
} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "../../src/express-lane-auction/Burner.sol";

contract MockERC20 is ERC20 {
contract MockERC20 is ERC20Burnable {
constructor() ERC20("LANE", "LNE") {
_mint(msg.sender, 1_000_000);
}
Expand Down Expand Up @@ -970,6 +975,86 @@ contract ExpressLaneAuctionTest is Test {
rs.auction.flushBeneficiaryBalance();
}

function testFlushToBurner() public {
vm.chainId(137);
Bid memory bid0;
Bid memory bid1;
ExpressLaneAuction auction;
MockERC20 erc20 = new MockERC20();
Burner burner = new Burner(address(erc20));
{
ProxyAdmin proxyAdmin = new ProxyAdmin();
ExpressLaneAuction impl = new ExpressLaneAuction();

auction = ExpressLaneAuction(
address(new TransparentUpgradeableProxy(address(impl), address(proxyAdmin), ""))
);
InitArgs memory args = createArgs(address(erc20));
args._beneficiary = address(burner);
auction.initialize(args);

erc20.transfer(bidders[0].addr, bidders[0].amount);
erc20.transfer(bidders[1].addr, bidders[1].amount);

vm.startPrank(bidders[0].addr);
erc20.approve(address(auction), bidders[0].amount);
auction.deposit(bidders[0].amount);
vm.stopPrank();

vm.startPrank(bidders[1].addr);
erc20.approve(address(auction), bidders[1].amount);
auction.deposit(bidders[1].amount);
vm.stopPrank();

(int64 o, uint64 roundDurationSeconds, uint64 auctionClosingSeconds, ) = auction
.roundTimingInfo();
vm.warp(
uint64(o) +
(roundDurationSeconds * testRound) +
roundDurationSeconds -
auctionClosingSeconds
);
uint64 biddingForRound = auction.currentRound() + 1;

bytes32 h0 = auction.getBidHash(biddingForRound, bidders[0].elc, bidders[0].amount / 2);
bid0 = Bid({
amount: bidders[0].amount / 2,
expressLaneController: bidders[0].elc,
signature: sign(bidders[0].privKey, h0)
});
bytes32 h1 = auction.getBidHash(biddingForRound, bidders[1].elc, bidders[1].amount / 2);
bid1 = Bid({
amount: bidders[1].amount / 2,
expressLaneController: bidders[1].elc,
signature: sign(bidders[1].privKey, h1)
});
}

vm.prank(auctioneer);
auction.resolveMultiBidAuction(bid1, bid0);

assertFalse(auction.beneficiaryBalance() == 0, "bal before");
uint256 auctionBalanceBefore = erc20.balanceOf(address(auction));
uint256 beneficiaryBalanceBefore = erc20.balanceOf(auction.beneficiary());
assertEq(erc20.balanceOf(address(burner)), 0);

// any random address should be able to call this
vm.prank(vm.addr(34567890));
auction.flushBeneficiaryBalance();

uint256 auctionBalanceAfter = erc20.balanceOf(address(auction));
uint256 beneficiaryBalanceAfter = erc20.balanceOf(auction.beneficiary());
assertTrue(auction.beneficiaryBalance() == 0, "bal after");
assertEq(beneficiaryBalanceAfter - beneficiaryBalanceBefore, bid0.amount);
assertEq(auctionBalanceBefore - auctionBalanceAfter, bid0.amount);
assertEq(erc20.balanceOf(address(burner)), bid0.amount);

vm.prank(vm.addr(948765));
burner.burn();

assertEq(erc20.balanceOf(address(burner)), 0);
}

function testCannotResolveNotAuctioneer() public {
ResolveSetup memory rs = deployDepositAndBids();
vm.stopPrank();
Expand Down
49 changes: 49 additions & 0 deletions test/foundry/ExpressLaneBurner.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import {
ERC20BurnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import {Burner} from "../../src/express-lane-auction/Burner.sol";
import "../../src/express-lane-auction/Errors.sol";

contract MockERC20 is ERC20BurnableUpgradeable {
function initialize() public initializer {
__ERC20_init("LANE", "LNE");
_mint(msg.sender, 1_000_000);
}
}

contract ExpressLaneBurner is Test {
event Transfer(address indexed from, address indexed to, uint256 value);

function testBurn() public {
vm.expectRevert(ZeroAddress.selector);
new Burner(address(0));

MockERC20 erc20 = new MockERC20();
erc20.initialize();
Burner burner = new Burner(address(erc20));
assertEq(address(burner.token()), address(erc20));

erc20.transfer(address(burner), 20);

uint256 totalSupplyBefore = erc20.totalSupply();
assertEq(erc20.balanceOf(address(burner)), 20);

vm.expectEmit(true, true, true, true);
emit Transfer(address(burner), address(0), 20);
vm.prank(vm.addr(137));
burner.burn();

assertEq(totalSupplyBefore - erc20.totalSupply(), 20);
assertEq(erc20.balanceOf(address(burner)), 0);

// can burn 0 if we want to
vm.expectEmit(true, true, true, true);
emit Transfer(address(burner), address(0), 0);
vm.prank(vm.addr(138));
burner.burn();
}
}

0 comments on commit e4dd36a

Please sign in to comment.