Init
This commit is contained in:
commit
9d6c18fbdc
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.sol linguist-language=Solidity
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
out
|
||||||
|
cache
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[submodule "lib/forge-std"]
|
||||||
|
path = lib/forge-std
|
||||||
|
url = https://github.com/foundry-rs/forge-std
|
||||||
|
branch = v1.5.5
|
20
README.md
Normal file
20
README.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Foundryup ([Windows](https://github.com/altugbakan/foundryup-windows), [Linux](https://book.getfoundry.sh/getting-started/installation))
|
||||||
|
- Node 14 or higher ([Windows](https://github.com/coreybutler/nvm-windows), [Linux](https://github.com/nvm-sh/nvm))
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone --recurse-submodules https://git.tornado.ws/Theo/proposal-19-contract.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contract
|
||||||
|
|
||||||
|
https://etherscan.io/address/0xfd533220d9f030a166ca0e126202d17fa4818d89#code
|
8
foundry.toml
Normal file
8
foundry.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[profile.default]
|
||||||
|
solc-version = "0.8.19"
|
||||||
|
src = 'src'
|
||||||
|
out = 'out'
|
||||||
|
libs = ["node_modules", "lib"]
|
||||||
|
chain_id = 1
|
||||||
|
optimizer = true
|
||||||
|
optimizer-runs = 10_000_000
|
1
lib/forge-std
Submodule
1
lib/forge-std
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 73d44ec7d124e3831bc5f832267889ffb6f9bc3f
|
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "proposal-19-contract",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Tornado proposal #19 smart contract code & tests",
|
||||||
|
"main": "index.js",
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib",
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "forge test -vvvvv --fork-url https://rpc.mevblocker.io --block-number 17179667",
|
||||||
|
"build": "forge build --optimize"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.tornado.ws/Theo/proposal-19-contract"
|
||||||
|
},
|
||||||
|
"author": "Theo",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
6
remappings.txt
Normal file
6
remappings.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@proprietary/=src/proprietary/
|
||||||
|
@interfaces/=src/interfaces/
|
||||||
|
@root/=src/
|
||||||
|
|
||||||
|
@forge-std/=lib/forge-std/src/
|
||||||
|
|
38
src/Proposal.sol
Normal file
38
src/Proposal.sol
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
|
import "@interfaces/ISablier.sol";
|
||||||
|
import "@interfaces/IERC20.sol";
|
||||||
|
|
||||||
|
contract Proposal {
|
||||||
|
function executeProposal() external {
|
||||||
|
uint256 FISCAL_QUARTER_DURATION = 91 days;
|
||||||
|
uint256 REMUNERATION_START_TS = block.timestamp;
|
||||||
|
uint256 REMUNERATION_AMOUNT = 4172 ether;
|
||||||
|
uint256 REMUNERATION_NORMALISED_AMOUNT = REMUNERATION_AMOUNT -
|
||||||
|
(REMUNERATION_AMOUNT % FISCAL_QUARTER_DURATION);
|
||||||
|
|
||||||
|
uint256 SERVICES_COST_REIMBURSEMENT_AMOUNT = 834 ether;
|
||||||
|
|
||||||
|
address _tokenAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
|
||||||
|
address _sablierAddress = 0xCD18eAa163733Da39c232722cBC4E8940b1D8888;
|
||||||
|
address REMUNERATION_ADDRESS = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58;
|
||||||
|
|
||||||
|
IERC20(_tokenAddress).transfer(
|
||||||
|
REMUNERATION_ADDRESS,
|
||||||
|
SERVICES_COST_REIMBURSEMENT_AMOUNT
|
||||||
|
);
|
||||||
|
|
||||||
|
IERC20(_tokenAddress).approve(
|
||||||
|
_sablierAddress,
|
||||||
|
REMUNERATION_NORMALISED_AMOUNT
|
||||||
|
);
|
||||||
|
|
||||||
|
ISablier(_sablierAddress).createStream(
|
||||||
|
REMUNERATION_ADDRESS,
|
||||||
|
REMUNERATION_NORMALISED_AMOUNT,
|
||||||
|
_tokenAddress,
|
||||||
|
REMUNERATION_START_TS,
|
||||||
|
REMUNERATION_START_TS + FISCAL_QUARTER_DURATION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
src/interfaces/IERC20.sol
Normal file
15
src/interfaces/IERC20.sol
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
|
interface IERC20 {
|
||||||
|
function transfer(address to, uint256 amount) external returns (bool);
|
||||||
|
|
||||||
|
function transferFrom(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
) external returns (bool);
|
||||||
|
|
||||||
|
function balanceOf(address owner) external returns (uint256);
|
||||||
|
|
||||||
|
function approve(address spender, uint256 amount) external;
|
||||||
|
}
|
21
src/interfaces/IGovernance.sol
Normal file
21
src/interfaces/IGovernance.sol
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
|
interface IGovernance {
|
||||||
|
function propose(
|
||||||
|
address target,
|
||||||
|
string memory description
|
||||||
|
) external returns (uint256);
|
||||||
|
|
||||||
|
function castVote(uint256 proposalId, bool support) external;
|
||||||
|
|
||||||
|
function execute(uint256 proposalId) external payable;
|
||||||
|
|
||||||
|
function lock(
|
||||||
|
address owner,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 deadline,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) external;
|
||||||
|
}
|
11
src/interfaces/ISablier.sol
Normal file
11
src/interfaces/ISablier.sol
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
|
interface ISablier {
|
||||||
|
function createStream(
|
||||||
|
address recipent,
|
||||||
|
uint256 deposit,
|
||||||
|
address tokenAddress,
|
||||||
|
uint256 startTime,
|
||||||
|
uint256 stopTime
|
||||||
|
) external returns (uint256);
|
||||||
|
}
|
40
src/proprietary/Mock.sol
Normal file
40
src/proprietary/Mock.sol
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
|
contract Mock {
|
||||||
|
uint256 constant TEST_PRIVATE_KEY_ONE =
|
||||||
|
0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0;
|
||||||
|
uint256 constant TEST_PRIVATE_KEY_TWO =
|
||||||
|
0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064;
|
||||||
|
address constant TEST_ADDRESS_ONE =
|
||||||
|
0x118251976c65AFAf291f5255450ddb5b6A4d8B88;
|
||||||
|
address constant TEST_ADDRESS_TWO =
|
||||||
|
0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20;
|
||||||
|
|
||||||
|
uint256 constant PROPOSAL_DURATION = 7 days;
|
||||||
|
uint256 constant PROPOSAL_THRESHOLD = 25000 ether;
|
||||||
|
string constant PROPOSAL_DESCRIPTION =
|
||||||
|
"{title:'Proposal #19: New developer and remuneration',description:''}";
|
||||||
|
|
||||||
|
address constant VERIFIER_ADDRESS =
|
||||||
|
0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
|
||||||
|
|
||||||
|
bytes32 constant PERMIT_TYPEHASH =
|
||||||
|
keccak256(
|
||||||
|
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
||||||
|
);
|
||||||
|
|
||||||
|
bytes32 constant EIP712_DOMAIN =
|
||||||
|
keccak256(
|
||||||
|
abi.encode(
|
||||||
|
keccak256(
|
||||||
|
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
||||||
|
),
|
||||||
|
keccak256(bytes("TornadoCash")),
|
||||||
|
keccak256(bytes("1")),
|
||||||
|
1,
|
||||||
|
VERIFIER_ADDRESS
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
uint16 constant PERMIT_FUNC_SELECTOR = uint16(0x1901);
|
||||||
|
}
|
18
src/proprietary/Parameters.sol
Normal file
18
src/proprietary/Parameters.sol
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
|
contract Parameters {
|
||||||
|
uint256 FISCAL_QUARTER_DURATION = 91 days;
|
||||||
|
uint256 REMUNERATION_START_TS = block.timestamp;
|
||||||
|
uint256 REMUNERATION_AMOUNT = 4172 ether;
|
||||||
|
uint256 REMUNERATION_NORMALISED_AMOUNT =
|
||||||
|
REMUNERATION_AMOUNT - (REMUNERATION_AMOUNT % FISCAL_QUARTER_DURATION);
|
||||||
|
uint256 SERVICES_COST_REIMBURSEMENT_AMOUNT = 834 ether;
|
||||||
|
|
||||||
|
address REMUNERATION_ADDRESS = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58;
|
||||||
|
|
||||||
|
// Beneficary addresses
|
||||||
|
address public _governanceAddress =
|
||||||
|
0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce;
|
||||||
|
address public _tokenAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
|
||||||
|
address public _sablierAddress = 0xCD18eAa163733Da39c232722cBC4E8940b1D8888;
|
||||||
|
}
|
117
test/Proposal.t.sol
Normal file
117
test/Proposal.t.sol
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
pragma solidity ^0.8.1;
|
||||||
|
|
||||||
|
import "@interfaces/IGovernance.sol";
|
||||||
|
import "@interfaces/IERC20.sol";
|
||||||
|
|
||||||
|
import "@proprietary/Parameters.sol";
|
||||||
|
import "@proprietary/Mock.sol";
|
||||||
|
|
||||||
|
import "@root/Proposal.sol";
|
||||||
|
import "@forge-std/Test.sol";
|
||||||
|
|
||||||
|
contract ProposalTest is Test, Parameters, Mock {
|
||||||
|
modifier conditionStateChecks() {
|
||||||
|
checkParameters();
|
||||||
|
_;
|
||||||
|
checkResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testProposal() public conditionStateChecks {
|
||||||
|
uint256 proposalId = voteAndCreateProposal(address(new Proposal()));
|
||||||
|
|
||||||
|
IGovernance(_governanceAddress).execute(proposalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function voteAndCreateProposal(
|
||||||
|
address proposalAddress
|
||||||
|
) public returns (uint256) {
|
||||||
|
retrieveAndLockBalance(
|
||||||
|
TEST_PRIVATE_KEY_ONE,
|
||||||
|
TEST_ADDRESS_ONE,
|
||||||
|
PROPOSAL_THRESHOLD
|
||||||
|
);
|
||||||
|
retrieveAndLockBalance(TEST_PRIVATE_KEY_TWO, TEST_ADDRESS_TWO, 1 ether);
|
||||||
|
|
||||||
|
/* ----------PROPOSER------------ */
|
||||||
|
vm.startPrank(TEST_ADDRESS_ONE);
|
||||||
|
|
||||||
|
uint256 proposalId = IGovernance(_governanceAddress).propose(
|
||||||
|
proposalAddress,
|
||||||
|
PROPOSAL_DESCRIPTION
|
||||||
|
);
|
||||||
|
|
||||||
|
// TIME-TRAVEL
|
||||||
|
vm.warp(block.timestamp + 6 hours);
|
||||||
|
|
||||||
|
IGovernance(_governanceAddress).castVote(proposalId, true);
|
||||||
|
|
||||||
|
vm.stopPrank();
|
||||||
|
/* ------------------------------ */
|
||||||
|
|
||||||
|
/* -------------VOTER-------------*/
|
||||||
|
vm.startPrank(TEST_ADDRESS_TWO);
|
||||||
|
IGovernance(_governanceAddress).castVote(proposalId, true);
|
||||||
|
vm.stopPrank();
|
||||||
|
/* ------------------------------ */
|
||||||
|
|
||||||
|
// TIME-TRAVEL
|
||||||
|
vm.warp(block.timestamp + PROPOSAL_DURATION);
|
||||||
|
|
||||||
|
return proposalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function retrieveAndLockBalance(
|
||||||
|
uint256 privateKey,
|
||||||
|
address voter,
|
||||||
|
uint256 amount
|
||||||
|
) internal {
|
||||||
|
uint256 lockTimestamp = block.timestamp + PROPOSAL_DURATION;
|
||||||
|
bytes32 messageHash = keccak256(
|
||||||
|
abi.encodePacked(
|
||||||
|
PERMIT_FUNC_SELECTOR,
|
||||||
|
EIP712_DOMAIN,
|
||||||
|
keccak256(
|
||||||
|
abi.encode(
|
||||||
|
PERMIT_TYPEHASH,
|
||||||
|
voter,
|
||||||
|
_governanceAddress,
|
||||||
|
amount,
|
||||||
|
0,
|
||||||
|
lockTimestamp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* ----------GOVERNANCE------- */
|
||||||
|
vm.startPrank(_governanceAddress);
|
||||||
|
IERC20(_tokenAddress).transfer(voter, amount);
|
||||||
|
vm.stopPrank();
|
||||||
|
/* ----------------------------*/
|
||||||
|
|
||||||
|
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, messageHash);
|
||||||
|
|
||||||
|
/* ----------VOTER------------ */
|
||||||
|
vm.startPrank(voter);
|
||||||
|
IGovernance(_governanceAddress).lock(
|
||||||
|
voter,
|
||||||
|
amount,
|
||||||
|
lockTimestamp,
|
||||||
|
v,
|
||||||
|
r,
|
||||||
|
s
|
||||||
|
);
|
||||||
|
vm.stopPrank();
|
||||||
|
/* ----------------------------*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkParameters() internal {
|
||||||
|
require(REMUNERATION_NORMALISED_AMOUNT > 4100 ether);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkResults() internal {
|
||||||
|
require(
|
||||||
|
IERC20(_tokenAddress).balanceOf(REMUNERATION_ADDRESS) == 834 ether
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user