Contracts for Degen Domain Name Service
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

469 lines
17 KiB

// Run tests:
// npx hardhat test test/nft/angel/angel.test.js
const { expect } = require("chai");
function calculateGasCosts(testName, receipt) {
console.log(testName + " gasUsed: " + receipt.gasUsed);
// coin prices in USD
const eth = 1000;
const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("20", "gwei")) * Number(receipt.gasUsed)), "ether");
const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
}
describe("Degen Anon minting contract", function () {
let tldContract;
const tldName = ".anon";
const tldSymbol = ".ANON";
const tldPrice = 0;
const tldRoyalty = 0;
const tldReferral = 0;
let paymentTokenContract;
const paymentTokenDecimals = 6;
const paymentTokenName = "USDC";
const paymentTokenSymbol = "USDC";
let mintContract;
const maxTotalPayments = ethers.utils.parseUnits("100000", paymentTokenDecimals); // $100k
const price1char = ethers.utils.parseUnits("10000", paymentTokenDecimals); // $10k
const price2char = ethers.utils.parseUnits("3000", paymentTokenDecimals);
const price3char = ethers.utils.parseUnits("999", paymentTokenDecimals);
const price4char = ethers.utils.parseUnits("199", paymentTokenDecimals);
const price5char = ethers.utils.parseUnits("69", paymentTokenDecimals);
let metadataContract;
let signer;
let user1;
let user2;
beforeEach(async function () {
[signer, user1, user2] = await ethers.getSigners();
//----
const DegenForbiddenTlds = await ethers.getContractFactory("DegenForbiddenTlds");
const forbTldsContract = await DegenForbiddenTlds.deploy();
const FlexiDegenMetadata = await ethers.getContractFactory("FlexiDegenMetadata");
const flexiMetadataContract = await FlexiDegenMetadata.deploy();
const DegenTLDFactory = await ethers.getContractFactory("FlexiDegenTLDFactory");
const priceToCreateTld = ethers.utils.parseUnits("100", "ether");
const factoryContract = await DegenTLDFactory.deploy(priceToCreateTld, forbTldsContract.address, flexiMetadataContract.address);
await forbTldsContract.addFactoryAddress(factoryContract.address);
const DegenTLD = await ethers.getContractFactory("FlexiDegenTLD");
tldContract = await DegenTLD.deploy(
tldName,
tldSymbol,
signer.address, // TLD owner
tldPrice,
false, // buying enabled
tldRoyalty,
factoryContract.address,
flexiMetadataContract.address
);
// Create mock payment token
const Erc20ContractDecimals = await ethers.getContractFactory("MockErc20TokenDecimals");
paymentTokenContract = await Erc20ContractDecimals.deploy(paymentTokenName, paymentTokenSymbol, paymentTokenDecimals);
// transfer all signer's tokens to user1
paymentTokenContract.transfer(user1.address, ethers.utils.parseUnits("1000", paymentTokenDecimals));
// deploy Degen Anons metadata contract
const DegenAnonMetadata = await ethers.getContractFactory("DegenAnonMetadata");
metadataContract = await DegenAnonMetadata.deploy();
// add it to the TLD contract
await tldContract.changeMetadataAddress(metadataContract.address);
// Minter contract
const minterCode = await ethers.getContractFactory("DegenAnonMinter");
mintContract = await minterCode.deploy(
paymentTokenContract.address, // payment token address
tldContract.address, // TLD address
metadataContract.address, // metadata contract
maxTotalPayments,
price1char, price2char, price3char, price4char, price5char
);
// set minter contract as TLD minter address
await tldContract.changeMinter(mintContract.address);
// set minter contract in the metadata contract
await metadataContract.changeMinter(mintContract.address);
});
it("should confirm TLD name & symbol", async function () {
const _tldName = await tldContract.name();
expect(_tldName).to.equal(tldName);
const _tldSymbol = await tldContract.symbol();
expect(_tldSymbol).to.equal(tldSymbol);
});
it("should mint two 5+ char domains", async function () {
const featureIds = ["3A1174741911F257FFCA965A000000231", "3A1174741911F257FFCA965A000000121", "3A1174741911F257FFCA965A000000020"];
await mintContract.togglePaused();
// user1 has 1000 payment tokens
// should fail at minting before allowance is set
await expect(mintContract.connect(user1).mint(
"1234", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
featureIds
)).to.be.reverted;
// Give payment token allowance
await paymentTokenContract.connect(user1).approve(
mintContract.address, // spender
price5char // amount
);
// how many domains user1 has before minting
const balanceDomainBefore = await tldContract.balanceOf(user1.address);
expect(balanceDomainBefore).to.equal(0);
// TLD contract owner's balance before minting
const ownerBalanceBefore = await paymentTokenContract.balanceOf(signer.address);
expect(ownerBalanceBefore).to.equal(0);
console.log("Signer's payment token balance before first mint: " + ethers.utils.formatUnits(ownerBalanceBefore, paymentTokenDecimals) + " " + paymentTokenSymbol);
// Mint a domain
const tx = await mintContract.connect(user1).mint(
"user12", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
featureIds
);
const receipt = await tx.wait();
calculateGasCosts("Mint", receipt);
// get metadata
const metadata1 = await tldContract.tokenURI(1);
const mdJson1 = Buffer.from(metadata1.substring(29), "base64");
const mdResult1 = JSON.parse(mdJson1);
expect(mdResult1.name).to.equal("user12.degen");
//console.log(mdResult1.image);
const balanceDomainAfter = await tldContract.balanceOf(user1.address);
expect(balanceDomainAfter).to.equal(1);
const domainHolder = await tldContract.getDomainHolder("user12");
expect(domainHolder).to.equal(user1.address);
// TLD contract owner's balance after minting
const ownerBalanceAfter = await paymentTokenContract.balanceOf(signer.address);
expect(ownerBalanceAfter).to.equal(price5char); // signer gets both royalty and the rest of the domain payment
console.log("Signer's payment token balance after first mint: " + ethers.utils.formatUnits(ownerBalanceAfter, paymentTokenDecimals) + " " + paymentTokenSymbol);
// Give payment token allowance
await paymentTokenContract.connect(user1).approve(
mintContract.address, // spender
price5char // amount
);
// should not fail at minting another domain
await mintContract.connect(user1).mint(
"user1second", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
featureIds
);
const balanceDomainAfter2 = await tldContract.balanceOf(user1.address);
expect(balanceDomainAfter2).to.equal(2);
// get metadata2
const metadata2 = await tldContract.tokenURI(2);
const mdJson2 = Buffer.from(metadata2.substring(29), "base64");
const mdResult2 = JSON.parse(mdJson2);
expect(mdResult2.name).to.equal("user1second.degen");
//console.log(mdResult1.image);
// should FAIL at minting a domain with 4 chars
// Give payment token allowance
await paymentTokenContract.connect(user1).approve(
mintContract.address, // spender
price5char // amount
);
// should fail if domain is 4 chars, but payment is for 5 chars (too low)
await expect(mintContract.connect(user1).mint(
"user", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
featureIds
)).to.be.reverted;
const balanceDomainAfter3 = await tldContract.balanceOf(user1.address);
expect(balanceDomainAfter3).to.equal(2);
// should revert becuase feature IDs is already taken
await expect(mintContract.connect(user1).mint(
"hello", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
["3A1174741911F257FFCA965A000000231", "3A1174741911F257FFCA965A000000121"] // already used feature IDs
)).to.be.revertedWith("Feature IDs already used");
const balanceDomainAfter4 = await tldContract.balanceOf(user1.address);
expect(balanceDomainAfter4).to.equal(2);
});
it("should pause minting when max payment amount is reached", async function () {
// max payment amount is $100k
// 10 domains (1 char for $10k)
const featureIds = [
"3A1174741911F257FFCA965A000000231",
"3A1174741911F257FFCA965A000000121",
"3A1174741911F257FFCA965A000000120",
"3A1174741911F257FFCA965A000000110",
"3A1174741911F257FFCA965A000000000",
"3A1174741911F257FFCA965A000000100",
"3A1174741911F257FFCA965A000000210",
"3A1174741911F257FFCA965A000000200",
"3A1174741911F257FFCA965A000000220",
"3A1174741911F257FFCA965A000000221"
];
const pausedBefore1 = await mintContract.paused();
expect(pausedBefore1).to.be.true;
await mintContract.togglePaused();
const pausedBefore2 = await mintContract.paused();
expect(pausedBefore2).to.be.false;
// mint new payment tokens for user1
await paymentTokenContract.connect(user1).mint(
user1.address,
maxTotalPayments
);
// Give payment token allowance
await paymentTokenContract.connect(user1).approve(
mintContract.address, // spender
maxTotalPayments // amount
);
// how many domains user1 has before minting
const balanceDomainBefore = await tldContract.balanceOf(user1.address);
expect(balanceDomainBefore).to.equal(0);
// TLD contract owner's balance before minting
const ownerBalanceBefore = await paymentTokenContract.balanceOf(signer.address);
expect(ownerBalanceBefore).to.equal(0);
console.log("Signer's payment token balance before first mint: " + ethers.utils.formatUnits(ownerBalanceBefore, paymentTokenDecimals) + " " + paymentTokenSymbol);
// Mint 10 domains
await mintContract.connect(user1).mint(
"0", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[0]]
);
await mintContract.connect(user1).mint(
"1", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[1]]
);
await mintContract.connect(user1).mint(
"2", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[2]]
);
await mintContract.connect(user1).mint(
"3", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[3]]
);
await mintContract.connect(user1).mint(
"4", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[4]]
);
await mintContract.connect(user1).mint(
"5", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[5]]
);
await mintContract.connect(user1).mint(
"6", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[6]]
);
await mintContract.connect(user1).mint(
"7", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[7]]
);
await mintContract.connect(user1).mint(
"8", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[8]]
);
await mintContract.connect(user1).mint(
"9", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
[featureIds[9]]
);
// get metadata for 5.degen
const metadata = await tldContract.tokenURI(6);
const mdJson = Buffer.from(metadata.substring(29), "base64");
const mdResult = JSON.parse(mdJson);
expect(mdResult.name).to.equal("5.degen");
expect(mdResult.paid).to.equal(price1char);
console.log(mdResult.attributes);
const pausedAfter = await mintContract.paused();
expect(pausedAfter).to.be.true;
const balanceDomainAfter = await tldContract.balanceOf(user1.address);
expect(balanceDomainAfter).to.equal(10);
// TLD contract owner's balance after minting
const ownerBalanceAfter = await paymentTokenContract.balanceOf(signer.address);
expect(ownerBalanceAfter).to.equal(maxTotalPayments); // signer gets both royalty and the rest of the domain payment
console.log("Signer's payment token balance after first mint: " + ethers.utils.formatUnits(ownerBalanceAfter, paymentTokenDecimals) + " " + paymentTokenSymbol);
// TRY ANOTHER MINT
// Give payment token allowance
await paymentTokenContract.connect(user1).approve(
mintContract.address, // spender
maxTotalPayments // amount
);
// should revert because minting is paused
await expect(mintContract.connect(user1).mint(
"hello", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
["3A1174741911F257FFCA965A000000331"]
)).to.be.revertedWith("Minting paused");
// unpause minting
await mintContract.togglePaused();
// should revert because max payment is reached
await expect(mintContract.connect(user1).mint(
"hello", // domain name (without TLD)
user1.address, // domain holder
ethers.constants.AddressZero, // no referrer in this case
["3A1174741911F257FFCA965A000000331"]
)).to.be.revertedWith("Max total payments reached");
});
it("should change domain price (only owner)", async function () {
const priceBefore = await mintContract.price5char();
expect(priceBefore).to.equal(price5char);
const newPrice = ethers.utils.parseUnits("70", paymentTokenDecimals); // domain price is in payment tokens
await mintContract.changePrice(
newPrice,
5 // chars (price for domains with 5 chars)
);
const priceAfter = await mintContract.price5char();
expect(priceAfter).to.equal(newPrice);
// cannot be zero
await expect(mintContract.changePrice(0, 5)).to.be.revertedWith('Cannot be zero');
// if user is not owner, the tx should revert
await expect(mintContract.connect(user1).changePrice(123456, 5)).to.be.revertedWith('Ownable: caller is not the owner');
});
it("should change referral fee (only owner)", async function () {
const refBefore = await mintContract.referralFee();
expect(refBefore).to.equal(1000);
const newRef = 1500;
await mintContract.changeReferralFee(newRef);
const refAfter = await mintContract.referralFee();
expect(refAfter).to.equal(newRef);
// cannot exceed 20%
await expect(mintContract.changeReferralFee(2100)).to.be.revertedWith('Cannot exceed 20%');
// if user is not owner, the tx should revert
await expect(mintContract.connect(user1).changeReferralFee(666)).to.be.revertedWith('Ownable: caller is not the owner');
});
it("should recover ERC-20 tokens mistakenly sent to contract address", async function () {
const ERC20MockToken = await ethers.getContractFactory("MockErc20Token");
const mockErc20Contract = await ERC20MockToken.deploy("Mock", "MOCK");
const signerBalance = await mockErc20Contract.balanceOf(signer.address);
expect(Number(ethers.utils.formatEther(signerBalance))).to.equal(1000); // 1000 tokens minted in the ERC20 contract constructor
const mintContractBalance = await mockErc20Contract.balanceOf(mintContract.address);
expect(mintContractBalance).to.equal(0); // should be 0
// send 200 tokens to contract
await mockErc20Contract.transfer(mintContract.address, ethers.utils.parseEther("200"));
const signerBalance2 = await mockErc20Contract.balanceOf(signer.address);
expect(Number(ethers.utils.formatEther(signerBalance2))).to.equal(800);
const mintContractBalance2 = await mockErc20Contract.balanceOf(mintContract.address);
expect(Number(ethers.utils.formatEther(mintContractBalance2))).to.equal(200);
// recover tokens from contract
await mintContract.recoverERC20(
mockErc20Contract.address, // token address
ethers.utils.parseEther("200"), // token amount
signer.address // recipient
);
const signerBalance3 = await mockErc20Contract.balanceOf(signer.address);
expect(Number(ethers.utils.formatEther(signerBalance3))).to.equal(1000); // back to 1000
const mintContractBalance3 = await mockErc20Contract.balanceOf(mintContract.address);
expect(Number(ethers.utils.formatEther(mintContractBalance3))).to.equal(0); // back to 0
});
});