non-fungible-apps/contracts/FleekERC721.sol

161 lines
4.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "./FleekAccessControl.sol";
contract FleekERC721 is ERC721, FleekAccessControl {
using Strings for uint256;
using Counters for Counters.Counter;
struct Build {
string commit_hash;
string git_repository;
}
struct Site {
bytes32 external_url; //ipfs hash example
bytes32 ENS;
uint256 current_build;
mapping(uint256 => Build) builds;
}
Counters.Counter private _tokenIds;
mapping(uint256 => Site) private _sites;
constructor(
string memory _name,
string memory _symbol
) ERC721(_name, _symbol) {}
modifier requireTokenOwner(uint256 tokenId) {
require(
msg.sender == ownerOf(tokenId),
"FleekERC721: must be token owner"
);
_;
}
function mint(
address to,
bytes32 external_url,
bytes32 ENS,
string memory commit_hash,
string memory git_repository
) public payable requireCollectionOwner returns (uint256) {
uint256 tokenId = _tokenIds.current();
_mint(to, tokenId);
addTokenController(tokenId, to);
_tokenIds.increment();
Site storage site = _sites[tokenId];
site.external_url = external_url;
site.ENS = ENS;
// The mint interaction is considered to be the first build of the site. Updates from now on all increment the current_build by one and update the mapping.
site.current_build = 0;
site.builds[0] = Build(commit_hash, git_repository);
return tokenId;
}
function upgradeTokenBuild(
uint256 tokenId,
string memory commit,
string memory repository
) public payable requireTokenOwner(tokenId) {
_requireMinted(tokenId);
_setTokenBuild(tokenId, commit, repository);
}
function tokenURI(
uint256 tokenId
) public view virtual override returns (string memory) {
_requireMinted(tokenId);
address owner = ownerOf(tokenId);
Site storage site = _sites[tokenId];
// prettier-ignore
bytes memory dataURI = abi.encodePacked(
'{',
'"owner":"', owner, '",',
'"ENS":"', site.ENS, '",',
'"external_url":"', site.external_url, '",',
'"build:{',
'"id":"', site.current_build, '",',
'"commit_hash":"', site.builds[site.current_build].commit_hash, '",',
'"repository":"', site.builds[site.current_build].git_repository, '"'
'}',
'}'
);
return string(abi.encodePacked(_baseURI(), dataURI));
}
function addTokenController(
uint256 tokenId,
address controller
) public requireTokenOwner(tokenId) {
_requireMinted(tokenId);
_grantRole(_tokenRole(tokenId, "CONTROLLER"), controller);
}
function removeTokenController(
uint256 tokenId,
address controller
) public requireTokenOwner(tokenId) {
_requireMinted(tokenId);
_revokeRole(_tokenRole(tokenId, "CONTROLLER"), controller);
}
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC721, AccessControl) returns (bool) {
return super.supportsInterface(interfaceId);
}
function _baseURI() internal view virtual override returns (string memory) {
return "data:application/json;base64,";
}
function _setTokenExternalURL(
uint256 tokenId,
bytes32 _tokenExternalURL
) internal virtual requireTokenController(tokenId) {
_requireMinted(tokenId);
_sites[tokenId].external_url = _tokenExternalURL;
}
function _setTokenENS(
uint256 tokenId,
bytes32 _tokenENS
) internal virtual requireTokenController(tokenId) {
_requireMinted(tokenId);
_sites[tokenId].ENS = _tokenENS;
}
function _setTokenBuild(
uint256 tokenId,
string memory _commit_hash,
string memory _git_repository
) internal virtual requireTokenController(tokenId) {
_requireMinted(tokenId);
_sites[tokenId].current_build = _sites[tokenId].current_build + 1;
_sites[tokenId].builds[_sites[tokenId].current_build] = Build(_commit_hash, _git_repository);
}
function _burn(uint256 tokenId) internal virtual override {
require(
ownerOf(tokenId) == msg.sender,
"FleekERC721: must be token owner"
);
super._burn(tokenId);
if (_sites[tokenId].external_url.length != 0) {
delete _sites[tokenId];
}
}
}