Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a Factory contract to deploy IAOs on chain. #12

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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
176 changes: 176 additions & 0 deletions contracts/IAOLinearVestingFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

/*
______ ______
/ \ / \
| ▓▓▓▓▓▓\ ______ ______ | ▓▓▓▓▓▓\__ __ __ ______ ______
| ▓▓__| ▓▓/ \ / \| ▓▓___\▓▓ \ | \ | \| \ / \
| ▓▓ ▓▓ ▓▓▓▓▓▓\ ▓▓▓▓▓▓\\▓▓ \| ▓▓ | ▓▓ | ▓▓ \▓▓▓▓▓▓\ ▓▓▓▓▓▓\
| ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ ▓▓ ▓▓_\▓▓▓▓▓▓\ ▓▓ | ▓▓ | ▓▓/ ▓▓ ▓▓ | ▓▓
| ▓▓ | ▓▓ ▓▓__/ ▓▓ ▓▓▓▓▓▓▓▓ \__| ▓▓ ▓▓_/ ▓▓_/ ▓▓ ▓▓▓▓▓▓▓ ▓▓__/ ▓▓
| ▓▓ | ▓▓ ▓▓ ▓▓\▓▓ \\▓▓ ▓▓\▓▓ ▓▓ ▓▓\▓▓ ▓▓ ▓▓ ▓▓
\▓▓ \▓▓ ▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓\▓▓▓▓ \▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓
| ▓▓ | ▓▓
| ▓▓ | ▓▓
\▓▓ \▓▓

* App: https://apeswap.finance
* Medium: https://ape-swap.medium.com
* Twitter: https://twitter.com/ape_swap
* Discord: https://discord.com/invite/apeswap
* Telegram: https://t.me/ape_swap
* Announcements: https://t.me/ape_swap_news
* GitHub: https://github.com/ApeSwapFinance
*/

import './IAOUpgradeProxy.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

interface IIAOLinearVesting {
function initialize(
IERC20 _stakeToken,
IERC20 _offeringToken,
uint256 _startBlock,
uint256 _endBlockOffset,
uint256 _vestingBlockOffset, // Block offset between vesting distributions
uint256 _offeringAmount,
uint256 _raisingAmount,
address _adminAddress
) external;
}

