UNPKG

@gooddollar/goodprotocol

Version:
446 lines (382 loc) 14.3 kB
import hre, { ethers, upgrades } from "hardhat"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; import FeeFormulaABI from "@gooddollar/goodcontracts/build/contracts/SenderFeeFormula.json"; import FeeFormulaRecipientABI from "@gooddollar/goodcontracts/build/contracts/FeeFormula.json"; import TransferAndCallMockABI from "@gooddollar/goodcontracts/build/contracts/TransferAndCallMock.json"; import { IIdentity, GoodDollar, ISuperGoodDollar } from "../../types"; import { createDAO, deploySuperGoodDollar } from "../helpers"; import { Contract } from "ethers"; const BN = ethers.BigNumber; describe("GoodDollar Token", () => { let receiver: Contract, identity, feeFormula, newFormula, token: ISuperGoodDollar, unCappedToken: ISuperGoodDollar, cappedToken: ISuperGoodDollar, newtoken: ISuperGoodDollar, feetoken: ISuperGoodDollar, founder, outsider, whitelisted; let signers; let avatar, gd: ISuperGoodDollar, Controller, id: IIdentity, superFluid; const regularTokenState = async () => { const deployedDAO = createDAO("regular"); const token = (await ethers.getContractAt( "GoodDollar", ( await deployedDAO ).gd )) as GoodDollar; await token.mint(founder.address, 100000000); return token; }; before(async () => { [founder, whitelisted, outsider, ...signers] = await ethers.getSigners(); let { daoCreator, controller, avatar: av, gd: gooddollar, identity: idv2, sfContracts } = await loadFixture(createDAO); superFluid = sfContracts; avatar = av; identity = await ethers.getContractAt("IdentityV2", idv2); const FeeFormulaFactory = new ethers.ContractFactory( FeeFormulaABI.abi, FeeFormulaABI.bytecode, founder ); const FeeFormulaRecipientFactory = new ethers.ContractFactory( FeeFormulaRecipientABI.abi, FeeFormulaRecipientABI.bytecode, founder ); receiver = await new ethers.ContractFactory( TransferAndCallMockABI.abi, TransferAndCallMockABI.bytecode, founder ).deploy(); token = (await ethers.getContractAt( "ISuperGoodDollar", gooddollar )) as ISuperGoodDollar; feeFormula = FeeFormulaFactory.attach(await token.formula()); // isProduction ? "GoodDollar" : "GoodDollar Dev", // "G$", // 0, // FeeFormula.address, // Identity.address, // ethers.constants.AddressZero, // daoCreator.address // unCappedToken = (await upgrades.deployProxy( // await ethers.getContractFactory("GoodDollar"), // [ // "Test", // "TDD", // 0, // feeFormula.address, // identity.address, // receiver.address, // founder.address // ], // { // initializer: // "initialize(string,string,uint256,address,address,address,address)" // } // )) as GoodDollar; unCappedToken = (await deploySuperGoodDollar(superFluid, [ "Test", "TDD", 0, feeFormula.address, identity.address, receiver.address, founder.address ])) as ISuperGoodDollar; cappedToken = (await deploySuperGoodDollar(superFluid, [ "Test", "TDD", 1000, feeFormula.address, identity.address, receiver.address, founder.address ])) as ISuperGoodDollar; newFormula = await FeeFormulaFactory.deploy(1); const newFormulaRecipient = await FeeFormulaRecipientFactory.deploy(1); newtoken = (await deploySuperGoodDollar(superFluid, [ "gd", "gd", 1000000, newFormula.address, identity.address, av, founder.address ])) as ISuperGoodDollar; feetoken = (await deploySuperGoodDollar(superFluid, [ "gd", "gd", 1000000, newFormulaRecipient.address, identity.address, av, founder.address ])) as ISuperGoodDollar; // cappedToken = (await upgrades.deployProxy( // await ethers.getContractFactory("GoodDollar"), // [ // "Test", // "TDD", // 1000, // feeFormula.address, // identity.address, // receiver.address, // founder.address // ], // { // initializer: // "initialize(string,string,uint256,address,address,address,address)" // } // )) as GoodDollar; // newtoken = (await upgrades.deployProxy( // await ethers.getContractFactory("GoodDollar"), // [ // "gd", // "gd", // 1000000, // newFormula.address, // identity.address, // av, // founder.address // ], // { // initializer: // "initialize(string,string,uint256,address,address,address,address)" // } // )) as GoodDollar; await token.mint(founder.address, 100000000); await newtoken.mint(whitelisted.address, 100000); await feetoken.mint(whitelisted.address, 100000); await token.transfer(whitelisted.address, 100000); await token.transfer(founder.address, 10000); await token.transfer(outsider.address, 1000); await identity.addWhitelisted(whitelisted.address); await identity.addWhitelisted(outsider.address); }); it("should have low gas cost for transfer with regular token", async () => { const token = await loadFixture(regularTokenState); const firstTime = await ( await token.transfer(signers[2].address, 1000) ).wait(); const secondTime = await ( await token.transfer(signers[2].address, 1000) ).wait(); expect(firstTime.gasUsed).lt(70000); expect(secondTime.gasUsed).lt(55000); }); it("should have high gas cost for transfer with superfluid token", async () => { const firstTime = await ( await token.transfer(signers[2].address, 1000) ).wait(); const secondTime = await ( await token.transfer(signers[2].address, 1000) ).wait(); expect(firstTime.gasUsed).gt(160000); expect(secondTime.gasUsed).gt(149000); }); it("should have avatar as default pauser", async () => { expect(await token.isPauser(avatar)).true; }); it("should let owner set identity and formula", async () => { await expect(newtoken.setFormula(ethers.constants.AddressZero)).not .reverted; await expect(newtoken.setIdentity(ethers.constants.AddressZero)).not .reverted; expect(await newtoken.identity()).eq(ethers.constants.AddressZero); expect(await newtoken.formula()).eq(ethers.constants.AddressZero); await newtoken.setIdentity(identity.address); await newtoken.setFormula(newFormula.address); }); it("should not let non owner set identity and formula", async () => { await expect( newtoken.connect(signers[0]).setFormula(ethers.constants.AddressZero) ).revertedWith(/not owner/); await expect( newtoken.connect(signers[0]).setIdentity(ethers.constants.AddressZero) ).revertedWith(/not owner/); }); it("should fail transfer", async () => { let data = "0x"; await expect( token .connect(outsider) .transferAndCall( receiver.address, (await token.balanceOf(outsider.address)).add(100000), data ) ).reverted; }); it("should not transfer and not call function", async () => { let data = "0x"; await expect( token.transferAndCall(receiver.address, 1000, data) ).revertedWith(/Contract Fallback failed/); expect(await receiver.wasCalled()).false; }); it("should transfer, not call and return true if not contract", async () => { let data = "0x"; await expect(token.transferAndCall(founder.address, 300, data)).not .reverted; }); it("should transfer and call correct function on receiver contract", async () => { let data = receiver.interface.encodeFunctionData("mockTransfer", []); expect(await token.transferAndCall(receiver.address, 1000, data)); expect(await receiver.wasCalled()).true; }); it("should increase allowance", async () => { expect(await token.increaseAllowance(whitelisted.address, 2000)); }); it("should allow to transfer from", async () => { await expect( token .connect(whitelisted) .transferFrom(founder.address, whitelisted.address, 1000) ).not.reverted; }); it("should decrease allowance", async () => { await expect(token.decreaseAllowance(whitelisted.address, 1000)).not .reverted; }); it("should allow to burn", async () => { expect(token.connect(whitelisted)["burn(uint256)"](1000)).not.reverted; }); it("should allow to burn from", async () => { await expect(token.approve(whitelisted.address, 2000)).not.reverted; await expect(token.connect(whitelisted).burnFrom(founder.address, 2000)).not .reverted; }); it("should not allow to mint beyond cap", async () => { await expect(unCappedToken.mint(founder.address, 1000)).not.reverted; await expect(cappedToken.mint(founder.address, 1200)).revertedWith( /Cannot increase supply beyond cap/ ); }); it("should collect transaction fee", async () => { const oldReserve = await newtoken.balanceOf(avatar); const initBalance = await newtoken.balanceOf(whitelisted.address); await newtoken.connect(whitelisted).transfer(outsider.address, 20000); const postBalance = await newtoken.balanceOf(whitelisted.address); // Check that reserve has received fees const reserve = (await newtoken.balanceOf(avatar)) as any; const reserveDiff = reserve.sub(oldReserve); const totalFees = await newtoken["getFees(uint256)"](20000).then( _ => _["0"] ); expect(totalFees).gt(0); expect(postBalance).eq(initBalance.sub(20000).sub(totalFees)); //senderpays expect(reserveDiff.toString()).to.be.equal(totalFees.toString()); }); it("should collect transaction fee for transferAndCall", async () => { const oldReserve = await newtoken.balanceOf(avatar); const initBalance = await newtoken.balanceOf(whitelisted.address); const tx = await newtoken .connect(whitelisted) .transferAndCall(outsider.address, 20000, "0x00"); const postBalance = await newtoken.balanceOf(whitelisted.address); const events = (await tx.wait()).events; const parser = await ethers.getContractFactory("SuperGoodDollar"); const parsedEvents = events.map(_ => { try { return parser.interface.parseLog(_); } catch (e) { return {}; } }) as any; const transferEvents = parsedEvents.filter( _ => _.name === "Transfer" && _.args.to === outsider.address ); // Check that reserve has received fees const reserve = (await newtoken.balanceOf(avatar)) as any; const reserveDiff = reserve.sub(oldReserve); const totalFees = await newtoken["getFees(uint256)"](20000).then( _ => _["0"] ); expect(totalFees).gt(0); expect(reserveDiff.toString()).to.be.equal(totalFees.toString()); expect(postBalance).eq(initBalance.sub(20000).sub(totalFees)); //senderpays expect(transferEvents.length).gt(1); //atleast regular + erc677 Transfer transferEvents.forEach(_ => { expect(_.args.value || _.args.amount).eq(20000); }); }); it("should collect transaction fee from recipient for transferAndCall", async () => { const oldReserve = await feetoken.balanceOf(avatar); const initBalance = await feetoken.balanceOf(whitelisted.address); const tx = await feetoken .connect(whitelisted) .transferAndCall(outsider.address, 5000, "0x00"); const postBalance = await feetoken.balanceOf(whitelisted.address); const events = (await tx.wait()).events; const parser = await ethers.getContractFactory("SuperGoodDollar"); const parsedEvents = events.map(_ => { try { return parser.interface.parseLog(_); } catch (e) { return {}; } }) as any; const transferEvents = parsedEvents.filter( _ => _.name === "Transfer" && _.args.to === outsider.address ); // Check that reserve has received fees const reserve = (await feetoken.balanceOf(avatar)) as any; const reserveDiff = reserve.sub(oldReserve); const totalFees = await feetoken["getFees(uint256)"](5000).then( _ => _["0"] ); expect(totalFees).gt(0); expect(reserveDiff.toString()).to.be.equal(totalFees.toString()); expect(postBalance).eq(initBalance.sub(5000)); //recipient pays fee expect(transferEvents.length).gt(1); //atleast regular + erc677 Transfer transferEvents.forEach(_ => { expect(_.args.value || _.args.amount).eq(5000 - totalFees.toNumber()); }); }); it("should get same results from overloaded getFees method", async () => { const totalFees = await newtoken["getFees(uint256)"](3000) //fix overload issue .then(_ => _["0"]); const totalFees2 = await newtoken["getFees(uint256,address,address)"]( 3000, whitelisted.address, whitelisted.address ).then(_ => _["0"]); expect(totalFees2.toNumber()).to.be.gt(0); expect(totalFees2.toString()).to.be.equal(totalFees.toString()); }); it("should collect transaction fee from sender", async () => { const oldReserve = await newtoken.balanceOf(avatar); const oldFounder = await newtoken.balanceOf(founder.address); const newWhitelistedBefore = await newtoken.balanceOf(whitelisted.address); await newtoken.connect(whitelisted).transfer(founder.address, 20000); // Check that reserve has received fees const reserve = (await newtoken.balanceOf(avatar)) as any; const newFounder = (await newtoken.balanceOf(founder.address)) as any; const newWhitelisted = await newtoken.balanceOf(whitelisted.address); const reserveDiff = reserve.sub(oldReserve); const founderDiff = newFounder.sub(oldFounder); const totalFees = await newtoken["getFees(uint256)"](20000).then( _ => _["0"] ); expect(totalFees).gt(0); expect(reserveDiff.toString()).to.be.equal(totalFees.toString()); expect(founderDiff.toString()).to.be.equal("20000"); expect(newWhitelistedBefore.sub(newWhitelisted)).to.be.equal("20200"); //20000 + 1% }); });