//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
contract BlogNft is ERC1155, Ownable, ERC1155Supply {
struct TokenInfo {
bool exists; // A solidity mapping holds a value for any key. Thus an empty struct is returned if a key actually does not exist. With the flag we make sure that a token really exists.
uint256 maxAmountAllowed; // Max amount of mintable tokens
string metadataUrl; // Token specific url
}
mapping(uint256 => TokenInfo) tokenInfos; // Mapping from token id to token info
string public name; // Required by marketplace
constructor(string memory contractName, string memory contractMetadataUrl) ERC1155(contractMetadataUrl) {
name = contractName;
}
function add(uint256 tokenId, uint256 maxAmountAllowed, string memory metadataUrl) public onlyOwner {
require(!tokenInfos[tokenId].exists, "Token with given id already exists"); // Ensure we can only add and not override
tokenInfos[tokenId] = TokenInfo(true, maxAmountAllowed, metadataUrl); // Add token informations for token id
}
function mint(uint256 tokenId, uint256 amount) public onlyOwner {
TokenInfo memory tokenInfo = tokenInfos[tokenId]; // Get token information for token id
require(tokenInfo.exists, "Token with given id does not exists"); // Ensure token id is allowed to be minted
uint256 minted = super.totalSupply(tokenId); // Get amount of already minted tokens
require((minted + amount) <= tokenInfo.maxAmountAllowed, "To many tokens"); // Prevent minting more than allowed
_mint(msg.sender, tokenId, amount, ""); // Mint token
}
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal override(ERC1155, ERC1155Supply) {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
// Token level metadata
function uri(uint256 tokenId) public view override returns (string memory) {
return tokenInfos[tokenId].metadataUrl;
}
// Contract level metadata
function contractURI() public view returns (string memory) {
return super.uri(0);
}
}
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("BlogNft", function () {
let contract = null; // Contract
let ownerUser = null; // Owner of the contract
let otherUser = null; // Some other user
beforeEach(async function() {
// Get interacting ether account
[ownerUser, otherUser] = await ethers.getSigners();
// Load factory for our contract
const contractFactory = await ethers.getContractFactory("BlogNft");
// Deploy the contract using the
const contractName = "MyContract";
const contractLevelMetadataUrl = "https://myUrl.sap"
contract = await contractFactory.deploy(contractName, contractLevelMetadataUrl);
// Ensure it is deployed
await contract.deployed();
});
it("Prevent add token twice", async function () {
// Add a token that is allowed to be minted
await contract.connect(ownerUser).add(1, 10, "https://myToken.sap");
// Ensure the same request would be reverted
await expect(contract.connect(ownerUser).add(1, 10, "https://myToken.sap")).to.be.revertedWith('Token with given id already exists');
});
it("Add and mint multiple tokens", async function () {
await contract.connect(ownerUser).add(1, 10, "https://myToken.sap"); // Allow 10 tokens of id 1
await contract.connect(ownerUser).mint(1, 5); // Mint 5 of id 1
await contract.connect(ownerUser).add(2, 1, "https://myToken.sap"); // Allow 1 token of id 2
await contract.connect(ownerUser).mint(2, 1); // Mint 1 of id 2
});
it("Ensure mintablity", async function () {
const tokenId = 1;
await contract.connect(ownerUser).add(tokenId, 10, "https://myToken.sap");
await contract.connect(ownerUser).mint(tokenId, 5); // Mint 5
await expect(await contract.connect(ownerUser).balanceOf(ownerUser.address, tokenId)).to.equal(5);
await contract.connect(ownerUser).mint(tokenId, 5); // Mint 5 more
await expect(await contract.connect(ownerUser).balanceOf(ownerUser.address, tokenId)).to.equal(10);
});
it("Prevent minting of non existing token", async function () {
const tokenId = 1;
await expect(contract.connect(ownerUser).mint(tokenId, 5)).to.be.revertedWith('Token with given id does not exists');
});
it("Prevent minting more than allowed", async function () {
const tokenId = 1;
await contract.connect(ownerUser).add(tokenId, 10, "https://myToken.sap");
await expect(contract.connect(ownerUser).mint(tokenId, 10 + 1)).to.be.revertedWith('To many tokens');
});
it("Ensure only owner can mint", async function () {
const tokenId = 1;
await contract.connect(ownerUser).add(tokenId, 10, "https://myToken.sap");
await expect(contract.connect(otherUser).mint(tokenId, 10)).to.be.revertedWith('caller is not the owner');
});
it("Ensure only owner can add", async function () {
const tokenId = 1;
await expect(contract.connect(otherUser).add(tokenId, 10, "https://myToken.sap")).to.be.revertedWith('caller is not the owner');
});
it("Ensure contract level metadata url matches", async function () {
await expect(await contract.connect(ownerUser).contractURI()).to.be.equal('https://myUrl.sap');
});
it("Ensure token level metadata url matches", async function () {
await contract.connect(ownerUser).add(1, 10, "https://myToken1.sap");
await expect(await contract.connect(ownerUser).uri(1)).to.be.equal('https://myToken1.sap');
await contract.connect(ownerUser).add(2, 10, "https://myToken2.sap");
await expect(await contract.connect(ownerUser).uri(2)).to.be.equal('https://myToken2.sap');
});
});
<html lang="en">
<head>
<title>NFT</title>
<style>
body {
background: black;
color: white;
display: flex;
justify-content: center;
align-items: center;
font-family: Arial, Helvetica, sans-serif;
}
.container {
text-align: center;
}
.glitch {
font-size: 5rem;
font-weight: bold;
text-transform: uppercase;
position: relative;
text-shadow: 0.05em 0 0 #00fffc, -0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
animation: glitch 725ms infinite;
}
.glitch span {
position: absolute;
top: 0;
left: 0;
}
.glitch span:first-child {
animation: glitch 500ms infinite;
clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%);
transform: translate(-0.04em, -0.03em);
opacity: 0.75;
}
.glitch span:last-child {
animation: glitch 375ms infinite;
clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%);
transform: translate(0.04em, 0.03em);
opacity: 0.75;
}
@keyframes glitch {
0% {
text-shadow: 0.05em 0 0 #00fffc, -0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
}
15% {
text-shadow: 0.05em 0 0 #00fffc, -0.03em -0.04em 0 #fc00ff,
0.025em 0.04em 0 #fffc00;
}
16% {
text-shadow: -0.05em -0.025em 0 #00fffc, 0.025em 0.035em 0 #fc00ff,
-0.05em -0.05em 0 #fffc00;
}
49% {
text-shadow: -0.05em -0.025em 0 #00fffc, 0.025em 0.035em 0 #fc00ff,
-0.05em -0.05em 0 #fffc00;
}
50% {
text-shadow: 0.05em 0.035em 0 #00fffc, 0.03em 0 0 #fc00ff,
0 -0.04em 0 #fffc00;
}
99% {
text-shadow: 0.05em 0.035em 0 #00fffc, 0.03em 0 0 #fc00ff,
0 -0.04em 0 #fffc00;
}
100% {
text-shadow: -0.05em 0 0 #00fffc, -0.025em -0.04em 0 #fc00ff,
-0.04em -0.025em 0 #fffc00;
}
}
</style>
</head>
<body>
<div class="container">
<p class="glitch">
<span aria-hidden="true">SAP</span>
SAP
<span aria-hidden="true">SAP</span>
</p>
</div>
</body>
</html>
{
"name": "Blog Collection",
"description": "This collection belongs to a tutorial blog",
"image": "ipfs://bafkreifbpdqq3tv5imylrbvpjdnwypnsnpgmwahhxauxs5om25yirsyouq"
}
{
"image":"ipfs://bafkreidmpvevxecjjysiaay2h6kn2f43bupyixtxki4kccs32yt7dbrwme",
"animation_url":"ipfs://bafkreiazww7dwdobtahy72w446hde6buqjuukg4tbibpnmj7sxin2yokeu",
"background_color":"000000",
"description":"SAP Token tutorial",
"name":"SAP Token 1",
"attributes":[
{
"value":"black",
"trait_type":"color"
},
{
"value":"glitch",
"trait_type":"type"
}
]
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
43 | |
25 | |
17 | |
15 | |
10 | |
7 | |
7 | |
6 | |
6 | |
6 |