/// @title IAOFactory
/// @notice Use to deploy IAO contracts on chain
contract IAOLinearVestingFactory is AccessControlEnumerable, Initializable {

IIAOLinearVesting[] public IAOLinearVestingImplementations;
uint256 public IAOLinearVestingVersion;

IIAOLinearVesting[] public deployedIAOContracts;
address public iaoProxyAdmin;
address public iaoAdmin;

bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER");

event IAOLinearVestingCreated(IIAOLinearVesting indexed newIAO);
event PushIAOLinearVestingVersion(IIAOLinearVesting indexed newIAO, uint256 newVersionId);
event SetIAOLinearVestingVersion(uint256 previousVersionId, uint256 newVersionId);
event UpdateIAOAdmins(address previousIAOProxyAdmin, address indexed newIAOProxyAdmin, address indexed previousIAOAdmin, address indexed newIAOAdmin);
event SweepWithdraw(address indexed receiver, IERC20 indexed token, uint256 balance);


/// @notice Constructor
/// @param _factoryAdmin: Admin to set creation roles.
/// @param _iaoProxyAdmin: Admin of the proxy deployed for IAOs. This address has the power to upgrade the IAOLinearVesting Contract
/// @param _iaoAdmin: Admin of the IAOs. This address has the power to change IAO settings
/// @param _implementation: Address of the implementation contract to use.
function initialize(
address _factoryAdmin,
address _iaoProxyAdmin,
address _iaoAdmin,
IIAOLinearVesting _implementation
) external initializer {
require(_iaoProxyAdmin != _iaoAdmin, 'iaoProxyAdmin and iaoAdmin cannot be the same');
// Setup access control
_setupRole(DEFAULT_ADMIN_ROLE, _factoryAdmin);
_setupRole(DEPLOYER_ROLE, _factoryAdmin);
// Admin role can add new users to deployer role
_setRoleAdmin(DEPLOYER_ROLE, DEFAULT_ADMIN_ROLE);
_pushImplementationContract(_implementation);

iaoProxyAdmin = _iaoProxyAdmin;
iaoAdmin = _iaoAdmin;
}

/// @notice Deploy a new IAOLinearVesting contract based on the current implementation version
function deployNewIAOLinearVesting(
IERC20 _stakeToken,
IERC20 _offeringToken,
uint256 _startBlock,
uint256 _endBlockOffset,
uint256 _vestingBlockOffset, // Block offset between vesting distributions
uint256 _offeringAmount,
uint256 _raisingAmount
) public onlyRole(DEPLOYER_ROLE) returns (IIAOLinearVesting newIAO) {
IAOUpgradeProxy newProxy = new IAOUpgradeProxy(
iaoProxyAdmin,
address(activeImplementationContract()),
""
);
newIAO = IIAOLinearVesting(address(newProxy));

newIAO.initialize(
_stakeToken,
_offeringToken,
_startBlock,
_endBlockOffset,
_vestingBlockOffset,
_offeringAmount,
_raisingAmount,
iaoAdmin
);

deployedIAOContracts.push(newIAO);
emit IAOLinearVestingCreated(newIAO);
}

/// @notice Get total number of IAOLinearVesting contracts deployed through this factory
function getNumberOfDeployedContracts() external view returns (uint256) {
return deployedIAOContracts.length;
}

/// @notice Returns current active implementation address
function activeImplementationContract() public view returns (IIAOLinearVesting) {
return IAOLinearVestingImplementations[IAOLinearVestingVersion];
}

/// @notice Add and use new implementation
/// @dev EXTCODESIZE returns 0 if it is called from the constructor of a contract
function pushImplementationContract(IIAOLinearVesting _newImplementation) external onlyRole(DEFAULT_ADMIN_ROLE) {
uint32 size;
assembly {
size := extcodesize(_newImplementation)
}
require(size > 0, "Not a contract");
_pushImplementationContract(_newImplementation);
}

/// @notice Add and use new implementation
function _pushImplementationContract(IIAOLinearVesting _newImplementation) internal {
IAOLinearVestingImplementations.push(_newImplementation);
IAOLinearVestingVersion = IAOLinearVestingImplementations.length - 1;
emit PushIAOLinearVestingVersion(_newImplementation, IAOLinearVestingVersion);
}

/// @notice Change active implementation
function setImplementationContract(uint256 _index) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_index < IAOLinearVestingImplementations.length, "version out of bounds");
emit SetIAOLinearVestingVersion(IAOLinearVestingVersion, _index);
IAOLinearVestingVersion = _index;
}

/// @notice change the address of the proxy admin used when deploying new IAO contracts
/// @dev The proxy admin must be different than the admin of the implementation as calls from proxyAdmin stop at the proxy contract
function setIAOAdmins(address _newIAOProxyAdmin, address _newIAOAdmin) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_newIAOProxyAdmin != _newIAOAdmin, 'iaoProxyAdmin and iaoAdmin cannot be the same');
emit UpdateIAOAdmins(iaoProxyAdmin, _newIAOProxyAdmin, iaoAdmin, _newIAOAdmin);
iaoProxyAdmin = _newIAOProxyAdmin;
iaoAdmin = _newIAOAdmin;
}

