文件 1 的 14:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 14:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this;
return msg.data;
}
}
文件 3 的 14:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 4 的 14:ERC721.sol
pragma solidity ^0.8.0;
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "./extensions/IERC721Enumerable.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
string private _name;
string private _symbol;
mapping (uint256 => address) private _owners;
mapping (address => uint256) private _balances;
mapping (uint256 => address) private _tokenApprovals;
mapping (address => mapping (address => bool)) private _operatorApprovals;
constructor (string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC721).interfaceId
|| interfaceId == type(IERC721Metadata).interfaceId
|| super.supportsInterface(interfaceId);
}
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: owner query for nonexistent token");
return owner;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0
? string(abi.encodePacked(baseURI, tokenId.toString()))
: '';
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
function getApproved(uint256 tokenId) public view virtual override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
}
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
_mint(to, tokenId);
require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
_approve(address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
_approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}
文件 5 的 14:ERC721Holder.sol
pragma solidity ^0.8.0;
import "../IERC721Receiver.sol";
contract ERC721Holder is IERC721Receiver {
function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
}
文件 6 的 14:EtherFreakers.sol
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
struct Freaker {
uint8 species;
uint8 stamina;
uint8 fortune;
uint8 agility;
uint8 offense;
uint8 defense;
}
struct EnergyBalance {
uint128 basic;
uint128 index;
}
struct CombatMultipliers {
uint128 attack;
uint128 defend;
}
struct SpeciesCounter {
int32 pluto;
int32 mercury;
int32 saturn;
int32 uranus;
int32 venus;
int32 mars;
int32 neptune;
int32 jupiter;
}
contract EtherFreakers is ERC721 {
uint128 public numTokens;
uint256[] public birthCertificates;
uint128 public creatorIndex;
uint128 public freakerIndex;
uint128 public totalFortune;
mapping(uint128 => Freaker) public freakers;
mapping(uint128 => EnergyBalance) public energyBalances;
mapping(address => CombatMultipliers) public combatMultipliers;
mapping(address => SpeciesCounter) public speciesCounters;
event Born(address mother, uint128 energy, uint128 indexed freakerId, Freaker freaker);
event Missed(address attacker, address defender, uint128 indexed sourceId, uint128 indexed targetId);
event Thwarted(address attacker, address defender, uint128 indexed sourceId, uint128 indexed targetId);
event Captured(address attacker, address defender, uint128 indexed sourceId, uint128 indexed targetId);
constructor(address author) ERC721("EtherFreakers", "EFKR") {
for (uint i = 0; i < 8; i++) {
_mint(author, numTokens++);
}
}
function _baseURI() internal pure override returns (string memory) {
return "https://ether.freakers.art/m/";
}
function birth() payable public {
birthTo(payable(msg.sender));
}
function birthTo(address payable to) payable public {
uint256 middle = middlePrice();
require(msg.value > middle * 1005 / 1000, "Not enough energy");
uint128 freakerId = numTokens++;
uint8 speciesDie = uint8(_randomishIntLessThan("species", 20));
uint8 species = (
(speciesDie < 1 ? 0 :
(speciesDie < 3 ? 1 :
(speciesDie < 5 ? 2 :
(speciesDie < 8 ? 3 :
(speciesDie < 11 ? 4 :
(speciesDie < 15 ? 5 :
(speciesDie < 19 ? 6 : 7))))))));
uint8 stamina = uint8(_randomishIntLessThan("stamina", 10));
uint8 fortune = uint8(_randomishIntLessThan("fortune", species < 3 ? 30 : 10) + 1);
uint8 agility = uint8(_randomishIntLessThan("agility",
(species == 5 || species == 6 ? 30 :
(species == 2 || species == 7 ? 20 : 10))) + 1);
uint8 offense = uint8(_randomishIntLessThan("offense",
(species == 3 ? 30 :
(species == 0 || species == 4 || species == 5 ? 20 : 10))) + 1);
uint8 defense = uint8(_randomishIntLessThan("defense",
(species == 4 || species == 7 ? 30 :
(species == 1 || species == 4 || species == 6 ? 20 : 10))) + 1);
Freaker memory freaker = Freaker({
species: species,
stamina: stamina,
fortune: fortune,
agility: agility,
offense: offense,
defense: defense
});
freakers[freakerId] = freaker;
uint128 value = uint128(msg.value);
uint128 half = value / 2;
_dissipateEnergyIntoPool(half);
energyBalances[freakerId] = EnergyBalance({
basic: half,
index: freakerIndex
});
totalFortune += fortune;
birthCertificates.push(msg.value);
emit Born(to, value, freakerId, freaker);
_safeMint(to, freakerId, "");
}
function attack(uint128 sourceId, uint128 targetId) public returns (bool) {
address attacker = ownerOf(sourceId);
address defender = ownerOf(targetId);
require(attacker != defender, "Cannot attack self");
require(attacker == msg.sender, "Sender does not own source");
if (isEnlightened(sourceId) || isEnlightened(targetId)) {
revert("Enlightened beings can neither attack nor be attacked");
}
Freaker memory source = freakers[sourceId];
Freaker memory target = freakers[targetId];
if (_randomishIntLessThan("hit?", source.agility + target.agility) > source.agility) {
uint128 sourceCharge = energyOf(sourceId);
uint128 sourceSpent = sourceCharge * (1 * (10 - source.stamina)) / 1000;
energyBalances[sourceId] = EnergyBalance({
basic: sourceCharge - sourceSpent,
index: freakerIndex
});
_dissipateEnergyIntoPool(sourceSpent);
emit Missed(attacker, defender, sourceId, targetId);
return false;
}
if (_randomishIntLessThan("win?", attackPower(sourceId)) < defendPower(targetId)) {
uint128 sourceCharge = energyOf(sourceId);
uint128 targetCharge = energyOf(targetId);
uint128 sourceSpent = sourceCharge * (1 * (10 - source.stamina)) / 100;
uint128 targetSpent = targetCharge * (1 * (10 - target.stamina)) / 100;
energyBalances[sourceId] = EnergyBalance({
basic: sourceCharge - sourceSpent,
index: freakerIndex
});
energyBalances[targetId] = EnergyBalance({
basic: targetCharge - targetSpent,
index: freakerIndex
});
_dissipateEnergyIntoPool(sourceSpent);
_dissipateEnergyIntoPool(targetSpent);
emit Thwarted(attacker, defender, sourceId, targetId);
return false;
} else {
uint128 sourceCharge = energyOf(sourceId);
uint128 targetCharge = energyOf(targetId);
uint128 sourceSpent = sourceCharge * (2 * (10 - source.stamina)) / 100;
uint128 sourceRemaining = sourceCharge - sourceSpent;
if (!payable(defender).send(targetCharge)) {
creatorIndex += targetCharge / 8;
}
_transfer(defender, attacker, targetId);
_dissipateEnergyIntoPool(sourceSpent);
uint128 half = sourceRemaining / 2;
energyBalances[sourceId] = EnergyBalance({
basic: half,
index: freakerIndex
});
energyBalances[targetId] = EnergyBalance({
basic: half,
index: freakerIndex
});
emit Captured(attacker, defender, sourceId, targetId);
return true;
}
}
function tap(uint128 creatorId) public {
require(isCreator(creatorId), "Not a creator");
address owner = ownerOf(creatorId);
uint128 unclaimed = creatorIndex - energyBalances[creatorId].index;
energyBalances[creatorId].index = creatorIndex;
payable(owner).transfer(unclaimed);
}
function charge(uint128 freakerId) payable public {
address owner = ownerOf(freakerId);
require(msg.sender == owner, "Sender does not own freaker");
require(isFreaker(freakerId), "Not a freaker");
EnergyBalance memory balance = energyBalances[freakerId];
energyBalances[freakerId] = EnergyBalance({
basic: balance.basic + uint128(msg.value),
index: balance.index
});
}
function discharge(uint128 freakerId, uint128 amount) public {
address owner = ownerOf(freakerId);
require(msg.sender == owner, "Sender does not own freaker");
require(isFreaker(freakerId), "Not a freaker");
uint128 energy = energyOf(freakerId);
uint128 capped = amount > energy ? energy : amount;
energyBalances[freakerId] = EnergyBalance({
basic: energy - capped,
index: freakerIndex
});
payable(owner).transfer(capped);
}
function isCreator(uint256 tokenId) public pure returns (bool) { return tokenId < 8; }
function isFreaker(uint256 tokenId) public pure returns (bool) { return tokenId >= 8; }
function isEnlightened(uint128 tokenId) public view returns (bool) {
if (isCreator(tokenId)) {
return true;
}
address owner = ownerOf(tokenId);
SpeciesCounter memory c = speciesCounters[owner];
return (
c.pluto > 0 && c.mercury > 0 && c.saturn > 0 && c.uranus > 0 &&
c.venus > 0 && c.mars > 0 && c.neptune > 0 && c.jupiter > 0
);
}
function energyOf(uint128 tokenId) public view returns (uint128) {
if (isCreator(tokenId)) {
EnergyBalance memory balance = energyBalances[tokenId];
return balance.basic + (creatorIndex - balance.index);
} else {
Freaker memory freaker = freakers[tokenId];
EnergyBalance memory balance = energyBalances[tokenId];
return balance.basic + (freakerIndex - balance.index) * freaker.fortune;
}
}
function attackPower(uint128 freakerId) public view returns (uint128) {
address attacker = ownerOf(freakerId);
return combatMultipliers[attacker].attack * energyOf(freakerId);
}
function defendPower(uint128 freakerId) public view returns (uint128) {
address defender = ownerOf(freakerId);
return combatMultipliers[defender].defend * energyOf(freakerId);
}
function middlePrice() public view returns (uint256) {
uint256 length = birthCertificates.length;
return length > 0 ? birthCertificates[length / 2] : 0;
}
function _randomishIntLessThan(bytes32 salt, uint256 n) internal view returns (uint256) {
if (n == 0)
return 0;
return uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, salt))) % n;
}
function _dissipateEnergyIntoPool(uint128 amount) internal {
if (amount > 0) {
if (totalFortune > 0) {
uint128 creatorAmount = amount * 20 / 100;
uint128 freakerAmount = amount * 80 / 100;
creatorIndex += creatorAmount / 8;
freakerIndex += freakerAmount / totalFortune;
} else {
creatorIndex += amount / 8;
}
}
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override {
if (isFreaker(tokenId)) {
uint128 freakerId = uint128(tokenId);
Freaker memory freaker = freakers[freakerId];
if (from != address(0)) {
CombatMultipliers memory multipliers = combatMultipliers[from];
combatMultipliers[from] = CombatMultipliers({
attack: multipliers.attack - freaker.offense * uint128(freaker.offense),
defend: multipliers.defend - freaker.defense * uint128(freaker.defense)
});
_countSpecies(from, freaker.species, -1);
}
if (to != address(0)) {
CombatMultipliers memory multipliers = combatMultipliers[to];
combatMultipliers[to] = CombatMultipliers({
attack: multipliers.attack + freaker.offense * uint128(freaker.offense),
defend: multipliers.defend + freaker.defense * uint128(freaker.defense)
});
_countSpecies(to, freaker.species, 1);
}
if (from != address(0) && to != address(0)) {
uint128 freakerCharge = energyOf(freakerId);
uint128 freakerSpent = freakerCharge / 1000;
energyBalances[freakerId] = EnergyBalance({
basic: freakerCharge - freakerSpent,
index: freakerIndex
});
_dissipateEnergyIntoPool(freakerSpent);
}
}
}
function _countSpecies(address account, uint8 species, int8 delta) internal {
if (species < 4) {
if (species < 2) {
if (species == 0) {
speciesCounters[account].pluto += delta;
} else {
speciesCounters[account].mercury += delta;
}
} else {
if (species == 2) {
speciesCounters[account].saturn += delta;
} else {
speciesCounters[account].uranus += delta;
}
}
} else {
if (species < 6) {
if (species == 4) {
speciesCounters[account].venus += delta;
} else {
speciesCounters[account].mars += delta;
}
} else {
if (species == 6) {
speciesCounters[account].neptune += delta;
} else {
speciesCounters[account].jupiter += delta;
}
}
}
}
}
文件 7 的 14:FreakerAttack.sol
pragma solidity ^0.8.2;
import "./EtherFreakers.sol";
import "./FreakerFortress.sol";
contract FreakerAttack {
address payable public owner;
address public etherFreakersAddress;
constructor(address payable creator, address _etherFreakersAddress) {
owner = creator;
etherFreakersAddress = _etherFreakersAddress;
}
function attack(address payable onBehalfOf, uint128 sourceId, uint128 targetId) external returns (bool) {
require(msg.sender == owner, "FreakerAttack: Only owner");
require(address(this) == EtherFreakers(etherFreakersAddress).ownerOf(sourceId), "FreakerAttack: does not own sourceId");
bool success = EtherFreakers(etherFreakersAddress).attack(sourceId, targetId);
if(success){
EtherFreakers(etherFreakersAddress).approve(owner, targetId);
FreakerFortress(owner).depositFreakerFree(onBehalfOf, targetId);
return true;
}
return false;
}
function sendBack(uint128[] calldata freakers) external {
for(uint i=0; i < freakers.length; i++){
EtherFreakers(etherFreakersAddress).transferFrom(address(this), owner, freakers[i]);
}
}
}
文件 8 的 14:FreakerFortress.sol
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "./EtherFreakers.sol";
import "./FreakerAttack.sol";
contract FreakerFortress is ERC721, ERC721Holder {
address public manager;
uint128 public joinFeeWei = 1e17;
uint128 public attackFeeWei = 5e17;
address public etherFreakersAddress;
address public attackContract;
uint8 public maxRemoteAttackers = 4;
constructor(address author, address _etherFreakersAddress) ERC721("FreakerFortress", "FEFKR") {
manager = author;
etherFreakersAddress = _etherFreakersAddress;
}
modifier ownerOrApproved(uint128 freakerID) {
require(_isApprovedOrOwner(msg.sender, freakerID), "FreakerFortress: caller is not owner nor approved");
_;
}
modifier managerOnly() {
require(msg.sender == manager, "FreakerFortress: caller is not owner nor approved");
_;
}
function depositFreaker(address payable mintTo, uint128 freakerID) payable external {
require(msg.value >= joinFeeWei, "FreakerFortress: Join fee too low");
EtherFreakers(etherFreakersAddress).transferFrom(msg.sender, address(this), freakerID);
_safeMint(mintTo, freakerID, "");
}
function depositFreakerFree(address payable mintTo, uint128 freakerID) payable external {
require(msg.sender == attackContract, "FreakerFortress: Attack contract only");
EtherFreakers(etherFreakersAddress).transferFrom(msg.sender, address(this), freakerID);
_safeMint(mintTo, freakerID, "");
}
function withdrawFreaker(address to, uint128 freakerID) payable external ownerOrApproved(freakerID) {
EtherFreakers(etherFreakersAddress).safeTransferFrom(address(this), to, freakerID);
_burn(freakerID);
}
function discharge(uint128 freakerID, uint128 amount) public {
require(ownerOf(freakerID) == msg.sender, "FreakerFortress: only owner");
uint128 energy = EtherFreakers(etherFreakersAddress).energyOf(freakerID);
uint128 capped = amount > energy ? energy : amount;
EtherFreakers(etherFreakersAddress).discharge(freakerID, amount);
address owner = ownerOf(freakerID);
payable(owner).transfer(capped);
}
function charge(uint128 freakerID) payable ownerOrApproved(freakerID) public {
EtherFreakers(etherFreakersAddress).charge{value: msg.value}(freakerID);
}
function tokenURI(uint256 tokenID) public view virtual override returns (string memory) {
return EtherFreakers(etherFreakersAddress).tokenURI(tokenID);
}
function claimToken(address to, uint256 freakerID) payable external {
require(!_exists(freakerID), "FreakerFortress: token has owner");
require(EtherFreakers(etherFreakersAddress).ownerOf(freakerID) == address(this), "FreakerFortress: fortress does not own token");
_safeMint(to, freakerID, "");
}
function createAttackContract() external {
require(attackContract == address(0), "FreakerFortress: attack contract already exists");
attackContract = address(new FreakerAttack(payable(address(this)), etherFreakersAddress));
}
function remoteAttack(uint128[] calldata freakers, uint128 sourceId, uint128 targetId) external payable returns(bool response) {
require(msg.value >= attackFeeWei, "FreakerFortress: Attack fee too low");
require(attackContract != address(0), "FreakerFortress: attack contract does not exist");
require(EtherFreakers(etherFreakersAddress).ownerOf(targetId) != address(this), "FreakerFortress: cannot attack freak in fortress");
require(!EtherFreakers(etherFreakersAddress).isEnlightened(targetId), "FreakerFortress: target is enlightened");
require(freakers.length <= maxRemoteAttackers, "FreakerFortress: too many attackers");
for(uint i=0; i < freakers.length; i++){
EtherFreakers(etherFreakersAddress).transferFrom(address(this), attackContract, freakers[i]);
}
response = FreakerAttack(attackContract).attack(payable(msg.sender), sourceId, targetId);
FreakerAttack(attackContract).sendBack(freakers);
}
function updateFightFee(uint128 _fee) external managerOnly {
attackFeeWei = _fee;
}
function updateJoinFee(uint128 _fee) external managerOnly {
joinFeeWei = _fee;
}
function updateManager(address _manager) external managerOnly {
manager = _manager;
}
function updateMaxRemoteAttackers(uint8 count) external managerOnly {
maxRemoteAttackers = count;
}
function payManager(uint256 amount) external managerOnly {
require(amount <= address(this).balance, "FreakerFortress: amount too high");
payable(manager).transfer(amount);
}
receive() payable external {
}
}
文件 9 的 14:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 10 的 14:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
文件 11 的 14:IERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 12 的 14:IERC721Metadata.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 13 的 14:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}
文件 14 的 14:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant alphabet = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = alphabet[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
{
"compilationTarget": {
"contracts/FreakerFortress.sol": "FreakerFortress"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"author","type":"address"},{"internalType":"address","name":"_etherFreakersAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"attackContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"attackFeeWei","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"freakerID","type":"uint128"}],"name":"charge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"freakerID","type":"uint256"}],"name":"claimToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"createAttackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"mintTo","type":"address"},{"internalType":"uint128","name":"freakerID","type":"uint128"}],"name":"depositFreaker","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"mintTo","type":"address"},{"internalType":"uint128","name":"freakerID","type":"uint128"}],"name":"depositFreakerFree","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint128","name":"freakerID","type":"uint128"},{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"discharge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"etherFreakersAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"joinFeeWei","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRemoteAttackers","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"payManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128[]","name":"freakers","type":"uint128[]"},{"internalType":"uint128","name":"sourceId","type":"uint128"},{"internalType":"uint128","name":"targetId","type":"uint128"}],"name":"remoteAttack","outputs":[{"internalType":"bool","name":"response","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenID","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_fee","type":"uint128"}],"name":"updateFightFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_fee","type":"uint128"}],"name":"updateJoinFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"updateManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"count","type":"uint8"}],"name":"updateMaxRemoteAttackers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint128","name":"freakerID","type":"uint128"}],"name":"withdrawFreaker","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]