import { expect } from 'chai' import hre from "hardhat"; import { getBytes, solidityPackedKeccak256, } from 'ethers' import { loadFixture, } from "@nomicfoundation/hardhat-toolbox/network-helpers"; describe('GameItemMall', function() { async function deployOneContract() { const [owner, otherAccount] = await hre.ethers.getSigners(); const verifier = owner.address; const OperatorAllowlist = await hre.ethers.getContractFactory("OperatorAllowlist"); const operatorAllowlist = await OperatorAllowlist.deploy(owner.address); const CFFT = await hre.ethers.getContractFactory("ImmutableERC20MinterBurnerPermit"); const ft = await CFFT.deploy(owner.address, owner.address, owner.address, "test usdc", "usdc", '100000000000000000000000000'); await ft.grantMinterRole(owner.address); await ft.mint(otherAccount.address, '1000'); const GameItemMall = await hre.ethers.getContractFactory("GameItemMall"); const mall = await GameItemMall.deploy( ft.target, verifier, verifier, 3600); const chainId = hre.network.config.chainId await operatorAllowlist.grantRegistrarRole(owner.address) await operatorAllowlist.addAddressToAllowlist([mall.target]) return { mall, owner, otherAccount, verifier, ft, chainId }; } describe("Deployment", function () { it('should deploy GameIteMall with the correct verifier', async function() { const { mall, verifier } = await loadFixture(deployOneContract); expect(await mall.verifier()).to.equal(verifier); }); it('should deploy GameItemMall with the correct FT address', async function() { const { mall, ft } = await loadFixture(deployOneContract); expect(await mall.erc20Supported(ft.target)).to.equal(true); }); }) describe("buy", function () { it('should buy item success', async function() { const { mall, ft, otherAccount, owner, chainId } = await loadFixture(deployOneContract); const amount = 100; // @ts-ignore await ft.connect(otherAccount).approve(mall.target, amount); const nonce = (Math.random() * 1000) | 0; const now = Date.now() / 1000 | 0; const orderId = '100000001' /** function getMessageHash( address _buyer, address _passport, uint256 _orderId, address _currency, uint256 _amount, address _contract, uint256 _chainId, uint256 _signTime, uint256 _saltNonce ) */ let localMsgHash = solidityPackedKeccak256(["address","address", "uint256", "address", "uint256", "address", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, orderId, ft.target, amount, mall.target, chainId, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); /** * function buy( address passport, uint256 orderId, address currency, uint256 amount, uint256 signTime, uint256 saltNonce, bytes calldata signature ) */ //@ts-ignore await expect(mall.connect(otherAccount).buy(otherAccount.address, orderId, ft.target, amount, now, nonce, signature)) .to.emit(mall, "ItemSoldOut") .withArgs(otherAccount.address, otherAccount.address, orderId, ft.target, amount); expect(await ft.balanceOf(otherAccount.address)).to.equal(900); }); it('should revert buy item for signature error', async function() { const { mall, ft, otherAccount, owner, chainId } = await loadFixture(deployOneContract); const amount = 100; // @ts-ignore await ft.connect(otherAccount).approve(mall.target, amount); const nonce = (Math.random() * 1000) | 0; const now = Date.now() / 1000 | 0; const orderId = '100000001' let localMsgHash = solidityPackedKeccak256(["address","address", "uint256", "address", "uint256", "address", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, orderId, ft.target, amount, mall.target, chainId, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); //@ts-ignore await expect(mall.connect(otherAccount).buy(otherAccount.address, orderId, ft.target, amount + 1, now, nonce, signature)).to.be.revertedWith("invalid signature"); }); it('should revert buy item for not approval', async function() { const { mall, ft, otherAccount, owner, chainId } = await loadFixture(deployOneContract); const amount = 100; const nonce = (Math.random() * 1000) | 0; const now = Date.now() / 1000 | 0; const orderId = '100000001' let localMsgHash = solidityPackedKeccak256(["address","address", "uint256", "address", "uint256", "address", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, orderId, ft.target, amount, mall.target, chainId, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); //@ts-ignore await expect(mall.connect(otherAccount).buy(otherAccount.address, orderId, ft.target, amount, now, nonce, signature)).to.be.revertedWith("ERC20: insufficient allowance"); }); it('should revert buy item for signature used', async function() { const { mall, ft, otherAccount, owner, chainId } = await loadFixture(deployOneContract); const amount = 100; // @ts-ignore await ft.connect(otherAccount).approve(mall.target, amount); const nonce = (Math.random() * 1000) | 0; const now = Date.now() / 1000 | 0; const orderId = '100000001' let localMsgHash = solidityPackedKeccak256(["address","address", "uint256", "address", "uint256", "address", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, orderId, ft.target, amount, mall.target, chainId, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); //@ts-ignore await mall.connect(otherAccount).buy(otherAccount.address, orderId, ft.target, amount, now, nonce, signature) //@ts-ignore await expect(mall.connect(otherAccount).buy(otherAccount.address, orderId, ft.target, amount, now, nonce, signature)).to.be.revertedWith("signature used. please send another transaction with new signature"); }); it('should revert buy item for unsupport currency', async function() { const { mall, ft, otherAccount, owner, chainId } = await loadFixture(deployOneContract); const amount = 100; // @ts-ignore await ft.connect(otherAccount).approve(mall.target, amount); const nonce = (Math.random() * 1000) | 0; const now = Date.now() / 1000 | 0; const orderId = '100000001' await mall.removeERC20Support(ft.target); let localMsgHash = solidityPackedKeccak256(["address","address", "uint256", "address", "uint256", "address", "uint256", "uint256", "uint256"], [otherAccount.address, otherAccount.address, orderId, ft.target, amount, mall.target, chainId, now, nonce]); const signature = await owner.signMessage(getBytes(localMsgHash)); //@ts-ignore await expect(mall.connect(otherAccount).buy(otherAccount.address, orderId, ft.target, amount, now, nonce, signature)).to.be.revertedWith("currency is not supported"); }); }) })