// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/Base64.sol"; import "./interfaces/INftTemplateContract.sol"; contract TixSellNftTemplate is Ownable, AccessControl { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); using Strings for uint256; // Mappings modifier onlyFounders() { require(hasRole(ADMIN_ROLE, msg.sender), "Only founders can do that"); _; } modifier onlyAdmin() { require( msg.sender == owner() || hasRole(ADMIN_ROLE, msg.sender), "Only admins can do that" ); _; } mapping(uint256 => address) templateSmartContract; //TemplateId => Address constructor( address initialOwner, address[] memory _admins ) Ownable(initialOwner) { for (uint256 i = 0; i < _admins.length; ++i) { _grantRole(ADMIN_ROLE, _admins[i]); _grantRole(DEFAULT_ADMIN_ROLE, _admins[i]); } } function addTemplate( uint256 _templateId, address _smartContract ) external onlyAdmin returns (bool done) { require(_smartContract != address(0), "address cant be null"); templateSmartContract[_templateId] = _smartContract; return true; } // Renvoi le % de comission pris en fonction du type de NFT // get event & ticket information // based on ticketType get SVG function getURI( address _ticketAddress, TixSellLibrary.NftTicketInfo memory _nftTicketInfo, bool revealed ) public view returns (string memory finalSVG) { // UTiliser un tableau de mapping avec l'addresse des smarts contracts correspondant à chaque type // On passe les elements du ticket au smart contract et on récupère le SVG //Définir une interface pour les smartsContractTemplate if (_nftTicketInfo.templateId == 1 || _nftTicketInfo.templateId == 2 || _nftTicketInfo.templateId == 4) { require( templateSmartContract[_nftTicketInfo.templateId] != address(0), "No template for this id" ); } //require que celui a l'orgine de la transaction soit _ticketAddressSmartContract (pour pas se fabriquer des billets en externe) require( msg.sender == _ticketAddress || hasRole(ADMIN_ROLE, msg.sender), "Not authorized" ); INftTemplateContract nftTemplateSmartContract = INftTemplateContract( templateSmartContract[_nftTicketInfo.templateId] ); string memory libelle = string.concat( _nftTicketInfo.ticketDesignInfo.ticketType, " #" ); string memory name = string.concat( libelle, _nftTicketInfo.tokenId.toString() ); //Attributs : freeDrink , priorityQueue string memory freeDrink = "false"; if (_nftTicketInfo.freeDrink) { freeDrink = "true"; } string memory priorityQueue = "false"; if (_nftTicketInfo.priorityQueue) { priorityQueue = "true"; } string memory canStream = "false"; if (_nftTicketInfo.canStream) { canStream = "true"; } string memory sellable = "false"; string memory description =""; if (_nftTicketInfo.sellable) { sellable = "true"; description = unicode"Billet revendable. Uniquement utilisable sur www.selltix.live. Ticket sellable. Only usable on www.selltix.live. Retrouvez tous nos événements sur / Find all our events on www.selltix.live/evenements-public"; } else{ description = unicode"Billet non revendable. Uniquement utilisable sur www.selltix.live. Ticket not sellable. Only usable on www.selltix.live. Retrouvez tous nos événements sur / Find all our events on www.selltix.live/evenements-public"; } // Ticket Premium l'image ne peut pas être on chain en tant que SVG est donc url vers fichier pinata if (revealed == false) { return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( abi.encodePacked( '{"name":"', name, '", "description":"', description, '", "image": "', _nftTicketInfo.image, //hidden URI '", "attributes": ', '[{"trait_type":"freeDrink","value":"', freeDrink, '"},{"trait_type":"priorityQueue","value":"', priorityQueue, '"},{"trait_type":"sellable","value":"', sellable, '"},{"trait_type":"canStream","value":"', canStream, '"}]', "}" ) ) ) ) ); } else { if ( _nftTicketInfo.templateId == 3 ) { //Change to return a build image too return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( abi.encodePacked( '{"name":"', name, '", "description":"', description, '", "image": "', _nftTicketInfo.ticketDesignInfo.svgUrl, '", "attributes": ', '[{"trait_type":"freeDrink","value":"', freeDrink, '"},{"trait_type":"priorityQueue","value":"', priorityQueue, '"},{"trait_type":"sellable","value":"', sellable, '"},{"trait_type":"canStream","value":"', canStream, '"}]', "}" ) ) ) ) ); } else { //TicketType 1,2 et 4 string memory svgImage = nftTemplateSmartContract.buildImage( address(this), _nftTicketInfo ); return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( abi.encodePacked( '{"name":"', name, '", "description":"', description, '", "image": "', "data:image/svg+xml;base64,", svgImage, '", "attributes": ', '[{"trait_type":"freeDrink","value":"', freeDrink, '"},{"trait_type":"priorityQueue","value":"', priorityQueue, '"},{"trait_type":"sellable","value":"', sellable, '"},{"trait_type":"canStream","value":"', canStream, '"}]', "}" ) ) ) ) ); } } } function weiToEtherString( uint256 amountInWei ) public pure returns (string memory) { uint256 amountInFinney = amountInWei / 1e15; // 1 finney == 1e15 return string( abi.encodePacked( Strings.toString(amountInFinney / 1000), //left of decimal ".", Strings.toString((amountInFinney % 1000) / 100), //first decimal Strings.toString(((amountInFinney % 1000) % 100) / 10) // first decimal ) ); } function addressToString(address x) internal pure returns (string memory) { bytes memory s = new bytes(40); for (uint256 i = 0; i < 20; i++) { bytes1 b = bytes1( uint8(uint256(uint160(x)) / (2 ** (8 * (19 - i)))) ); bytes1 hi = bytes1(uint8(b) / 16); bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); s[2 * i] = char(hi); s[2 * i + 1] = char(lo); } return string(s); } function char(bytes1 b) internal pure returns (bytes1 c) { if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); else return bytes1(uint8(b) + 0x57); } }