File 1 of 1: chonks.sol
pragma solidity ^0.8.19;
error ZeroValue();
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external;
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external;
function transferFrom(address sender, address recipient, uint256 amount) external;
}
interface ICHONKS {
function walletOfOwner(address) external view returns (uint256[] memory);
function safeTransferFrom(address, address, uint256) external;
function transferFrom(address, address, uint256) external;
}
contract Holder {
address constant chonksNft = 0x07152bfde079b5319e5308C43fB1Dbc9C76cb4F9;
address constant controller = 0x172ab1C8278Fc267D959682534f319609B6Dc258;
function withdraw(uint256 id, address to) external {
if (msg.sender != controller) {
revert();
}
ICHONKS(chonksNft).transferFrom(address(this), to, id);
}
}
contract HolderManager {
address constant self = 0x172ab1C8278Fc267D959682534f319609B6Dc258;
address constant clone = 0x6dF9D496f26f03ACf63977F68b95875de275C424;
function getHolder(uint256 id) internal pure returns(address predicted){
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), self)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), clone)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), id)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 0x55)
}
}
function createHolder(uint256 id) internal {
assembly {
mstore(0x00, or(shr(0xe8, shl(0x60, clone)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
mstore(0x20, or(shl(0x78, clone), 0x5af43d82803e903d91602b57fd5bf3))
let returnAddress := create2(0, 0x09, 0x37, id)
}
}
}
contract WrappedChonks is HolderManager {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event WrapChonk(address indexed from, uint256 indexed tokenId);
event UnwrapChonk(address indexed to, uint256 indexed tokenId);
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
address constant public chonksNft = 0x07152bfde079b5319e5308C43fB1Dbc9C76cb4F9;
string private _name = "Wrapped Chonks";
string private _symbol = "CHONKS";
uint256 constant private _decimals = 18;
uint256 private _supply = IERC20(chonksNft).totalSupply() * 10**_decimals;
uint256 constant private _oneToken = 10**_decimals;
uint256[] private _selfChonksIdQueue;
uint256 private _selfChonksQueueIndex;
mapping (uint256 => bool) private holderExists;
constructor () {
_balances[address(this)] = _supply;
emit Transfer(address(0), address(this), _supply);
}
function owner() public pure returns (address) {
return address(0);
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function decimals() public pure returns (uint256) {
return _decimals;
}
function totalSupply() public view returns (uint256) {
return _supply;
}
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public {
_transfer(msg.sender, recipient, amount);
}
function allowance(address holder, address spender) public view returns (uint256) {
return _allowances[holder][spender];
}
function approve(address spender, uint256 amount) public {
_approve(msg.sender, spender, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public {
if (sender != msg.sender) {
_approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
}
_transfer(sender, recipient, amount);
}
function _approve(address holder, address spender, uint256 amount) private {
_allowances[holder][spender] = amount;
emit Approval(holder, spender, amount);
}
function _transfer(address from, address to, uint256 amount) private {
if (amount == 0) revert ZeroValue();
_balances[from] -= amount;
_balances[to] += amount;
emit Transfer(from, to, amount);
}
function unwrapChonks(uint256 chonksAmount) external {
_transfer(msg.sender, address(this), chonksAmount * _oneToken);
for (; chonksAmount > 0; --chonksAmount) {
_unwrapChonk();
}
}
function _unwrapChonk() private {
uint256 unwrapId = _selfChonksIdQueue[_selfChonksQueueIndex];
Holder(getHolder(unwrapId)).withdraw(unwrapId, msg.sender);
delete _selfChonksIdQueue[_selfChonksQueueIndex];
unchecked {
++_selfChonksQueueIndex;
}
emit UnwrapChonk(msg.sender, unwrapId);
}
function wrappedIds(uint256 amount) public view returns(uint256[] memory) {
uint256 totalLength = _selfChonksIdQueue.length - _selfChonksQueueIndex;
if (amount == 0 || amount > totalLength) {
amount = totalLength;
}
uint256[] memory storedIds = new uint256[](amount);
for (uint256 i; i < amount; ++i) {
unchecked {
storedIds[i] = _selfChonksIdQueue[_selfChonksQueueIndex+i];
}
}
return storedIds;
}
function wrappedChonksAmount() public view returns(uint256) {
return _selfChonksIdQueue.length - _selfChonksQueueIndex;
}
function onERC721Received(address _address, address from, uint256 tokenId, bytes calldata _data) external returns (bytes4) {
if (msg.sender != chonksNft) {
revert();
}
if (!holderExists[tokenId]) {
createHolder(tokenId);
holderExists[tokenId] = true;
}
ICHONKS(chonksNft).transferFrom(address(this), getHolder(tokenId), tokenId);
_selfChonksIdQueue.push(tokenId);
emit WrapChonk(from, tokenId);
_transfer(address(this), from, _oneToken);
return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
}
}