|
|
|
@ -1,61 +1,71 @@
|
|
|
|
|
// SPDX-License-Identifier: UNLICENSED
|
|
|
|
|
pragma solidity ^0.8.13;
|
|
|
|
|
|
|
|
|
|
// xxxx.xxxxxx
|
|
|
|
|
// xxxx xx xxx
|
|
|
|
|
// xxxxx xxx xxx
|
|
|
|
|
// x.xxxx xxx xx
|
|
|
|
|
// xx x xx xx
|
|
|
|
|
// xxx . xxx xx
|
|
|
|
|
// xxx xxxxxx xx
|
|
|
|
|
// xx x xx xx
|
|
|
|
|
// x xxxxxxxx.xxx xx x.x
|
|
|
|
|
// . x xxx..xxxx xxx .x
|
|
|
|
|
// xx xxx...xxx xxxxxxxxxx xx...x
|
|
|
|
|
// x x xx......x.....xx xxxxx x..x
|
|
|
|
|
// xx xx..xxxxxx..xx.xx....xxxx xxx x.x
|
|
|
|
|
// xx x.xxxxxx xxxx. xxxxxxxxxx...xxx .x
|
|
|
|
|
// .x x...xx x. .x x.xxxxxxxx ..x..x.x xx xx
|
|
|
|
|
// xxxx....xxx xx x.x xx x xxx x.xxxx.xxxx x.x xx
|
|
|
|
|
// xx x....xx xx .xx xx.xxxx x.xx xx xxx xxx...x x. xx
|
|
|
|
|
// x x...... xx xxxxx xxxxxxxxxxxxxxxxxxx xxx.x x.x x
|
|
|
|
|
// xx ...... xxxxxxxxxxxxxxxx xx...........xxxxx xx x.x x
|
|
|
|
|
// x ......xxx................x xx..xxxx.............xxxxx xx
|
|
|
|
|
// x x.....x..x xx.xx..........x x.. xxxxx.........xx.x xx
|
|
|
|
|
// x x........xx x..........x xxxx..x xxx............x..x .
|
|
|
|
|
// x x..........xx...........xxx .x...................xx..x x
|
|
|
|
|
// xxx.xx...................x x x. x................x x..x xx
|
|
|
|
|
// xx .x x..xx.............xxx x x...xx..............x x...x x
|
|
|
|
|
// xx.x .. x........xxxx xx xxxxxxxx...........xxx xxx.x x x
|
|
|
|
|
// xx.x x. xxxx..xxxxx xx.x xxx xxxxx..xxxxxx x ... . x
|
|
|
|
|
// xx..x .x xxxx x. xxx xx xx xxxxxx ...... xx
|
|
|
|
|
// x..x .x xx......x...xx.x x. ...... .x
|
|
|
|
|
// xxx. .x x xx x..xx.....xx ... ...... x xx
|
|
|
|
|
// xxxxx.x xx xx xxxxxxxxxxxxxx x... ...xx x. xx
|
|
|
|
|
// x x... xx x xxxxxxx xxxxxxx ... ..x x.. xx
|
|
|
|
|
// ...x .xxxxxxxxxxxxxxxx.xx . ..x .. x...x x
|
|
|
|
|
// xx..x xxxxxxxx x..xxx.x x..... x
|
|
|
|
|
// xxxx..x xxxx.xxxx x.xxx..x x.....x x
|
|
|
|
|
// xx x...x x.xxx...x x..xx xxxx
|
|
|
|
|
// . x...x x.xxx.... xx x...x
|
|
|
|
|
// x x..xxx xxxxx...xx xx x....x
|
|
|
|
|
// xx x..xxxx xxxxxxxxxxxxxx..xx xxx x......xx
|
|
|
|
|
// .xxx..x x..xxxxxxxxxxx...xx xxx x....x xx.xx
|
|
|
|
|
// xxxxx.xx.x...x .xxxxx.......xxxx..xxxxxxx.....xxxxxx xxxx
|
|
|
|
|
// xxx xxxx x xx.x ....x xx .......xxxxxxxx xxx xxx
|
|
|
|
|
// xxxxx xxxx x x x.x x xx.xxxx..x xxxx xx xxx
|
|
|
|
|
// xx xxx x x xxx.....xx...xxx x xx xx
|
|
|
|
|
// x x x .xx.xxxxx xxxx
|
|
|
|
|
// x x xxxxx xxxx
|
|
|
|
|
// x x x.xxx x x
|
|
|
|
|
|
|
|
|
|
/// This contract is the main logic interface for the Unaboomer NFT project.
|
|
|
|
|
|
|
|
|
|
// ___ooo_..._.
|
|
|
|
|
// .___. o_ __.
|
|
|
|
|
// ._.._ ._o. ..o_.
|
|
|
|
|
// _oo_...._._. .._.
|
|
|
|
|
// __.. o. ._ __
|
|
|
|
|
// ._.. .o..... .o.
|
|
|
|
|
// .o. ....___. __.
|
|
|
|
|
// __. .... _o __.
|
|
|
|
|
// __ ..._.______.. .__ _x_.
|
|
|
|
|
// __ ..... ..._ooxo__.. .__. oo.
|
|
|
|
|
// o. . .__ooo_. ..__oxo__. ..oo_xo
|
|
|
|
|
// ._... ._oxxxxxoxxxx__. ______._.. .oxx_
|
|
|
|
|
// __. .oxx_ooo__oxo.xooxoo___. .___ .oo
|
|
|
|
|
// __ _o__o_._.____o ____.oo_.oxx___. . .oo.
|
|
|
|
|
// ._. _oxoo_.o_ .x o_....____. .xxoxx__. ._ _o
|
|
|
|
|
// ._o._oxx_.o..o_ _xo oo.._. o. .x..o_xo__ .x_ __
|
|
|
|
|
// __._oxxo__.o..o. __o_. .o. o_o o_o .._o_ox__ .oo. __
|
|
|
|
|
// .o _xxxxo_ _o.oo_..._o___.._x__xoox_.___. ...ooxx_ oo_ _.
|
|
|
|
|
// o _xxxxxo.......__......... .._oooooo____.....___ .oo_ ..
|
|
|
|
|
// _. _xxxxxo.._ooxxxxxxxxxxxxx_..__xxxxxxxxxxxxxxxxxx__.__ .o_
|
|
|
|
|
// _..oxxxxo._xooxxxxxxxxxxxxxxx. _xo ..oxxxxxxxxxxxxx._x_ .x.
|
|
|
|
|
// _..xxxxxxox.. ._.oxxxxxxxxxo...._xo. ...oxxxxxxxxxx.xxo .o.
|
|
|
|
|
// _..xxxxxxxxxx_.._xxxxxxxxxxo._..___xxo__oxxxxxxxxxxxxxx.xxx _.
|
|
|
|
|
// _..xxoxxxxxxxxxxxxxxxxxxxxx o. o_.xxxxxxxxxxxxxxxxxx_ xxx _.
|
|
|
|
|
// _o.ox .xxoxxxxxxxxxxxxxxxx.__. ox_.oxxxxxxxxxxxxxxx_ xxx_ .o.
|
|
|
|
|
// .._xo ox_ _xxxxxxxxxxxx_..._. _ooo_._oxxxxxxxxxxxx_...xoxo ._ ..
|
|
|
|
|
// .._o.._o. ._oxxxxxx___...o_ . ._..._ooxxxxxxo_..o o..ox. _. .o
|
|
|
|
|
// _.xxo.o_ ...._oo_... .o._ __ _..._oo_..__. ._.oxxo .o __
|
|
|
|
|
// xxx._x ..... _oo.oo_..._o_.__ ..... ._ _xxxxxo .o.
|
|
|
|
|
// xxx._x _..ox_oxxxxxxxxxx_ .ox. _xxxxxo .x.
|
|
|
|
|
// o.o_.x_ ... .. . _oo_ _oxxo_.. oxx. _xxxxo. oo _.
|
|
|
|
|
// .o.oooo ._ .__ ..__o._..___.___. .xxx. _xxo. ox_._
|
|
|
|
|
// .xxxo .x__._.______...____.__ _xx_ .ox. .oox_ o.
|
|
|
|
|
// .xxx_ _.. .......____._ _ .oxo..oo .oxxxo o
|
|
|
|
|
// _._xx_. .._o_. _xo_.oxo _xxxxx_ _.
|
|
|
|
|
// .o_.oxx_. ._________ .oo_.oxx_ oxxxo__ ._
|
|
|
|
|
// __ _xxx_ .oo_.oxxx_ _xo_ ._oo_
|
|
|
|
|
// ._ .oxxx_. .oo__oxxxo. ._ _oxxo.
|
|
|
|
|
// __ _xx___ .____.oxxo_. __ .xxxxo
|
|
|
|
|
// __ oxx _o_. ........__..ooxo_. ___. ._xxxooxo_..
|
|
|
|
|
// ._xoo_xx_ _ox__________oxxx_. .__. ..oxxxx_. ._oo_.
|
|
|
|
|
// ......_o__o_xox_ .o__...oxxxxxo__._oo______oxxxxxo_.__.. ..___.
|
|
|
|
|
// ...... .___ .o_.ox_ oxxo. .o oxxxxxxooooooo_. ___. ..__.
|
|
|
|
|
// ._.... ._._ o_. .o_ _. .oxo__oxx_.oo__ .__. ._..
|
|
|
|
|
// ... .._. o_. __.xxxoo__xxo__o _. ._. .._.
|
|
|
|
|
// .. o_. .o._ooo__. o__.
|
|
|
|
|
// o_. .._.o. _.__
|
|
|
|
|
// o_. ..x_o. o o
|
|
|
|
|
|
|
|
|
|
import {Owned} from "solmate/auth/Owned.sol";
|
|
|
|
|
import {Unaboomer} from "./Unaboomer.sol";
|
|
|
|
|
import {Mailbomb} from "./Mailbomb.sol";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@title UnaboomerNFT
|
|
|
|
|
@author 0xgrimey.eth
|
|
|
|
|
@notice This is the main contract interface for the Unaboomer NFT project drop and chain based game.
|
|
|
|
|
It contains the logic between an ERC-721 contract containing Unaboomer tokens (pixelated Unabomber
|
|
|
|
|
inspired profile pictures) and an ERC-1155 contract containing Mailbomb tokens (utility tokens).
|
|
|
|
|
Unaboomer is a chain based game with some mechanics based around "killing" other players by sending
|
|
|
|
|
them mailbombs until a certain amount of players or "survivors" remain. The motif was inspired by
|
|
|
|
|
the real life story of Theodore Kaczynski, known as the Unabomber, who conducted a nationwide
|
|
|
|
|
mail bombing campaign against people he believed to be advancing modern technology and the
|
|
|
|
|
destruction of the environment. Ironic, isn't it?
|
|
|
|
|
*/
|
|
|
|
|
contract Main is Owned {
|
|
|
|
|
|
|
|
|
|
/// Track the number of kills for each address
|
|
|
|
@ -73,7 +83,7 @@ contract Main is Owned {
|
|
|
|
|
/// Mailbomb contract
|
|
|
|
|
Mailbomb public mailbomb;
|
|
|
|
|
|
|
|
|
|
/// SentBomb event is for recording the results of sendBombs
|
|
|
|
|
/// SentBomb event is for recording the results of sendBombs for real-time feedback to a frontend interface
|
|
|
|
|
/// @param from Sender of the bombs
|
|
|
|
|
/// @param tokenId Unaboomer token which was targeted
|
|
|
|
|
/// @param hit Whether or not the bomb killed the token or not (was a dud / already killed)
|
|
|
|
@ -92,13 +102,13 @@ contract Main is Owned {
|
|
|
|
|
payable(msg.sender).transfer(balance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set price for 1 BOOMR
|
|
|
|
|
/// Set price per BOOMR
|
|
|
|
|
/// @param _price Price in wei to mint BOOMR token
|
|
|
|
|
function setBoomerPrice(uint256 _price) external onlyOwner {
|
|
|
|
|
unaboomerPrice = _price;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set price for 1 BOMB
|
|
|
|
|
/// Set price per BOMB
|
|
|
|
|
/// @param _price Price in wei to mint BOMB token
|
|
|
|
|
function setBombPrice(uint256 _price) external onlyOwner {
|
|
|
|
|
bombPrice = _price;
|
|
|
|
@ -121,7 +131,7 @@ contract Main is Owned {
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
/// This modifier prevents actions once the Unaboomer survivor count is breached.
|
|
|
|
|
/// The game stops.
|
|
|
|
|
/// 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()),
|
|
|
|
@ -136,42 +146,50 @@ contract Main is Owned {
|
|
|
|
|
|
|
|
|
|
/// Get BOOMR token balance of wallet
|
|
|
|
|
/// @param _address Wallet address to query balance of BOOMR token
|
|
|
|
|
/// @return balance Amount of BOOMR tokens owned by _address
|
|
|
|
|
function unaboomerBalance(address _address) public view returns (uint256) {
|
|
|
|
|
return unaboomer.balanceOf(_address);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get BOOMR token total supply
|
|
|
|
|
/// @return supply Amount of BOOMR tokens minted in total
|
|
|
|
|
function unaboomerSupply() public view returns (uint256) {
|
|
|
|
|
return unaboomer.totalSupply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get BOOMR kill count (unaboomers killed)
|
|
|
|
|
/// @return killCount Amount of BOOMR tokens "killed" (dead pfp)
|
|
|
|
|
function unaboomersKilled() public view returns (uint256) {
|
|
|
|
|
return unaboomer.totalKillCount();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get BOOMR token max supply
|
|
|
|
|
/// @return maxSupply Maximum amount of BOOMR tokens that can ever exist
|
|
|
|
|
function unaboomerMaxSupply() public view returns (uint256) {
|
|
|
|
|
return unaboomer.MAX_SUPPLY();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get BOMB token balance of wallet
|
|
|
|
|
/// @param _address Wallet address to query balance of BOMB token
|
|
|
|
|
/// @return balance Amount of BOMB tokens owned by _address
|
|
|
|
|
function bombBalance(address _address) public view returns (uint256) {
|
|
|
|
|
return mailbomb.balanceOf(_address, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get BOMB token supply
|
|
|
|
|
/// @return supply Amount of BOMB tokens ever minted / "assembled"
|
|
|
|
|
function bombSupply() public view returns (uint256) {
|
|
|
|
|
return mailbomb.bombsAssembled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get BOMB exploded amount
|
|
|
|
|
/// @return exploded Amount of BOMB tokens that have burned / "exploded"
|
|
|
|
|
function bombsExploded() public view returns (uint256) {
|
|
|
|
|
return mailbomb.bombsExploded();
|
|
|
|
|
}
|
|
|
|
@ -181,7 +199,7 @@ contract Main is Owned {
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
/// Radicalize a boomer to become a Unaboomer - start with 2 bombs
|
|
|
|
|
/// @param _amount Amount of Unaboomers to radicalize (mint)
|
|
|
|
|
/// @param _amount Amount of Unaboomers to mint / "radicalize"
|
|
|
|
|
function radicalizeBoomers(uint256 _amount) external payable missionNotCompleted {
|
|
|
|
|
require(msg.value >= _amount * unaboomerPrice, "not enough ether");
|
|
|
|
|
unaboomer.radicalize(msg.sender, _amount);
|
|
|
|
@ -189,7 +207,7 @@ contract Main is Owned {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Assemble additional mailbombs to kill targets
|
|
|
|
|
/// @param _amount Amount of bombs to assemble (mint)
|
|
|
|
|
/// @param _amount Amount of bombs mint / "assemble"
|
|
|
|
|
function assembleBombs(uint256 _amount) external payable missionNotCompleted {
|
|
|
|
|
require(msg.value >= _amount * bombPrice, "not enough ether");
|
|
|
|
|
mailbomb.create(msg.sender, _amount);
|
|
|
|
@ -200,22 +218,30 @@ contract Main is Owned {
|
|
|
|
|
/// Update a leaderboard with updated kill counts.
|
|
|
|
|
/// @dev Pick a pseudo-random tokenID from Unaboomer contract and toggle a mapping value
|
|
|
|
|
/// @dev The likelihood of killing a boomer decreases as time goes on - i.e. more duds
|
|
|
|
|
/// @param _amount Amount of bombs to send to kill Unaboomers
|
|
|
|
|
/// @param _amount Amount of bombs to send to kill Unaboomers (dead pfps)
|
|
|
|
|
function sendBombs(uint256 _amount) external missionNotCompleted {
|
|
|
|
|
// Ensure _amount will not exceed wallet balance of bombs, Unaboomer supply, and active Unaboomers
|
|
|
|
|
uint256 supply = unaboomer.totalSupply();
|
|
|
|
|
require(_amount <= bombBalance(msg.sender), "not enough bombs");
|
|
|
|
|
require(_amount <= supply, "not enough supply");
|
|
|
|
|
require(_amount <= supply - unaboomersKilled(), "not enough active boomers");
|
|
|
|
|
for (uint256 i; i < _amount; i++) {
|
|
|
|
|
// Pick a pseudo-random Unaboomer token
|
|
|
|
|
uint256 randomBoomer = uint256(keccak256(abi.encodePacked(i, _amount, block.timestamp, msg.sender))) % supply;
|
|
|
|
|
// Check if it was already killed
|
|
|
|
|
bool dud = unaboomer.tokenDead(randomBoomer);
|
|
|
|
|
// Kill it (does nothing if already toggled as dead)
|
|
|
|
|
unaboomer.die(randomBoomer);
|
|
|
|
|
// Check if the sender owns it (misfired, kills own pfp)
|
|
|
|
|
bool senderOwned = msg.sender == unaboomer.ownerOf(randomBoomer);
|
|
|
|
|
// Emit event for displaying in web app
|
|
|
|
|
emit SentBomb(msg.sender, randomBoomer, !dud, senderOwned);
|
|
|
|
|
// Increment kill count if successfully killed another player's Unaboomer
|
|
|
|
|
if(!dud && !senderOwned) {
|
|
|
|
|
killCount[msg.sender]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Update the leaderboard and pointer for tracking the highest amount of kills for wallets
|
|
|
|
|
uint256 kills = killCount[msg.sender];
|
|
|
|
|
address leader = leaderboard[leaderboardPointer];
|
|
|
|
|
if (kills > killCount[leader]) {
|
|
|
|
@ -224,6 +250,8 @@ contract Main is Owned {
|
|
|
|
|
leaderboard[leaderboardPointer] = msg.sender;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Burn ERC-1155 BOMB tokens (bombs go away after sending / exploding)
|
|
|
|
|
mailbomb.explode(msg.sender, _amount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|