352 lines
11 KiB
Solidity
352 lines
11 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.7;
|
|
|
|
import "@gnosis.pm/zodiac/contracts/factory/ModuleProxyFactory.sol";
|
|
import "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol";
|
|
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
|
|
import "./Baal.sol";
|
|
|
|
contract BaalSummoner is Initializable, OwnableUpgradeable, UUPSUpgradeable {
|
|
// when some of the init addresses are updated
|
|
uint256 public addrsVersion;
|
|
|
|
address payable public template; // fixed template for baal using eip-1167 proxy pattern
|
|
|
|
// Template contract to use for new Gnosis safe proxies
|
|
address public gnosisSingleton;
|
|
|
|
// Library to use for EIP1271 compatability
|
|
address public gnosisFallbackLibrary;
|
|
|
|
// Library to use for all safe transaction executions
|
|
address public gnosisMultisendLibrary;
|
|
|
|
// template contract to clone for loot ERC20 token
|
|
address public lootSingleton;
|
|
|
|
// template contract to clone for shares ERC20 token
|
|
address public sharesSingleton;
|
|
|
|
// Proxy summoners
|
|
//
|
|
GnosisSafeProxyFactory gnosisSafeProxyFactory;
|
|
ModuleProxyFactory moduleProxyFactory;
|
|
|
|
event SetAddrsVersion(
|
|
uint256 version
|
|
);
|
|
|
|
event SummonBaal(
|
|
address indexed baal,
|
|
address indexed loot,
|
|
address indexed shares,
|
|
address safe,
|
|
address forwarder,
|
|
uint256 existingAddrs
|
|
);
|
|
|
|
event DaoReferral(
|
|
bytes32 referrer,
|
|
address daoAddress
|
|
);
|
|
|
|
event DeployBaalTokens(
|
|
address lootToken,
|
|
address sharesToken
|
|
);
|
|
|
|
event DeployBaalSafe(
|
|
address baalSafe,
|
|
address moduleAddr
|
|
);
|
|
|
|
function initialize() initializer public {
|
|
__Ownable_init();
|
|
__UUPSUpgradeable_init();
|
|
}
|
|
|
|
// must be called after deploy to set libraries
|
|
function setAddrs(
|
|
address payable _template,
|
|
address _gnosisSingleton,
|
|
address _gnosisFallbackLibrary,
|
|
address _gnosisMultisendLibrary,
|
|
address _gnosisSafeProxyFactory,
|
|
address _moduleProxyFactory,
|
|
address _lootSingleton,
|
|
address _sharesSingleton
|
|
) public onlyOwner {
|
|
require(_lootSingleton != address(0), "!lootSingleton");
|
|
require(_sharesSingleton != address(0), "!sharesSingleton");
|
|
require(_gnosisSingleton != address(0), "!gnosisSingleton");
|
|
require(_gnosisFallbackLibrary != address(0), '!gnosisFallbackLibrary');
|
|
require(_gnosisMultisendLibrary != address(0), '!gnosisMultisendLibrary');
|
|
require(_gnosisSafeProxyFactory != address(0), '!gnosisSafeProxyFactory');
|
|
require(_moduleProxyFactory != address(0), '!moduleProxyFactory');
|
|
|
|
template = _template;
|
|
gnosisSingleton = _gnosisSingleton;
|
|
gnosisFallbackLibrary = _gnosisFallbackLibrary;
|
|
gnosisMultisendLibrary = _gnosisMultisendLibrary;
|
|
gnosisSafeProxyFactory = GnosisSafeProxyFactory(_gnosisSafeProxyFactory);
|
|
moduleProxyFactory = ModuleProxyFactory(_moduleProxyFactory);
|
|
lootSingleton = _lootSingleton;
|
|
sharesSingleton = _sharesSingleton;
|
|
|
|
emit SetAddrsVersion(
|
|
addrsVersion++
|
|
);
|
|
|
|
}
|
|
|
|
function encodeMultisend(bytes[] memory _calls, address _target)
|
|
public
|
|
pure
|
|
returns (bytes memory encodedMultisend)
|
|
{
|
|
bytes memory encodedActions;
|
|
for (uint256 i = 0; i < _calls.length; i++) {
|
|
encodedActions = abi.encodePacked(
|
|
encodedActions,
|
|
uint8(0),
|
|
_target,
|
|
uint256(0),
|
|
uint256(_calls[i].length),
|
|
bytes(_calls[i])
|
|
);
|
|
}
|
|
encodedMultisend = abi.encodeWithSignature(
|
|
"multiSend(bytes)",
|
|
encodedActions
|
|
);
|
|
}
|
|
|
|
function summonBaal(
|
|
bytes calldata initializationParams,
|
|
bytes[] calldata initializationActions,
|
|
uint256 _saltNonce
|
|
) external returns (address) {
|
|
|
|
return
|
|
_summonBaal(
|
|
initializationParams,
|
|
initializationActions,
|
|
_saltNonce
|
|
);
|
|
}
|
|
|
|
// Add a referrer to help keep track of where deploies are coming from
|
|
function summonBaalFromReferrer(
|
|
bytes calldata initializationParams,
|
|
bytes[] calldata initializationActions,
|
|
uint256 _saltNonce,
|
|
bytes32 referrer
|
|
) external payable returns (address) {
|
|
address daoAddress;
|
|
|
|
daoAddress = _summonBaal(
|
|
initializationParams,
|
|
initializationActions,
|
|
_saltNonce
|
|
);
|
|
|
|
emit DaoReferral(referrer, daoAddress);
|
|
return daoAddress;
|
|
}
|
|
|
|
// deploy new share and loot contracts
|
|
function deployTokens(string memory _name, string memory _symbol)
|
|
public
|
|
returns (address lootToken, address sharesToken)
|
|
{
|
|
lootToken = address(new ERC1967Proxy(
|
|
lootSingleton,
|
|
abi.encodeWithSelector(
|
|
IBaalToken(lootSingleton).setUp.selector,
|
|
string(abi.encodePacked(_name, " LOOT")),
|
|
string(abi.encodePacked(_symbol, "-LOOT")))
|
|
));
|
|
|
|
sharesToken = address(new ERC1967Proxy(
|
|
sharesSingleton,
|
|
abi.encodeWithSelector(
|
|
IBaalToken(sharesSingleton).setUp.selector,
|
|
_name,
|
|
_symbol)
|
|
));
|
|
|
|
emit DeployBaalTokens(lootToken, sharesToken);
|
|
|
|
}
|
|
|
|
function configureSafe(address _moduleAddr, GnosisSafe _safe) internal {
|
|
// Generate delegate calls so the safe calls enableModule on itself during setup
|
|
bytes memory _enableBaal = abi.encodeWithSignature(
|
|
"enableModule(address)",
|
|
address(_moduleAddr)
|
|
);
|
|
bytes memory _enableBaalMultisend = abi.encodePacked(
|
|
uint8(0),
|
|
address(_safe),
|
|
uint256(0),
|
|
uint256(_enableBaal.length),
|
|
bytes(_enableBaal)
|
|
);
|
|
|
|
bytes memory _multisendAction = abi.encodeWithSignature(
|
|
"multiSend(bytes)",
|
|
_enableBaalMultisend
|
|
);
|
|
|
|
// Workaround for solidity dynamic memory array
|
|
address[] memory _owners = new address[](1);
|
|
_owners[0] = address(_moduleAddr);
|
|
|
|
// Call setup on safe to enable our new module and set the module as the only signer
|
|
_safe.setup(
|
|
_owners,
|
|
1,
|
|
gnosisMultisendLibrary,
|
|
_multisendAction,
|
|
gnosisFallbackLibrary,
|
|
address(0),
|
|
0,
|
|
payable(address(0))
|
|
);
|
|
}
|
|
|
|
|
|
// deploy a safe with module and single module signer setup
|
|
// with nounce
|
|
function deployAndSetupSafe(address _moduleAddr, uint256 _saltNonce)
|
|
public
|
|
returns (address)
|
|
{
|
|
GnosisSafe _safe = GnosisSafe(
|
|
payable(
|
|
gnosisSafeProxyFactory.createProxyWithNonce(
|
|
gnosisSingleton,
|
|
bytes(""),
|
|
_saltNonce
|
|
)
|
|
)
|
|
);
|
|
|
|
configureSafe(_moduleAddr, _safe);
|
|
|
|
emit DeployBaalSafe(address(_safe), address(_moduleAddr));
|
|
|
|
return address(_safe);
|
|
}
|
|
|
|
|
|
// deploy a safe with module and single module signer setup
|
|
function deployAndSetupSafe(address _moduleAddr)
|
|
public
|
|
returns (address)
|
|
{
|
|
GnosisSafe _safe = GnosisSafe(
|
|
payable(
|
|
gnosisSafeProxyFactory.createProxy(
|
|
gnosisSingleton,
|
|
bytes("")
|
|
)
|
|
)
|
|
);
|
|
|
|
configureSafe(_moduleAddr, _safe);
|
|
|
|
emit DeployBaalSafe(address(_safe), address(_moduleAddr));
|
|
|
|
return address(_safe);
|
|
}
|
|
|
|
// advanced summon baal with different configurations
|
|
// name and symbol can be blank if bringing own baal tokens
|
|
// zero address for either loot or shares token will summon new ones
|
|
// if bringing own tokens the ownership must be transfered to the new DAO
|
|
// zero address for Safe with summon and setup a new Safe
|
|
// if bringing existing safe the new dao must be enabled as a module
|
|
// todo: add a simple summon that just creates a dao with a single summoner
|
|
function _summonBaal(
|
|
bytes calldata initializationParams,
|
|
bytes[] calldata initializationActions,
|
|
uint256 _saltNonce
|
|
) internal returns (address) {
|
|
uint256 existingAddrs; // 1 tokens, 2 safe, 3 both
|
|
(
|
|
string memory _name, /*_name Name for erc20 `shares` accounting, empty if token */
|
|
string memory _symbol, /*_symbol Symbol for erc20 `shares` accounting, empty if token*/
|
|
address _safeAddr, /*address of safe, 0 addr if new*/
|
|
address _forwarder, /*Trusted forwarder address for meta-transactions (EIP 2771), 0 addr if initially disabled*/
|
|
address _lootToken, /*predeployed loot token, 0 addr if new*/
|
|
address _sharesToken /*predeployed shares token, 0 addr if new*/
|
|
) = abi.decode(initializationParams, (string, string, address, address, address, address));
|
|
|
|
Baal _baal = Baal(
|
|
moduleProxyFactory.deployModule(
|
|
template,
|
|
abi.encodeWithSignature("avatar()"),
|
|
_saltNonce
|
|
)
|
|
);
|
|
|
|
// if loot or shares are zero address new tokens are deployed
|
|
// tokens need to be baalTokens
|
|
if(_lootToken == address(0) || _sharesToken == address(0)){
|
|
(_lootToken, _sharesToken) = deployTokens(_name, _symbol);
|
|
// pause tokens by default and transfer to the DAO
|
|
IBaalToken(_lootToken).pause();
|
|
IBaalToken(_sharesToken).pause();
|
|
IBaalToken(_lootToken).transferOwnership(address(_baal));
|
|
IBaalToken(_sharesToken).transferOwnership(address(_baal));
|
|
} else {
|
|
existingAddrs += 1;
|
|
}
|
|
|
|
// if zero address deploy a new safe
|
|
// Needs to be a valid zodiac treasury
|
|
if(_safeAddr == address(0)){
|
|
_safeAddr = deployAndSetupSafe(address(_baal), _saltNonce);
|
|
} else {
|
|
existingAddrs += 2;
|
|
}
|
|
|
|
bytes memory _initializationMultisendData = encodeMultisend(
|
|
initializationActions,
|
|
address(_baal)
|
|
);
|
|
bytes memory _initializer = abi.encode(
|
|
_lootToken,
|
|
_sharesToken,
|
|
gnosisMultisendLibrary,
|
|
_safeAddr,
|
|
_forwarder,
|
|
_initializationMultisendData
|
|
);
|
|
// can run the actions now because we have a baal
|
|
_baal.setUp(_initializer);
|
|
|
|
emit SummonBaal(
|
|
address(_baal),
|
|
address(_baal.lootToken()),
|
|
address(_baal.sharesToken()),
|
|
_safeAddr,
|
|
_forwarder,
|
|
existingAddrs
|
|
);
|
|
|
|
return (address(_baal));
|
|
}
|
|
|
|
function _authorizeUpgrade(address newImplementation)
|
|
internal
|
|
onlyOwner
|
|
override
|
|
{}
|
|
}
|