-
Notifications
You must be signed in to change notification settings - Fork 34
/
crystalDAO.sol
150 lines (128 loc) · 4.4 KB
/
crystalDAO.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {Initializable} from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
import {EIP712Upgradeable} from "@openzeppelin-upgradeable/contracts/utils/cryptography/EIP712Upgradeable.sol";
/**
* @title DaoVault interface.
*/
interface IDaoVault {
/**
* @dev Executes an order.
* @param v v component of the signature.
* @param r r component of the signature.
* @param s s component of the signature.
* @param target Address of the contract to be called.
* @param val Value to be sent to the contract.
* @param execOrder Encoded order to be executed.
* @param deadline Deadline for the order to be executed.
*/
function execWithSignature(
uint8 v,
bytes32 r,
bytes32 s,
address target,
uint256 val,
bytes memory execOrder,
uint256 deadline
) external payable;
/**
* @dev Returns the domain separator.
* @return bytes32 Domain separator.
*/
function getDomainSeparator() external view returns (bytes32);
}
/**
* @title DaoVaultImplementation.
* @dev Implementation of a vault for DAOs.
* @dev This contract is meant to be used via proxy by a `DaoVault` contract.
*/
contract DaoVaultImplementation is Initializable, EIP712Upgradeable {
// Owner of the vault
address public owner;
// Mapping of used signatures
mapping(bytes32 => bool) private usedSigs;
mapping(address => uint256) public nonces;
// _EXEC_TYPEHASH
bytes32 private constant _EXEC_TYPEHASH =
keccak256("Exec(address target,uint256 value,bytes memory execOrder,uint256 nonce,uint256 deadline)");
constructor() {
// disable owner
owner = msg.sender;
_disableInitializers();
}
/**
* @dev Initializes the vault.
* @param _owner Owner of the vault.
*/
function initialize(address _owner) public initializer {
// EIP712 init: name DaoWallet, version 1.0
__EIP712_init("DaoWallet", "1.0");
// postInit: set owner with gas optimizations
assembly {
sstore(0, _owner)
}
}
/**
* @dev Returns the domain separator.
* @return bytes32 Domain separator.
*/
function getDomainSeparator() public view returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev Executes a transaction from the vault.
* @param target Address of the contract to execute the transaction on.
* @param val Value to send with the transaction.
* @param execOrder Encoded transaction to execute.
*/
function execWithSignature(
uint8 v,
bytes32 r,
bytes32 s,
address target,
uint256 val,
bytes memory execOrder,
uint256 deadline
) external payable {
require(deadline > block.timestamp, "Execution window expired!");
// Construct the message struct
bytes32 structHash = keccak256(abi.encode(_EXEC_TYPEHASH, target, val, execOrder, nonces[owner]++, deadline));
// Hash the struct and add EIP712 prefix
bytes32 hash = _hashTypedDataV4(structHash);
// Recover signer from signature
address signer = ecrecover(hash, v, r, s);
require(owner == signer, "Only owner can execute!");
require(!usedSigs[hash], "Signature has already been used!");
// Mark signature as used
usedSigs[hash] = true;
// Execute transaction
(bool success, bytes memory data) = target.call{value: val}(execOrder);
require(success, string(data));
}
receive() external payable {
// donations come here
}
}
/**
* @title FactoryDao.
* @dev Factory contract for DaoVaults.
*/
contract FactoryDao {
// Address of the implementation contract
address payable public immutable walletImplementation;
constructor() {
// Deploy implementation contract
walletImplementation = payable(address(new DaoVaultImplementation()));
}
/**
* @dev Creates a new wallet.
* @return wallet Address of the new wallet.
*/
function newWallet() public returns (address payable wallet) {
// Deploy clone
wallet = payable(Clones.clone(walletImplementation));
// Initialize clone
DaoVaultImplementation(wallet).initialize(msg.sender);
}
}