cleanup and tighten tests

master
lza_menace 1 year ago
parent 16cd16a979
commit ce306d953f

@ -32,12 +32,6 @@ contract Mailbomb is ERC1155, Owned {
// Admin
// =========================================================================
/// Withdraw funds to contract owner
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
}
/// Set metadata URI for all BOMB (token 1)
/// @param _baseURI IPFS hash or URL to retrieve JSON metadata
function setBaseURI(string calldata _baseURI) external onlyOwner {

@ -99,7 +99,8 @@ contract Main is Owned {
/// Withdraw funds to contract owner
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
(bool success, ) = payable(msg.sender).call{value: balance}("");
require(success, "failed to withdraw");
}
/// Set price per BOOMR
@ -134,7 +135,7 @@ contract Main is Owned {
/// The game stops; no more bombing/killing. Survivors make it to the next round.
modifier missionNotCompleted {
require(
unaboomer.totalKillCount() <= (unaboomer.MAX_SUPPLY() - unaboomer.SURVIVOR_COUNT()),
unaboomer.totalKillCount() < (unaboomer.MAX_SUPPLY() - unaboomer.MAX_SURVIVOR_COUNT()),
"mission already completed"
);
_;
@ -171,8 +172,8 @@ contract Main is Owned {
/// Get BOOMR token survivor count
/// @return survivorCount Maximum amount of BOOMR survivor tokens that can ever exist
function unaboomerSurvivorCount() public view returns (uint256) {
return unaboomer.SURVIVOR_COUNT();
function unaboomerMaxSurvivorCount() public view returns (uint256) {
return unaboomer.MAX_SURVIVOR_COUNT();
}
/// Get BOMB token balance of wallet

@ -15,7 +15,7 @@ Each Unaboomer is a unique, dynamically generated pixel avatar in the likeness
of the real-life Unabomber, Theodore Kaczynski. Unaboomers can be "killed" by
other players by "sending" (burning) mailbombs. When Unaboomers are killed their
corresponding image is replaced with an explosion, rendering it worthless as any
rarity associated with it ceases to exist. The game stops when SURVIVOR_COUNT
rarity associated with it ceases to exist. The game stops when MAX_SURVIVOR_COUNT
threshold is breached. The surviving players (any address which holds an "alive"
Unaboomer) will advance to the next round of gameplay.
@dev All contract functions regarding token burning and minting are limited to
@ -29,7 +29,7 @@ contract Unaboomer is ERC721, Owned {
/// Maximum supply of BOOMR tokens
uint256 public constant MAX_SUPPLY = 10000;
/// Maximum amount of survivors remaining to advance to the next round
uint256 public constant SURVIVOR_COUNT = 1000;
uint256 public constant MAX_SURVIVOR_COUNT = 1000;
/// The total amount of Unaboomers who have been killed during the game
uint256 public totalKillCount;
/// Number of tokens minted (total supply)
@ -47,12 +47,6 @@ contract Unaboomer is ERC721, Owned {
// Admin
// =========================================================================
/// Withdraw funds to contract owner
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
}
/// Set metadata URI for living Unaboomer tokens
/// @param _baseURI IPFS hash or URL to retrieve JSON metadata for living Unaboomer tokens
function setAliveURI(string calldata _baseURI) external onlyOwner {

@ -7,11 +7,15 @@ import {Unaboomer} from "../src/Unaboomer.sol";
import {Mailbomb} from "../src/Mailbomb.sol";
contract UnaboomerTest is Test {
using stdStorage for StdStorage;
Main public main;
Unaboomer public boomr;
Mailbomb public bomb;
uint256 unaboomerPrice;
uint256 bombPrice;
address victim = address(1);
address killer = address(2);
function setUp() public {
main = new Main();
@ -42,8 +46,6 @@ contract UnaboomerTest is Test {
// ensure killing increments leaderboard
function testLeaderboard() public {
address victim = address(1);
address killer = address(2);
uint256 amt = 20;
hoax(victim);
main.radicalizeBoomers{value: unaboomerPrice * amt}(amt);
@ -60,21 +62,112 @@ contract UnaboomerTest is Test {
}
// ensure killing toggles URI
function testURIToggling() public {
boomr.setAliveURI('ipfs://alive/');
boomr.setDeadURI('ipfs://dead/');
startHoax(victim);
main.radicalizeBoomers{value: unaboomerPrice * 1}(1);
assertEq(boomr.tokenURI(1), 'ipfs://alive/1.json');
main.sendBombs(1);
assertEq(boomr.tokenURI(1), 'ipfs://dead/1.json');
}
// ensure sending bombs burns bombs
function testBombBurning() public {
hoax(victim);
main.radicalizeBoomers{value: unaboomerPrice * 20}(20);
startHoax(killer);
main.assembleBombs{value: bombPrice * 20}(20);
assertEq(main.bombBalance(killer), 20);
assertEq(main.bombsExploded(), 0);
main.sendBombs(5);
assertEq(main.bombBalance(killer), 15);
assertEq(main.bombsExploded(), 5);
}
// ensure supply limits enforced
// ensure survivor limit enforced
// ensure only owners can withdraw funds
// ensure withdraw function actuall works
function testMaximumSupply() public {
uint256 maxSupply = main.unaboomerMaxSupply();
uint256 slot = stdstore
.target(address(boomr))
.sig("minted()")
.find();
bytes32 loc = bytes32(slot);
bytes32 mockedCurrentTokenId = bytes32(abi.encode(maxSupply - 1));
vm.store(address(boomr), loc, mockedCurrentTokenId);
assertEq(main.unaboomerSupply(), (maxSupply - 1));
startHoax(victim);
main.radicalizeBoomers{value: unaboomerPrice}(1);
vm.expectRevert(bytes("supply reached"));
main.radicalizeBoomers{value: unaboomerPrice}(1);
}
// ensure survivor limit enforced and actions halted
function testSurvivors() public {
uint256 maxSupply = main.unaboomerMaxSupply();
uint256 maxSurvivorCount = main.unaboomerMaxSurvivorCount();
uint256 slot = stdstore
.target(address(boomr))
.sig("minted()")
.find();
bytes32 loc = bytes32(slot);
bytes32 a = bytes32(abi.encode(maxSupply)); // 10k
vm.store(address(boomr), loc, a);
slot = stdstore
.target(address(boomr))
.sig("totalKillCount()")
.find();
loc = bytes32(slot);
a = bytes32(abi.encode((maxSupply - maxSurvivorCount))); // 1k
vm.store(address(boomr), loc, a);
startHoax(victim);
vm.expectRevert(bytes("mission already completed"));
main.radicalizeBoomers{value: unaboomerPrice}(1);
vm.expectRevert(bytes("mission already completed"));
main.assembleBombs{value: bombPrice}(1);
vm.expectRevert(bytes("mission already completed"));
main.sendBombs(1);
}
// ensure withdraw function works as expected
function testWithdrawalWorksAsOwner() public {
address owner = main.owner();
uint256 ownerStartBalance = owner.balance;
assertEq(address(main).balance, 0);
hoax(victim);
main.radicalizeBoomers{value: unaboomerPrice * 10}(10);
uint256 contractBalance = address(main).balance;
assertEq(contractBalance, unaboomerPrice * 10);
main.withdraw();
assertEq(owner.balance, ownerStartBalance + contractBalance);
}
// ensure only owner can withdraw
function testWithdrawalFailsAsNotOwner() public {
hoax(victim);
main.radicalizeBoomers{value: unaboomerPrice * 10}(10);
uint256 contractBalance = address(main).balance;
assertEq(contractBalance, unaboomerPrice * 10);
vm.expectRevert("UNAUTHORIZED");
hoax(address(0xd3ad));
main.withdraw();
vm.expectRevert("UNAUTHORIZED");
hoax(address(123));
main.withdraw();
}
// ensure NFT contracts do not accept Ether
function testNoAcceptEther() public {
(bool tbb, ) = payable(address(bomb)).call{value: .5 ether}("");
assertEq(tbb, false);
(bool tbr, ) = payable(address(boomr)).call{value: .5 ether}("");
assertEq(tbr, false);
}
// function testURILogic() public {
// address t = address(1);
// boomr.setAliveURI('ipfs://alive/');
// boomr.setDeadURI('ipfs://dead/');
// startHoax(t);
// main.radicalizeBoomers{value: .01 ether}(1);
// assertEq(boomr.tokenURI(0), 'ipfs://alive/0.json');
// main.sendBombs(1);
// assertEq(boomr.tokenURI(0), 'ipfs://dead/0.json');
// }
// Function to receive Ether. msg.data must be empty
receive() external payable {}
// Fallback function is called when msg.data is not empty
fallback() external payable {}
}

Loading…
Cancel
Save