编译器
0.8.20+commit.a1b79de6
文件 1 的 5:Boba.sol
pragma solidity ^0.8.19;
import "./Metadata.sol";
interface Receiver {
function onERC721Received(
address _operator,
address _from,
uint256 _tokenId,
bytes calldata _data
) external returns (bytes4);
}
interface Router {
function WETH() external pure returns (address);
function factory() external pure returns (address);
function addLiquidityETH(
address,
uint256,
uint256,
uint256,
address,
uint256
) external payable returns (uint256, uint256, uint256);
}
interface Factory {
function createPair(address, address) external returns (address);
}
contract BobaTea {
uint256 private constant UINT_MAX = type(uint256).max;
uint256 private constant TOTAL_SUPPLY = 256;
uint256 private constant LIQUIDITY_TOKENS = 88;
Router private constant ROUTER =
Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
uint256 private constant M1 =
0x5555555555555555555555555555555555555555555555555555555555555555;
uint256 private constant M2 =
0x3333333333333333333333333333333333333333333333333333333333333333;
uint256 private constant M4 =
0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 private constant H01 =
0x0101010101010101010101010101010101010101010101010101010101010101;
bytes32 private constant TRANSFER_TOPIC =
keccak256(bytes("Transfer(address,address,uint256)"));
bytes32 private constant APPROVAL_TOPIC =
keccak256(bytes("Approval(address,address,uint256)"));
uint256 public constant MINT_COST = 0.1 ether;
uint8 public constant decimals = 0;
struct User {
bytes32 mask;
mapping(address => uint256) allowance;
mapping(address => bool) approved;
}
struct Info {
bytes32 salt;
address pair;
address owner;
Metadata metadata;
mapping(address => User) users;
mapping(uint256 => address) approved;
address[] holders;
}
Info private info;
mapping(bytes4 => bool) public supportsInterface;
event Transfer(
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event ERC20Transfer(
bytes32 indexed topic0,
address indexed from,
address indexed to,
uint256 tokens
) anonymous;
event Approval(
address indexed owner,
address indexed spender,
uint256 indexed tokenId
);
event ERC20Approval(
bytes32 indexed topic0,
address indexed owner,
address indexed spender,
uint256 tokens
) anonymous;
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
modifier _onlyOwner() {
require(msg.sender == owner());
_;
}
constructor() payable {
require(msg.value > 0);
info.owner = 0xEF2cffB1c7104AB935cDA78a404a5AEbd85fe517;
info.metadata = new Metadata();
supportsInterface[0x01ffc9a7] = true;
supportsInterface[0x80ac58cd] = true;
supportsInterface[0x5b5e139f] = true;
info.salt = keccak256(
abi.encodePacked("Salt:", blockhash(block.number - 1))
);
}
function setOwner(address _owner) external _onlyOwner {
info.owner = _owner;
}
function setMetadata(Metadata _metadata) external _onlyOwner {
info.metadata = _metadata;
}
function initialize() external {
require(pair() == address(0x0));
address _this = address(this);
address _weth = ROUTER.WETH();
info.users[_this].mask = bytes32(UINT_MAX);
info.holders.push(_this);
emit ERC20Transfer(TRANSFER_TOPIC, address(0x0), _this, TOTAL_SUPPLY);
for (uint256 i = 0; i < TOTAL_SUPPLY; i++) {
emit Transfer(address(0x0), _this, TOTAL_SUPPLY + i + 1);
}
_approveERC20(_this, address(ROUTER), LIQUIDITY_TOKENS);
info.pair = Factory(ROUTER.factory()).createPair(_weth, _this);
ROUTER.addLiquidityETH{value:_this.balance}(_this, LIQUIDITY_TOKENS, 0, 0, owner(), block.timestamp);
_transferERC20(_this, 0xEF2cffB1c7104AB935cDA78a404a5AEbd85fe517, 20);
_transferERC20(_this, owner(), 20);
}
function mint() external payable {
address _this = address(this);
uint256 _available = balanceOf(_this);
require(1 <= _available);
uint256 _cost = 1 * MINT_COST;
require(msg.value >= _cost);
_transferERC20(_this, msg.sender, 1);
payable(owner()).transfer(_cost);
if (msg.value > _cost) {
payable(msg.sender).transfer(msg.value - _cost);
}
}
function approve(address _spender, uint256 _tokens) external returns (bool) {
if (_tokens > TOTAL_SUPPLY && _tokens <= 2 * TOTAL_SUPPLY) {
_approveNFT(_spender, _tokens);
} else {
_approveERC20(msg.sender, _spender, _tokens);
}
return true;
}
function setApprovalForAll(address _operator, bool _approved) external {
info.users[msg.sender].approved[_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function transfer(address _to, uint256 _tokens) external returns (bool) {
_transferERC20(msg.sender, _to, _tokens);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _tokens
) external returns (bool) {
if (_tokens > TOTAL_SUPPLY && _tokens <= 2 * TOTAL_SUPPLY) {
_transferNFT(_from, _to, _tokens);
} else {
uint256 _allowance = allowance(_from, msg.sender);
require(_allowance >= _tokens);
if (_allowance != UINT_MAX) {
info.users[_from].allowance[msg.sender] -= _tokens;
}
_transferERC20(_from, _to, _tokens);
}
return true;
}
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
) external {
safeTransferFrom(_from, _to, _tokenId, "");
}
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
) public {
_transferNFT(_from, _to, _tokenId);
uint32 _size;
assembly {
_size := extcodesize(_to)
}
if (_size > 0) {
require(
Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) ==
0x150b7a02
);
}
}
function bulkTransfer(address _to, uint256[] memory _tokenIds) external {
_transferNFTs(_to, _tokenIds);
}
function owner() public view returns (address) {
return info.owner;
}
function pair() public view returns (address) {
return info.pair;
}
function holders() public view returns (address[] memory) {
return info.holders;
}
function salt() external view returns (bytes32) {
return info.salt;
}
function metadata() external view returns (address) {
return address(info.metadata);
}
function name() external view returns (string memory) {
return info.metadata.name();
}
function symbol() external view returns (string memory) {
return info.metadata.symbol();
}
function tokenURI(uint256 _tokenId) public view returns (string memory) {
return info.metadata.tokenURI(_tokenId);
}
function totalSupply() public pure returns (uint256) {
return TOTAL_SUPPLY;
}
function maskOf(address _user) public view returns (bytes32) {
return info.users[_user].mask;
}
function balanceOf(address _user) public view returns (uint256) {
return _popcount(maskOf(_user));
}
function allowance(
address _user,
address _spender
) public view returns (uint256) {
return info.users[_user].allowance[_spender];
}
function ownerOf(uint256 _tokenId) public view returns (address) {
unchecked {
require(_tokenId > TOTAL_SUPPLY && _tokenId <= 2 * TOTAL_SUPPLY);
bytes32 _mask = bytes32(1 << (_tokenId - TOTAL_SUPPLY - 1));
address[] memory _holders = holders();
for (uint256 i = 0; i < _holders.length; i++) {
if (maskOf(_holders[i]) & _mask == _mask) {
return _holders[i];
}
}
return address(0x0);
}
}
function getApproved(uint256 _tokenId) public view returns (address) {
require(_tokenId > TOTAL_SUPPLY && _tokenId <= 2 * TOTAL_SUPPLY);
return info.approved[_tokenId];
}
function isApprovedForAll(
address _owner,
address _operator
) public view returns (bool) {
return info.users[_owner].approved[_operator];
}
function getToken(
uint256 _tokenId
)
public
view
returns (address tokenOwner, address approved, string memory uri)
{
return (ownerOf(_tokenId), getApproved(_tokenId), tokenURI(_tokenId));
}
function getTokens(
uint256[] memory _tokenIds
)
external
view
returns (
address[] memory owners,
address[] memory approveds,
string[] memory uris
)
{
uint256 _length = _tokenIds.length;
owners = new address[](_length);
approveds = new address[](_length);
uris = new string[](_length);
for (uint256 i = 0; i < _length; i++) {
(owners[i], approveds[i], uris[i]) = getToken(_tokenIds[i]);
}
}
function _approveERC20(
address _owner,
address _spender,
uint256 _tokens
) internal {
info.users[_owner].allowance[_spender] = _tokens;
emit ERC20Approval(APPROVAL_TOPIC, _owner, _spender, _tokens);
}
function _approveNFT(address _spender, uint256 _tokenId) internal {
bytes32 _mask = bytes32(1 << (_tokenId - TOTAL_SUPPLY - 1));
require(maskOf(msg.sender) & _mask == _mask);
info.approved[_tokenId] = _spender;
emit Approval(msg.sender, _spender, _tokenId);
}
function _transferERC20(
address _from,
address _to,
uint256 _tokens
) internal {
unchecked {
bytes32 _mask;
uint256 _pos = 0;
uint256 _count = 0;
uint256 _n = uint256(maskOf(_from));
uint256[] memory _tokenIds = new uint256[](_tokens);
while (_n > 0 && _count < _tokens) {
if (_n & 1 == 1) {
_mask |= bytes32(1 << _pos);
_tokenIds[_count++] = TOTAL_SUPPLY + _pos + 1;
}
_pos++;
_n >>= 1;
}
require(_count == _tokens);
require(maskOf(_from) & _mask == _mask);
_transfer(_from, _to, _mask, _tokenIds);
}
}
function _transferNFT(address _from, address _to, uint256 _tokenId) internal {
unchecked {
require(_tokenId > TOTAL_SUPPLY && _tokenId <= 2 * TOTAL_SUPPLY);
bytes32 _mask = bytes32(1 << (_tokenId - TOTAL_SUPPLY - 1));
require(maskOf(_from) & _mask == _mask);
require(
msg.sender == _from ||
msg.sender == getApproved(_tokenId) ||
isApprovedForAll(_from, msg.sender)
);
uint256[] memory _tokenIds = new uint256[](1);
_tokenIds[0] = _tokenId;
_transfer(_from, _to, _mask, _tokenIds);
}
}
function _transferNFTs(address _to, uint256[] memory _tokenIds) internal {
unchecked {
bytes32 _mask;
for (uint256 i = 0; i < _tokenIds.length; i++) {
_mask |= bytes32(1 << (_tokenIds[i] - TOTAL_SUPPLY - 1));
}
require(_popcount(_mask) == _tokenIds.length);
require(maskOf(msg.sender) & _mask == _mask);
_transfer(msg.sender, _to, _mask, _tokenIds);
}
}
function _transfer(
address _from,
address _to,
bytes32 _mask,
uint256[] memory _tokenIds
) internal {
unchecked {
require(_tokenIds.length > 0);
for (uint256 i = 0; i < _tokenIds.length; i++) {
if (getApproved(_tokenIds[i]) != address(0x0)) {
info.approved[_tokenIds[i]] = address(0x0);
emit Approval(address(0x0), address(0x0), _tokenIds[i]);
}
emit Transfer(_from, _to, _tokenIds[i]);
}
info.users[_from].mask ^= _mask;
bool _from0 = maskOf(_from) == 0x0;
bool _to0 = maskOf(_to) == 0x0;
info.users[_to].mask |= _mask;
if (_from0) {
uint256 _index;
address[] memory _holders = holders();
for (uint256 i = 0; i < _holders.length; i++) {
if (_holders[i] == _from) {
_index = i;
break;
}
}
if (_to0) {
info.holders[_index] = _to;
} else {
info.holders[_index] = _holders[_holders.length - 1];
info.holders.pop();
}
} else if (_to0) {
info.holders.push(_to);
}
require(maskOf(_from) & maskOf(_to) == 0x0);
emit ERC20Transfer(TRANSFER_TOPIC, _from, _to, _tokenIds.length);
}
}
function _popcount(bytes32 _b) internal pure returns (uint256) {
uint256 _n = uint256(_b);
if (_n == UINT_MAX) {
return 256;
}
unchecked {
_n -= (_n >> 1) & M1;
_n = (_n & M2) + ((_n >> 2) & M2);
_n = (_n + (_n >> 4)) & M4;
_n = (_n * H01) >> 248;
}
return _n;
}
}
contract Deploy {
BobaTea public immutable bobaTea;
constructor() payable {
bobaTea = new BobaTea{ value: msg.value }();
bobaTea.initialize();
}
}
文件 2 的 5:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 3 的 5:Metadata.sol
pragma solidity ^0.8.19;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
interface BT {
function salt() external view returns (bytes32);
}
contract Metadata {
string public name = "Boba Tea";
string public symbol = "BOBA";
BT public immutable bobaTea;
struct Trait {
string attributeType;
string[] valueList;
uint256[] weightList;
uint256 weightTotal;
}
mapping(uint256 traitId => Trait) traits;
uint256 traitLength;
string baseTokenURI;
constructor() {
bobaTea = BT(msg.sender);
uint256 traitId;
Trait storage t = traits[traitId];
t.attributeType = "Tea";
t.valueList = [
"Milk Tea",
"Strawberry Fruit Tea",
"Matcha Tea",
"Mango Fruit Tea",
"Taro Tea"
];
t.weightList = [3_000, 2_000, 2_000, 2_000, 1_000];
t.weightTotal = 10_000;
traitId++;
t = traits[traitId];
t.attributeType = "Topping";
t.valueList = [
"Tapioca Pearls",
"Coffee Jelly",
"Grass Jelly",
"Pudding",
"Popping Boba"
];
t.weightList = [3_500, 2_500, 1_500, 1_500, 1_000];
t.weightTotal = 10_000;
traitId++;
t = traits[traitId];
t.attributeType = "Ice";
t.valueList = ["No Ice", "Blue", "Pink"];
t.weightList = [4_000, 4_000, 2_000];
t.weightTotal = 10_000;
traitId++;
t = traits[traitId];
t.attributeType = "Straw";
t.valueList = [
"None",
"Blue",
"Red",
"Green",
"Orange",
"Purple",
"Rainbow"
];
t.weightList = [1_500, 1_500, 1_500, 1_500, 1_500, 1_500, 1_000];
t.weightTotal = 10_000;
traitId++;
t = traits[traitId];
t.attributeType = "Background";
t.valueList = [
"White",
"Orange",
"Purple",
"Green",
"Red",
"Yellow",
"Blue"
];
t.weightList = [2_500, 2_000, 1_500, 1_500, 1_500, 500, 500];
t.weightTotal = 10_000;
traitId++;
traitLength = traitId;
baseTokenURI = "https://www.bobatea.xyz/api/nft/";
}
function tokenURI(uint256 id) external view returns (string memory) {
uint256 seed = uint256(keccak256(abi.encodePacked("seed", id)));
string memory json = string(
abi.encodePacked(
'data:application/json;utf8,{"name": "Boba Tea (ERC404) #',
Strings.toString(id),
'","description":"A collection of 10,000 Boba Tea enabled by ERC404, an experimental token standard.","external_url":"https://www.bobatea.xyz/","image":"',
baseTokenURI,
Strings.toString(id),
'","attributes":['
)
);
uint256 i;
for (; i < traitLength; ) {
Trait storage _trait = traits[i];
seed = uint256(keccak256(abi.encodePacked(_trait.attributeType, seed)));
json = string(
abi.encodePacked(
json,
'{"trait_type":"',
_trait.attributeType,
'","value":"',
getTraitValue(i, seed),
'"}',
","
)
);
unchecked {
++i;
}
}
seed = uint256(keccak256(abi.encodePacked("Sweetness", seed)));
json = string(
abi.encodePacked(
json,
'{"trait_type":"Sweetness","value":"',
Strings.toString(getSweetness(seed)),
'"}'
)
);
json = string.concat(json, "]}");
return json;
}
function getTraitValue(
uint256 traitId,
uint256 seed
) public view returns (string memory) {
Trait storage ts = traits[traitId];
uint256 value = (seed % ts.weightTotal) + 1;
uint256 i;
uint256 len = ts.valueList.length;
for (; i < len; ) {
if (value > ts.weightList[i]) {
value = value - ts.weightList[i];
} else {
return ts.valueList[i];
}
unchecked {
++i;
}
}
}
function getSweetness(uint256 seed) public view returns (uint256) {
return seed % 101;
}
function getTraits() public view returns (Trait[] memory) {
uint256 i;
Trait[] memory _traits = new Trait[](traitLength);
for (; i < traitLength; ) {
_traits[i] = traits[i];
unchecked {
++i;
}
}
return _traits;
}
function _uint2str(uint256 _value) internal pure returns (string memory) {
unchecked {
uint256 _digits = 1;
uint256 _n = _value;
while (_n > 9) {
_n /= 10;
_digits++;
}
bytes memory _out = new bytes(_digits);
for (uint256 i = 0; i < _out.length; i++) {
uint256 _dec = (_value / (10 ** (_out.length - i - 1))) % 10;
_out[i] = bytes1(uint8(_dec) + 48);
}
return string(_out);
}
}
function _col2str(bytes3 _col) internal pure returns (string memory str) {
unchecked {
str = "#";
for (uint256 i = 0; i < 6; i++) {
uint256 _hex = (uint24(_col) >> (4 * (i + 1 - 2 * (i % 2)))) % 16;
bytes memory _char = new bytes(1);
_char[0] = bytes1(uint8(_hex) + (_hex > 9 ? 87 : 48));
str = string(abi.encodePacked(str, string(_char)));
}
}
}
}
文件 4 的 5:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 5 的 5:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
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] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
{
"compilationTarget": {
"contracts/Boba.sol": "BobaTea"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","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":true,"inputs":[{"indexed":true,"internalType":"bytes32","name":"topic0","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"ERC20Approval","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes32","name":"topic0","type":"bytes32"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"ERC20Transfer","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":[],"name":"MINT_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"bulkTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"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":"uint256","name":"_tokenId","type":"uint256"}],"name":"getToken","outputs":[{"internalType":"address","name":"tokenOwner","type":"address"},{"internalType":"address","name":"approved","type":"address"},{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"getTokens","outputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"address[]","name":"approveds","type":"address[]"},{"internalType":"string[]","name":"uris","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"holders","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"_user","type":"address"}],"name":"maskOf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadata","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[],"name":"salt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Metadata","name":"_metadata","type":"address"}],"name":"setMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","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":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]