feat: fleek erc721 (#5)
* wip: refactor on current nft contract * wip: FleekERC721 contract * refactor: FleekERC721 * feat: add token controller control functions * fix: sintax wise issues for compilation * Fix errors and make the contract environment ready for compiling. * Remove fleekbuilds.sol & update erc721 with a fix * Update config files. * fix: working deploy * Make set methods public, add comments and notes to clarify issues. * Update package.json to add the deploy script & remove package-lock.json from the repository * Add two deploy scripts for local and mumbai deployments, update hardhat config to match two types and package.json * Update TokenURI and the metadata struct * Update deploy script and package.json to match the mumbai deploy script * Add setTokenName, setTokenDescription, setTokenImage * Add events to all set functions * Foundry init configs * Add foundry tests init (name, symbol, placeholder functions) * test: hardhat (#21) * chore: update hardhat config * test: add FleekERC721 tests and remove not used SitesNFTs suit * test: verify ERC721 compatibility * Content type on second abi.encodePacked call in tokenURI * Fix abi encoding on tokenURI * chore: update hardhat config * test: add FleekERC721 tests and remove not used SitesNFTs suit * test: verify ERC721 compatibility * Content type on second abi.encodePacked call in tokenURI * test: improve assertion using deep equality * chore: remove 0.4.24 version from hardhat compilers * refactor: clear empty bytes from bytes32 * refactor: change properties from bytes32 to string Co-authored-by: janison <jsonsivar@gmail.com> * feat: add interaction scripts * feat: add function signature to remove token controllers on transfer functions * Update test commands & add forge-cache and out to .gitignore * refactor: change token controller role validation to _beforeTokenTransfer function * refactor: remove upgradeTokenBuild and fix burn requirement * refactor: add isTokenController and move _clearTokenControllers to FleekAccessControl contract * refactor: remove localhost and wrong mumbai deployments * refactor: rename polygonMumbai to mumbai * refactor: remove twiced name on gitignore * chore: mumbai deployments * refactor: util script to get contract using hardhat defined network * chore: move forge-std as a submodule * chore: move forge-std as a submodule Co-authored-by: EmperorOrokuSaki <artie.eth@gmail.com> Co-authored-by: daltoncoder <71679972+daltoncoder@users.noreply.github.com> Co-authored-by: janison <jsonsivar@gmail.com>
This commit is contained in:
parent
315672243b
commit
94c364836e
|
|
@ -0,0 +1,3 @@
|
||||||
|
API_URL=https://matic-mumbai.chainstacklabs.com/
|
||||||
|
PRIVATE_KEY =0x
|
||||||
|
POLYSCAN_API=
|
||||||
|
|
@ -4,3 +4,11 @@ node_modules
|
||||||
# hardhat
|
# hardhat
|
||||||
cache
|
cache
|
||||||
artifacts
|
artifacts
|
||||||
|
deployments/localhost
|
||||||
|
|
||||||
|
# NPM
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Foundry
|
||||||
|
out
|
||||||
|
forge-cache
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "lib/forge-std"]
|
||||||
|
path = lib/forge-std
|
||||||
|
url = https://github.com/foundry-rs/forge-std
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "../interfaces/IFleek.sol";
|
|
||||||
import "./FleekBuilds.sol";
|
|
||||||
import "./FleekAccessControl.sol";
|
|
||||||
|
|
||||||
abstract contract Fleek is IFleek, FleekBuilds {
|
|
||||||
string name;
|
|
||||||
string description;
|
|
||||||
|
|
||||||
constructor(string memory _name, string memory _description) {
|
|
||||||
name = _name;
|
|
||||||
description = _description;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setName(
|
|
||||||
string calldata _name
|
|
||||||
) external override requireController {
|
|
||||||
name = _name;
|
|
||||||
emit MetadataUpdated(name, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDescription(
|
|
||||||
string calldata _description
|
|
||||||
) external override requireController {
|
|
||||||
description = _description;
|
|
||||||
emit MetadataUpdated(name, description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,33 +2,59 @@
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
pragma solidity ^0.8.7;
|
||||||
|
|
||||||
import "../interfaces/IFleekSite.sol";
|
|
||||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
||||||
|
|
||||||
abstract contract FleekAccessControl is AccessControl {
|
abstract contract FleekAccessControl is AccessControl {
|
||||||
bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
|
bytes32 public constant COLLECTION_OWNER_ROLE =
|
||||||
bytes32 public constant CONTROLLER_ROLE = keccak256("CONTROLLER_ROLE");
|
keccak256("COLLECTION_OWNER_ROLE");
|
||||||
|
bytes32 public constant COLLECTION_CONTROLLER_ROLE =
|
||||||
|
keccak256("COLLECTION_CONTROLLER_ROLE");
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
_setRoleAdmin(OWNER_ROLE, DEFAULT_ADMIN_ROLE);
|
_setRoleAdmin(COLLECTION_OWNER_ROLE, DEFAULT_ADMIN_ROLE);
|
||||||
_grantRole(OWNER_ROLE, msg.sender);
|
_grantRole(COLLECTION_OWNER_ROLE, msg.sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier requireOwner() {
|
modifier requireCollectionOwner() {
|
||||||
require(
|
require(
|
||||||
hasRole(OWNER_ROLE, msg.sender),
|
hasRole(COLLECTION_OWNER_ROLE, msg.sender),
|
||||||
"FleekAccessControl: must have owner role"
|
"FleekAccessControl: must have collection owner role"
|
||||||
);
|
);
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier requireController() {
|
modifier requireCollectionController() {
|
||||||
bool hasPermission = hasRole(CONTROLLER_ROLE, msg.sender) ||
|
|
||||||
hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
|
||||||
require(
|
require(
|
||||||
hasPermission,
|
hasRole(COLLECTION_OWNER_ROLE, msg.sender) ||
|
||||||
"FleekAccessControl: caller is not a controller"
|
hasRole(COLLECTION_CONTROLLER_ROLE, msg.sender),
|
||||||
|
"FleekAccessControl: must have collection controller role"
|
||||||
);
|
);
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modifier requireTokenController(uint256 tokenId) {
|
||||||
|
require(
|
||||||
|
hasRole(_tokenRole(tokenId, "CONTROLLER"), msg.sender),
|
||||||
|
"FleekAccessControl: must have token role"
|
||||||
|
);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTokenController(
|
||||||
|
uint256 tokenId,
|
||||||
|
address account
|
||||||
|
) public view returns (bool) {
|
||||||
|
return hasRole(_tokenRole(tokenId, "CONTROLLER"), account);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tokenRole(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory role
|
||||||
|
) internal pure returns (bytes32) {
|
||||||
|
return keccak256(abi.encodePacked("TOKEN_", role, tokenId));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _clearTokenControllers(uint256 tokenId) internal {
|
||||||
|
// TODO: Remove token controllers from AccessControl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "../interfaces/IFleekBuilds.sol";
|
|
||||||
import "./FleekAccessControl.sol";
|
|
||||||
|
|
||||||
abstract contract FleekBuilds is IFleekBuilds, FleekAccessControl {
|
|
||||||
build[] public builds;
|
|
||||||
|
|
||||||
function update(
|
|
||||||
build calldata _newBuild
|
|
||||||
) external override requireController {
|
|
||||||
builds.push(_newBuild);
|
|
||||||
emit Upgraded(_newBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentBuild() external view override returns (build memory) {
|
|
||||||
return builds[builds.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBuildById(
|
|
||||||
uint256 _buildId
|
|
||||||
) external view override returns (build memory) {
|
|
||||||
return builds[_buildId];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBuilds() external view override returns (build[] memory) {
|
|
||||||
return builds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8.7;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/Counters.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/Base64.sol";
|
||||||
|
import "./FleekAccessControl.sol";
|
||||||
|
|
||||||
|
contract FleekERC721 is ERC721, FleekAccessControl {
|
||||||
|
using Strings for uint256;
|
||||||
|
using Counters for Counters.Counter;
|
||||||
|
|
||||||
|
event NewBuild(uint256 indexed token, string indexed commit_hash);
|
||||||
|
event NewTokenName(uint256 indexed token, string indexed name);
|
||||||
|
event NewTokenDescription(uint256 indexed token, string indexed description);
|
||||||
|
event NewTokenImage(uint256 indexed token, string indexed image);
|
||||||
|
event NewTokenExternalURL(uint256 indexed token, string indexed external_url);
|
||||||
|
event NewTokenENS(uint256 indexed token, string indexed ENS);
|
||||||
|
|
||||||
|
struct Build {
|
||||||
|
string commit_hash;
|
||||||
|
string git_repository;
|
||||||
|
string author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The properties are stored as string to keep consistency with
|
||||||
|
* other token contracts, we might consider changing for bytes32
|
||||||
|
* in the future due to gas optimization
|
||||||
|
*/
|
||||||
|
struct App {
|
||||||
|
string name; // Name of the site
|
||||||
|
string description; // Description about the site
|
||||||
|
string image; // Preview Image IPFS Link
|
||||||
|
string external_url; // Site URL
|
||||||
|
string ENS; // ENS ID
|
||||||
|
uint256 current_build; // The current build number (Increments by one with each change, starts at zero)
|
||||||
|
mapping(uint256 => Build) builds; // Mapping to build details for each build number
|
||||||
|
}
|
||||||
|
|
||||||
|
Counters.Counter private _tokenIds;
|
||||||
|
mapping(uint256 => App) private _apps;
|
||||||
|
|
||||||
|
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,
|
||||||
|
string memory name,
|
||||||
|
string memory description,
|
||||||
|
string memory image,
|
||||||
|
string memory external_url,
|
||||||
|
string memory ENS,
|
||||||
|
string memory commit_hash,
|
||||||
|
string memory git_repository,
|
||||||
|
string memory author
|
||||||
|
) public payable requireCollectionOwner returns (uint256) {
|
||||||
|
uint256 tokenId = _tokenIds.current();
|
||||||
|
_mint(to, tokenId);
|
||||||
|
_tokenIds.increment();
|
||||||
|
|
||||||
|
App storage app = _apps[tokenId];
|
||||||
|
app.name = name;
|
||||||
|
app.description = description;
|
||||||
|
app.image = image;
|
||||||
|
app.external_url = external_url;
|
||||||
|
app.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.
|
||||||
|
app.current_build = 0;
|
||||||
|
app.builds[0] = Build(commit_hash, git_repository, author);
|
||||||
|
|
||||||
|
return tokenId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenURI(
|
||||||
|
uint256 tokenId
|
||||||
|
) public view virtual override returns (string memory) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
address owner = ownerOf(tokenId);
|
||||||
|
App storage app = _apps[tokenId];
|
||||||
|
|
||||||
|
bytes memory dataURI = abi.encodePacked(
|
||||||
|
'{',
|
||||||
|
'"name":"', app.name, '",',
|
||||||
|
'"description":"', app.description, '",',
|
||||||
|
'"owner":"', Strings.toHexString(uint160(owner), 20), '",',
|
||||||
|
'"external_url":"', app.external_url, '",',
|
||||||
|
'"image":"', app.image, '",',
|
||||||
|
'"attributes": [',
|
||||||
|
'{"trait_type": "ENS", "value":"', app.ENS,'"},',
|
||||||
|
'{"trait_type": "Commit Hash", "value":"', app.builds[app.current_build].commit_hash,'"},',
|
||||||
|
'{"trait_type": "Repository", "value":"', app.builds[app.current_build].git_repository,'"},',
|
||||||
|
'{"trait_type": "Author", "value":"', app.builds[app.current_build].author,'"},',
|
||||||
|
'{"trait_type": "Version", "value":"', Strings.toString(app.current_build),'"}',
|
||||||
|
']',
|
||||||
|
'}'
|
||||||
|
);
|
||||||
|
|
||||||
|
return string(abi.encodePacked(_baseURI(), Base64.encode((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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Override of _beforeTokenTransfer of ERC721.
|
||||||
|
* Here it needs to update the token controller roles for mint, burn and transfer.
|
||||||
|
* IMPORTANT: The function for clearing token controllers is not implemented yet.
|
||||||
|
*/
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 tokenId,
|
||||||
|
uint256 batchSize
|
||||||
|
) internal virtual override {
|
||||||
|
if (from != address(0) && to != address(0)) {
|
||||||
|
// Transfer
|
||||||
|
_clearTokenControllers(tokenId);
|
||||||
|
_grantRole(_tokenRole(tokenId, "CONTROLLER"), to);
|
||||||
|
} else if (from == address(0)) {
|
||||||
|
// Mint
|
||||||
|
_grantRole(_tokenRole(tokenId, "CONTROLLER"), to);
|
||||||
|
} else if (to == address(0)) {
|
||||||
|
// Burn
|
||||||
|
_clearTokenControllers(tokenId);
|
||||||
|
}
|
||||||
|
super._beforeTokenTransfer(from, to, tokenId, batchSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _baseURI() internal view virtual override returns (string memory) {
|
||||||
|
return "data:application/json;base64,";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenExternalURL(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory _tokenExternalURL
|
||||||
|
) public virtual requireTokenController(tokenId) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
_apps[tokenId].external_url = _tokenExternalURL;
|
||||||
|
emit NewTokenExternalURL(tokenId, _tokenExternalURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenENS(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory _tokenENS
|
||||||
|
) public virtual requireTokenController(tokenId) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
_apps[tokenId].ENS = _tokenENS;
|
||||||
|
emit NewTokenENS(tokenId, _tokenENS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenName(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory _tokenName
|
||||||
|
) public virtual requireTokenController(tokenId) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
_apps[tokenId].name = _tokenName;
|
||||||
|
emit NewTokenName(tokenId, _tokenName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenDescription(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory _tokenDescription
|
||||||
|
) public virtual requireTokenController(tokenId) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
_apps[tokenId].description = _tokenDescription;
|
||||||
|
emit NewTokenDescription(tokenId, _tokenDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenImage(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory _tokenImage
|
||||||
|
) public virtual requireTokenController(tokenId) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
_apps[tokenId].image = _tokenImage;
|
||||||
|
emit NewTokenImage(tokenId, _tokenImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokenBuild(
|
||||||
|
uint256 tokenId,
|
||||||
|
string memory _commit_hash,
|
||||||
|
string memory _git_repository,
|
||||||
|
string memory _author
|
||||||
|
) public virtual requireTokenController(tokenId) {
|
||||||
|
_requireMinted(tokenId);
|
||||||
|
_apps[tokenId].builds[++_apps[tokenId].current_build] = Build(
|
||||||
|
_commit_hash,
|
||||||
|
_git_repository,
|
||||||
|
_author
|
||||||
|
);
|
||||||
|
emit NewBuild(tokenId, _commit_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function burn(
|
||||||
|
uint256 tokenId
|
||||||
|
) public virtual requireTokenOwner(tokenId) {
|
||||||
|
super._burn(tokenId);
|
||||||
|
|
||||||
|
if (bytes(_apps[tokenId].external_url).length != 0) {
|
||||||
|
delete _apps[tokenId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "./Fleek.sol";
|
|
||||||
import "../interfaces/IFleekSite.sol";
|
|
||||||
|
|
||||||
contract FleekSite is IFleekSite, Fleek {
|
|
||||||
string thumbnail;
|
|
||||||
string external_url;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
string memory _name,
|
|
||||||
string memory _description,
|
|
||||||
string memory _thumbnail,
|
|
||||||
string memory _external_url
|
|
||||||
) Fleek(_name, _description) {
|
|
||||||
thumbnail = _thumbnail;
|
|
||||||
external_url = _external_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setThumbnail(
|
|
||||||
string calldata _thumbnail
|
|
||||||
) external override requireController {
|
|
||||||
thumbnail = _thumbnail;
|
|
||||||
emit SiteMetadataUpdated(name, description, thumbnail, external_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setExternalUrl(
|
|
||||||
string calldata _external_url
|
|
||||||
) external override requireController {
|
|
||||||
external_url = _external_url;
|
|
||||||
emit SiteMetadataUpdated(name, description, thumbnail, external_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
|
|
||||||
import "@openzeppelin/contracts/utils/Counters.sol";
|
|
||||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
||||||
import "../interfaces/ISitesNFTs.sol";
|
|
||||||
|
|
||||||
contract SitesNFTs is ISitesNFTs, ERC721URIStorage, AccessControl {
|
|
||||||
using Counters for Counters.Counter;
|
|
||||||
Counters.Counter private _tokenIds;
|
|
||||||
string private baseURI;
|
|
||||||
|
|
||||||
bytes32 public constant MINTER_ROLE =
|
|
||||||
0x4d494e5445525f524f4c45000000000000000000000000000000000000000000; // "MINTER_ROLE"
|
|
||||||
|
|
||||||
modifier canMint() {
|
|
||||||
bool isMinterOrAdmin = hasRole(MINTER_ROLE, msg.sender) ||
|
|
||||||
hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
|
||||||
require(isMinterOrAdmin, "Caller has no permission to mint.");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
modifier canChangeBaseURI() {
|
|
||||||
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(string memory name, string memory symbol) ERC721(name, symbol) {
|
|
||||||
baseURI = "data:application/json;base64,";
|
|
||||||
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mint(
|
|
||||||
string memory base64EncodedMetadata,
|
|
||||||
address account
|
|
||||||
) public override canMint returns (uint256) {
|
|
||||||
uint256 newItemId = _tokenIds.current();
|
|
||||||
_safeMint(account, newItemId);
|
|
||||||
_setTokenURI(newItemId, base64EncodedMetadata);
|
|
||||||
|
|
||||||
_tokenIds.increment();
|
|
||||||
return newItemId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTokenURI(
|
|
||||||
address tokenHolderAddress,
|
|
||||||
uint256 tokenId,
|
|
||||||
string memory base64EncodedMetadata
|
|
||||||
) public override canMint {
|
|
||||||
address tokenOwner = ownerOf(tokenId);
|
|
||||||
require(
|
|
||||||
tokenOwner == tokenHolderAddress,
|
|
||||||
"Address does not own provided tokenId"
|
|
||||||
);
|
|
||||||
_setTokenURI(tokenId, base64EncodedMetadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
function supportsInterface(
|
|
||||||
bytes4 interfaceId
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
virtual
|
|
||||||
override(IERC165, ERC721, AccessControl)
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return super.supportsInterface(interfaceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBaseURI(
|
|
||||||
string memory _newBbaseURI
|
|
||||||
) public override canChangeBaseURI returns (string memory) {
|
|
||||||
baseURI = _newBbaseURI;
|
|
||||||
return baseURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentTokenId() public view override returns (uint256) {
|
|
||||||
return _tokenIds.current();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _baseURI() internal view override returns (string memory) {
|
|
||||||
return baseURI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = async ({ getNamedAccounts, deployments }) => {
|
||||||
|
const { deploy, log } = deployments;
|
||||||
|
const namedAccounts = await getNamedAccounts();
|
||||||
|
const { deployer } = namedAccounts;
|
||||||
|
|
||||||
|
const deployResult = await deploy('FleekERC721', {
|
||||||
|
from: deployer,
|
||||||
|
args: ['FleekSites', 'FLKSITE'],
|
||||||
|
});
|
||||||
|
if (deployResult.newlyDeployed) {
|
||||||
|
log(
|
||||||
|
`contract FleekSites deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log(`using pre-existing contract FleekSites at ${deployResult.address}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//You can put an array of tags below. Tags can be anything and say when a this script should be run. So you can write different scripts for local, prod or other deploys
|
||||||
|
//For example when you run 'npx hardhat --network hardhat deploy --tags local' hardhat will run all deploy scripts that have the tag local, could be multiple dif scripts
|
||||||
|
module.exports.tags = ['local'];
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports = async ({ getNamedAccounts, deployments }) => {
|
||||||
|
const { deploy, log } = deployments;
|
||||||
|
const namedAccounts = await getNamedAccounts();
|
||||||
|
const { privateKey } = namedAccounts;
|
||||||
|
|
||||||
|
const deployResult = await deploy('FleekERC721', {
|
||||||
|
from: privateKey,
|
||||||
|
args: ['FleekSites', 'FLKSITE'],
|
||||||
|
});
|
||||||
|
if (deployResult.newlyDeployed) {
|
||||||
|
log(
|
||||||
|
`contract FleekSites deployed at ${deployResult.address} using ${deployResult.receipt.gasUsed} gas`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log(`using pre-existing contract FleekSites at ${deployResult.address}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//You can put an array of tags below. Tags can be anything and say when a this script should be run. So you can write different scripts for local, prod or other deploys
|
||||||
|
//For example when you run 'npx hardhat --network hardhat deploy --tags local' hardhat will run all deploy scripts that have the tag local, could be multiple dif scripts
|
||||||
|
module.exports.tags = ['mumbai'];
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
80001
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,6 @@
|
||||||
|
[profile.default]
|
||||||
|
src = 'contracts'
|
||||||
|
out = 'out'
|
||||||
|
libs = ['node_modules', 'lib']
|
||||||
|
test = 'test/foundry'
|
||||||
|
cache_path = 'forge-cache'
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
require('@nomiclabs/hardhat-waffle');
|
|
||||||
require('@nomiclabs/hardhat-etherscan');
|
|
||||||
require('hardhat-deploy');
|
|
||||||
require('solidity-coverage');
|
|
||||||
require('hardhat-gas-reporter');
|
|
||||||
require('hardhat-contract-sizer');
|
|
||||||
require('dotenv').config();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type import('hardhat/config').HardhatUserConfig
|
|
||||||
*/
|
|
||||||
|
|
||||||
const MAINNET_RPC_URL =
|
|
||||||
process.env.MAINNET_RPC_URL ||
|
|
||||||
process.env.ALCHEMY_MAINNET_RPC_URL ||
|
|
||||||
'https://eth-mainnet.alchemyapi.io/v2/your-api-key';
|
|
||||||
const GOERLI_RPC_URL =
|
|
||||||
process.env.GOERLI_RPC_URL ||
|
|
||||||
'https://eth-goerli.alchemyapi.io/v2/your-api-key';
|
|
||||||
const POLYGON_MAINNET_RPC_URL =
|
|
||||||
process.env.POLYGON_MAINNET_RPC_URL ||
|
|
||||||
'https://polygon-mainnet.alchemyapi.io/v2/your-api-key';
|
|
||||||
|
|
||||||
const POLYGON_MUMBAI_RPC_URL =
|
|
||||||
process.env.POLYGON_MUMBAI_RPC_URL ||
|
|
||||||
'https://polygon-mumbai.g.alchemy.com/v2/aIjNlC4r4aLYOHrdCTFT_JUX6OJsOsu0';
|
|
||||||
const PRIVATE_KEY = process.env.PRIVATE_KEY || '0x';
|
|
||||||
// optional
|
|
||||||
const MNEMONIC = process.env.MNEMONIC || 'your mnemonic';
|
|
||||||
|
|
||||||
// Your API key for Etherscan, obtain one at https://etherscan.io/
|
|
||||||
const ETHERSCAN_API_KEY =
|
|
||||||
process.env.ETHERSCAN_API_KEY || 'Your etherscan API key';
|
|
||||||
const POLYGONSCAN_API_KEY =
|
|
||||||
process.env.POLYGONSCAN_API_KEY || 'Your polygonscan API key';
|
|
||||||
const REPORT_GAS = process.env.REPORT_GAS || false;
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
defaultNetwork: 'hardhat',
|
|
||||||
networks: {
|
|
||||||
hardhat: {
|
|
||||||
// // If you want to do some forking, uncomment this
|
|
||||||
// forking: {
|
|
||||||
// url: MAINNET_RPC_URL
|
|
||||||
// }
|
|
||||||
chainId: 31337,
|
|
||||||
},
|
|
||||||
localhost: {
|
|
||||||
chainId: 31337,
|
|
||||||
},
|
|
||||||
// goerli: {
|
|
||||||
// url: GOERLI_RPC_URL,
|
|
||||||
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
|
|
||||||
// // accounts: {
|
|
||||||
// // mnemonic: MNEMONIC,
|
|
||||||
// // },
|
|
||||||
// saveDeployments: true,
|
|
||||||
// chainId: 5,
|
|
||||||
// },
|
|
||||||
// mainnet: {
|
|
||||||
// url: MAINNET_RPC_URL,
|
|
||||||
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
|
|
||||||
// // accounts: {
|
|
||||||
// // mnemonic: MNEMONIC,
|
|
||||||
// // },
|
|
||||||
// saveDeployments: true,
|
|
||||||
// chainId: 1,
|
|
||||||
// },
|
|
||||||
// polygon: {
|
|
||||||
// url: POLYGON_MAINNET_RPC_URL,
|
|
||||||
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
|
|
||||||
// saveDeployments: true,
|
|
||||||
// chainId: 137,
|
|
||||||
// },
|
|
||||||
// poligonMumbai: {
|
|
||||||
// url: POLYGON_MUMBAI_RPC_URL,
|
|
||||||
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
|
|
||||||
// saveDeployments: true,
|
|
||||||
// chainId: 80001,
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
etherscan: {
|
|
||||||
// npx hardhat verify --network <NETWORK> <CONTRACT_ADDRESS> <CONSTRUCTOR_PARAMETERS>
|
|
||||||
apiKey: {
|
|
||||||
goerli: ETHERSCAN_API_KEY,
|
|
||||||
polygon: POLYGONSCAN_API_KEY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
gasReporter: {
|
|
||||||
enabled: REPORT_GAS,
|
|
||||||
currency: 'USD',
|
|
||||||
outputFile: 'gas-report.txt',
|
|
||||||
noColors: true,
|
|
||||||
// coinmarketcap: process.env.COINMARKETCAP_API_KEY,
|
|
||||||
},
|
|
||||||
contractSizer: {
|
|
||||||
runOnCompile: false,
|
|
||||||
only: ['NftMarketplace'],
|
|
||||||
},
|
|
||||||
namedAccounts: {
|
|
||||||
deployer: {
|
|
||||||
default: 0, // here this will by default take the first account as deployer
|
|
||||||
1: 0, // similarly on mainnet it will take the first account as deployer. Note though that depending on how hardhat network are configured, the account 0 on one network can be different than on another
|
|
||||||
},
|
|
||||||
player: {
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
solidity: {
|
|
||||||
compilers: [
|
|
||||||
{
|
|
||||||
version: '0.8.7',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
version: '0.4.24',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
mocha: {
|
|
||||||
timeout: 200000, // 200 seconds max for running tests
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import '@nomiclabs/hardhat-ethers';
|
||||||
|
import '@nomiclabs/hardhat-web3';
|
||||||
|
import '@nomicfoundation/hardhat-chai-matchers';
|
||||||
|
import 'hardhat-deploy';
|
||||||
|
import 'solidity-coverage';
|
||||||
|
import 'hardhat-gas-reporter';
|
||||||
|
import 'hardhat-contract-sizer';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
import { HardhatUserConfig } from 'hardhat/types';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const {
|
||||||
|
API_URL = 'https://polygon-mainnet.alchemyapi.io/v2/your-api-key',
|
||||||
|
PRIVATE_KEY,
|
||||||
|
REPORT_GAS,
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
const config: HardhatUserConfig = {
|
||||||
|
defaultNetwork: 'hardhat',
|
||||||
|
networks: {
|
||||||
|
hardhat: {
|
||||||
|
chainId: 31337,
|
||||||
|
},
|
||||||
|
localhost: {
|
||||||
|
chainId: 31337,
|
||||||
|
},
|
||||||
|
mumbai: {
|
||||||
|
url: API_URL,
|
||||||
|
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
|
||||||
|
saveDeployments: true,
|
||||||
|
chainId: 80001,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gasReporter: {
|
||||||
|
enabled: REPORT_GAS === 'true' || false,
|
||||||
|
currency: 'USD',
|
||||||
|
outputFile: 'gas-report.txt',
|
||||||
|
noColors: true,
|
||||||
|
// coinmarketcap: process.env.COINMARKETCAP_API_KEY,
|
||||||
|
},
|
||||||
|
contractSizer: {
|
||||||
|
runOnCompile: false,
|
||||||
|
only: ['NftMarketplace'],
|
||||||
|
},
|
||||||
|
namedAccounts: {
|
||||||
|
deployer: {
|
||||||
|
default: 1, // here this will by default take the first account as deployer
|
||||||
|
1: 0,
|
||||||
|
},
|
||||||
|
privateKey: {
|
||||||
|
default: `privatekey://${PRIVATE_KEY}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
solidity: {
|
||||||
|
compilers: [
|
||||||
|
{
|
||||||
|
version: '0.8.7',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
mocha: {
|
||||||
|
timeout: 200000, // 200 seconds max for running tests
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "./IFleekBuilds.sol";
|
|
||||||
import "@openzeppelin/contracts/access/IAccessControl.sol";
|
|
||||||
|
|
||||||
interface IFleek is IFleekBuilds, IAccessControl {
|
|
||||||
event MetadataUpdated(string name, string description);
|
|
||||||
|
|
||||||
function setName(string calldata _name) external;
|
|
||||||
|
|
||||||
function setDescription(string calldata _description) external;
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
interface IFleekBuilds {
|
|
||||||
struct build {
|
|
||||||
string _uri;
|
|
||||||
string _hash;
|
|
||||||
string _repo;
|
|
||||||
string _repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
event InitialVersionDeploy();
|
|
||||||
|
|
||||||
event Upgraded(build _build);
|
|
||||||
|
|
||||||
function update(build calldata _newBuild) external;
|
|
||||||
|
|
||||||
function getCurrentBuild() external view returns (build memory);
|
|
||||||
|
|
||||||
function getBuildById(
|
|
||||||
uint256 _buildId
|
|
||||||
) external view returns (build memory);
|
|
||||||
|
|
||||||
function getBuilds() external view returns (build[] memory);
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "./IFleek.sol";
|
|
||||||
|
|
||||||
interface IFleekSite is IFleek {
|
|
||||||
event SiteMetadataUpdated(
|
|
||||||
string name,
|
|
||||||
string description,
|
|
||||||
string thumbnail,
|
|
||||||
string external_url
|
|
||||||
);
|
|
||||||
|
|
||||||
function setThumbnail(string calldata _thumbnail) external;
|
|
||||||
|
|
||||||
function setExternalUrl(string calldata _external_url) external;
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "@openzeppelin/contracts/interfaces/IERC721.sol";
|
|
||||||
import "@openzeppelin/contracts/access/IAccessControl.sol";
|
|
||||||
|
|
||||||
/// @title SitesNFTs - A contract for managing sites NFTs
|
|
||||||
/// @dev See
|
|
||||||
interface ISitesNFTs is IERC721 {
|
|
||||||
function mint(
|
|
||||||
string memory base64EncodedMetadata,
|
|
||||||
address account
|
|
||||||
) external returns (uint256);
|
|
||||||
|
|
||||||
function updateTokenURI(
|
|
||||||
address tokenHolderAddress,
|
|
||||||
uint256 tokenId,
|
|
||||||
string memory base64EncodedMetadata
|
|
||||||
) external;
|
|
||||||
|
|
||||||
function setBaseURI(
|
|
||||||
string memory _newBbaseURI
|
|
||||||
) external returns (string memory);
|
|
||||||
|
|
||||||
function getCurrentTokenId() external view returns (uint256);
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit cd7d533f9a0ee0ec02ad81e0a8f262bc4203c653
|
||||||
35
package.json
35
package.json
|
|
@ -1,37 +1,50 @@
|
||||||
{
|
{
|
||||||
"name": "sites_nfts",
|
"name": "fleek_contracts",
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "hardhat test",
|
"test": "hardhat test && forge test --via-ir",
|
||||||
"format": "prettier --write \"./**/*.{js,ts,sol}\""
|
"test:foundry": "forge test --via-ir",
|
||||||
|
"test:hardhat": "hardhat test",
|
||||||
|
"format": "prettier --write \"./**/*.{js,ts,sol}\"",
|
||||||
|
"node:hardhat": "hardhat node --tags local",
|
||||||
|
"deploy:local": "hardhat deploy --tags local",
|
||||||
|
"deploy:mumbai": "hardhat deploy --tags mumbai --network mumbai",
|
||||||
|
"compile": "hardhat compile"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/FleekHQ/sites_nfts.git"
|
"url": "git+https://github.com/FleekHQ/contracts.git"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/FleekHQ/sites_nfts/issues"
|
"url": "https://github.com/FleekHQ/contracts/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/FleekHQ/sites_nfts#readme",
|
"homepage": "https://github.com/FleekHQ/contracts#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
|
||||||
|
"@nomicfoundation/hardhat-network-helpers": "^1.0.7",
|
||||||
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
||||||
"@nomiclabs/hardhat-ethers": "^2.1.1",
|
"@nomiclabs/hardhat-ethers": "^2.2.1",
|
||||||
"@nomiclabs/hardhat-etherscan": "^3.1.0",
|
"@nomiclabs/hardhat-etherscan": "^3.1.0",
|
||||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||||
"@openzeppelin/contracts": "^4.7.3",
|
"@openzeppelin/contracts": "^4.7.3",
|
||||||
|
"@types/mocha": "^10.0.1",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
"dotenv": "^16.0.2",
|
"dotenv": "^16.0.2",
|
||||||
"ethereum-waffle": "^3.4.4",
|
"ethers": "^5.7.2",
|
||||||
"hardhat": "^2.11.2",
|
"hardhat": "^2.11.2",
|
||||||
"hardhat-contract-sizer": "^2.6.1",
|
"hardhat-contract-sizer": "^2.6.1",
|
||||||
"hardhat-deploy": "^0.11.15",
|
"hardhat-deploy": "^0.11.15",
|
||||||
"hardhat-gas-reporter": "^1.0.9",
|
"hardhat-gas-reporter": "^1.0.9",
|
||||||
|
"minimist": "^1.2.7",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-plugin-solidity": "^1.0.0",
|
"prettier-plugin-solidity": "^1.0.0",
|
||||||
"solidity-coverage": "^0.8.2"
|
"solidity-coverage": "^0.8.2",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^4.9.3",
|
||||||
|
"web3": "^1.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
ds-test/=lib/forge-std/lib/ds-test/src/
|
||||||
|
forge-std/=lib/forge-std/src/
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
async function main() {
|
|
||||||
const [deployer] = await ethers.getSigners();
|
|
||||||
|
|
||||||
console.log('Deploying contracts with the account:', deployer.address);
|
|
||||||
|
|
||||||
console.log('Account balance:', (await deployer.getBalance()).toString());
|
|
||||||
|
|
||||||
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
|
|
||||||
const sitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
|
|
||||||
|
|
||||||
console.log('SitesNFTs address:', sitesNFTs.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
.then(() => process.exit(0))
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// npx hardhat run scripts/mint.js --network mumbai
|
||||||
|
const { getContract } = require('./util');
|
||||||
|
|
||||||
|
// TODO: make this arguments
|
||||||
|
const params = [
|
||||||
|
'0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049', // to
|
||||||
|
'Fleek App', // name
|
||||||
|
'Description', // description
|
||||||
|
'https://fleek.network/fleek-network-logo-minimal.png', // image
|
||||||
|
'https://fleek.co/', // external url
|
||||||
|
'fleek.eth', // ens
|
||||||
|
'6ea6ad16c46ae85faced7e50555ff7368422f57', // commit hash
|
||||||
|
'https://github.com/org/repo', // repo
|
||||||
|
'fleek', // author
|
||||||
|
];
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const contract = getContract('FleekERC721');
|
||||||
|
|
||||||
|
const transaction = await contract.mint(...params);
|
||||||
|
|
||||||
|
console.log('Response: ', transaction);
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// npx hardhat run scripts/tokenURI.js --network mumbai
|
||||||
|
const { getContract } = require('./util');
|
||||||
|
|
||||||
|
// TODO: make this arguments
|
||||||
|
const tokenId = 1;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const contract = await getContract('FleekERC721');
|
||||||
|
|
||||||
|
const transaction = await contract.tokenURI(tokenId);
|
||||||
|
|
||||||
|
const parsed = JSON.parse(
|
||||||
|
Buffer.from(transaction.slice(29), 'base64').toString('utf-8')
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Response: ', parsed);
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// npx hardhat run scripts/upgrade.js --network mumbai
|
||||||
|
const { getContract } = require('./util');
|
||||||
|
|
||||||
|
// TODO: make this arguments
|
||||||
|
const params = [
|
||||||
|
1, // tokenId
|
||||||
|
'97e7908f70f0862d753c66689ff09e70caa43df2', // commit hash
|
||||||
|
'https://github.com/org/new-repo', // repo
|
||||||
|
'new-author', // author
|
||||||
|
];
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const contract = await getContract('FleekERC721');
|
||||||
|
|
||||||
|
const transaction = await contract.setTokenBuild(...params);
|
||||||
|
|
||||||
|
console.log('Response: ', transaction);
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
module.exports.getContract = async function (contractName) {
|
||||||
|
const {
|
||||||
|
address,
|
||||||
|
} = require(`../deployments/${hre.network.name}/${contractName}.json`);
|
||||||
|
|
||||||
|
return hre.ethers.getContractAt(contractName, address);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { ethers } from 'hardhat';
|
||||||
|
import web3 from 'web3';
|
||||||
|
|
||||||
|
describe('FleekERC721', () => {
|
||||||
|
const COLLECTION_OWNER_ROLE = web3.utils.keccak256('COLLECTION_OWNER_ROLE');
|
||||||
|
|
||||||
|
const MINT_PARAMS = Object.freeze({
|
||||||
|
name: 'Fleek Test App',
|
||||||
|
description: 'Fleek Test App Description',
|
||||||
|
image: 'https://fleek.co/image.png',
|
||||||
|
ens: 'fleek.eth',
|
||||||
|
externalUrl: 'https://fleek.co',
|
||||||
|
commitHash: 'b72e47171746b6a9e29b801af9cb655ecf4d665c',
|
||||||
|
gitRepository: 'https://github.com/fleekxyz/contracts',
|
||||||
|
author: 'author',
|
||||||
|
});
|
||||||
|
|
||||||
|
const COLLECTION_PARAMS = Object.freeze({
|
||||||
|
name: 'FleekERC721',
|
||||||
|
symbol: 'FLEEK',
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultFixture = async () => {
|
||||||
|
// Contracts are deployed using the first signer/account by default
|
||||||
|
const [owner, otherAccount] = await ethers.getSigners();
|
||||||
|
|
||||||
|
const Contract = await ethers.getContractFactory('FleekERC721');
|
||||||
|
const contract = await Contract.deploy(
|
||||||
|
COLLECTION_PARAMS.name,
|
||||||
|
COLLECTION_PARAMS.symbol
|
||||||
|
);
|
||||||
|
|
||||||
|
return { owner, otherAccount, contract };
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Deployment', () => {
|
||||||
|
it('should assign the name and the symbol of the ERC721 contract', async () => {
|
||||||
|
const { contract } = await loadFixture(defaultFixture);
|
||||||
|
|
||||||
|
expect(await contract.name()).to.equal(COLLECTION_PARAMS.name);
|
||||||
|
expect(await contract.symbol()).to.equal(COLLECTION_PARAMS.symbol);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should assign the owner of the contract', async () => {
|
||||||
|
const { owner, contract } = await loadFixture(defaultFixture);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contract.hasRole(COLLECTION_OWNER_ROLE, owner.address)
|
||||||
|
).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support ERC721 interface', async () => {
|
||||||
|
const { contract } = await loadFixture(defaultFixture);
|
||||||
|
|
||||||
|
expect(await contract.supportsInterface('0x80ac58cd')).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Minting', () => {
|
||||||
|
it('should be able to mint a new token', async () => {
|
||||||
|
const { owner, contract } = await loadFixture(defaultFixture);
|
||||||
|
|
||||||
|
const response = await contract.mint(
|
||||||
|
owner.address,
|
||||||
|
MINT_PARAMS.name,
|
||||||
|
MINT_PARAMS.description,
|
||||||
|
MINT_PARAMS.image,
|
||||||
|
MINT_PARAMS.externalUrl,
|
||||||
|
MINT_PARAMS.ens,
|
||||||
|
MINT_PARAMS.commitHash,
|
||||||
|
MINT_PARAMS.gitRepository,
|
||||||
|
MINT_PARAMS.author
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.value).to.be.instanceOf(ethers.BigNumber);
|
||||||
|
expect(response.value.toNumber()).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be able to mint a new token if not the owner', async () => {
|
||||||
|
const { otherAccount, contract } = await loadFixture(defaultFixture);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contract
|
||||||
|
.connect(otherAccount)
|
||||||
|
.mint(
|
||||||
|
otherAccount.address,
|
||||||
|
MINT_PARAMS.name,
|
||||||
|
MINT_PARAMS.description,
|
||||||
|
MINT_PARAMS.image,
|
||||||
|
MINT_PARAMS.externalUrl,
|
||||||
|
MINT_PARAMS.ens,
|
||||||
|
MINT_PARAMS.commitHash,
|
||||||
|
MINT_PARAMS.gitRepository,
|
||||||
|
MINT_PARAMS.author
|
||||||
|
)
|
||||||
|
).to.be.revertedWith(
|
||||||
|
'FleekAccessControl: must have collection owner role'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Token', () => {
|
||||||
|
let tokenId: number;
|
||||||
|
let fixture: Awaited<ReturnType<typeof defaultFixture>>;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture(defaultFixture);
|
||||||
|
const { contract } = fixture;
|
||||||
|
|
||||||
|
const response = await contract.mint(
|
||||||
|
fixture.owner.address,
|
||||||
|
MINT_PARAMS.name,
|
||||||
|
MINT_PARAMS.description,
|
||||||
|
MINT_PARAMS.image,
|
||||||
|
MINT_PARAMS.externalUrl,
|
||||||
|
MINT_PARAMS.ens,
|
||||||
|
MINT_PARAMS.commitHash,
|
||||||
|
MINT_PARAMS.gitRepository,
|
||||||
|
MINT_PARAMS.author
|
||||||
|
);
|
||||||
|
|
||||||
|
tokenId = response.value.toNumber();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the token URI', async () => {
|
||||||
|
const { contract } = fixture;
|
||||||
|
const tokenURI = await contract.tokenURI(tokenId);
|
||||||
|
|
||||||
|
const tokenURIDecoded = Buffer.from(
|
||||||
|
tokenURI.replace('data:application/json;base64,', ''),
|
||||||
|
'base64'
|
||||||
|
).toString('ascii');
|
||||||
|
|
||||||
|
const parsedURI = JSON.parse(tokenURIDecoded);
|
||||||
|
|
||||||
|
expect(parsedURI).to.eql({
|
||||||
|
owner: fixture.owner.address.toLowerCase(),
|
||||||
|
name: MINT_PARAMS.name,
|
||||||
|
description: MINT_PARAMS.description,
|
||||||
|
image: MINT_PARAMS.image,
|
||||||
|
external_url: MINT_PARAMS.externalUrl,
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
trait_type: 'ENS',
|
||||||
|
value: MINT_PARAMS.ens,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trait_type: 'Commit Hash',
|
||||||
|
value: MINT_PARAMS.commitHash,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trait_type: 'Repository',
|
||||||
|
value: MINT_PARAMS.gitRepository,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trait_type: 'Author',
|
||||||
|
value: MINT_PARAMS.author,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
trait_type: 'Version',
|
||||||
|
value: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match the token owner', async () => {
|
||||||
|
const { contract, owner } = fixture;
|
||||||
|
const tokenOwner = await contract.ownerOf(tokenId);
|
||||||
|
expect(tokenOwner).to.equal(owner.address);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
const { expect } = require('chai');
|
|
||||||
const { loadFixture } = require('ethereum-waffle');
|
|
||||||
const { ethers } = require('hardhat');
|
|
||||||
const hre = require('hardhat');
|
|
||||||
|
|
||||||
describe('FleekSite contract', function () {
|
|
||||||
//TODO check values are setted right on the contract
|
|
||||||
const _name = 'Fleek Site';
|
|
||||||
const _description = 'Fleek Site Description';
|
|
||||||
const _thumbnail = 'https://fleek.co';
|
|
||||||
const _externalUrl = 'https://fleek.co';
|
|
||||||
|
|
||||||
async function deploy() {
|
|
||||||
const [owner] = await hre.ethers.getSigners();
|
|
||||||
|
|
||||||
const FleekSite = await hre.ethers.getContractFactory('FleekSite');
|
|
||||||
|
|
||||||
const hardhatFleekSite = await FleekSite.deploy(
|
|
||||||
'Fleek Site',
|
|
||||||
'Fleek Site Description',
|
|
||||||
'https://fleek.co',
|
|
||||||
'https://fleek.co'
|
|
||||||
);
|
|
||||||
|
|
||||||
return { owner, hardhatFleekSite };
|
|
||||||
}
|
|
||||||
describe('Deployment', () => {
|
|
||||||
it('Deploy FleekSit contract with name Fleek Site and builds[] should be 0', async () => {
|
|
||||||
const { hardhatFleekSite } = await loadFixture(deploy);
|
|
||||||
|
|
||||||
const currentBuilds = await hardhatFleekSite.getBuilds();
|
|
||||||
expect(currentBuilds.length).to.equal(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deployment should assign to OWNER_ROLE the DEFAULT_ADMIN_ROLE', async () => {
|
|
||||||
const { hardhatFleekSite } = await loadFixture(deploy);
|
|
||||||
|
|
||||||
const OWNER_ROLE = 'OWNER_ROLE';
|
|
||||||
const DEFAULT_ADMIN_ROLE_STRING = '';
|
|
||||||
|
|
||||||
const role = await hardhatFleekSite.getRoleAdmin(
|
|
||||||
ethers.utils.formatBytes32String(OWNER_ROLE)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(role).to.equal(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE_STRING)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
const { expect } = require('chai');
|
|
||||||
const { loadFixture } = require('ethereum-waffle');
|
|
||||||
|
|
||||||
describe('SitesNFTs contract', function () {
|
|
||||||
const name = 'Sites NFTs';
|
|
||||||
const symbol = 'SNFT';
|
|
||||||
|
|
||||||
async function deploy() {
|
|
||||||
const [owner, address1, address2] = await hre.ethers.getSigners();
|
|
||||||
|
|
||||||
const SitesNFTs = await hre.ethers.getContractFactory('SitesNFTs');
|
|
||||||
|
|
||||||
const hardhatSitesNFTs = await SitesNFTs.deploy(name, symbol);
|
|
||||||
|
|
||||||
return { owner, address1, address2, hardhatSitesNFTs };
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Deployment', () => {
|
|
||||||
it('Deployment should assign the name and the symbol of the ERC721 contract', async () => {
|
|
||||||
const { hardhatSitesNFTs } = await loadFixture(deploy);
|
|
||||||
|
|
||||||
const contractName = await hardhatSitesNFTs.name();
|
|
||||||
const contractSymbol = await hardhatSitesNFTs.symbol();
|
|
||||||
|
|
||||||
expect(contractName).to.equal(name);
|
|
||||||
expect(contractSymbol).to.equal(symbol);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deployment should assign the deployer DEFAULT_ADMIN_ROLE', async () => {
|
|
||||||
const { hardhatSitesNFTs, owner } = await loadFixture(deploy);
|
|
||||||
|
|
||||||
const DEFAULT_ADMIN_ROLE_STRING = '';
|
|
||||||
|
|
||||||
const hasAdminRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE_STRING),
|
|
||||||
await owner.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasAdminRole).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Deployment should assign initial tokenId to 0', async () => {
|
|
||||||
const { hardhatSitesNFTs } = await loadFixture(deploy);
|
|
||||||
const currentTokenId = await hardhatSitesNFTs.getCurrentTokenId();
|
|
||||||
|
|
||||||
expect(currentTokenId).to.equal(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Access control', () => {
|
|
||||||
it('User with DEFAULT_ADMIN_ROLE should be able to assign MINTER_ROLE to another user', async () => {
|
|
||||||
const { hardhatSitesNFTs, address1 } = await loadFixture(deploy);
|
|
||||||
const MINTER_ROLE = 'MINTER_ROLE';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasMinterRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasMinterRole).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User with DEFAULT_ADMIN_ROLE should be able to assign MINTER_ROLE to himself and still have DEFAULT_ADMIN_ROLE', async () => {
|
|
||||||
const { hardhatSitesNFTs, owner } = await loadFixture(deploy);
|
|
||||||
const MINTER_ROLE = 'MINTER_ROLE';
|
|
||||||
const DEFAULT_ADMIN_ROLE = '';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await owner.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasMinterRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await owner.getAddress()
|
|
||||||
);
|
|
||||||
const hasAdminRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await owner.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasMinterRole).to.equal(true);
|
|
||||||
expect(hasAdminRole).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User with DEFAULT_ADMIN_ROLE should be able to assign DEFAULT_ADMIN_ROLE to another user', async () => {
|
|
||||||
const { hardhatSitesNFTs, address1 } = await loadFixture(deploy);
|
|
||||||
const DEFAULT_ADMIN_ROLE = '';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasAdminRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasAdminRole).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User with DEFAULT_ADMIN_ROLE should be able to assign DEFAULT_ADMIN_ROLE to another user and still have DEFAULT_ADMIN_ROLE', async () => {
|
|
||||||
const { hardhatSitesNFTs, owner, address1 } = await loadFixture(deploy);
|
|
||||||
const DEFAULT_ADMIN_ROLE = '';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
let hasAdminRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasAdminRole).to.equal(true);
|
|
||||||
|
|
||||||
hasAdminRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await owner.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasAdminRole).to.equal(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User without DEFAULT_ADMIN_ROLE shouldnt be able to assign DEFAULT_ADMIN_ROLE to another user', async () => {
|
|
||||||
const [owner, address1, address2] = await ethers.getSigners();
|
|
||||||
|
|
||||||
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
|
|
||||||
|
|
||||||
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
|
|
||||||
|
|
||||||
const DEFAULT_ADMIN_ROLE = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
await hardhatSitesNFTs
|
|
||||||
.connect(address1)
|
|
||||||
.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await address2.getAddress()
|
|
||||||
);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
const hasAdminRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
|
|
||||||
await address2.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasAdminRole).to.equal(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User without DEFAULT_ADMIN_ROLE shouldnt be able to assign MINTER_ROLE to another user', async () => {
|
|
||||||
const [owner, address1, address2] = await ethers.getSigners();
|
|
||||||
|
|
||||||
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
|
|
||||||
|
|
||||||
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
|
|
||||||
|
|
||||||
const MINTER_ROLE = 'MINTER_ROLE';
|
|
||||||
|
|
||||||
try {
|
|
||||||
await hardhatSitesNFTs
|
|
||||||
.connect(address1)
|
|
||||||
.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await address2.getAddress()
|
|
||||||
);
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
const hasMinterRole = await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await address2.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(hasMinterRole).to.equal(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Minting', () => {
|
|
||||||
it('User with DEFAULT_ADMIN_ROLE should be able to mint', async () => {
|
|
||||||
const { hardhatSitesNFTs, address1 } = await loadFixture(deploy);
|
|
||||||
const tokenURI = 'tokenURI';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.mint(tokenURI, await address1.getAddress());
|
|
||||||
|
|
||||||
const balance = await hardhatSitesNFTs.balanceOf(
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(balance).to.equal(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User with MINTER_ROLE should be able to mint', async () => {
|
|
||||||
const { hardhatSitesNFTs, address1, address2 } = await loadFixture(
|
|
||||||
deploy
|
|
||||||
);
|
|
||||||
const MINTER_ROLE = 'MINTER_ROLE';
|
|
||||||
const tokenURI = 'tokenURI';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.grantRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.hasRole(
|
|
||||||
ethers.utils.formatBytes32String(MINTER_ROLE),
|
|
||||||
await address1.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
await hardhatSitesNFTs
|
|
||||||
.connect(address1)
|
|
||||||
.mint(tokenURI, await address2.getAddress());
|
|
||||||
|
|
||||||
const balance = await hardhatSitesNFTs.balanceOf(
|
|
||||||
await address2.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(balance).to.equal(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('User without MINTER_ROLE or DEFAULT_ADMIN_ROLE shouldnt be able to mint', async () => {
|
|
||||||
const [owner, address1, address2] = await ethers.getSigners();
|
|
||||||
|
|
||||||
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
|
|
||||||
|
|
||||||
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
|
|
||||||
|
|
||||||
const tokenURI = 'tokenURI';
|
|
||||||
|
|
||||||
try {
|
|
||||||
await hardhatSitesNFTs
|
|
||||||
.connect(address1)
|
|
||||||
.mint(tokenURI, await address2.getAddress());
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
const balance = await hardhatSitesNFTs.balanceOf(
|
|
||||||
await address2.getAddress()
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(balance).to.equal(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Minted NFT should have data:application/json;base64, baseURI', async () => {
|
|
||||||
const { hardhatSitesNFTs, address1 } = await loadFixture(deploy);
|
|
||||||
const tokenURI = 'tokenURI';
|
|
||||||
|
|
||||||
await hardhatSitesNFTs.mint(tokenURI, await address1.getAddress());
|
|
||||||
|
|
||||||
const mintedNFT = await hardhatSitesNFTs.tokenURI(0);
|
|
||||||
|
|
||||||
expect(mintedNFT.includes('data:application/json;base64,')).to.equal(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
pragma solidity ^0.8.7;
|
||||||
|
|
||||||
|
import "forge-std/Test.sol";
|
||||||
|
import "../../contracts/FleekERC721.sol";
|
||||||
|
|
||||||
|
contract ContractBTest is Test {
|
||||||
|
FleekERC721 fleekContract;
|
||||||
|
uint256 testNumber;
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
fleekContract = new FleekERC721('Test Contract', 'FLKAPS');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testName() public {
|
||||||
|
assertEq(fleekContract.name(), 'Test Contract'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSymbol() public {
|
||||||
|
assertEq(fleekContract.symbol(), 'FLKAPS'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMint() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTokenURI() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBurn() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSetTokenName() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSetTokenDescription() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSetTokenImage() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSetTokenExternalURL() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSetTokenBuild() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testUpgradeTokenBuild() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSetTokenENS() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAddTokenController() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRemoveTokenController() public {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue