DFO-Based Farming Extension Contract
This is another example of a custom farming extension based on a DFO. This contract makes use of the manageFarming microservice discussed here to receive and send back reward tokens (through the two mandatory methods transferTo and backToYou we discussed here), and its address must be approved by a DFO proposal in order to do this. The init method body has been rewritten in order to pass the DFO doubleProxy as host, and to inhibit the treasury address (which is of course the DFO Wallet itself). The hostOnly() modifier has been edited too, in order to accept calls only by the DFO authorized Microservices.
This is a scratch. The entire code template can be found here.
1
contract DFOBasedFarmExtension is IFarmExtension {
2
3
string private constant FUNCTIONALITY_NAME = "manageFarming";
4
5
// wallet who has control on the extension
6
address internal _doubleProxy;
7
8
// mapping that contains all the farming contract linked to this extension
9
address internal _farmingContract;
10
11
// the reward token address linked to this farming contract
12
address internal _rewardTokenAddress;
13
14
// whether the token is by mint or by reserve
15
bool internal _byMint;
16
17
/** MODIFIERS */
18
19
/** @dev farmingOnly modifier used to check for unauthorized transfers. */
20
modifier farmingOnly() {
21
require(msg.sender == _farmingContract, "Unauthorized");
22
_;
23
}
24
25
/** @dev hostOnly modifier used to check for unauthorized edits. */
26
modifier hostOnly() {
27
require(_isFromDFO(msg.sender), "Unauthorized");
28
_;
29
}
30
31
/** PUBLIC METHODS */
32
33
function init(bool byMint, address host, address) public virtual override {
34
require(_farmingContract == address(0), "Already init");
35
require((_doubleProxy = host) != address(0), "blank host");
36
_rewardTokenAddress = IFarmMain(_farmingContract = msg.sender)._rewardTokenAddress();
37
_byMint = byMint;
38
}
39
40
/** @dev allows the DFO to update the double proxy address.
41
* @param newDoubleProxy new double proxy address.
42
*/
43
function setHost(address newDoubleProxy) public virtual override hostOnly {
44
_doubleProxy = newDoubleProxy;
45
}
46
47
/** @dev method used to update the extension treasury.
48
*/
49
function setTreasury(address) public virtual override hostOnly {
50
revert("Impossibru!");
51
}
52
53
function data() view public virtual override returns(address farmingContract, bool byMint, address host, address treasury, address rewardTokenAddress) {
54
return (_farmingContract, _byMint, _doubleProxy, IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).getMVDWalletAddress(), _rewardTokenAddress);
55
}
56
57
/** @dev transfers the input amount to the caller farming contract.
58
* @param amount amount of erc20 to transfer or mint.
59
*/
60
function transferTo(uint256 amount) override public farmingOnly {
61
IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).submit(FUNCTIONALITY_NAME, abi.encode(address(0), 0, true, _rewardTokenAddress, _farmingContract, amount, _byMint));
62
}
63
64
/** @dev transfers the input amount from the caller farming contract to the extension.
65
* @param amount amount of erc20 to transfer back or burn.
66
*/
67
function backToYou(uint256 amount) override payable public farmingOnly {
68
if(_rewardTokenAddress != address(0)) {
69
_safeTransferFrom(_rewardTokenAddress, msg.sender, address(this), amount);
70
_safeApprove(_rewardTokenAddress, _getFunctionalityAddress(), amount);
71
IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).submit(FUNCTIONALITY_NAME, abi.encode(address(0), 0, false, _rewardTokenAddress, msg.sender, amount, _byMint));
72
} else {
73
IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).submit{value : amount}(FUNCTIONALITY_NAME, abi.encode(address(0), 0, false, _rewardTokenAddress, msg.sender, amount, _byMint));
74
}
75
}
76
77
/** @dev this function calls the farming contract with the given address and sets the given farming setups.
78
* @param farmingSetups array containing all the farming setups.
79
*/
80
function setFarmingSetups(FarmingSetupConfiguration[] memory farmingSetups) public override hostOnly {
81
IFarmMain(_farmingContract).setFarmingSetups(farmingSetups);
82
}
83
84
/** PRIVATE METHODS */
85
86
/** @dev this function returns the address of the functionality with the FUNCTIONALITY_NAME.
87
* @return functionalityAddress functionality FUNCTIONALITY_NAME address.
88
*/
89
function _getFunctionalityAddress() private view returns(address functionalityAddress) {
90
(functionalityAddress,,,,) = IMVDFunctionalitiesManager(IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).getMVDFunctionalitiesManagerAddress()).getFunctionalityData(FUNCTIONALITY_NAME);
91
}
92
93
/** @dev this function returns the address of the wallet of the linked DFO.
94
* @return linked DFO wallet address.
95
*/
96
function _getDFOWallet() private view returns(address) {
97
return IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).getMVDWalletAddress();
98
}
99
100
/** @dev this function returns true if the sender is an authorized DFO functionality, false otherwise.
101
* @param sender address of the caller.
102
* @return true if the call is from a DFO, false otherwise.
103
*/
104
function _isFromDFO(address sender) private view returns(bool) {
105
return IMVDFunctionalitiesManager(IMVDProxy(IDoubleProxy(_doubleProxy).proxy()).getMVDFunctionalitiesManagerAddress()).isAuthorizedFunctionality(sender);
106
}
107
108
/** @dev function used to safely approve ERC20 transfers.
109
* @param erc20TokenAddress address of the token to approve.
110
* @param to receiver of the approval.
111
* @param value amount to approve for.
112
*/
113
function _safeApprove(address erc20TokenAddress, address to, uint256 value) internal virtual {
114
bytes memory returnData = _call(erc20TokenAddress, abi.encodeWithSelector(IERC20(erc20TokenAddress).approve.selector, to, value));
115
require(returnData.length == 0 || abi.decode(returnData, (bool)), 'APPROVE_FAILED');
116
}
117
118
/** @dev this function safely transfers the given ERC20 value from an address to another.
119
* @param erc20TokenAddress erc20 token address.
120
* @param from address from.
121
* @param to address to.
122
* @param value amount to transfer.
123
*/
124
function _safeTransferFrom(address erc20TokenAddress, address from, address to, uint256 value) private {
125
bytes memory returnData = _call(erc20TokenAddress, abi.encodeWithSelector(IERC20(erc20TokenAddress).transferFrom.selector, from, to, value));
126
require(returnData.length == 0 || abi.decode(returnData, (bool)), 'TRANSFERFROM_FAILED');
127
}
128
129
function _call(address location, bytes memory payload) private returns(bytes memory returnData) {
130
assembly {
131
let result := call(gas(), location, 0, add(payload, 0x20), mload(payload), 0, 0)
132
let size := returndatasize()
133
returnData := mload(0x40)
134
mstore(returnData, size)
135
let returnDataPayloadStart := add(returnData, 0x20)
136
returndatacopy(returnDataPayloadStart, 0, size)
137
mstore(0x40, add(returnDataPayloadStart, size))
138
switch result case 0 {revert(returnDataPayloadStart, size)}
139
}
140
}
141
}
Copied!
Copy link