Skip to content

Commit 3e41544

Browse files
author
Zer0dot
authored
Merge pull request #127 from aave/refactor/testnet-profile-creation-proxy
2 parents df2a15f + fd7bfea commit 3e41544

File tree

4 files changed

+72
-194
lines changed

4 files changed

+72
-194
lines changed

contracts/misc/ProfileCreationProxy.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pragma solidity 0.8.10;
55
import {ILensHub} from '../interfaces/ILensHub.sol';
66
import {DataTypes} from '../libraries/DataTypes.sol';
77
import {Errors} from '../libraries/Errors.sol';
8-
import {Events} from '../libraries/Events.sol';
98
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
109

1110
/**

contracts/mocks/MockProfileCreationProxy.sol

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,69 +10,31 @@ import {Errors} from '../libraries/Errors.sol';
1010
* @title MockProfileCreationProxy
1111
* @author Lens Protocol
1212
*
13-
* @dev This is a proxy to allow profiles to be created from any address while adding some handle restrictions.
13+
* @notice This is a proxy contract that enforces ".test" handle suffixes and adds char validations at profile creation.
1414
*/
1515
contract MockProfileCreationProxy {
1616
ILensHub immutable LENS_HUB;
1717

18-
address governance;
19-
uint256 requiredMinHandleLengthBeforeSuffix;
20-
string requiredHandleSuffix;
21-
mapping(bytes1 => bool) isCharacterInvalid;
22-
23-
modifier onlyGov() {
24-
if (msg.sender != governance) revert Errors.NotGovernance();
25-
_;
26-
}
27-
28-
constructor(
29-
uint256 minHandleLengthBeforeSuffix,
30-
string memory handleSuffix,
31-
string memory invalidCharacters,
32-
address newGovernance,
33-
address hub
34-
) {
35-
requiredMinHandleLengthBeforeSuffix = minHandleLengthBeforeSuffix;
36-
requiredHandleSuffix = handleSuffix;
37-
for (uint256 i = 0; i < bytes(invalidCharacters).length; ++i) {
38-
isCharacterInvalid[bytes(invalidCharacters)[i]] = true;
39-
}
40-
governance = newGovernance;
41-
LENS_HUB = ILensHub(hub);
18+
constructor(ILensHub hub) {
19+
LENS_HUB = hub;
4220
}
4321

4422
function proxyCreateProfile(DataTypes.CreateProfileData memory vars) external {
4523
uint256 handleLength = bytes(vars.handle).length;
46-
if (handleLength < requiredMinHandleLengthBeforeSuffix) {
47-
revert Errors.HandleLengthInvalid();
48-
}
49-
for (uint256 i = 0; i < handleLength; ++i) {
50-
if (isCharacterInvalid[bytes(vars.handle)[i]]) {
51-
revert Errors.HandleContainsInvalidCharacters();
52-
}
53-
}
54-
if (bytes(requiredHandleSuffix).length > 0) {
55-
vars.handle = string(abi.encodePacked(vars.handle, requiredHandleSuffix));
56-
}
57-
LENS_HUB.createProfile(vars);
58-
}
59-
60-
function setRequiredHandleSuffix(string memory handleSuffix) external onlyGov {
61-
requiredHandleSuffix = handleSuffix;
62-
}
24+
if (handleLength < 5) revert Errors.HandleLengthInvalid();
6325

64-
function setCharacterValidity(bytes1 character, bool isValid) external onlyGov {
65-
isCharacterInvalid[character] = !isValid;
66-
}
26+
bytes1 firstByte = bytes(vars.handle)[0];
27+
if (firstByte == '-' || firstByte == '_' || firstByte == '.')
28+
revert Errors.HandleFirstCharInvalid();
6729

68-
function setRequiredMinHandleLengthBeforeSuffix(uint256 minHandleLengthBeforeSuffix)
69-
external
70-
onlyGov
71-
{
72-
requiredMinHandleLengthBeforeSuffix = minHandleLengthBeforeSuffix;
73-
}
30+
for (uint256 i = 1; i < handleLength; ) {
31+
if (bytes(vars.handle)[i] == '.') revert Errors.HandleContainsInvalidCharacters();
32+
unchecked {
33+
++i;
34+
}
35+
}
7436

75-
function setGovernance(address newGovernance) external onlyGov {
76-
governance = newGovernance;
37+
vars.handle = string(abi.encodePacked(vars.handle, '.test'));
38+
LENS_HUB.createProfile(vars);
7739
}
7840
}

tasks/testnet-full-deploy-verify.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,17 +272,10 @@ task(
272272
// Deploy MockProfileCreationProxy
273273
console.log('\n\t-- Deploying Profile Creation Proxy --');
274274
const profileCreationProxy = await deployWithVerify(
275-
new MockProfileCreationProxy__factory(deployer).deploy(
276-
4,
277-
'.test',
278-
'.',
279-
lensHub.address,
280-
governance.address,
281-
{
282-
nonce: deployerNonce++,
283-
}
284-
),
285-
[4, '.test', '.', lensHub.address],
275+
new MockProfileCreationProxy__factory(deployer).deploy(lensHub.address, {
276+
nonce: deployerNonce++,
277+
}),
278+
[lensHub.address],
286279
'contracts/mocks/MockProfileCreationProxy.sol:MockProfileCreationProxy'
287280
);
288281

test/other/mock-profile-creation-proxy.spec.ts

Lines changed: 53 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,33 @@ import {
1313
MOCK_PROFILE_URI,
1414
user,
1515
userAddress,
16-
governanceAddress,
16+
deployerAddress,
1717
} from '../__setup.spec';
1818
import { BigNumber } from 'ethers';
1919
import { TokenDataStructOutput } from '../../typechain-types/LensHub';
2020
import { getTimestamp } from '../helpers/utils';
2121

2222
makeSuiteCleanRoom('Mock Profile Creation Proxy', function () {
23-
let mockProfileCreationProxy: MockProfileCreationProxy;
24-
let requiredSuffix = '.lens';
25-
let invalidChars = '.';
26-
let requiredMinHandleLengthBeforeSuffix = 4;
23+
const REQUIRED_SUFFIX = '.test';
24+
const MINIMUM_LENGTH = 5;
25+
26+
let profileCreationProxy: MockProfileCreationProxy;
2727
beforeEach(async function () {
28-
mockProfileCreationProxy = await new MockProfileCreationProxy__factory(deployer).deploy(
29-
requiredMinHandleLengthBeforeSuffix,
30-
requiredSuffix,
31-
invalidChars,
32-
governanceAddress,
28+
profileCreationProxy = await new MockProfileCreationProxy__factory(deployer).deploy(
3329
lensHub.address
3430
);
3531
await expect(
36-
lensHub.connect(governance).whitelistProfileCreator(mockProfileCreationProxy.address, true)
32+
lensHub.connect(governance).whitelistProfileCreator(profileCreationProxy.address, true)
3733
).to.not.be.reverted;
3834
});
3935

4036
context('Negatives', function () {
41-
it('User should fail to create profile if handle length before suffix does not reach minimum length', async function () {
37+
it('Should fail to create profile if handle length before suffix does not reach minimum length', async function () {
38+
const handle = 'a'.repeat(MINIMUM_LENGTH - 1);
4239
await expect(
43-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
40+
profileCreationProxy.proxyCreateProfile({
4441
to: userAddress,
45-
handle: '69',
42+
handle: handle,
4643
imageURI: MOCK_PROFILE_URI,
4744
followModule: ZERO_ADDRESS,
4845
followModuleInitData: [],
@@ -51,9 +48,9 @@ makeSuiteCleanRoom('Mock Profile Creation Proxy', function () {
5148
).to.be.revertedWith(ERRORS.INVALID_HANDLE_LENGTH);
5249
});
5350

54-
it('User should fail to create profile if handle contains an invalid character before the suffix', async function () {
51+
it('Should fail to create profile if handle contains an invalid character before the suffix', async function () {
5552
await expect(
56-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
53+
profileCreationProxy.proxyCreateProfile({
5754
to: userAddress,
5855
handle: 'dots.are.invalid',
5956
imageURI: MOCK_PROFILE_URI,
@@ -64,149 +61,76 @@ makeSuiteCleanRoom('Mock Profile Creation Proxy', function () {
6461
).to.be.revertedWith(ERRORS.HANDLE_CONTAINS_INVALID_CHARACTERS);
6562
});
6663

67-
it('User should fail to create profile if handle length before suffix does not reach new minimum length', async function () {
64+
it('Should fail to create profile if handle starts with a dash, underscore or period', async function () {
6865
await expect(
69-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
66+
profileCreationProxy.proxyCreateProfile({
7067
to: userAddress,
71-
handle: 'validhandle',
68+
handle: '.abcdef',
7269
imageURI: MOCK_PROFILE_URI,
7370
followModule: ZERO_ADDRESS,
7471
followModuleInitData: [],
7572
followNFTURI: MOCK_FOLLOW_NFT_URI,
7673
})
77-
).to.not.be.reverted;
78-
await mockProfileCreationProxy.connect(governance).setRequiredMinHandleLengthBeforeSuffix(15);
74+
).to.be.revertedWith(ERRORS.HANDLE_FIRST_CHARACTER_INVALID);
75+
7976
await expect(
80-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
77+
profileCreationProxy.proxyCreateProfile({
8178
to: userAddress,
82-
handle: 'validhandletoo',
79+
handle: '-abcdef',
8380
imageURI: MOCK_PROFILE_URI,
8481
followModule: ZERO_ADDRESS,
8582
followModuleInitData: [],
8683
followNFTURI: MOCK_FOLLOW_NFT_URI,
8784
})
88-
).to.be.revertedWith(ERRORS.INVALID_HANDLE_LENGTH);
89-
});
85+
).to.be.revertedWith(ERRORS.HANDLE_FIRST_CHARACTER_INVALID);
9086

91-
it('User should fail to create profile if handle contains a new invalid character before the suffix', async function () {
9287
await expect(
93-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
88+
profileCreationProxy.proxyCreateProfile({
9489
to: userAddress,
95-
handle: 'validhandle',
90+
handle: '_abcdef',
9691
imageURI: MOCK_PROFILE_URI,
9792
followModule: ZERO_ADDRESS,
9893
followModuleInitData: [],
9994
followNFTURI: MOCK_FOLLOW_NFT_URI,
10095
})
101-
).to.not.be.reverted;
102-
// Sets 'h' character (0x68 in UTF-8) as invalid
103-
await mockProfileCreationProxy.connect(governance).setCharacterValidity('0x68', false);
96+
).to.be.revertedWith(ERRORS.HANDLE_FIRST_CHARACTER_INVALID);
97+
});
98+
});
99+
100+
context('Scenarios', function () {
101+
it('Should be able to create a profile using the whitelisted proxy, received NFT should be valid', async function () {
102+
let timestamp: any;
103+
let owner: string;
104+
let totalSupply: BigNumber;
105+
let profileId: BigNumber;
106+
let mintTimestamp: BigNumber;
107+
let tokenData: TokenDataStructOutput;
108+
const validHandleBeforeSuffix = 'v_al-id';
109+
const expectedHandle = 'v_al-id'.concat(REQUIRED_SUFFIX);
110+
104111
await expect(
105-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
112+
profileCreationProxy.proxyCreateProfile({
106113
to: userAddress,
107-
handle: 'validhandletoo',
114+
handle: validHandleBeforeSuffix,
108115
imageURI: MOCK_PROFILE_URI,
109116
followModule: ZERO_ADDRESS,
110117
followModuleInitData: [],
111118
followNFTURI: MOCK_FOLLOW_NFT_URI,
112119
})
113-
).to.be.revertedWith(ERRORS.HANDLE_CONTAINS_INVALID_CHARACTERS);
114-
});
115-
116-
it('User should fail to change min handle length before suffix if it is not the governance address', async function () {
117-
await expect(
118-
mockProfileCreationProxy.connect(user).setRequiredMinHandleLengthBeforeSuffix(15)
119-
).to.be.revertedWith(ERRORS.NOT_GOVERNANCE);
120-
});
121-
122-
it('User should fail to change suffix if it is not the governance address', async function () {
123-
await expect(
124-
mockProfileCreationProxy.connect(user).setRequiredHandleSuffix('.user')
125-
).to.be.revertedWith(ERRORS.NOT_GOVERNANCE);
126-
});
127-
128-
it('User should fail to change character validity if it is not the governance address', async function () {
129-
await expect(
130-
mockProfileCreationProxy.connect(user).setCharacterValidity('0x68', true)
131-
).to.be.revertedWith(ERRORS.NOT_GOVERNANCE);
132-
});
120+
).to.not.be.reverted;
133121

134-
it('User should fail to change governance if it is not the governance address', async function () {
135-
await expect(
136-
mockProfileCreationProxy.connect(user).setGovernance(userAddress)
137-
).to.be.revertedWith(ERRORS.NOT_GOVERNANCE);
122+
timestamp = await getTimestamp();
123+
owner = await lensHub.ownerOf(FIRST_PROFILE_ID);
124+
totalSupply = await lensHub.totalSupply();
125+
profileId = await lensHub.getProfileIdByHandle(expectedHandle);
126+
mintTimestamp = await lensHub.mintTimestampOf(FIRST_PROFILE_ID);
127+
tokenData = await lensHub.tokenDataOf(FIRST_PROFILE_ID);
128+
expect(owner).to.eq(userAddress);
129+
expect(totalSupply).to.eq(FIRST_PROFILE_ID);
130+
expect(profileId).to.eq(FIRST_PROFILE_ID);
131+
expect(mintTimestamp).to.eq(timestamp);
132+
expect(tokenData.owner).to.eq(userAddress);
133+
expect(tokenData.mintTimestamp).to.eq(timestamp);
138134
});
139135
});
140-
141-
it('User should be able to create a profile using the whitelisted proxy, received NFT should be valid', async function () {
142-
let timestamp: any;
143-
let owner: string;
144-
let totalSupply: BigNumber;
145-
let profileId: BigNumber;
146-
let mintTimestamp: BigNumber;
147-
let tokenData: TokenDataStructOutput;
148-
const validHandleBeforeSuffix = 'validhandle';
149-
const expectedHandle = 'validhandle'.concat(requiredSuffix);
150-
151-
await expect(
152-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
153-
to: userAddress,
154-
handle: validHandleBeforeSuffix,
155-
imageURI: MOCK_PROFILE_URI,
156-
followModule: ZERO_ADDRESS,
157-
followModuleInitData: [],
158-
followNFTURI: MOCK_FOLLOW_NFT_URI,
159-
})
160-
).to.not.be.reverted;
161-
162-
timestamp = await getTimestamp();
163-
owner = await lensHub.ownerOf(FIRST_PROFILE_ID);
164-
totalSupply = await lensHub.totalSupply();
165-
profileId = await lensHub.getProfileIdByHandle(expectedHandle);
166-
mintTimestamp = await lensHub.mintTimestampOf(FIRST_PROFILE_ID);
167-
tokenData = await lensHub.tokenDataOf(FIRST_PROFILE_ID);
168-
expect(owner).to.eq(userAddress);
169-
expect(totalSupply).to.eq(FIRST_PROFILE_ID);
170-
expect(profileId).to.eq(FIRST_PROFILE_ID);
171-
expect(mintTimestamp).to.eq(timestamp);
172-
expect(tokenData.owner).to.eq(userAddress);
173-
expect(tokenData.mintTimestamp).to.eq(timestamp);
174-
});
175-
176-
it('User should get the expected handle after governance update the required suffix', async function () {
177-
const handleBeforeSuffix = 'validhandle';
178-
await expect(
179-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
180-
to: userAddress,
181-
handle: handleBeforeSuffix,
182-
imageURI: MOCK_PROFILE_URI,
183-
followModule: ZERO_ADDRESS,
184-
followModuleInitData: [],
185-
followNFTURI: MOCK_FOLLOW_NFT_URI,
186-
})
187-
).to.not.be.reverted;
188-
expect(await lensHub.getHandle(1)).to.eq(handleBeforeSuffix.concat(requiredSuffix));
189-
190-
const newSuffix = '.test';
191-
await mockProfileCreationProxy.connect(governance).setRequiredHandleSuffix(newSuffix);
192-
193-
await expect(
194-
mockProfileCreationProxy.connect(user).proxyCreateProfile({
195-
to: userAddress,
196-
handle: handleBeforeSuffix,
197-
imageURI: MOCK_PROFILE_URI,
198-
followModule: ZERO_ADDRESS,
199-
followModuleInitData: [],
200-
followNFTURI: MOCK_FOLLOW_NFT_URI,
201-
})
202-
).to.not.be.reverted;
203-
expect(await lensHub.getHandle(2)).to.eq(handleBeforeSuffix.concat(newSuffix));
204-
});
205-
206-
it('User should succeed making a onlyGov call after setting him as governance address', async function () {
207-
await mockProfileCreationProxy.connect(governance).setGovernance(userAddress);
208-
await expect(
209-
mockProfileCreationProxy.connect(user).setRequiredHandleSuffix('.user')
210-
).to.not.be.reverted;
211-
});
212136
});

0 commit comments

Comments
 (0)