Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
NormiesRenderer
Compiler Version
v0.8.33+commit.64118f21
Optimization Enabled:
Yes with 10000 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.33;
import { INormiesRenderer } from "./interfaces/INormiesRenderer.sol";
import { INormiesStorage } from "./interfaces/INormiesStorage.sol";
import { NormiesTraits } from "./NormiesTraits.sol";
import { LibString } from "solady/utils/LibString.sol";
import { Base64 } from "solady/utils/Base64.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Lifebuoy } from "solady/utils/Lifebuoy.sol";
/**
* @title NormiesRenderer
* @author Normies by Serc (https://x.com/serc1n)
* @author Smart Contract by Yigit Duman (https://x.com/yigitduman)
*/
contract NormiesRenderer is INormiesRenderer, Ownable, Lifebuoy {
using LibString for uint256;
INormiesStorage public storageContract;
error TokenDataNotSet(uint256 tokenId);
constructor(INormiesStorage _storage) Ownable() Lifebuoy() {
storageContract = _storage;
}
function tokenURI(uint256 tokenId) external view override returns (string memory) {
require(storageContract.isTokenDataSet(tokenId), TokenDataNotSet(tokenId));
if (!storageContract.isRevealed()) {
return _buildPreRevealMetadata(tokenId);
}
bytes memory imageData = storageContract.getTokenRawImageData(tokenId);
bytes8 traits = storageContract.getTokenTraits(tokenId);
string memory svg = _renderSvg(imageData);
string memory svgBase64 = Base64.encode(bytes(svg));
bytes memory part1 = abi.encodePacked('{"name":"Normie #', tokenId.toString(), '","attributes":[');
bytes memory part2 = abi.encodePacked(
_buildAttributes(traits), '],"image":"data:image/svg+xml;base64,', svgBase64, '","animation_url":"'
);
bytes memory part3 = abi.encodePacked(_buildAnimationUrl(imageData), '"}');
string memory json = string(abi.encodePacked(part1, part2, part3));
return string(abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(json))));
}
/**
* @notice Builds pre-reveal metadata with animated noise image
* @param tokenId The token ID (used as seed for unique noise pattern)
* @return Complete data URI with pre-reveal JSON metadata
*/
function _buildPreRevealMetadata(uint256 tokenId) internal pure returns (string memory) {
string memory svg = _renderNoiseSvg(tokenId);
string memory svgBase64 = Base64.encode(bytes(svg));
bytes memory part1 = abi.encodePacked('{"name":"Normie #', tokenId.toString(), '","attributes":[');
bytes memory part2 = abi.encodePacked(
'{"trait_type":"Revealed","value":"No"}',
'],"image":"data:image/svg+xml;base64,',
svgBase64,
'","animation_url":"data:image/svg+xml;base64,'
);
bytes memory part3 = abi.encodePacked(svgBase64, '"}');
string memory json = string(abi.encodePacked(part1, part2, part3));
return string(abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(json))));
}
/**
* @notice Builds the JSON attributes array from packed trait indices
* @param traits Packed bytes8 trait indices
* @return The JSON attributes array contents (without surrounding brackets)
*/
function _buildAttributes(bytes8 traits) internal pure returns (string memory) {
bytes memory part1 = abi.encodePacked(
_traitJson("Type", NormiesTraits.typeName(uint8(traits[0]))),
",",
_traitJson("Gender", NormiesTraits.genderName(uint8(traits[1]))),
",",
_traitJson("Age", NormiesTraits.ageName(uint8(traits[2]))),
",",
_traitJson("Hair Style", NormiesTraits.hairStyleName(uint8(traits[3])))
);
bytes memory part2 = abi.encodePacked(
",",
_traitJson("Facial Feature", NormiesTraits.facialFeatureName(uint8(traits[4]))),
",",
_traitJson("Eyes", NormiesTraits.eyesName(uint8(traits[5]))),
",",
_traitJson("Expression", NormiesTraits.expressionName(uint8(traits[6]))),
",",
_traitJson("Accessory", NormiesTraits.accessoryName(uint8(traits[7]))),
",",
_numericTraitJson("Level", 1) // ◕⩊◕
);
return string(abi.encodePacked(part1, part2));
}
/**
* @notice Formats a single trait as a JSON object
* @param traitType The trait category name
* @param value The trait value
* @return JSON string: {"trait_type":"...","value":"..."}
*/
function _traitJson(string memory traitType, string memory value) internal pure returns (string memory) {
return string(abi.encodePacked('{"trait_type":"', traitType, '","value":"', value, '"}'));
}
/**
* @notice Formats a numeric trait as a JSON object with display_type "number"
* @param traitType The trait category name
* @param value The numeric trait value
* @return JSON string: {"display_type":"number","trait_type":"...","value":N}
*/
function _numericTraitJson(string memory traitType, uint256 value) internal pure returns (string memory) {
return string(
abi.encodePacked('{"display_type":"number","trait_type":"', traitType, '","value":', value.toString(), "}")
);
}
/**
* @notice Builds an HTML page that renders the bitmap on a canvas via fillRect for pixel-perfect output
* @param imageData The raw 200-byte monochrome bitmap (40x40, 1 bit/pixel, MSB first)
* @return A data:text/html;base64 URI
* @dev Draws each "on" pixel as a 50x50 filled rect on a 2000x2000 canvas, avoiding SVG rasterisation artifacts
*/
function _buildAnimationUrl(bytes memory imageData) internal pure returns (string memory) {
bytes memory html = abi.encodePacked(
"<html><body style='margin:0;overflow:hidden;width:100vw;height:100vh;"
"display:flex;align-items:center;justify-content:center;background:#e3e5e4'>"
"<canvas id='c' width='2000' height='2000' style='image-rendering:pixelated;width:min(100vw,100vh);height:min(100vw,100vh)'></canvas>"
"<script>var h='",
LibString.toHexStringNoPrefix(imageData)
);
html = abi.encodePacked(
html,
"';var c=document.getElementById('c').getContext('2d');"
"c.fillStyle='#e3e5e4';c.fillRect(0,0,2000,2000);c.fillStyle='#48494b';"
"for(var y=0;y<40;y++)for(var x=0;x<40;x++){var i=y*40+x,b=parseInt(h.substr((i>>3)*2,2),16);"
"if((b>>(7-(i&7)))&1)c.fillRect(x*50,y*50,50,50)}" "</script></body></html>"
);
return string(abi.encodePacked("data:text/html;base64,", Base64.encode(html)));
}
/**
* @notice Renders an animated 40×40 grayscale block-noise SVG using CSS keyframes
* @param seed Unique seed per token for deterministic noise pattern
* @return The complete SVG string (~95KB)
* @dev 32 shared @keyframes animations × 8 delay offsets = 256 visual patterns.
* Uses pre-allocated buffer with cursor to avoid O(n²) abi.encodePacked copies.
*/
function _renderNoiseSvg(uint256 seed) internal pure returns (string memory) {
bytes memory buf = new bytes(100_000);
uint256 cursor;
uint256 rand = seed;
// SVG header + default rect animation styles
cursor = _noiseWriteStr(
buf,
cursor,
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" '
'width="800" height="800" shape-rendering="crispEdges"><style>'
"rect{animation-duration:.8s;animation-timing-function:step-end;" "animation-iteration-count:infinite}"
);
// Animation name classes: .n0{animation-name:n0} … .n31{…}
for (uint256 i; i < 32; i++) {
cursor = _noiseWriteStr(buf, cursor, ".n");
cursor = _noiseWriteUint(buf, cursor, i);
cursor = _noiseWriteStr(buf, cursor, "{animation-name:n");
cursor = _noiseWriteUint(buf, cursor, i);
cursor = _noiseWriteByte(buf, cursor, 0x7D);
}
// Delay classes: .d1{animation-delay:-.1s} … .d7{…}
for (uint256 d = 1; d < 8; d++) {
cursor = _noiseWriteStr(buf, cursor, ".d");
cursor = _noiseWriteByte(buf, cursor, uint8(0x30 + d));
cursor = _noiseWriteStr(buf, cursor, "{animation-delay:-.");
cursor = _noiseWriteByte(buf, cursor, uint8(0x30 + d));
cursor = _noiseWriteStr(buf, cursor, "s}");
}
// 32 @keyframes definitions (8 hashes, 4 animations per hash)
for (uint256 i; i < 32; i++) {
if (i & 3 == 0) rand = uint256(keccak256(abi.encodePacked(rand)));
cursor = _noiseWriteStr(buf, cursor, "@keyframes n");
cursor = _noiseWriteUint(buf, cursor, i);
cursor = _noiseWriteByte(buf, cursor, 0x7B);
for (uint256 f; f < 8; f++) {
cursor = _noiseWriteUint(buf, cursor, f * 125 / 10);
cursor = _noiseWriteStr(buf, cursor, "%{fill:#");
uint256 g = 62 + (uint8(bytes32(rand)[(i & 3) * 8 + f]) % 162);
cursor = _noiseWriteHexByte(buf, cursor, g);
cursor = _noiseWriteHexByte(buf, cursor, g);
cursor = _noiseWriteHexByte(buf, cursor, g);
cursor = _noiseWriteByte(buf, cursor, 0x7D);
}
cursor = _noiseWriteByte(buf, cursor, 0x7D);
}
cursor = _noiseWriteStr(buf, cursor, "</style>");
// 1600 rects (50 hashes, 32 rects per hash)
for (uint256 y; y < 40; y++) {
for (uint256 x; x < 40; x++) {
uint256 idx = y * 40 + x;
if (idx & 31 == 0) rand = uint256(keccak256(abi.encodePacked(rand)));
uint256 pack = uint8(bytes32(rand)[idx & 31]);
uint256 animIdx = pack >> 3;
uint256 delayIdx = pack & 7;
cursor = _noiseWriteStr(buf, cursor, '<rect x="');
cursor = _noiseWriteUint(buf, cursor, x);
cursor = _noiseWriteStr(buf, cursor, '" y="');
cursor = _noiseWriteUint(buf, cursor, y);
cursor = _noiseWriteStr(buf, cursor, '" width="1" height="1" class="n');
cursor = _noiseWriteUint(buf, cursor, animIdx);
if (delayIdx > 0) {
cursor = _noiseWriteStr(buf, cursor, " d");
cursor = _noiseWriteByte(buf, cursor, uint8(0x30 + delayIdx));
}
cursor = _noiseWriteStr(buf, cursor, '"/>');
}
}
cursor = _noiseWriteStr(buf, cursor, "</svg>");
assembly {
mstore(buf, cursor)
}
return string(buf);
}
// ─── Buffer writers for noise SVG (no memory reallocation) ───────────
function _noiseWriteStr(bytes memory buf, uint256 cursor, string memory s) internal pure returns (uint256) {
bytes memory sb = bytes(s);
uint256 len = sb.length;
assembly {
let dst := add(add(buf, 32), cursor)
let src := add(sb, 32)
for { let i := 0 } lt(i, len) { i := add(i, 32) } {
mstore(add(dst, i), mload(add(src, i)))
}
}
return cursor + len;
}
function _noiseWriteByte(bytes memory buf, uint256 cursor, uint8 b) internal pure returns (uint256) {
assembly {
mstore8(add(add(buf, 32), cursor), b)
}
return cursor + 1;
}
function _noiseWriteHexByte(bytes memory buf, uint256 cursor, uint256 b) internal pure returns (uint256) {
assembly {
let ptr := add(add(buf, 32), cursor)
let hi := and(shr(4, b), 0x0F)
let lo := and(b, 0x0F)
mstore8(ptr, add(add(hi, 0x30), mul(gt(hi, 9), 0x27)))
mstore8(add(ptr, 1), add(add(lo, 0x30), mul(gt(lo, 9), 0x27)))
}
return cursor + 2;
}
function _noiseWriteUint(bytes memory buf, uint256 cursor, uint256 val) internal pure returns (uint256) {
if (val == 0) {
return _noiseWriteByte(buf, cursor, 0x30);
}
uint256 temp = val;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
uint256 end = cursor + digits;
uint256 pos = end;
assembly {
let ptr := add(buf, 32)
for { } gt(val, 0) { } {
pos := sub(pos, 1)
mstore8(add(ptr, pos), add(48, mod(val, 10)))
val := div(val, 10)
}
}
return end;
}
/**
* @notice Renders a 40x40 monochrome bitmap as a 1000x1000 SVG
* @param imageData The raw 200-byte bitmap (1 bit per pixel, MSB first)
* @return The SVG string
* @dev Uses row-scanning with run-length encoding to merge consecutive
* "on" pixels into single <rect> elements for efficiency
*/
function _renderSvg(bytes memory imageData) internal pure returns (string memory) {
bytes memory svg = abi.encodePacked(
'<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 40 40" '
'shape-rendering="crispEdges"><rect width="40" height="40" fill="#e3e5e4"/>'
);
for (uint256 y = 0; y < 40; y++) {
for (uint256 x = 0; x < 40; x++) {
if (_isPixelOn(imageData, x, y)) {
svg = abi.encodePacked(
svg, '<rect x="', x.toString(), '" y="', y.toString(), '" width="1" height="1" fill="#48494b"/>'
);
}
}
}
svg = abi.encodePacked(svg, "</svg>");
return string(svg);
}
/**
* @notice Checks if a pixel is "on" in the bitmap
* @param imageData The raw bitmap bytes
* @param x The x coordinate (0-39)
* @param y The y coordinate (0-39)
* @return True if the pixel is set (#48494b)
*/
function _isPixelOn(bytes memory imageData, uint256 x, uint256 y) internal pure returns (bool) {
uint256 flatIndex = y * 40 + x;
uint256 byteIndex = flatIndex >> 3;
uint256 bitPos = 7 - (flatIndex & 7);
return (uint8(imageData[byteIndex]) >> bitPos) & 1 == 1;
}
/**
* @notice Sets the storage contract
* @param _storage The new storage contract
*/
function setStorageContract(INormiesStorage _storage) external onlyOwner {
storageContract = _storage;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.33;
interface INormiesRenderer {
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.33;
interface INormiesStorage {
function getTokenRawImageData(uint256 tokenId) external view returns (bytes memory);
function getTokenTraits(uint256 tokenId) external view returns (bytes8);
function setTokenRawImageData(uint256 tokenId, bytes calldata imageData) external;
function setTokenTraits(uint256 tokenId, bytes8 traits) external;
function isTokenDataSet(uint256 tokenId) external view returns (bool);
function isRevealed() external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.33;
/**
* @title NormiesTraits
* @author Normies by Serc (https://x.com/serc1n)
* @author Smart Contract by Yigit Duman (https://x.com/yigitduman)
*/
library NormiesTraits {
error InvalidTraitIndex();
function typeName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Human";
if (index == 1) return "Cat";
if (index == 2) return "Alien";
if (index == 3) return "Agent";
revert InvalidTraitIndex();
}
function genderName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Male";
if (index == 1) return "Female";
if (index == 2) return "Non-Binary";
revert InvalidTraitIndex();
}
function ageName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Young";
if (index == 1) return "Middle-Aged";
if (index == 2) return "Old";
revert InvalidTraitIndex();
}
function hairStyleName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Short Hair";
if (index == 1) return "Long Hair";
if (index == 2) return "Curly Hair";
if (index == 3) return "Straight Hair";
if (index == 4) return "Wavy Hair";
if (index == 5) return "Spiky Hair";
if (index == 6) return "Wild Hair";
if (index == 7) return "Messy Hair";
if (index == 8) return "Mohawk";
if (index == 9) return "Crazy Hair";
if (index == 10) return "Braided Hair";
if (index == 11) return "Bald";
if (index == 12) return "Ponytail";
if (index == 13) return "Pigtails";
if (index == 14) return "Afro";
if (index == 15) return "Buzz Cut";
if (index == 16) return "Frumpy Hair";
if (index == 17) return "Stringy Hair";
if (index == 18) return "Peak Spike";
if (index == 19) return "Half Shaved";
if (index == 20) return "Knitted Cap";
revert InvalidTraitIndex();
}
function facialFeatureName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Full Beard";
if (index == 1) return "Mustache";
if (index == 2) return "Goatee";
if (index == 3) return "Chin Strap";
if (index == 4) return "Muttonchops";
if (index == 5) return "Shadow Beard";
if (index == 6) return "Luxurious Beard";
if (index == 7) return "Handlebars";
if (index == 8) return "Big Beard";
if (index == 9) return "Normal Beard";
if (index == 10) return "Clean Shaven";
if (index == 11) return "Freckles";
if (index == 12) return "Mole";
if (index == 13) return "Rosy Cheeks";
if (index == 14) return "Dimples";
if (index == 15) return "High Cheekbones";
if (index == 16) return "Spots";
revert InvalidTraitIndex();
}
function eyesName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Classic Shades";
if (index == 1) return "Big Shades";
if (index == 2) return "Regular Shades";
if (index == 3) return "Small Shades";
if (index == 4) return "Horned Rim Glasses";
if (index == 5) return "Nerd Glasses";
if (index == 6) return "VR Headset";
if (index == 7) return "3D Glasses";
if (index == 8) return "Eye Mask";
if (index == 9) return "Eye Patch";
if (index == 10) return "Round Glasses";
if (index == 11) return "Square Glasses";
if (index == 12) return "Aviators";
if (index == 13) return "No Glasses";
revert InvalidTraitIndex();
}
function expressionName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Neutral";
if (index == 1) return "Slight Smile";
if (index == 2) return "Serious";
if (index == 3) return "Content";
if (index == 4) return "Peaceful";
if (index == 5) return "Confident";
if (index == 6) return "Friendly";
revert InvalidTraitIndex();
}
function accessoryName(uint8 index) internal pure returns (string memory) {
if (index == 0) return "Top Hat";
if (index == 1) return "Fedora";
if (index == 2) return "Cowboy Hat";
if (index == 3) return "Beanie";
if (index == 4) return "Cap";
if (index == 5) return "Cap Forward";
if (index == 6) return "Bandana";
if (index == 7) return "Headband";
if (index == 8) return "Do-Rag";
if (index == 9) return "Hoodie";
if (index == 10) return "Earring";
if (index == 11) return "Gold Chain";
if (index == 12) return "Silver Chain";
if (index == 13) return "Bow Tie";
if (index == 14) return "No Accessories";
revert InvalidTraitIndex();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {LibBytes} from "./LibBytes.sol";
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated string storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native string storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct StringStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRING STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the string storage `$` to `s`.
function set(StringStorage storage $, string memory s) internal {
LibBytes.set(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to `s`.
function setCalldata(StringStorage storage $, string calldata s) internal {
LibBytes.setCalldata(bytesStorage($), bytes(s));
}
/// @dev Sets the value of the string storage `$` to the empty string.
function clear(StringStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty string "".
function isEmpty(StringStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(StringStorage storage $) internal view returns (uint256) {
return LibBytes.length(bytesStorage($));
}
/// @dev Returns the value stored in `$`.
function get(StringStorage storage $) internal view returns (string memory) {
return string(LibBytes.get(bytesStorage($)));
}
/// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
function uint8At(StringStorage storage $, uint256 i) internal view returns (uint8) {
return LibBytes.uint8At(bytesStorage($), i);
}
/// @dev Helper to cast `$` to a `BytesStorage`.
function bytesStorage(StringStorage storage $)
internal
pure
returns (LibBytes.BytesStorage storage casted)
{
/// @solidity memory-safe-assembly
assembly {
casted.slot := $.slot
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end of the memory to calculate the length later.
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 1)`.
// Store the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(result, add(48, mod(temp, 10)))
temp := div(temp, 10) // Keep dividing `temp` until zero.
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory result) {
if (value >= 0) return toString(uint256(value));
unchecked {
result = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let n := mload(result) // Load the string length.
mstore(result, 0x2d) // Store the '-' character.
result := sub(result, 1) // Move back the string pointer by a byte.
mstore(result, add(n, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2 + 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value, byteCount);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `byteCount` bytes.
/// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
/// giving a total length of `byteCount * 2` bytes.
/// Reverts if `byteCount` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 byteCount)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `byteCount * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
result := add(mload(0x40), and(add(shl(1, byteCount), 0x42), not(0x1f)))
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(result, add(byteCount, byteCount))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(result, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := add(mload(result), 2) // Compute the length.
mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value)
internal
pure
returns (string memory result)
{
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
let n := mload(result) // Get the length.
result := add(result, o) // Move the pointer, accounting for leading zero.
mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
result := add(mload(0x40), 0x80)
mstore(0x40, add(result, 0x20)) // Allocate memory.
mstore(result, 0) // Zeroize the slot after the string.
let end := result // Cache the end to calculate the length later.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
result := add(result, w) // `sub(result, 2)`.
mstore8(add(result, 1), mload(and(temp, 15)))
mstore8(result, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let n := sub(end, result)
result := sub(result, 0x20)
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory result) {
result = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(result, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory result) {
result = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Allocate memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(result, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
result := add(result, 2)
mstore(result, 40) // Store the length.
let o := add(result, 0x20)
mstore(add(o, 40), 0) // Zeroize the slot after the string.
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory result) {
result = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let n := add(mload(result), 2) // Compute the length.
mstore(result, 0x3078) // Store the "0x" prefix.
result := sub(result, 2) // Move the pointer.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let n := mload(raw)
result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(result, add(n, n)) // Store the length of the output.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let o := add(result, 0x20)
let end := add(raw, n)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
let mask := shl(7, div(not(0), 255))
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
for { let end := add(o, mload(s)) } 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(string memory subject, string memory needle, string memory replacement)
internal
pure
returns (string memory)
{
return string(LibBytes.replace(bytes(subject), bytes(needle), bytes(replacement)));
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.indexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(string memory subject, string memory needle) internal pure returns (uint256) {
return LibBytes.indexOf(bytes(subject), bytes(needle), 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle, uint256 from)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), from);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(string memory subject, string memory needle)
internal
pure
returns (uint256)
{
return LibBytes.lastIndexOf(bytes(subject), bytes(needle), type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.contains(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.startsWith(bytes(subject), bytes(needle));
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(string memory subject, string memory needle) internal pure returns (bool) {
return LibBytes.endsWith(bytes(subject), bytes(needle));
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times) internal pure returns (string memory) {
return string(LibBytes.repeat(bytes(subject), times));
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory)
{
return string(LibBytes.slice(bytes(subject), start, end));
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start) internal pure returns (string memory) {
return string(LibBytes.slice(bytes(subject), start, type(uint256).max));
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory needle)
internal
pure
returns (uint256[] memory)
{
return LibBytes.indicesOf(bytes(subject), bytes(needle));
}
/// @dev Returns an arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
bytes[] memory a = LibBytes.split(bytes(subject), bytes(delimiter));
/// @solidity memory-safe-assembly
assembly {
result := a
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b) internal pure returns (string memory) {
return string(LibBytes.concat(bytes(a), bytes(b)));
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(subject)
if n {
result := mload(0x40)
let o := add(result, 0x20)
let d := sub(subject, result)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
for { let end := add(o, n) } 1 {} {
let b := byte(0, mload(add(d, o)))
mstore8(o, xor(and(shr(b, flags), 0x20), b))
o := add(o, 1)
if eq(o, end) { break }
}
mstore(result, n) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n) // Store the length.
let o := add(result, 0x20)
mstore(o, s) // Store the bytes of the string.
mstore(add(o, n), 0) // Zeroize the slot after the string.
mstore(0x40, add(result, 0x40)) // Allocate memory.
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let end := add(s, mload(s))
let o := add(result, 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(o, c)
o := add(o, 1)
continue
}
let t := shr(248, mload(c))
mstore(o, mload(and(t, 0x1f)))
o := add(o, shr(5, t))
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(o, c)
o := add(o, 1)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), c)
o := add(o, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(o, mload(0x19)) // "\\u00XX".
o := add(o, 6)
continue
}
mstore8(o, 0x5c) // "\\".
mstore8(add(o, 1), mload(add(c, 8)))
o := add(o, 2)
}
if addDoubleQuotes {
mstore8(o, 34)
o := add(1, o)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Encodes `s` so that it can be safely used in a URI,
/// just like `encodeURIComponent` in JavaScript.
/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
/// See: https://datatracker.ietf.org/doc/html/rfc2396
/// See: https://datatracker.ietf.org/doc/html/rfc3986
function encodeURIComponent(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
// Store "0123456789ABCDEF" in scratch space.
// Uppercased to be consistent with JavaScript's implementation.
mstore(0x0f, 0x30313233343536373839414243444546)
let o := add(result, 0x20)
for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// If not in `[0-9A-Z-a-z-_.!~*'()]`.
if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
mstore8(o, 0x25) // '%'.
mstore8(add(o, 1), mload(and(shr(4, c), 15)))
mstore8(add(o, 2), mload(and(c, 15)))
o := add(o, 3)
continue
}
mstore8(o, c)
o := add(o, 1)
}
mstore(result, sub(o, add(result, 0x20))) // Store the length.
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
/// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
function cmp(string memory a, string memory b) internal pure returns (int256) {
return LibBytes.cmp(bytes(a), bytes(b));
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40) // Grab the free memory pointer.
mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
mstore(result, 0) // Zeroize the length slot.
mstore(add(result, 0x1f), packed) // Store the length and bytes.
mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLen := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
or( // Load the length and the bytes of `a` and `b`.
shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
// `totalLen != 0 && totalLen < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLen, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
resultA := mload(0x40) // Grab the free memory pointer.
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode strings in Base64. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol) /// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <[email protected]>. library Base64 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ENCODING / DECODING */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// See: https://datatracker.ietf.org/doc/html/rfc4648 /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. /// @param noPadding Whether to strip away the padding. function encode(bytes memory data, bool fileSafe, bool noPadding) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let dataLength := mload(data) if dataLength { // Multiply by 4/3 rounded up. // The `shl(2, ...)` is equivalent to multiplying by 4. let encodedLength := shl(2, div(add(dataLength, 2), 3)) // Set `result` to point to the start of the free memory. result := mload(0x40) // Store the table into the scratch space. // Offsetted by -1 byte so that the `mload` will load the character. // We will rewrite the free memory pointer at `0x40` later with // the allocated size. // The magic constant 0x0670 will turn "-_" into "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670))) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) let dataEnd := add(add(0x20, data), dataLength) let dataEndValue := mload(dataEnd) // Cache the value at the `dataEnd` slot. mstore(dataEnd, 0x00) // Zeroize the `dataEnd` slot to clear dirty bits. // Run over the input, 3 bytes at a time. for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8(0, mload(and(shr(18, input), 0x3F))) mstore8(1, mload(and(shr(12, input), 0x3F))) mstore8(2, mload(and(shr(6, input), 0x3F))) mstore8(3, mload(and(input, 0x3F))) mstore(ptr, mload(0x00)) ptr := add(ptr, 4) // Advance 4 bytes. if iszero(lt(ptr, end)) { break } } mstore(dataEnd, dataEndValue) // Restore the cached value at `dataEnd`. mstore(0x40, add(end, 0x20)) // Allocate the memory. // Equivalent to `o = [0, 2, 1][dataLength % 3]`. let o := div(2, mod(dataLength, 3)) // Offset `ptr` and pad with '='. We can simply write over the end. mstore(sub(ptr, o), shl(240, 0x3d3d)) // Set `o` to zero if there is padding. o := mul(iszero(iszero(noPadding)), o) mstore(sub(ptr, o), 0) // Zeroize the slot after the string. mstore(result, sub(encodedLength, o)) // Store the length. } } } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, false, false)`. function encode(bytes memory data) internal pure returns (string memory result) { result = encode(data, false, false); } /// @dev Encodes `data` using the base64 encoding described in RFC 4648. /// Equivalent to `encode(data, fileSafe, false)`. function encode(bytes memory data, bool fileSafe) internal pure returns (string memory result) { result = encode(data, fileSafe, false); } /// @dev Decodes base64 encoded `data`. /// /// Supports: /// - RFC 4648 (both standard and file-safe mode). /// - RFC 3501 (63: ','). /// /// Does not support: /// - Line breaks. /// /// Note: For performance reasons, /// this function will NOT revert on invalid `data` inputs. /// Outputs for invalid inputs will simply be undefined behaviour. /// It is the user's responsibility to ensure that the `data` /// is a valid base64 encoded string. function decode(string memory data) internal pure returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { let dataLength := mload(data) if dataLength { let decodedLength := mul(shr(2, dataLength), 3) for {} 1 {} { // If padded. if iszero(and(dataLength, 3)) { let t := xor(mload(add(data, dataLength)), 0x3d3d) // forgefmt: disable-next-item decodedLength := sub( decodedLength, add(iszero(byte(30, t)), iszero(byte(31, t))) ) break } // If non-padded. decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) break } result := mload(0x40) // Write the length of the bytes. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, decodedLength) // Load the table into the scratch space. // Constants are optimized for smaller bytecode with zero gas overhead. // `m` also doubles as the mask of the upper 6 bits. let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc mstore(0x5b, m) mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064) mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. // forgefmt: disable-next-item mstore(ptr, or( and(m, mload(byte(28, input))), shr(6, or( and(m, mload(byte(29, input))), shr(6, or( and(m, mload(byte(30, input))), shr(6, mload(byte(31, input))) )) )) )) ptr := add(ptr, 3) if iszero(lt(ptr, end)) { break } } mstore(0x40, add(end, 0x20)) // Allocate the memory. mstore(end, 0) // Zeroize the slot after the bytes. mstore(0x60, 0) // Restore the zero slot. } } } }
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Class that allows for rescue of ETH, ERC20, ERC721 tokens.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Lifebuoy.sol)
///
/// @dev This contract is created to mitigate the following disasters:
/// - Careless user sends tokens to the wrong chain or wrong contract.
/// - Careless dev deploys a contract without a withdraw function in attempt to rescue
/// careless user's tokens, due to deployment nonce mismatch caused by
/// script misfire / misconfiguration.
/// - Careless dev forgets to add a withdraw function to a NFT sale contract.
///
/// Note: if you are deploying via a untrusted `tx.origin`,
/// you MUST override `_lifebuoyDefaultDeployer` to return a trusted address.
///
/// For best safety:
/// - For non-escrow contracts, inherit Lifebuoy as much as possible,
/// and leave it unlocked.
/// - For escrow contracts, lock access as tight as possible,
/// as soon as possible. Or simply don't inherit Lifebuoy.
/// Escrow: Your contract is designed to hold ETH, ERC20s, ERC721s
/// (e.g. liquidity pools).
///
/// All rescue and rescue authorization functions require either:
/// - Caller is the deployer
/// AND the contract is not a proxy
/// AND `rescueLocked() & _LIFEBUOY_DEPLOYER_ACCESS_LOCK == 0`.
/// - Caller is `owner()`
/// AND `rescueLocked() & _LIFEBUOY_OWNER_ACCESS_LOCK == 0`.
///
/// The choice of using bit flags to represent locked statuses is for
/// efficiency, flexibility, convenience.
///
/// This contract is optimized with a priority on minimal bytecode size,
/// as the methods are not intended to be called often.
contract Lifebuoy {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to rescue or lock the rescue function.
error RescueUnauthorizedOrLocked();
/// @dev The rescue operation has failed due to a failed transfer.
error RescueTransferFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* LOCK FLAGS CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// These flags are kept internal to avoid bloating up the function dispatch.
// You can just copy paste this into your own code.
/// @dev Flag to denote that the deployer's access is locked. (1)
uint256 internal constant _LIFEBUOY_DEPLOYER_ACCESS_LOCK = 1 << 0;
/// @dev Flag to denote that the `owner()`'s access is locked. (2)
uint256 internal constant _LIFEBUOY_OWNER_ACCESS_LOCK = 1 << 1;
/// @dev Flag to denote that the `lockRescue` function is locked. (4)
uint256 internal constant _LIFEBUOY_LOCK_RESCUE_LOCK = 1 << 2;
/// @dev Flag to denote that the `rescueETH` function is locked. (8)
uint256 internal constant _LIFEBUOY_RESCUE_ETH_LOCK = 1 << 3;
/// @dev Flag to denote that the `rescueERC20` function is locked. (16)
uint256 internal constant _LIFEBUOY_RESCUE_ERC20_LOCK = 1 << 4;
/// @dev Flag to denote that the `rescueERC721` function is locked. (32)
uint256 internal constant _LIFEBUOY_RESCUE_ERC721_LOCK = 1 << 5;
/// @dev Flag to denote that the `rescueERC1155` function is locked. (64)
uint256 internal constant _LIFEBUOY_RESCUE_ERC1155_LOCK = 1 << 6;
/// @dev Flag to denote that the `rescueERC6909` function is locked. (128)
uint256 internal constant _LIFEBUOY_RESCUE_ERC6909_LOCK = 1 << 7;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For checking that the caller is the deployer and
/// that the context is not a delegatecall
/// (so that the implementation deployer cannot drain proxies).
bytes32 internal immutable _lifebuoyDeployerHash;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The rescue locked flags slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_RESCUE_LOCKED_FLAGS_SLOT_NOT")))))`.
/// It is intentionally chosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
bytes32 internal constant _RESCUE_LOCKED_FLAGS_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
constructor() payable {
bytes32 hash;
uint256 deployer = uint160(_lifebuoyDefaultDeployer());
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, address())
mstore(0x20, deployer)
hash := keccak256(0x00, 0x40)
}
_lifebuoyDeployerHash = hash;
}
/// @dev Returns `tx.origin` by default. Override to return another address if needed.
///
/// Note: If you are deploying via a untrusted `tx.origin` (e.g. ERC4337 bundler)
/// you MUST override this function to return a trusted address.
function _lifebuoyDefaultDeployer() internal view virtual returns (address) {
// I know about EIP7645, and I will stop it if it gets traction.
// Worse case, I will add an `ecrecover` method. But not today.
return tx.origin;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RESCUE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` (in wei) ETH from the current contract to `to`.
/// Reverts upon failure.
function rescueETH(address to, uint256 amount)
public
payable
virtual
onlyRescuer(_LIFEBUOY_RESCUE_ETH_LOCK)
{
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0x7ec62e76) // `RescueTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Does not check for existence of token or return data. Reverts upon failure.
function rescueERC20(address token, address to, uint256 amount)
public
payable
virtual
onlyRescuer(_LIFEBUOY_RESCUE_ERC20_LOCK)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
// `RescueTransferFailed()` and `transfer(address,uint256)`.
mstore(0x00, shl(96, 0x7ec62e76a9059cbb))
if iszero(call(gas(), token, callvalue(), 0x10, 0x44, codesize(), 0x00)) {
revert(0x0c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends `id` of ERC721 `token` from the current contract to `to`.
/// Does not check for existence of token or return data. Reverts upon failure.
function rescueERC721(address token, address to, uint256 id)
public
payable
virtual
onlyRescuer(_LIFEBUOY_RESCUE_ERC721_LOCK)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, id) // Store the `id` argument.
mstore(0x40, shr(96, shl(96, to))) // Store the `to` argument.
mstore(0x20, address()) // Store the `from` argument.
// `RescueTransferFailed()` and `transferFrom(address,address,uint256)`.
mstore(0x00, 0x7ec62e7623b872dd)
if iszero(call(gas(), token, callvalue(), 0x1c, 0x64, codesize(), 0x00)) {
revert(0x18, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of `id` of ERC1155 `token` from the current contract to `to`.
/// Does not check for existence of token or return data. Reverts upon failure.
function rescueERC1155(
address token,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) public payable virtual onlyRescuer(_LIFEBUOY_RESCUE_ERC1155_LOCK) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
// `RescueTransferFailed()` and `safeTransferFrom(address,address,uint256,uint256,bytes)`.
mstore(m, 0x7ec62e76f242432a)
mstore(add(0x20, m), address()) // Store the `from` argument.
mstore(add(0x40, m), shr(96, shl(96, to))) // Store the `to` argument.
mstore(add(0x60, m), id) // Store the `id` argument.
mstore(add(0x80, m), amount) // Store the `amount` argument.
mstore(add(0xa0, m), 0xa0) // Store the offset to `data`.
mstore(add(0xc0, m), data.length)
calldatacopy(add(m, 0xe0), data.offset, data.length)
// forgefmt: disable-next-item
if iszero(
call(gas(), token, callvalue(), add(m, 0x1c), add(0xc4, data.length), codesize(), 0x00)
) { revert(add(m, 0x18), 0x04) }
}
}
/// @dev Sends `amount` of `id` of ERC6909 `token` from the current contract to `to`.
/// Does not check for existence of token or return data. Reverts upon failure.
function rescueERC6909(address token, address to, uint256 id, uint256 amount)
public
payable
virtual
onlyRescuer(_LIFEBUOY_RESCUE_ERC6909_LOCK)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, id) // Store the `id` argument.
mstore(0x54, amount) // Store the `amount` argument.
// `RescueTransferFailed()` and `transfer(address,uint256,uint256)`.
mstore(0x00, shl(96, 0x7ec62e76095bcdb6))
if iszero(call(gas(), token, callvalue(), 0x10, 0x64, codesize(), 0x00)) {
revert(0x0c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RESCUE AUTHORIZATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the flags denoting whether access to rescue functions
/// (including `lockRescue`) is locked.
function rescueLocked() public view virtual returns (uint256 locks) {
/// @solidity memory-safe-assembly
assembly {
locks := sload(_RESCUE_LOCKED_FLAGS_SLOT)
}
}
/// @dev Locks (i.e. permanently removes) access to rescue functions (including `lockRescue`).
function lockRescue(uint256 locksToSet)
public
payable
virtual
onlyRescuer(_LIFEBUOY_LOCK_RESCUE_LOCK)
{
_lockRescue(locksToSet);
}
/// @dev Internal function to set the lock flags without going through access control.
function _lockRescue(uint256 locksToSet) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let s := _RESCUE_LOCKED_FLAGS_SLOT
sstore(s, or(sload(s), locksToSet))
}
}
/// @dev Requires that the rescue function being guarded is:
/// 1. Not locked, AND
/// 2. Called by either:
/// (a) The `owner()`, OR
/// (b) The deployer (if not via a delegate call and deployer is an EOA).
function _checkRescuer(uint256 modeLock) internal view virtual {
uint256 locks = rescueLocked();
bytes32 h = _lifebuoyDeployerHash;
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
// If the `modeLock` flag is true, set all bits in `locks` to true.
locks := or(sub(0, iszero(iszero(and(modeLock, locks)))), locks)
// Caller is the deployer
// AND the contract is not a proxy
// AND `locks & _LIFEBUOY_DEPLOYER_ACCESS_LOCK` is false.
mstore(0x20, caller())
mstore(and(locks, _LIFEBUOY_DEPLOYER_ACCESS_LOCK), address())
if eq(keccak256(0x00, 0x40), h) { break }
// If the caller is `owner()`
// AND `locks & _LIFEBUOY_OWNER_ACCESS_LOCK` is false.
mstore(0x08, 0x8da5cb5b0a0362e0) // `owner()` and `RescueUnauthorizedOrLocked()`.
if and( // The arguments of `and` are evaluated from right to left.
lt(
and(locks, _LIFEBUOY_OWNER_ACCESS_LOCK),
and(gt(returndatasize(), 0x1f), eq(mload(0x00), caller()))
),
staticcall(gas(), address(), 0x20, 0x04, 0x00, 0x20)
) { break }
revert(0x24, 0x04)
}
}
}
/// @dev Modifier that calls `_checkRescuer()` at the start of the function.
modifier onlyRescuer(uint256 modeLock) virtual {
_checkRescuer(modeLock);
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for byte related operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)
library LibBytes {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated bytes storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct BytesStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the bytes.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the bytes storage `$` to `s`.
function set(BytesStorage storage $, bytes memory s) internal {
/// @solidity memory-safe-assembly
assembly {
let n := mload(s)
let packed := or(0xff, shl(8, n))
for { let i := 0 } 1 {} {
if iszero(gt(n, 0xfe)) {
i := 0x1f
packed := or(n, shl(8, mload(add(s, i))))
if iszero(gt(n, i)) { break }
}
let o := add(s, 0x20)
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), mload(add(o, i)))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to `s`.
function setCalldata(BytesStorage storage $, bytes calldata s) internal {
/// @solidity memory-safe-assembly
assembly {
let packed := or(0xff, shl(8, s.length))
for { let i := 0 } 1 {} {
if iszero(gt(s.length, 0xfe)) {
i := 0x1f
packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))
if iszero(gt(s.length, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))
i := add(i, 0x20)
if iszero(lt(i, s.length)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to the empty bytes.
function clear(BytesStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty bytes "".
function isEmpty(BytesStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(BytesStorage storage $) internal view returns (uint256 result) {
result = uint256($._spacer);
/// @solidity memory-safe-assembly
assembly {
let n := and(0xff, result)
result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))
}
}
/// @dev Returns the value stored in `$`.
function get(BytesStorage storage $) internal view returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
let packed := sload($.slot)
let n := shr(8, packed)
for { let i := 0 } 1 {} {
if iszero(eq(or(packed, 0xff), packed)) {
mstore(o, packed)
n := and(0xff, packed)
i := 0x1f
if iszero(gt(n, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
mstore(add(o, i), sload(add(p, shr(5, i))))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
mstore(result, n) // Store the length of the memory.
mstore(add(o, n), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.
}
}
/// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
function uint8At(BytesStorage storage $, uint256 i) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
for { let packed := sload($.slot) } 1 {} {
if iszero(eq(or(packed, 0xff), packed)) {
if iszero(gt(i, 0x1e)) {
result := byte(i, packed)
break
}
if iszero(gt(i, and(0xff, packed))) {
mstore(0x00, $.slot)
let j := sub(i, 0x1f)
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
}
break
}
if iszero(gt(i, shr(8, packed))) {
mstore(0x00, $.slot)
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
}
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(bytes memory subject, bytes memory needle, bytes memory replacement)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let needleLen := mload(needle)
let replacementLen := mload(replacement)
let d := sub(result, subject) // Memory difference.
let i := add(subject, 0x20) // Subject bytes pointer.
mstore(0x00, add(i, mload(subject))) // End of subject.
if iszero(gt(needleLen, mload(subject))) {
let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, needleLen), h)) {
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
j := add(j, 0x20)
if iszero(lt(j, replacementLen)) { break }
}
d := sub(add(d, replacementLen), needleLen)
if needleLen {
i := add(i, needleLen)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
}
let end := mload(0x00)
let n := add(sub(d, add(result, 0x20)), end)
// Copy the rest of the bytes one word at a time.
for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
let o := add(i, d)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
for { let subjectLen := mload(subject) } 1 {} {
if iszero(mload(needle)) {
result := from
if iszero(gt(from, subjectLen)) { break }
result := subjectLen
break
}
let needleLen := mload(needle)
let subjectStart := add(subject, 0x20)
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
let s := mload(add(needle, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
if iszero(lt(needleLen, 0x20)) {
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`. Optimized for byte needles.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOfByte(bytes memory subject, bytes1 needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
if gt(mload(subject), from) {
let start := add(subject, 0x20)
let end := add(start, mload(subject))
let m := div(not(0), 255) // `0x0101 ... `.
let h := mul(byte(0, needle), m) // Replicating needle mask.
m := not(shl(7, m)) // `0x7f7f ... `.
for { let i := add(start, from) } 1 {} {
let c := xor(mload(i), h) // Load 32-byte chunk and xor with mask.
c := not(or(or(add(and(c, m), m), c), m)) // Each needle byte will be `0x80`.
if c {
c := and(not(shr(shl(3, sub(end, i)), not(0))), c) // Truncate bytes past the end.
if c {
let r := shl(7, lt(0x8421084210842108cc6318c6db6d54be, c)) // Save bytecode.
r := or(shl(6, lt(0xffffffffffffffff, shr(r, c))), r)
// forgefmt: disable-next-item
result := add(sub(i, start), shr(3, xor(byte(and(0x1f, shr(byte(24,
mul(0x02040810204081, shr(r, c))), 0x8421084210842108cc6318c6db6d54be)),
0xc0c8c8d0c8e8d0d8c8e8e0e8d0d8e0f0c8d0e8d0e0e0d8f0d0d0e0d8f8f8f8f8), r)))
break
}
}
i := add(i, 0x20)
if iszero(lt(i, end)) { break }
}
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right. Optimized for byte needles.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOfByte(bytes memory subject, bytes1 needle)
internal
pure
returns (uint256 result)
{
return indexOfByte(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {
return indexOf(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let needleLen := mload(needle)
if gt(needleLen, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), needleLen)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256)
{
return lastIndexOf(subject, needle, type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) {
return indexOf(subject, needle) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
// Just using keccak256 directly is actually cheaper.
let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))
result := lt(gt(n, mload(subject)), t)
}
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
let notInRange := gt(n, mload(subject))
// `subject + 0x20 + max(subject.length - needle.length, 0)`.
let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))
// Just using keccak256 directly is actually cheaper.
result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(bytes memory subject, uint256 times)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(or(iszero(times), iszero(l))) {
result := mload(0x40)
subject := add(subject, 0x20)
let o := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(o, j), mload(add(subject, j)))
j := add(j, 0x20)
if iszero(lt(j, l)) { break }
}
o := add(o, l)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(bytes memory subject, uint256 start, uint256 end)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(gt(l, end)) { end := l }
if iszero(gt(l, start)) { start := l }
if lt(start, end) {
result := mload(0x40)
let n := sub(end, start)
let i := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let j := and(add(n, 0x1f), w) } 1 {} {
mstore(add(result, j), mload(add(i, j)))
j := add(j, w) // `sub(j, 0x20)`.
if iszero(j) { break }
}
let o := add(add(result, 0x20), n)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset.
function slice(bytes memory subject, uint256 start)
internal
pure
returns (bytes memory result)
{
result = slice(subject, start, type(uint256).max);
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, end), sub(end, start))
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, subject.length), sub(subject.length, start))
}
}
/// @dev Reduces the size of `subject` to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncate(bytes memory subject, uint256 n)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := subject
mstore(mul(lt(n, mload(result)), result), n)
}
}
/// @dev Returns a copy of `subject`, with the length reduced to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncatedCalldata(bytes calldata subject, uint256 n)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
result.offset := subject.offset
result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))
}
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLen := mload(needle)
if iszero(gt(searchLen, mload(subject))) {
result := mload(0x40)
let i := add(subject, 0x20)
let o := add(result, 0x20)
let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, searchLen), h)) {
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
o := add(o, 0x20)
i := add(i, searchLen) // Advance `i` by `searchLen`.
if searchLen {
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(o, 0x20))
}
}
}
/// @dev Returns an arrays of bytess based on the `delimiter` inside of the `subject` bytes.
function split(bytes memory subject, bytes memory delimiter)
internal
pure
returns (bytes[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
for { let prevIndex := 0 } 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let l := sub(index, prevIndex)
mstore(element, l) // Store the length of the element.
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(l, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes.
// Allocate memory for the length and the bytes, rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(l, 0x3f), w)))
mstore(indexPtr, element) // Store the `element` into the array.
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated bytes of `a` and `b`.
/// Cheaper than `bytes.concat()` and does not de-align the free memory pointer.
function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let w := not(0x1f)
let aLen := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLen, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLen := mload(b)
let output := add(result, aLen)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLen, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLen := add(aLen, bLen)
let last := add(add(result, 0x20), totalLen)
mstore(last, 0) // Zeroize the slot after the bytes.
mstore(result, totalLen) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate memory.
}
}
/// @dev Returns whether `a` equals `b`.
function eq(bytes memory a, bytes memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small bytes.
function eqs(bytes memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`.
/// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1.
function cmp(bytes memory a, bytes memory b) internal pure returns (int256 result) {
/// @solidity memory-safe-assembly
assembly {
let aLen := mload(a)
let bLen := mload(b)
let n := and(xor(aLen, mul(xor(aLen, bLen), lt(bLen, aLen))), not(0x1f))
if n {
for { let i := 0x20 } 1 {} {
let x := mload(add(a, i))
let y := mload(add(b, i))
if iszero(or(xor(x, y), eq(i, n))) {
i := add(i, 0x20)
continue
}
result := sub(gt(x, y), lt(x, y))
break
}
}
// forgefmt: disable-next-item
if iszero(result) {
let l := 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201
let x := and(mload(add(add(a, 0x20), n)), shl(shl(3, byte(sub(aLen, n), l)), not(0)))
let y := and(mload(add(add(b, 0x20), n)), shl(shl(3, byte(sub(bLen, n), l)), not(0)))
result := sub(gt(x, y), lt(x, y))
if iszero(result) { result := sub(gt(aLen, bLen), lt(aLen, bLen)) }
}
}
}
/// @dev Directly returns `a` without copying.
function directReturn(bytes memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
// Assumes that the bytes does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the bytes is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
mstore(retStart, 0x20) // Store the return offset.
// End the transaction, returning the bytes.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
/// @dev Directly returns `a` with minimal copying.
function directReturn(bytes[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(a) // `a.length`.
let o := add(a, 0x20) // Start of elements in `a`.
let u := a // Highest memory slot.
let w := not(0x1f)
for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } {
let c := add(o, shl(5, i)) // Location of pointer to `a[i]`.
let s := mload(c) // `a[i]`.
let l := mload(s) // `a[i].length`.
let r := and(l, 0x1f) // `a[i].length % 32`.
let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`.
// If `s` comes before `o`, or `s` is not zero right padded.
if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) {
let m := mload(0x40)
mstore(m, l) // Copy `a[i].length`.
for {} 1 {} {
mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards.
z := add(z, w) // `sub(z, 0x20)`.
if iszero(z) { break }
}
let e := add(add(m, 0x20), l)
mstore(e, 0) // Zeroize the slot after the copied bytes.
mstore(0x40, add(e, 0x20)) // Allocate memory.
s := m
}
mstore(c, sub(s, o)) // Convert to calldata offset.
let t := add(l, add(s, 0x20))
if iszero(lt(t, u)) { u := t }
}
let retStart := add(a, w) // Assumes `a` doesn't start from scratch space.
mstore(retStart, 0x20) // Store the return offset.
return(retStart, add(0x40, sub(u, retStart))) // End the transaction.
}
}
/// @dev Returns the word at `offset`, without any bounds checks.
function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(add(add(a, 0x20), offset))
}
}
/// @dev Returns the word at `offset`, without any bounds checks.
function loadCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
result := calldataload(add(a.offset, offset))
}
}
/// @dev Returns a slice representing a static struct in the calldata. Performs bounds checks.
function staticStructInCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
let l := sub(a.length, 0x20)
result.offset := add(a.offset, offset)
result.length := sub(a.length, offset)
if or(shr(64, or(l, a.offset)), gt(offset, l)) { revert(l, 0x00) }
}
}
/// @dev Returns a slice representing a dynamic struct in the calldata. Performs bounds checks.
function dynamicStructInCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
let l := sub(a.length, 0x20)
let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`.
result.offset := add(a.offset, s)
result.length := sub(a.length, s)
if or(shr(64, or(s, or(l, a.offset))), gt(offset, l)) { revert(l, 0x00) }
}
}
/// @dev Returns bytes in calldata. Performs bounds checks.
function bytesInCalldata(bytes calldata a, uint256 offset)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
let l := sub(a.length, 0x20)
let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`.
result.offset := add(add(a.offset, s), 0x20)
result.length := calldataload(add(a.offset, s))
// forgefmt: disable-next-item
if or(shr(64, or(result.length, or(s, or(l, a.offset)))),
or(gt(add(s, result.length), l), gt(offset, l))) { revert(l, 0x00) }
}
}
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
function checkInCalldata(bytes calldata x, bytes calldata a) internal pure {
/// @solidity memory-safe-assembly
assembly {
if or(
or(lt(x.offset, a.offset), gt(add(x.offset, x.length), add(a.length, a.offset))),
shr(64, or(x.length, x.offset))
) { revert(0x00, 0x00) }
}
}
/// @dev Checks if `x` is in `a`. Assumes `a` has been checked.
function checkInCalldata(bytes[] calldata x, bytes calldata a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let e := sub(add(a.length, a.offset), 0x20)
if or(lt(x.offset, a.offset), shr(64, x.offset)) { revert(0x00, 0x00) }
for { let i := 0 } iszero(eq(x.length, i)) { i := add(i, 1) } {
let o := calldataload(add(x.offset, shl(5, i)))
let t := add(o, x.offset)
let l := calldataload(t)
if or(shr(64, or(l, o)), gt(add(t, l), e)) { revert(0x00, 0x00) }
}
}
}
/// @dev Returns empty calldata bytes. For silencing the compiler.
function emptyCalldata() internal pure returns (bytes calldata result) {
/// @solidity memory-safe-assembly
assembly {
result.length := 0
}
}
/// @dev Returns the most significant 20 bytes as an address.
function msbToAddress(bytes32 x) internal pure returns (address) {
return address(bytes20(x));
}
/// @dev Returns the least significant 20 bytes as an address.
function lsbToAddress(bytes32 x) internal pure returns (address) {
return address(uint160(uint256(x)));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}{
"remappings": [
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"forge-std/=node_modules/forge-std/",
"@manifoldxyz/creator-core-solidity/=node_modules/@manifoldxyz/creator-core-solidity/",
"solady/=node_modules/solady/src/",
"@ensdomains/=node_modules/@ensdomains/",
"@limitbreak/=node_modules/@limitbreak/",
"erc721a/=node_modules/erc721a/"
],
"optimizer": {
"enabled": true,
"runs": 10000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract INormiesStorage","name":"_storage","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidTraitIndex","type":"error"},{"inputs":[],"name":"RescueTransferFailed","type":"error"},{"inputs":[],"name":"RescueUnauthorizedOrLocked","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"TokenDataNotSet","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":[{"internalType":"uint256","name":"locksToSet","type":"uint256"}],"name":"lockRescue","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"rescueERC1155","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueERC20","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueERC6909","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"rescueERC721","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"rescueLocked","outputs":[{"internalType":"uint256","name":"locks","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract INormiesStorage","name":"_storage","type":"address"}],"name":"setStorageContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storageContract","outputs":[{"internalType":"contract INormiesStorage","name":"","type":"address"}],"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":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a060405234801561000f575f5ffd5b506040516140d33803806140d383398101604081905261002e916100bb565b6100373361006c565b305f9081523260205260409020608052600180546001600160a01b0319166001600160a01b03929092169190911790556100e8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f602082840312156100cb575f5ffd5b81516001600160a01b03811681146100e1575f5ffd5b9392505050565b608051613fd36101005f395f6109040152613fd35ff3fe6080604052600436106100ce575f3560e01c80637f1f0c671161007c578063b2118a8d11610057578063b2118a8d14610202578063c87b56dd14610215578063dc38b0a214610241578063f2fde38b14610260575f5ffd5b80637f1f0c67146101b35780638da5cb5b146101c65780639311ca89146101ef575f5ffd5b80633571ae8f116100ac5780633571ae8f14610150578063715018a61461018c5780637df325e1146101a0575f5ffd5b8063099a04e5146100d25780631135c5d2146100e757806311ce0267146100fa575b5f5ffd5b6100e56100e0366004613105565b61027f565b005b6100e56100f536600461312f565b6102a8565b348015610105575f5ffd5b506001546101269073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561015b575f5ffd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b54604051908152602001610147565b348015610197575f5ffd5b506100e5610317565b6100e56101ae3660046131d4565b61032a565b6100e56101c1366004613212565b610376565b3480156101d1575f5ffd5b505f5473ffffffffffffffffffffffffffffffffffffffff16610126565b6100e56101fd366004613229565b6103ac565b6100e56102103660046131d4565b6103f6565b348015610220575f5ffd5b5061023461022f366004613212565b610435565b604051610147919061326c565b34801561024c575f5ffd5b506100e561025b3660046132a1565b6107d1565b34801561026b575f5ffd5b506100e561027a3660046132a1565b610820565b600861028a816108d7565b5f385f3885875af16102a357637ec62e765f526004601cfd5b505050565b60406102b3816108d7565b604051677ec62e76f242432a81523081602001528660601b60601c816040015285816060015284816080015260a08160a00152828160c00152828460e08301375f388460c401601c8401348c5af161030d57600460188201fd5b5050505050505050565b61031f610972565b6103285f6109f2565b565b6020610335816108d7565b604051826060528360601b60601c60405230602052677ec62e7623b872dd5f525f386064601c34895af16103695760046018fd5b5f60605260405250505050565b6004610381816108d7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b8054831790555050565b60806103b7816108d7565b604051846014528360345282605452677ec62e76095bcdb660601b5f525f3860646010348a5af16103e8576004600cfd5b5f6060526040525050505050565b6010610401816108d7565b8260145281603452677ec62e76a9059cbb60601b5f525f386044601034885af161042b576004600cfd5b5f60345250505050565b6001546040517f0aebf22c0000000000000000000000000000000000000000000000000000000081526004810183905260609173ffffffffffffffffffffffffffffffffffffffff1690630aebf22c90602401602060405180830381865afa1580156104a3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104c791906132bc565b829061050b576040517f62ca365800000000000000000000000000000000000000000000000000000000815260040161050291815260200190565b60405180910390fd5b5060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166354214f696040518163ffffffff1660e01b8152600401602060405180830381865afa158015610576573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061059a91906132bc565b6105ad576105a782610a66565b92915050565b6001546040517f6985bf3c000000000000000000000000000000000000000000000000000000008152600481018490525f9173ffffffffffffffffffffffffffffffffffffffff1690636985bf3c906024015f60405180830381865afa158015610619573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526106409190810190613308565b6001546040517f94e56847000000000000000000000000000000000000000000000000000000008152600481018690529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906394e5684790602401602060405180830381865afa1580156106b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106d691906133bc565b90505f6106e283610b4d565b90505f6106ee82610cea565b90505f6106fa87610cf7565b60405160200161070a9190613412565b60405160208183030381529060405290505f61072585610d39565b83604051602001610737929190613470565b60405160208183030381529060405290505f61075287611021565b6040516020016107629190613504565b60405160208183030381529060405290505f8383836040516020016107899392919061353c565b60405160208183030381529060405290506107a381610cea565b6040516020016107b3919061355c565b60405160208183030381529060405298505050505050505050919050565b6107d9610972565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b610828610972565b73ffffffffffffffffffffffffffffffffffffffff81166108cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610502565b6108d4816109f2565b50565b5f6109007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b5490565b90507f00000000000000000000000000000000000000000000000000000000000000008182841615155f03179150336020523060018316528060405f2003156102a357678da5cb5b0a0362e060085260205f60046020305afa335f5114601f3d11166002841610166102a35760046024fd5b5f5473ffffffffffffffffffffffffffffffffffffffff163314610328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610502565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60605f610a72836110a0565b90505f610a7e82610cea565b90505f610a8a85610cf7565b604051602001610a9a9190613412565b60405160208183030381529060405290505f82604051602001610abd919061358d565b60405160208183030381529060405290505f83604051602001610ae09190613504565b60405160208183030381529060405290505f838383604051602001610b079392919061353c565b6040516020818303038152906040529050610b2181610cea565b604051602001610b31919061355c565b6040516020818303038152906040529650505050505050919050565b60605f604051602001610c41907f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d223130303022206865696768743d2231303060208201527f30222076696577426f783d22302030203430203430222073686170652d72656e60408201527f646572696e673d2263726973704564676573223e3c726563742077696474683d60608201527f22343022206865696768743d223430222066696c6c3d2223653365356534222f60808201527f3e0000000000000000000000000000000000000000000000000000000000000060a082015260a10190565b60408051601f1981840301815291905290505f5b6028811015610cc1575f5b6028811015610cb857610c74858284611718565b15610cb05782610c8382610cf7565b610c8c84610cf7565b604051602001610c9e93929190613682565b60405160208183030381529060405292505b600101610c60565b50600101610c55565b5080604051602001610cd39190613745565b60408051601f198184030181529190529392505050565b60606105a7825f5f611779565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480610d10575050819003601f19909101908152919050565b60605f610d956040518060400160405280600481526020017f5479706500000000000000000000000000000000000000000000000000000000815250610d90855f60088110610d8a57610d8a61377d565b1a611886565b6119c9565b60408051808201909152600681527f47656e64657200000000000000000000000000000000000000000000000000006020820152610dd990610d908660011a6119f5565b60408051808201909152600381527f41676500000000000000000000000000000000000000000000000000000000006020820152610e1d90610d908760021a611ac2565b60408051808201909152600a81527f48616972205374796c65000000000000000000000000000000000000000000006020820152610e6190610d908860031a611b8f565b604051602001610e7494939291906137aa565b60408051601f19818403018152828201909152600e82527f46616369616c2046656174757265000000000000000000000000000000000000602083015291505f90610ec590610d908660041a612124565b60408051808201909152600481527f45796573000000000000000000000000000000000000000000000000000000006020820152610f0990610d908760051a6125a9565b60408051808201909152600a81527f45787072657373696f6e000000000000000000000000000000000000000000006020820152610f4d90610d908860061a612962565b60408051808201909152600981527f4163636573736f727900000000000000000000000000000000000000000000006020820152610f9190610d908960071a612b3f565b610fd16040518060400160405280600581526020017f4c6576656c0000000000000000000000000000000000000000000000000000008152506001612f3c565b604051602001610fe5959493929190613854565b60405160208183030381529060405290508181604051602001611009929190613957565b60405160208183030381529060405292505050919050565b60605f61102d83612f59565b60405160200161103d9190613965565b60405160208183030381529060405290508060405160200161105f9190613aef565b604051602081830303815290604052905061107981610cea565b6040516020016110899190613c59565b604051602081830303815290604052915050919050565b60408051620186a0808252620186c082019092526060915f91906020820181803683370190505090505f5f8490506110f2838360405180610100016040528060db8152602001613eec60db9139612fbe565b91505f5b60208110156111b35761113f84846040518060400160405280600281526020017f2e6e000000000000000000000000000000000000000000000000000000000000815250612fbe565b925061114c848483613001565b925061118e84846040518060400160405280601181526020017f7b616e696d6174696f6e2d6e616d653a6e000000000000000000000000000000815250612fbe565b925061119b848483613001565b92506111a98484607d613087565b92506001016110f6565b5060015b60088110156112b75761120084846040518060400160405280600281526020017f2e64000000000000000000000000000000000000000000000000000000000000815250612fbe565b92506112178484611212846030613cb7565b613087565b925061125984846040518060400160405280601381526020017f7b616e696d6174696f6e2d64656c61793a2d2e00000000000000000000000000815250612fbe565b925061126b8484611212846030613cb7565b92506112ad84846040518060400160405280600281526020017f737d000000000000000000000000000000000000000000000000000000000000815250612fbe565b92506001016111b7565b505f5b602081101561145f57806003165f036112f457604080516020810184905201604051602081830303815290604052805190602001205f1c91505b61133484846040518060400160405280600c81526020017f406b65796672616d6573206e0000000000000000000000000000000000000000815250612fbe565b9250611341848483613001565b925061134f8484607b613087565b92505f5b60088110156114485761137d8585600a61136e85607d613cca565b6113789190613d0e565b613001565b93506113bf85856040518060400160405280600881526020017f257b66696c6c3a23000000000000000000000000000000000000000000000000815250612fbe565b93505f60a284836113d4600387166008613cca565b6113de9190613cb7565b602081106113ee576113ee61377d565b6113fa9291901a613d21565b61140590603e613d42565b60ff1690506114158686836130a3565b94506114228686836130a3565b945061142f8686836130a3565b945061143d8686607d613087565b945050600101611353565b506114558484607d613087565b92506001016112ba565b506114a083836040518060400160405280600881526020017f3c2f7374796c653e000000000000000000000000000000000000000000000000815250612fbe565b91505f5b60288110156116cc575f5b60288110156116c3575f816114c5846028613cca565b6114cf9190613cb7565b905080601f165f0361150257604080516020810186905201604051602081830303815290604052805190602001205f1c93505b5f84601f8316602081106115185761151861377d565b60408051808201909152600981527f3c7265637420783d220000000000000000000000000000000000000000000000602082015291901a9150600382901c906007831690611569908a908a90612fbe565b9750611576898987613001565b97506115b889896040518060400160405280600581526020017f2220793d22000000000000000000000000000000000000000000000000000000815250612fbe565b97506115c5898988613001565b975061160789896040518060400160405280601f81526020017f222077696474683d223122206865696768743d22312220636c6173733d226e00815250612fbe565b9750611614898984613001565b975080156116715761165c89896040518060400160405280600281526020017f2064000000000000000000000000000000000000000000000000000000000000815250612fbe565b975061166e8989611212846030613cb7565b97505b6116b189896040518060400160405280600381526020017f222f3e0000000000000000000000000000000000000000000000000000000000815250612fbe565b975050600190930192506114af915050565b506001016114a4565b5061170d83836040518060400160405280600681526020017f3c2f7376673e0000000000000000000000000000000000000000000000000000815250612fbe565b835250909392505050565b5f8083611726846028613cca565b6117309190613cb7565b9050600381901c5f611746600780851690613d5b565b90508087838151811061175b5761175b61377d565b60209101015160f81c901c60019081161493505050505b9392505050565b60608351801561187e576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020830181810183886020010180515f82525b60038a0199508951603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f5184526004840193508284106117f45790526020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b60608160ff165f036118cb57505060408051808201909152600581527f48756d616e000000000000000000000000000000000000000000000000000000602082015290565b8160ff1660010361190f57505060408051808201909152600381527f4361740000000000000000000000000000000000000000000000000000000000602082015290565b8160ff1660020361195357505060408051808201909152600581527f416c69656e000000000000000000000000000000000000000000000000000000602082015290565b8160ff1660030361199757505060408051808201909152600581527f4167656e74000000000000000000000000000000000000000000000000000000602082015290565b6040517f319abe9000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606082826040516020016119de929190613d6e565b604051602081830303815290604052905092915050565b60608160ff165f03611a3a57505060408051808201909152600481527f4d616c6500000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600103611a7e57505060408051808201909152600681527f46656d616c650000000000000000000000000000000000000000000000000000602082015290565b8160ff1660020361199757505060408051808201909152600a81527f4e6f6e2d42696e61727900000000000000000000000000000000000000000000602082015290565b60608160ff165f03611b0757505060408051808201909152600581527f596f756e67000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600103611b4b57505060408051808201909152600b81527f4d6964646c652d41676564000000000000000000000000000000000000000000602082015290565b8160ff1660020361199757505060408051808201909152600381527f4f6c640000000000000000000000000000000000000000000000000000000000602082015290565b60608160ff165f03611bd457505060408051808201909152600a81527f53686f7274204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600103611c1857505060408051808201909152600981527f4c6f6e6720486169720000000000000000000000000000000000000000000000602082015290565b8160ff16600203611c5c57505060408051808201909152600a81527f4375726c79204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600303611ca057505060408051808201909152600d81527f5374726169676874204861697200000000000000000000000000000000000000602082015290565b8160ff16600403611ce457505060408051808201909152600981527f5761767920486169720000000000000000000000000000000000000000000000602082015290565b8160ff16600503611d2857505060408051808201909152600a81527f5370696b79204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600603611d6c57505060408051808201909152600981527f57696c6420486169720000000000000000000000000000000000000000000000602082015290565b8160ff16600703611db057505060408051808201909152600a81527f4d65737379204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600803611df457505060408051808201909152600681527f4d6f6861776b0000000000000000000000000000000000000000000000000000602082015290565b8160ff16600903611e3857505060408051808201909152600a81527f4372617a79204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600a03611e7c57505060408051808201909152600c81527f4272616964656420486169720000000000000000000000000000000000000000602082015290565b8160ff16600b03611ec057505060408051808201909152600481527f42616c6400000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600c03611f0457505060408051808201909152600881527f506f6e797461696c000000000000000000000000000000000000000000000000602082015290565b8160ff16600d03611f4857505060408051808201909152600881527f5069677461696c73000000000000000000000000000000000000000000000000602082015290565b8160ff16600e03611f8c57505060408051808201909152600481527f4166726f00000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600f03611fd057505060408051808201909152600881527f42757a7a20437574000000000000000000000000000000000000000000000000602082015290565b8160ff1660100361201457505060408051808201909152600b81527f4672756d70792048616972000000000000000000000000000000000000000000602082015290565b8160ff1660110361205857505060408051808201909152600c81527f537472696e677920486169720000000000000000000000000000000000000000602082015290565b8160ff1660120361209c57505060408051808201909152600a81527f5065616b205370696b6500000000000000000000000000000000000000000000602082015290565b8160ff166013036120e057505060408051808201909152600b81527f48616c6620536861766564000000000000000000000000000000000000000000602082015290565b8160ff1660140361199757505060408051808201909152600b81527f4b6e697474656420436170000000000000000000000000000000000000000000602082015290565b60608160ff165f0361216957505060408051808201909152600a81527f46756c6c20426561726400000000000000000000000000000000000000000000602082015290565b8160ff166001036121ad57505060408051808201909152600881527f4d75737461636865000000000000000000000000000000000000000000000000602082015290565b8160ff166002036121f157505060408051808201909152600681527f476f617465650000000000000000000000000000000000000000000000000000602082015290565b8160ff1660030361223557505060408051808201909152600a81527f4368696e20537472617000000000000000000000000000000000000000000000602082015290565b8160ff1660040361227957505060408051808201909152600b81527f4d7574746f6e63686f7073000000000000000000000000000000000000000000602082015290565b8160ff166005036122bd57505060408051808201909152600c81527f536861646f772042656172640000000000000000000000000000000000000000602082015290565b8160ff1660060361230157505060408051808201909152600f81527f4c75787572696f75732042656172640000000000000000000000000000000000602082015290565b8160ff1660070361234557505060408051808201909152600a81527f48616e646c656261727300000000000000000000000000000000000000000000602082015290565b8160ff1660080361238957505060408051808201909152600981527f4269672042656172640000000000000000000000000000000000000000000000602082015290565b8160ff166009036123cd57505060408051808201909152600c81527f4e6f726d616c2042656172640000000000000000000000000000000000000000602082015290565b8160ff16600a0361241157505060408051808201909152600c81527f436c65616e2053686176656e0000000000000000000000000000000000000000602082015290565b8160ff16600b0361245557505060408051808201909152600881527f467265636b6c6573000000000000000000000000000000000000000000000000602082015290565b8160ff16600c0361249957505060408051808201909152600481527f4d6f6c6500000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600d036124dd57505060408051808201909152600b81527f526f737920436865656b73000000000000000000000000000000000000000000602082015290565b8160ff16600e0361252157505060408051808201909152600781527f44696d706c657300000000000000000000000000000000000000000000000000602082015290565b8160ff16600f0361256557505060408051808201909152600f81527f4869676820436865656b626f6e65730000000000000000000000000000000000602082015290565b8160ff1660100361199757505060408051808201909152600581527f53706f7473000000000000000000000000000000000000000000000000000000602082015290565b60608160ff165f036125ee57505060408051808201909152600e81527f436c617373696320536861646573000000000000000000000000000000000000602082015290565b8160ff1660010361263257505060408051808201909152600a81527f4269672053686164657300000000000000000000000000000000000000000000602082015290565b8160ff1660020361267657505060408051808201909152600e81527f526567756c617220536861646573000000000000000000000000000000000000602082015290565b8160ff166003036126ba57505060408051808201909152600c81527f536d616c6c205368616465730000000000000000000000000000000000000000602082015290565b8160ff166004036126fe57505060408051808201909152601281527f486f726e65642052696d20476c61737365730000000000000000000000000000602082015290565b8160ff1660050361274257505060408051808201909152600c81527f4e65726420476c61737365730000000000000000000000000000000000000000602082015290565b8160ff1660060361278657505060408051808201909152600a81527f5652204865616473657400000000000000000000000000000000000000000000602082015290565b8160ff166007036127ca57505060408051808201909152600a81527f334420476c617373657300000000000000000000000000000000000000000000602082015290565b8160ff1660080361280e57505060408051808201909152600881527f457965204d61736b000000000000000000000000000000000000000000000000602082015290565b8160ff1660090361285257505060408051808201909152600981527f4579652050617463680000000000000000000000000000000000000000000000602082015290565b8160ff16600a0361289657505060408051808201909152600d81527f526f756e6420476c617373657300000000000000000000000000000000000000602082015290565b8160ff16600b036128da57505060408051808201909152600e81527f53717561726520476c6173736573000000000000000000000000000000000000602082015290565b8160ff16600c0361291e57505060408051808201909152600881527f41766961746f7273000000000000000000000000000000000000000000000000602082015290565b8160ff16600d0361199757505060408051808201909152600a81527f4e6f20476c617373657300000000000000000000000000000000000000000000602082015290565b60608160ff165f036129a757505060408051808201909152600781527f4e65757472616c00000000000000000000000000000000000000000000000000602082015290565b8160ff166001036129eb57505060408051808201909152600c81527f536c6967687420536d696c650000000000000000000000000000000000000000602082015290565b8160ff16600203612a2f57505060408051808201909152600781527f536572696f757300000000000000000000000000000000000000000000000000602082015290565b8160ff16600303612a7357505060408051808201909152600781527f436f6e74656e7400000000000000000000000000000000000000000000000000602082015290565b8160ff16600403612ab757505060408051808201909152600881527f506561636566756c000000000000000000000000000000000000000000000000602082015290565b8160ff16600503612afb57505060408051808201909152600981527f436f6e666964656e740000000000000000000000000000000000000000000000602082015290565b8160ff1660060361199757505060408051808201909152600881527f467269656e646c79000000000000000000000000000000000000000000000000602082015290565b60608160ff165f03612b8457505060408051808201909152600781527f546f702048617400000000000000000000000000000000000000000000000000602082015290565b8160ff16600103612bc857505060408051808201909152600681527f4665646f72610000000000000000000000000000000000000000000000000000602082015290565b8160ff16600203612c0c57505060408051808201909152600a81527f436f77626f792048617400000000000000000000000000000000000000000000602082015290565b8160ff16600303612c5057505060408051808201909152600681527f4265616e69650000000000000000000000000000000000000000000000000000602082015290565b8160ff16600403612c9457505060408051808201909152600381527f4361700000000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600503612cd857505060408051808201909152600b81527f43617020466f7277617264000000000000000000000000000000000000000000602082015290565b8160ff16600603612d1c57505060408051808201909152600781527f42616e64616e6100000000000000000000000000000000000000000000000000602082015290565b8160ff16600703612d6057505060408051808201909152600881527f4865616462616e64000000000000000000000000000000000000000000000000602082015290565b8160ff16600803612da457505060408051808201909152600681527f446f2d5261670000000000000000000000000000000000000000000000000000602082015290565b8160ff16600903612de857505060408051808201909152600681527f486f6f6469650000000000000000000000000000000000000000000000000000602082015290565b8160ff16600a03612e2c57505060408051808201909152600781527f45617272696e6700000000000000000000000000000000000000000000000000602082015290565b8160ff16600b03612e7057505060408051808201909152600a81527f476f6c6420436861696e00000000000000000000000000000000000000000000602082015290565b8160ff16600c03612eb457505060408051808201909152600c81527f53696c76657220436861696e0000000000000000000000000000000000000000602082015290565b8160ff16600d03612ef857505060408051808201909152600781527f426f772054696500000000000000000000000000000000000000000000000000602082015290565b8160ff16600e0361199757505060408051808201909152600e81527f4e6f204163636573736f72696573000000000000000000000000000000000000602082015290565b606082612f4883610cf7565b6040516020016119de929190613dfe565b8051604051818001600282019081526f30313233343536373839616263646566600f52918301906022015b818414612faf57600184019350600f845116516001820153600f845160041c16518153600201612f84565b5f815260200160405250919050565b80515f90829060208587018101908301845b83811015612fe8578181015183820152602001612fd0565b5050508085612ff79190613cb7565b9695505050505050565b5f815f0361301c5761301584846030613087565b9050611772565b815f5b8115613045578061302f81613eb4565b915061303e9050600a83613d0e565b915061301f565b5f6130508287613cb7565b905080602088015b861561307a57600182039150600a870660300182820153600a87049650613058565b5090979650505050505050565b5f818360208601015361309b836001613cb7565b949350505050565b5f826020850101600f8360041c16600f84166027600983110260308301018353602760098211026030820101600184015350505082600261309b9190613cb7565b73ffffffffffffffffffffffffffffffffffffffff811681146108d4575f5ffd5b5f5f60408385031215613116575f5ffd5b8235613121816130e4565b946020939093013593505050565b5f5f5f5f5f5f60a08789031215613144575f5ffd5b863561314f816130e4565b9550602087013561315f816130e4565b94506040870135935060608701359250608087013567ffffffffffffffff811115613188575f5ffd5b8701601f81018913613198575f5ffd5b803567ffffffffffffffff8111156131ae575f5ffd5b8960208284010111156131bf575f5ffd5b60208201935080925050509295509295509295565b5f5f5f606084860312156131e6575f5ffd5b83356131f1816130e4565b92506020840135613201816130e4565b929592945050506040919091013590565b5f60208284031215613222575f5ffd5b5035919050565b5f5f5f5f6080858703121561323c575f5ffd5b8435613247816130e4565b93506020850135613257816130e4565b93969395505050506040820135916060013590565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156132b1575f5ffd5b8135611772816130e4565b5f602082840312156132cc575f5ffd5b81518015158114611772575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215613318575f5ffd5b815167ffffffffffffffff81111561332e575f5ffd5b8201601f8101841361333e575f5ffd5b805167ffffffffffffffff811115613358576133586132db565b604051601f19603f601f19601f8501160116810181811067ffffffffffffffff82111715613388576133886132db565b60405281815282820160200186101561339f575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f602082840312156133cc575f5ffd5b81517fffffffffffffffff00000000000000000000000000000000000000000000000081168114611772575f5ffd5b5f81518060208401855e5f93019283525090919050565b7f7b226e616d65223a224e6f726d6965202300000000000000000000000000000081525f61344360118301846133fb565b7f222c2261747472696275746573223a5b0000000000000000000000000000000081526010019392505050565b5f6134d66134d061348184876133fb565b7f5d2c22696d616765223a22646174613a696d6167652f7376672b786d6c3b626181527f736536342c000000000000000000000000000000000000000000000000000000602082015260250190565b846133fb565b7f222c22616e696d6174696f6e5f75726c223a22000000000000000000000000008152601301949350505050565b5f61350f82846133fb565b7f227d00000000000000000000000000000000000000000000000000000000000081526002019392505050565b5f6135536134d061354d84886133fb565b866133fb565b95945050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f611772601d8301846133fb565b7f7b2274726169745f74797065223a2252657665616c6564222c2276616c75652281527f3a224e6f227d000000000000000000000000000000000000000000000000000060208201527f5d2c22696d616765223a22646174613a696d6167652f7376672b786d6c3b626160268201527f736536342c00000000000000000000000000000000000000000000000000000060468201525f61362f604b83016134d0565b7f222c22616e696d6174696f6e5f75726c223a22646174613a696d6167652f737681527f672b786d6c3b6261736536342c000000000000000000000000000000000000006020820152602d019392505050565b5f61368d82866133fb565b7f3c7265637420783d22000000000000000000000000000000000000000000000081526136bd60098201866133fb565b90507f2220793d2200000000000000000000000000000000000000000000000000000081526136ef60058201856133fb565b7f222077696474683d223122206865696768743d2231222066696c6c3d2223343881527f34393462222f3e0000000000000000000000000000000000000000000000000060208201526027019695505050505050565b5f61375082846133fb565b7f3c2f7376673e000000000000000000000000000000000000000000000000000081526006019392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6137b582876133fb565b7f2c0000000000000000000000000000000000000000000000000000000000000081526137e560018201876133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261381760018201866133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261384960018201856133fb565b979650505050505050565b7f2c0000000000000000000000000000000000000000000000000000000000000081525f61388560018301886133fb565b7f2c0000000000000000000000000000000000000000000000000000000000000081526138b560018201886133fb565b90507f2c0000000000000000000000000000000000000000000000000000000000000081526138e760018201876133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261391960018201866133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261394b60018201856133fb565b98975050505050505050565b5f61309b6134d083866133fb565b7f3c68746d6c3e3c626f6479207374796c653d276d617267696e3a303b6f76657281527f666c6f773a68696464656e3b77696474683a31303076773b6865696768743a3160208201527f303076683b646973706c61793a666c65783b616c69676e2d6974656d733a636560408201527f6e7465723b6a7573746966792d636f6e74656e743a63656e7465723b6261636b60608201527f67726f756e643a23653365356534273e3c63616e7661732069643d276327207760808201527f696474683d273230303027206865696768743d273230303027207374796c653d60a08201527f27696d6167652d72656e646572696e673a706978656c617465643b776964746860c08201527f3a6d696e2831303076772c3130307668293b6865696768743a6d696e2831303060e08201527f76772c313030766829273e3c2f63616e7661733e3c7363726970743e766172206101008201527f683d2700000000000000000000000000000000000000000000000000000000006101208201525f6117726101238301846133fb565b5f613afa82846133fb565b7f273b76617220633d646f63756d656e742e676574456c656d656e74427949642881527f276327292e676574436f6e746578742827326427293b632e66696c6c5374796c60208201527f653d2723653365356534273b632e66696c6c5265637428302c302c323030302c60408201527f32303030293b632e66696c6c5374796c653d2723343834393462273b666f722860608201527f76617220793d303b793c34303b792b2b29666f722876617220783d303b783c3460808201527f303b782b2b297b76617220693d792a34302b782c623d7061727365496e74286860a08201527f2e7375627374722828693e3e33292a322c32292c3136293b69662828623e3e2860c08201527f372d28692637292929263129632e66696c6c5265637428782a35302c792a353060e08201527f2c35302c3530297d3c2f7363726970743e3c2f626f64793e3c2f68746d6c3e0061010082015261011f019392505050565b7f646174613a746578742f68746d6c3b6261736536342c0000000000000000000081525f61177260168301846133fb565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808201808211156105a7576105a7613c8a565b80820281158282048414176105a7576105a7613c8a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82613d1c57613d1c613ce1565b500490565b5f60ff831680613d3357613d33613ce1565b8060ff84160691505092915050565b60ff81811683821601908111156105a7576105a7613c8a565b818103818111156105a7576105a7613c8a565b7f7b2274726169745f74797065223a22000000000000000000000000000000000081525f613d9f600f8301856133fb565b7f222c2276616c7565223a220000000000000000000000000000000000000000008152613dcf600b8201856133fb565b7f227d000000000000000000000000000000000000000000000000000000000000815260020195945050505050565b7f7b22646973706c61795f74797065223a226e756d626572222c2274726169745f81527f74797065223a220000000000000000000000000000000000000000000000000060208201525f613e5560278301856133fb565b7f222c2276616c7565223a000000000000000000000000000000000000000000008152613e85600a8201856133fb565b7f7d00000000000000000000000000000000000000000000000000000000000000815260010195945050505050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ee457613ee4613c8a565b506001019056fe3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737667222076696577426f783d22302030203430203430222077696474683d2238303022206865696768743d22383030222073686170652d72656e646572696e673d2263726973704564676573223e3c7374796c653e726563747b616e696d6174696f6e2d6475726174696f6e3a2e38733b616e696d6174696f6e2d74696d696e672d66756e6374696f6e3a737465702d656e643b616e696d6174696f6e2d697465726174696f6e2d636f756e743a696e66696e6974657da164736f6c6343000821000a0000000000000000000000001b976baf51cf51f0e369c070d47fbc47a706e602
Deployed Bytecode
0x6080604052600436106100ce575f3560e01c80637f1f0c671161007c578063b2118a8d11610057578063b2118a8d14610202578063c87b56dd14610215578063dc38b0a214610241578063f2fde38b14610260575f5ffd5b80637f1f0c67146101b35780638da5cb5b146101c65780639311ca89146101ef575f5ffd5b80633571ae8f116100ac5780633571ae8f14610150578063715018a61461018c5780637df325e1146101a0575f5ffd5b8063099a04e5146100d25780631135c5d2146100e757806311ce0267146100fa575b5f5ffd5b6100e56100e0366004613105565b61027f565b005b6100e56100f536600461312f565b6102a8565b348015610105575f5ffd5b506001546101269073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561015b575f5ffd5b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b54604051908152602001610147565b348015610197575f5ffd5b506100e5610317565b6100e56101ae3660046131d4565b61032a565b6100e56101c1366004613212565b610376565b3480156101d1575f5ffd5b505f5473ffffffffffffffffffffffffffffffffffffffff16610126565b6100e56101fd366004613229565b6103ac565b6100e56102103660046131d4565b6103f6565b348015610220575f5ffd5b5061023461022f366004613212565b610435565b604051610147919061326c565b34801561024c575f5ffd5b506100e561025b3660046132a1565b6107d1565b34801561026b575f5ffd5b506100e561027a3660046132a1565b610820565b600861028a816108d7565b5f385f3885875af16102a357637ec62e765f526004601cfd5b505050565b60406102b3816108d7565b604051677ec62e76f242432a81523081602001528660601b60601c816040015285816060015284816080015260a08160a00152828160c00152828460e08301375f388460c401601c8401348c5af161030d57600460188201fd5b5050505050505050565b61031f610972565b6103285f6109f2565b565b6020610335816108d7565b604051826060528360601b60601c60405230602052677ec62e7623b872dd5f525f386064601c34895af16103695760046018fd5b5f60605260405250505050565b6004610381816108d7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b8054831790555050565b60806103b7816108d7565b604051846014528360345282605452677ec62e76095bcdb660601b5f525f3860646010348a5af16103e8576004600cfd5b5f6060526040525050505050565b6010610401816108d7565b8260145281603452677ec62e76a9059cbb60601b5f525f386044601034885af161042b576004600cfd5b5f60345250505050565b6001546040517f0aebf22c0000000000000000000000000000000000000000000000000000000081526004810183905260609173ffffffffffffffffffffffffffffffffffffffff1690630aebf22c90602401602060405180830381865afa1580156104a3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104c791906132bc565b829061050b576040517f62ca365800000000000000000000000000000000000000000000000000000000815260040161050291815260200190565b60405180910390fd5b5060015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166354214f696040518163ffffffff1660e01b8152600401602060405180830381865afa158015610576573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061059a91906132bc565b6105ad576105a782610a66565b92915050565b6001546040517f6985bf3c000000000000000000000000000000000000000000000000000000008152600481018490525f9173ffffffffffffffffffffffffffffffffffffffff1690636985bf3c906024015f60405180830381865afa158015610619573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526106409190810190613308565b6001546040517f94e56847000000000000000000000000000000000000000000000000000000008152600481018690529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906394e5684790602401602060405180830381865afa1580156106b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106d691906133bc565b90505f6106e283610b4d565b90505f6106ee82610cea565b90505f6106fa87610cf7565b60405160200161070a9190613412565b60405160208183030381529060405290505f61072585610d39565b83604051602001610737929190613470565b60405160208183030381529060405290505f61075287611021565b6040516020016107629190613504565b60405160208183030381529060405290505f8383836040516020016107899392919061353c565b60405160208183030381529060405290506107a381610cea565b6040516020016107b3919061355c565b60405160208183030381529060405298505050505050505050919050565b6107d9610972565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b610828610972565b73ffffffffffffffffffffffffffffffffffffffff81166108cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610502565b6108d4816109f2565b50565b5f6109007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffb8e2915b5490565b90507ffd43169ab2219dde2566217c3154527c6435686a675464e5f817e91674ba8d188182841615155f03179150336020523060018316528060405f2003156102a357678da5cb5b0a0362e060085260205f60046020305afa335f5114601f3d11166002841610166102a35760046024fd5b5f5473ffffffffffffffffffffffffffffffffffffffff163314610328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610502565b5f805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60605f610a72836110a0565b90505f610a7e82610cea565b90505f610a8a85610cf7565b604051602001610a9a9190613412565b60405160208183030381529060405290505f82604051602001610abd919061358d565b60405160208183030381529060405290505f83604051602001610ae09190613504565b60405160208183030381529060405290505f838383604051602001610b079392919061353c565b6040516020818303038152906040529050610b2181610cea565b604051602001610b31919061355c565b6040516020818303038152906040529650505050505050919050565b60605f604051602001610c41907f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f737667222077696474683d223130303022206865696768743d2231303060208201527f30222076696577426f783d22302030203430203430222073686170652d72656e60408201527f646572696e673d2263726973704564676573223e3c726563742077696474683d60608201527f22343022206865696768743d223430222066696c6c3d2223653365356534222f60808201527f3e0000000000000000000000000000000000000000000000000000000000000060a082015260a10190565b60408051601f1981840301815291905290505f5b6028811015610cc1575f5b6028811015610cb857610c74858284611718565b15610cb05782610c8382610cf7565b610c8c84610cf7565b604051602001610c9e93929190613682565b60405160208183030381529060405292505b600101610c60565b50600101610c55565b5080604051602001610cd39190613745565b60408051601f198184030181529190529392505050565b60606105a7825f5f611779565b60606080604051019050602081016040525f8152805f19835b928101926030600a8206018453600a900480610d10575050819003601f19909101908152919050565b60605f610d956040518060400160405280600481526020017f5479706500000000000000000000000000000000000000000000000000000000815250610d90855f60088110610d8a57610d8a61377d565b1a611886565b6119c9565b60408051808201909152600681527f47656e64657200000000000000000000000000000000000000000000000000006020820152610dd990610d908660011a6119f5565b60408051808201909152600381527f41676500000000000000000000000000000000000000000000000000000000006020820152610e1d90610d908760021a611ac2565b60408051808201909152600a81527f48616972205374796c65000000000000000000000000000000000000000000006020820152610e6190610d908860031a611b8f565b604051602001610e7494939291906137aa565b60408051601f19818403018152828201909152600e82527f46616369616c2046656174757265000000000000000000000000000000000000602083015291505f90610ec590610d908660041a612124565b60408051808201909152600481527f45796573000000000000000000000000000000000000000000000000000000006020820152610f0990610d908760051a6125a9565b60408051808201909152600a81527f45787072657373696f6e000000000000000000000000000000000000000000006020820152610f4d90610d908860061a612962565b60408051808201909152600981527f4163636573736f727900000000000000000000000000000000000000000000006020820152610f9190610d908960071a612b3f565b610fd16040518060400160405280600581526020017f4c6576656c0000000000000000000000000000000000000000000000000000008152506001612f3c565b604051602001610fe5959493929190613854565b60405160208183030381529060405290508181604051602001611009929190613957565b60405160208183030381529060405292505050919050565b60605f61102d83612f59565b60405160200161103d9190613965565b60405160208183030381529060405290508060405160200161105f9190613aef565b604051602081830303815290604052905061107981610cea565b6040516020016110899190613c59565b604051602081830303815290604052915050919050565b60408051620186a0808252620186c082019092526060915f91906020820181803683370190505090505f5f8490506110f2838360405180610100016040528060db8152602001613eec60db9139612fbe565b91505f5b60208110156111b35761113f84846040518060400160405280600281526020017f2e6e000000000000000000000000000000000000000000000000000000000000815250612fbe565b925061114c848483613001565b925061118e84846040518060400160405280601181526020017f7b616e696d6174696f6e2d6e616d653a6e000000000000000000000000000000815250612fbe565b925061119b848483613001565b92506111a98484607d613087565b92506001016110f6565b5060015b60088110156112b75761120084846040518060400160405280600281526020017f2e64000000000000000000000000000000000000000000000000000000000000815250612fbe565b92506112178484611212846030613cb7565b613087565b925061125984846040518060400160405280601381526020017f7b616e696d6174696f6e2d64656c61793a2d2e00000000000000000000000000815250612fbe565b925061126b8484611212846030613cb7565b92506112ad84846040518060400160405280600281526020017f737d000000000000000000000000000000000000000000000000000000000000815250612fbe565b92506001016111b7565b505f5b602081101561145f57806003165f036112f457604080516020810184905201604051602081830303815290604052805190602001205f1c91505b61133484846040518060400160405280600c81526020017f406b65796672616d6573206e0000000000000000000000000000000000000000815250612fbe565b9250611341848483613001565b925061134f8484607b613087565b92505f5b60088110156114485761137d8585600a61136e85607d613cca565b6113789190613d0e565b613001565b93506113bf85856040518060400160405280600881526020017f257b66696c6c3a23000000000000000000000000000000000000000000000000815250612fbe565b93505f60a284836113d4600387166008613cca565b6113de9190613cb7565b602081106113ee576113ee61377d565b6113fa9291901a613d21565b61140590603e613d42565b60ff1690506114158686836130a3565b94506114228686836130a3565b945061142f8686836130a3565b945061143d8686607d613087565b945050600101611353565b506114558484607d613087565b92506001016112ba565b506114a083836040518060400160405280600881526020017f3c2f7374796c653e000000000000000000000000000000000000000000000000815250612fbe565b91505f5b60288110156116cc575f5b60288110156116c3575f816114c5846028613cca565b6114cf9190613cb7565b905080601f165f0361150257604080516020810186905201604051602081830303815290604052805190602001205f1c93505b5f84601f8316602081106115185761151861377d565b60408051808201909152600981527f3c7265637420783d220000000000000000000000000000000000000000000000602082015291901a9150600382901c906007831690611569908a908a90612fbe565b9750611576898987613001565b97506115b889896040518060400160405280600581526020017f2220793d22000000000000000000000000000000000000000000000000000000815250612fbe565b97506115c5898988613001565b975061160789896040518060400160405280601f81526020017f222077696474683d223122206865696768743d22312220636c6173733d226e00815250612fbe565b9750611614898984613001565b975080156116715761165c89896040518060400160405280600281526020017f2064000000000000000000000000000000000000000000000000000000000000815250612fbe565b975061166e8989611212846030613cb7565b97505b6116b189896040518060400160405280600381526020017f222f3e0000000000000000000000000000000000000000000000000000000000815250612fbe565b975050600190930192506114af915050565b506001016114a4565b5061170d83836040518060400160405280600681526020017f3c2f7376673e0000000000000000000000000000000000000000000000000000815250612fbe565b835250909392505050565b5f8083611726846028613cca565b6117309190613cb7565b9050600381901c5f611746600780851690613d5b565b90508087838151811061175b5761175b61377d565b60209101015160f81c901c60019081161493505050505b9392505050565b60608351801561187e576003600282010460021b60405192507f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f526106708515027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f526020830181810183886020010180515f82525b60038a0199508951603f8160121c16515f53603f81600c1c1651600153603f8160061c1651600253603f811651600353505f5184526004840193508284106117f45790526020016040527f3d3d00000000000000000000000000000000000000000000000000000000000060038406600204808303919091525f8615159091029182900352900382525b509392505050565b60608160ff165f036118cb57505060408051808201909152600581527f48756d616e000000000000000000000000000000000000000000000000000000602082015290565b8160ff1660010361190f57505060408051808201909152600381527f4361740000000000000000000000000000000000000000000000000000000000602082015290565b8160ff1660020361195357505060408051808201909152600581527f416c69656e000000000000000000000000000000000000000000000000000000602082015290565b8160ff1660030361199757505060408051808201909152600581527f4167656e74000000000000000000000000000000000000000000000000000000602082015290565b6040517f319abe9000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606082826040516020016119de929190613d6e565b604051602081830303815290604052905092915050565b60608160ff165f03611a3a57505060408051808201909152600481527f4d616c6500000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600103611a7e57505060408051808201909152600681527f46656d616c650000000000000000000000000000000000000000000000000000602082015290565b8160ff1660020361199757505060408051808201909152600a81527f4e6f6e2d42696e61727900000000000000000000000000000000000000000000602082015290565b60608160ff165f03611b0757505060408051808201909152600581527f596f756e67000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600103611b4b57505060408051808201909152600b81527f4d6964646c652d41676564000000000000000000000000000000000000000000602082015290565b8160ff1660020361199757505060408051808201909152600381527f4f6c640000000000000000000000000000000000000000000000000000000000602082015290565b60608160ff165f03611bd457505060408051808201909152600a81527f53686f7274204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600103611c1857505060408051808201909152600981527f4c6f6e6720486169720000000000000000000000000000000000000000000000602082015290565b8160ff16600203611c5c57505060408051808201909152600a81527f4375726c79204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600303611ca057505060408051808201909152600d81527f5374726169676874204861697200000000000000000000000000000000000000602082015290565b8160ff16600403611ce457505060408051808201909152600981527f5761767920486169720000000000000000000000000000000000000000000000602082015290565b8160ff16600503611d2857505060408051808201909152600a81527f5370696b79204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600603611d6c57505060408051808201909152600981527f57696c6420486169720000000000000000000000000000000000000000000000602082015290565b8160ff16600703611db057505060408051808201909152600a81527f4d65737379204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600803611df457505060408051808201909152600681527f4d6f6861776b0000000000000000000000000000000000000000000000000000602082015290565b8160ff16600903611e3857505060408051808201909152600a81527f4372617a79204861697200000000000000000000000000000000000000000000602082015290565b8160ff16600a03611e7c57505060408051808201909152600c81527f4272616964656420486169720000000000000000000000000000000000000000602082015290565b8160ff16600b03611ec057505060408051808201909152600481527f42616c6400000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600c03611f0457505060408051808201909152600881527f506f6e797461696c000000000000000000000000000000000000000000000000602082015290565b8160ff16600d03611f4857505060408051808201909152600881527f5069677461696c73000000000000000000000000000000000000000000000000602082015290565b8160ff16600e03611f8c57505060408051808201909152600481527f4166726f00000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600f03611fd057505060408051808201909152600881527f42757a7a20437574000000000000000000000000000000000000000000000000602082015290565b8160ff1660100361201457505060408051808201909152600b81527f4672756d70792048616972000000000000000000000000000000000000000000602082015290565b8160ff1660110361205857505060408051808201909152600c81527f537472696e677920486169720000000000000000000000000000000000000000602082015290565b8160ff1660120361209c57505060408051808201909152600a81527f5065616b205370696b6500000000000000000000000000000000000000000000602082015290565b8160ff166013036120e057505060408051808201909152600b81527f48616c6620536861766564000000000000000000000000000000000000000000602082015290565b8160ff1660140361199757505060408051808201909152600b81527f4b6e697474656420436170000000000000000000000000000000000000000000602082015290565b60608160ff165f0361216957505060408051808201909152600a81527f46756c6c20426561726400000000000000000000000000000000000000000000602082015290565b8160ff166001036121ad57505060408051808201909152600881527f4d75737461636865000000000000000000000000000000000000000000000000602082015290565b8160ff166002036121f157505060408051808201909152600681527f476f617465650000000000000000000000000000000000000000000000000000602082015290565b8160ff1660030361223557505060408051808201909152600a81527f4368696e20537472617000000000000000000000000000000000000000000000602082015290565b8160ff1660040361227957505060408051808201909152600b81527f4d7574746f6e63686f7073000000000000000000000000000000000000000000602082015290565b8160ff166005036122bd57505060408051808201909152600c81527f536861646f772042656172640000000000000000000000000000000000000000602082015290565b8160ff1660060361230157505060408051808201909152600f81527f4c75787572696f75732042656172640000000000000000000000000000000000602082015290565b8160ff1660070361234557505060408051808201909152600a81527f48616e646c656261727300000000000000000000000000000000000000000000602082015290565b8160ff1660080361238957505060408051808201909152600981527f4269672042656172640000000000000000000000000000000000000000000000602082015290565b8160ff166009036123cd57505060408051808201909152600c81527f4e6f726d616c2042656172640000000000000000000000000000000000000000602082015290565b8160ff16600a0361241157505060408051808201909152600c81527f436c65616e2053686176656e0000000000000000000000000000000000000000602082015290565b8160ff16600b0361245557505060408051808201909152600881527f467265636b6c6573000000000000000000000000000000000000000000000000602082015290565b8160ff16600c0361249957505060408051808201909152600481527f4d6f6c6500000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600d036124dd57505060408051808201909152600b81527f526f737920436865656b73000000000000000000000000000000000000000000602082015290565b8160ff16600e0361252157505060408051808201909152600781527f44696d706c657300000000000000000000000000000000000000000000000000602082015290565b8160ff16600f0361256557505060408051808201909152600f81527f4869676820436865656b626f6e65730000000000000000000000000000000000602082015290565b8160ff1660100361199757505060408051808201909152600581527f53706f7473000000000000000000000000000000000000000000000000000000602082015290565b60608160ff165f036125ee57505060408051808201909152600e81527f436c617373696320536861646573000000000000000000000000000000000000602082015290565b8160ff1660010361263257505060408051808201909152600a81527f4269672053686164657300000000000000000000000000000000000000000000602082015290565b8160ff1660020361267657505060408051808201909152600e81527f526567756c617220536861646573000000000000000000000000000000000000602082015290565b8160ff166003036126ba57505060408051808201909152600c81527f536d616c6c205368616465730000000000000000000000000000000000000000602082015290565b8160ff166004036126fe57505060408051808201909152601281527f486f726e65642052696d20476c61737365730000000000000000000000000000602082015290565b8160ff1660050361274257505060408051808201909152600c81527f4e65726420476c61737365730000000000000000000000000000000000000000602082015290565b8160ff1660060361278657505060408051808201909152600a81527f5652204865616473657400000000000000000000000000000000000000000000602082015290565b8160ff166007036127ca57505060408051808201909152600a81527f334420476c617373657300000000000000000000000000000000000000000000602082015290565b8160ff1660080361280e57505060408051808201909152600881527f457965204d61736b000000000000000000000000000000000000000000000000602082015290565b8160ff1660090361285257505060408051808201909152600981527f4579652050617463680000000000000000000000000000000000000000000000602082015290565b8160ff16600a0361289657505060408051808201909152600d81527f526f756e6420476c617373657300000000000000000000000000000000000000602082015290565b8160ff16600b036128da57505060408051808201909152600e81527f53717561726520476c6173736573000000000000000000000000000000000000602082015290565b8160ff16600c0361291e57505060408051808201909152600881527f41766961746f7273000000000000000000000000000000000000000000000000602082015290565b8160ff16600d0361199757505060408051808201909152600a81527f4e6f20476c617373657300000000000000000000000000000000000000000000602082015290565b60608160ff165f036129a757505060408051808201909152600781527f4e65757472616c00000000000000000000000000000000000000000000000000602082015290565b8160ff166001036129eb57505060408051808201909152600c81527f536c6967687420536d696c650000000000000000000000000000000000000000602082015290565b8160ff16600203612a2f57505060408051808201909152600781527f536572696f757300000000000000000000000000000000000000000000000000602082015290565b8160ff16600303612a7357505060408051808201909152600781527f436f6e74656e7400000000000000000000000000000000000000000000000000602082015290565b8160ff16600403612ab757505060408051808201909152600881527f506561636566756c000000000000000000000000000000000000000000000000602082015290565b8160ff16600503612afb57505060408051808201909152600981527f436f6e666964656e740000000000000000000000000000000000000000000000602082015290565b8160ff1660060361199757505060408051808201909152600881527f467269656e646c79000000000000000000000000000000000000000000000000602082015290565b60608160ff165f03612b8457505060408051808201909152600781527f546f702048617400000000000000000000000000000000000000000000000000602082015290565b8160ff16600103612bc857505060408051808201909152600681527f4665646f72610000000000000000000000000000000000000000000000000000602082015290565b8160ff16600203612c0c57505060408051808201909152600a81527f436f77626f792048617400000000000000000000000000000000000000000000602082015290565b8160ff16600303612c5057505060408051808201909152600681527f4265616e69650000000000000000000000000000000000000000000000000000602082015290565b8160ff16600403612c9457505060408051808201909152600381527f4361700000000000000000000000000000000000000000000000000000000000602082015290565b8160ff16600503612cd857505060408051808201909152600b81527f43617020466f7277617264000000000000000000000000000000000000000000602082015290565b8160ff16600603612d1c57505060408051808201909152600781527f42616e64616e6100000000000000000000000000000000000000000000000000602082015290565b8160ff16600703612d6057505060408051808201909152600881527f4865616462616e64000000000000000000000000000000000000000000000000602082015290565b8160ff16600803612da457505060408051808201909152600681527f446f2d5261670000000000000000000000000000000000000000000000000000602082015290565b8160ff16600903612de857505060408051808201909152600681527f486f6f6469650000000000000000000000000000000000000000000000000000602082015290565b8160ff16600a03612e2c57505060408051808201909152600781527f45617272696e6700000000000000000000000000000000000000000000000000602082015290565b8160ff16600b03612e7057505060408051808201909152600a81527f476f6c6420436861696e00000000000000000000000000000000000000000000602082015290565b8160ff16600c03612eb457505060408051808201909152600c81527f53696c76657220436861696e0000000000000000000000000000000000000000602082015290565b8160ff16600d03612ef857505060408051808201909152600781527f426f772054696500000000000000000000000000000000000000000000000000602082015290565b8160ff16600e0361199757505060408051808201909152600e81527f4e6f204163636573736f72696573000000000000000000000000000000000000602082015290565b606082612f4883610cf7565b6040516020016119de929190613dfe565b8051604051818001600282019081526f30313233343536373839616263646566600f52918301906022015b818414612faf57600184019350600f845116516001820153600f845160041c16518153600201612f84565b5f815260200160405250919050565b80515f90829060208587018101908301845b83811015612fe8578181015183820152602001612fd0565b5050508085612ff79190613cb7565b9695505050505050565b5f815f0361301c5761301584846030613087565b9050611772565b815f5b8115613045578061302f81613eb4565b915061303e9050600a83613d0e565b915061301f565b5f6130508287613cb7565b905080602088015b861561307a57600182039150600a870660300182820153600a87049650613058565b5090979650505050505050565b5f818360208601015361309b836001613cb7565b949350505050565b5f826020850101600f8360041c16600f84166027600983110260308301018353602760098211026030820101600184015350505082600261309b9190613cb7565b73ffffffffffffffffffffffffffffffffffffffff811681146108d4575f5ffd5b5f5f60408385031215613116575f5ffd5b8235613121816130e4565b946020939093013593505050565b5f5f5f5f5f5f60a08789031215613144575f5ffd5b863561314f816130e4565b9550602087013561315f816130e4565b94506040870135935060608701359250608087013567ffffffffffffffff811115613188575f5ffd5b8701601f81018913613198575f5ffd5b803567ffffffffffffffff8111156131ae575f5ffd5b8960208284010111156131bf575f5ffd5b60208201935080925050509295509295509295565b5f5f5f606084860312156131e6575f5ffd5b83356131f1816130e4565b92506020840135613201816130e4565b929592945050506040919091013590565b5f60208284031215613222575f5ffd5b5035919050565b5f5f5f5f6080858703121561323c575f5ffd5b8435613247816130e4565b93506020850135613257816130e4565b93969395505050506040820135916060013590565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156132b1575f5ffd5b8135611772816130e4565b5f602082840312156132cc575f5ffd5b81518015158114611772575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f60208284031215613318575f5ffd5b815167ffffffffffffffff81111561332e575f5ffd5b8201601f8101841361333e575f5ffd5b805167ffffffffffffffff811115613358576133586132db565b604051601f19603f601f19601f8501160116810181811067ffffffffffffffff82111715613388576133886132db565b60405281815282820160200186101561339f575f5ffd5b8160208401602083015e5f91810160200191909152949350505050565b5f602082840312156133cc575f5ffd5b81517fffffffffffffffff00000000000000000000000000000000000000000000000081168114611772575f5ffd5b5f81518060208401855e5f93019283525090919050565b7f7b226e616d65223a224e6f726d6965202300000000000000000000000000000081525f61344360118301846133fb565b7f222c2261747472696275746573223a5b0000000000000000000000000000000081526010019392505050565b5f6134d66134d061348184876133fb565b7f5d2c22696d616765223a22646174613a696d6167652f7376672b786d6c3b626181527f736536342c000000000000000000000000000000000000000000000000000000602082015260250190565b846133fb565b7f222c22616e696d6174696f6e5f75726c223a22000000000000000000000000008152601301949350505050565b5f61350f82846133fb565b7f227d00000000000000000000000000000000000000000000000000000000000081526002019392505050565b5f6135536134d061354d84886133fb565b866133fb565b95945050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081525f611772601d8301846133fb565b7f7b2274726169745f74797065223a2252657665616c6564222c2276616c75652281527f3a224e6f227d000000000000000000000000000000000000000000000000000060208201527f5d2c22696d616765223a22646174613a696d6167652f7376672b786d6c3b626160268201527f736536342c00000000000000000000000000000000000000000000000000000060468201525f61362f604b83016134d0565b7f222c22616e696d6174696f6e5f75726c223a22646174613a696d6167652f737681527f672b786d6c3b6261736536342c000000000000000000000000000000000000006020820152602d019392505050565b5f61368d82866133fb565b7f3c7265637420783d22000000000000000000000000000000000000000000000081526136bd60098201866133fb565b90507f2220793d2200000000000000000000000000000000000000000000000000000081526136ef60058201856133fb565b7f222077696474683d223122206865696768743d2231222066696c6c3d2223343881527f34393462222f3e0000000000000000000000000000000000000000000000000060208201526027019695505050505050565b5f61375082846133fb565b7f3c2f7376673e000000000000000000000000000000000000000000000000000081526006019392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f6137b582876133fb565b7f2c0000000000000000000000000000000000000000000000000000000000000081526137e560018201876133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261381760018201866133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261384960018201856133fb565b979650505050505050565b7f2c0000000000000000000000000000000000000000000000000000000000000081525f61388560018301886133fb565b7f2c0000000000000000000000000000000000000000000000000000000000000081526138b560018201886133fb565b90507f2c0000000000000000000000000000000000000000000000000000000000000081526138e760018201876133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261391960018201866133fb565b90507f2c00000000000000000000000000000000000000000000000000000000000000815261394b60018201856133fb565b98975050505050505050565b5f61309b6134d083866133fb565b7f3c68746d6c3e3c626f6479207374796c653d276d617267696e3a303b6f76657281527f666c6f773a68696464656e3b77696474683a31303076773b6865696768743a3160208201527f303076683b646973706c61793a666c65783b616c69676e2d6974656d733a636560408201527f6e7465723b6a7573746966792d636f6e74656e743a63656e7465723b6261636b60608201527f67726f756e643a23653365356534273e3c63616e7661732069643d276327207760808201527f696474683d273230303027206865696768743d273230303027207374796c653d60a08201527f27696d6167652d72656e646572696e673a706978656c617465643b776964746860c08201527f3a6d696e2831303076772c3130307668293b6865696768743a6d696e2831303060e08201527f76772c313030766829273e3c2f63616e7661733e3c7363726970743e766172206101008201527f683d2700000000000000000000000000000000000000000000000000000000006101208201525f6117726101238301846133fb565b5f613afa82846133fb565b7f273b76617220633d646f63756d656e742e676574456c656d656e74427949642881527f276327292e676574436f6e746578742827326427293b632e66696c6c5374796c60208201527f653d2723653365356534273b632e66696c6c5265637428302c302c323030302c60408201527f32303030293b632e66696c6c5374796c653d2723343834393462273b666f722860608201527f76617220793d303b793c34303b792b2b29666f722876617220783d303b783c3460808201527f303b782b2b297b76617220693d792a34302b782c623d7061727365496e74286860a08201527f2e7375627374722828693e3e33292a322c32292c3136293b69662828623e3e2860c08201527f372d28692637292929263129632e66696c6c5265637428782a35302c792a353060e08201527f2c35302c3530297d3c2f7363726970743e3c2f626f64793e3c2f68746d6c3e0061010082015261011f019392505050565b7f646174613a746578742f68746d6c3b6261736536342c0000000000000000000081525f61177260168301846133fb565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808201808211156105a7576105a7613c8a565b80820281158282048414176105a7576105a7613c8a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82613d1c57613d1c613ce1565b500490565b5f60ff831680613d3357613d33613ce1565b8060ff84160691505092915050565b60ff81811683821601908111156105a7576105a7613c8a565b818103818111156105a7576105a7613c8a565b7f7b2274726169745f74797065223a22000000000000000000000000000000000081525f613d9f600f8301856133fb565b7f222c2276616c7565223a220000000000000000000000000000000000000000008152613dcf600b8201856133fb565b7f227d000000000000000000000000000000000000000000000000000000000000815260020195945050505050565b7f7b22646973706c61795f74797065223a226e756d626572222c2274726169745f81527f74797065223a220000000000000000000000000000000000000000000000000060208201525f613e5560278301856133fb565b7f222c2276616c7565223a000000000000000000000000000000000000000000008152613e85600a8201856133fb565b7f7d00000000000000000000000000000000000000000000000000000000000000815260010195945050505050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613ee457613ee4613c8a565b506001019056fe3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737667222076696577426f783d22302030203430203430222077696474683d2238303022206865696768743d22383030222073686170652d72656e646572696e673d2263726973704564676573223e3c7374796c653e726563747b616e696d6174696f6e2d6475726174696f6e3a2e38733b616e696d6174696f6e2d74696d696e672d66756e6374696f6e3a737465702d656e643b616e696d6174696f6e2d697465726174696f6e2d636f756e743a696e66696e6974657da164736f6c6343000821000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001b976baf51cf51f0e369c070d47fbc47a706e602
-----Decoded View---------------
Arg [0] : _storage (address): 0x1B976bAf51cF51F0e369C070d47FBc47A706e602
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000001b976baf51cf51f0e369c070d47fbc47a706e602
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.