// 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);
}
}