/// @notice A public function to sweep accidental ERC20 transfers to this contract.
/// @param _tokens Array of ERC20 addresses to sweep
/// @param _to Address to send tokens to
function sweepTokens(IERC20[] memory _tokens, address _to) external onlyRole(DEFAULT_ADMIN_ROLE) {
for (uint256 index = 0; index < _tokens.length; index++) {
IERC20 token = _tokens[index];
uint256 balance = token.balanceOf(address(this));
token.transfer(_to, balance);
emit SweepWithdraw(_to, token, balance);
}
}
}
3 changes: 2 additions & 1 deletion deploy-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function getNetworkConfig(network, accounts) {
return {
adminAddress: '0x50Cf6cdE8f63316b2BD6AACd0F5581aEf5dD235D', // BSC GSafe General Admin
proxyAdminAddress: '0xA75125CF0A7be136D6745B39DB1FeBadE269ba4D', // BSC GSafe General Proxy Admin
// adminAddress: '0x6c905b4108A87499CEd1E0498721F2B831c6Ab13', // (Deprecated) General Admin
deployerAddress: '0x6c905b4108A87499CEd1E0498721F2B831c6Ab13', // (Deprecated) General Admin
// proxyAdminAddress: '0xf81A0Ee9BB9606e375aeff30364FfA17Bb8a7FD1', // (Deprecated) Proxy Admin
rpcProvider: 'https://bsc-dataseed.binance.org',
}
Expand All @@ -15,6 +15,7 @@ function getNetworkConfig(network, accounts) {
adminAddress: '0xE375D169F8f7bC18a544a6e5e546e63AD7511581',
proxyAdminAddress: '0x56Cb8F9199A8F43933cAE300Ef548dfA4ADE7Da0',
rpcProvider: 'https://data-seed-prebsc-2-s1.binance.org:8545',
// rpcProvider: process.env.TESTNET_RPC,
}
} else if (['development'].includes(network)) {
console.log(`Deploying with development config.`)
Expand Down
79 changes: 79 additions & 0 deletions migrations_/2_deploy_iao_factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const IAOUpgradeProxy = artifacts.require("IAOUpgradeProxy");
const IAOLinearVestingFactory = artifacts.require("IAOLinearVestingFactory");
const IAOLinearVesting = artifacts.require("IAOLinearVesting");
const ethers = require('ethers');
const { getNetworkConfig } = require('../deploy-config')

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider('https://bsc-dataseed.binance.org'));

module.exports = async function (deployer, network, accounts) {
const ADDRESS_0 = '0x0000000000000000000000000000000000000000';
const { adminAddress, proxyAdminAddress, rpcProvider } = getNetworkConfig(network, accounts);

// Find current block
const provider = new ethers.providers.JsonRpcProvider(rpcProvider);
const block = await provider.getBlock('latest');

/// Deploy initial implementation
await deployer.deploy(IAOLinearVesting);
const iaoLinearVesting = await IAOLinearVesting.at(IAOLinearVesting.address);
await iaoLinearVesting.initialize(
ADDRESS_0,
ADDRESS_0,
block.number + 10,
1,
1,
0,
0,
adminAddress
)

await deployer.deploy(IAOLinearVestingFactory);

const abiEncodeData = web3.eth.abi.encodeFunctionCall({
"inputs": [
{
"internalType": "address",
"name": "_factoryAdmin",
"type": "address"
},
{
"internalType": "address",
"name": "_iaoProxyAdmin",
"type": "address"
},
{
"internalType": "address",
"name": "_iaoAdmin",
"type": "address"
},
{
"internalType": "contract IIAOLinearVesting",
"name": "_implementation",
"type": "address"
},
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}, [
adminAddress, // Factory admin
proxyAdminAddress, // IAO Proxy Admin
adminAddress, // IAO Admin
IAOLinearVesting.address
]);

await deployer.deploy(IAOUpgradeProxy, proxyAdminAddress, IAOLinearVestingFactory.address, abiEncodeData);

console.dir({
IAOLinearVesting: IAOLinearVesting.address,
IAOLinearVestingFactory: IAOLinearVestingFactory.address,
adminAddress,
proxyAdminAddress,
})
};



1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@openzeppelin/contracts-upgradeable": "^4.4.2",
"@openzeppelin/test-environment": "^0.1.9",
"@openzeppelin/test-helpers": "^0.5.6",
"@openzeppelin/truffle-upgrades": "^1.14.0",
"@truffle/hdwallet-provider": "^2.0.0",
"chai": "^4.3.4",
"dotenv": "^8.2.0",
Expand Down