import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import chai from 'chai'; import chaiAsPromised from 'chai-as-promised'; import { BigNumber, constants } from 'ethers'; import { ethers } from 'hardhat'; import { ForwarderDeployer } from '../scripts/deployers/ForwarderDeployer'; import { VenueRegistarDeployer } from '../scripts/deployers/VenueRegistarDeployer'; import { toETH, toUSDC } from './utils'; import { USDCDeployer } from '../scripts/deployers/USDCDeployer'; import { ERC721EFactoryDeployer } from '../scripts/deployers/ERC721EFactoryDeployer'; import { ERC721E, ERC721EFactory, NFTWalletFactory, ReservForwarder, USDC, VenueFactory, VenueRegistar, VenueSBT, } from '../types'; import { VenueFactoryDeployer } from '../scripts/deployers/VenueFactoryDeployer'; import { getLatestTimestamp, timeIncreaseTo } from '../scripts/time'; import { NFTWalletFactoryDeployer } from '../scripts/deployers/NFTWalletFactoryDeployer'; import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; import { isAddress } from 'ethers/lib/utils'; chai.use(chaiAsPromised); const { expect } = chai; let signer: SignerWithAddress; let otherSigner: SignerWithAddress; let buyer: SignerWithAddress; let receiver: SignerWithAddress; let venueRegistar: VenueRegistar; let usdc: USDC; let forwarder: ReservForwarder; let myVenue: VenueSBT; let erc721EFactory: ERC721EFactory; let myEvent: ERC721E; let nftWalletFactory: NFTWalletFactory; describe('NFT_ERC721E', function () { let saleEnd: number; let timestamp: number; before(async () => { [signer, otherSigner, buyer, receiver] = await ethers.getSigners(); }); beforeEach(async () => { const USDC = new USDCDeployer(signer); usdc = await USDC.deploy({ name: 'USDC', symbol: 'USDC', }); const venueRegistarDeployer = new VenueRegistarDeployer(signer); venueRegistar = await venueRegistarDeployer.deployAndInitialize(toETH(1), signer.address); const VenueFactory = new VenueFactoryDeployer(signer); const venueFactory = (await VenueFactory.deploy(venueRegistar.address)) as VenueFactory; await venueRegistar.functions.setVenueFactory(venueFactory.address); await venueRegistar.functions.setVenueSBTVersion('1.0.0-beta.0+fob.rsv.iVenueSBT'); const ReservForwarder = new ForwarderDeployer(signer); forwarder = await ReservForwarder.deployAndInitialize({ venueRegistar: venueRegistar.address, }); await venueRegistar.functions.setForwarder(forwarder.address); await venueRegistar.functions.whitelistPaymentToken(usdc.address, true); const erc721EFactoryDeployer = new ERC721EFactoryDeployer(signer); erc721EFactory = await erc721EFactoryDeployer.deploy({ forwarder: forwarder.address, }); const nftWalletFactoryDeployer = new NFTWalletFactoryDeployer(signer); nftWalletFactory = await nftWalletFactoryDeployer.deploy(signer.address); await forwarder.functions.addfactory(erc721EFactory.address); expect(await forwarder.getFactoryAt(1)).to.be.equal(erc721EFactory.address); await venueRegistar.functions.ownerDeployVenue('Santiagos', 'SDV', 'sdv.com/', otherSigner.address); const venues = await venueRegistar.getVenues(); myVenue = (await ethers.getContractAt('VenueSBT', venues[0], otherSigner)) as VenueSBT; const factoryType = await forwarder.getFactoryPosition(erc721EFactory.address); timestamp = (await getLatestTimestamp()).toNumber(); const abi = ethers.utils.defaultAbiCoder; const extraData = abi.encode(['uint256'], [timestamp + 100]); // console.log(abi.encode(['uint256'], [1675281600])); const tx = await ( await myVenue.functions.mint(factoryType, 'MyEvent', 'MEV', 'www.sdv.mev.com/', usdc.address, extraData) ).wait(); const event = tx.events?.find((e) => e.event === 'NewEvent')?.args?._event; myEvent = (await ethers.getContractAt('ERC721E', event)) as ERC721E; saleEnd = timestamp + 60; }); describe('Deploy Wallet', function () { it('Deploys wallet', async () => { await expect(nftWalletFactory.functions.deployWallet(123)) .to.emit(nftWalletFactory, 'NewWallet') .withArgs(123, anyValue); const walletAddress = await nftWalletFactory.userToWallet(123); expect(isAddress(walletAddress)).to.be.true; expect(walletAddress).to.not.be.equal(constants.AddressZero); }); it('Tries to deploy another wallet with same user id', async () => { await nftWalletFactory.functions.deployWallet(123); await expect(nftWalletFactory.functions.deployWallet(123)).to.be.revertedWith('UserAlreadyHasAWallet'); }); }); describe('NFT Wallet', () => { it('Receives a token', async () => { const tx = await ( await myEvent.connect(otherSigner).functions.createEntry('VIP', toUSDC(100), 10, 10, saleEnd) ).wait(); let entryPos = tx.events?.find((e) => e.event === 'NewEntry')?.args?.pos as BigNumber; const prices = new Array(10).fill(toUSDC(150)); await nftWalletFactory.functions.deployWallet(123); const walletAddress = await nftWalletFactory.userToWallet(123); await myEvent.connect(signer).buyTickets(walletAddress, 123, entryPos, 10, prices); expect(await myEvent.balanceOf(walletAddress)).to.be.equal(10); }); it('Attaches user', async () => { await nftWalletFactory.functions.deployWallet(123); const walletAddress = await nftWalletFactory.userToWallet(123); const nftWallet = await ethers.getContractAt('NFTWallet', walletAddress); await nftWallet.attachUser(buyer.address); expect(await nftWallet.hasRole(await nftWallet.NFT_OWNER(), buyer.address)).to.be.true; }); it('Transfers out NFT', async () => { const tx = await ( await myEvent.connect(otherSigner).functions.createEntry('VIP', toUSDC(100), 10, 10, saleEnd) ).wait(); let entryPos = tx.events?.find((e) => e.event === 'NewEntry')?.args?.pos as BigNumber; const prices = new Array(10).fill(toUSDC(150)); await nftWalletFactory.functions.deployWallet(123); const walletAddress = await nftWalletFactory.userToWallet(123); const nftWallet = await ethers.getContractAt('NFTWallet', walletAddress); await myEvent.connect(signer).buyTickets(walletAddress, 123, entryPos, 10, prices); await nftWallet.functions.transferOut(myEvent.address, receiver.address, 0); expect(await myEvent.balanceOf(walletAddress)).to.be.equal(9); expect(await myEvent.balanceOf(receiver.address)).to.be.equal(1); await nftWallet.attachUser(buyer.address); await nftWallet.functions.transferOut(myEvent.address, receiver.address, 1); expect(await myEvent.balanceOf(walletAddress)).to.be.equal(8); expect(await myEvent.balanceOf(receiver.address)).to.be.equal(2); await nftWallet.connect(buyer).transferOut(myEvent.address, receiver.address, 2); expect(await myEvent.balanceOf(walletAddress)).to.be.equal(7); expect(await myEvent.balanceOf(receiver.address)).to.be.equal(3); }); it('Tries to transfer out token where user is not NFT_OWNER role', async () => { const tx = await ( await myEvent.connect(otherSigner).functions.createEntry('VIP', toUSDC(100), 10, 10, saleEnd) ).wait(); let entryPos = tx.events?.find((e) => e.event === 'NewEntry')?.args?.pos as BigNumber; const prices = new Array(10).fill(toUSDC(150)); await nftWalletFactory.functions.deployWallet(123); const walletAddress = await nftWalletFactory.userToWallet(123); await myEvent.connect(signer).buyTickets(walletAddress, 123, entryPos, 10, prices); const nftWallet = await ethers.getContractAt('NFTWallet', walletAddress); await expect(nftWallet.connect(buyer).transferOut(myEvent.address, receiver.address, 1)).to.be.revertedWith( 'NFTWalletCannotTransfer', ); }); describe('Secondary Sales', () => { it('Buys ticket on secondary sales', async () => { const splitter = await venueRegistar.splitterByVenue(myVenue.address); const tx = await ( await myEvent.connect(otherSigner).functions.createEntry('VIP', toUSDC(100), 10, 10, saleEnd) ).wait(); let entryPos = tx.events?.find((e) => e.event === 'NewEntry')?.args?.pos as BigNumber; const prices = new Array(10).fill(toUSDC(150)); await nftWalletFactory.functions.deployWallet(123); const walletAddress = await nftWalletFactory.userToWallet(123); await myEvent.connect(signer).buyTickets(walletAddress, 123, entryPos, 10, prices); const nftWallet = await ethers.getContractAt('NFTWallet', walletAddress); await usdc.functions.mint(buyer.address, toUSDC(1500)); await usdc.connect(buyer).functions.approve(myEvent.address, toUSDC(1500)); const tokensToBuy = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; const newPrices: Array = new Array(10).fill(toUSDC(200)); const buyerBeforeBalance = await usdc.balanceOf(buyer.address); const walletBalanceBefore = await usdc.balanceOf(walletAddress); await expect(myEvent.connect(buyer).functions.buyBatch(tokensToBuy, newPrices)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 0, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 1, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 2, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 3, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 4, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 5, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 6, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 7, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 8, toUSDC(150)) .to.emit(myEvent, 'Sell') .withArgs(buyer.address, walletAddress, 9, toUSDC(150)); expect(await myEvent.balanceOf(buyer.address)).to.be.equal(10); expect(await myEvent.ownerOf(9)).to.be.equal(buyer.address); expect(await myEvent.priceByToken(9)).to.be.equal(toUSDC(200)); const totalPaid = toUSDC(150).mul(10); expect(await usdc.balanceOf(buyer.address)).to.be.equal(buyerBeforeBalance.sub(totalPaid)); expect(await usdc.balanceOf(walletAddress)).to.be.equal( walletBalanceBefore.add(totalPaid.sub(totalPaid.mul(10).div(100))), ); //minus 10% expect(await usdc.balanceOf(splitter)).to.be.equal(totalPaid.sub(totalPaid.mul(90).div(100))); //minus 90% await expect(nftWallet.connect(otherSigner).withdrawToken(usdc.address)).to.be.reverted; await nftWallet.attachUser(otherSigner.address); await nftWallet.connect(otherSigner).withdrawToken(usdc.address); expect(await usdc.balanceOf(otherSigner.address)).to.be.equal(totalPaid.sub(totalPaid.mul(10).div(100))); expect(await usdc.balanceOf(walletAddress)).to.be.equal(0); }); }); }); });