Merge branch 'main' into refactor/general-optimizations

This commit is contained in:
Peter Michael
2022-03-24 15:35:15 -04:00
12 changed files with 461 additions and 14 deletions

View File

@@ -2,6 +2,8 @@ module.exports = {
skipFiles: [
'/core/base/ERC721Time.sol',
'/core/base/ERC721Enumerable.sol',
'/core/modules/follow/SecretCodeFollowModule.sol',
'/upgradeability',
'/interfaces',
'/mocks',
],

View File

@@ -133,7 +133,8 @@ contract FeeCollectModule is ICollectModule, FeeModuleBase, FollowValidationModu
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
@@ -173,6 +174,7 @@ contract FeeCollectModule is ICollectModule, FeeModuleBase, FollowValidationModu
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -153,7 +153,8 @@ contract LimitedFeeCollectModule is ICollectModule, FeeModuleBase, FollowValidat
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
@@ -193,6 +194,7 @@ contract LimitedFeeCollectModule is ICollectModule, FeeModuleBase, FollowValidat
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -164,7 +164,8 @@ contract LimitedTimedFeeCollectModule is ICollectModule, FeeModuleBase, FollowVa
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
@@ -204,6 +205,7 @@ contract LimitedTimedFeeCollectModule is ICollectModule, FeeModuleBase, FollowVa
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -149,7 +149,8 @@ contract TimedFeeCollectModule is ICollectModule, FeeModuleBase, FollowValidatio
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
@@ -189,6 +190,7 @@ contract TimedFeeCollectModule is ICollectModule, FeeModuleBase, FollowValidatio
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -88,7 +88,8 @@ contract FeeFollowModule is IFollowModule, FeeModuleBase, FollowValidatorFollowM
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(follower, recipient, adjustedAmount);
IERC20(currency).safeTransferFrom(follower, treasury, treasuryAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(follower, treasury, treasuryAmount);
}
/**

View File

@@ -26,16 +26,17 @@ export function matchEvent(
receipt: TransactionReceipt,
name: string,
expectedArgs?: any[],
eventContract: Contract = eventsLib
eventContract: Contract = eventsLib,
emitterAddress?: string
) {
const events = receipt.logs;
if (events != undefined) {
// match name from list of events in eventContract, when found, compute the sigHash
let sigHash: string | undefined;
for (let contractEvents of Object.keys(eventContract.interface.events)) {
if (contractEvents.startsWith(name) && contractEvents.charAt(name.length) == '(') {
sigHash = keccak256(toUtf8Bytes(contractEvents));
for (let contractEvent of Object.keys(eventContract.interface.events)) {
if (contractEvent.startsWith(name) && contractEvent.charAt(name.length) == '(') {
sigHash = keccak256(toUtf8Bytes(contractEvent));
break;
}
}
@@ -51,6 +52,10 @@ export function matchEvent(
for (let emittedEvent of events) {
// If we find one with the correct sighash, check if it is the one we're looking for
if (emittedEvent.topics[0] == sigHash) {
// If an emitter address is passed, validate that this is indeed the correct emitter, if not, continue
if (emitterAddress) {
if (emittedEvent.address != emitterAddress) continue;
}
const event = eventContract.interface.parseLog(emittedEvent);
// If there are expected arguments, validate them, otherwise, return here
if (expectedArgs) {
@@ -106,7 +111,9 @@ export function matchEvent(
if (invalidParamsButExists) {
logger.throwError(`Event "${name}" found in logs but with unexpected args`);
} else {
logger.throwError(`Event "${name}" not found in given transaction log`);
logger.throwError(
`Event "${name}" not found emitted by "${emitterAddress}" in given transaction log`
);
}
} else {
logger.throwError('No events were emitted');

View File

@@ -2,6 +2,7 @@ import { BigNumber } from '@ethersproject/contracts/node_modules/@ethersproject/
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';
@@ -13,6 +14,7 @@ import {
FIRST_PROFILE_ID,
governance,
lensHub,
lensHubImpl,
makeSuiteCleanRoom,
MOCK_FOLLOW_NFT_URI,
MOCK_PROFILE_HANDLE,
@@ -22,6 +24,7 @@ import {
REFERRAL_FEE_BPS,
treasuryAddress,
TREASURY_FEE_BPS,
user,
userAddress,
userTwo,
userTwoAddress,
@@ -138,6 +141,105 @@ makeSuiteCleanRoom('Fee Collect Module', function () {
).to.not.be.reverted;
});
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([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(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,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.connect(userTwo).mirror({
profileId: secondProfileId,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
})
).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([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(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'],

View File

@@ -174,6 +174,105 @@ makeSuiteCleanRoom('Limited Fee Collect Module', function () {
).to.not.be.reverted;
});
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([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(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,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.connect(userTwo).mirror({
profileId: secondProfileId,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
})
).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([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(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'],

View File

@@ -174,6 +174,105 @@ makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
).to.not.be.reverted;
});
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([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(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,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.connect(userTwo).mirror({
profileId: secondProfileId,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
})
).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([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(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'],

View File

@@ -138,6 +138,105 @@ makeSuiteCleanRoom('Timed Fee Collect Module', function () {
).to.not.be.reverted;
});
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([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(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,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.connect(userTwo).mirror({
profileId: secondProfileId,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
})
).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([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(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'],

View File

@@ -113,6 +113,36 @@ makeSuiteCleanRoom('Fee Follow Module', function () {
).to.not.be.reverted;
});
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([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'],