Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ on:
pull_request:

jobs:
compile_and_run_tests:
hardhat:
strategy:
fail-fast: true

name: Hardhat Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -23,3 +27,32 @@ jobs:
run: npm run compile
- name: Run test coverage
run: npm run coverage

foundry:
strategy:
fail-fast: true

name: Foundry Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build
- name: Run Forge tests
run: |
forge test -vvv
id: forge-test
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ coverage.json

addresses.json

contracts/core/modules/follow/SecretCodeFollowModule.sol
contracts/core/modules/follow/SecretCodeFollowModule.sol

#Foundry
artifacts-foundry
cache-foundry
broadcast
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ Cleanup leftover Docker containers:
USERID=$UID docker-compose down
```

### Foundry Setup

We also support writing tests in Solidity via [Foundry](https://github.com/foundry-rs/foundry).

1. For now, this requires NPM packages to be installed locally. Therefore you must have NodeJS 16 installed and run: `npm install`.
2. Install Foundry as per the installation instructions here: https://getfoundry.sh/
3. You can now run tests with: `forge test`.

**Local deployment steps are a work-in-progress with Foundry.**

## Protocol Overview

The Lens Protocol transfers ownership of social graphs to the participants of that graph themselves. This is achieved by creating direct links between `profiles` and their `followers`, while allowing fine-grained control of additional logic, including monetization, to be executed during those interactions on a profile-by-profile basis.
Expand Down
4 changes: 4 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[default]
out = 'artifacts-foundry'
src = 'contracts'
cache_path = 'cache-foundry'
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at d72ef5
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
forge-std/=lib/forge-std/src/
@openzeppelin/=node_modules/@openzeppelin/
157 changes: 157 additions & 0 deletions scripts/Deploy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {ModuleGlobals} from '../contracts/core/modules/ModuleGlobals.sol';
import {LensHub} from '../contracts/core/LensHub.sol';
import {FollowNFT} from '../contracts/core/FollowNFT.sol';
import {CollectNFT} from '../contracts/core/CollectNFT.sol';

import {TransparentUpgradeableProxy} from '../contracts/upgradeability/TransparentUpgradeableProxy.sol';

import {FeeCollectModule} from '../contracts/core/modules/collect/FeeCollectModule.sol';
import {LimitedFeeCollectModule} from '../contracts/core/modules/collect/LimitedFeeCollectModule.sol';
import {TimedFeeCollectModule} from '../contracts/core/modules/collect/TimedFeeCollectModule.sol';
import {LimitedTimedFeeCollectModule} from '../contracts/core/modules/collect/LimitedTimedFeeCollectModule.sol';
import {RevertCollectModule} from '../contracts/core/modules/collect/RevertCollectModule.sol';
import {FreeCollectModule} from '../contracts/core/modules/collect/FreeCollectModule.sol';

import {FeeFollowModule} from '../contracts/core/modules/follow/FeeFollowModule.sol';
import {ProfileFollowModule} from '../contracts/core/modules/follow/ProfileFollowModule.sol';
import {RevertFollowModule} from '../contracts/core/modules/follow/RevertFollowModule.sol';
import {ApprovalFollowModule} from '../contracts/core/modules/follow/ApprovalFollowModule.sol';

import {FollowerOnlyReferenceModule} from '../contracts/core/modules/reference/FollowerOnlyReferenceModule.sol';

import {LensPeriphery} from '../contracts/misc/LensPeriphery.sol';
import {UIDataProvider} from '../contracts/misc/UIDataProvider.sol';
import {ProfileCreationProxy} from '../contracts/misc/ProfileCreationProxy.sol';

import {Currency} from '../contracts/mocks/Currency.sol';

import {ScriptTypes} from './ScriptTypes.sol';

import 'forge-std/Script.sol';

contract Deploy is Script {
uint16 constant TREASURY_FEE_BPS = 50;
string constant LENS_HUB_NFT_NAME = 'Lens Protocol Profiles';
string constant LENS_HUB_NFT_SYMBOL = 'LPP';

function run(address governance, address treasury) external returns (ScriptTypes.Contracts memory contracts) {
require(governance != address(0), "Governance address not set!");
require(treasury != address(0), "Treasury address not set!");

vm.startBroadcast();

contracts.moduleGlobals = new ModuleGlobals(governance, treasury, TREASURY_FEE_BPS);

uint256 deployerNonce = vm.getNonce(msg.sender);

uint256 followNFTNonce = deployerNonce + 1;
uint256 collectNFTNonce = deployerNonce + 2;
uint256 hubProxyNonce = deployerNonce + 3;

address followNFTImplAddress = addressFrom(msg.sender, followNFTNonce);
address collectNFTImplAddress = addressFrom(msg.sender, collectNFTNonce);
address hubProxyAddress = addressFrom(msg.sender, hubProxyNonce);

contracts.lensHubImpl = new LensHub(followNFTImplAddress, collectNFTImplAddress);

contracts.followNFT = new FollowNFT(hubProxyAddress);
contracts.collectNFT = new CollectNFT(hubProxyAddress);

contracts.proxy = new TransparentUpgradeableProxy(
address(contracts.lensHubImpl),
msg.sender,
abi.encodeWithSelector(
LensHub.initialize.selector,
LENS_HUB_NFT_NAME,
LENS_HUB_NFT_SYMBOL,
governance
)
);

contracts.lensHub = LensHub(address(contracts.proxy));

contracts.lensPeriphery = new LensPeriphery(contracts.lensHub);

contracts.currency = new Currency();

address lensHubAddress = address(contracts.lensHub);
address moduleGlobalsAddress = address(contracts.moduleGlobals);

contracts.feeCollectModule = new FeeCollectModule(lensHubAddress, moduleGlobalsAddress);
contracts.limitedFeeCollectModule = new LimitedFeeCollectModule(
lensHubAddress,
moduleGlobalsAddress
);
contracts.timedFeeCollectModule = new TimedFeeCollectModule(
lensHubAddress,
moduleGlobalsAddress
);
contracts.limitedTimedFeeCollectModule = new LimitedTimedFeeCollectModule(
lensHubAddress,
moduleGlobalsAddress
);
contracts.revertCollectModule = new RevertCollectModule();
contracts.freeCollectModule = new FreeCollectModule(lensHubAddress);

contracts.feeFollowModule = new FeeFollowModule(lensHubAddress, moduleGlobalsAddress);
contracts.profileFollowModule = new ProfileFollowModule(lensHubAddress);
contracts.revertFollowModule = new RevertFollowModule(lensHubAddress);
contracts.approvalFollowModule = new ApprovalFollowModule(lensHubAddress);

contracts.followerOnlyReferenceModule = new FollowerOnlyReferenceModule(lensHubAddress);

contracts.uiDataProvider = new UIDataProvider(contracts.lensHub);

contracts.profileCreationProxy = new ProfileCreationProxy(msg.sender, contracts.lensHub);

vm.stopBroadcast();
}

function addressFrom(address _origin, uint256 _nonce) internal pure returns (address _address) {
bytes memory data;
if (_nonce == 0x00)
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x80));
else if (_nonce <= 0x7f)
data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, uint8(_nonce));
else if (_nonce <= 0xff)
data = abi.encodePacked(
bytes1(0xd7),
bytes1(0x94),
_origin,
bytes1(0x81),
uint8(_nonce)
);
else if (_nonce <= 0xffff)
data = abi.encodePacked(
bytes1(0xd8),
bytes1(0x94),
_origin,
bytes1(0x82),
uint16(_nonce)
);
else if (_nonce <= 0xffffff)
data = abi.encodePacked(
bytes1(0xd9),
bytes1(0x94),
_origin,
bytes1(0x83),
uint24(_nonce)
);
else
data = abi.encodePacked(
bytes1(0xda),
bytes1(0x94),
_origin,
bytes1(0x84),
uint32(_nonce)
);
bytes32 hash = keccak256(data);
assembly {
mstore(0, hash)
_address := mload(0)
}
}
}
55 changes: 55 additions & 0 deletions scripts/ScriptTypes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {ModuleGlobals} from '../contracts/core/modules/ModuleGlobals.sol';
import {LensHub} from '../contracts/core/LensHub.sol';
import {FollowNFT} from '../contracts/core/FollowNFT.sol';
import {CollectNFT} from '../contracts/core/CollectNFT.sol';

import {TransparentUpgradeableProxy} from '../contracts/upgradeability/TransparentUpgradeableProxy.sol';

import {FeeCollectModule} from '../contracts/core/modules/collect/FeeCollectModule.sol';
import {LimitedFeeCollectModule} from '../contracts/core/modules/collect/LimitedFeeCollectModule.sol';
import {TimedFeeCollectModule} from '../contracts/core/modules/collect/TimedFeeCollectModule.sol';
import {LimitedTimedFeeCollectModule} from '../contracts/core/modules/collect/LimitedTimedFeeCollectModule.sol';
import {RevertCollectModule} from '../contracts/core/modules/collect/RevertCollectModule.sol';
import {FreeCollectModule} from '../contracts/core/modules/collect/FreeCollectModule.sol';

import {FeeFollowModule} from '../contracts/core/modules/follow/FeeFollowModule.sol';
import {ProfileFollowModule} from '../contracts/core/modules/follow/ProfileFollowModule.sol';
import {RevertFollowModule} from '../contracts/core/modules/follow/RevertFollowModule.sol';
import {ApprovalFollowModule} from '../contracts/core/modules/follow/ApprovalFollowModule.sol';

import {FollowerOnlyReferenceModule} from '../contracts/core/modules/reference/FollowerOnlyReferenceModule.sol';

import {LensPeriphery} from '../contracts/misc/LensPeriphery.sol';
import {UIDataProvider} from '../contracts/misc/UIDataProvider.sol';
import {ProfileCreationProxy} from '../contracts/misc/ProfileCreationProxy.sol';

import {Currency} from '../contracts/mocks/Currency.sol';

library ScriptTypes {
struct Contracts {
ModuleGlobals moduleGlobals;
LensHub lensHubImpl;
FollowNFT followNFT;
CollectNFT collectNFT;
TransparentUpgradeableProxy proxy;
LensHub lensHub;
LensPeriphery lensPeriphery;
Currency currency;
FeeCollectModule feeCollectModule;
LimitedFeeCollectModule limitedFeeCollectModule;
TimedFeeCollectModule timedFeeCollectModule;
LimitedTimedFeeCollectModule limitedTimedFeeCollectModule;
RevertCollectModule revertCollectModule;
FreeCollectModule freeCollectModule;
FeeFollowModule feeFollowModule;
ProfileFollowModule profileFollowModule;
RevertFollowModule revertFollowModule;
ApprovalFollowModule approvalFollowModule;
FollowerOnlyReferenceModule followerOnlyReferenceModule;
UIDataProvider uiDataProvider;
ProfileCreationProxy profileCreationProxy;
}
}
42 changes: 42 additions & 0 deletions scripts/Whitelist.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;

import {DataTypes} from '../contracts/libraries/DataTypes.sol';

import {ScriptTypes} from './ScriptTypes.sol';

import 'forge-std/Script.sol';

contract Whitelist is Script {
function run(ScriptTypes.Contracts calldata contracts) external {
vm.startBroadcast(msg.sender);

contracts.lensHub.whitelistCollectModule(address(contracts.feeCollectModule), true);
contracts.lensHub.whitelistCollectModule(address(contracts.limitedFeeCollectModule), true);
contracts.lensHub.whitelistCollectModule(address(contracts.timedFeeCollectModule), true);
contracts.lensHub.whitelistCollectModule(
address(contracts.limitedTimedFeeCollectModule),
true
);
contracts.lensHub.whitelistCollectModule(address(contracts.revertCollectModule), true);
contracts.lensHub.whitelistCollectModule(address(contracts.freeCollectModule), true);

contracts.lensHub.whitelistFollowModule(address(contracts.feeFollowModule), true);
contracts.lensHub.whitelistFollowModule(address(contracts.profileFollowModule), true);
contracts.lensHub.whitelistFollowModule(address(contracts.revertFollowModule), true);
contracts.lensHub.whitelistFollowModule(address(contracts.approvalFollowModule), true);

contracts.lensHub.whitelistReferenceModule(
address(contracts.followerOnlyReferenceModule),
true
);

contracts.moduleGlobals.whitelistCurrency(address(contracts.currency), true);

contracts.lensHub.whitelistProfileCreator(address(contracts.profileCreationProxy), true);

contracts.lensHub.setState(DataTypes.ProtocolState.Unpaused);

vm.stopBroadcast();
}
}
Loading