G_data:application/vnd.facet.tx+json;rule=esip6,{"op":"create","data":{"source_code":"pragma(:rubidity, \"1.0.0\")\ncontract(:ERC20, abstract: true) {\n event(:Transfer, { from: :address, to: :address, amount: :uint256 })\n event(:Approval, { owner: :address, spender: :address, amount: :uint256 })\n string(:public, :name)\n string(:public, :symbol)\n uint8(:public, :decimals)\n uint256(:public, :totalSupply)\n mapping(({ address: :uint256 }), :public, :balanceOf)\n mapping(({ address: mapping(address: :uint256) }), :public, :allowance)\n constructor(name: :string, symbol: :string, decimals: :uint8) {\n s.name=name\n s.symbol=symbol\n s.decimals=decimals\n }\n function(:approve, { spender: :address, amount: :uint256 }, :public, :virtual, returns: :bool) {\n s.allowance[msg.sender][spender] = amount\n emit(:Approval, owner: msg.sender, spender: spender, amount: amount)\n return true\n }\n function(:transfer, { to: :address, amount: :uint256 }, :public, :virtual, returns: :bool) {\n require(s.balanceOf[msg.sender] \u003e= amount, \"Insufficient balance\")\n s.balanceOf[msg.sender] -= amount\n s.balanceOf[to] += amount\n emit(:Transfer, from: msg.sender, to: to, amount: amount)\n return true\n }\n function(:transferFrom, { from: :address, to: :address, amount: :uint256 }, :public, :virtual, returns: :bool) {\n allowed = s.allowance[from][msg.sender]\n require(s.balanceOf[from] \u003e= amount, \"Insufficient balance\")\n require(allowed \u003e= amount, \"Insufficient allowance\")\n s.allowance[from][msg.sender] = allowed - amount\n s.balanceOf[from] -= amount\n s.balanceOf[to] += amount\n emit(:Transfer, from: from, to: to, amount: amount)\n return true\n }\n function(:_mint, { to: :address, amount: :uint256 }, :internal, :virtual) {\n s.totalSupply += amount\n s.balanceOf[to] += amount\n emit(:Transfer, from: address(0), to: to, amount: amount)\n }\n function(:_burn, { from: :address, amount: :uint256 }, :internal, :virtual) {\n require(s.balanceOf[from] \u003e= amount, \"Insufficient balance\")\n s.balanceOf[from] -= amount\n s.totalSupply -= amount\n emit(:Transfer, from: from, to: address(0), amount: amount)\n }\n}\ncontract(:ERC721, abstract: true) {\n event(:Transfer, { from: :address, to: :address, id: :uint256 })\n event(:Approval, { owner: :address, spender: :address, id: :uint256 })\n event(:ApprovalForAll, { owner: :address, operator: :address, approved: :bool })\n string(:public, :name)\n string(:public, :symbol)\n mapping(({ uint256: :address }), :internal, :_ownerOf)\n mapping(({ address: :uint256 }), :internal, :_balanceOf)\n mapping(({ uint256: :address }), :public, :getApproved)\n mapping(({ address: mapping(address: :bool) }), :public, :isApprovedForAll)\n constructor(name: :string, symbol: :string) {\n s.name=name\n s.symbol=symbol\n }\n function(:ownerOf, { id: :uint256 }, :public, :view, :virtual, returns: :address) {\n owner = s._ownerOf[id]\n require(owner != address(0), \"ERC721: owner query for nonexistent token\")\n return owner\n }\n function(:balanceOf, { owner: :address }, :public, :view, :virtual, returns: :uint256) {\n require(owner != address(0), \"ERC721: balance query for nonexistent owner\")\n return s._balanceOf[owner]\n }\n function(:approve, { spender: :address, id: :uint256 }, :public, :virtual) {\n owner = ownerOf(id)\n require(msg.sender == owner || s.isApprovedForAll[owner][msg.sender], \"ERC721: msg.sender not authorized to approve\")\n s.getApproved[id] = spender\n emit(:Approval, owner: owner, spender: spender, id: id)\n }\n function(:setApprovalForAll, { operator: :address, approved: :bool }, :public, :virtual) {\n s.isApprovedForAll[msg.sender][operator] = approved\n emit(:ApprovalForAll, owner: msg.sender, operator: operator, approved: approved)\n }\n function(:transferFrom, { from: :address, to: :address, id: :uint256 }, :public, :virtual) {\n require(from == ownerOf(id), \"ERC721: transfer of token that is not own\")\n require(to != address(0), \"ERC721: transfer to the zero address\")\n require(isApprovedOrOwner(spender: msg.sender, id: id), \"ERC721: msg.sender not authorized to call transferFrom\")\n s._balanceOf[from] -= 1\n s._balanceOf[to] += 1\n s._ownerOf[id] = to\n s.getApproved[id] = address(0)\n emit(:Transfer, from: from, to: to, id: id)\n }\n function(:isApprovedOrOwner, { spender: :address, id: :uint256 }, :public, :view, :virtual, returns: :bool) {\n owner = ownerOf(id)\n spender == owner || s.getApproved[id] == spender || s.isApprovedForAll[owner][spender]\n }\n function(:_exists, { id: :uint256 }, :internal, :view, :virtual, returns: :bool) {\n return s._ownerOf[id] != address(0)\n }\n function(:_mint, { to: :address, id: :uint256 }, :internal, :virtual) {\n require(to != address(0), \"ERC721: mint to the zero address\")\n require(s._ownerOf[id] == address(0), \"ERC721: token already minted\")\n s._balanceOf[to] += 1\n s._ownerOf[id] = to\n emit(:Transfer, from: address(0), to: to, id: id)\n }\n function(:_burn, { id: :uint256 }, :internal, :virtual) {\n owner = s._ownerOf[id]\n require(owner != address(0), \"ERC721: burn of nonexistent token\")\n s._balanceOf[owner] -= 1\n s._ownerOf[id] = address(0)\n s.getApproved[id] = address(0)\n emit(:Transfer, from: owner, to: address(0), id: id)\n }\n function(:tokenURI, { id: :uint256 }, :public, :view, :virtual, returns: :string) {\n }\n}\ncontract(:INFTCollection01, abstract: true) {\n function(:owner, :external, :view, returns: :address)\n}\ncontract(:Upgradeable, abstract: true) {\n address(:public, :upgradeAdmin)\n event(:ContractUpgraded, { oldHash: :bytes32, newHash: :bytes32 })\n event(:UpgradeAdminChanged, { newUpgradeAdmin: :address })\n constructor(upgradeAdmin: :address) {\n s.upgradeAdmin=upgradeAdmin\n }\n function(:setUpgradeAdmin, { newUpgradeAdmin: :address }, :public) {\n require(msg.sender == s.upgradeAdmin, \"NOT_AUTHORIZED\")\n s.upgradeAdmin=newUpgradeAdmin\n emit(:UpgradeAdminChanged, newUpgradeAdmin: newUpgradeAdmin)\n }\n function(:upgradeAndCall, { newHash: :bytes32, newSource: :string, migrationCalldata: :string }, :public) {\n upgrade(newHash: newHash, newSource: newSource)\n (success, data) = address(this).call(migrationCalldata)\n require(success, \"Migration failed\")\n }\n function(:upgrade, { newHash: :bytes32, newSource: :string }, :public) {\n currentHash = this.currentInitCodeHash\n require(msg.sender == s.upgradeAdmin, \"NOT_AUTHORIZED\")\n this.upgradeImplementation(newHash, newSource)\n emit(:ContractUpgraded, oldHash: currentHash, newHash: newHash)\n }\n}\ncontract(:TokenUpgradeRenderer02, is: :Upgradeable, upgradeable: true) {\n event(:CollectionInitialized, { collection: :address, contractInfo: :ContractInfo, initialLevel: :TokenUpgradeLevel })\n event(:UpgradeLevelUpdated, { collection: :address, index: :uint256, name: :string, imageURI: :string, animationURI: :string, startTime: :uint256, endTime: :uint256, newRecord: :bool })\n event(:TokenUpgraded, { collection: :address, tokenId: :uint256, upgradeLevel: :uint256 })\n event(:ContractInfoUpdated, { collection: :address, newInfo: :ContractInfo })\n struct(:TokenUpgradeLevel) {\n string(:name)\n string(:imageURI)\n string(:animationURI)\n string(:extraAttributesJson)\n uint256(:startTime)\n uint256(:endTime)\n }\n struct(:TokenStatus) {\n uint256(:upgradeLevel)\n uint256(:lastUpgradeTime)\n }\n struct(:ContractInfo) {\n string(:name)\n string(:description)\n string(:imageURI)\n }\n mapping(({ address: array(:TokenUpgradeLevel, 1) }), :public, :tokenUpgradeLevelsByCollection)\n mapping(({ address: mapping(uint256: :TokenStatus) }), :public, :tokenStatusByCollection)\n mapping(({ address: :ContractInfo }), :public, :contractInfoByCollection)\n mapping(({ address: mapping(uint256: array(:string)) }), :public, :tokenUpgradeLevelImageURIsByCollection)\n mapping(({ address: mapping(uint256: :bytes32) }), :public, :blockHashByTokenLevelByCollection)\n uint256(:public, :perUpgradeFee)\n address(:public, :feeTo)\n address(:public, :WETH)\n uint256(:public, :maxUpgradeLevelCount)\n constructor(perUpgradeFee: :uint256, feeTo: :address, weth: :address) {\n self.Upgradeable.constructor(upgradeAdmin: msg.sender)\n s.maxUpgradeLevelCount=30\n s.perUpgradeFee=perUpgradeFee\n s.feeTo=feeTo\n s.WETH=weth\n }\n function(:addUpgradeLevel, { collection: :address, newLevel: :TokenUpgradeLevel, imageURIs: [:string] }, :public) {\n requireSenderAdmin(collection)\n lastLevel = s.tokenUpgradeLevelsByCollection[collection].last\n require(newLevel.endTime \u003e newLevel.startTime, \"End time must be after start time\")\n require(newLevel.startTime \u003e lastLevel.endTime, \"Start time must be after last level end time\")\n require(s.tokenUpgradeLevelsByCollection[collection].length + 1 \u003c= s.maxUpgradeLevelCount, \"Max upgrade level count reached\")\n require(imageURIs.length \u003c= 25, \"Max 25 image URIs allowed\")\n s.tokenUpgradeLevelsByCollection[collection].push(newLevel)\n index = s.tokenUpgradeLevelsByCollection[collection].length - 1\n s.tokenUpgradeLevelImageURIsByCollection[collection][index] = imageURIs\n s.blockHashByTokenLevelByCollection[collection][index] = blockhash(block.number)\n emit(:UpgradeLevelUpdated, collection: collection, index: s.tokenUpgradeLevelsByCollection[collection].length - 1, name: newLevel.name, imageURI: newLevel.imageURI, animationURI: newLevel.animationURI, startTime: newLevel.startTime, endTime: newLevel.endTime, newRecord: true)\n }\n function(:editUpgradeLevel, { collection: :address, index: :uint256, newLevel: :TokenUpgradeLevel, imageURIs: [:string] }, :public) {\n requireSenderAdmin(collection)\n require(imageURIs.length \u003c= 25, \"Max 25 image URIs allowed\")\n editingFirstLevel = index == 0\n editingLastLevel = index == s.tokenUpgradeLevelsByCollection[collection].length.-(1)\n unless editingLastLevel\n nextLevel = s.tokenUpgradeLevelsByCollection[collection][index + 1]\n require(newLevel.endTime \u003c nextLevel.startTime, \"End time must be before next level start time\")\n end\n if editingFirstLevel\n newLevel.startTime=0\n newLevel.endTime=0\n else\n precedingLevel = s.tokenUpgradeLevelsByCollection[collection][index - 1]\n require(newLevel.startTime \u003e precedingLevel.endTime, \"Start time must be after preceding level end time\")\n require(newLevel.endTime \u003e newLevel.startTime, \"End time must be after start time\")\n end\n s.tokenUpgradeLevelsByCollection[collection][index] = newLevel\n s.tokenUpgradeLevelImageURIsByCollection[collection][index] = imageURIs\n emit(:UpgradeLevelUpdated, collection: collection, index: index, name: newLevel.name, imageURI: newLevel.imageURI, animationURI: newLevel.animationURI, startTime: newLevel.startTime, endTime: newLevel.endTime, newRecord: false)\n }\n function(:activeUpgradeLevelIndex, { collection: :address }, :public, :view, returns: :uint256) {\n forLoop(condition: -\u003e(i) {\n i \u003c s.tokenUpgradeLevelsByCollection[collection].length\n }) { |i|\n level = s.tokenUpgradeLevelsByCollection[collection][i]\n if level.startTime \u003c= block.timestamp \u0026\u0026 level.endTime \u003e block.timestamp\n return i\n else\n if level.startTime \u003e block.timestamp\n return 0\n end\n end\n }\n return 0\n }\n function(:activeUpgradeLevel, { collection: :address }, :public, :view, returns: :TokenUpgradeLevel) {\n index = activeUpgradeLevelIndex(collection)\n return index == 0 ? TokenUpgradeLevel() : s.tokenUpgradeLevelsByCollection[collection][index]\n }\n function(:_upgradeToken, { collection: :address, tokenId: :uint256, activeUpgrade: :TokenUpgradeLevel }, :internal) {\n require(ERC721(collection).isApprovedOrOwner(spender: msg.sender, id: tokenId), \"TokenUpgradeRenderer: msg.sender not authorized to upgrade id #{tokenId.toString}\")\n tokenStatus = s.tokenStatusByCollection[collection][tokenId]\n require(tokenStatus.lastUpgradeTime \u003c activeUpgrade.startTime, \"TokenUpgradeRenderer: Token already upgraded during this period\")\n targetLevelIndex = tokenStatus.upgradeLevel + 1\n require(targetLevelIndex \u003c s.tokenUpgradeLevelsByCollection[collection].length, \"TokenUpgradeRenderer: No more upgrade levels\")\n tokenStatus.upgradeLevel=targetLevelIndex\n tokenStatus.lastUpgradeTime=block.timestamp\n emit(:TokenUpgraded, collection: collection, tokenId: tokenId, upgradeLevel: tokenStatus.upgradeLevel)\n }\n function(:upgradeMultipleTokens, { collection: :address, tokenIds: [:uint256] }, :public) {\n require(tokenIds.length \u003c= 100, \"TokenUpgradeRenderer: Cannot upgrade more than 50 tokens at once\")\n totalFee = s.perUpgradeFee * tokenIds.length\n if totalFee \u003e 0 \u0026\u0026 s.feeTo != address(0)\n ERC20(s.WETH).transferFrom(msg.sender, s.feeTo, totalFee)\n end\n activeUpgradeIndex = activeUpgradeLevelIndex(collection)\n require(activeUpgradeIndex \u003e 0, \"TokenUpgradeRenderer: No active upgrade level\")\n activeUpgrade = s.tokenUpgradeLevelsByCollection[collection][activeUpgradeIndex]\n forLoop(condition: -\u003e(i) {\n i \u003c tokenIds.length\n }) { |i|\n _upgradeToken(collection: collection, tokenId: tokenIds[i], activeUpgrade: activeUpgrade)\n }\n }\n function(:setContractInfo, { collection: :address, info: :ContractInfo }, :public) {\n requireSenderAdmin(collection)\n s.contractInfoByCollection[collection] = info\n emit(:ContractInfoUpdated, collection: collection, newInfo: info)\n }\n function(:lastUpgradeLevel, { collection: :address, tokenId: :uint256 }, :public, :view, returns: :TokenUpgradeLevel) {\n status = s.tokenStatusByCollection[collection][tokenId]\n upgradeTime = status.lastUpgradeTime\n if upgradeTime == 0\n return TokenUpgradeLevel()\n end\n forLoop(condition: -\u003e(i) {\n i \u003c s.tokenUpgradeLevelsByCollection[collection].length\n }) { |i|\n level = s.tokenUpgradeLevelsByCollection[collection][i]\n if level.startTime \u003c= upgradeTime \u0026\u0026 level.endTime \u003e upgradeTime\n return level\n end\n }\n return TokenUpgradeLevel()\n }\n function(:tokenURI, { tokenId: :uint256 }, :external, :view, returns: :string) {\n collection = msg.sender\n status = s.tokenStatusByCollection[collection][tokenId]\n upgradeLevel = s.tokenUpgradeLevelsByCollection[collection][status.upgradeLevel]\n name_json = json.stringify(\"#{upgradeLevel.name} ##{tokenId.toString}\")\n description_json = json.stringify(s.contractInfoByCollection[collection].description)\n uriAryLength = s.tokenUpgradeLevelImageURIsByCollection[collection][status.upgradeLevel].length\n imageURI = if uriAryLength \u003e 0\n blockhash = s.blockHashByTokenLevelByCollection[collection][status.upgradeLevel]\n entropy = uint256(keccak256(abi.encodePacked(collection, blockhash, tokenId)))\n s.tokenUpgradeLevelImageURIsByCollection[collection][status.upgradeLevel][entropy % uriAryLength]\n else\n upgradeLevel.imageURI\n end\n image_field = if imageURI.length == 0\n \"\"\n else\n \"\\\"image\\\": #{json.stringify(imageURI)},\\n\"\n end\n animation_url_field = if upgradeLevel.animationURI.length == 0\n \"\"\n else\n \"\\\"animation_url\\\": #{json.stringify(upgradeLevel.animationURI)},\\n\"\n end\n last_level = lastUpgradeLevel(collection: collection, tokenId: tokenId)\n last_upgrade_level_json = if last_level != TokenUpgradeLevel()\n \", {\\\"trait_type\\\": \\\"Last Upgrade Level\\\", \\\"value\\\": #{json.stringify(last_level.name)}}\\n\"\n else\n \"\"\n end\n extra_attributes_json = if upgradeLevel.extraAttributesJson != \"\"\n \", \" + upgradeLevel.extraAttributesJson\n else\n \"\"\n end\n json_data = \u003c\u003c-HEREDOC\n {\n \"name\": #{name_json},\n \"description\": #{description_json},\n #{image_field}\n #{animation_url_field}\n \"attributes\": [\n {\"trait_type\": \"Number\", \"display_type\": \"number\", \"value\": #{tokenId.toString}},\n {\"trait_type\": \"Level\", \"value\": #{json.stringify(upgradeLevel.name)}}\n #{last_upgrade_level_json}\n #{extra_attributes_json}\n ]\n }\n HEREDOC\n \"data:application/json;base64,\" + json_data.base64Encode\n }\n function(:initializeWithData, { contractInfo: :ContractInfo, initialLevel: :TokenUpgradeLevel }, :external) {\n setContractInfo(collection: msg.sender, info: contractInfo)\n editUpgradeLevel(collection: msg.sender, index: 0, newLevel: initialLevel)\n emit(:CollectionInitialized, collection: msg.sender, contractInfo: contractInfo, initialLevel: initialLevel)\n }\n function(:contractURI, :external, :view, returns: :string) {\n collection = msg.sender\n contractInfo = s.contractInfoByCollection[collection]\n json_data = json.stringify(name: contractInfo.name, description: contractInfo.description, image: contractInfo.imageURI)\n \"data:application/json;base64,\" + json_data.base64Encode\n }\n function(:upgradeLevelCount, { collection: :address }, :public, :view, returns: :uint256) {\n return s.tokenUpgradeLevelsByCollection[collection].length\n }\n function(:requireSenderAdmin, { target: :address }, :internal, :view) {\n require(target == msg.sender || INFTCollection01(target).owner == msg.sender, \"Admin access only\")\n }\n function(:setFeeTo, { feeTo: :address }, :public) {\n require(msg.sender == s.feeTo, \"Only feeTo can change feeTo\")\n s.feeTo=feeTo\n nil\n }\n}\n","init_code_hash":"0x048cc2bb0751a06b409ab438941182f7c4e753eb2b3c5a40b338d7c44d7b43d1","args":{"perUpgradeFee":0,"feeTo":"0x0000000000000000000000000000000000000000","weth":"0x0000000000000000000000000000000000000000"}}}