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

Express lane auction burn #242

Merged
merged 3 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/express-lane-auction/Burner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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
yahgwai marked this conversation as resolved.
Show resolved Hide resolved
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();
}
}
Loading