From 633981e687300dc31392fcfa3c15da02ca48e7e7 Mon Sep 17 00:00:00 2001 From: Theo Date: Wed, 22 Nov 2023 16:13:01 -0800 Subject: [PATCH] Add reimbursement amount calculation, proposal code and tests --- README.md | 1 - data/.gitkeep | 0 data/amountInTorn.txt | 1 + lib/forge-std | 1 + package.json | 4 +- scripts/calculateReimbursement.ts | 17 +++++++ scripts/utils.ts | 34 ------------- src/ExampleProposal.sol | 11 ---- src/ReimbursementProposal.sol | 15 ++++++ src/interfaces/IGnosisSafe.sol | 84 ------------------------------- src/interfaces/IGovernance.sol | 57 --------------------- test/ExampleProposal.t.sol | 24 --------- test/ReimbursementProposal.t.sol | 32 ++++++++++++ 13 files changed, 69 insertions(+), 212 deletions(-) delete mode 100644 data/.gitkeep create mode 100644 data/amountInTorn.txt create mode 160000 lib/forge-std create mode 100644 scripts/calculateReimbursement.ts delete mode 100644 scripts/utils.ts delete mode 100644 src/ExampleProposal.sol create mode 100644 src/ReimbursementProposal.sol delete mode 100644 src/interfaces/IGnosisSafe.sol delete mode 100644 src/interfaces/IGovernance.sol delete mode 100644 test/ExampleProposal.t.sol create mode 100644 test/ReimbursementProposal.t.sol diff --git a/README.md b/README.md index e63c749..e2054bf 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ git clone --recurse-submodules cd npm install -npm run init ``` ### Testing diff --git a/data/.gitkeep b/data/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/data/amountInTorn.txt b/data/amountInTorn.txt new file mode 100644 index 0000000..a6ebe64 --- /dev/null +++ b/data/amountInTorn.txt @@ -0,0 +1 @@ +uint256 public constant reimbursementAmount = 6226 ether; \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 0000000..2f11269 --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 diff --git a/package.json b/package.json index 40c7245..b7be3e1 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "private": false, "scripts": { "init": "cd lib && git clone --recurse-submodules https://github.com/foundry-rs/forge-std", + "calculate": "ts-node --esm scripts/calculateReimbursement.ts", "test:windows": ".\\.env.bat && forge test", "test:linux": ". .env && forge test", "test:gas:windows": ".\\.env.bat && forge test --gas-report", @@ -16,7 +17,8 @@ "dependencies": { "@ensdomains/ens-contracts": "^0.0.21", "@openzeppelin/contracts": "^4.9.0", - "@openzeppelin/upgrades-core": "^1.26.2" + "@openzeppelin/upgrades-core": "^1.26.2", + "node-fetch": "^3.3.2" }, "optionalDependencies": { "@gnosis.pm/ido-contracts": "^0.5.0", diff --git a/scripts/calculateReimbursement.ts b/scripts/calculateReimbursement.ts new file mode 100644 index 0000000..364f3af --- /dev/null +++ b/scripts/calculateReimbursement.ts @@ -0,0 +1,17 @@ +import fs from "fs"; +import path from "path"; +import fetch from "node-fetch"; + +const rpcMonthlyPrice = 1000; +const servicesMonthlyPrice = 600; +const amountInUsd = (rpcMonthlyPrice + servicesMonthlyPrice) * 12; + +const binanceApiLink = "https://api.binance.com/api/v3/ticker?symbol=TORNBUSD&windowSize=7d"; +const res = await fetch(binanceApiLink); +const { weightedAvgPrice } = (await res.json()) as { weightedAvgPrice: string }; + +const amountInTorn = Math.floor((amountInUsd + (amountInUsd * 20) / 100) / Number(weightedAvgPrice)); + +fs.writeFileSync(path.join(".", "data", "amountInTorn.txt"), `uint256 public constant reimbursementAmount = ${amountInTorn} ether;`, { + flag: "w+", +}); diff --git a/scripts/utils.ts b/scripts/utils.ts deleted file mode 100644 index ff27200..0000000 --- a/scripts/utils.ts +++ /dev/null @@ -1,34 +0,0 @@ -type NodeVarObject = { [key: string]: string }; -type NodeVarArray = [string, string]; - -const solidityCodePadding = " ".repeat(8); -const pad = (decl: string, padding: string = solidityCodePadding) => padding + decl + "\n"; - -class DeclCalculator { - public constructor( - private declType: string, - private padding: string = solidityCodePadding, - private transformator: Function = ( - () => (x: any) => - x - )(), - private variableNameChanger: Function = ( - () => (x: any) => - x - )() - ) {} - - private displayVariableName(varObj: NodeVarObject) { - return Object.keys(varObj)[0]; - } - - public calculateDecl = (varInfo: NodeVarObject | NodeVarArray, type: string = "bytes32") => { - const solidityVariableName = this.variableNameChanger(Array.isArray(varInfo) ? varInfo[0] : this.displayVariableName(varInfo)); - const solidityVariableValue = this.transformator(Array.isArray(varInfo) ? varInfo[1] : Object.values(varInfo)[0]); - const solidityDeclaration = `${this.declType || type} ${solidityVariableName} = ${solidityVariableValue};`; - - return pad(solidityDeclaration, this.padding); - }; -} - -export { DeclCalculator }; diff --git a/src/ExampleProposal.sol b/src/ExampleProposal.sol deleted file mode 100644 index 68fd33d..0000000 --- a/src/ExampleProposal.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.19; - -import { IGovernance } from "@interfaces/IGovernance.sol"; - -contract ExampleProposal { - function executeProposal() public { - /* ... */ - } -} diff --git a/src/ReimbursementProposal.sol b/src/ReimbursementProposal.sol new file mode 100644 index 0000000..75b547b --- /dev/null +++ b/src/ReimbursementProposal.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.19; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract ReimbursementProposal { + address public constant developerAddress = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58; + IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); + uint256 public constant reimbursementAmount = 6226 ether; // check scripts/calculateReimbursement.ts + + function executeProposal() public { + TORN.transfer(developerAddress, reimbursementAmount); + } +} diff --git a/src/interfaces/IGnosisSafe.sol b/src/interfaces/IGnosisSafe.sol deleted file mode 100644 index 5a96277..0000000 --- a/src/interfaces/IGnosisSafe.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.19; - -interface IGnosisSafe { - enum Operation { - Call, - DelegateCall - } - - function NAME() external view returns (string memory); - - function VERSION() external view returns (string memory); - - function nonce() external view returns (uint256); - - function domainSeparator() external view returns (bytes32); - - function signedMessages(bytes32) external view returns (uint256); - - function approvedHashes(address, bytes32) external view returns (uint256); - - function setup( - address[] calldata _owners, - uint256 _threshold, - address to, - bytes calldata data, - address fallbackHandler, - address paymentToken, - uint256 payment, - address payable paymentReceiver - ) external; - - function execTransaction( - address to, - uint256 value, - bytes calldata data, - Operation operation, - uint256 safeTxGas, - uint256 baseGas, - uint256 gasPrice, - address gasToken, - address payable refundReceiver, - bytes calldata signatures - ) external returns (bool success); - - function requiredTxGas(address to, uint256 value, bytes calldata data, Operation operation) - external - returns (uint256); - - function approveHash(bytes32 hashToApprove) external; - - function signMessage(bytes calldata _data) external; - - function isValidSignature(bytes calldata _data, bytes calldata _signature) external returns (bytes4); - - function getMessageHash(bytes memory message) external view returns (bytes32); - - function encodeTransactionData( - address to, - uint256 value, - bytes memory data, - Operation operation, - uint256 safeTxGas, - uint256 baseGas, - uint256 gasPrice, - address gasToken, - address refundReceiver, - uint256 _nonce - ) external view returns (bytes memory); - - function getTransactionHash( - address to, - uint256 value, - bytes memory data, - Operation operation, - uint256 safeTxGas, - uint256 baseGas, - uint256 gasPrice, - address gasToken, - address refundReceiver, - uint256 _nonce - ) external view returns (bytes32); -} diff --git a/src/interfaces/IGovernance.sol b/src/interfaces/IGovernance.sol deleted file mode 100644 index 87b124d..0000000 --- a/src/interfaces/IGovernance.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.19; - -enum ProposalState { - Pending, - Active, - Defeated, - Timelocked, - AwaitingExecution, - Executed, - Expired -} - -struct Proposal { - // Creator of the proposal - address proposer; - // target addresses for the call to be made - address target; - // The block at which voting begins - uint256 startTime; - // The block at which voting ends: votes must be cast prior to this block - uint256 endTime; - // Current number of votes in favor of this proposal - uint256 forVotes; - // Current number of votes in opposition to this proposal - uint256 againstVotes; - // Flag marking whether the proposal has been executed - bool executed; - // Flag marking whether the proposal voting time has been extended - // Voting time can be extended once, if the proposal outcome has changed during CLOSING_PERIOD - bool extended; -} - -interface IGovernance { - function initialized() external view returns (bool); - function initializing() external view returns (bool); - function EXECUTION_DELAY() external view returns (uint256); - function EXECUTION_EXPIRATION() external view returns (uint256); - function QUORUM_VOTES() external view returns (uint256); - function PROPOSAL_THRESHOLD() external view returns (uint256); - function VOTING_DELAY() external view returns (uint256); - function VOTING_PERIOD() external view returns (uint256); - function CLOSING_PERIOD() external view returns (uint256); - function VOTE_EXTEND_TIME() external view returns (uint256); - function torn() external view returns (address); - function proposals(uint256 index) external view returns (Proposal memory); - function proposalCount() external view returns (uint256); - function lockedBalance(address account) external view returns (uint256); - function propose(address target, string memory description) external returns (uint256); - function castVote(uint256 proposalId, bool support) external; - function lock(address owner, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; - function lockWithApproval(uint256 amount) external; - function unlock(uint256 amount) external; - function execute(uint256 proposalId) external payable; - function state(uint256 proposalId) external view returns (ProposalState); -} diff --git a/test/ExampleProposal.t.sol b/test/ExampleProposal.t.sol deleted file mode 100644 index 9efd7bc..0000000 --- a/test/ExampleProposal.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import { ProposalUtils } from "./utils/ProposalUtils.sol"; -import { ExampleProposal } from "@root/ExampleProposal.sol"; - -import { console2 } from "@forge-std/console2.sol"; - -contract TestExampleProposal is ProposalUtils { - modifier executeCurrentProposalBefore() { - createAndExecuteProposal(); - _; - } - - function createAndExecuteProposal() public { - address proposalAddress = address(new ExampleProposal()); /* your proposal initialization */ - - proposeAndExecute(proposalAddress); - } - - /* your tests */ - - function testProposal() public executeCurrentProposalBefore { } -} diff --git a/test/ReimbursementProposal.t.sol b/test/ReimbursementProposal.t.sol new file mode 100644 index 0000000..84651eb --- /dev/null +++ b/test/ReimbursementProposal.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { ProposalUtils } from "./utils/ProposalUtils.sol"; +import { ReimbursementProposal } from "@root/ReimbursementProposal.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { console2 } from "@forge-std/console2.sol"; + +contract TestReimbursementProposal is ProposalUtils { + address public constant developerAddress = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58; + + function createAndExecuteProposal() public { + address proposalAddress = address(new ReimbursementProposal()); + + proposeAndExecute(proposalAddress); + } + + function testFundsTransferred() public { + IERC20 TORN = IERC20(tornTokenAddress); + + uint256 amountBeforeProposalExecution = TORN.balanceOf(developerAddress); + console2.log("Balance before execution: %s", amountBeforeProposalExecution / 1e18); + + createAndExecuteProposal(); + + uint256 amountAfterProposalExecution = TORN.balanceOf(developerAddress); + console2.log("Balance after execution: %s", amountAfterProposalExecution / 1e18); + + require(amountAfterProposalExecution - amountBeforeProposalExecution == 6226 * 1e18); + } +}