编译器
0.8.13+commit.abaa5c0e
文件 1 的 12:ERC1155.sol
pragma solidity >=0.8.0;
abstract contract ERC1155 {
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
function uri(uint256 id) public view virtual returns (string memory);
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual {
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual {
uint256 idsLength = ids.length;
require(idsLength == amounts.length, "LENGTH_MISMATCH");
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
for (uint256 i = 0; i < idsLength; ) {
uint256 id = ids[i];
uint256 amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function balanceOfBatch(address[] memory owners, uint256[] memory ids)
public
view
virtual
returns (uint256[] memory balances)
{
uint256 ownersLength = owners.length;
require(ownersLength == ids.length, "LENGTH_MISMATCH");
balances = new uint256[](owners.length);
unchecked {
for (uint256 i = 0; i < ownersLength; i++) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0xd9b67a26 ||
interfaceId == 0x0e89341c;
}
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal {
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal {
uint256 idsLength = ids.length;
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchBurn(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal {
uint256 idsLength = ids.length;
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function _burn(
address from,
uint256 id,
uint256 amount
) internal {
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
interface ERC1155TokenReceiver {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 amount,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external returns (bytes4);
}
文件 2 的 12:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 3 的 12:ERC721.sol
pragma solidity >=0.8.0;
abstract contract ERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
mapping(address => uint256) public balanceOf;
mapping(uint256 => address) public ownerOf;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
function approve(address spender, uint256 id) public virtual {
address owner = ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes memory data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(ownerOf[id] == address(0), "ALREADY_MINTED");
unchecked {
balanceOf[to]++;
}
ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = ownerOf[id];
require(ownerOf[id] != address(0), "NOT_MINTED");
unchecked {
balanceOf[owner]--;
}
delete ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
interface ERC721TokenReceiver {
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4);
}
文件 4 的 12:ExitPayloadReader.sol
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
library ExitPayloadReader {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant WORD_SIZE = 32;
struct ExitPayload {
RLPReader.RLPItem[] data;
}
struct Receipt {
RLPReader.RLPItem[] data;
bytes raw;
uint256 logIndex;
}
struct Log {
RLPReader.RLPItem data;
RLPReader.RLPItem[] list;
}
struct LogTopics {
RLPReader.RLPItem[] data;
}
function copy(
uint256 src,
uint256 dest,
uint256 len
) private pure {
if (len == 0) return;
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
uint256 mask = 256**(WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) {
RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList();
return ExitPayload(payloadData);
}
function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[0].toUint();
}
function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[1].toBytes();
}
function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[2].toUint();
}
function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[3].toUint();
}
function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) {
return bytes32(payload.data[4].toUint());
}
function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) {
return bytes32(payload.data[5].toUint());
}
function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) {
receipt.raw = payload.data[6].toBytes();
RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();
if (receiptItem.isList()) {
receipt.data = receiptItem.toList();
} else {
bytes memory typedBytes = receipt.raw;
bytes memory result = new bytes(typedBytes.length - 1);
uint256 srcPtr;
uint256 destPtr;
assembly {
srcPtr := add(33, typedBytes)
destPtr := add(0x20, result)
}
copy(srcPtr, destPtr, result.length);
receipt.data = result.toRlpItem().toList();
}
receipt.logIndex = getReceiptLogIndex(payload);
return receipt;
}
function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[7].toBytes();
}
function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) {
return payload.data[8].toBytes();
}
function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[8].toUint();
}
function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) {
return payload.data[9].toUint();
}
function toBytes(Receipt memory receipt) internal pure returns (bytes memory) {
return receipt.raw;
}
function getLog(Receipt memory receipt) internal pure returns (Log memory) {
RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
return Log(logData, logData.toList());
}
function getEmitter(Log memory log) internal pure returns (address) {
return RLPReader.toAddress(log.list[0]);
}
function getTopics(Log memory log) internal pure returns (LogTopics memory) {
return LogTopics(log.list[1].toList());
}
function getData(Log memory log) internal pure returns (bytes memory) {
return log.list[2].toBytes();
}
function toRlpBytes(Log memory log) internal pure returns (bytes memory) {
return log.data.toRlpBytes();
}
function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) {
return topics.data[index];
}
}
文件 5 的 12:FXBaseChildTunnel.sol
pragma solidity ^0.8.0;
interface IFxMessageProcessor {
function processMessageFromRoot(
uint256 stateId,
address rootMessageSender,
bytes calldata data
) external;
}
abstract contract FxBaseChildTunnel is IFxMessageProcessor {
event MessageSent(bytes message);
address public fxChild;
address public fxRootTunnel;
constructor(address _fxChild) {
fxChild = _fxChild;
}
modifier validateSender(address sender) {
require(sender == fxRootTunnel, "FxBaseChildTunnel: INVALID_SENDER_FROM_ROOT");
_;
}
function setFxRootTunnel(address _fxRootTunnel) external {
require(fxRootTunnel == address(0x0), "FxBaseChildTunnel: ROOT_TUNNEL_ALREADY_SET");
fxRootTunnel = _fxRootTunnel;
}
function processMessageFromRoot(
uint256 stateId,
address rootMessageSender,
bytes calldata data
) external override {
require(msg.sender == fxChild, "FxBaseChildTunnel: INVALID_SENDER");
_processMessageFromRoot(stateId, rootMessageSender, data);
}
function _sendMessageToRoot(bytes memory message) internal {
emit MessageSent(message);
}
function _processMessageFromRoot(
uint256 stateId,
address sender,
bytes memory message
) internal virtual;
}
文件 6 的 12:FxBaseRootTunnel.sol
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
import {MerklePatriciaProof} from "./MerklePatriciaProof.sol";
import {Merkle} from "./Merkle.sol";
import "./ExitPayloadReader.sol";
interface IFxStateSender {
function sendMessageToChild(address _receiver, bytes calldata _data) external;
}
contract ICheckpointManager {
struct HeaderBlock {
bytes32 root;
uint256 start;
uint256 end;
uint256 createdAt;
address proposer;
}
mapping(uint256 => HeaderBlock) public headerBlocks;
}
abstract contract FxBaseRootTunnel {
using RLPReader for RLPReader.RLPItem;
using Merkle for bytes32;
using ExitPayloadReader for bytes;
using ExitPayloadReader for ExitPayloadReader.ExitPayload;
using ExitPayloadReader for ExitPayloadReader.Log;
using ExitPayloadReader for ExitPayloadReader.LogTopics;
using ExitPayloadReader for ExitPayloadReader.Receipt;
bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;
IFxStateSender public fxRoot;
ICheckpointManager public checkpointManager;
address public fxChildTunnel;
mapping(bytes32 => bool) public processedExits;
constructor(address _checkpointManager, address _fxRoot) {
checkpointManager = ICheckpointManager(_checkpointManager);
fxRoot = IFxStateSender(_fxRoot);
}
function setFxChildTunnel(address _fxChildTunnel) public {
require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
fxChildTunnel = _fxChildTunnel;
}
function _sendMessageToChild(bytes memory message) internal {
fxRoot.sendMessageToChild(fxChildTunnel, message);
}
function _validateAndExtractMessage(bytes memory inputData) internal returns (bytes memory) {
ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();
bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
uint256 blockNumber = payload.getBlockNumber();
bytes32 exitHash = keccak256(
abi.encodePacked(
blockNumber,
MerklePatriciaProof._getNibbleArray(branchMaskBytes),
payload.getReceiptLogIndex()
)
);
require(processedExits[exitHash] == false, "FxRootTunnel: EXIT_ALREADY_PROCESSED");
processedExits[exitHash] = true;
ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
ExitPayloadReader.Log memory log = receipt.getLog();
require(fxChildTunnel == log.getEmitter(), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");
bytes32 receiptRoot = payload.getReceiptRoot();
require(
MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot),
"FxRootTunnel: INVALID_RECEIPT_PROOF"
);
_checkBlockMembershipInCheckpoint(
blockNumber,
payload.getBlockTime(),
payload.getTxRoot(),
receiptRoot,
payload.getHeaderNumber(),
payload.getBlockProof()
);
ExitPayloadReader.LogTopics memory topics = log.getTopics();
require(
bytes32(topics.getField(0).toUint()) == SEND_MESSAGE_EVENT_SIG,
"FxRootTunnel: INVALID_SIGNATURE"
);
bytes memory message = abi.decode(log.getData(), (bytes));
return message;
}
function _checkBlockMembershipInCheckpoint(
uint256 blockNumber,
uint256 blockTime,
bytes32 txRoot,
bytes32 receiptRoot,
uint256 headerNumber,
bytes memory blockProof
) private view returns (uint256) {
(bytes32 headerRoot, uint256 startBlock, , uint256 createdAt, ) = checkpointManager.headerBlocks(headerNumber);
require(
keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
blockNumber - startBlock,
headerRoot,
blockProof
),
"FxRootTunnel: INVALID_HEADER"
);
return createdAt;
}
function receiveMessage(bytes memory inputData) public virtual {
bytes memory message = _validateAndExtractMessage(inputData);
_processMessageFromChild(message);
}
function _processMessageFromChild(bytes memory message) internal virtual;
}
文件 7 的 12:LlamaZoo.sol
pragma solidity ^0.8.12;
import "./ERC1155.sol";
import "./SpitToken.sol";
import "./ERC721.sol";
import "./FxBaseRootTunnel.sol";
import "./Ownable.sol";
contract LlamaZoo is FxBaseRootTunnel, Ownable {
ERC721 public pixellatedLlamaContract;
ERC721 public llamaDrawsContract;
ERC1155 public boostContract;
ERC1155 public pixletContract;
struct Staker {
uint256[] stakedLlamas;
uint256 stakedPixletCanvas;
uint256 stakedLlamaDraws;
uint128 stakedSilverBoosts;
uint128 stakedGoldBoosts;
}
mapping(address => Staker) public userInfo;
bool public stakingPaused;
constructor(
address checkpointManager,
address fxRoot,
address _pixellatedLlamaContract,
address _llamaDrawsContract,
address _boostContract,
address _pixletContract
) FxBaseRootTunnel(checkpointManager, fxRoot) {
pixellatedLlamaContract = ERC721(_pixellatedLlamaContract);
llamaDrawsContract = ERC721(_llamaDrawsContract);
boostContract = ERC1155(_boostContract);
pixletContract = ERC1155(_pixletContract);
}
function setContractAddresses(
address _pixellatedLlamaContract,
address _llamaDrawsContract,
address _boostContract,
address _pixletContract
) public onlyOwner {
pixellatedLlamaContract = ERC721(_pixellatedLlamaContract);
llamaDrawsContract = ERC721(_llamaDrawsContract);
boostContract = ERC1155(_boostContract);
pixletContract = ERC1155(_pixletContract);
}
function setStakingPaused(bool paused) public onlyOwner {
stakingPaused = paused;
}
function balanceOf(address owner) public view returns (uint256) {
uint[] memory llamas = userInfo[owner].stakedLlamas;
if(llamas.length == 0) return 0;
for (uint256 i = 0; i < llamas.length; i++) {
if(llamas[i] < 500) return 1;
}
return 2;
}
function getStakedTokens(address user)
public
view
returns (
uint256[] memory llamas,
uint256 pixletCanvas,
uint256 llamaDraws,
uint128 silverBoosts,
uint128 goldBoosts
)
{
Staker memory staker = userInfo[user];
return (
staker.stakedLlamas,
staker.stakedPixletCanvas,
staker.stakedLlamaDraws,
staker.stakedSilverBoosts,
staker.stakedGoldBoosts
);
}
function bulkStake(
uint256[] memory llamas,
uint128 silverBoosts,
uint128 goldBoosts,
uint256 pixletStudio,
uint256 llamaDraws
) public {
if (llamas.length > 0) stakeMultipleLlamas(llamas);
stakeBoosts(silverBoosts, goldBoosts);
if (pixletStudio != 0) stakePixletCanvas(pixletStudio);
if (llamaDraws != 0) stakeLlamaDraws(llamaDraws);
}
function bulkUnstake(
uint256[] memory llamas,
uint128 silverBoosts,
uint128 goldBoosts,
bool pixletStudio,
bool llamaDraws
) public {
if (llamas.length > 0) unstakeMultipleLlamas(llamas);
unstakeBoosts(silverBoosts, goldBoosts);
if (pixletStudio) unstakePixletCanvas();
if (llamaDraws) unstakeLlamaDraws();
}
function stakeMultipleLlamas(uint256[] memory tokenIds) public {
require(!stakingPaused, "Staking is currently paused.");
uint256 animatedCount = 0;
Staker storage staker = userInfo[msg.sender];
for (uint256 i = 0; i < tokenIds.length; i++) {
if (tokenIds[i] < 500) ++animatedCount;
staker.stakedLlamas.push(tokenIds[i]);
pixellatedLlamaContract.transferFrom(
msg.sender,
address(this),
tokenIds[i]
);
}
if (animatedCount > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.AnimatedLlama),
animatedCount,
true
)
);
}
if ((tokenIds.length - animatedCount) > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.StaticLlama),
tokenIds.length - animatedCount,
true
)
);
}
}
function unstakeMultipleLlamas(uint256[] memory tokenIds) public {
require(!stakingPaused, "Staking is currently paused.");
uint256 animatedCount = 0;
Staker storage staker = userInfo[msg.sender];
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
require(containsElement(staker.stakedLlamas, tokenId), "You do not own this llama.");
if (tokenId < 500) ++animatedCount;
pixellatedLlamaContract.transferFrom(
address(this),
msg.sender,
tokenId
);
uint256[] memory stakedLlamas = staker.stakedLlamas;
uint256 index;
for (uint256 j; j < stakedLlamas.length; j++) {
if (stakedLlamas[j] == tokenId) index = j;
}
if (stakedLlamas[index] == tokenId) {
staker.stakedLlamas[index] = stakedLlamas[
staker.stakedLlamas.length - 1
];
staker.stakedLlamas.pop();
}
}
if (animatedCount > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.AnimatedLlama),
animatedCount,
false
)
);
}
if ((tokenIds.length - animatedCount) > 0) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.StaticLlama),
tokenIds.length - animatedCount,
false
)
);
}
}
function stakeLlama(uint256 tokenId) external {
require(!stakingPaused, "Staking is currently paused.");
bool animated = tokenId < 500;
Staker storage staker = userInfo[msg.sender];
staker.stakedLlamas.push(tokenId);
pixellatedLlamaContract.transferFrom(
msg.sender,
address(this),
tokenId
);
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(
animated ? TokenType.AnimatedLlama : TokenType.StaticLlama
),
1,
true
)
);
}
function unstakeLlama(uint256 tokenId) external {
require(!stakingPaused, "Staking is currently paused.");
bool animated = tokenId < 500;
Staker storage staker = userInfo[msg.sender];
require(containsElement(staker.stakedLlamas, tokenId), "You do not own this llama.");
pixellatedLlamaContract.transferFrom(
address(this),
msg.sender,
tokenId
);
uint256[] memory stakedLlamas = staker.stakedLlamas;
uint256 index;
for (uint256 i; i < stakedLlamas.length; i++) {
if (stakedLlamas[i] == tokenId) index = i;
}
if (stakedLlamas[index] == tokenId) {
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(
animated
? TokenType.AnimatedLlama
: TokenType.StaticLlama
),
1,
false
)
);
staker.stakedLlamas[index] = stakedLlamas[
staker.stakedLlamas.length - 1
];
staker.stakedLlamas.pop();
}
}
function stakeSilverBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Staking 0 is not allowed.");
userInfo[msg.sender].stakedSilverBoosts += amount;
boostContract.safeTransferFrom(
msg.sender,
address(this),
2,
amount,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.SilverBoost), amount, true)
);
}
function unstakeSilverBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Unstaking 0 is not allowed.");
userInfo[msg.sender].stakedSilverBoosts -= amount;
boostContract.safeTransferFrom(
address(this),
msg.sender,
2,
amount,
""
);
_sendMessageToChild(
abi.encode(
msg.sender,
uint256(TokenType.SilverBoost),
amount,
false
)
);
}
function stakeGoldBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Staking 0 is not allowed.");
userInfo[msg.sender].stakedGoldBoosts += amount;
boostContract.safeTransferFrom(
msg.sender,
address(this),
1,
amount,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.GoldBoost), amount, true)
);
}
function unstakeGoldBoosts(uint128 amount) public {
require(!stakingPaused, "Staking is currently paused.");
require(amount != 0, "Unstaking 0 is not allowed.");
userInfo[msg.sender].stakedGoldBoosts -= amount;
boostContract.safeTransferFrom(
address(this),
msg.sender,
1,
amount,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.GoldBoost), amount, false)
);
}
function stakeBoosts(uint128 silverAmount, uint128 goldAmount) public {
if (silverAmount != 0) stakeSilverBoosts(silverAmount);
if (goldAmount != 0) stakeGoldBoosts(goldAmount);
}
function unstakeBoosts(uint128 silverAmount, uint128 goldAmount) public {
if (silverAmount != 0) unstakeSilverBoosts(silverAmount);
if (goldAmount != 0) unstakeGoldBoosts(goldAmount);
}
function stakePixletCanvas(uint256 tokenId) public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedPixletCanvas == 0,
"You already have a pixlet canvas staked."
);
userInfo[msg.sender].stakedPixletCanvas = tokenId;
pixletContract.safeTransferFrom(
msg.sender,
address(this),
tokenId,
1,
""
);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.PixletCanvas), 1, true)
);
}
function unstakePixletCanvas() public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedPixletCanvas != 0,
"You do not have a pixlet canvas staked."
);
pixletContract.safeTransferFrom(
address(this),
msg.sender,
userInfo[msg.sender].stakedPixletCanvas,
1,
""
);
userInfo[msg.sender].stakedPixletCanvas = 0;
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.PixletCanvas), 1, false)
);
}
function stakeLlamaDraws(uint256 tokenId) public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedLlamaDraws == 0,
"You already have a llamadraws staked."
);
userInfo[msg.sender].stakedLlamaDraws = tokenId;
llamaDrawsContract.transferFrom(msg.sender, address(this), tokenId);
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.LlamaDraws), 1, true)
);
}
function unstakeLlamaDraws() public {
require(!stakingPaused, "Staking is currently paused.");
require(
userInfo[msg.sender].stakedLlamaDraws != 0,
"You do not have a llamadraws staked."
);
llamaDrawsContract.transferFrom(
address(this),
msg.sender,
userInfo[msg.sender].stakedLlamaDraws
);
userInfo[msg.sender].stakedLlamaDraws = 0;
_sendMessageToChild(
abi.encode(msg.sender, uint256(TokenType.LlamaDraws), 1, false)
);
}
function _processMessageFromChild(bytes memory message) internal override {
}
function containsElement(uint[] memory elements, uint tokenId) internal returns (bool) {
for (uint256 i = 0; i < elements.length; i++) {
if(elements[i] == tokenId) return true;
}
return false;
}
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 amount,
bytes calldata data
) external returns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external returns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}
文件 8 的 12:Merkle.sol
pragma solidity ^0.8.0;
library Merkle {
function checkMembership(
bytes32 leaf,
uint256 index,
bytes32 rootHash,
bytes memory proof
) internal pure returns (bool) {
require(proof.length % 32 == 0, "Invalid proof length");
uint256 proofHeight = proof.length / 32;
require(index < 2**proofHeight, "Leaf index is too big");
bytes32 proofElement;
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
assembly {
proofElement := mload(add(proof, i))
}
if (index % 2 == 0) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
index = index / 2;
}
return computedHash == rootHash;
}
}
文件 9 的 12:MerklePatriciaProof.sol
pragma solidity ^0.8.0;
import {RLPReader} from "./RLPReader.sol";
library MerklePatriciaProof {
function verify(
bytes memory value,
bytes memory encodedPath,
bytes memory rlpParentNodes,
bytes32 root
) internal pure returns (bool) {
RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);
bytes memory currentNode;
RLPReader.RLPItem[] memory currentNodeList;
bytes32 nodeKey = root;
uint256 pathPtr = 0;
bytes memory path = _getNibbleArray(encodedPath);
if (path.length == 0) {
return false;
}
for (uint256 i = 0; i < parentNodes.length; i++) {
if (pathPtr > path.length) {
return false;
}
currentNode = RLPReader.toRlpBytes(parentNodes[i]);
if (nodeKey != keccak256(currentNode)) {
return false;
}
currentNodeList = RLPReader.toList(parentNodes[i]);
if (currentNodeList.length == 17) {
if (pathPtr == path.length) {
if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) {
return true;
} else {
return false;
}
}
uint8 nextPathNibble = uint8(path[pathPtr]);
if (nextPathNibble > 16) {
return false;
}
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble]));
pathPtr += 1;
} else if (currentNodeList.length == 2) {
uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr);
if (pathPtr + traversed == path.length) {
if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) {
return true;
} else {
return false;
}
}
if (traversed == 0) {
return false;
}
pathPtr += traversed;
nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
} else {
return false;
}
}
}
function _nibblesToTraverse(
bytes memory encodedPartialPath,
bytes memory path,
uint256 pathPtr
) private pure returns (uint256) {
uint256 len = 0;
bytes memory partialPath = _getNibbleArray(encodedPartialPath);
bytes memory slicedPath = new bytes(partialPath.length);
for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
bytes1 pathNibble = path[i];
slicedPath[i - pathPtr] = pathNibble;
}
if (keccak256(partialPath) == keccak256(slicedPath)) {
len = partialPath.length;
} else {
len = 0;
}
return len;
}
function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) {
bytes memory nibbles = "";
if (b.length > 0) {
uint8 offset;
uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
if (hpNibble == 1 || hpNibble == 3) {
nibbles = new bytes(b.length * 2 - 1);
bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
nibbles[0] = oddNibble;
offset = 1;
} else {
nibbles = new bytes(b.length * 2 - 2);
offset = 0;
}
for (uint256 i = offset; i < nibbles.length; i++) {
nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
}
}
return nibbles;
}
function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) {
return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10);
}
}
文件 10 的 12:Ownable.sol
pragma solidity ^0.8.10;
error NotOwner();
abstract contract Ownable {
address internal _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
modifier onlyOwner() {
require(_owner == msg.sender);
_;
}
constructor() {
_owner = msg.sender;
}
function owner() external view returns (address) {
return _owner;
}
function transferOwnership(address _newOwner) external {
if (msg.sender != _owner) revert NotOwner();
_owner = _newOwner;
}
function renounceOwnership() public {
if (msg.sender != _owner) revert NotOwner();
_owner = address(0);
}
function supportsInterface(bytes4 interfaceId)
public
pure
virtual
returns (bool)
{
return interfaceId == 0x7f5828d0;
}
}
文件 11 的 12:RLPReader.sol
pragma solidity ^0.8.0;
library RLPReader {
uint8 constant STRING_SHORT_START = 0x80;
uint8 constant STRING_LONG_START = 0xb8;
uint8 constant LIST_SHORT_START = 0xc0;
uint8 constant LIST_LONG_START = 0xf8;
uint8 constant WORD_SIZE = 32;
struct RLPItem {
uint256 len;
uint256 memPtr;
}
struct Iterator {
RLPItem item;
uint256 nextPtr;
}
function next(Iterator memory self) internal pure returns (RLPItem memory) {
require(hasNext(self));
uint256 ptr = self.nextPtr;
uint256 itemLength = _itemLength(ptr);
self.nextPtr = ptr + itemLength;
return RLPItem(itemLength, ptr);
}
function hasNext(Iterator memory self) internal pure returns (bool) {
RLPItem memory item = self.item;
return self.nextPtr < item.memPtr + item.len;
}
function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
uint256 memPtr;
assembly {
memPtr := add(item, 0x20)
}
return RLPItem(item.length, memPtr);
}
function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
require(isList(self));
uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
return Iterator(self, ptr);
}
function rlpLen(RLPItem memory item) internal pure returns (uint256) {
return item.len;
}
function payloadLen(RLPItem memory item) internal pure returns (uint256) {
return item.len - _payloadOffset(item.memPtr);
}
function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
require(isList(item));
uint256 items = numItems(item);
RLPItem[] memory result = new RLPItem[](items);
uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
uint256 dataLen;
for (uint256 i = 0; i < items; i++) {
dataLen = _itemLength(memPtr);
result[i] = RLPItem(dataLen, memPtr);
memPtr = memPtr + dataLen;
}
return result;
}
function isList(RLPItem memory item) internal pure returns (bool) {
if (item.len == 0) return false;
uint8 byte0;
uint256 memPtr = item.memPtr;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < LIST_SHORT_START) return false;
return true;
}
function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
uint256 ptr = item.memPtr;
uint256 len = item.len;
bytes32 result;
assembly {
result := keccak256(ptr, len)
}
return result;
}
function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) {
uint256 offset = _payloadOffset(item.memPtr);
uint256 memPtr = item.memPtr + offset;
uint256 len = item.len - offset;
return (memPtr, len);
}
function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
(uint256 memPtr, uint256 len) = payloadLocation(item);
bytes32 result;
assembly {
result := keccak256(memPtr, len)
}
return result;
}
function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
bytes memory result = new bytes(item.len);
if (result.length == 0) return result;
uint256 ptr;
assembly {
ptr := add(0x20, result)
}
copy(item.memPtr, ptr, item.len);
return result;
}
function toBoolean(RLPItem memory item) internal pure returns (bool) {
require(item.len == 1);
uint256 result;
uint256 memPtr = item.memPtr;
assembly {
result := byte(0, mload(memPtr))
}
return result == 0 ? false : true;
}
function toAddress(RLPItem memory item) internal pure returns (address) {
require(item.len == 21);
return address(uint160(toUint(item)));
}
function toUint(RLPItem memory item) internal pure returns (uint256) {
require(item.len > 0 && item.len <= 33);
uint256 offset = _payloadOffset(item.memPtr);
uint256 len = item.len - offset;
uint256 result;
uint256 memPtr = item.memPtr + offset;
assembly {
result := mload(memPtr)
if lt(len, 32) {
result := div(result, exp(256, sub(32, len)))
}
}
return result;
}
function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
require(item.len == 33);
uint256 result;
uint256 memPtr = item.memPtr + 1;
assembly {
result := mload(memPtr)
}
return result;
}
function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
require(item.len > 0);
uint256 offset = _payloadOffset(item.memPtr);
uint256 len = item.len - offset;
bytes memory result = new bytes(len);
uint256 destPtr;
assembly {
destPtr := add(0x20, result)
}
copy(item.memPtr + offset, destPtr, len);
return result;
}
function numItems(RLPItem memory item) private pure returns (uint256) {
if (item.len == 0) return 0;
uint256 count = 0;
uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
uint256 endPtr = item.memPtr + item.len;
while (currPtr < endPtr) {
currPtr = currPtr + _itemLength(currPtr);
count++;
}
return count;
}
function _itemLength(uint256 memPtr) private pure returns (uint256) {
uint256 itemLen;
uint256 byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START) itemLen = 1;
else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1;
else if (byte0 < LIST_SHORT_START) {
assembly {
let byteLen := sub(byte0, 0xb7)
memPtr := add(memPtr, 1)
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen)))
itemLen := add(dataLen, add(byteLen, 1))
}
} else if (byte0 < LIST_LONG_START) {
itemLen = byte0 - LIST_SHORT_START + 1;
} else {
assembly {
let byteLen := sub(byte0, 0xf7)
memPtr := add(memPtr, 1)
let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen)))
itemLen := add(dataLen, add(byteLen, 1))
}
}
return itemLen;
}
function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
uint256 byte0;
assembly {
byte0 := byte(0, mload(memPtr))
}
if (byte0 < STRING_SHORT_START) return 0;
else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1;
else if (byte0 < LIST_SHORT_START)
return byte0 - (STRING_LONG_START - 1) + 1;
else return byte0 - (LIST_LONG_START - 1) + 1;
}
function copy(
uint256 src,
uint256 dest,
uint256 len
) private pure {
if (len == 0) return;
for (; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
}
if (len == 0) return;
uint256 mask = 256**(WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
}
文件 12 的 12:SpitToken.sol
pragma solidity ^0.8.12;
import "./ERC20.sol";
import "./FXBaseChildTunnel.sol";
import "./Ownable.sol";
enum TokenType {
StaticLlama,
AnimatedLlama,
SilverBoost,
GoldBoost,
PixletCanvas,
LlamaDraws
}
contract SpitToken is ERC20, FxBaseChildTunnel, Ownable {
struct Rewards {
uint256 staticLlama;
uint256 animatedLlama;
uint256 silverEnergy;
uint256 goldEnergy;
uint256 pixletCanvas;
uint256 llamaDraws;
}
Rewards public rewards;
mapping(address => mapping(uint256 => uint256)) public balances;
mapping(address => uint256) public lastUpdated;
constructor(address _fxChild)
FxBaseChildTunnel(_fxChild)
ERC20("Spit Token", "SPIT", 18)
{
rewards.staticLlama = (uint256(10) * 1e18) / 1 days;
rewards.animatedLlama = (uint256(30) * 1e18) / 1 days;
rewards.silverEnergy = (uint256(4) * 1e18) / 1 days;
rewards.goldEnergy = (uint256(12) * 1e18) / 1 days;
rewards.llamaDraws = (uint256(1) * 1e18) / 1 days;
rewards.pixletCanvas = (uint256(1) * 1e18) / 1 days;
_mint(address(this), 100_000_000 * 1e18);
uint256 allocation = (30_000_000 + 5_000_000 + 5_000_000 + 2_500_000) *
1e18;
balanceOf[address(this)] -= allocation;
unchecked {
balanceOf[
0xcc5cDaB325689Bcd654aB8611c528e60CC8CBe6A
] += (30_000_000 * 1e18);
balanceOf[
0x58B96f5C8ef1CdD7e12a9b71Bbbe575E7B26b142
] += (5_000_000 * 1e18);
balanceOf[
0x58caDf06fcC222f573F81B08B6Cc156e420D35d7
] += (5_000_000 * 1e18);
balanceOf[
0x5D31E4A33470e1a15e54aAdD1d913b613fd0E9ED
] += (2_500_000 * 1e18);
}
emit Transfer(
address(this),
0xcc5cDaB325689Bcd654aB8611c528e60CC8CBe6A,
30_000_000 * 1e18
);
emit Transfer(
address(this),
0x58B96f5C8ef1CdD7e12a9b71Bbbe575E7B26b142,
5_000_000 * 1e18
);
emit Transfer(
address(this),
0x58caDf06fcC222f573F81B08B6Cc156e420D35d7,
5_000_000 * 1e18
);
emit Transfer(
address(this),
0x5D31E4A33470e1a15e54aAdD1d913b613fd0E9ED,
2_500_000 * 1e18
);
}
modifier updateReward(address account) {
uint256 amount = earned(account);
balanceOf[address(this)] -= amount;
unchecked {
balanceOf[account] += amount;
}
lastUpdated[account] = block.timestamp;
emit Transfer(address(this), account, amount);
_;
}
function processStake(
address account,
TokenType tokenType,
uint256 amount
) internal updateReward(account) {
balances[account][uint256(tokenType)] += amount;
}
function processUnstake(
address account,
TokenType tokenType,
uint256 amount
) internal updateReward(account) {
balances[account][uint256(tokenType)] -= amount;
}
function _processMessageFromRoot(
uint256 stateId,
address sender,
bytes memory message
) internal override validateSender(sender) {
(address from, uint256 token, uint256 count, bool action) = abi.decode(
message,
(address, uint256, uint256, bool)
);
action
? processStake(from, TokenType(token), count)
: processUnstake(from, TokenType(token), count);
}
function purchaseUtility(
address owner,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public updateReward(owner) {
permit(owner, msg.sender, value, deadline, v, r, s);
transferFrom(owner, address(this), value);
}
function earned(address account) public view returns (uint256) {
return
spitPerSecond(account) * (block.timestamp - lastUpdated[account]);
}
function totalBalance(address account) public view returns (uint256) {
return balanceOf[account] + earned(account);
}
function spitPerSecond(address account) public view returns (uint256) {
return ((balances[account][0] * rewards.staticLlama) +
(balances[account][1] * rewards.animatedLlama) +
(min(balances[account][2], balances[account][0]) *
rewards.silverEnergy) +
(min(balances[account][3], balances[account][1]) *
rewards.goldEnergy) +
(balances[account][4] * rewards.pixletCanvas) +
(balances[account][5] * rewards.llamaDraws));
}
function burn(uint256 amount) public onlyOwner {
_burn(address(this), amount);
}
function airdrop(address[] calldata accounts, uint256[] calldata amounts) public onlyOwner {
require(accounts.length == amounts.length);
for(uint i = 0; i < accounts.length; i++) {
uint amount = amounts[i];
balanceOf[address(this)] -= amount;
unchecked {
balanceOf[accounts[i]] += amount;
}
emit Transfer(address(this), accounts[i], amount);
}
}
function mint(uint256 amount) public onlyOwner {
_mint(address(this), amount);
}
function withdrawSpit(address recipient, uint256 amount) public onlyOwner {
balanceOf[address(this)] -= amount;
unchecked {
balanceOf[recipient] += amount;
}
emit Transfer(address(this), recipient, amount);
}
function setRewardRates(
uint256 staticLlama,
uint256 animatedLlama,
uint256 silverEnergy,
uint256 goldEnergy,
uint256 pixletCanvas,
uint256 llamaDraws
) public onlyOwner {
rewards.staticLlama = staticLlama;
rewards.animatedLlama = animatedLlama;
rewards.silverEnergy = silverEnergy;
rewards.goldEnergy = goldEnergy;
rewards.pixletCanvas = pixletCanvas;
rewards.llamaDraws = llamaDraws;
}
function updateFxRootRunnel(address _fxRootTunnel) external onlyOwner {
fxRootTunnel = _fxRootTunnel;
}
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return x < y ? x : y;
}
}
{
"compilationTarget": {
"LlamaZoo.sol": "LlamaZoo"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 9999
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"checkpointManager","type":"address"},{"internalType":"address","name":"fxRoot","type":"address"},{"internalType":"address","name":"_pixellatedLlamaContract","type":"address"},{"internalType":"address","name":"_llamaDrawsContract","type":"address"},{"internalType":"address","name":"_boostContract","type":"address"},{"internalType":"address","name":"_pixletContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NotOwner","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"SEND_MESSAGE_EVENT_SIG","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostContract","outputs":[{"internalType":"contract ERC1155","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"llamas","type":"uint256[]"},{"internalType":"uint128","name":"silverBoosts","type":"uint128"},{"internalType":"uint128","name":"goldBoosts","type":"uint128"},{"internalType":"uint256","name":"pixletStudio","type":"uint256"},{"internalType":"uint256","name":"llamaDraws","type":"uint256"}],"name":"bulkStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"llamas","type":"uint256[]"},{"internalType":"uint128","name":"silverBoosts","type":"uint128"},{"internalType":"uint128","name":"goldBoosts","type":"uint128"},{"internalType":"bool","name":"pixletStudio","type":"bool"},{"internalType":"bool","name":"llamaDraws","type":"bool"}],"name":"bulkUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpointManager","outputs":[{"internalType":"contract ICheckpointManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxChildTunnel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fxRoot","outputs":[{"internalType":"contract IFxStateSender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getStakedTokens","outputs":[{"internalType":"uint256[]","name":"llamas","type":"uint256[]"},{"internalType":"uint256","name":"pixletCanvas","type":"uint256"},{"internalType":"uint256","name":"llamaDraws","type":"uint256"},{"internalType":"uint128","name":"silverBoosts","type":"uint128"},{"internalType":"uint128","name":"goldBoosts","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"llamaDrawsContract","outputs":[{"internalType":"contract ERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pixellatedLlamaContract","outputs":[{"internalType":"contract ERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pixletContract","outputs":[{"internalType":"contract ERC1155","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"processedExits","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"inputData","type":"bytes"}],"name":"receiveMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pixellatedLlamaContract","type":"address"},{"internalType":"address","name":"_llamaDrawsContract","type":"address"},{"internalType":"address","name":"_boostContract","type":"address"},{"internalType":"address","name":"_pixletContract","type":"address"}],"name":"setContractAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_fxChildTunnel","type":"address"}],"name":"setFxChildTunnel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"paused","type":"bool"}],"name":"setStakingPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"silverAmount","type":"uint128"},{"internalType":"uint128","name":"goldAmount","type":"uint128"}],"name":"stakeBoosts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"stakeGoldBoosts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"stakeLlama","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"stakeLlamaDraws","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stakeMultipleLlamas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"stakePixletCanvas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"stakeSilverBoosts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"silverAmount","type":"uint128"},{"internalType":"uint128","name":"goldAmount","type":"uint128"}],"name":"unstakeBoosts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"unstakeGoldBoosts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"unstakeLlama","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakeLlamaDraws","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstakeMultipleLlamas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakePixletCanvas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"unstakeSilverBoosts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"stakedPixletCanvas","type":"uint256"},{"internalType":"uint256","name":"stakedLlamaDraws","type":"uint256"},{"internalType":"uint128","name":"stakedSilverBoosts","type":"uint128"},{"internalType":"uint128","name":"stakedGoldBoosts","type":"uint128"}],"stateMutability":"view","type":"function"}]