More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 78 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Unlock Batch | 102445660 | 566 days ago | IN | 0 ETH | 0.000123709752 | ||||
Unlock Batch | 91519785 | 608 days ago | IN | 0 ETH | 0.000129974417 | ||||
Unlock Batch | 90029583 | 612 days ago | IN | 0 ETH | 0.00012644093 | ||||
Unlock Batch | 90029224 | 612 days ago | IN | 0.05 ETH | 0.000128789279 | ||||
Unlock Batch | 89645483 | 613 days ago | IN | 0 ETH | 0.000115644768 | ||||
Unlock Batch | 89542289 | 613 days ago | IN | 0 ETH | 0.000134722706 | ||||
Unlock Batch | 89526282 | 613 days ago | IN | 0 ETH | 0.000108901739 | ||||
Unlock Batch | 89524961 | 613 days ago | IN | 0 ETH | 0.000108901739 | ||||
Unlock Batch | 89506750 | 613 days ago | IN | 0 ETH | 0.000097174755 | ||||
Unlock Batch | 89506417 | 613 days ago | IN | 0 ETH | 0.000097174755 | ||||
Unlock Batch | 89489642 | 613 days ago | IN | 0 ETH | 0.000109359121 | ||||
Unlock Batch | 81855390 | 638 days ago | IN | 0 ETH | 0.0000834466 | ||||
Unlock Batch | 80642728 | 643 days ago | IN | 0 ETH | 0.000170836382 | ||||
Unlock Batch | 80642278 | 643 days ago | IN | 0 ETH | 0.000130437064 | ||||
Unlock Batch | 80389935 | 643 days ago | IN | 0 ETH | 0.000199009519 | ||||
Unlock Batch | 80389903 | 643 days ago | IN | 0 ETH | 0.000199009519 | ||||
Unlock Batch | 80389856 | 643 days ago | IN | 0 ETH | 0.000199009519 | ||||
Unlock Batch | 79815196 | 646 days ago | IN | 0 ETH | 0.000095519422 | ||||
Unlock Batch | 79813461 | 646 days ago | IN | 0 ETH | 0.000104766008 | ||||
Unlock Batch | 79813387 | 646 days ago | IN | 0 ETH | 0.000111094069 | ||||
Unlock Batch | 79791548 | 646 days ago | IN | 0 ETH | 0.000198199577 | ||||
Unlock Batch | 79738689 | 646 days ago | IN | 0 ETH | 0.000108553623 | ||||
Unlock Batch | 79698435 | 646 days ago | IN | 0 ETH | 0.000111217765 | ||||
Unlock Batch | 79614124 | 647 days ago | IN | 0 ETH | 0.000275754622 | ||||
Unlock Batch | 79254573 | 648 days ago | IN | 0 ETH | 0.000110252112 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH | ||||
107403947 | 508 days ago | 0 ETH |
Loading...
Loading
Contract Name:
PixelStore
Compiler Version
v0.8.15+commit.e14f2714
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { OwnedRoles } from "@turbo-eth/solbase-sol/src/auth/OwnedRoles.sol"; import { PixelPoolyAssets } from "./PixelPoolyAssets.sol"; import { PixelPoolyStorage } from "./PixelPoolyStorage.sol"; import { IAssets } from "./interfaces/IAssets.sol"; import { IPixelCharacter } from "./interfaces/IPixelCharacter.sol"; contract PixelStore is OwnedRoles { address public erc721KStorage; address public pixelAssets; address public birbAddress; address public flapAddress; uint256 public totalReceived = 0; mapping(IAssets.Tier => uint256) internal tierPrices; /// ----------------------------------------------------------------------- /// Storage /// ----------------------------------------------------------------------- address internal erc721K; // TODO: possibly refactor this to use PixelPoolyStorage _elements so we dont have redundant storage of unlocked traits mapping(bytes32 => bool) internal _isEnabled; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor( IAssets.Tier[] memory tiers, uint256[] memory prices, address _pixelAssets, address _birbAddress_, address _flapAddress_, address _owner ) OwnedRoles() { _initializeOwner(_owner); pixelAssets = _pixelAssets; require(tiers.length == prices.length, "PixelStore: invalid input"); for (uint256 index = 0; index < tiers.length; index++) { tierPrices[tiers[index]] = prices[index]; } birbAddress = _birbAddress_; flapAddress = _flapAddress_; } /// ----------------------------------------------------------------------- /// External Functions /// ----------------------------------------------------------------------- // ======================== // READS // ======================== function isEnabled( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external view returns (bool unlocked) { return _isEnabled[_seid(tokenId, layer, frame)]; } function getTierPrice(IAssets.Tier tier) external view returns (uint256) { return tierPrices[tier]; } function getTierPrices(IAssets.Tier[] calldata tiers) external view returns (uint256[] memory prices) { prices = new uint256[](tiers.length); for (uint256 index = 0; index < tiers.length; index++) { prices[index] = tierPrices[tiers[index]]; } } function getFramePrice(uint8 layer, uint8 frame) external view returns (uint256) { return tierPrices[PixelPoolyAssets(pixelAssets).getTier(_eid(layer, frame))]; } function getFramePrices(uint8[] memory layers, uint8[] calldata frames) external view returns (uint256[] memory prices) { prices = new uint256[](frames.length); for (uint256 index = 0; index < frames.length; index++) { prices[index] = tierPrices[ PixelPoolyAssets(pixelAssets).getTier(_eid(layers[index], frames[index])) ]; } } // ======================== // WRITES // ======================== function unlock( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external payable { uint256 price_ = _handleUnlock(tokenId, layer, frame); require(msg.value >= price_, "PixelStore: Insufficient funds"); totalReceived += msg.value; if (msg.value > 0){ _handleStreamSplit(msg.value); } } function unlockBatch( uint256 tokenId, IPixelCharacter.Layer[] calldata layer, uint8[] calldata frame ) external payable { require( layer.length == frame.length, "PixelStore: layer and frame arrays must be the same length" ); uint256 totalCost; for (uint256 i = 0; i < layer.length; i++) { uint256 price_ = _handleUnlock(tokenId, layer[i], frame[i]); totalCost += price_; } require(msg.value >= totalCost, "PixelStore: Insufficient funds"); totalReceived += msg.value; if (msg.value > 0){ _handleStreamSplit(msg.value); } } function setERC721KStorage(address _erc721KStorage) external onlyOwner { erc721KStorage = _erc721KStorage; } function release(address to, uint256 value) external onlyOwner { (bool _success, ) = payable(to).call{ value: value }(""); require(_success, "PixelStore:eth-release-failed"); } /// ----------------------------------------------------------------------- /// Internal Functions /// ----------------------------------------------------------------------- function _handleUnlock(uint256 _tokenId, IPixelCharacter.Layer _layer, uint8 _frame) internal returns (uint256 price) { bytes32 seid = _seid(_tokenId, _layer, _frame); require(!_isEnabled[seid], "PixelStore:pixel-unlocked"); bytes32 eid = _eid(_layer, _frame); uint256 expiryTimestamp = PixelPoolyAssets(pixelAssets).getExpiryTime(eid); if (expiryTimestamp > 0){ require(block.timestamp < expiryTimestamp, "PixelStore:trait-expired"); } IAssets.Tier tier = PixelPoolyAssets(pixelAssets).getTier(eid); PixelPoolyStorage(erc721KStorage).setLayerFrameByController(_tokenId, _layer, _frame); _isEnabled[seid] = true; return tierPrices[tier]; } function _handleStreamSplit(uint256 _amount) internal { uint256 birbPercent_ = 80; uint256 totalPercent_ = 100; uint256 birbShare_ = (_amount * birbPercent_) / totalPercent_; uint256 flapShare_ = _amount - birbShare_; (bool _success, ) = payable(birbAddress).call{ value: birbShare_ }(""); require(_success, "PixelStore:birb-eth-release-failed"); (bool _success2, ) = payable(flapAddress).call{ value: flapShare_ }(""); require(_success2, "PixelStore:eth-release-failed"); } function _eid(IPixelCharacter.Layer layer, uint8 frame) internal pure returns (bytes32) { return keccak256(abi.encodePacked(uint8(layer), frame)); } function _eid(uint8 layer, uint8 frame) internal pure returns (bytes32) { return keccak256(abi.encodePacked(layer, frame)); } function _seid( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(tokenId, layer, frame)); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.15; import { Base64 } from "@turbo-eth/solbase-sol/src/utils/Base64.sol"; import { OwnedRoles } from "@turbo-eth/solbase-sol/src/auth/OwnedRoles.sol"; import { IERC721KImage } from "./interfaces/IERC721KImage.sol"; import { IERC721KTraits } from "./interfaces/IERC721KTraits.sol"; /** * @title ERC721Storage * @author Kames Geraghty */ abstract contract ERC721Storage is OwnedRoles { address internal svgRenderInstance; address internal traitsFetchInstance; ContractURI internal _contractURI; struct ContractURI { string name; string description; string image; string externalLink; string sellerFeeBasisPoints; string feeRecipient; } event SvgRenderUpdated(address svgRender); event TraitsFetchUpdated(address traitsFetch); event ContractURIUpdated(ContractURI contractURI); constructor( address svgRender_Instance, address traitsFetchInstance_, ContractURI memory _contractURI_ ) OwnedRoles() { svgRenderInstance = svgRender_Instance; traitsFetchInstance = traitsFetchInstance_; _contractURI = _contractURI_; _initializeOwner(msg.sender); } /* ===================================================================================== */ /* Virtual Functions */ /* ===================================================================================== */ function _parseName(uint256 _tokenId) internal view virtual returns (string memory); function _parseDescription(uint256 _tokenId) internal view virtual returns (string memory); /* ===================================================================================== */ /* External Functions */ /* ===================================================================================== */ function getERC721KRender() external view returns (address) { return svgRenderInstance; } function getERC72KTraits() external view returns (address) { return traitsFetchInstance; } function getContractDescription() external view returns (ContractURI memory) { return _contractURI; } function render(bytes memory input) external view returns (string memory) { return IERC721KImage(svgRenderInstance).render(input); } function constructTokenURI( uint256 tokenId, bytes memory input0, bytes memory input1 ) external view virtual returns (string memory uri) { string memory image_ = IERC721KImage(svgRenderInstance).render(input0); string memory traits_ = IERC721KTraits(traitsFetchInstance).fetch(input1); return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( string.concat( '{"name":', '"', _parseName(tokenId), '",', '"description":', '"', _parseDescription(tokenId), '",', '"image":', '"', image_, '",', '"attributes": [', traits_, "]", "}" ) ) ) ) ); } function constructContractURI() external view virtual returns (string memory uri) { return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( string.concat( '{"name":', '"', _contractURI.name, '",', '"description":', '"', _contractURI.description, '",', '"image":', '"', _contractURI.image, '",', '"externalLink":', '"', _contractURI.externalLink, '",', '"sellerFeeBasisPoints":', '"', _contractURI.sellerFeeBasisPoints, '",', '"feeRecipient":', '"', _contractURI.feeRecipient, '"', "}" ) ) ) ) ); } function setSvgRender(address svgRender) external onlyOwner { svgRenderInstance = svgRender; emit SvgRenderUpdated(svgRender); } function setTraitsFetch(address traitsFetch) external onlyOwner { traitsFetchInstance = traitsFetch; emit TraitsFetchUpdated(traitsFetch); } function setContractURI(ContractURI memory contractURI) external onlyOwner { _contractURI = contractURI; emit ContractURIUpdated(contractURI); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; interface IERC721KImage { function render(bytes memory input) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; interface IERC721KTraits { enum DisplayType { Base, Generic, BoostNumber, BoostPercent, Number, Date } function fetch(bytes memory input) external view returns (string memory); }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore( 0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. ) ) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div( and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2 ) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add( add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)) ) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add( add( add(_bytes, lengthmod), mul(0x20, iszero(lengthmod)) ), _start ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div( mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000 ) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div( and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2 ) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for { } eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import "@openzeppelin/contracts/utils/Strings.sol"; import "./svgUtils.sol"; /** * @title svg * @author Kames Geraghty * @notice SVG construction library using web-like API. * @dev Original code from w1nt3r-eth/hot-chain-svg (https://github.com/w1nt3r-eth/hot-chain-svg) */ library svg { using Strings for uint256; using Strings for uint8; function g(string memory _props, string memory _children) internal pure returns (string memory) { return el("g", _props, _children); } function path(string memory _props, string memory _children) internal pure returns (string memory) { return el("path", _props, _children); } function text(string memory _props, string memory _children) internal pure returns (string memory) { return el("text", _props, _children); } function line(string memory _props, string memory _children) internal pure returns (string memory) { return el("line", _props, _children); } function circle(string memory _props, string memory _children) internal pure returns (string memory) { return el("circle", _props, _children); } function circle(string memory _props) internal pure returns (string memory) { return el("circle", _props); } function rect(string memory _props, string memory _children) internal pure returns (string memory) { return el("rect", _props, _children); } function rect(string memory _props) internal pure returns (string memory) { return el("rect", _props); } function stop(string memory _props) internal pure returns (string memory) { return el("stop", _props); } function filter(string memory _props, string memory _children) internal pure returns (string memory) { return el("filter", _props, _children); } function defs(string memory _children) internal pure returns (string memory) { return el("defs", "", _children); } function cdata(string memory _content) internal pure returns (string memory) { return string.concat("<![CDATA[", _content, "]]>"); } /* GRADIENTS */ function radialGradient(string memory _props, string memory _children) internal pure returns (string memory) { return el("radialGradient", _props, _children); } function linearGradient(string memory _props, string memory _children) internal pure returns (string memory) { return el("linearGradient", _props, _children); } function gradientStop( uint256 offset, string memory stopColor, string memory _props ) internal pure returns (string memory) { return el( "stop", string.concat( prop("stop-color", stopColor), " ", prop("offset", string.concat(svgUtils.uint2str(offset), "%")), " ", _props ) ); } function animateTransform(string memory _props) internal pure returns (string memory) { return el("animateTransform", _props); } function image(string memory _href, string memory _props) internal pure returns (string memory) { return el("image", string.concat(prop("href", _href), " ", _props)); } /* COMMON */ // A generic element, can be used to construct any SVG (or HTML) element function el( string memory _tag, string memory _props, string memory _children ) internal pure returns (string memory) { return string.concat("<", _tag, " ", _props, ">", _children, "</", _tag, ">"); } // A generic element, can be used to construct any SVG (or HTML) element without children function el(string memory _tag, string memory _props) internal pure returns (string memory) { return string.concat("<", _tag, " ", _props, "/>"); } function start(string memory _tag, string memory _props) internal pure returns (string memory) { return string.concat("<", _tag, " ", _props, ">"); } function start(string memory _tag) internal pure returns (string memory) { return string.concat("<", _tag, ">"); } function end(string memory _tag) internal pure returns (string memory) { return string.concat("</", _tag, ">"); } // an SVG attribute function prop(string memory _key, string memory _val) internal pure returns (string memory) { return string.concat(_key, "=", '"', _val, '" '); } function stringifyIntSet( bytes memory _data, uint256 _offset, uint256 _len ) public pure returns (bytes memory) { bytes memory res; require(_data.length >= _offset + _len, "Out of range"); for (uint256 i = _offset; i < _offset + _len; i++) { res = abi.encodePacked(res, byte2uint8(_data, i).toString(), " "); } return res; } function byte2uint8(bytes memory _data, uint256 _offset) public pure returns (uint8) { require(_data.length > _offset, "Out of range"); return uint8(_data[_offset]); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.15; // import "@openzeppelin/contracts/utils/LibString.sol"; import { LibString } from "@turbo-eth/solbase-sol/src/utils/LibString.sol"; import { BytesLib } from "../libraries/BytesLib.sol"; /** * @title SVG Utilities * @author Kames Geraghty * @notice The SVG Utilities Library provides functions for constructing SVG; format CSS and numbers. * @dev Original code from w1nt3r-eth/hot-chain-svg (https://github.com/w1nt3r-eth/hot-chain-svg) */ library svgUtils { using LibString for uint256; using LibString for uint8; /// @notice Empty SVG element string internal constant NULL = ""; /** * @notice Formats a CSS variable line. Includes a semicolon for formatting. * @param _key User for which to calculate prize amount. * @param _val User for which to calculate prize amount. * @return string Generated CSS variable. */ function setCssVar(string memory _key, string memory _val) internal pure returns (string memory) { return string.concat("--", _key, ":", _val, ";"); } /** * @notice Formats getting a css variable * @param _key User for which to calculate prize amount. * @return string Generated CSS variable. */ function getCssVar(string memory _key) internal pure returns (string memory) { return string.concat("var(--", _key, ")"); } // formats getting a def URL function getDefURL(string memory _id) internal pure returns (string memory) { return string.concat("url(#", _id, ")"); } // checks if two LibString are equal function LibStringEqual(string memory _a, string memory _b) internal pure returns (bool) { return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b)); } // returns the length of a string in characters function utfStringLength(string memory _str) internal pure returns (uint256 length) { uint256 i = 0; bytes memory string_rep = bytes(_str); while (i < string_rep.length) { if (string_rep[i] >> 7 == 0) i += 1; else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) i += 2; else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) i += 3; else if (string_rep[i] >> 3 == bytes1(uint8(0x1E))) i += 4; //For safety else i += 1; length++; } } function round2Txt( uint256 _value, uint8 _decimals, uint8 _prec ) internal pure returns (bytes memory) { return abi.encodePacked( (_value / 10**_decimals).toString(), ".", (_value / 10**(_decimals - _prec) - (_value / 10**(_decimals)) * 10**_prec).toString() ); } // converts an unsigned integer to a string function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) { if (_i == 0) { return "0"; } uint256 j = _i; uint256 len; while (j != 0) { len++; j /= 10; } bytes memory bstr = new bytes(len); uint256 k = len; while (_i != 0) { k = k - 1; uint8 temp = (48 + uint8(_i - (_i / 10) * 10)); bytes1 b1 = bytes1(temp); bstr[k] = b1; _i /= 10; } return string(bstr); } function splitAddress(address account) internal pure returns (string memory) { string memory addy = LibString.toHexString(uint256(uint160(account)), 20); bytes memory start = BytesLib.slice(abi.encodePacked(addy), 0, 6); bytes memory end = BytesLib.slice(abi.encodePacked(addy), 37, 4); return string.concat(string(abi.encodePacked(start)), "...", string(abi.encodePacked(end))); } function toString(address account) internal pure returns (string memory) { return toString(abi.encodePacked(account)); } function toString(uint256 value) internal pure returns (string memory) { return toString(abi.encodePacked(value)); } function toString(bytes32 value) internal pure returns (string memory) { return toString(abi.encodePacked(value)); } function toString(bytes memory data) internal pure returns (string memory) { bytes memory alphabet = "0123456789abcdef"; bytes memory str = new bytes(2 + data.length * 2); str[0] = "0"; str[1] = "x"; for (uint256 i = 0; i < data.length; i++) { str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))]; str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))]; } return string(str); } function getColor(bytes memory _colorHex) internal view returns (bytes memory) { require(_colorHex.length == 3, "Unknown color"); return abi.encodePacked(_colorHex, hex"64"); } function getColor(bytes memory _colorHex, uint8 _alpha) internal view returns (bytes memory) { require(_colorHex.length == 3, "Unknown color"); return abi.encodePacked(_colorHex, _alpha); } function getRgba(bytes memory _colorHex) internal view returns (string memory) { return string(toRgba(getColor(_colorHex), 0)); } function toRgba(bytes memory _rgba, uint256 offset) internal pure returns (bytes memory) { return abi.encodePacked( "rgba(", byte2uint8(_rgba, offset).toString(), ",", byte2uint8(_rgba, offset + 1).toString(), ",", byte2uint8(_rgba, offset + 2).toString(), ",", byte2uint8(_rgba, offset + 3).toString(), "%)" ); } function byte2uint8(bytes memory _data, uint256 _offset) internal pure returns (uint8) { require(_data.length > _offset, "Out of range"); return uint8(_data[_offset]); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner and multiroles authorization mixin. /// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/auth/OwnedRoles.sol) /// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/auth/OwnableRoles.sol) /// @dev While the ownable portion follows EIP-173 (https://eips.ethereum.org/EIPS/eip-173) /// for compatibility, the nomenclature for the 2-step ownership handover and roles /// may be unique to this codebase. abstract contract OwnedRoles { /// ----------------------------------------------------------------------- /// Events /// ----------------------------------------------------------------------- /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev The `user`'s roles is updated to `roles`. /// Each bit of `roles` represents whether the role is set. event RolesUpdated(address indexed user, uint256 indexed roles); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE = 0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26; /// ----------------------------------------------------------------------- /// Custom Errors /// ----------------------------------------------------------------------- /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`. uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900; /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`. uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae; /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`. uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818; /// ----------------------------------------------------------------------- /// Storage /// ----------------------------------------------------------------------- /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`. /// It is intentionally choosen 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. /// /// The role slot of `user` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT)) /// let roleSlot := keccak256(0x00, 0x20) /// ``` /// This automatically ignores the upper bits of the `user` in case /// they are not clean, as well as keep the `keccak256` under 32-bytes. uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /// ----------------------------------------------------------------------- /// Internal Functions /// ----------------------------------------------------------------------- /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(not(_OWNER_SLOT_NOT), newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { assembly { let ownerSlot := not(_OWNER_SLOT_NOT) // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } /// @dev Grants the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn on. function _grantRoles(address user, uint256 roles) internal virtual { assembly { // Compute the role slot. mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT)) let roleSlot := keccak256(0x00, 0x20) // Load the current value and `or` it with `roles`. let newRoles := or(sload(roleSlot), roles) // Store the new value. sstore(roleSlot, newRoles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles) } } /// @dev Removes the roles directly without authorization guard. /// Each bit of `roles` represents the role to turn off. function _removeRoles(address user, uint256 roles) internal virtual { assembly { // Compute the role slot. mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT)) let roleSlot := keccak256(0x00, 0x20) // Load the current value. let currentRoles := sload(roleSlot) // Use `and` to compute the intersection of `currentRoles` and `roles`, // `xor` it with `currentRoles` to flip the bits in the intersection. let newRoles := xor(currentRoles, and(currentRoles, roles)) // Then, store the new value. sstore(roleSlot, newRoles) // Emit the {RolesUpdated} event. log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, shl(96, user)), newRoles) } } /// ----------------------------------------------------------------------- /// Public Update Functions /// ----------------------------------------------------------------------- /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Reverts if the `newOwner` is the zero address. if iszero(newOwner) { mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR) revert(0x1c, 0x04) } // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), newOwner) // Store the new value. sstore(not(_OWNER_SLOT_NOT), newOwner) } } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { assembly { // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), 0) // Store the new value. sstore(not(_OWNER_SLOT_NOT), 0) } } /// @dev Request a two-step ownership handover to the caller. /// The request will be automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + ownershipHandoverValidFor(); assembly { // Compute and set the handover slot to 1. mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED)) sstore(keccak256(0x00, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { assembly { // Compute and set the handover slot to 0. mstore(0x00, or(shl(96, caller()), _HANDOVER_SLOT_SEED)) sstore(keccak256(0x00, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { assembly { // Clean the upper 96 bits. pendingOwner := shr(96, shl(96, pendingOwner)) // Compute and set the handover slot to 0. mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED)) let handoverSlot := keccak256(0x00, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR) revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, caller(), pendingOwner) // Store the new value. sstore(not(_OWNER_SLOT_NOT), pendingOwner) } } /// @dev Allows the owner to grant `user` `roles`. /// If the `user` already has a role, then it will be an no-op for the role. function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { _grantRoles(user, roles); } /// @dev Allows the owner to remove `user` `roles`. /// If the `user` does not have a role, then it will be an no-op for the role. function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { _removeRoles(user, roles); } /// @dev Allow the caller to remove their own roles. /// If the caller does not have a role, then it will be an no-op for the role. function renounceRoles(uint256 roles) public payable virtual { _removeRoles(msg.sender, roles); } /// ----------------------------------------------------------------------- /// Public Read Functions /// ----------------------------------------------------------------------- /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { assembly { result := sload(not(_OWNER_SLOT_NOT)) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { assembly { // Compute the handover slot. mstore(0x00, or(shl(96, pendingOwner), _HANDOVER_SLOT_SEED)) // Load the handover slot. result := sload(keccak256(0x00, 0x20)) } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. function ownershipHandoverValidFor() public view virtual returns (uint64) { return 48 * 3600; } /// @dev Returns whether `user` has any of `roles`. function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) { assembly { // Compute the role slot. mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT)) // Load the stored value, and set the result to whether the // `and` intersection of the value and `roles` is not zero. result := iszero(iszero(and(sload(keccak256(0x00, 0x20)), roles))) } } /// @dev Returns whether `user` has all of `roles`. function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) { assembly { // Compute the role slot. mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT)) // Whether the stored value is contains all the set bits in `roles`. result := eq(and(sload(keccak256(0x00, 0x20)), roles), roles) } } /// @dev Returns the roles of `user`. function rolesOf(address user) public view virtual returns (uint256 roles) { assembly { // Compute the role slot. mstore(0x00, or(shl(96, user), _OWNER_SLOT_NOT)) // Load the stored value. roles := sload(keccak256(0x00, 0x20)) } } /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) { assembly { // Skip the length slot. let o := add(ordinals, 0x20) // `shl` 5 is equivalent to multiplying by 0x20. let end := add(o, shl(5, mload(ordinals))) // prettier-ignore for {} iszero(eq(o, end)) { o := add(o, 0x20) } { roles := or(roles, shl(and(mload(o), 0xff), 1)) } } } /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. /// This is meant for frontends like Etherscan, and is therefore not fully optimized. /// Not recommended to be called on-chain. function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) { assembly { // Grab the pointer to the free memory. let ptr := add(mload(0x40), 0x20) // The absence of lookup tables, De Bruijn, etc., here is intentional for // smaller bytecode, as this function is not meant to be called on-chain. // prettier-ignore for { let i := 0 } 1 { i := add(i, 1) } { mstore(ptr, i) // `shr` 5 is equivalent to multiplying by 0x20. // Push back into the ordinals array if the bit is set. ptr := add(ptr, shl(5, and(roles, 1))) roles := shr(1, roles) // prettier-ignore if iszero(roles) { break } } // Set `ordinals` to the start of the free memory. ordinals := mload(0x40) // Allocate the memory. mstore(0x40, ptr) // Store the length of `ordinals`. mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20)))) } } /// ----------------------------------------------------------------------- /// Modifiers /// ----------------------------------------------------------------------- /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } _; } /// @dev Marks a function as only callable by an account with `roles`. modifier onlyRoles(uint256 roles) virtual { assembly { // Compute the role slot. mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT)) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x00, 0x20)), roles)) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } _; } /// @dev Marks a function as only callable by the owner or by an account /// with `roles`. Checks for ownership first, then lazily checks for roles. modifier onlyOwnerOrRoles(uint256 roles) virtual { assembly { // If the caller is not the stored owner. if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { // Compute the role slot. mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT)) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x00, 0x20)), roles)) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } } _; } /// @dev Marks a function as only callable by an account with `roles` /// or the owner. Checks for roles first, then lazily checks for ownership. modifier onlyRolesOrOwner(uint256 roles) virtual { assembly { // Compute the role slot. mstore(0x00, or(shl(96, caller()), _OWNER_SLOT_NOT)) // Load the stored value, and if the `and` intersection // of the value and `roles` is zero, revert. if iszero(and(sload(keccak256(0x00, 0x20)), roles)) { // If the caller is not the stored owner. if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) revert(0x1c, 0x04) } } } _; } /// ----------------------------------------------------------------------- /// Role Constants /// ----------------------------------------------------------------------- uint256 internal constant _ROLE_0 = 1 << 0; uint256 internal constant _ROLE_1 = 1 << 1; uint256 internal constant _ROLE_2 = 1 << 2; uint256 internal constant _ROLE_3 = 1 << 3; uint256 internal constant _ROLE_4 = 1 << 4; uint256 internal constant _ROLE_5 = 1 << 5; uint256 internal constant _ROLE_6 = 1 << 6; uint256 internal constant _ROLE_7 = 1 << 7; uint256 internal constant _ROLE_8 = 1 << 8; uint256 internal constant _ROLE_9 = 1 << 9; uint256 internal constant _ROLE_10 = 1 << 10; uint256 internal constant _ROLE_11 = 1 << 11; uint256 internal constant _ROLE_12 = 1 << 12; uint256 internal constant _ROLE_13 = 1 << 13; uint256 internal constant _ROLE_14 = 1 << 14; uint256 internal constant _ROLE_15 = 1 << 15; uint256 internal constant _ROLE_16 = 1 << 16; uint256 internal constant _ROLE_17 = 1 << 17; uint256 internal constant _ROLE_18 = 1 << 18; uint256 internal constant _ROLE_19 = 1 << 19; uint256 internal constant _ROLE_20 = 1 << 20; uint256 internal constant _ROLE_21 = 1 << 21; uint256 internal constant _ROLE_22 = 1 << 22; uint256 internal constant _ROLE_23 = 1 << 23; uint256 internal constant _ROLE_24 = 1 << 24; uint256 internal constant _ROLE_25 = 1 << 25; uint256 internal constant _ROLE_26 = 1 << 26; uint256 internal constant _ROLE_27 = 1 << 27; uint256 internal constant _ROLE_28 = 1 << 28; uint256 internal constant _ROLE_29 = 1 << 29; uint256 internal constant _ROLE_30 = 1 << 30; uint256 internal constant _ROLE_31 = 1 << 31; uint256 internal constant _ROLE_32 = 1 << 32; uint256 internal constant _ROLE_33 = 1 << 33; uint256 internal constant _ROLE_34 = 1 << 34; uint256 internal constant _ROLE_35 = 1 << 35; uint256 internal constant _ROLE_36 = 1 << 36; uint256 internal constant _ROLE_37 = 1 << 37; uint256 internal constant _ROLE_38 = 1 << 38; uint256 internal constant _ROLE_39 = 1 << 39; uint256 internal constant _ROLE_40 = 1 << 40; uint256 internal constant _ROLE_41 = 1 << 41; uint256 internal constant _ROLE_42 = 1 << 42; uint256 internal constant _ROLE_43 = 1 << 43; uint256 internal constant _ROLE_44 = 1 << 44; uint256 internal constant _ROLE_45 = 1 << 45; uint256 internal constant _ROLE_46 = 1 << 46; uint256 internal constant _ROLE_47 = 1 << 47; uint256 internal constant _ROLE_48 = 1 << 48; uint256 internal constant _ROLE_49 = 1 << 49; uint256 internal constant _ROLE_50 = 1 << 50; uint256 internal constant _ROLE_51 = 1 << 51; uint256 internal constant _ROLE_52 = 1 << 52; uint256 internal constant _ROLE_53 = 1 << 53; uint256 internal constant _ROLE_54 = 1 << 54; uint256 internal constant _ROLE_55 = 1 << 55; uint256 internal constant _ROLE_56 = 1 << 56; uint256 internal constant _ROLE_57 = 1 << 57; uint256 internal constant _ROLE_58 = 1 << 58; uint256 internal constant _ROLE_59 = 1 << 59; uint256 internal constant _ROLE_60 = 1 << 60; uint256 internal constant _ROLE_61 = 1 << 61; uint256 internal constant _ROLE_62 = 1 << 62; uint256 internal constant _ROLE_63 = 1 << 63; uint256 internal constant _ROLE_64 = 1 << 64; uint256 internal constant _ROLE_65 = 1 << 65; uint256 internal constant _ROLE_66 = 1 << 66; uint256 internal constant _ROLE_67 = 1 << 67; uint256 internal constant _ROLE_68 = 1 << 68; uint256 internal constant _ROLE_69 = 1 << 69; uint256 internal constant _ROLE_70 = 1 << 70; uint256 internal constant _ROLE_71 = 1 << 71; uint256 internal constant _ROLE_72 = 1 << 72; uint256 internal constant _ROLE_73 = 1 << 73; uint256 internal constant _ROLE_74 = 1 << 74; uint256 internal constant _ROLE_75 = 1 << 75; uint256 internal constant _ROLE_76 = 1 << 76; uint256 internal constant _ROLE_77 = 1 << 77; uint256 internal constant _ROLE_78 = 1 << 78; uint256 internal constant _ROLE_79 = 1 << 79; uint256 internal constant _ROLE_80 = 1 << 80; uint256 internal constant _ROLE_81 = 1 << 81; uint256 internal constant _ROLE_82 = 1 << 82; uint256 internal constant _ROLE_83 = 1 << 83; uint256 internal constant _ROLE_84 = 1 << 84; uint256 internal constant _ROLE_85 = 1 << 85; uint256 internal constant _ROLE_86 = 1 << 86; uint256 internal constant _ROLE_87 = 1 << 87; uint256 internal constant _ROLE_88 = 1 << 88; uint256 internal constant _ROLE_89 = 1 << 89; uint256 internal constant _ROLE_90 = 1 << 90; uint256 internal constant _ROLE_91 = 1 << 91; uint256 internal constant _ROLE_92 = 1 << 92; uint256 internal constant _ROLE_93 = 1 << 93; uint256 internal constant _ROLE_94 = 1 << 94; uint256 internal constant _ROLE_95 = 1 << 95; uint256 internal constant _ROLE_96 = 1 << 96; uint256 internal constant _ROLE_97 = 1 << 97; uint256 internal constant _ROLE_98 = 1 << 98; uint256 internal constant _ROLE_99 = 1 << 99; uint256 internal constant _ROLE_100 = 1 << 100; uint256 internal constant _ROLE_101 = 1 << 101; uint256 internal constant _ROLE_102 = 1 << 102; uint256 internal constant _ROLE_103 = 1 << 103; uint256 internal constant _ROLE_104 = 1 << 104; uint256 internal constant _ROLE_105 = 1 << 105; uint256 internal constant _ROLE_106 = 1 << 106; uint256 internal constant _ROLE_107 = 1 << 107; uint256 internal constant _ROLE_108 = 1 << 108; uint256 internal constant _ROLE_109 = 1 << 109; uint256 internal constant _ROLE_110 = 1 << 110; uint256 internal constant _ROLE_111 = 1 << 111; uint256 internal constant _ROLE_112 = 1 << 112; uint256 internal constant _ROLE_113 = 1 << 113; uint256 internal constant _ROLE_114 = 1 << 114; uint256 internal constant _ROLE_115 = 1 << 115; uint256 internal constant _ROLE_116 = 1 << 116; uint256 internal constant _ROLE_117 = 1 << 117; uint256 internal constant _ROLE_118 = 1 << 118; uint256 internal constant _ROLE_119 = 1 << 119; uint256 internal constant _ROLE_120 = 1 << 120; uint256 internal constant _ROLE_121 = 1 << 121; uint256 internal constant _ROLE_122 = 1 << 122; uint256 internal constant _ROLE_123 = 1 << 123; uint256 internal constant _ROLE_124 = 1 << 124; uint256 internal constant _ROLE_125 = 1 << 125; uint256 internal constant _ROLE_126 = 1 << 126; uint256 internal constant _ROLE_127 = 1 << 127; uint256 internal constant _ROLE_128 = 1 << 128; uint256 internal constant _ROLE_129 = 1 << 129; uint256 internal constant _ROLE_130 = 1 << 130; uint256 internal constant _ROLE_131 = 1 << 131; uint256 internal constant _ROLE_132 = 1 << 132; uint256 internal constant _ROLE_133 = 1 << 133; uint256 internal constant _ROLE_134 = 1 << 134; uint256 internal constant _ROLE_135 = 1 << 135; uint256 internal constant _ROLE_136 = 1 << 136; uint256 internal constant _ROLE_137 = 1 << 137; uint256 internal constant _ROLE_138 = 1 << 138; uint256 internal constant _ROLE_139 = 1 << 139; uint256 internal constant _ROLE_140 = 1 << 140; uint256 internal constant _ROLE_141 = 1 << 141; uint256 internal constant _ROLE_142 = 1 << 142; uint256 internal constant _ROLE_143 = 1 << 143; uint256 internal constant _ROLE_144 = 1 << 144; uint256 internal constant _ROLE_145 = 1 << 145; uint256 internal constant _ROLE_146 = 1 << 146; uint256 internal constant _ROLE_147 = 1 << 147; uint256 internal constant _ROLE_148 = 1 << 148; uint256 internal constant _ROLE_149 = 1 << 149; uint256 internal constant _ROLE_150 = 1 << 150; uint256 internal constant _ROLE_151 = 1 << 151; uint256 internal constant _ROLE_152 = 1 << 152; uint256 internal constant _ROLE_153 = 1 << 153; uint256 internal constant _ROLE_154 = 1 << 154; uint256 internal constant _ROLE_155 = 1 << 155; uint256 internal constant _ROLE_156 = 1 << 156; uint256 internal constant _ROLE_157 = 1 << 157; uint256 internal constant _ROLE_158 = 1 << 158; uint256 internal constant _ROLE_159 = 1 << 159; uint256 internal constant _ROLE_160 = 1 << 160; uint256 internal constant _ROLE_161 = 1 << 161; uint256 internal constant _ROLE_162 = 1 << 162; uint256 internal constant _ROLE_163 = 1 << 163; uint256 internal constant _ROLE_164 = 1 << 164; uint256 internal constant _ROLE_165 = 1 << 165; uint256 internal constant _ROLE_166 = 1 << 166; uint256 internal constant _ROLE_167 = 1 << 167; uint256 internal constant _ROLE_168 = 1 << 168; uint256 internal constant _ROLE_169 = 1 << 169; uint256 internal constant _ROLE_170 = 1 << 170; uint256 internal constant _ROLE_171 = 1 << 171; uint256 internal constant _ROLE_172 = 1 << 172; uint256 internal constant _ROLE_173 = 1 << 173; uint256 internal constant _ROLE_174 = 1 << 174; uint256 internal constant _ROLE_175 = 1 << 175; uint256 internal constant _ROLE_176 = 1 << 176; uint256 internal constant _ROLE_177 = 1 << 177; uint256 internal constant _ROLE_178 = 1 << 178; uint256 internal constant _ROLE_179 = 1 << 179; uint256 internal constant _ROLE_180 = 1 << 180; uint256 internal constant _ROLE_181 = 1 << 181; uint256 internal constant _ROLE_182 = 1 << 182; uint256 internal constant _ROLE_183 = 1 << 183; uint256 internal constant _ROLE_184 = 1 << 184; uint256 internal constant _ROLE_185 = 1 << 185; uint256 internal constant _ROLE_186 = 1 << 186; uint256 internal constant _ROLE_187 = 1 << 187; uint256 internal constant _ROLE_188 = 1 << 188; uint256 internal constant _ROLE_189 = 1 << 189; uint256 internal constant _ROLE_190 = 1 << 190; uint256 internal constant _ROLE_191 = 1 << 191; uint256 internal constant _ROLE_192 = 1 << 192; uint256 internal constant _ROLE_193 = 1 << 193; uint256 internal constant _ROLE_194 = 1 << 194; uint256 internal constant _ROLE_195 = 1 << 195; uint256 internal constant _ROLE_196 = 1 << 196; uint256 internal constant _ROLE_197 = 1 << 197; uint256 internal constant _ROLE_198 = 1 << 198; uint256 internal constant _ROLE_199 = 1 << 199; uint256 internal constant _ROLE_200 = 1 << 200; uint256 internal constant _ROLE_201 = 1 << 201; uint256 internal constant _ROLE_202 = 1 << 202; uint256 internal constant _ROLE_203 = 1 << 203; uint256 internal constant _ROLE_204 = 1 << 204; uint256 internal constant _ROLE_205 = 1 << 205; uint256 internal constant _ROLE_206 = 1 << 206; uint256 internal constant _ROLE_207 = 1 << 207; uint256 internal constant _ROLE_208 = 1 << 208; uint256 internal constant _ROLE_209 = 1 << 209; uint256 internal constant _ROLE_210 = 1 << 210; uint256 internal constant _ROLE_211 = 1 << 211; uint256 internal constant _ROLE_212 = 1 << 212; uint256 internal constant _ROLE_213 = 1 << 213; uint256 internal constant _ROLE_214 = 1 << 214; uint256 internal constant _ROLE_215 = 1 << 215; uint256 internal constant _ROLE_216 = 1 << 216; uint256 internal constant _ROLE_217 = 1 << 217; uint256 internal constant _ROLE_218 = 1 << 218; uint256 internal constant _ROLE_219 = 1 << 219; uint256 internal constant _ROLE_220 = 1 << 220; uint256 internal constant _ROLE_221 = 1 << 221; uint256 internal constant _ROLE_222 = 1 << 222; uint256 internal constant _ROLE_223 = 1 << 223; uint256 internal constant _ROLE_224 = 1 << 224; uint256 internal constant _ROLE_225 = 1 << 225; uint256 internal constant _ROLE_226 = 1 << 226; uint256 internal constant _ROLE_227 = 1 << 227; uint256 internal constant _ROLE_228 = 1 << 228; uint256 internal constant _ROLE_229 = 1 << 229; uint256 internal constant _ROLE_230 = 1 << 230; uint256 internal constant _ROLE_231 = 1 << 231; uint256 internal constant _ROLE_232 = 1 << 232; uint256 internal constant _ROLE_233 = 1 << 233; uint256 internal constant _ROLE_234 = 1 << 234; uint256 internal constant _ROLE_235 = 1 << 235; uint256 internal constant _ROLE_236 = 1 << 236; uint256 internal constant _ROLE_237 = 1 << 237; uint256 internal constant _ROLE_238 = 1 << 238; uint256 internal constant _ROLE_239 = 1 << 239; uint256 internal constant _ROLE_240 = 1 << 240; uint256 internal constant _ROLE_241 = 1 << 241; uint256 internal constant _ROLE_242 = 1 << 242; uint256 internal constant _ROLE_243 = 1 << 243; uint256 internal constant _ROLE_244 = 1 << 244; uint256 internal constant _ROLE_245 = 1 << 245; uint256 internal constant _ROLE_246 = 1 << 246; uint256 internal constant _ROLE_247 = 1 << 247; uint256 internal constant _ROLE_248 = 1 << 248; uint256 internal constant _ROLE_249 = 1 << 249; uint256 internal constant _ROLE_250 = 1 << 250; uint256 internal constant _ROLE_251 = 1 << 251; uint256 internal constant _ROLE_252 = 1 << 252; uint256 internal constant _ROLE_253 = 1 << 253; uint256 internal constant _ROLE_254 = 1 << 254; uint256 internal constant _ROLE_255 = 1 << 255; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Three-step single owner authorization mixin. /// @author SolBase (https://github.com/Sol-DAO/solbase/blob/main/src/auth/OwnedThreeStep.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) abstract contract OwnedThreeStep { /// ----------------------------------------------------------------------- /// Events /// ----------------------------------------------------------------------- event OwnerUpdateInitiated(address indexed user, address indexed ownerCandidate); event OwnershipTransferred(address indexed user, address indexed newOwner); /// ----------------------------------------------------------------------- /// Custom Errors /// ----------------------------------------------------------------------- error Unauthorized(); /// ----------------------------------------------------------------------- /// Ownership Storage /// ----------------------------------------------------------------------- address public owner; address internal _ownerCandidate; bool internal _ownerCandidateConfirmed; modifier onlyOwner() virtual { if (msg.sender != owner) revert Unauthorized(); _; } /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- /// @notice Create contract and set `owner`. /// @param _owner The `owner` of contract. constructor(address _owner) { owner = _owner; emit OwnershipTransferred(address(0), _owner); } /// ----------------------------------------------------------------------- /// Ownership Logic /// ----------------------------------------------------------------------- /// @notice Initiate ownership transfer. /// @param newOwner The `_ownerCandidate` that will `confirmOwner()`. function transferOwnership(address newOwner) public payable virtual onlyOwner { _ownerCandidate = newOwner; emit OwnerUpdateInitiated(msg.sender, newOwner); } /// @notice Confirm ownership between `owner` and `_ownerCandidate`. function confirmOwner() public payable virtual { if (_ownerCandidateConfirmed) { if (msg.sender != owner) revert Unauthorized(); delete _ownerCandidateConfirmed; address newOwner = _ownerCandidate; owner = newOwner; emit OwnershipTransferred(msg.sender, newOwner); } else { if (msg.sender != _ownerCandidate) revert Unauthorized(); _ownerCandidateConfirmed = true; } } /// @notice Terminate ownership by `owner`. function renounceOwner() public payable virtual onlyOwner { delete owner; emit OwnershipTransferred(msg.sender, address(0)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library to encode and decode strings in Base64. /// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/utils/Base64.sol) /// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) library Base64 { /// @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) { 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 0x0230 will translate "-_" + "+/". mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") mstore(0x3f, sub("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0230))) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) let end := add(ptr, encodedLength) // Run over the input, 3 bytes at a time. // prettier-ignore for {} 1 {} { data := add(data, 3) // Advance 3 bytes. let input := mload(data) // Write 4 bytes. Optimized for fewer stack operations. mstore8( ptr , mload(and(shr(18, input), 0x3F))) mstore8(add(ptr, 1), mload(and(shr(12, input), 0x3F))) mstore8(add(ptr, 2), mload(and(shr( 6, input), 0x3F))) mstore8(add(ptr, 3), mload(and( input , 0x3F))) ptr := add(ptr, 4) // Advance 4 bytes. // prettier-ignore if iszero(lt(ptr, end)) { break } } let r := mod(dataLength, 3) switch noPadding case 0 { // Offset `ptr` and pad with '='. We can simply write over the end. mstore8(sub(ptr, iszero(iszero(r))), 0x3d) // Pad at `ptr - 1` if `r > 0`. mstore8(sub(ptr, shl(1, eq(r, 1))), 0x3d) // Pad at `ptr - 2` if `r == 1`. // Write the length of the string. mstore(result, encodedLength) } default { // Write the length of the string. mstore(result, sub(encodedLength, add(iszero(iszero(r)), eq(r, 1)))) } // Allocate the memory for the string. // Add 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(end, 31), not(31))) } } } /// @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) { assembly { let dataLength := mload(data) if dataLength { let end := add(data, dataLength) let decodedLength := mul(shr(2, dataLength), 3) switch and(dataLength, 3) case 0 { // If padded. decodedLength := sub( decodedLength, add(eq(and(mload(end), 0xFF), 0x3d), eq(and(mload(end), 0xFFFF), 0x3d3d)) ) } default { // If non-padded. decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) } result := mload(0x40) // Write the length of the string. mstore(result, decodedLength) // Skip the first slot, which stores the length. let ptr := add(result, 0x20) // 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) // prettier-ignore for {} 1 {} { // Read 4 bytes. data := add(data, 4) let input := mload(data) // Write 3 bytes. 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) // prettier-ignore if iszero(lt(data, end)) { break } } // Allocate the memory for the string. // Add 32 + 31 and mask with `not(31)` to round the // free memory pointer up the next multiple of 32. mstore(0x40, and(add(add(result, decodedLength), 63), not(31))) // Restore the zero slot. mstore(0x60, 0) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author SolDAO (https://github.com/Sol-DAO/solbase/blob/main/src/utils/LibString.sol) /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) library LibString { /// ----------------------------------------------------------------------- /// Custom Errors /// ----------------------------------------------------------------------- /// @dev The `length` of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /// ----------------------------------------------------------------------- /// Constants /// ----------------------------------------------------------------------- /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = uint256(int256(-1)); /// ----------------------------------------------------------------------- /// Decimal Operations /// ----------------------------------------------------------------------- /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { 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. Total: 5 * 0x20 = 0xa0. let m := add(mload(0x40), 0xa0) // Update the free memory pointer to allocate. mstore(0x40, m) // Assign the `str` to the end. str := sub(m, 0x20) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. // prettier-ignore for { let temp := value } 1 {} { str := sub(str, 1) // Write the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(str, add(48, mod(temp, 10))) // Keep dividing `temp` until zero. temp := div(temp, 10) // prettier-ignore if iszero(temp) { break } } let length := sub(end, str) // Move the pointer 32 bytes leftwards to make room for the length. str := sub(str, 0x20) // Store the length. mstore(str, length) } } /// ----------------------------------------------------------------------- /// Hexadecimal Operations /// ----------------------------------------------------------------------- /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { assembly { let start := mload(0x40) // We need 0x20 bytes for the trailing zeros padding, `length * 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. let m := add(start, and(add(shl(1, length), 0x62), not(0x1f))) // Allocate the memory. mstore(0x40, m) // Assign the `str` to the end. str := sub(m, 0x20) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) 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. // prettier-ignore for {} 1 {} { str := sub(str, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) length := sub(length, 1) // prettier-ignore if iszero(length) { break } } if temp { // Store the function selector of `HexLengthInsufficient()`. mstore(0x00, 0x2194895a) // Revert with (offset, size). revert(0x1c, 0x04) } // Compute the string's length. let strLength := add(sub(end, str), 2) // Move the pointer and write the "0x" prefix. str := sub(str, 0x20) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, strLength) } } /// @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 str) { assembly { let start := mload(0x40) // 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. let m := add(start, 0xa0) // Allocate the memory. mstore(0x40, m) // Assign the `str` to the end. str := sub(m, 0x20) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. // prettier-ignore for { let temp := value } 1 {} { str := sub(str, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) // prettier-ignore if iszero(temp) { break } } // Compute the string's length. let strLength := add(sub(end, str), 2) // Move the pointer and write the "0x" prefix. str := sub(str, 0x20) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, strLength) } } /// @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 str) { assembly { let start := mload(0x40) // We need 0x20 bytes for the length, 0x02 bytes for the prefix, // and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x02 + 0x28) is 0x60. str := add(start, 0x60) // Allocate the memory. mstore(0x40, str) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let length := 20 // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. // prettier-ignore for { let temp := value } 1 {} { str := sub(str, 2) mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) length := sub(length, 1) // prettier-ignore if iszero(length) { break } } // Move the pointer and write the "0x" prefix. str := sub(str, 32) mstore(str, 0x3078) // Move the pointer and write the length. str := sub(str, 2) mstore(str, 42) } } /// ----------------------------------------------------------------------- /// Other String Operations /// ----------------------------------------------------------------------- // For performance and bytecode compactness, all indices of the following operations // are byte (ASCII) offsets, not UTF character offsets. /// @dev Returns `subject` all occurances of `search` replaced with `replacement`. function replace( string memory subject, string memory search, string memory replacement ) internal pure returns (string memory result) { assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 32)) { h := keccak256(search, searchLength) } let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(search) // prettier-ignore for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. // prettier-ignore for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) // prettier-ignore if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. // prettier-ignore for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) // Zeroize the slot after the string. let last := add(add(result, 0x20), k) mstore(last, 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) // Store the length of the result. mstore(result, k) } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf( string memory subject, string memory search, uint256 from ) internal pure returns (uint256 result) { assembly { // prettier-ignore for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { // `result = min(from, subjectLength)`. result := xor(from, mul(xor(from, subjectLength), lt(subjectLength, from))) break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let subjectSearchEnd := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(add(search, 0x20)) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } if iszero(lt(searchLength, 32)) { // prettier-ignore for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } break } // prettier-ignore for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf( string memory subject, string memory search, uint256 from ) internal pure returns (uint256 result) { assembly { // prettier-ignore for {} 1 {} { let searchLength := mload(search) let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } if iszero(mload(search)) { result := from break } result := not(0) // Initialize to `NOT_FOUND`. let subjectSearchEnd := sub(add(subject, 0x20), 1) subject := add(add(subject, 0x20), from) // prettier-ignore if iszero(gt(subject, subjectSearchEnd)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. // prettier-ignore for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(subjectSearchEnd, 1)) break } subject := sub(subject, 1) // prettier-ignore if iszero(gt(subject, subjectSearchEnd)) { break } } break } } } /// @dev Returns the index of the first location of `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. result := and( iszero(gt(searchLength, mload(subject))), eq(keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength)) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) // prettier-ignore for {} 1 {} { // Copy the `subject` one word at a time. // prettier-ignore for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) // prettier-ignore if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) // prettier-ignore if iszero(times) { break } } // Zeroize the slot after the string. mstore(output, 0) // Store the length. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 63), not(31)))) } } } /// @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 result) { assembly { let subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) // Copy the `subject` one word at a time, backwards. // prettier-ignore for { let o := and(add(resultLength, 31), not(31)) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 63), not(31)))) } } } /// @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 result) { result = slice(subject, start, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 32)) { h := keccak256(search, searchLength) } let m := shl(3, sub(32, and(searchLength, 31))) let s := mload(search) // prettier-ignore for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) // prettier-ignore if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 0x20)) } } } /// @dev Returns a 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) { uint256[] memory indices = indicesOf(subject, delimiter); assembly { if mload(indices) { let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(sub(indicesEnd, 0x20), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 // prettier-ignore for {} 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. // prettier-ignore for { let o := and(add(elementLength, 31), not(31)) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 63), not(31)))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) // prettier-ignore 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 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 result) { assembly { result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. // prettier-ignore for { let o := and(add(mload(a), 32), not(31)) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } let bLength := mload(b) let output := add(result, mload(a)) // Copy `b` one word at a time, backwards. // prettier-ignore for { let o := and(add(bLength, 32), not(31)) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := sub(o, 0x20) // prettier-ignore if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 31), not(31))) } } /// @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) { 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 behaviour is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @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) { assembly { let aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or(shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength))), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, 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 behaviour is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { assembly { // Grab the free memory pointer. resultA := mload(0x40) 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 { assembly { // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(add(a, 0x20), mload(a)), 0) // Store the return offset. // Assumes that the string does not start from the scratch space. mstore(sub(a, 0x20), 0x20) // End the transaction, returning the string. return(sub(a, 0x20), add(mload(a), 0x40)) } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0; /// @title Base64 /// @author Brecht Devos - <[email protected]> /// @notice Provides functions for encoding/decoding base64 library Base64 { string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; bytes internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000" hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000" hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000" hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000"; function encode(bytes memory data) internal pure returns (string memory) { if (data.length == 0) return ''; // load the table into memory string memory table = TABLE_ENCODE; // multiply by 4/3 rounded up uint256 encodedLen = 4 * ((data.length + 2) / 3); // add some extra buffer at the end required for the writing string memory result = new string(encodedLen + 32); assembly { // set the actual output length mstore(result, encodedLen) // prepare the lookup table let tablePtr := add(table, 1) // input ptr let dataPtr := data let endPtr := add(dataPtr, mload(data)) // result ptr, jump over length let resultPtr := add(result, 32) // run over the input, 3 bytes at a time for {} lt(dataPtr, endPtr) {} { // read 3 bytes dataPtr := add(dataPtr, 3) let input := mload(dataPtr) // write 4 characters mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) resultPtr := add(resultPtr, 1) mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) resultPtr := add(resultPtr, 1) mstore8(resultPtr, mload(add(tablePtr, and(shr( 6, input), 0x3F)))) resultPtr := add(resultPtr, 1) mstore8(resultPtr, mload(add(tablePtr, and( input, 0x3F)))) resultPtr := add(resultPtr, 1) } // padding with '=' switch mod(mload(data), 3) case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) } case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) } } return result; } function decode(string memory _data) internal pure returns (bytes memory) { bytes memory data = bytes(_data); if (data.length == 0) return new bytes(0); require(data.length % 4 == 0, "invalid base64 decoder input"); // load the table into memory bytes memory table = TABLE_DECODE; // every 4 characters represent 3 bytes uint256 decodedLen = (data.length / 4) * 3; // add some extra buffer at the end required for the writing bytes memory result = new bytes(decodedLen + 32); assembly { // padding with '=' let lastBytes := mload(add(data, mload(data))) if eq(and(lastBytes, 0xFF), 0x3d) { decodedLen := sub(decodedLen, 1) if eq(and(lastBytes, 0xFFFF), 0x3d3d) { decodedLen := sub(decodedLen, 1) } } // set the actual output length mstore(result, decodedLen) // prepare the lookup table let tablePtr := add(table, 1) // input ptr let dataPtr := data let endPtr := add(dataPtr, mload(data)) // result ptr, jump over length let resultPtr := add(result, 32) // run over the input, 4 characters at a time for {} lt(dataPtr, endPtr) {} { // read 4 characters dataPtr := add(dataPtr, 4) let input := mload(dataPtr) // write 3 bytes let output := add( add( shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)), shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))), add( shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)), and(mload(add(tablePtr, and( input , 0xFF))), 0xFF) ) ) mstore(resultPtr, shl(232, output)) resultPtr := add(resultPtr, 3) } } return result; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { OwnedThreeStep } from "@turbo-eth/solbase-sol/src/auth/OwnedThreeStep.sol"; import { svg } from "@erc721k/periphery-sol/contracts/svg/svg.sol"; import { svgUtils } from "@erc721k/periphery-sol/contracts/svg/svgUtils.sol"; import { IAssets } from "./interfaces/IAssets.sol"; import { IPixelCharacter } from "./interfaces/IPixelCharacter.sol"; contract PixelPoolyAssets is IAssets, OwnedThreeStep { mapping(bytes32 => Element) private elements; // holds the total amount of frames added per layer uint16[] private totalFrames; // perhaps rename to something like totalFramesPerLayerArray // mapping of layer id to totalFrames index mapping(uint8 => IndexInfo) private layerIndex; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor(address owner) OwnedThreeStep(owner) {} /// ----------------------------------------------------------------------- /// External Functions /// ----------------------------------------------------------------------- // ======================== // READS // ======================== function getElement(bytes32 eid) external view returns (IAssets.Element memory) { return elements[eid]; } function getElement(IPixelCharacter.Layer layer, uint8 frame) external view returns (IAssets.Element memory) { return elements[_eid(layer, frame)]; } function getSVG(bytes32 eid) external view returns (string memory) { return elements[eid].svg; } function getSVG(IPixelCharacter.Layer layer, uint8 frame) external view returns (string memory) { return elements[_eid(layer, frame)].svg; } function getDetails(bytes32 eid) external view returns (string memory, string memory) { Element memory e = elements[eid]; return (e.traitName, e.traitDescription); } function getDetails(uint256 layer, uint8 frame) external view returns (string memory, string memory) { Element memory e = elements[_eid(layer, frame)]; return (e.traitName, e.traitDescription); } function getDetails(IPixelCharacter.Layer layer, uint8 frame) external view returns (string memory, string memory) { Element memory e = elements[_eid(layer, frame)]; return (e.traitName, e.traitDescription); } function getStats(bytes32 eid) external view returns (IAssets.Stat[] memory stats) { return elements[eid].stats; } function getStats(IPixelCharacter.Layer layer, uint8 frame) external view returns (IAssets.Stat[] memory stats) { return elements[_eid(layer, frame)].stats; } function getStats(uint256 layer, uint8 frame) external view returns (IAssets.Stat[] memory stats) { return elements[_eid(layer, frame)].stats; } function getTier(bytes32 eid) external view returns (Tier) { return elements[eid].tier; } function getExpiryTime(bytes32 eid) external view returns (uint256) { return elements[eid].expiry; } function getIndexInfoFromLayer(IPixelCharacter.Layer layer) external view returns (IndexInfo memory) { return layerIndex[uint8(layer)]; } function getTotalFrames() external view returns (uint16[] memory) { return totalFrames; } function getTotalLayers() external view returns (uint256) { return totalFrames.length; } function getTotalPossibleFrames() external view returns (uint256) { uint16 maxFrames_ = 0; for (uint8 i = 0; i < totalFrames.length; i++) { if (totalFrames[i] > maxFrames_){ maxFrames_ = totalFrames[i]; } } return maxFrames_ * totalFrames.length; } function getLayerFromIndex(uint256 _index) external view returns (uint8 layerId) { bool found_ = false; for (uint8 i = 0; i < 12; i++) { IndexInfo memory info_ = layerIndex[i]; if (info_.isSet && info_.index == _index){ layerId = i; found_ = true; } } require(found_, "PixelPoolyAssets:Layer-Index-not-found"); } function getTotalFramesPerLayer(IPixelCharacter.Layer layer) external view returns (uint16) { IndexInfo memory info_ = layerIndex[uint8(layer)]; require(info_.isSet, "PixelPoolyAssets:Layer-not-found"); return totalFrames[info_.index]; } // ======================== // WRITES // ======================== function setElement( IPixelCharacter.Layer layer, uint8 frame, Element memory element ) external onlyOwner { require(element.id == _eid(layer, frame), "PixelPoolyAssets: invalid element id"); if (elements[element.id].id != element.id){ // if the element is new, then increment the counter IndexInfo memory info_ = layerIndex[uint8(layer)]; if (info_.isSet){ // this means the index value of info_ is real // totalFrames[info_.index] = totalFrames[info_.index]++; totalFrames[info_.index]++; } else { // this means the layer is not being tracked in the array yet info_.index = uint8(totalFrames.length); info_.isSet = true; layerIndex[uint8(layer)] = info_; totalFrames.push(1); } elements[element.id].id = element.id; } elements[element.id].tier = element.tier; elements[element.id].layer = element.layer; elements[element.id].frame = element.frame; elements[element.id].expiry = element.expiry; elements[element.id].traitName = element.traitName; elements[element.id].traitDescription = element.traitDescription; elements[element.id].svg = element.svg; for (uint256 i = 0; i < element.stats.length; i++) { elements[element.id].stats.push(element.stats[i]); } } function setSVG( IPixelCharacter.Layer layer, uint8 frame, string memory svg_ ) external onlyOwner { elements[_eid(layer, frame)].svg = svg_; } function setTraitData( IPixelCharacter.Layer layer, uint8 frame, string memory traitName, string memory traitDescription ) external onlyOwner { bytes32 __eid = _eid(layer, frame); elements[__eid].traitName = traitName; elements[__eid].traitDescription = traitDescription; } /// ----------------------------------------------------------------------- /// Internal /// ----------------------------------------------------------------------- function _eid(IPixelCharacter.Layer layer, uint8 frame) internal pure returns (bytes32) { return keccak256(abi.encodePacked(uint8(layer), frame)); } function _eid(uint256 layer, uint8 frame) internal pure returns (bytes32) { return keccak256(abi.encodePacked(uint8(layer), frame)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { Base64 } from "base64-sol/base64.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { ERC721Storage } from "@erc721k/core-sol/contracts/ERC721Storage.sol"; import { IERC721KImage } from "@erc721k/core-sol/contracts/interfaces/IERC721KImage.sol"; import { IERC721KTraits } from "@erc721k/core-sol/contracts/interfaces/IERC721KTraits.sol"; import { IAssets } from "./interfaces/IAssets.sol"; import { IPixelCharacter } from "./interfaces/IPixelCharacter.sol"; import { PixelPoolyAssets } from "./PixelPoolyAssets.sol"; import { PixelStore } from "./PixelStore.sol"; /** * @title PixelPoolyStorage * @author Kames Geraghty */ contract PixelPoolyStorage is IPixelCharacter, ERC721Storage { /// @notice Manager role uint256 private immutable CONTROLLER_ROLE = 1e18; uint256 private immutable SIGNAL_ROLE = 2e18; struct SignalDetails { string signalDescription; uint8 frameId; uint256 expiry; } // @notice Toggle the "Birb Signal" for all active Pixel Pooly NFTs. SignalDetails private _signalDetails; address public erc721KInstance; address public erc721ImageSignalRenderInstance; address public erc721ImageSignalTraitsInstance; /// @notice PixelStore instance address public pixelStoreInstance; /// @notice PixelPoolyAssets instance address public pixelPoolyAssetsInstance; /// @notice The active item frames for a Pixel Pooly character. mapping(uint256 => Character) private _characters; /// @notice Track unlocked item frames using a bytes32 (hash of item type and frame) mapping(uint256 => mapping(bytes32 => bool)) private _elements; /// ----------------------------------------------------------------------- /// Constructor /// ----------------------------------------------------------------------- constructor( address pixelPoolyRender, address pixelPoolyTraits, ContractURI memory _contractURI ) ERC721Storage(pixelPoolyRender, pixelPoolyTraits, _contractURI) {} /// ----------------------------------------------------------------------- /// Overrides & EIP Functions /// ----------------------------------------------------------------------- function constructTokenURI( uint256 tokenId, bytes memory input0, bytes memory input1 ) external view override returns (string memory uri) { if (_isSignalActive()) { return _signalRender(tokenId, input1); } else { string memory image_ = IERC721KImage(svgRenderInstance).render(input0); string memory traits_ = IERC721KTraits(traitsFetchInstance).fetch(input1); return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( string.concat( '{"name":', '"', _parseName(tokenId), '",', '"description":', '"', _parseDescription(tokenId), '",', '"image":', '"', image_, '",', '"attributes": [', traits_, "]", "}" ) ) ) ) ); } } /// ----------------------------------------------------------------------- /// External Functions /// ----------------------------------------------------------------------- // ======================== // READS // ======================== function getCharacter(uint256 tokenId) external view returns (Character memory) { return _characters[tokenId]; } function getUnlockedTraits(uint256 tokenId) external view returns (IAssets.Element[] memory traits) { uint256 maxPossibleElements_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getTotalPossibleFrames(); IAssets.Element[] memory unlockedTraits_ = new IAssets.Element[](maxPossibleElements_); uint16 index_ = 0; uint16[] memory totalFramesArray_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getTotalFrames(); for (uint8 i = 0; i < totalFramesArray_.length; i++) { uint8 layer_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getLayerFromIndex(i); for (uint8 frame_ = 0; frame_ < totalFramesArray_[i]; frame_++) { bytes32 eid_ = _eid(layer_, frame_); if (_elements[tokenId][eid_]){ IAssets.Element memory element_ = PixelPoolyAssets(pixelPoolyAssetsInstance).getElement(eid_); unlockedTraits_[index_++] = element_; } } } IAssets.Element[] memory unlockedTraitsCleaned = new IAssets.Element[](index_); for (uint16 j = 0; j < index_; j++) { unlockedTraitsCleaned[j] = unlockedTraits_[j]; } return unlockedTraitsCleaned; } function getStore() external view returns (address) { return pixelStoreInstance; } /** * @notice Get the active item frames for a Pixel Pooly character * @param tokenId uint256 - The token ID to query * @return bytes - Packed bytes of the active item frames */ function getPreview(uint256 tokenId) external view returns (bytes memory) { return _imageBytes(tokenId); } /** * @notice Get the active item frames for a Pixel Pooly character * @param tokenId uint256 - The token ID to query * @return bytes - Packed bytes of the active item frames */ function getImageBytes(uint256 tokenId) external view returns (bytes memory) { return _imageBytes(tokenId); } function _imageBytes(uint256 tokenId) internal view returns (bytes memory) { Character memory character = _characters[tokenId]; return bytes( abi.encodePacked( character.bg, character.right, character.left, character.body, character.bodyAcc, character.head, character.headAcc, character.bodyLower, character.leftAcc, character.rightAcc, character.faceAcc, character.wildcard ) ); } /** * @notice Get the trait bytes for a Pixel Pooly character * @param tokenId uint256 - The token ID to query. * @return bytes - Packed instructions for traits PixelPoolyTraits */ function getTraitsBytes(uint256 tokenId) external view returns (bytes memory) { Character memory character = _characters[tokenId]; string[] memory traitTypes = new string[](12); traitTypes[0] = "Background"; traitTypes[1] = "Right"; traitTypes[2] = "Left"; traitTypes[3] = "Body"; traitTypes[4] = "Body Acc"; traitTypes[5] = "Head"; traitTypes[6] = "Head Acc"; traitTypes[7] = "Body Lower"; traitTypes[8] = "Left Acc"; traitTypes[9] = "Right Acc"; traitTypes[10] = "Face Acc"; traitTypes[11] = "Wildcard"; uint8[] memory values = new uint8[](12); values[0] = character.bg; values[1] = character.right; values[2] = character.left; values[3] = character.body; values[4] = character.bodyAcc; values[5] = character.head; values[6] = character.headAcc; values[7] = character.bodyLower; values[8] = character.leftAcc; values[9] = character.rightAcc; values[10] = character.faceAcc; values[11] = character.wildcard; string[] memory traitValues = new string[](12); uint8[] memory statsValues = new uint8[](7); for (uint256 index = 0; index < traitTypes.length; index++) { (string memory name, string memory description) = PixelPoolyAssets(pixelPoolyAssetsInstance) .getDetails(index, values[index]); traitValues[index] = string.concat(name, " - ", description); IAssets.Stat[] memory stats = PixelPoolyAssets(pixelPoolyAssetsInstance).getStats( index, values[index] ); statsValues = _calculateTotalStats(statsValues, stats); } IERC721KTraits.DisplayType[] memory displayTypes = new IERC721KTraits.DisplayType[](12); for (uint256 index = 0; index < displayTypes.length; index++) { displayTypes[index] = IERC721KTraits.DisplayType.Base; } return bytes(abi.encode(traitTypes, traitValues, displayTypes, statsValues)); } function formatTraitsBytesFromCharacter(Character memory character) external view returns (bytes memory) { return _getTraitsBytes(character); } function formatTraitsBytes(bytes memory data) external view returns (bytes memory) { ( uint8 background, uint8 right, uint8 left, uint8 body, uint8 bodyAcc, uint8 head, uint8 headAcc, uint8 bodyLower, uint8 leftAcc, uint8 rightAcc, uint8 faceAcc, uint8 wildcard ) = abi.decode( data, (uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8) ); Character memory character = Character( background, right, left, body, bodyAcc, head, headAcc, bodyLower, leftAcc, rightAcc, faceAcc, wildcard ); return _getTraitsBytes(character); } function _getTraitsBytes(Character memory character) internal view returns (bytes memory) { string[] memory traitTypes = new string[](12); traitTypes[0] = "Background"; traitTypes[1] = "Right"; traitTypes[2] = "Left"; traitTypes[3] = "Body"; traitTypes[4] = "Body Acc"; traitTypes[5] = "Head"; traitTypes[6] = "Head Acc"; traitTypes[7] = "Body Lower"; traitTypes[8] = "Left Acc"; traitTypes[9] = "Right Acc"; traitTypes[10] = "Face Acc"; traitTypes[11] = "Wildcard"; uint8[] memory values = new uint8[](12); values[0] = character.bg; values[1] = character.right; values[2] = character.left; values[3] = character.body; values[4] = character.bodyAcc; values[5] = character.head; values[6] = character.headAcc; values[7] = character.bodyLower; values[8] = character.leftAcc; values[9] = character.rightAcc; values[10] = character.faceAcc; values[11] = character.wildcard; string[] memory traitValues = new string[](12); uint8[] memory statsValues = new uint8[](7); for (uint256 index = 0; index < traitTypes.length; index++) { (string memory name, string memory description) = PixelPoolyAssets(pixelPoolyAssetsInstance) .getDetails(index, values[index]); traitValues[index] = string.concat(name, " - ", description); IAssets.Stat[] memory stats = PixelPoolyAssets(pixelPoolyAssetsInstance).getStats( index, values[index] ); statsValues = _calculateTotalStats(statsValues, stats); } IERC721KTraits.DisplayType[] memory displayTypes = new IERC721KTraits.DisplayType[](12); for (uint256 index = 0; index < displayTypes.length; index++) { displayTypes[index] = IERC721KTraits.DisplayType.Base; } return bytes(abi.encode(traitTypes, traitValues, displayTypes, statsValues)); } function _calculateTotalStats(uint8[] memory statsCounter, IAssets.Stat[] memory itemStats) internal pure returns (uint8[] memory) { for (uint256 index = 0; index < itemStats.length; index++) { statsCounter[uint256(itemStats[index].category)] += itemStats[index].value; } return statsCounter; } function isFrameActive( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external view returns (bool) { bytes32 eid = keccak256(abi.encodePacked(layer, frame)); return _elements[tokenId][eid]; } // ======================== // WRITES // ======================== function setSignal( SignalDetails calldata signal ) external { require(hasAllRoles(msg.sender, SIGNAL_ROLE), "PixelPooly:signal-unauthorized"); _signalDetails = signal; } function disableElement( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external { require(hasAllRoles(msg.sender, CONTROLLER_ROLE), "PixelPooly:lock-unauthorized"); _elements[tokenId][_eid(layer, frame)] = false; } function enableElement( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external { require(hasAllRoles(msg.sender, CONTROLLER_ROLE), "PixelPooly:unlock-unauthorized"); _elements[tokenId][_eid(layer, frame)] = true; } function setLayerFrameByOwner( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external { require( IERC721(erc721KInstance).ownerOf(tokenId) == msg.sender, "PixelPooly:owner-unauthorized" ); bytes32 eid = _eid(layer, frame); require(_elements[tokenId][eid], "PixelPooly:layer-frame-not-unlocked"); _setLayerFrame(tokenId, layer, frame); } function setLayerFrameByERC721K( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external { require(msg.sender == erc721KInstance, "PixelPooly:unauthorized"); bytes32 eid = _eid(layer, frame); require(_elements[tokenId][eid], "PixelPooly:layer-frame-not-unlocked"); _setLayerFrame(tokenId, layer, frame); } function setLayerFrameByController( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) external { require(hasAllRoles(msg.sender, CONTROLLER_ROLE), "PixelPooly:unlock-unauthorized"); bytes32 eid = _eid(layer, frame); _elements[tokenId][eid] = true; _setLayerFrame(tokenId, layer, frame); } function _setLayerFrame( uint256 tokenId, IPixelCharacter.Layer layer, uint8 frame ) internal { if (layer == Layer.Background) { _characters[tokenId].bg = frame; } else if (layer == Layer.Body) { _characters[tokenId].body = frame; } else if (layer == Layer.Head) { _characters[tokenId].head = frame; } else if (layer == Layer.BodyLower) { _characters[tokenId].bodyLower = frame; } else if (layer == Layer.Left) { _characters[tokenId].left = frame; } else if (layer == Layer.Right) { _characters[tokenId].right = frame; } else if (layer == Layer.HeadAcc) { _characters[tokenId].headAcc = frame; } else if (layer == Layer.FaceAcc) { _characters[tokenId].faceAcc = frame; } else if (layer == Layer.BodyAcc) { _characters[tokenId].bodyAcc = frame; } else if (layer == Layer.Wildcard) { _characters[tokenId].wildcard = frame; } else if (layer == Layer.LeftAcc) { _characters[tokenId].leftAcc = frame; } else if (layer == Layer.RightAcc) { _characters[tokenId].rightAcc = frame; } } /// ------------------------ /// Administrative /// ------------------------ function setERC721KInstance(address _erc721KInstance) external onlyOwner { erc721KInstance = _erc721KInstance; } function setPixelStoreInstance(address _pixelStoreInstance) external onlyOwner { pixelStoreInstance = _pixelStoreInstance; } function setPixelPoolyAssetsInstance(address _pixelPoolyAssetsInstance) external onlyOwner { pixelPoolyAssetsInstance = _pixelPoolyAssetsInstance; } /// ----------------------------------------------------------------------- /// Internal Functions /// ----------------------------------------------------------------------- function _isSignalActive() internal view returns (bool) { return _signalDetails.expiry > block.timestamp; } function _eid(IPixelCharacter.Layer layer, uint8 frame) internal pure returns (bytes32) { return keccak256(abi.encodePacked(uint8(layer), frame)); } function _eid(uint8 layer, uint8 frame) internal pure returns (bytes32) { return keccak256(abi.encodePacked(layer, frame)); } function _parseName(uint256 _tokenId) internal view override returns (string memory) { return string.concat("PixelPooly #", Strings.toString(_tokenId)); } function _parseDescription(uint256 _tokenId) internal view override returns (string memory) { return string.concat("Member ", Strings.toString(_tokenId), " of the DeFi Defenders Flock"); } function _signalRender(uint256 tokenId, bytes memory input1) internal view returns (string memory uri) { string memory image_ = IERC721KImage(svgRenderInstance).render(bytes( abi.encodePacked( _signalDetails.frameId ) )); string memory traits_ = IERC721KTraits(traitsFetchInstance).fetch(input1); return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( string.concat( '{"name":', '"', _parseName(tokenId), '",', '"description":', '"', _signalDetails.signalDescription, '",', '"image":', '"', image_, '",', '"attributes": [', traits_, "]", "}" ) ) ) ) ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.15; import { IPixelCharacter } from "./IPixelCharacter.sol"; interface IAssets { enum Tier { COMMON, RARE, EPIC, LEGENDARY } enum StatCategory { STRENGTH, DEFENSE, CONSTITUTION, INTELLIGENCE, CHARISMA, WISDOM, LUCK } struct Stat { StatCategory category; uint8 value; } struct Element { bytes32 id; Tier tier; IPixelCharacter.Layer layer; uint8 frame; uint256 expiry; string traitName; string traitDescription; Stat[] stats; string svg; } struct IndexInfo { uint8 index; bool isSet; } function getElement(bytes32 eid) external view returns (Element memory); function getElement(IPixelCharacter.Layer layer, uint8 frame) external view returns (Element memory); function getDetails(bytes32 eid) external view returns (string memory, string memory); function getSVG(bytes32 eid) external view returns (string memory); function getSVG(IPixelCharacter.Layer layer, uint8 frame) external view returns (string memory); function getStats(bytes32 eid) external view returns (Stat[] memory); function getStats(IPixelCharacter.Layer layer, uint8 frame) external view returns (Stat[] memory); }
//SPDX-License-Identifier: MIT pragma solidity 0.8.15; interface IPixelCharacter { enum Layer { Background, Right, Left, Body, BodyAcc, Head, HeadAcc, BodyLower, LeftAcc, RightAcc, FaceAcc, Wildcard, Signal } struct Character { uint8 bg; uint8 right; uint8 left; uint8 body; uint8 bodyAcc; uint8 head; uint8 headAcc; uint8 bodyLower; uint8 leftAcc; uint8 rightAcc; uint8 faceAcc; uint8 wildcard; } }
{ "evmVersion": "istanbul", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "viaIR": true, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"enum IAssets.Tier[]","name":"tiers","type":"uint8[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"},{"internalType":"address","name":"_pixelAssets","type":"address"},{"internalType":"address","name":"_birbAddress_","type":"address"},{"internalType":"address","name":"_flapAddress_","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"inputs":[],"name":"birbAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"erc721KStorage","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flapAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"layer","type":"uint8"},{"internalType":"uint8","name":"frame","type":"uint8"}],"name":"getFramePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"layers","type":"uint8[]"},{"internalType":"uint8[]","name":"frames","type":"uint8[]"}],"name":"getFramePrices","outputs":[{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IAssets.Tier","name":"tier","type":"uint8"}],"name":"getTierPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IAssets.Tier[]","name":"tiers","type":"uint8[]"}],"name":"getTierPrices","outputs":[{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum IPixelCharacter.Layer","name":"layer","type":"uint8"},{"internalType":"uint8","name":"frame","type":"uint8"}],"name":"isEnabled","outputs":[{"internalType":"bool","name":"unlocked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"ordinalsFromRoles","outputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pixelAssets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"release","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"name":"rolesFromOrdinals","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_erc721KStorage","type":"address"}],"name":"setERC721KStorage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum IPixelCharacter.Layer","name":"layer","type":"uint8"},{"internalType":"uint8","name":"frame","type":"uint8"}],"name":"unlock","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"enum IPixelCharacter.Layer[]","name":"layer","type":"uint8[]"},{"internalType":"uint8[]","name":"frame","type":"uint8[]"}],"name":"unlockBatch","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
608060405234620001015762001a47803803806200001d8162000137565b92833981019060c081830312620001015780516001600160401b039190828111620001015781019183601f8401121562000101578251926200006962000063856200016c565b62000137565b9081948083526020808094019160051b8301019187831162000101578301905b82821062000106575050508201519081116200010157620000f193620000b191830162000196565b620000bf60408301620001f3565b620000cd60608401620001f3565b91620000ea60a0620000e260808701620001f3565b9501620001f3565b9462000297565b6040516116799081620003ce8239f35b600080fd5b815160048110156200010157815290830190830162000089565b50634e487b7160e01b600052604160045260246000fd5b6040519190601f01601f191682016001600160401b038111838210176200015d57604052565b6200016762000120565b604052565b6020906001600160401b03811162000186575b60051b0190565b6200019062000120565b6200017f565b9080601f830112156200010157815190620001b562000063836200016c565b9182938184526020808095019260051b82010192831162000101578301905b828210620001e3575050505090565b81518152908301908301620001d4565b51906001600160a01b03821682036200010157565b6000198114620002185760010190565b634e487b7160e01b600052601160045260246000fd5b8051821015620002435760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b516004811015620002675790565b634e487b7160e01b600052602160045260246000fd5b600481101562000267576000526005602052604060002090565b90929594600060045560018060a01b0380911680638b78c6d8195560007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3600180546001600160a01b03191691909316178255805183510362000388576000825b62000338575b5050600280546001600160a01b039687166001600160a01b0319918216179091556003805494909616931692909217909355509050565b8151811015620003825780620003536200037b92866200022e565b51620003746200036e6200036884876200022e565b62000259565b6200027d565b5562000208565b82620002fb565b62000301565b60405162461bcd60e51b815260206004820152601960248201527f506978656c53746f72653a20696e76616c696420696e707574000000000000006044820152606490fdfe60806040526004361015610013575b600080fd5b60003560e01c80630357371d1461026b57806304a7ec781461026257806313a661ed14610259578063183a4f6e146102505780631c10893f146102475780631cd64df41461023e57806325692962146102355780632b28bb121461022c5780632de94807146102235780634a4ee7b11461021a5780634bc3201d1461021157806350ca771b14610208578063514e62fc146101ff5780635329d742146101f657806354d1f13d146101ed5780635542686d146101e45780635586402d146101db578063642be325146101d2578063715018a6146101c95780637359e41f146101c057806374a0024a146101b75780638da5cb5b146101ae578063a3c2c462146101a5578063ac35ccfa1461019c578063d1f0befb14610193578063d7533f021461018a578063e21de2e814610181578063f04e283e14610178578063f2fde38b1461016f5763fee81cf41461016757600080fd5b61000e610f0f565b5061000e610ea3565b5061000e610e0c565b5061000e610de2565b5061000e610dc3565b5061000e610c84565b5061000e610bdb565b5061000e610b81565b5061000e610b53565b5061000e610b0a565b5061000e610aa9565b5061000e610a14565b5061000e6109ea565b5061000e61099d565b5061000e61084b565b5061000e6107d0565b5061000e6107a6565b5061000e61076b565b5061000e61072e565b5061000e610704565b5061000e6106d5565b5061000e6106a0565b5061000e61066b565b5061000e6105e3565b5061000e61059c565b5061000e61052f565b5061000e610516565b5061000e6104a8565b5061000e6102f8565b5061000e61028a565b600435906001600160a01b038216820361000e57565b503461000e57604036600319011261000e576102a4610274565b638b78c6d8195433036102df576040516102dd916000918291829190602435906001600160a01b03165af16102d76110fb565b50611149565b005b6382b429006000526004601cfd5b60ff81160361000e57565b503461000e57604036600319011261000e5761038061036f60043561031c816102ed565b602061033f60243561032d816102ed565b6001546001600160a01b0316936115a1565b602460405180948193636e4e8d4f60e11b835260048301525afa9081156103b2575b600091610384575b50610f90565b546040519081529081906020820190565b0390f35b6103a5915060203d81116103ab575b61039d81836103f7565b81019061106d565b38610369565b503d610393565b6103ba611082565b610361565b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116103ea57604052565b6103f26103bf565b604052565b90601f8019910116810190811067ffffffffffffffff8211176103ea57604052565b60209067ffffffffffffffff8111610433575b60051b0190565b61043b6103bf565b61042c565b81601f8201121561000e5780359161045783610419565b9261046560405194856103f7565b808452602092838086019260051b82010192831161000e578301905b82821061048f575050505090565b838091833561049d816102ed565b815201910190610481565b503461000e5760208060031936011261000e5760043567ffffffffffffffff811161000e576104db903690600401610440565b906000918180820191805160051b01015b8082036104fe57604051848152602090f35b90918093600160ff8551161b179201909291926104ec565b50602036600319011261000e576102dd60043533610f2e565b50604036600319011261000e57610544610274565b638b78c6d8195433036102df57638b78c6d88160601b1760005260206000209060243582541780925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3005b503461000e57604036600319011261000e5760206105b8610274565b638b78c6d86024359160601b176000528082600020541614604051908152f35b600091031261000e57565b506000806003193601126106325763389a75e13360601b1781526202a30042016020822055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a2604051f35b80fd5b600d111561000e57565b606090600319011261000e576004359060243561065b81610635565b90604435610668816102ed565b90565b503461000e5761068361067d3661063f565b916115d4565b6000526007602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e57638b78c6d86106bf610274565b60601b1760005260208060002054604051908152f35b50604036600319011261000e576106ea610274565b638b78c6d8195433036102df576102dd9060243590610f2e565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b5061074c61074461073e3661063f565b9161126e565b341015611099565b610758346004546110e5565b6004553461076257005b6102dd34611461565b503461000e57604036600319011261000e57638b78c6d861078a610274565b60601b1760005260206024358160002054161515604051908152f35b503461000e57600036600319011261000e576003546040516001600160a01b039091168152602090f35b506000806003193601126106325763389a75e13360601b178152806020812055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a2604051f35b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b50606036600319011261000e5760043567ffffffffffffffff60243581811161000e5761087c90369060040161081a565b92909160443590811161000e5761089790369060040161081a565b8085036109285792919060009384955b8087106108d5576108ba86341015611099565b6108ce6108c9346004546110e5565b600455565b3461076257005b909192939461091761091d916109116108f76108f28b878c611033565b6110f1565b61090a6109058c898b611033565b61108f565b908961126e565b906110e5565b96611000565b9594939291906108a7565b60405162461bcd60e51b815260206004820152603a60248201527f506978656c53746f72653a206c6179657220616e64206672616d65206172726160448201527f7973206d757374206265207468652073616d65206c656e6774680000000000006064820152608490fd5b6004111561000e57565b503461000e57602036600319011261000e576004356109bb81610993565b60048110156109dd575b60005260056020526020604060002054604051908152f35b6109e5610f79565b6109c5565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b5060008060031936011261063257638b78c6d81980543303610a5e57819081337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a355604051f35b6382b4290082526004601cfd5b6020908160408183019282815285518094520193019160005b828110610a92575050505090565b835160ff1685529381019392810192600101610a84565b503461000e5760208060031936011261000e576004358160405101906000915b828152838260051b16019060011c918215610ae8576001019190610ac9565b60408051838252808403601f190160051c815290519081906103809082610a6b565b503461000e57602036600319011261000e57610b24610274565b638b78c6d8195433036102df57600080546001600160a01b0319166001600160a01b0392909216919091179055005b503461000e57600036600319011261000e57638b78c6d819546040516001600160a01b039091168152602090f35b503461000e57600036600319011261000e576020600454604051908152f35b6020908160408183019282815285518094520193019160005b828110610bc7575050505090565b835185529381019392810192600101610bb9565b503461000e5760208060031936011261000e57600490813567ffffffffffffffff811161000e57610c0f903690840161081a565b9091610c1a82610fb7565b93600092835b818110610c3557604051806103808982610ba0565b80610c44610c72928489611033565b35610c4e81610993565b84811015610c77575b8652600585526040862054610c6c828a61104b565b52611000565b610c20565b610c7f610f79565b610c57565b503461000e5760408060031936011261000e57600467ffffffffffffffff813581811161000e57610cb89036908401610440565b9060243590811161000e57610cd0903690840161081a565b610cd981610fb7565b936000928392610d02610cf6610cf660015460018060a01b031690565b6001600160a01b031690565b935b818110610d18578851806103808a82610ba0565b80610d89610d6e610d4d610d39610d32610d94968d61104b565b5160ff1690565b610d4761090586898b611033565b906115a1565b8c5190636e4e8d4f60e11b8252818060209485938c83019190602083019252565b03818c5afa918215610db6575b8a92610d99575b5050610f90565b54610c6c828b61104b565b610d04565b610daf9250803d106103ab5761039d81836103f7565b3880610d82565b610dbe611082565b610d7b565b503461000e57600036600319011261000e5760206040516202a3008152f35b503461000e57600036600319011261000e576002546040516001600160a01b039091168152602090f35b50602036600319011261000e57610e21610274565b638b78c6d819805433036102df57606082901b6bffffffffffffffffffffffff191663389a75e117600090815260208120805491936001600160a01b031692914211610e965783905581337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08580a355604051f35b636f5e881884526004601cfd5b50602036600319011261000e57610eb8610274565b638b78c6d81990815433036102df576001600160a01b0316908115610f015781337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b637448fbae6000526004601cfd5b503461000e57602036600319011261000e5763389a75e16106bf610274565b638b78c6d88160601b1760005260206000209182549081161880925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3565b50634e487b7160e01b600052602160045260246000fd5b6004811015610faa575b6000526005602052604060002090565b610fb2610f79565b610f9a565b90610fc182610419565b610fce60405191826103f7565b8281528092610fdf601f1991610419565b0190602036910137565b50634e487b7160e01b600052601160045260246000fd5b6001906000198114611010570190565b611018610fe9565b0190565b50634e487b7160e01b600052603260045260246000fd5b91908110156110435760051b0190565b61043b61101c565b6020918151811015611060575b60051b010190565b61106861101c565b611058565b9081602091031261000e575161066881610993565b506040513d6000823e3d90fd5b35610668816102ed565b156110a057565b60405162461bcd60e51b815260206004820152601e60248201527f506978656c53746f72653a20496e73756666696369656e742066756e647300006044820152606490fd5b81198111611010570190565b3561066881610635565b3d15611144573d9067ffffffffffffffff8211611137575b6040519161112b601f8201601f1916602001846103f7565b82523d6000602084013e565b61113f6103bf565b611113565b606090565b1561115057565b60405162461bcd60e51b815260206004820152601d60248201527f506978656c53746f72653a6574682d72656c656173652d6661696c65640000006044820152606490fd5b1561119c57565b60405162461bcd60e51b815260206004820152601960248201527f506978656c53746f72653a706978656c2d756e6c6f636b6564000000000000006044820152606490fd5b9081602091031261000e575190565b156111f757565b60405162461bcd60e51b815260206004820152601860248201527f506978656c53746f72653a74726169742d6578706972656400000000000000006044820152606490fd5b91604091949360ff9160608501968552600d811015611261575b602085015216910152565b611269610f79565b611256565b9161127a8183856115d4565b916112a86112a361129f611298866000526007602052604060002090565b5460ff1690565b1590565b611195565b6112b28282611544565b926113286112cd610cf6610cf660015460018060a01b031690565b6040516312288acf60e21b815260048101879052602096879290918381602481855afa908115611454575b600091611427575b5080611416575b506040518080958194636e4e8d4f60e11b8352600483019190602083019252565b03915afa948515611409575b6000956113ea575b505060005461135590610cf6906001600160a01b031681565b91823b1561000e5760006113bb946113ae9461138d93836113c09a60405196879586948593634f604be760e11b85526004850161123c565b03925af180156113dd575b6113c4575b506000526007602052604060002090565b805460ff19166001179055565b610f90565b5490565b806113d16113d7926103d6565b806105d8565b3861139d565b6113e5611082565b611398565b611401929550803d106103ab5761039d81836103f7565b92388061133c565b611411611082565b611334565b6114219042106111f0565b38611307565b6114479150843d861161144d575b61143f81836103f7565b8101906111e1565b38611300565b503d611435565b61145c611082565b6112f8565b806000190460501181151516611537575b606460508202049081811061152a575b60018060a01b03600254169060008080809486604051915af16114a36110fb565b50156114da578180916114d8946114c7610cf6610cf660035460018060a01b031690565b906040519203905af16102d76110fb565b565b60405162461bcd60e51b815260206004820152602260248201527f506978656c53746f72653a626972622d6574682d72656c656173652d6661696c604482015261195960f21b6064820152608490fd5b611532610fe9565b611482565b61153f610fe9565b611472565b9061158e61158091600d841015611594575b6040519283916020830195869060029260ff60f81b809260f81b16835260f81b1660018201520190565b03601f1981018352826103f7565b51902090565b61159c610f79565b611556565b6040516001600160f81b031960f892831b8116602083019081529390921b909116602182015261158e8160228101611580565b916040519160208301938452600d811015611636575b60f890811b60408401521b6001600160f81b0319166041820152602281526060810167ffffffffffffffff811182821017611629575b60405251902090565b6116316103bf565b611620565b61163e610f79565b6115ea56fea2646970667358221220b6c73f779187fa3f5d5f88384ea6227af2d1f28caef0ddb788af288223ec188764736f6c634300080f003300000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b22742948c4aa488b991e5047e553dfda8ec2cdc0000000000000000000000002f050994e566ce4c7bdd6227bf90d0134ecda81f00000000000000000000000034bc2128e1dca41e26824390d2e907e6272bfb42000000000000000000000000d61fa937b8f237901d354f48f6b14995fe468bf2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b20000
Deployed Bytecode
0x60806040526004361015610013575b600080fd5b60003560e01c80630357371d1461026b57806304a7ec781461026257806313a661ed14610259578063183a4f6e146102505780631c10893f146102475780631cd64df41461023e57806325692962146102355780632b28bb121461022c5780632de94807146102235780634a4ee7b11461021a5780634bc3201d1461021157806350ca771b14610208578063514e62fc146101ff5780635329d742146101f657806354d1f13d146101ed5780635542686d146101e45780635586402d146101db578063642be325146101d2578063715018a6146101c95780637359e41f146101c057806374a0024a146101b75780638da5cb5b146101ae578063a3c2c462146101a5578063ac35ccfa1461019c578063d1f0befb14610193578063d7533f021461018a578063e21de2e814610181578063f04e283e14610178578063f2fde38b1461016f5763fee81cf41461016757600080fd5b61000e610f0f565b5061000e610ea3565b5061000e610e0c565b5061000e610de2565b5061000e610dc3565b5061000e610c84565b5061000e610bdb565b5061000e610b81565b5061000e610b53565b5061000e610b0a565b5061000e610aa9565b5061000e610a14565b5061000e6109ea565b5061000e61099d565b5061000e61084b565b5061000e6107d0565b5061000e6107a6565b5061000e61076b565b5061000e61072e565b5061000e610704565b5061000e6106d5565b5061000e6106a0565b5061000e61066b565b5061000e6105e3565b5061000e61059c565b5061000e61052f565b5061000e610516565b5061000e6104a8565b5061000e6102f8565b5061000e61028a565b600435906001600160a01b038216820361000e57565b503461000e57604036600319011261000e576102a4610274565b638b78c6d8195433036102df576040516102dd916000918291829190602435906001600160a01b03165af16102d76110fb565b50611149565b005b6382b429006000526004601cfd5b60ff81160361000e57565b503461000e57604036600319011261000e5761038061036f60043561031c816102ed565b602061033f60243561032d816102ed565b6001546001600160a01b0316936115a1565b602460405180948193636e4e8d4f60e11b835260048301525afa9081156103b2575b600091610384575b50610f90565b546040519081529081906020820190565b0390f35b6103a5915060203d81116103ab575b61039d81836103f7565b81019061106d565b38610369565b503d610393565b6103ba611082565b610361565b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116103ea57604052565b6103f26103bf565b604052565b90601f8019910116810190811067ffffffffffffffff8211176103ea57604052565b60209067ffffffffffffffff8111610433575b60051b0190565b61043b6103bf565b61042c565b81601f8201121561000e5780359161045783610419565b9261046560405194856103f7565b808452602092838086019260051b82010192831161000e578301905b82821061048f575050505090565b838091833561049d816102ed565b815201910190610481565b503461000e5760208060031936011261000e5760043567ffffffffffffffff811161000e576104db903690600401610440565b906000918180820191805160051b01015b8082036104fe57604051848152602090f35b90918093600160ff8551161b179201909291926104ec565b50602036600319011261000e576102dd60043533610f2e565b50604036600319011261000e57610544610274565b638b78c6d8195433036102df57638b78c6d88160601b1760005260206000209060243582541780925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3005b503461000e57604036600319011261000e5760206105b8610274565b638b78c6d86024359160601b176000528082600020541614604051908152f35b600091031261000e57565b506000806003193601126106325763389a75e13360601b1781526202a30042016020822055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d8280a2604051f35b80fd5b600d111561000e57565b606090600319011261000e576004359060243561065b81610635565b90604435610668816102ed565b90565b503461000e5761068361067d3661063f565b916115d4565b6000526007602052602060ff604060002054166040519015158152f35b503461000e57602036600319011261000e57638b78c6d86106bf610274565b60601b1760005260208060002054604051908152f35b50604036600319011261000e576106ea610274565b638b78c6d8195433036102df576102dd9060243590610f2e565b503461000e57600036600319011261000e576001546040516001600160a01b039091168152602090f35b5061074c61074461073e3661063f565b9161126e565b341015611099565b610758346004546110e5565b6004553461076257005b6102dd34611461565b503461000e57604036600319011261000e57638b78c6d861078a610274565b60601b1760005260206024358160002054161515604051908152f35b503461000e57600036600319011261000e576003546040516001600160a01b039091168152602090f35b506000806003193601126106325763389a75e13360601b178152806020812055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c928280a2604051f35b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b50606036600319011261000e5760043567ffffffffffffffff60243581811161000e5761087c90369060040161081a565b92909160443590811161000e5761089790369060040161081a565b8085036109285792919060009384955b8087106108d5576108ba86341015611099565b6108ce6108c9346004546110e5565b600455565b3461076257005b909192939461091761091d916109116108f76108f28b878c611033565b6110f1565b61090a6109058c898b611033565b61108f565b908961126e565b906110e5565b96611000565b9594939291906108a7565b60405162461bcd60e51b815260206004820152603a60248201527f506978656c53746f72653a206c6179657220616e64206672616d65206172726160448201527f7973206d757374206265207468652073616d65206c656e6774680000000000006064820152608490fd5b6004111561000e57565b503461000e57602036600319011261000e576004356109bb81610993565b60048110156109dd575b60005260056020526020604060002054604051908152f35b6109e5610f79565b6109c5565b503461000e57600036600319011261000e576000546040516001600160a01b039091168152602090f35b5060008060031936011261063257638b78c6d81980543303610a5e57819081337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a355604051f35b6382b4290082526004601cfd5b6020908160408183019282815285518094520193019160005b828110610a92575050505090565b835160ff1685529381019392810192600101610a84565b503461000e5760208060031936011261000e576004358160405101906000915b828152838260051b16019060011c918215610ae8576001019190610ac9565b60408051838252808403601f190160051c815290519081906103809082610a6b565b503461000e57602036600319011261000e57610b24610274565b638b78c6d8195433036102df57600080546001600160a01b0319166001600160a01b0392909216919091179055005b503461000e57600036600319011261000e57638b78c6d819546040516001600160a01b039091168152602090f35b503461000e57600036600319011261000e576020600454604051908152f35b6020908160408183019282815285518094520193019160005b828110610bc7575050505090565b835185529381019392810192600101610bb9565b503461000e5760208060031936011261000e57600490813567ffffffffffffffff811161000e57610c0f903690840161081a565b9091610c1a82610fb7565b93600092835b818110610c3557604051806103808982610ba0565b80610c44610c72928489611033565b35610c4e81610993565b84811015610c77575b8652600585526040862054610c6c828a61104b565b52611000565b610c20565b610c7f610f79565b610c57565b503461000e5760408060031936011261000e57600467ffffffffffffffff813581811161000e57610cb89036908401610440565b9060243590811161000e57610cd0903690840161081a565b610cd981610fb7565b936000928392610d02610cf6610cf660015460018060a01b031690565b6001600160a01b031690565b935b818110610d18578851806103808a82610ba0565b80610d89610d6e610d4d610d39610d32610d94968d61104b565b5160ff1690565b610d4761090586898b611033565b906115a1565b8c5190636e4e8d4f60e11b8252818060209485938c83019190602083019252565b03818c5afa918215610db6575b8a92610d99575b5050610f90565b54610c6c828b61104b565b610d04565b610daf9250803d106103ab5761039d81836103f7565b3880610d82565b610dbe611082565b610d7b565b503461000e57600036600319011261000e5760206040516202a3008152f35b503461000e57600036600319011261000e576002546040516001600160a01b039091168152602090f35b50602036600319011261000e57610e21610274565b638b78c6d819805433036102df57606082901b6bffffffffffffffffffffffff191663389a75e117600090815260208120805491936001600160a01b031692914211610e965783905581337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08580a355604051f35b636f5e881884526004601cfd5b50602036600319011261000e57610eb8610274565b638b78c6d81990815433036102df576001600160a01b0316908115610f015781337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b637448fbae6000526004601cfd5b503461000e57602036600319011261000e5763389a75e16106bf610274565b638b78c6d88160601b1760005260206000209182549081161880925560018060a01b03167f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a3565b50634e487b7160e01b600052602160045260246000fd5b6004811015610faa575b6000526005602052604060002090565b610fb2610f79565b610f9a565b90610fc182610419565b610fce60405191826103f7565b8281528092610fdf601f1991610419565b0190602036910137565b50634e487b7160e01b600052601160045260246000fd5b6001906000198114611010570190565b611018610fe9565b0190565b50634e487b7160e01b600052603260045260246000fd5b91908110156110435760051b0190565b61043b61101c565b6020918151811015611060575b60051b010190565b61106861101c565b611058565b9081602091031261000e575161066881610993565b506040513d6000823e3d90fd5b35610668816102ed565b156110a057565b60405162461bcd60e51b815260206004820152601e60248201527f506978656c53746f72653a20496e73756666696369656e742066756e647300006044820152606490fd5b81198111611010570190565b3561066881610635565b3d15611144573d9067ffffffffffffffff8211611137575b6040519161112b601f8201601f1916602001846103f7565b82523d6000602084013e565b61113f6103bf565b611113565b606090565b1561115057565b60405162461bcd60e51b815260206004820152601d60248201527f506978656c53746f72653a6574682d72656c656173652d6661696c65640000006044820152606490fd5b1561119c57565b60405162461bcd60e51b815260206004820152601960248201527f506978656c53746f72653a706978656c2d756e6c6f636b6564000000000000006044820152606490fd5b9081602091031261000e575190565b156111f757565b60405162461bcd60e51b815260206004820152601860248201527f506978656c53746f72653a74726169742d6578706972656400000000000000006044820152606490fd5b91604091949360ff9160608501968552600d811015611261575b602085015216910152565b611269610f79565b611256565b9161127a8183856115d4565b916112a86112a361129f611298866000526007602052604060002090565b5460ff1690565b1590565b611195565b6112b28282611544565b926113286112cd610cf6610cf660015460018060a01b031690565b6040516312288acf60e21b815260048101879052602096879290918381602481855afa908115611454575b600091611427575b5080611416575b506040518080958194636e4e8d4f60e11b8352600483019190602083019252565b03915afa948515611409575b6000956113ea575b505060005461135590610cf6906001600160a01b031681565b91823b1561000e5760006113bb946113ae9461138d93836113c09a60405196879586948593634f604be760e11b85526004850161123c565b03925af180156113dd575b6113c4575b506000526007602052604060002090565b805460ff19166001179055565b610f90565b5490565b806113d16113d7926103d6565b806105d8565b3861139d565b6113e5611082565b611398565b611401929550803d106103ab5761039d81836103f7565b92388061133c565b611411611082565b611334565b6114219042106111f0565b38611307565b6114479150843d861161144d575b61143f81836103f7565b8101906111e1565b38611300565b503d611435565b61145c611082565b6112f8565b806000190460501181151516611537575b606460508202049081811061152a575b60018060a01b03600254169060008080809486604051915af16114a36110fb565b50156114da578180916114d8946114c7610cf6610cf660035460018060a01b031690565b906040519203905af16102d76110fb565b565b60405162461bcd60e51b815260206004820152602260248201527f506978656c53746f72653a626972622d6574682d72656c656173652d6661696c604482015261195960f21b6064820152608490fd5b611532610fe9565b611482565b61153f610fe9565b611472565b9061158e61158091600d841015611594575b6040519283916020830195869060029260ff60f81b809260f81b16835260f81b1660018201520190565b03601f1981018352826103f7565b51902090565b61159c610f79565b611556565b6040516001600160f81b031960f892831b8116602083019081529390921b909116602182015261158e8160228101611580565b916040519160208301938452600d811015611636575b60f890811b60408401521b6001600160f81b0319166041820152602281526060810167ffffffffffffffff811182821017611629575b60405251902090565b6116316103bf565b611620565b61163e610f79565b6115ea56fea2646970667358221220b6c73f779187fa3f5d5f88384ea6227af2d1f28caef0ddb788af288223ec188764736f6c634300080f0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000160000000000000000000000000b22742948c4aa488b991e5047e553dfda8ec2cdc0000000000000000000000002f050994e566ce4c7bdd6227bf90d0134ecda81f00000000000000000000000034bc2128e1dca41e26824390d2e907e6272bfb42000000000000000000000000d61fa937b8f237901d354f48f6b14995fe468bf2000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b20000
-----Decoded View---------------
Arg [0] : tiers (uint8[]): 0,1,2,3
Arg [1] : prices (uint256[]): 0,50000000000000000,200000000000000000,500000000000000000
Arg [2] : _pixelAssets (address): 0xb22742948c4AA488B991E5047e553DfDa8Ec2cdC
Arg [3] : _birbAddress_ (address): 0x2F050994E566ce4c7BdD6227bF90D0134ecDa81f
Arg [4] : _flapAddress_ (address): 0x34Bc2128e1Dca41E26824390D2e907e6272bFB42
Arg [5] : _owner (address): 0xd61FA937b8f237901D354f48f6b14995fE468bF2
-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [2] : 000000000000000000000000b22742948c4aa488b991e5047e553dfda8ec2cdc
Arg [3] : 0000000000000000000000002f050994e566ce4c7bdd6227bf90d0134ecda81f
Arg [4] : 00000000000000000000000034bc2128e1dca41e26824390d2e907e6272bfb42
Arg [5] : 000000000000000000000000d61fa937b8f237901d354f48f6b14995fe468bf2
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [14] : 00000000000000000000000000000000000000000000000002c68af0bb140000
Arg [15] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
[ Download: CSV Export ]
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.