idle_moloch/lib/Baal/contracts/BaalSummoner.sol
2024-11-01 11:55:27 +01:00

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
{}
}