Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@ensdomains/ens-contracts/contracts/registry/ENS.sol": {
"content": "//SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
interface ENS {
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
// Logged when an operator is added or removed.
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
function setRecord(
bytes32 node,
address owner,
address resolver,
uint64 ttl
) external;
function setSubnodeRecord(
bytes32 node,
bytes32 label,
address owner,
address resolver,
uint64 ttl
) external;
function setSubnodeOwner(
bytes32 node,
bytes32 label,
address owner
) external returns (bytes32);
function setResolver(bytes32 node, address resolver) external;
function setOwner(bytes32 node, address owner) external;
function setTTL(bytes32 node, uint64 ttl) external;
function setApprovalForAll(address operator, bool approved) external;
function owner(bytes32 node) external view returns (address);
function resolver(bytes32 node) external view returns (address);
function ttl(bytes32 node) external view returns (uint64);
function recordExists(bytes32 node) external view returns (bool);
function isApprovedForAll(
address owner,
address operator
) external view returns (bool);
}
"
},
"@ensdomains/ens-contracts/contracts/resolvers/profiles/INameResolver.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
interface INameResolver {
event NameChanged(bytes32 indexed node, string name);
/// Returns the name associated with an ENS node, for reverse records.
/// Defined in EIP181.
/// @param node The ENS node to query.
/// @return The associated name.
function name(bytes32 node) external view returns (string memory);
}
"
},
"@ensdomains/ens-contracts/contracts/reverseRegistrar/IReverseRegistrar.sol": {
"content": "pragma solidity >=0.8.4;
interface IReverseRegistrar {
function setDefaultResolver(address resolver) external;
function claim(address owner) external returns (bytes32);
function claimForAddr(
address addr,
address owner,
address resolver
) external returns (bytes32);
function claimWithResolver(
address owner,
address resolver
) external returns (bytes32);
function setName(string memory name) external returns (bytes32);
function setNameForAddr(
address addr,
address owner,
address resolver,
string memory name
) external returns (bytes32);
function node(address addr) external pure returns (bytes32);
}
"
},
"@openzeppelin/contracts/utils/Base64.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Base64.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
* See sections 4 and 5 of https://datatracker.ietf.org/doc/html/rfc4648
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string internal constant _TABLE_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
return _encode(data, _TABLE, true);
}
/**
* @dev Converts a `bytes` to its Bytes64Url `string` representation.
* Output is not padded with `=` as specified in https://www.rfc-editor.org/rfc/rfc4648[rfc4648].
*/
function encodeURL(bytes memory data) internal pure returns (string memory) {
return _encode(data, _TABLE_URL, false);
}
/**
* @dev Internal table-agnostic conversion
*/
function _encode(bytes memory data, string memory table, bool withPadding) private pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// If padding is enabled, the final length should be `bytes` data length divided by 3 rounded up and then
// multiplied by 4 so that it leaves room for padding the last chunk
// - `data.length + 2` -> Prepare for division rounding up
// - `/ 3` -> Number of 3-bytes chunks (rounded up)
// - `4 *` -> 4 characters for each chunk
// This is equivalent to: 4 * Math.ceil(data.length / 3)
//
// If padding is disabled, the final length should be `bytes` data length multiplied by 4/3 rounded up as
// opposed to when padding is required to fill the last chunk.
// - `4 * data.length` -> 4 characters for each chunk
// - ` + 2` -> Prepare for division rounding up
// - `/ 3` -> Number of 3-bytes chunks (rounded up)
// This is equivalent to: Math.ceil((4 * data.length) / 3)
uint256 resultLength = withPadding ? 4 * ((data.length + 2) / 3) : (4 * data.length + 2) / 3;
string memory result = new string(resultLength);
assembly ("memory-safe") {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 0x20)
let dataPtr := data
let endPtr := add(data, mload(data))
// In some cases, the last iteration will read bytes after the end of the data. We cache the value, and
// set it to zero to make sure no dirty bytes are read in that section.
let afterPtr := add(endPtr, 0x20)
let afterCache := mload(afterPtr)
mstore(afterPtr, 0x00)
// Run over the input, 3 bytes at a time
for {} lt(dataPtr, endPtr) {} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 byte (24 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F to bitmask the least significant 6 bits.
// Use this as an index into the lookup table, mload an entire word
// so the desired character is in the least significant byte, and
// mstore8 this least significant byte into the result and continue.
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// Reset the value that was cached
mstore(afterPtr, afterCache)
if withPadding {
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
}
return result;
}
}
"
},
"contracts/BugcatCodexRenderer.sol": {
"content": "// SPDX-License-Identifier: WTFPL
pragma solidity ^0.8.30;
import "./utils/ENSResolver.sol";
import "solady/src/utils/LibString.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
interface IBugcatsRegistry {
function bugs(uint256) external view returns (address);
}
contract BugcatCodexRenderer {
IBugcatsRegistry public immutable bugcatRegistry;
string private specialCodeLight;
string private specialCodeDark;
constructor(address _bugcatRegistry, string memory _specialCodeLight, string memory _specialCodeDark) {
bugcatRegistry = IBugcatsRegistry(_bugcatRegistry);
specialCodeLight = _specialCodeLight;
specialCodeDark = _specialCodeDark;
}
function renderImage(uint256 tokenId, address caretaker, uint8 bugcatIndex, string memory code, bool light, bool compiled) external view returns (string memory) {
address bugcat = bugcatRegistry.bugs(bugcatIndex);
string memory svg = _generateSvg(tokenId, caretaker, bugcat, code, light, compiled);
return string.concat("data:image/svg+xml;base64,", Base64.encode(bytes(svg)));
}
function renderAnimationUrl(uint256 tokenId, address caretaker, uint8 bugcatIndex, string memory code, bool light, bool compiled, uint8[] memory preservedBugcatIndexes) external view returns (string memory) {
address bugcat = bugcatRegistry.bugs(bugcatIndex);
string memory svg = _generateSvg(tokenId, caretaker, bugcat, code, light, compiled);
string memory backSvg = _generateBackSvg(tokenId, caretaker, preservedBugcatIndexes, light);
return _wrapInInteractiveHtml(svg, backSvg, light);
}
function _generateSvg(uint256 tokenId, address caretaker, address bugcat, string memory code, bool light, bool compiled) internal view returns (string memory) {
string memory bgColor = light ? "#f5f5f5" : "#0a0a0a";
string memory boxBgColor = light ? "#ffffff" : "#1a1a1a";
string memory textColor = light ? "#3a3a3a" : "#e0e0e0";
string memory hlBg = light ? "#dddddd" : "#444444";
string memory bytecode = LibString.toHexString(bugcat.code);
string memory content = compiled ? _twoColsAutoHighlight(bytecode) : _escapeHtml(code);
string memory comment = compiled ? "" : string.concat("<!-- ", bytecode, " -->");
return string.concat(
"<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 1000 1000\" preserveAspectRatio=\"xMidYMid meet\" style=\"background-color: ", bgColor, "\" xmlns=\"http://www.w3.org/2000/svg\">",
"<defs><style>\
",
".header { font-family: monospace; font-size: 17px; line-height: 1.0; letter-spacing: 0.01em; white-space: pre-wrap; word-break: break-all; overflow: hidden; height: 100%; color: ", textColor, "; }\
",
".code { font-family: monospace; font-size: 17px; line-height: 1.3; letter-spacing: 0.1em; white-space: pre-wrap; word-break: break-all; overflow: hidden; height: 100%; color: ", textColor, "; }\
",
".cols { display: flex; gap: 28px; }\
",
".col { flex: 1 1 0; }\
",
".hl { background:", hlBg, "; padding: 0 2px; border-radius: 2px; }\
",
"</style></defs>",
"<rect width=\"1000\" height=\"1000\" fill=\"", boxBgColor, "\"/>",
_getHeader(tokenId, caretaker),
"<foreignObject x=\"60\" y=\"220\" width=\"880\" height=\"790\">",
"<div xmlns=\"http://www.w3.org/1999/xhtml\" class=\"code\">",
content,
"</div></foreignObject>",
comment,
"</svg>"
);
}
function _isComplete(uint8[] memory preservedBugcatIndexes) internal pure returns (bool) {
if (preservedBugcatIndexes.length < 5) return false;
bool[5] memory found;
for (uint i = 0; i < preservedBugcatIndexes.length; i++) {
uint8 index = preservedBugcatIndexes[i];
if (index < 5) {
found[index] = true;
}
}
for (uint i = 0; i < 5; i++) {
if (!found[i]) return false;
}
return true;
}
function _generateBackSvg(uint256 tokenId, address caretaker, uint8[] memory preservedBugcatIndexes, bool light) internal view returns (string memory) {
string memory bgColor = light ? "#f5f5f5" : "#0a0a0a";
string memory boxBgColor = light ? "#ffffff" : "#1a1a1a";
string memory textColor = light ? "#3a3a3a" : "#e0e0e0";
string memory content;
if (_isComplete(preservedBugcatIndexes)) {
content = light ? specialCodeLight : specialCodeDark;
} else {
content = _buildIncompleteMessage(preservedBugcatIndexes);
}
return string.concat(
"<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 1000 1000\" preserveAspectRatio=\"xMidYMid meet\" style=\"background-color: ", bgColor, "\" xmlns=\"http://www.w3.org/2000/svg\">",
"<defs><style>\
",
".header { font-family: monospace; font-size: 17px; line-height: 1.0; letter-spacing: 0.01em; white-space: pre-wrap; word-break: break-all; overflow: hidden; height: 100%; color: ", textColor, "; }\
",
".code { font-family: monospace; font-size: 17px; line-height: 1.3; letter-spacing: 0.1em; white-space: pre-wrap; word-break: break-all; overflow: hidden; height: 100%; color: ", textColor, "; }\
",
".message { font-family: monospace; font-size: 17px; line-height: 0.95; letter-spacing: 0.01em; white-space: pre-wrap; word-break: break-all; overflow: hidden; height: 100%; color: ", textColor, "; }\
",
"</style></defs>",
"<rect width=\"1000\" height=\"1000\" fill=\"", boxBgColor, "\"/>",
_getHeader(tokenId, caretaker),
"<foreignObject x=\"60\" y=\"220\" width=\"880\" height=\"790\">",
"<div xmlns=\"http://www.w3.org/1999/xhtml\" class=\"message\">",
content,
"</div></foreignObject>",
"</svg>"
);
}
function _getHeader(uint256 tokenId, address caretaker) internal view returns (string memory) {
string memory caretakerStr;
try ENSResolver.resolveAddress(caretaker) returns (string memory nameOrAddr) {
caretakerStr = nameOrAddr;
} catch {
caretakerStr = LibString.toHexStringChecksummed(caretaker);
}
return string.concat(
"<foreignObject x=\"55\" y=\"30\" width=\"945\" height=\"30\">",
"<div class=\"code\" xmlns=\"http://www.w3.org/1999/xhtml\">",
"Codex #", _toString(tokenId), " preserved by ", caretakerStr, "\
",
"</div></foreignObject>",
"<foreignObject x=\"55\" y=\"70\" width=\"925\" height=\"120\">",
"<div class=\"header\" xmlns=\"http://www.w3.org/1999/xhtml\">",
"/////////////////////////////////////////////////////////////////////////////////////////////////////\
",
"// ____ __ __ ___ ___ ___ ______ ___ ___ ____ ____ _ _ //\
",
"// || )) || || // \\\\ // // \\\\ | || | // // \\\\ || \\\\ || \\\\ // //\
",
"// ||=) || || (( ___ (( ||=|| || (( (( )) || )) ||== )X( //\
",
"// ||_)) \\\\_// \\\\_|| \\\\__ || || || \\\\__ \\\\_// ||_// ||___ // \\\\ //\
",
"// //\
",
"/////////////////////////////////////////////////////////////////////////////////////////////////////\
",
"</div></foreignObject>"
);
}
function _buildIncompleteMessage(uint8[] memory preservedBugcatIndexes) internal view returns (string memory) {
string memory statusLine = "";
for (uint i = 0; i < 5; i++) {
bool hasThisBugcat = false;
string memory woundName = "";
for (uint j = 0; j < preservedBugcatIndexes.length; j++) {
if (preservedBugcatIndexes[j] == i) {
hasThisBugcat = true;
address bugcat = bugcatRegistry.bugs(i);
woundName = _extractWoundFromBytecode(bugcat);
break;
}
}
woundName = hasThisBugcat ? woundName : "----------";
statusLine = string.concat(statusLine, "[", woundName, "]");
if (i < 4) {
statusLine = string.concat(statusLine, " ");
}
}
return string.concat(
"BUGCAT CODEX: INCOMPLETE\
\
",
statusLine,
"\
\
Some wounds witnessed. The Codex remembers you."
);
}
function _buildWoundsList(uint8[] memory preservedBugcatIndexes) internal view returns (string memory) {
string memory result = "";
for (uint i = 0; i < preservedBugcatIndexes.length; i++) {
uint8 bugcatIndex = preservedBugcatIndexes[i];
address bugcat = bugcatRegistry.bugs(bugcatIndex);
string memory wound = _extractWoundFromBytecode(bugcat);
result = string.concat(
result,
"<tspan x=\"100\" dy=\"25\">[x] I remember ", wound, " cat and its wound</tspan>"
);
}
return result;
}
function _extractWoundFromBytecode(address bugcat) internal view returns (string memory) {
bytes memory bytecode = bugcat.code;
if (bytecode.length == 0) return "n/a";
(bool found, string memory catName) = _extractFromPushInstructions(bytecode);
if (found) {
return catName;
}
catName = _findLongestValidString(bytecode);
if (bytes(catName).length > 0) {
return catName;
}
return "n/a";
}
function _extractFromPushInstructions(bytes memory bytecode) internal pure returns (bool found, string memory result) {
uint256 bestScore = 0;
bytes memory bestCandidate;
uint256 pc = 0;
while (pc < bytecode.length) {
uint8 opcode = uint8(bytecode[pc]);
if (opcode >= 0x60 && opcode <= 0x7f) {
uint256 pushSize = opcode - 0x5f;
if (pc + pushSize < bytecode.length) {
bytes memory data = new bytes(pushSize);
for (uint256 i = 0; i < pushSize; i++) {
data[i] = bytecode[pc + 1 + i];
}
if (_isValidWoundString(data)) {
uint256 score = pushSize * 10 + 100;
if (score > bestScore) {
bestScore = score;
bestCandidate = data;
}
}
}
pc += 1 + pushSize;
} else {
pc++;
}
}
if (bestScore > 0) {
return (true, string(bestCandidate));
}
return (false, "");
}
function _findLongestValidString(bytes memory bytecode) internal pure returns (string memory) {
uint256 bestLength = 0;
uint256 bestStart = 0;
for (uint256 i = 0; i < bytecode.length; i++) {
if (bytecode[i] >= 0x61 && bytecode[i] <= 0x7a) {
uint256 start = i;
uint256 end = i + 1;
while (end < bytecode.length && bytecode[end] >= 0x61 && bytecode[end] <= 0x7a) {
end++;
}
uint256 length = end - start;
if (length >= 6 && length <= 15 && length > bestLength) {
bytes memory candidate = new bytes(length);
for (uint256 j = 0; j < length; j++) {
candidate[j] = bytecode[start + j];
}
if (_isValidWord(candidate)) {
bestLength = length;
bestStart = start;
}
}
i = end - 1;
}
}
if (bestLength > 0) {
bytes memory result = new bytes(bestLength);
for (uint256 i = 0; i < bestLength; i++) {
result[i] = bytecode[bestStart + i];
}
return string(result);
}
return "";
}
function _isValidWoundString(bytes memory data) internal pure returns (bool) {
if (data.length < 6 || data.length > 15) return false;
for (uint256 i = 0; i < data.length; i++) {
uint8 b = uint8(data[i]);
if (b == 0x60) return false;
if (b == 0x20) return false;
if (b < 0x20 || b > 0x7e) return false;
}
bool allLowercase = true;
for (uint256 i = 0; i < data.length; i++) {
if (data[i] < 0x61 || data[i] > 0x7a) {
allLowercase = false;
break;
}
}
if (allLowercase) {
return _hasReasonableVowels(data);
}
return false;
}
function _isValidWord(bytes memory data) internal pure returns (bool) {
bool hasVowel = false;
uint256 consonantStreak = 0;
uint256 maxConsonantStreak = 0;
for (uint256 i = 0; i < data.length; i++) {
uint8 b = uint8(data[i]);
if (b == 0x61 || b == 0x65 || b == 0x69 || b == 0x6f || b == 0x75) {
hasVowel = true;
consonantStreak = 0;
} else {
consonantStreak++;
if (consonantStreak > maxConsonantStreak) {
maxConsonantStreak = consonantStreak;
}
}
}
return hasVowel && maxConsonantStreak <= 4;
}
function _hasReasonableVowels(bytes memory data) internal pure returns (bool) {
uint256 vowelCount = 0;
for (uint256 i = 0; i < data.length; i++) {
uint8 b = uint8(data[i]);
if (b == 0x61 || b == 0x65 || b == 0x69 || b == 0x6f || b == 0x75) {
vowelCount++;
}
}
return vowelCount > 0 && vowelCount * 100 / data.length <= 60;
}
function _containsPattern(bytes memory haystack, bytes memory needle) internal pure returns (bool) {
if (needle.length == 0 || needle.length > haystack.length) {
return false;
}
for (uint256 i = 0; i <= haystack.length - needle.length; i++) {
bool matched = true;
for (uint256 j = 0; j < needle.length; j++) {
if (haystack[i + j] != needle[j]) {
matched = false;
break;
}
}
if (matched) {
return true;
}
}
return false;
}
function _wrapInInteractiveHtml(string memory svg, string memory backSvg, bool light) internal pure returns (string memory) {
string memory bgColor = light ? "#f5f5f5" : "#0a0a0a";
string memory html = string.concat(
"data:text/html;charset=utf-8;base64,",
Base64.encode(bytes(string.concat(
"<!DOCTYPE html><html><head><style>",
"html,body{margin:0;padding:0;width:100%;height:100%;background:", bgColor, ";overflow:hidden;perspective:1000px;}",
".card-container{position:absolute;top:0;left:0;width:100vw;height:100vh;display:flex;justify-content:center;align-items:center;}",
".card{position:relative;width:100vw;height:100vh;transform-style:preserve-3d;transition:transform 0.6s ease-in-out;}",
".card-face{position:absolute;top:0;left:0;width:100vw;height:100vh;backface-visibility:hidden;display:flex;justify-content:center;align-items:center;}",
".card-face svg{max-width:100%;max-height:100%;width:auto;height:auto;display:block;}",
".card-front{transform:rotateY(0deg);}",
".card-back{transform:rotateY(180deg);}",
"body{cursor:pointer;}",
"body.flipped .card{transform:rotateY(180deg);}",
"</style></head><body onclick=\"handleClick(event)\">",
"<script>",
"let isSelecting=false;",
"let mouseDownTime=0;",
"document.addEventListener('mousedown',()=>mouseDownTime=Date.now());",
"document.addEventListener('selectstart',()=>isSelecting=true);",
"document.addEventListener('selectionchange',()=>{",
"if(window.getSelection().toString()==='')isSelecting=false;",
"});",
"function handleClick(e){",
"const clickDuration=Date.now()-mouseDownTime;",
"if(isSelecting||clickDuration>200)return;",
"document.body.classList.toggle('flipped');",
"}",
"</script>",
"<div class=\"card-container\">",
"<div class=\"card\">",
"<div class=\"card-face card-front\">", svg, "</div>",
"<div class=\"card-face card-back\">", backSvg, "</div>",
"</div>",
"</div>",
"</body></html>"
)))
);
return html;
}
function _twoColsAutoHighlight(string memory hex0x) internal pure returns (string memory) {
string memory hexStr = _strip0x(hex0x);
bytes memory hb = bytes(hexStr);
bytes memory raw = _hexToBytes(hb);
uint256 bestStart = type(uint256).max;
uint256 bestLen = 0;
(bool found, string memory pushResult) = _extractFromPushInstructions(raw);
if (found) {
bytes memory pushBytes = bytes(pushResult);
bestLen = pushBytes.length;
for (uint256 i = 0; i <= raw.length - bestLen; i++) {
bool matched = true;
for (uint256 j = 0; j < bestLen; j++) {
if (raw[i + j] != pushBytes[j]) {
matched = false;
break;
}
}
if (matched) {
bestStart = i;
break;
}
}
}
if (bestStart == type(uint256).max) {
uint256 i = 0;
while (i < raw.length) {
if (raw[i] >= 0x61 && raw[i] <= 0x7a) {
uint256 j = i + 1;
while (j < raw.length && raw[j] >= 0x61 && raw[j] <= 0x7a) { unchecked { ++j; } }
uint256 len = j - i;
if (len >= 6 && len <= 15 && len > bestLen) {
bytes memory candidate = new bytes(len);
for (uint256 k = 0; k < len; k++) {
candidate[k] = raw[i + k];
}
if (_isValidWord(candidate)) {
bestLen = len;
bestStart = i;
}
}
i = j;
} else { unchecked { ++i; } }
}
}
uint256 pairs = hb.length / 2;
uint256 leftPairs = pairs / 2;
uint256 split = leftPairs * 2;
string memory runHex = "";
uint256 runPosHex = type(uint256).max;
if (bestLen >= 6) {
runHex = _bytesToHex(raw, bestStart, bestLen);
runPosHex = LibString.indexOf(hexStr, runHex);
if (runPosHex <= split && split < runPosHex + bytes(runHex).length) {
split = runPosHex + bytes(runHex).length;
if ((split & 1) == 1) split++;
if (split > hb.length) split = hb.length;
}
}
string memory left = LibString.slice(hexStr, 0, split);
string memory right = LibString.slice(hexStr, split);
string memory leftG = _groupPairsHex(left);
string memory rightG = _groupPairsHex(right);
if (runPosHex != type(uint256).max) {
string memory runHexG = _groupPairsHex(runHex);
string memory wrapped = string.concat("<span class=\"hl\">", runHexG, "</span>");
if (runPosHex < split) {
leftG = _replaceOnce(leftG, runHexG, wrapped);
} else {
rightG = _replaceOnce(rightG, runHexG, wrapped);
}
}
return string.concat(
"<div class=\"cols\"><div class=\"col\">", leftG, "</div><div class=\"col\">", rightG, "</div></div>"
);
}
function _groupPairsHex(string memory hexWithOpt0x) internal pure returns (string memory) {
bytes memory s = bytes(hexWithOpt0x);
uint256 start = 0;
bool has0x = false;
if (s.length >= 2 && s[0] == "0" && (s[1] == "x" || s[1] == "X")) { has0x = true; start = 2; }
uint256 hexLen = s.length - start;
if (hexLen == 0) return hexWithOpt0x;
uint256 pairs = hexLen / 2;
uint256 spaces = pairs > 0 ? pairs - 1 : 0;
uint256 outLen = (has0x ? 3 : 0) + hexLen + spaces;
bytes memory out = new bytes(outLen);
uint256 idx = 0;
if (has0x) { out[idx++] = "0"; out[idx++] = "x"; out[idx++] = 0x20; }
for (uint256 i = 0; i < hexLen; i += 2) {
out[idx++] = s[start + i];
if (i + 1 < hexLen) out[idx++] = s[start + i + 1];
if (i + 2 < hexLen) out[idx++] = 0x20;
}
return string(out);
}
function _replaceOnce(string memory s, string memory needle, string memory replacement) internal pure returns (string memory) {
uint256 p = LibString.indexOf(s, needle);
if (p == type(uint256).max) return s;
string memory head = LibString.slice(s, 0, p);
string memory tail = LibString.slice(s, p + bytes(needle).length);
return string.concat(head, replacement, tail);
}
function _strip0x(string memory s) internal pure returns (string memory) {
bytes memory bs = bytes(s);
if (bs.length >= 2 && bs[0] == "0" && (bs[1] == "x" || bs[1] == "X")) {
bytes memory out = new bytes(bs.length - 2);
for (uint256 k = 2; k < bs.length; ++k) out[k-2] = bs[k];
return string(out);
}
return s;
}
function _hexToBytes(bytes memory hexChars) internal pure returns (bytes memory) {
require(hexChars.length % 2 == 0, "hex length");
bytes memory out = new bytes(hexChars.length / 2);
for (uint256 k = 0; k < out.length; ++k) {
out[k] = bytes1(( _nibble(hexChars[2*k]) << 4 ) | _nibble(hexChars[2*k+1]));
}
return out;
}
function _bytesToHex(bytes memory data, uint256 start, uint256 len) internal pure returns (string memory) {
bytes memory out = new bytes(len * 2);
for (uint256 k = 0; k < len; ++k) {
uint8 b = uint8(data[start + k]);
out[2*k] = _hexChar(b >> 4);
out[2*k + 1] = _hexChar(b & 0x0f);
}
return string(out);
}
function _nibble(bytes1 c) private pure returns (uint8) {
uint8 u = uint8(c);
if (u >= 48 && u <= 57) return u - 48;
if (u >= 65 && u <= 70) return u - 55;
if (u >= 97 && u <= 102) return u - 87;
revert("bad hex");
}
function _hexChar(uint8 nib) private pure returns (bytes1) {
return bytes1(nib + (nib < 10 ? 48 : 87));
}
function _toString(uint256 value) internal pure returns (string memory str) {
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;
}
str = string(buffer);
}
function _escapeHtml(string memory input) internal pure returns (string memory) {
string memory result = LibString.replace(input, "&", "&");
result = LibString.replace(result, "<", "<");
result = LibString.replace(result, ">", ">");
result = LibString.replace(result, "\"", """);
result = LibString.replace(result, "'", "'");
return result;
}
}
"
},
"contracts/utils/ENSResolver.sol": {
"content": "// SPDX-License-Identifier: MIT
// Author: @yigitduman
// Source: https://github.com/ygtdmn/drakeflipping/blob/main/src/DrakeflippingRenderer.sol
pragma solidity >=0.8.0;
import "solady/src/utils/LibString.sol";
import { ENS } from "@ensdomains/ens-contracts/contracts/registry/ENS.sol";
import { IReverseRegistrar } from "@ensdomains/ens-contracts/contracts/reverseRegistrar/IReverseRegistrar.sol";
import { INameResolver } from "@ensdomains/ens-contracts/contracts/resolvers/profiles/INameResolver.sol";
library ENSResolver {
/**
* @notice Retrieves the ENS name or checksummed address for a given address.
* @param addr The address to retrieve the ENS name or checksummed address for.
* @return The ENS name if available, otherwise the checksummed address.
*/
function resolveAddress(address addr) external view returns (string memory) {
ENS ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
IReverseRegistrar reverseRegistrar = IReverseRegistrar(0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb);
bytes32 node = reverseRegistrar.node(addr);
address resolverAddress = ens.resolver(node);
if (resolverAddress != address(0)) {
// If the resolver is not the zero address, try to get the name from the resolver
try INameResolver(resolverAddress).name(node) returns (string memory name) {
// If a name is found and it's not empty, return it
if (bytes(name).length > 0) {
return name;
}
} catch {}
}
// If no valid name is found, return the address as a string
return LibString.toHexStringChecksummed(addr);
}
}
"
},
"solady/src/utils/LibBytes.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for byte related operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol)
library LibBytes {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Goated bytes storage struct that totally MOGs, no cap, fr.
/// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af.
/// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight.
struct BytesStorage {
bytes32 _spacer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the bytes.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STORAGE OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sets the value of the bytes storage `$` to `s`.
function set(BytesStorage storage $, bytes memory s) internal {
/// @solidity memory-safe-assembly
assembly {
let n := mload(s)
let packed := or(0xff, shl(8, n))
for { let i := 0 } 1 {} {
if iszero(gt(n, 0xfe)) {
i := 0x1f
packed := or(n, shl(8, mload(add(s, i))))
if iszero(gt(n, i)) { break }
}
let o := add(s, 0x20)
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), mload(add(o, i)))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to `s`.
function setCalldata(BytesStorage storage $, bytes calldata s) internal {
/// @solidity memory-safe-assembly
assembly {
let packed := or(0xff, shl(8, s.length))
for { let i := 0 } 1 {} {
if iszero(gt(s.length, 0xfe)) {
i := 0x1f
packed := or(s.length, shl(8, shr(8, calldataload(s.offset))))
if iszero(gt(s.length, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
sstore(add(p, shr(5, i)), calldataload(add(s.offset, i)))
i := add(i, 0x20)
if iszero(lt(i, s.length)) { break }
}
break
}
sstore($.slot, packed)
}
}
/// @dev Sets the value of the bytes storage `$` to the empty bytes.
function clear(BytesStorage storage $) internal {
delete $._spacer;
}
/// @dev Returns whether the value stored is `$` is the empty bytes "".
function isEmpty(BytesStorage storage $) internal view returns (bool) {
return uint256($._spacer) & 0xff == uint256(0);
}
/// @dev Returns the length of the value stored in `$`.
function length(BytesStorage storage $) internal view returns (uint256 result) {
result = uint256($._spacer);
/// @solidity memory-safe-assembly
assembly {
let n := and(0xff, result)
result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n))))
}
}
/// @dev Returns the value stored in `$`.
function get(BytesStorage storage $) internal view returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let o := add(result, 0x20)
let packed := sload($.slot)
let n := shr(8, packed)
for { let i := 0 } 1 {} {
if iszero(eq(or(packed, 0xff), packed)) {
mstore(o, packed)
n := and(0xff, packed)
i := 0x1f
if iszero(gt(n, i)) { break }
}
mstore(0x00, $.slot)
for { let p := keccak256(0x00, 0x20) } 1 {} {
mstore(add(o, i), sload(add(p, shr(5, i))))
i := add(i, 0x20)
if iszero(lt(i, n)) { break }
}
break
}
mstore(result, n) // Store the length of the memory.
mstore(add(o, n), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(o, n), 0x20)) // Allocate memory.
}
}
/// @dev Returns the uint8 at index `i`. If out-of-bounds, returns 0.
function uint8At(BytesStorage storage $, uint256 i) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
for { let packed := sload($.slot) } 1 {} {
if iszero(eq(or(packed, 0xff), packed)) {
if iszero(gt(i, 0x1e)) {
result := byte(i, packed)
break
}
if iszero(gt(i, and(0xff, packed))) {
mstore(0x00, $.slot)
let j := sub(i, 0x1f)
result := byte(and(j, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, j))))
}
break
}
if iszero(gt(i, shr(8, packed))) {
mstore(0x00, $.slot)
result := byte(and(i, 0x1f), sload(add(keccak256(0x00, 0x20), shr(5, i))))
}
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTES OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
function replace(bytes memory subject, bytes memory needle, bytes memory replacement)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let needleLen := mload(needle)
let replacementLen := mload(replacement)
let d := sub(result, subject) // Memory difference.
let i := add(subject, 0x20) // Subject bytes pointer.
mstore(0x00, add(i, mload(subject))) // End of subject.
if iszero(gt(needleLen, mload(subject))) {
let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, needleLen), h)) {
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
j := add(j, 0x20)
if iszero(lt(j, replacementLen)) { break }
}
d := sub(add(d, replacementLen), needleLen)
if needleLen {
i := add(i, needleLen)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(add(i, d), t)
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
}
let end := mload(0x00)
let n := add(sub(d, add(result, 0x20)), end)
// Copy the rest of the bytes one word at a time.
for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
let o := add(i, d)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
for { let subjectLen := mload(subject) } 1 {} {
if iszero(mload(needle)) {
result := from
if iszero(gt(from, subjectLen)) { break }
result := subjectLen
break
}
let needleLen := mload(needle)
let subjectStart := add(subject, 0x20)
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
let s := mload(add(needle, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
if iszero(lt(needleLen, 0x20)) {
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right, starting from `from`. Optimized for byte needles.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOfByte(bytes memory subject, bytes1 needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0) // Initialize to `NOT_FOUND`.
if gt(mload(subject), from) {
let start := add(subject, 0x20)
let end := add(start, mload(subject))
let m := div(not(0), 255) // `0x0101 ... `.
let h := mul(byte(0, needle), m) // Replicating needle mask.
m := not(shl(7, m)) // `0x7f7f ... `.
for { let i := add(start, from) } 1 {} {
let c := xor(mload(i), h) // Load 32-byte chunk and xor with mask.
c := not(or(or(add(and(c, m), m), c), m)) // Each needle byte will be `0x80`.
if c {
c := and(not(shr(shl(3, sub(end, i)), not(0))), c) // Truncate bytes past the end.
if c {
let r := shl(7, lt(0x8421084210842108cc6318c6db6d54be, c)) // Save bytecode.
r := or(shl(6, lt(0xffffffffffffffff, shr(r, c))), r)
// forgefmt: disable-next-item
result := add(sub(i, start), shr(3, xor(byte(and(0x1f, shr(byte(24,
mul(0x02040810204081, shr(r, c))), 0x8421084210842108cc6318c6db6d54be)),
0xc0c8c8d0c8e8d0d8c8e8e0e8d0d8e0f0c8d0e8d0e0e0d8f0d0d0e0d8f8f8f8f8), r)))
break
}
}
i := add(i, 0x20)
if iszero(lt(i, end)) { break }
}
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right. Optimized for byte needles.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOfByte(bytes memory subject, bytes1 needle)
internal
pure
returns (uint256 result)
{
return indexOfByte(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) {
return indexOf(subject, needle, 0);
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let needleLen := mload(needle)
if gt(needleLen, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), needleLen)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
if eq(keccak256(subject, needleLen), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `needle` in `subject`,
/// needleing from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
function lastIndexOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256)
{
return lastIndexOf(subject, needle, type(uint256).max);
}
/// @dev Returns true if `needle` is found in `subject`, false otherwise.
function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) {
return indexOf(subject, needle) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `needle`.
function startsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
// Just using keccak256 directly is actually cheaper.
let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n))
result := lt(gt(n, mload(subject)), t)
}
}
/// @dev Returns whether `subject` ends with `needle`.
function endsWith(bytes memory subject, bytes memory needle)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(needle)
let notInRange := gt(n, mload(subject))
// `subject + 0x20 + max(subject.length - needle.length, 0)`.
let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n)))
// Just using keccak256 directly is actually cheaper.
result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(bytes memory subject, uint256 times)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(or(iszero(times), iszero(l))) {
result := mload(0x40)
subject := add(subject, 0x20)
let o := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let j := 0 } 1 {} {
mstore(add(o, j), mload(add(subject, j)))
j := add(j, 0x20)
if iszero(lt(j, l)) { break }
}
o := add(o, l)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, sub(o, add(result, 0x20))) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(bytes memory subject, uint256 start, uint256 end)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
let l := mload(subject) // Subject length.
if iszero(gt(l, end)) { end := l }
if iszero(gt(l, start)) { start := l }
if lt(start, end) {
result := mload(0x40)
let n := sub(end, start)
let i := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let j := and(add(n, 0x1f), w) } 1 {} {
mstore(add(result, j), mload(add(i, j)))
j := add(j, w) // `sub(j, 0x20)`.
if iszero(j) { break }
}
let o := add(add(result, 0x20), n)
mstore(o, 0) // Zeroize the slot after the bytes.
mstore(0x40, add(o, 0x20)) // Allocate memory.
mstore(result, n) // Store the length.
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset.
function slice(bytes memory subject, uint256 start)
internal
pure
returns (bytes memory result)
{
result = slice(subject, start, type(uint256).max);
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, end), sub(end, start))
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, subject.length), sub(subject.length, start))
}
}
/// @dev Reduces the size of `subject` to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncate(bytes memory subject, uint256 n)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := subject
mstore(mul(lt(n, mload(result)), result), n)
}
}
/// @dev Returns a copy of `subject`, with the length reduced to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncatedCalldata(bytes calldata subject, uint256 n)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
result.offset := subject.offset
result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n)))
}
}
/// @dev Returns all the indices of `needle` in `subject`.
/// The indices are byte offsets.
function indicesOf(bytes memory subject, bytes memory needle)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLen := mload(needle)
if iszero(gt(searchLen, mload(subject))) {
result := mload(0x40)
let i := add(subject, 0x20)
let o := add(result, 0x20)
let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
let h := 0 // The hash of `needle`.
if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
let s := mload(add(needle, 0x20))
for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
let t := mload(i)
// Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(i, searchLen), h)) {
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
o := add(o, 0x20)
i := add(i, searchLen) // Advance `i` by `searchLen`.
if searchLen {
if iszero(lt(i, subjectSearchEnd)) { break }
continue
}
}
i := add(i, 1)
if iszero(lt(i, subjectSearchEnd)) { break }
}
mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(o, 0x20))
}
}
}
/// @dev Returns an arrays of bytess based on the `delimiter` inside of the `subject` bytes.
function split(bytes memory subject, bytes memory delimiter)
internal
pure
returns (bytes[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
for { let prevIndex := 0 } 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let l := sub(index, prevIndex)
mstore(element, l) // Store the length of the element.
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(l, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes.
// Allocate memory for the length and the bytes, rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(l, 0x3f), w)))
mstore(indexPtr, element) // Store the `element` into the array.
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated bytes of `a` and `b`.
/// Cheaper than `bytes.concat()` and does not de-align the free memory pointer.
function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let w := not(0x1f)
let aLen := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLen, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLen := mload(b)
let output := add(result, aLen)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLen, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLen := add(aLen, bLen)
let last := add(add(result, 0x20), totalLen)
mstore(last, 0) // Zeroize the slot after the
Submitted on: 2025-09-26 10:40:01
Comments
Log in to comment.
No comments yet.