mirror of
https://github.com/lens-protocol/core.git
synced 2026-04-22 03:02:03 -04:00
fix: T-1448 delete V1 failing hardhat tests that are covered in foundry or are modules
This commit is contained in:
@@ -1,574 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import hre from 'hardhat';
|
||||
import { expect } from 'chai';
|
||||
import { CollectNFT__factory, FollowNFT__factory } from '../../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
collectReturningTokenIds,
|
||||
getAbbreviation,
|
||||
getCollectWithSigParts,
|
||||
getTimestamp,
|
||||
} from '../../helpers/utils';
|
||||
import {
|
||||
lensHub,
|
||||
freeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
testWallet,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
abiCoder,
|
||||
user,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Collecting', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Generic', function () {
|
||||
context('Negatives', function () {
|
||||
it('User two should fail to collect without being a follower', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('User two should follow, then transfer the followNFT and fail to collect', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNftAddr = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
await expect(
|
||||
FollowNFT__factory.connect(followNftAddr, userTwo).transferFrom(
|
||||
userTwoAddress,
|
||||
userAddress,
|
||||
1
|
||||
)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('User two should fail to collect a nonexistent publication', async function () {
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, 0, 0, [])).to.be.revertedWith(
|
||||
ERRORS.PUBLICATION_DOES_NOT_EXIST
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('Collecting should work if the collector is the publication owner even when he is not following himself and follow NFT was not deployed', async function () {
|
||||
await expect(lensHub.collect(userAddress, FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Collecting should work if the collector is the publication owner even when he is not following himself and follow NFT was deployed', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.transferFrom(userAddress, userTwoAddress, 1)).to.not.be.reverted;
|
||||
|
||||
await expect(lensHub.collect(userAddress, FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Should return the expected token IDs when collecting publications', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(testWallet).follow(testWallet.address, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
expect(
|
||||
await collectReturningTokenIds({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: 1,
|
||||
data: [],
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
expect(
|
||||
await collectReturningTokenIds({
|
||||
sender: userTwo,
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: 1,
|
||||
data: [],
|
||||
},
|
||||
})
|
||||
).to.eq(2);
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const { v, r, s } = await getCollectWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
[],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
expect(
|
||||
await collectReturningTokenIds({
|
||||
vars: {
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
},
|
||||
})
|
||||
).to.eq(3);
|
||||
|
||||
expect(
|
||||
await collectReturningTokenIds({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: 1,
|
||||
data: [],
|
||||
},
|
||||
})
|
||||
).to.eq(4);
|
||||
});
|
||||
|
||||
it('UserTwo should follow, then collect, receive a collect NFT with the expected properties', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])
|
||||
).to.not.be.reverted;
|
||||
const timestamp = await getTimestamp();
|
||||
|
||||
const collectNFTAddr = await lensHub.getCollectNFT(FIRST_PROFILE_ID, 1);
|
||||
expect(collectNFTAddr).to.not.eq(ZERO_ADDRESS);
|
||||
const collectNFT = CollectNFT__factory.connect(collectNFTAddr, userTwo);
|
||||
const id = await collectNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
|
||||
const name = await collectNFT.name();
|
||||
const symbol = await collectNFT.symbol();
|
||||
const pointer = await collectNFT.getSourcePublicationPointer();
|
||||
const owner = await collectNFT.ownerOf(id);
|
||||
const mintTimestamp = await collectNFT.mintTimestampOf(id);
|
||||
const tokenData = await collectNFT.tokenDataOf(id);
|
||||
|
||||
const expectedName = MOCK_PROFILE_HANDLE + '-Collect-' + '1';
|
||||
const expectedSymbol = getAbbreviation(MOCK_PROFILE_HANDLE) + '-Cl-' + '1';
|
||||
|
||||
expect(id).to.eq(1);
|
||||
expect(name).to.eq(expectedName);
|
||||
expect(symbol).to.eq(expectedSymbol);
|
||||
expect(pointer[0]).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pointer[1]).to.eq(1);
|
||||
expect(owner).to.eq(userTwoAddress);
|
||||
expect(tokenData.owner).to.eq(userTwoAddress);
|
||||
expect(tokenData.mintTimestamp).to.eq(timestamp);
|
||||
expect(mintTimestamp).to.eq(timestamp);
|
||||
});
|
||||
|
||||
it('UserTwo should follow, then mirror, then collect on their mirror, receive a collect NFT with expected properties', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'mockhandle',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, [])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const collectNFTAddr = await lensHub.getCollectNFT(FIRST_PROFILE_ID, 1);
|
||||
expect(collectNFTAddr).to.not.eq(ZERO_ADDRESS);
|
||||
const collectNFT = CollectNFT__factory.connect(collectNFTAddr, userTwo);
|
||||
const id = await collectNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
|
||||
const name = await collectNFT.name();
|
||||
const symbol = await collectNFT.symbol();
|
||||
const pointer = await collectNFT.getSourcePublicationPointer();
|
||||
|
||||
const expectedName = MOCK_PROFILE_HANDLE + '-Collect-' + '1';
|
||||
const expectedSymbol = getAbbreviation(MOCK_PROFILE_HANDLE) + '-Cl-' + '1';
|
||||
expect(id).to.eq(1);
|
||||
expect(name).to.eq(expectedName);
|
||||
expect(symbol).to.eq(expectedSymbol);
|
||||
expect(pointer[0]).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pointer[1]).to.eq(1);
|
||||
});
|
||||
|
||||
it('UserTwo should follow, then mirror, mirror their mirror then collect on their latest mirror, receive a collect NFT with expected properties', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'mockhandle',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: secondProfileId,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 2, [])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const collectNFTAddr = await lensHub.getCollectNFT(FIRST_PROFILE_ID, 1);
|
||||
expect(collectNFTAddr).to.not.eq(ZERO_ADDRESS);
|
||||
const collectNFT = CollectNFT__factory.connect(collectNFTAddr, userTwo);
|
||||
const id = await collectNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
|
||||
const name = await collectNFT.name();
|
||||
const symbol = await collectNFT.symbol();
|
||||
const pointer = await collectNFT.getSourcePublicationPointer();
|
||||
|
||||
const expectedName = MOCK_PROFILE_HANDLE + '-Collect-' + '1';
|
||||
const expectedSymbol = getAbbreviation(MOCK_PROFILE_HANDLE) + '-Cl-' + '1';
|
||||
expect(id).to.eq(1);
|
||||
expect(name).to.eq(expectedName);
|
||||
expect(symbol).to.eq(expectedSymbol);
|
||||
expect(pointer[0]).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pointer[1]).to.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Meta-tx', function () {
|
||||
context('Negatives', function () {
|
||||
it('TestWallet should fail to collect with sig with signature deadline mismatch', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(FIRST_PROFILE_ID, '1', [], nonce, '0');
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to collect with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(FIRST_PROFILE_ID, '1', [], nonce, '0');
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: '0',
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to collect with sig with invalid nonce', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
[],
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to collect with sig without being a follower', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
[],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should sign attempt to collect with sig, cancel via empty permitForAll, fail to collect with sig', async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).follow(testWallet.address, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
[],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await cancelWithPermitForAll();
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('TestWallet should follow, then collect with sig, receive a collect NFT with expected properties', async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).follow(testWallet.address, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
[],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const collectNFTAddr = await lensHub.getCollectNFT(FIRST_PROFILE_ID, 1);
|
||||
expect(collectNFTAddr).to.not.eq(ZERO_ADDRESS);
|
||||
const collectNFT = CollectNFT__factory.connect(collectNFTAddr, userTwo);
|
||||
const id = await collectNFT.tokenOfOwnerByIndex(testWallet.address, 0);
|
||||
const name = await collectNFT.name();
|
||||
const symbol = await collectNFT.symbol();
|
||||
const pointer = await collectNFT.getSourcePublicationPointer();
|
||||
|
||||
const expectedName = MOCK_PROFILE_HANDLE + '-Collect-' + '1';
|
||||
const expectedSymbol = getAbbreviation(MOCK_PROFILE_HANDLE) + '-Cl-' + '1';
|
||||
expect(id).to.eq(1);
|
||||
expect(name).to.eq(expectedName);
|
||||
expect(symbol).to.eq(expectedSymbol);
|
||||
expect(pointer[0]).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pointer[1]).to.eq(1);
|
||||
});
|
||||
|
||||
it('TestWallet should follow, mirror, then collect with sig on their mirror', async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).follow(testWallet.address, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(testWallet).createProfile({
|
||||
to: testWallet.address,
|
||||
handle: 'mockhandle',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(testWallet).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getCollectWithSigParts(
|
||||
secondProfileId.toString(),
|
||||
'1',
|
||||
[],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.collectWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
collector: testWallet.address,
|
||||
profileId: secondProfileId,
|
||||
pubId: '1',
|
||||
data: [],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const collectNFTAddr = await lensHub.getCollectNFT(FIRST_PROFILE_ID, 1);
|
||||
expect(collectNFTAddr).to.not.eq(ZERO_ADDRESS);
|
||||
const collectNFT = CollectNFT__factory.connect(collectNFTAddr, userTwo);
|
||||
const id = await collectNFT.tokenOfOwnerByIndex(testWallet.address, 0);
|
||||
const name = await collectNFT.name();
|
||||
const symbol = await collectNFT.symbol();
|
||||
const pointer = await collectNFT.getSourcePublicationPointer();
|
||||
|
||||
const expectedName = MOCK_PROFILE_HANDLE + '-Collect-' + '1';
|
||||
const expectedSymbol = getAbbreviation(MOCK_PROFILE_HANDLE) + '-Cl-' + '1';
|
||||
expect(id).to.eq(1);
|
||||
expect(name).to.eq(expectedName);
|
||||
expect(symbol).to.eq(expectedSymbol);
|
||||
expect(pointer[0]).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pointer[1]).to.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,389 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { FollowNFT__factory } from '../../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
expectEqualArrays,
|
||||
followReturningTokenIds,
|
||||
getAbbreviation,
|
||||
getFollowWithSigParts,
|
||||
getTimestamp,
|
||||
} from '../../helpers/utils';
|
||||
import {
|
||||
lensHub,
|
||||
FIRST_PROFILE_ID,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
testWallet,
|
||||
user,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
MOCK_PROFILE_URI,
|
||||
userAddress,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Following', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
context('Generic', function () {
|
||||
context('Negatives', function () {
|
||||
it('UserTwo should fail to follow a nonexistent profile', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID + 1], [[]])
|
||||
).to.be.revertedWith(ERRORS.TOKEN_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to follow with array mismatch', async function () {
|
||||
await expect(
|
||||
lensHub
|
||||
.connect(userTwo)
|
||||
.follow(userTwoAddress, [FIRST_PROFILE_ID, FIRST_PROFILE_ID], [[]])
|
||||
).to.be.revertedWith(ERRORS.ARRAY_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to follow a profile that has been burned', async function () {
|
||||
await expect(lensHub.burn(FIRST_PROFILE_ID)).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.be.revertedWith(ERRORS.TOKEN_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to follow profile with id 0', async function () {
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [0], [[]])).to.be.revertedWith(
|
||||
ERRORS.TOKEN_DOES_NOT_EXIST
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('UserTwo should follow profile 1, receive a followNFT with ID 1, followNFT properties should be correct', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const timestamp = await getTimestamp();
|
||||
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
expect(followNFT.address).to.not.eq(ZERO_ADDRESS);
|
||||
const id = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
|
||||
const name = await followNFT.name();
|
||||
const symbol = await followNFT.symbol();
|
||||
const owner = await followNFT.ownerOf(id);
|
||||
const mintTimestamp = await followNFT.mintTimestampOf(id);
|
||||
const followNFTURI = await followNFT.tokenURI(id);
|
||||
const tokenData = await followNFT.tokenDataOf(id);
|
||||
|
||||
expect(id).to.eq(1);
|
||||
expect(name).to.eq(MOCK_PROFILE_HANDLE + '-Follower');
|
||||
expect(symbol).to.eq(getAbbreviation(MOCK_PROFILE_HANDLE) + '-Fl');
|
||||
expect(owner).to.eq(userTwoAddress);
|
||||
expect(tokenData.owner).to.eq(userTwoAddress);
|
||||
expect(tokenData.mintTimestamp).to.eq(timestamp);
|
||||
expect(followNFTURI).to.eq(MOCK_FOLLOW_NFT_URI);
|
||||
expect(mintTimestamp).to.eq(timestamp);
|
||||
});
|
||||
|
||||
it('UserTwo should follow profile 1 twice, receiving followNFTs with IDs 1 and 2', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
const idOne = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
|
||||
const idTwo = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 1);
|
||||
expect(idOne).to.eq(1);
|
||||
expect(idTwo).to.eq(2);
|
||||
});
|
||||
|
||||
it('UserTwo should follow profile 1 3 times in the same call, receive IDs 1,2 and 3', async function () {
|
||||
await expect(
|
||||
lensHub
|
||||
.connect(userTwo)
|
||||
.follow(
|
||||
userTwoAddress,
|
||||
[FIRST_PROFILE_ID, FIRST_PROFILE_ID, FIRST_PROFILE_ID],
|
||||
[[], [], []]
|
||||
)
|
||||
).to.not.be.reverted;
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
const idOne = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
|
||||
const idTwo = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 1);
|
||||
const idThree = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 2);
|
||||
expect(idOne).to.eq(1);
|
||||
expect(idTwo).to.eq(2);
|
||||
expect(idThree).to.eq(3);
|
||||
});
|
||||
|
||||
it('Should return the expected token IDs when following profiles', async function () {
|
||||
expectEqualArrays(
|
||||
await followReturningTokenIds({
|
||||
vars: {
|
||||
profileIds: [FIRST_PROFILE_ID, FIRST_PROFILE_ID],
|
||||
datas: [[], []],
|
||||
},
|
||||
}),
|
||||
[1, 2]
|
||||
);
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const { v, r, s } = await getFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[[]],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
expectEqualArrays(
|
||||
await followReturningTokenIds({
|
||||
vars: {
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
},
|
||||
}),
|
||||
[3]
|
||||
);
|
||||
|
||||
expectEqualArrays(
|
||||
await followReturningTokenIds({
|
||||
sender: userTwo,
|
||||
vars: {
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
},
|
||||
}),
|
||||
[4]
|
||||
);
|
||||
|
||||
expectEqualArrays(
|
||||
await followReturningTokenIds({
|
||||
vars: {
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
},
|
||||
}),
|
||||
[5]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Meta-tx', function () {
|
||||
context('Negatives', function () {
|
||||
it('TestWallet should fail to follow with sig with signature deadline mismatch', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts([FIRST_PROFILE_ID], [[]], nonce, '0');
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to follow with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts([FIRST_PROFILE_ID], [[]], nonce, '0');
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: '0',
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to follow with sig with invalid nonce', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[[]],
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
);
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to follow a nonexistent profile with sig', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID + 1],
|
||||
[[]],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID + 1],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.TOKEN_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('TestWallet should sign attempt to follow with sig, cancel with empty permitForAll, then fail to follow with sig', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[[]],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await cancelWithPermitForAll();
|
||||
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('TestWallet should follow profile 1 with sig, receive a follow NFT with ID 1, follow NFT name and symbol should be correct', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[[]],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
datas: [[]],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
const id = await followNFT.tokenOfOwnerByIndex(testWallet.address, 0);
|
||||
expect(id).to.eq(1);
|
||||
const name = await followNFT.name();
|
||||
const symbol = await followNFT.symbol();
|
||||
expect(name).to.eq(MOCK_PROFILE_HANDLE + '-Follower');
|
||||
expect(symbol).to.eq(getAbbreviation(MOCK_PROFILE_HANDLE) + '-Fl');
|
||||
});
|
||||
|
||||
it('TestWallet should follow profile 1 with sig twice in the same call, receive follow NFTs with IDs 1 and 2', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID, FIRST_PROFILE_ID],
|
||||
[[], []],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.followWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID, FIRST_PROFILE_ID],
|
||||
datas: [[], []],
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
const idOne = await followNFT.tokenOfOwnerByIndex(testWallet.address, 0);
|
||||
const idTwo = await followNFT.tokenOfOwnerByIndex(testWallet.address, 1);
|
||||
expect(idOne).to.eq(1);
|
||||
expect(idTwo).to.eq(2);
|
||||
const name = await followNFT.name();
|
||||
const symbol = await followNFT.symbol();
|
||||
expect(name).to.eq(MOCK_PROFILE_HANDLE + '-Follower');
|
||||
expect(symbol).to.eq(getAbbreviation(MOCK_PROFILE_HANDLE) + '-Fl');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { governance, lensHub, makeSuiteCleanRoom, userAddress } from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Governance Functions', function () {
|
||||
context('Negatives', function () {
|
||||
it('User should not be able to call governance functions', async function () {
|
||||
await expect(lensHub.setGovernance(userAddress)).to.be.revertedWith(ERRORS.NOT_GOVERNANCE);
|
||||
await expect(lensHub.whitelistFollowModule(userAddress, true)).to.be.revertedWith(
|
||||
ERRORS.NOT_GOVERNANCE
|
||||
);
|
||||
await expect(lensHub.whitelistReferenceModule(userAddress, true)).to.be.revertedWith(
|
||||
ERRORS.NOT_GOVERNANCE
|
||||
);
|
||||
await expect(lensHub.whitelistCollectModule(userAddress, true)).to.be.revertedWith(
|
||||
ERRORS.NOT_GOVERNANCE
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('Governance should successfully whitelist and unwhitelist modules', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(userAddress, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistReferenceModule(userAddress, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(userAddress, true)
|
||||
).to.not.be.reverted;
|
||||
expect(await lensHub.isFollowModuleWhitelisted(userAddress)).to.eq(true);
|
||||
expect(await lensHub.isReferenceModuleWhitelisted(userAddress)).to.eq(true);
|
||||
expect(await lensHub.isCollectModuleWhitelisted(userAddress)).to.eq(true);
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(userAddress, false)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistReferenceModule(userAddress, false)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(userAddress, false)
|
||||
).to.not.be.reverted;
|
||||
expect(await lensHub.isFollowModuleWhitelisted(userAddress)).to.eq(false);
|
||||
expect(await lensHub.isReferenceModuleWhitelisted(userAddress)).to.eq(false);
|
||||
expect(await lensHub.isCollectModuleWhitelisted(userAddress)).to.eq(false);
|
||||
});
|
||||
|
||||
it('Governance should successfully change the governance address', async function () {
|
||||
await expect(lensHub.connect(governance).setGovernance(userAddress)).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,795 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import hre from 'hardhat';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
commentReturningTokenId,
|
||||
getCommentWithSigParts,
|
||||
} from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
freeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
mockReferenceModule,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
OTHER_MOCK_URI,
|
||||
testWallet,
|
||||
timedFeeCollectModule,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Publishing Comments', function () {
|
||||
context('Generic', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(timedFeeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistReferenceModule(mockReferenceModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
it('UserTwo should fail to publish a comment to a profile owned by User', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.EXECUTOR_INVALID);
|
||||
});
|
||||
|
||||
it('User should fail to comment with an unwhitelisted collect module', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.COLLECT_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('User should fail to comment with an unwhitelisted reference module', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: userAddress,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.REFERENCE_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('User should fail to comment with invalid collect module data format', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: [0x2, 0x12, 0x20],
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.NO_REASON_ABI_DECODE);
|
||||
});
|
||||
|
||||
it('User should fail to comment with invalid reference module data format', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: [0x12, 0x23],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.NO_REASON_ABI_DECODE);
|
||||
});
|
||||
|
||||
it('User should fail to comment on a publication that does not exist', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 3,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.PUBLICATION_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('User should fail to comment on the same comment they are creating (pubId = 2, commentCeption)', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 2,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.CANNOT_COMMENT_ON_SELF);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should create a comment with empty collect module data, reference module, and reference module data, fetched comment data should be accurate', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 2);
|
||||
expect(pub.profileIdPointed).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pub.pubIdPointed).to.eq(1);
|
||||
expect(pub.contentURI).to.eq(MOCK_URI);
|
||||
expect(pub.collectModule).to.eq(freeCollectModule.address);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
it('Should return the expected token IDs when commenting publications', async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).createProfile({
|
||||
to: testWallet.address,
|
||||
handle: 'testwallet',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(testWallet).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID + 1,
|
||||
OTHER_MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
expect(
|
||||
await commentReturningTokenId({
|
||||
vars: {
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID + 1,
|
||||
contentURI: OTHER_MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
expect(
|
||||
await commentReturningTokenId({
|
||||
sender: userTwo,
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID + 2,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
expect(
|
||||
await commentReturningTokenId({
|
||||
sender: testWallet,
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID + 1,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
},
|
||||
})
|
||||
).to.eq(2);
|
||||
|
||||
expect(
|
||||
await commentReturningTokenId({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
},
|
||||
})
|
||||
).to.eq(2);
|
||||
});
|
||||
|
||||
it('User should create a post using the mock reference module as reference module, then comment on that post', async function () {
|
||||
const data = abiCoder.encode(['uint256'], ['1']);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: data,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 2,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Meta-tx', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).createProfile({
|
||||
to: testWallet.address,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(testWallet).post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
it('Testwallet should fail to comment with sig with signature deadline mismatch', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to comment with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: '0',
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to comment with sig with invalid nonce', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to comment with sig with unwhitelisted collect module', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
userAddress,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: userAddress,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.COLLECT_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to comment with sig with unwhitelisted reference module', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
mockReferenceModule.address,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.REFERENCE_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to comment with sig on a publication that does not exist', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
OTHER_MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'3',
|
||||
referenceModuleData,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: OTHER_MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '3',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.PUBLICATION_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to comment with sig on the comment they are creating (commentCeption)', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
OTHER_MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'2',
|
||||
referenceModuleData,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: OTHER_MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '2',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.CANNOT_COMMENT_ON_SELF);
|
||||
});
|
||||
|
||||
it('TestWallet should sign attempt to comment with sig, cancel via empty permitForAll, then fail to comment with sig', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
OTHER_MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
// hre.tracer.enabled = true;
|
||||
// hre.tracer.sloads = true;
|
||||
// hre.tracer.sstores = true;
|
||||
await cancelWithPermitForAll();
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: OTHER_MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('TestWallet should comment with sig, fetched comment data should be accurate', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getCommentWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
OTHER_MOCK_URI,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.commentWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: OTHER_MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: referenceModuleData,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 2);
|
||||
expect(pub.profileIdPointed).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pub.pubIdPointed).to.eq(1);
|
||||
expect(pub.contentURI).to.eq(OTHER_MOCK_URI);
|
||||
expect(pub.collectModule).to.eq(freeCollectModule.address);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,635 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
getMirrorWithSigParts,
|
||||
mirrorReturningTokenId,
|
||||
} from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
freeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
mockReferenceModule,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
testWallet,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Publishing mirrors', function () {
|
||||
context('Generic', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistReferenceModule(mockReferenceModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
it('UserTwo should fail to publish a mirror to a profile owned by User', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.EXECUTOR_INVALID);
|
||||
});
|
||||
|
||||
it('User should fail to mirror with an unwhitelisted reference module', async function () {
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: userAddress,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.REFERENCE_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('User should fail to mirror with invalid reference module data format', async function () {
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: [0x12, 0x23],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.NO_REASON_ABI_DECODE);
|
||||
});
|
||||
|
||||
it('User should fail to mirror a publication that does not exist', async function () {
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 2,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.PUBLICATION_DOES_NOT_EXIST);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('Should return the expected token IDs when mirroring publications', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: testWallet.address,
|
||||
handle: 'testwallet',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
expect(
|
||||
await mirrorReturningTokenId({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
},
|
||||
})
|
||||
).to.eq(2);
|
||||
|
||||
expect(
|
||||
await mirrorReturningTokenId({
|
||||
sender: userTwo,
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID + 2,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 2,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID + 1,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
expect(
|
||||
await mirrorReturningTokenId({
|
||||
vars: {
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID + 1,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
expect(
|
||||
await mirrorReturningTokenId({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID + 1,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
},
|
||||
})
|
||||
).to.eq(3);
|
||||
});
|
||||
|
||||
it('User should create a mirror with empty reference module and reference module data, fetched mirror data should be accurate', async function () {
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 2);
|
||||
expect(pub.profileIdPointed).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pub.pubIdPointed).to.eq(1);
|
||||
expect(pub.contentURI).to.eq('');
|
||||
expect(pub.collectModule).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
it('User should mirror a mirror with empty reference module and reference module data, fetched mirror data should be accurate and point to the original post', async function () {
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 2,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 3);
|
||||
expect(pub.profileIdPointed).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pub.pubIdPointed).to.eq(1);
|
||||
expect(pub.contentURI).to.eq('');
|
||||
expect(pub.collectModule).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
it('User should create a post using the mock reference module as reference module, then mirror that post', async function () {
|
||||
const data = abiCoder.encode(['uint256'], ['1']);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: data,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 2,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Meta-tx', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).createProfile({
|
||||
to: testWallet.address,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(testWallet).post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
it('Testwallet should fail to mirror with sig with signature deadline mismatch', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to mirror with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: '0',
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to mirror with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to mirror with sig with unwhitelisted reference module', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
userAddress,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: userAddress,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.REFERENCE_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to mirror a publication with sig that does not exist yet', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'2',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '2',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.PUBLICATION_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('TestWallet should sign attempt to mirror with sig, cancel via empty permitForAll, then fail to mirror with sig', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await cancelWithPermitForAll();
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('Testwallet should mirror with sig, fetched mirror data should be accurate', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'1',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '1',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 2);
|
||||
expect(pub.profileIdPointed).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pub.pubIdPointed).to.eq(1);
|
||||
expect(pub.contentURI).to.eq('');
|
||||
expect(pub.collectModule).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
it('TestWallet should mirror a mirror with sig, fetched mirror data should be accurate', async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
|
||||
const { v, r, s } = await getMirrorWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
FIRST_PROFILE_ID,
|
||||
'2',
|
||||
referenceModuleData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirrorWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: '2',
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 3);
|
||||
expect(pub.profileIdPointed).to.eq(FIRST_PROFILE_ID);
|
||||
expect(pub.pubIdPointed).to.eq(1);
|
||||
expect(pub.contentURI).to.eq('');
|
||||
expect(pub.collectModule).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,664 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
BadMockEIP1271Implementer__factory,
|
||||
MockEIP1271Implementer__factory,
|
||||
} from '../../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
getPostWithSigMessageParts,
|
||||
getPostWithSigParts,
|
||||
postReturningTokenId,
|
||||
} from '../../helpers/utils';
|
||||
import {
|
||||
freeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
mockModuleData,
|
||||
mockReferenceModule,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
testWallet,
|
||||
timedFeeCollectModule,
|
||||
userAddress,
|
||||
userTwo,
|
||||
abiCoder,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Publishing Posts', function () {
|
||||
context('Generic', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
it('UserTwo should fail to post to a profile owned by User', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.EXECUTOR_INVALID);
|
||||
});
|
||||
|
||||
it('User should fail to post with an unwhitelisted collect module', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.COLLECT_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('User should fail to post with an unwhitelisted reference module', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: userAddress,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.REFERENCE_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('User should fail to post with invalid collect module data format', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(timedFeeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: [0x12, 0x34],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.NO_REASON_ABI_DECODE);
|
||||
});
|
||||
|
||||
it('User should fail to post with invalid reference module data format', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistReferenceModule(mockReferenceModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: [0x12, 0x23],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.NO_REASON_ABI_DECODE);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('Should return the expected token IDs when mirroring publications', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: testWallet.address,
|
||||
handle: 'testwallet',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
expect(
|
||||
await postReturningTokenId({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
expect(
|
||||
await postReturningTokenId({
|
||||
sender: userTwo,
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID + 2,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID + 1,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
expect(
|
||||
await postReturningTokenId({
|
||||
vars: {
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID + 1,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
},
|
||||
})
|
||||
).to.eq(1);
|
||||
|
||||
expect(
|
||||
await postReturningTokenId({
|
||||
vars: {
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
},
|
||||
})
|
||||
).to.eq(2);
|
||||
});
|
||||
|
||||
it('User should create a post with empty collect and reference module data, fetched post data should be accurate', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 1);
|
||||
expect(pub.profileIdPointed).to.eq(0);
|
||||
expect(pub.pubIdPointed).to.eq(0);
|
||||
expect(pub.contentURI).to.eq(MOCK_URI);
|
||||
expect(pub.collectModule).to.eq(freeCollectModule.address);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
it('User should create a post with a whitelisted collect and reference module', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistReferenceModule(mockReferenceModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: mockReferenceModule.address,
|
||||
referenceModuleInitData: mockModuleData,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Meta-tx', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).createProfile({
|
||||
to: testWallet.address,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
it('Testwallet should fail to post with sig with signature deadline mismatch', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
ZERO_ADDRESS,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to post with sig with invalid deadline', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
ZERO_ADDRESS,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: '0',
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to post with sig with invalid nonce', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
ZERO_ADDRESS,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: ZERO_ADDRESS,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to post with sig with an unwhitelisted collect module', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = [];
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
userAddress,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: userAddress,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.COLLECT_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('Testwallet should fail to post with sig with an unwhitelisted reference module', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
userAddress,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: userAddress,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.REFERENCE_MODULE_NOT_WHITELISTED);
|
||||
});
|
||||
|
||||
it('TestWallet should sign attempt to post with sig, cancel via empty permitForAll, then fail to post with sig', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await cancelWithPermitForAll();
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should deploy bad EIP1271 implementer, transfer profile to it, then fail to post with sig', async function () {
|
||||
const sigContract = await new BadMockEIP1271Implementer__factory(testWallet).deploy();
|
||||
const nonce = (await lensHub.sigNonces(sigContract.address)).toNumber();
|
||||
await expect(
|
||||
lensHub
|
||||
.connect(testWallet)
|
||||
.transferFrom(testWallet.address, sigContract.address, FIRST_PROFILE_ID)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const { v, r, s } = await getPostWithSigMessageParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('TestWallet should post with sig, fetched post data should be accurate', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const referenceModuleData = [];
|
||||
const { v, r, s } = await getPostWithSigParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const pub = await lensHub.getPub(FIRST_PROFILE_ID, 1);
|
||||
expect(pub.profileIdPointed).to.eq(0);
|
||||
expect(pub.pubIdPointed).to.eq(0);
|
||||
expect(pub.contentURI).to.eq(MOCK_URI);
|
||||
expect(pub.collectModule).to.eq(freeCollectModule.address);
|
||||
expect(pub.collectNFT).to.eq(ZERO_ADDRESS);
|
||||
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
it('TestWallet should deploy EIP1271 implementer, transfer profile to it, then post with sig', async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const sigContract = await new MockEIP1271Implementer__factory(testWallet).deploy();
|
||||
const nonce = (await lensHub.sigNonces(sigContract.address)).toNumber();
|
||||
await expect(
|
||||
lensHub
|
||||
.connect(testWallet)
|
||||
.transferFrom(testWallet.address, sigContract.address, FIRST_PROFILE_ID)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const collectModuleInitData = abiCoder.encode(['bool'], [true]);
|
||||
const referenceModuleInitData = [];
|
||||
const { v, r, s } = await getPostWithSigMessageParts(
|
||||
FIRST_PROFILE_ID,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
referenceModuleInitData,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.postWithSig({
|
||||
delegatedSigner: ZERO_ADDRESS,
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: referenceModuleInitData,
|
||||
sig: {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
},
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,742 +0,0 @@
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { ERC20__factory } from '../../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
BPS_MAX,
|
||||
currency,
|
||||
feeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
lensHubImpl,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
moduleGlobals,
|
||||
REFERRAL_FEE_BPS,
|
||||
treasuryAddress,
|
||||
TREASURY_FEE_BPS,
|
||||
user,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Fee Collect Module', function () {
|
||||
const DEFAULT_COLLECT_PRICE = parseEther('10');
|
||||
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(feeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
moduleGlobals.connect(governance).whitelistCurrency(currency.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Publication Creation', function () {
|
||||
it('user should fail to post with fee collect module using unwhitelisted currency', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, userTwoAddress, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with fee collect module using zero recipient', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, ZERO_ADDRESS, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with fee collect module using referral fee greater than max BPS', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, 10001, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with fee collect module using zero amount', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[0, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Collecting', function () {
|
||||
beforeEach(async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should fail to process collect without being the hub', async function () {
|
||||
await expect(
|
||||
feeCollectModule
|
||||
.connect(userTwo)
|
||||
.processCollect(
|
||||
0,
|
||||
0,
|
||||
userTwoAddress,
|
||||
userTwoAddress,
|
||||
FIRST_PROFILE_ID,
|
||||
1,
|
||||
[]
|
||||
)
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Governance should set the treasury fee BPS to zero, userTwo collecting should not emit a transfer event to the treasury', async function () {
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(1);
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, DEFAULT_COLLECT_PRICE],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, governance should set the treasury fee BPS to zero, userTwo collecting their mirror should not emit a transfer event to the treasury', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(2);
|
||||
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const amount = DEFAULT_COLLECT_PRICE.sub(expectedReferralAmount);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, amount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userTwoAddress, expectedReferralAmount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without following', async function () {
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected price in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected currency in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without first approving module with currency', async function () {
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['uint256'], [DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected price in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected currency in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should post with fee collect module as the collect module and data, correct events should be emitted', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
const tx = lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'PostCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
1,
|
||||
MOCK_URI,
|
||||
feeCollectModule.address,
|
||||
[collectModuleInitData],
|
||||
ZERO_ADDRESS,
|
||||
[],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should post with the fee collect module as the collect module and data, fetched publication data should be accurate', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
const postTimestamp = await getTimestamp();
|
||||
|
||||
const fetchedData = await feeCollectModule.getPublicationData(FIRST_PROFILE_ID, 1);
|
||||
expect(fetchedData.amount).to.eq(DEFAULT_COLLECT_PRICE);
|
||||
expect(fetchedData.recipient).to.eq(userAddress);
|
||||
expect(fetchedData.currency).to.eq(currency.address);
|
||||
expect(fetchedData.referralFee).to.eq(REFERRAL_FEE_BPS);
|
||||
expect(fetchedData.followerOnly).to.eq(true);
|
||||
});
|
||||
|
||||
it('User should post with the fee collect module as the collect module and data, allowing non-followers to collect, user two collects without following, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, false]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with the fee collect module as the collect module and data, user two follows, then collects and pays fee, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with the fee collect module as the collect module and data, user two follows, then collects twice, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(BigNumber.from(DEFAULT_COLLECT_PRICE).mul(2))
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount.mul(2));
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount.mul(2));
|
||||
});
|
||||
|
||||
it('User should post with the fee collect module as the collect module and data, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferrerAmount = BigNumber.from(MAX_UINT256)
|
||||
.sub(DEFAULT_COLLECT_PRICE)
|
||||
.add(expectedReferralAmount);
|
||||
const expectedRecipientAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.sub(expectedReferralAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(expectedReferrerAmount);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with the fee collect module as the collect module and data, with no referral fee, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, 0, true]
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: feeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,189 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
approvalFollowModule,
|
||||
freeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
user,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
abiCoder,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Free Collect Module', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Collecting', function () {
|
||||
it('UserTwo should fail to collect without following without any follow module set', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])).to.be.revertedWith(
|
||||
ERRORS.FOLLOW_INVALID
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, [])).to.be.revertedWith(
|
||||
ERRORS.FOLLOW_INVALID
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should post with the free collect module as the collect module and data, allowing non-followers to collect, user two collects without following', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [false]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should collect with success when following if the configuration only allows followers', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should collect with success when following according the follow module set', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(approvalFollowModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
approvalFollowModule.connect(user).approve(FIRST_PROFILE_ID, [userTwoAddress], [true])
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, collect with success from their mirror when following the original profile which has no follow module set', async function () {
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, [])).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,885 +0,0 @@
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
BPS_MAX,
|
||||
currency,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
limitedFeeCollectModule,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
moduleGlobals,
|
||||
REFERRAL_FEE_BPS,
|
||||
treasuryAddress,
|
||||
TREASURY_FEE_BPS,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Limited Fee Collect Module', function () {
|
||||
const DEFAULT_COLLECT_PRICE = parseEther('10');
|
||||
const DEFAULT_COLLECT_LIMIT = 3;
|
||||
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(limitedFeeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
moduleGlobals.connect(governance).whitelistCurrency(currency.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Publication Creation', function () {
|
||||
it('user should fail to post with limited fee collect module using zero collect limit', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[0, DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited fee collect module using unwhitelisted currency', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
userTwoAddress,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited fee collect module using zero recipient', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
ZERO_ADDRESS,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited fee collect module using referral fee greater than max BPS', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_LIMIT, DEFAULT_COLLECT_PRICE, currency.address, userAddress, 10001, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited fee collect module using zero amount', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_LIMIT, 0, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Collecting', function () {
|
||||
beforeEach(async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should fail to process collect without being the hub', async function () {
|
||||
await expect(
|
||||
limitedFeeCollectModule
|
||||
.connect(userTwo)
|
||||
.processCollect(0, 0, userTwoAddress, userTwoAddress, FIRST_PROFILE_ID, 1, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Governance should set the treasury fee BPS to zero, userTwo collecting should not emit a transfer event to the treasury', async function () {
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(1);
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, DEFAULT_COLLECT_PRICE],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, governance should set the treasury fee BPS to zero, userTwo collecting their mirror should not emit a transfer event to the treasury', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(2);
|
||||
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const amount = DEFAULT_COLLECT_PRICE.sub(expectedReferralAmount);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, amount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userTwoAddress, expectedReferralAmount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without following', async function () {
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected price in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected currency in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without first approving module with currency', async function () {
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected price in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected currency in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should post with limited fee collect module as the collect module and data, correct events should be emitted', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
const tx = lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'PostCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
1,
|
||||
MOCK_URI,
|
||||
limitedFeeCollectModule.address,
|
||||
collectModuleInitData,
|
||||
ZERO_ADDRESS,
|
||||
[],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, fetched publication data should be accurate', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const fetchedData = await limitedFeeCollectModule.getPublicationData(FIRST_PROFILE_ID, 1);
|
||||
expect(fetchedData.collectLimit).to.eq(DEFAULT_COLLECT_LIMIT);
|
||||
expect(fetchedData.amount).to.eq(DEFAULT_COLLECT_PRICE);
|
||||
expect(fetchedData.recipient).to.eq(userAddress);
|
||||
expect(fetchedData.currency).to.eq(currency.address);
|
||||
expect(fetchedData.referralFee).to.eq(REFERRAL_FEE_BPS);
|
||||
expect(fetchedData.followerOnly).to.eq(true);
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, allowing non-followers to collect, user two collects without following and pays fee, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
false,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, user two follows, then collects and pays fee, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, user two follows, then collects twice, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(BigNumber.from(DEFAULT_COLLECT_PRICE).mul(2))
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount.mul(2));
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount.mul(2));
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferrerAmount = BigNumber.from(MAX_UINT256)
|
||||
.sub(DEFAULT_COLLECT_PRICE)
|
||||
.add(expectedReferralAmount);
|
||||
const expectedRecipientAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.sub(expectedReferralAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(expectedReferrerAmount);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, with no referral fee, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_LIMIT, DEFAULT_COLLECT_PRICE, currency.address, userAddress, 0, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited fee collect module as the collect module and data, user two mirrors, follows, then collects once from the original, twice from the mirror, and fails to collect a third time from either the mirror or the original', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MINT_LIMIT_EXCEEDED);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MINT_LIMIT_EXCEEDED);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,963 +0,0 @@
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, setNextBlockTimestamp, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
BPS_MAX,
|
||||
currency,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
limitedTimedFeeCollectModule,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
moduleGlobals,
|
||||
REFERRAL_FEE_BPS,
|
||||
treasuryAddress,
|
||||
TREASURY_FEE_BPS,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
|
||||
const DEFAULT_COLLECT_PRICE = parseEther('10');
|
||||
const DEFAULT_COLLECT_LIMIT = 3;
|
||||
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(limitedTimedFeeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
moduleGlobals.connect(governance).whitelistCurrency(currency.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Publication Creation', function () {
|
||||
it('user should fail to post with limited timed fee collect module using zero collect limit', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[0, DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited timed fee collect module using unwhitelisted currency', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
userTwoAddress,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited timed fee collect module using zero recipient', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
ZERO_ADDRESS,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited timed fee collect module using referral fee greater than max BPS', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_LIMIT, DEFAULT_COLLECT_PRICE, currency.address, userAddress, 10001, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with limited timed fee collect module using zero amount', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_LIMIT, 0, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Collecting', function () {
|
||||
beforeEach(async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should fail to process collect without being the hub', async function () {
|
||||
await expect(
|
||||
limitedTimedFeeCollectModule
|
||||
.connect(userTwo)
|
||||
.processCollect(0, 0, userTwoAddress, userTwoAddress, FIRST_PROFILE_ID, 1, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Governance should set the treasury fee BPS to zero, userTwo collecting should not emit a transfer event to the treasury', async function () {
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(1);
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, DEFAULT_COLLECT_PRICE],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, governance should set the treasury fee BPS to zero, userTwo collecting their mirror should not emit a transfer event to the treasury', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(2);
|
||||
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const amount = DEFAULT_COLLECT_PRICE.sub(expectedReferralAmount);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, amount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userTwoAddress, expectedReferralAmount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without following', async function () {
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect after the collect end timestmap', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const currentTimestamp = await getTimestamp();
|
||||
await setNextBlockTimestamp(Number(currentTimestamp) + 24 * 60 * 60);
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.COLLECT_EXPIRED);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected price in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected currency in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without first approving module with currency', async function () {
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror after the collect end timestamp', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const currentTimestamp = await getTimestamp();
|
||||
await setNextBlockTimestamp(Number(currentTimestamp) + 24 * 60 * 60);
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.COLLECT_EXPIRED);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected price in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected currency in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should post with limited timed fee collect module as the collect module and data, correct events should be emitted', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
const tx = lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
const postTimestamp = await getTimestamp();
|
||||
const endTimestamp = BigNumber.from(postTimestamp).add(24 * 60 * 60);
|
||||
const expectedData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool', 'uint40'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
endTimestamp,
|
||||
]
|
||||
);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'PostCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
1,
|
||||
MOCK_URI,
|
||||
limitedTimedFeeCollectModule.address,
|
||||
expectedData,
|
||||
ZERO_ADDRESS,
|
||||
[],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, fetched publication data should be accurate', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
const postTimestamp = await getTimestamp();
|
||||
|
||||
const fetchedData = await limitedTimedFeeCollectModule.getPublicationData(
|
||||
FIRST_PROFILE_ID,
|
||||
1
|
||||
);
|
||||
expect(fetchedData.collectLimit).to.eq(DEFAULT_COLLECT_LIMIT);
|
||||
expect(fetchedData.amount).to.eq(DEFAULT_COLLECT_PRICE);
|
||||
expect(fetchedData.recipient).to.eq(userAddress);
|
||||
expect(fetchedData.currency).to.eq(currency.address);
|
||||
expect(fetchedData.referralFee).to.eq(REFERRAL_FEE_BPS);
|
||||
expect(fetchedData.followerOnly).to.eq(true);
|
||||
expect(fetchedData.endTimestamp).to.eq(BigNumber.from(postTimestamp).add(24 * 60 * 60));
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, allowing non-followers to collect, user two collects without following, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
false,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, user two follows, then collects and pays fee, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, user two follows, then collects twice, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(BigNumber.from(DEFAULT_COLLECT_PRICE).mul(2))
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount.mul(2));
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount.mul(2));
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferrerAmount = BigNumber.from(MAX_UINT256)
|
||||
.sub(DEFAULT_COLLECT_PRICE)
|
||||
.add(expectedReferralAmount);
|
||||
const expectedRecipientAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.sub(expectedReferralAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(expectedReferrerAmount);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, with no referral fee, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_LIMIT, DEFAULT_COLLECT_PRICE, currency.address, userAddress, 0, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with limited timed fee collect module as the collect module and data, user two mirrors, follows, then collects once from the original, twice from the mirror, and fails to collect a third time from either the mirror or the original', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[
|
||||
DEFAULT_COLLECT_LIMIT,
|
||||
DEFAULT_COLLECT_PRICE,
|
||||
currency.address,
|
||||
userAddress,
|
||||
REFERRAL_FEE_BPS,
|
||||
true,
|
||||
]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: limitedTimedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(limitedTimedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MINT_LIMIT_EXCEEDED);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MINT_LIMIT_EXCEEDED);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,118 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
revertCollectModule,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Revert Collect Module', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(revertCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: revertCollectModule.address,
|
||||
collectModuleInitData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Collecting', function () {
|
||||
it('UserTwo should fail to collect without following', async function () {
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress,FIRST_PROFILE_ID, 1, [])).to.be.revertedWith(
|
||||
ERRORS.COLLECT_NOT_ALLOWED
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress,secondProfileId, 1, [])).to.be.revertedWith(
|
||||
ERRORS.COLLECT_NOT_ALLOWED
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect while following', async function () {
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress,FIRST_PROFILE_ID, 1, [])).to.be.revertedWith(
|
||||
ERRORS.COLLECT_NOT_ALLOWED
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror while following the original profile', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).collect(userTwoAddress,secondProfileId, 1, [])).to.be.revertedWith(
|
||||
ERRORS.COLLECT_NOT_ALLOWED
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,797 +0,0 @@
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, setNextBlockTimestamp, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
BPS_MAX,
|
||||
currency,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
moduleGlobals,
|
||||
REFERRAL_FEE_BPS,
|
||||
timedFeeCollectModule,
|
||||
treasuryAddress,
|
||||
TREASURY_FEE_BPS,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Timed Fee Collect Module', function () {
|
||||
const DEFAULT_COLLECT_PRICE = parseEther('10');
|
||||
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(timedFeeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
moduleGlobals.connect(governance).whitelistCurrency(currency.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Publication Creation', function () {
|
||||
it('user should fail to post with timed fee collect module using unwhitelisted currency', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, userTwoAddress, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with timed fee collect module using zero recipient', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, ZERO_ADDRESS, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with timed fee collect module using referral fee greater than max BPS', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, 10001, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to post with timed fee collect module using zero amount', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[0, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Collecting', function () {
|
||||
beforeEach(async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should fail to process collect without being the hub', async function () {
|
||||
await expect(
|
||||
timedFeeCollectModule
|
||||
.connect(userTwo)
|
||||
.processCollect(0, 0, userTwoAddress, userTwoAddress, FIRST_PROFILE_ID, 1, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Governance should set the treasury fee BPS to zero, userTwo collecting should not emit a transfer event to the treasury', async function () {
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(1);
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, DEFAULT_COLLECT_PRICE],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, governance should set the treasury fee BPS to zero, userTwo collecting their mirror should not emit a transfer event to the treasury', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(2);
|
||||
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const amount = DEFAULT_COLLECT_PRICE.sub(expectedReferralAmount);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, amount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userTwoAddress, expectedReferralAmount],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without following', async function () {
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect after the collect end timestmap', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const currentTimestamp = await getTimestamp();
|
||||
await setNextBlockTimestamp(Number(currentTimestamp) + 24 * 60 * 60);
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.COLLECT_EXPIRED);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected price in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect passing a different expected currency in data', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to collect without first approving module with currency', async function () {
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror after the collect end timestamp', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const currentTimestamp = await getTimestamp();
|
||||
await setNextBlockTimestamp(Number(currentTimestamp) + 24 * 60 * 60);
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.COLLECT_EXPIRED);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected price in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror passing a different expected currency in data', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_COLLECT_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should post with timed fee collect module as the collect module and data, correct events should be emitted', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
|
||||
const tx = lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
const postTimestamp = await getTimestamp();
|
||||
const endTimestamp = BigNumber.from(postTimestamp).add(24 * 60 * 60);
|
||||
const expectedData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool', 'uint40'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true, endTimestamp]
|
||||
);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'PostCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
1,
|
||||
MOCK_URI,
|
||||
timedFeeCollectModule.address,
|
||||
expectedData,
|
||||
ZERO_ADDRESS,
|
||||
[],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should post with timed fee collect module as the collect module and data, fetched publication data should be accurate', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
const postTimestamp = await getTimestamp();
|
||||
|
||||
const fetchedData = await timedFeeCollectModule.getPublicationData(FIRST_PROFILE_ID, 1);
|
||||
expect(fetchedData.amount).to.eq(DEFAULT_COLLECT_PRICE);
|
||||
expect(fetchedData.recipient).to.eq(userAddress);
|
||||
expect(fetchedData.currency).to.eq(currency.address);
|
||||
expect(fetchedData.referralFee).to.eq(REFERRAL_FEE_BPS);
|
||||
expect(fetchedData.followerOnly).to.eq(true);
|
||||
expect(fetchedData.endTimestamp).to.eq(BigNumber.from(postTimestamp).add(24 * 60 * 60));
|
||||
});
|
||||
|
||||
it('User should post with timed fee collect module as the collect module and data, allowing non-followers to collect, user two collects without following, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, false]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with timed fee collect module as the collect module and data, user two follows, then collects and pays fee, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with timed fee collect module as the collect module and data, user two follows, then collects twice, fee distribution is valid', async function () {
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, FIRST_PROFILE_ID, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(BigNumber.from(DEFAULT_COLLECT_PRICE).mul(2))
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount.mul(2));
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount.mul(2));
|
||||
});
|
||||
|
||||
it('User should post with timed fee collect module as the collect module and data, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, REFERRAL_FEE_BPS, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferralAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.mul(REFERRAL_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedReferrerAmount = BigNumber.from(MAX_UINT256)
|
||||
.sub(DEFAULT_COLLECT_PRICE)
|
||||
.add(expectedReferralAmount);
|
||||
const expectedRecipientAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.sub(expectedTreasuryAmount)
|
||||
.sub(expectedReferralAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(expectedReferrerAmount);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should post with timed fee collect module as the collect module and data, with no referral fee, user two mirrors, follows, then collects from their mirror and pays fee, fee distribution is valid', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const collectModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address', 'uint16', 'bool'],
|
||||
[DEFAULT_COLLECT_PRICE, currency.address, userAddress, 0, true]
|
||||
);
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: timedFeeCollectModule.address,
|
||||
collectModuleInitData: collectModuleInitData,
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: secondProfileId,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(timedFeeCollectModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_COLLECT_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(userTwoAddress, secondProfileId, 1, data)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_COLLECT_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_COLLECT_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_COLLECT_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,244 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
approvalFollowModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
lensHubImpl,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
user,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Approval Follow Module', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(approvalFollowModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Initialization', function () {
|
||||
it('Initialize call should fail when sender is not the hub', async function () {
|
||||
await expect(
|
||||
approvalFollowModule.initializeFollowModule(FIRST_PROFILE_ID, userAddress, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
});
|
||||
|
||||
context('Approvals', function () {
|
||||
it('Approve should fail when calling it with addresses and toApprove params having different lengths', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
approvalFollowModule.connect(user).approve(FIRST_PROFILE_ID, [userTwoAddress], [])
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('Approve should fail when sender differs from profile owner', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
approvalFollowModule.connect(userTwo).approve(FIRST_PROFILE_ID, [userTwoAddress], [false])
|
||||
).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER);
|
||||
});
|
||||
});
|
||||
|
||||
context('Processing follow', function () {
|
||||
it('UserTwo should fail to process follow without being the hub', async function () {
|
||||
await expect(
|
||||
approvalFollowModule
|
||||
.connect(userTwo)
|
||||
.processFollow(0, userTwoAddress, userTwoAddress, FIRST_PROFILE_ID, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Follow should fail when follower address is not approved', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_NOT_APPROVED);
|
||||
});
|
||||
|
||||
it('Follow should fail when follower address approval is revoked after being approved', async function () {
|
||||
const data = abiCoder.encode(['address[]'], [[userTwoAddress]]);
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
approvalFollowModule.connect(user).approve(FIRST_PROFILE_ID, [userTwoAddress], [false])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_NOT_APPROVED);
|
||||
});
|
||||
|
||||
it('Follow should fail when follower address is not approved even when following itself', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.be.revertedWith(
|
||||
ERRORS.FOLLOW_NOT_APPROVED
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
context('Initialization', function () {
|
||||
it('Profile creation with initial approval data should emit expected event', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
const data = abiCoder.encode(['address[]'], [[userTwoAddress]]);
|
||||
|
||||
const tx = lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: 'secondhandle',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: approvalFollowModule.address,
|
||||
followModuleInitData: data,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(2);
|
||||
matchEvent(receipt, 'Transfer', [ZERO_ADDRESS, userAddress, secondProfileId], lensHubImpl);
|
||||
matchEvent(receipt, 'ProfileCreated', [
|
||||
secondProfileId,
|
||||
userAddress,
|
||||
userAddress,
|
||||
'secondhandle',
|
||||
MOCK_PROFILE_URI,
|
||||
approvalFollowModule.address,
|
||||
data,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('Setting follow module with initial approval data should emit expected event', async function () {
|
||||
const data = abiCoder.encode(['address[]'], [[userTwoAddress]]);
|
||||
const tx = lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, data);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'FollowModuleSet', [
|
||||
FIRST_PROFILE_ID,
|
||||
approvalFollowModule.address,
|
||||
data,
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('Setting follow module should work when calling it without initial approval data', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Setting follow module should work when calling it with initial approval data', async function () {
|
||||
const data = abiCoder.encode(['address[]'], [[userTwoAddress]]);
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, data)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
|
||||
context('Approvals and follows', function () {
|
||||
it('Approval should emit expected event', async function () {
|
||||
const tx = approvalFollowModule
|
||||
.connect(user)
|
||||
.approve(FIRST_PROFILE_ID, [userTwoAddress], [true]);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'FollowsApproved', [
|
||||
userAddress,
|
||||
FIRST_PROFILE_ID,
|
||||
[userTwoAddress],
|
||||
[true],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('Follow call should work when address was previously approved', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
approvalFollowModule.connect(user).approve(FIRST_PROFILE_ID, [userTwoAddress], [true])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Follow call to self should work when address was previously approved', async function () {
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, [])
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
approvalFollowModule.connect(user).approve(FIRST_PROFILE_ID, [userAddress], [true])
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
|
||||
context('View Functions', function () {
|
||||
beforeEach(async function () {
|
||||
const data = abiCoder.encode(['address[]'], [[userTwoAddress]]);
|
||||
await expect(
|
||||
lensHub.setFollowModule(FIRST_PROFILE_ID, approvalFollowModule.address, data)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Single approval getter should return expected values', async function () {
|
||||
expect(
|
||||
await approvalFollowModule.isApproved(userAddress, FIRST_PROFILE_ID, userTwoAddress)
|
||||
).to.eq(true);
|
||||
|
||||
expect(
|
||||
await approvalFollowModule.isApproved(userAddress, FIRST_PROFILE_ID, userAddress)
|
||||
).to.eq(false);
|
||||
});
|
||||
|
||||
it('Array approval getter should return expected values', async function () {
|
||||
const result = await approvalFollowModule.isApprovedArray(userAddress, FIRST_PROFILE_ID, [
|
||||
userTwoAddress,
|
||||
userAddress,
|
||||
]);
|
||||
expect(result[0]).to.eq(true);
|
||||
expect(result[1]).to.eq(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,349 +0,0 @@
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseEther } from '@ethersproject/units';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
BPS_MAX,
|
||||
currency,
|
||||
feeFollowModule,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
lensHubImpl,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
moduleGlobals,
|
||||
treasuryAddress,
|
||||
TREASURY_FEE_BPS,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Fee Follow Module', function () {
|
||||
const DEFAULT_FOLLOW_PRICE = parseEther('10');
|
||||
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(feeFollowModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
moduleGlobals.connect(governance).whitelistCurrency(currency.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Initialization', function () {
|
||||
it('user should fail to create a profile with fee follow module using unwhitelisted currency', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, userTwoAddress, userAddress]
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to create a profile with fee follow module using zero recipient', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, ZERO_ADDRESS]
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
|
||||
it('user should fail to create a profile with fee follow module using zero amount', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[0, currency.address, userAddress]
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.INIT_PARAMS_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Following', function () {
|
||||
beforeEach(async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, userAddress]
|
||||
);
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should fail to process follow without being the hub', async function () {
|
||||
await expect(
|
||||
feeFollowModule.connect(userTwo).processFollow(0, userTwoAddress, userTwoAddress, FIRST_PROFILE_ID, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Governance should set the treasury fee BPS to zero, userTwo following should not emit a transfer event to the treasury', async function () {
|
||||
await expect(moduleGlobals.connect(governance).setTreasuryFee(0)).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_FOLLOW_PRICE]
|
||||
);
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeFollowModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data]);
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
let currencyEventCount = 0;
|
||||
for (let log of receipt.logs) {
|
||||
if (log.address == currency.address) {
|
||||
currencyEventCount++;
|
||||
}
|
||||
}
|
||||
expect(currencyEventCount).to.eq(1);
|
||||
matchEvent(
|
||||
receipt,
|
||||
'Transfer',
|
||||
[userTwoAddress, userAddress, DEFAULT_FOLLOW_PRICE],
|
||||
currency,
|
||||
currency.address
|
||||
);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to follow passing a different expected price in data', async function () {
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_FOLLOW_PRICE.div(2)]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to follow passing a different expected currency in data', async function () {
|
||||
const data = abiCoder.encode(['address', 'uint256'], [userAddress, DEFAULT_FOLLOW_PRICE]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])
|
||||
).to.be.revertedWith(ERRORS.MODULE_DATA_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to follow without first approving module with currency', async function () {
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_FOLLOW_PRICE]
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should create a profile with the fee follow module as the follow module and data, correct events should be emitted', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, userAddress]
|
||||
);
|
||||
const tx = lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(2);
|
||||
matchEvent(receipt, 'Transfer', [ZERO_ADDRESS, userAddress, FIRST_PROFILE_ID], lensHubImpl);
|
||||
matchEvent(receipt, 'ProfileCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
userAddress,
|
||||
userAddress,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
feeFollowModule.address,
|
||||
followModuleInitData,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should create a profile then set the fee follow module as the follow module with data, correct events should be emitted', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, userAddress]
|
||||
);
|
||||
const tx = lensHub.setFollowModule(
|
||||
FIRST_PROFILE_ID,
|
||||
feeFollowModule.address,
|
||||
followModuleInitData
|
||||
);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'FollowModuleSet', [
|
||||
FIRST_PROFILE_ID,
|
||||
feeFollowModule.address,
|
||||
followModuleInitData,
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should create a profile with the fee follow module as the follow module and data, fetched profile data should be accurate', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, userAddress]
|
||||
);
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const fetchedData = await feeFollowModule.getProfileData(FIRST_PROFILE_ID);
|
||||
expect(fetchedData.amount).to.eq(DEFAULT_FOLLOW_PRICE);
|
||||
expect(fetchedData.recipient).to.eq(userAddress);
|
||||
expect(fetchedData.currency).to.eq(currency.address);
|
||||
});
|
||||
|
||||
it('User should create a profile with the fee follow module as the follow module and data, user two follows, fee distribution is valid', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, userAddress]
|
||||
);
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeFollowModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_FOLLOW_PRICE]
|
||||
);
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_FOLLOW_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_FOLLOW_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(DEFAULT_FOLLOW_PRICE)
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount);
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount);
|
||||
});
|
||||
|
||||
it('User should create a profile with the fee follow module as the follow module and data, user two follows twice, fee distribution is valid', async function () {
|
||||
const followModuleInitData = abiCoder.encode(
|
||||
['uint256', 'address', 'address'],
|
||||
[DEFAULT_FOLLOW_PRICE, currency.address, userAddress]
|
||||
);
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: feeFollowModule.address,
|
||||
followModuleInitData: followModuleInitData,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(currency.mint(userTwoAddress, MAX_UINT256)).to.not.be.reverted;
|
||||
await expect(
|
||||
currency.connect(userTwo).approve(feeFollowModule.address, MAX_UINT256)
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(
|
||||
['address', 'uint256'],
|
||||
[currency.address, DEFAULT_FOLLOW_PRICE]
|
||||
);
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])).to.not.be.reverted;
|
||||
|
||||
const expectedTreasuryAmount = BigNumber.from(DEFAULT_FOLLOW_PRICE)
|
||||
.mul(TREASURY_FEE_BPS)
|
||||
.div(BPS_MAX);
|
||||
const expectedRecipientAmount =
|
||||
BigNumber.from(DEFAULT_FOLLOW_PRICE).sub(expectedTreasuryAmount);
|
||||
|
||||
expect(await currency.balanceOf(userTwoAddress)).to.eq(
|
||||
BigNumber.from(MAX_UINT256).sub(BigNumber.from(DEFAULT_FOLLOW_PRICE).mul(2))
|
||||
);
|
||||
expect(await currency.balanceOf(userAddress)).to.eq(expectedRecipientAmount.mul(2));
|
||||
expect(await currency.balanceOf(treasuryAddress)).to.eq(expectedTreasuryAmount.mul(2));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,310 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { BytesLike } from 'ethers';
|
||||
import { ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
abiCoder,
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
lensHub,
|
||||
lensHubImpl,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
profileFollowModule,
|
||||
userAddress,
|
||||
userThreeAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Profile Follow Module', function () {
|
||||
let EMPTY_BYTES: BytesLike;
|
||||
let DEFAULT_FOLLOW_DATA: BytesLike;
|
||||
|
||||
before(async function () {
|
||||
EMPTY_BYTES = '0x';
|
||||
DEFAULT_FOLLOW_DATA = abiCoder.encode(['uint256'], [FIRST_PROFILE_ID + 1]);
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(profileFollowModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Initialization', function () {
|
||||
it('Initialize call should fail when sender is not the hub', async function () {
|
||||
await expect(
|
||||
profileFollowModule.initializeFollowModule(FIRST_PROFILE_ID, userAddress, EMPTY_BYTES)
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
});
|
||||
|
||||
context('Following', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: profileFollowModule.address,
|
||||
followModuleInitData: EMPTY_BYTES,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('UserTwo should fail to process follow without being the hub', async function () {
|
||||
await expect(
|
||||
profileFollowModule
|
||||
.connect(userTwo)
|
||||
.processFollow(0, userTwoAddress, userTwoAddress, FIRST_PROFILE_ID, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('Follow should fail when data is not holding the follower profile id encoded', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [])
|
||||
).to.be.revertedWith(ERRORS.ARRAY_MISMATCH);
|
||||
});
|
||||
|
||||
it('Follow should fail when the passed follower profile does not exist because has never been minted', async function () {
|
||||
const data = abiCoder.encode(['uint256'], [FIRST_PROFILE_ID + 1]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])
|
||||
).to.be.revertedWith(ERRORS.ERC721_OWNER_QUERY_FOR_NONEXISTENT_TOKEN);
|
||||
});
|
||||
|
||||
it('Follow should fail when the passed follower profile does not exist because has been burned', async function () {
|
||||
const secondProfileId = FIRST_PROFILE_ID + 1;
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).burn(secondProfileId)).to.not.be.reverted;
|
||||
|
||||
const data = abiCoder.encode(['uint256'], [secondProfileId]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])
|
||||
).to.be.revertedWith(ERRORS.ERC721_OWNER_QUERY_FOR_NONEXISTENT_TOKEN);
|
||||
});
|
||||
|
||||
it('Follow should fail when follower address is not the owner of the passed follower profile', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: 'user',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER);
|
||||
});
|
||||
|
||||
it('Follow should fail when the passed follower profile has already followed the profile', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.not.be.reverted;
|
||||
const followerProfileId = FIRST_PROFILE_ID + 1;
|
||||
expect(
|
||||
await profileFollowModule.isProfileFollowing(followerProfileId, FIRST_PROFILE_ID)
|
||||
).to.be.true;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('Follow should fail when the passed follower profile has already followed the profile even after the profile nft has been transfered', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.not.be.reverted;
|
||||
const followerProfileId = FIRST_PROFILE_ID + 1;
|
||||
expect(
|
||||
await profileFollowModule.isProfileFollowing(followerProfileId, FIRST_PROFILE_ID)
|
||||
).to.be.true;
|
||||
|
||||
await expect(
|
||||
lensHub.transferFrom(userAddress, userThreeAddress, FIRST_PROFILE_ID)
|
||||
).to.not.be.reverted;
|
||||
expect(
|
||||
await profileFollowModule.isProfileFollowing(followerProfileId, FIRST_PROFILE_ID)
|
||||
).to.be.true;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
context('Initialization', function () {
|
||||
it('Initialize call should succeed returning empty bytes even when sending non-empty data as input', async function () {
|
||||
expect(
|
||||
await profileFollowModule
|
||||
.connect(lensHub.address)
|
||||
.callStatic.initializeFollowModule(
|
||||
FIRST_PROFILE_ID,
|
||||
userAddress,
|
||||
abiCoder.encode(['uint256'], [0])
|
||||
)
|
||||
).to.eq(EMPTY_BYTES);
|
||||
});
|
||||
|
||||
it('Profile creation using profile follow module should succeed and emit expected event', async function () {
|
||||
const tx = lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: profileFollowModule.address,
|
||||
followModuleInitData: EMPTY_BYTES,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
});
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(2);
|
||||
matchEvent(receipt, 'Transfer', [ZERO_ADDRESS, userAddress, FIRST_PROFILE_ID], lensHubImpl);
|
||||
matchEvent(receipt, 'ProfileCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
userAddress,
|
||||
userAddress,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
profileFollowModule.address,
|
||||
EMPTY_BYTES,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('User should create a profile then set the profile follow module as the follow module, correct event should be emitted', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub.setFollowModule(
|
||||
FIRST_PROFILE_ID,
|
||||
profileFollowModule.address,
|
||||
EMPTY_BYTES
|
||||
);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'FollowModuleSet', [
|
||||
FIRST_PROFILE_ID,
|
||||
profileFollowModule.address,
|
||||
EMPTY_BYTES,
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
context('Processing follow', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: profileFollowModule.address,
|
||||
followModuleInitData: EMPTY_BYTES,
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Follow call should work when follower profile exists, is owned by the follower address and has not already followed the profile', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
const followerProfileId = FIRST_PROFILE_ID + 1;
|
||||
expect(
|
||||
await profileFollowModule.isProfileFollowing(followerProfileId, FIRST_PROFILE_ID)
|
||||
).to.be.false;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Follow call should work with each of your profiles when you have more than one', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo2',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [DEFAULT_FOLLOW_DATA])
|
||||
).to.not.be.reverted;
|
||||
const data = abiCoder.encode(['uint256'], [FIRST_PROFILE_ID + 2]);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [data])
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
FIRST_PROFILE_ID,
|
||||
governance,
|
||||
userTwoAddress,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
revertFollowModule,
|
||||
userAddress,
|
||||
userTwo,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Revert Follow Module', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistFollowModule(revertFollowModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
context('Initialization', function () {
|
||||
it('Initialize call should fail when sender is not the hub', async function () {
|
||||
await expect(
|
||||
revertFollowModule.initializeFollowModule(FIRST_PROFILE_ID, userAddress, [])
|
||||
).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
});
|
||||
|
||||
context('Processing follow', function () {
|
||||
it('UserTwo should fail to process follow', async function () {
|
||||
await lensHub.setFollowModule(FIRST_PROFILE_ID, revertFollowModule.address, []);
|
||||
expect(await lensHub.getFollowModule(FIRST_PROFILE_ID)).to.be.equal(
|
||||
revertFollowModule.address
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
context('Initialization', function () {
|
||||
it('Initialize call should succeed when passing non empty data and return empty bytes', async function () {
|
||||
const nonEmptyData = '0x1234';
|
||||
expect(
|
||||
await revertFollowModule
|
||||
.connect(lensHub.address)
|
||||
.initializeFollowModule(FIRST_PROFILE_ID, userAddress, nonEmptyData)
|
||||
).to.be.equals('0x');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,407 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { FollowNFT__factory } from '../../../typechain-types';
|
||||
import { ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import { getTimestamp, matchEvent, waitForTx } from '../../helpers/utils';
|
||||
import {
|
||||
freeCollectModule,
|
||||
FIRST_PROFILE_ID,
|
||||
followerOnlyReferenceModule,
|
||||
governance,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
MOCK_URI,
|
||||
user,
|
||||
userAddress,
|
||||
userThreeAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
abiCoder,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Follower Only Reference Module', function () {
|
||||
const SECOND_PROFILE_ID = FIRST_PROFILE_ID + 1;
|
||||
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'user2',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub
|
||||
.connect(governance)
|
||||
.whitelistReferenceModule(followerOnlyReferenceModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
|
||||
).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: followerOnlyReferenceModule.address,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('Negatives', function () {
|
||||
// We don't need a `publishing` or `initialization` context because initialization never reverts in the FollowerOnlyReferenceModule.
|
||||
context('Commenting', function () {
|
||||
it('Commenting should fail if commenter is not a follower and follow NFT not yet deployed', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).comment({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('Commenting should fail if commenter follows, then transfers the follow NFT before attempting to comment', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.connect(userTwo).transferFrom(userTwoAddress, userThreeAddress, 1)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).comment({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Mirroring', function () {
|
||||
it('Mirroring should fail if mirrorer is not a follower and follow NFT not yet deployed', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('Mirroring should fail if mirrorer follows, then transfers the follow NFT before attempting to mirror', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.connect(userTwo).transferFrom(userTwoAddress, userAddress, 1)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
context('Publishing', function () {
|
||||
it('Posting with follower only reference module as reference module should emit expected events', async function () {
|
||||
const tx = lensHub.post({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: followerOnlyReferenceModule.address,
|
||||
referenceModuleInitData: [],
|
||||
});
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'PostCreated', [
|
||||
FIRST_PROFILE_ID,
|
||||
2,
|
||||
MOCK_URI,
|
||||
freeCollectModule.address,
|
||||
abiCoder.encode(['bool'], [true]),
|
||||
followerOnlyReferenceModule.address,
|
||||
[],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
context('Commenting', function () {
|
||||
it('Commenting should work if the commenter is a follower', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).comment({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Commenting should work if the commenter is the publication owner and he is following himself', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Commenting should work if the commenter is the publication owner even when he is not following himself and follow NFT was not deployed', async function () {
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Commenting should work if the commenter is the publication owner even when he is not following himself and follow NFT was deployed', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.transferFrom(userAddress, userTwoAddress, 1)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.comment({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Commenting should work if the commenter follows, transfers the follow NFT then receives it back before attempting to comment', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.connect(userTwo).transferFrom(userTwoAddress, userAddress, 1)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(followNFT.transferFrom(userAddress, userTwoAddress, 1)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).comment({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
contentURI: MOCK_URI,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
collectModule: freeCollectModule.address,
|
||||
collectModuleInitData: abiCoder.encode(['bool'], [true]),
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
|
||||
context('Mirroring', function () {
|
||||
it('Mirroring should work if mirrorer is a follower', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Mirroring should work if mirrorer follows, transfers the follow NFT then receives it back before attempting to mirror', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.connect(userTwo).transferFrom(userTwoAddress, userAddress, 1)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(followNFT.transferFrom(userAddress, userTwoAddress, 1)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).mirror({
|
||||
profileId: SECOND_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Mirroring should work if the mirrorer is the publication owner and he is following himself', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Mirroring should work if the mirrorer is the publication owner even when he is not following himself and follow NFT was not deployed', async function () {
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
it('Mirroring should work if the mirrorer is the publication owner even when he is not following himself and follow NFT was deployed', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.transferFrom(userAddress, userTwoAddress, 1)).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.mirror({
|
||||
profileId: FIRST_PROFILE_ID,
|
||||
profileIdPointed: FIRST_PROFILE_ID,
|
||||
pubIdPointed: 1,
|
||||
referenceModuleData: [],
|
||||
referenceModule: ZERO_ADDRESS,
|
||||
referenceModuleInitData: [],
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,447 +0,0 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { FollowNFT, FollowNFT__factory } from '../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../helpers/constants';
|
||||
import { ERRORS } from '../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
getBlockNumber,
|
||||
getDelegateBySigParts,
|
||||
mine,
|
||||
} from '../helpers/utils';
|
||||
import {
|
||||
FIRST_PROFILE_ID,
|
||||
governanceAddress,
|
||||
helper,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
OTHER_MOCK_URI,
|
||||
testWallet,
|
||||
user,
|
||||
userAddress,
|
||||
userTwo,
|
||||
userTwoAddress,
|
||||
} from '../__setup.spec';
|
||||
|
||||
makeSuiteCleanRoom('Follow NFT', function () {
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: MOCK_PROFILE_HANDLE,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleInitData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
});
|
||||
|
||||
context('generic', function () {
|
||||
context('Negatives', function () {
|
||||
it('User should follow, and fail to re-initialize the follow NFT', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.initialize(FIRST_PROFILE_ID)).to.be.revertedWith(ERRORS.INITIALIZED);
|
||||
});
|
||||
|
||||
it("User should follow, userTwo should fail to burn user's follow NFT", async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
userTwo
|
||||
);
|
||||
|
||||
await expect(followNFT.burn(1)).to.be.revertedWith(ERRORS.NOT_OWNER_OR_APPROVED);
|
||||
});
|
||||
|
||||
it('User should follow, then fail to mint a follow NFT directly', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.mint(userAddress)).to.be.revertedWith(ERRORS.NOT_HUB);
|
||||
});
|
||||
|
||||
it('User should follow, then fail to get the power at a future block', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
const blockNumber = await getBlockNumber();
|
||||
|
||||
await expect(
|
||||
followNFT.getPowerByBlockNumber(userAddress, blockNumber + 1)
|
||||
).to.be.revertedWith(ERRORS.BLOCK_NUMBER_INVALID);
|
||||
await expect(followNFT.getDelegatedSupplyByBlockNumber(blockNumber + 1)).to.be.revertedWith(
|
||||
ERRORS.BLOCK_NUMBER_INVALID
|
||||
);
|
||||
});
|
||||
|
||||
it('user should follow, then fail to get the URI for a token that does not exist', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
await expect(followNFT.tokenURI(2)).to.be.revertedWith(ERRORS.TOKEN_DOES_NOT_EXIST);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should follow, then burn their follow NFT, governance power is zero before and after', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
const firstCheckpointBlock = await getBlockNumber();
|
||||
|
||||
await expect(followNFT.burn(1)).to.not.be.reverted;
|
||||
|
||||
const secondCheckpointBlock = await getBlockNumber();
|
||||
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, firstCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(firstCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, secondCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(secondCheckpointBlock)).to.eq(0);
|
||||
});
|
||||
|
||||
it('User should follow, delegate to themself, governance power should be zero before the last block, and 1 at the current block', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.delegate(userAddress)).to.not.be.reverted;
|
||||
|
||||
const blockNumber = await getBlockNumber();
|
||||
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, blockNumber - 1)).to.eq(0);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(blockNumber - 1)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, blockNumber)).to.eq(1);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(blockNumber)).to.eq(1);
|
||||
});
|
||||
|
||||
it('User and userTwo should follow, governance power should be zero, then users delegate multiple times, governance power should be accurate throughout', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
const firstCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// First, users delegate to themselves
|
||||
await expect(followNFT.delegate(userAddress)).to.not.be.reverted;
|
||||
await expect(followNFT.connect(userTwo).delegate(userTwoAddress)).to.not.be.reverted;
|
||||
const secondCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// Second, userTWo delegates to user
|
||||
await expect(followNFT.connect(userTwo).delegate(userAddress)).to.not.be.reverted;
|
||||
const thirdCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// Third, user delegates to userTwo
|
||||
await expect(followNFT.delegate(userTwoAddress)).to.not.be.reverted;
|
||||
const fourthCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// Fourth, users delegate to governance
|
||||
await expect(followNFT.delegate(governanceAddress)).to.not.be.reverted;
|
||||
await expect(followNFT.connect(userTwo).delegate(governanceAddress)).to.not.be.reverted;
|
||||
const fifthCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// Fifth, users delegate to zero (remove delegation)
|
||||
await expect(followNFT.delegate(ZERO_ADDRESS)).to.not.be.reverted;
|
||||
await expect(followNFT.connect(userTwo).delegate(ZERO_ADDRESS)).to.not.be.reverted;
|
||||
const sixthCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// Sixth, users delegate to user
|
||||
await expect(followNFT.delegate(userAddress)).to.not.be.reverted;
|
||||
await expect(followNFT.connect(userTwo).delegate(userAddress)).to.not.be.reverted;
|
||||
const seventhCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// First validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, firstCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, firstCheckpointBlock)).to.eq(
|
||||
0
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(firstCheckpointBlock)).to.eq(0);
|
||||
|
||||
// Second validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, secondCheckpointBlock)).to.eq(1);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, secondCheckpointBlock)).to.eq(
|
||||
1
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(secondCheckpointBlock)).to.eq(2);
|
||||
|
||||
// Third validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, thirdCheckpointBlock)).to.eq(2);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, thirdCheckpointBlock)).to.eq(
|
||||
0
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(thirdCheckpointBlock)).to.eq(2);
|
||||
|
||||
// Fourth validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, fourthCheckpointBlock)).to.eq(1);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, fourthCheckpointBlock)).to.eq(
|
||||
1
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(fourthCheckpointBlock)).to.eq(2);
|
||||
|
||||
// Fifth validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, fifthCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, fifthCheckpointBlock)).to.eq(
|
||||
0
|
||||
);
|
||||
expect(
|
||||
await followNFT.getPowerByBlockNumber(governanceAddress, fifthCheckpointBlock)
|
||||
).to.eq(2);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(fifthCheckpointBlock)).to.eq(2);
|
||||
|
||||
// Sixth validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, sixthCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, sixthCheckpointBlock)).to.eq(
|
||||
0
|
||||
);
|
||||
expect(
|
||||
await followNFT.getPowerByBlockNumber(governanceAddress, sixthCheckpointBlock)
|
||||
).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(ZERO_ADDRESS, sixthCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(sixthCheckpointBlock)).to.eq(0);
|
||||
|
||||
// Seventh validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, seventhCheckpointBlock)).to.eq(2);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, seventhCheckpointBlock)).to.eq(
|
||||
0
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(seventhCheckpointBlock)).to.eq(2);
|
||||
});
|
||||
|
||||
it('User and userTwo should follow, delegate to themselves, 10 blocks later user delegates to userTwo, 10 blocks later both delegate to user, governance power should be accurate throughout', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.delegate(userAddress)).to.not.be.reverted;
|
||||
await expect(followNFT.connect(userTwo).delegate(userTwoAddress)).to.not.be.reverted;
|
||||
const firstCheckpointBlock = await getBlockNumber();
|
||||
|
||||
await mine(10);
|
||||
|
||||
await expect(followNFT.delegate(userTwoAddress)).to.not.be.reverted;
|
||||
const secondCheckpointBlock = await getBlockNumber();
|
||||
|
||||
await mine(10);
|
||||
|
||||
await expect(followNFT.delegate(userAddress)).to.not.be.reverted;
|
||||
await expect(followNFT.connect(userTwo).delegate(userAddress)).to.not.be.reverted;
|
||||
const thirdCheckpointBlock = await getBlockNumber();
|
||||
|
||||
// First validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, firstCheckpointBlock)).to.eq(1);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, firstCheckpointBlock)).to.eq(
|
||||
1
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(firstCheckpointBlock)).to.eq(2);
|
||||
|
||||
// Second validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, secondCheckpointBlock)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, secondCheckpointBlock)).to.eq(
|
||||
2
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(secondCheckpointBlock)).to.eq(2);
|
||||
|
||||
// Last validation
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, thirdCheckpointBlock)).to.eq(2);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, thirdCheckpointBlock)).to.eq(
|
||||
0
|
||||
);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(secondCheckpointBlock)).to.eq(2);
|
||||
});
|
||||
|
||||
it('user and userTwo should follow, user delegates to userTwo twice, governance power should be accurate', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow(userTwoAddress, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
|
||||
await expect(followNFT.delegate(userTwoAddress)).to.not.be.reverted;
|
||||
await expect(followNFT.delegate(userTwoAddress)).to.not.be.reverted;
|
||||
|
||||
const blockNumber = await getBlockNumber();
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, blockNumber)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(userTwoAddress, blockNumber)).to.eq(1);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(blockNumber)).to.eq(1);
|
||||
});
|
||||
|
||||
it('user should follow, then get the URI for their token, URI should be accurate', async function () {
|
||||
await expect(lensHub.follow(userAddress, [FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
const followNFT = FollowNFT__factory.connect(
|
||||
await lensHub.getFollowNFT(FIRST_PROFILE_ID),
|
||||
user
|
||||
);
|
||||
expect(await followNFT.tokenURI(1)).to.eq(MOCK_FOLLOW_NFT_URI);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('meta-tx', function () {
|
||||
let followNFT: FollowNFT;
|
||||
beforeEach(async function () {
|
||||
await expect(
|
||||
lensHub.connect(testWallet).follow(testWallet.address, [FIRST_PROFILE_ID], [[]])
|
||||
).to.not.be.reverted;
|
||||
followNFT = FollowNFT__factory.connect(await lensHub.getFollowNFT(FIRST_PROFILE_ID), user);
|
||||
});
|
||||
|
||||
context('negatives', function () {
|
||||
it('TestWallet should fail to delegate with sig with signature deadline mismatch', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getDelegateBySigParts(
|
||||
followNFT.address,
|
||||
await followNFT.name(),
|
||||
testWallet.address,
|
||||
userAddress,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.delegateBySig(testWallet.address, userAddress, {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to delegate with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getDelegateBySigParts(
|
||||
followNFT.address,
|
||||
await followNFT.name(),
|
||||
testWallet.address,
|
||||
userAddress,
|
||||
nonce,
|
||||
'0'
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.delegateBySig(testWallet.address, userAddress, {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: '0',
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED);
|
||||
});
|
||||
|
||||
it('TestWallet should fail to delegate with sig with invalid nonce', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getDelegateBySigParts(
|
||||
followNFT.address,
|
||||
await followNFT.name(),
|
||||
testWallet.address,
|
||||
userAddress,
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await expect(
|
||||
followNFT.delegateBySig(testWallet.address, userAddress, {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
|
||||
it('TestWallet should sign attempt to delegate by sig, cancel with empty permitForAll, then fail to delegate by sig', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getDelegateBySigParts(
|
||||
followNFT.address,
|
||||
await followNFT.name(),
|
||||
testWallet.address,
|
||||
userAddress,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
await cancelWithPermitForAll(followNFT.address);
|
||||
|
||||
await expect(
|
||||
followNFT.delegateBySig(testWallet.address, userAddress, {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
})
|
||||
).to.be.revertedWith(ERRORS.SIGNATURE_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('TestWallet should delegate by sig to user, governance power should be accurate before and after', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const { v, r, s } = await getDelegateBySigParts(
|
||||
followNFT.address,
|
||||
await followNFT.name(),
|
||||
testWallet.address,
|
||||
userAddress,
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
let blockNumber = await getBlockNumber();
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, blockNumber)).to.eq(0);
|
||||
expect(await followNFT.getPowerByBlockNumber(testWallet.address, blockNumber)).to.eq(0);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(blockNumber)).to.eq(0);
|
||||
|
||||
await expect(
|
||||
followNFT.delegateBySig(testWallet.address, userAddress, {
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
deadline: MAX_UINT256,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
blockNumber = await getBlockNumber();
|
||||
expect(await followNFT.getPowerByBlockNumber(userAddress, blockNumber)).to.eq(1);
|
||||
expect(await followNFT.getPowerByBlockNumber(testWallet.address, blockNumber)).to.eq(0);
|
||||
expect(await followNFT.getDelegatedSupplyByBlockNumber(blockNumber)).to.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user