Sending Reward Tokens to the Extension

The host of a contract sends the reward tokens for all the created setups to the extension. The extension must receive, at a minimum, enough tokens to reward every setup created for the contract. If it doesn't, the setups will still be correctly created within the contract, but won't be able to be activated.

If any reward tokens are from a reserve, it is best practice to deploy the contract and the extension and then immediately send to the extension the minimum amount of reward tokens required by all setups.

If the contract host is a wallet, simply send the required amount of reward tokens to the extension address manually via a transfer function (or using a provider such as Metamask).

If the contract host is a DFO, DAO, or other type of organization, you must use the appropriate procedure to transfer the tokens to the extension. For example, if the host is a DFO, make a proposal to install in it the manageFarming microservice (only required the first time a contract is hosted by the DFO) and then enable the extension in the DFO StateHolder (required every time). This will authorize the DFO to send the required amount of reward tokens from the DFO's treasury to be sent to the contract's extension:

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma abicoder v2;

contract ProposalCode {

    string private _metadataLink;

    constructor(string memory metadataLink) {
        _metadataLink = metadataLink;
    }

    function getMetadataLink() public view returns(string memory) {
        return _metadataLink;
    }

    function onStart(address, address) public {
        IMVDProxy proxy = IMVDProxy(msg.sender);
        IStateHolder stateHolder = IStateHolder(proxy.getStateHolderAddress());
        stateHolder.setBool(_toStateHolderKey("farming.authorized", _toString(/*extension_address*/)), true);
    }

    function onStop(address) public {
    }

    function manageFarming(address sender, uint256, bool transfer, address erc20TokenAddress, address to, uint256 value, bool byMint) public {
        IMVDProxy proxy = IMVDProxy(msg.sender);
        IStateHolder stateHolder = IStateHolder(proxy.getStateHolderAddress());
        require(stateHolder.getBool(_toStateHolderKey("farming.authorized", _toString(sender))), "Unauthorized action");
        IERC20 token = IERC20(erc20TokenAddress);
        if(transfer) {
            if(byMint) {
                uint256 lastAmount = token.balanceOf(msg.sender);
                token.mint(value);
                proxy.flushToWallet(erc20TokenAddress, false, 0);
                if(lastAmount > 0) {
                    proxy.transfer(msg.sender, lastAmount, address(token));
                }
            }
            proxy.transfer(to, value, erc20TokenAddress);
        } else {
            if(erc20TokenAddress == address(0)) {
                return;
            }
            token.transferFrom(sender, byMint ? address(this) : proxy.getMVDWalletAddress(), value);
            if(byMint) {
                token.burn(value);
            }
        }
    }

    function _toStateHolderKey(string memory a, string memory b) private pure returns(string memory) {
        return _toLowerCase(string(abi.encodePacked(a, ".", b)));
    }

    function _toString(address _addr) private pure returns(string memory) {
        bytes32 value = bytes32(uint256(_addr));
        bytes memory alphabet = "0123456789abcdef";

        bytes memory str = new bytes(42);
        str[0] = '0';
        str[1] = 'x';
        for (uint i = 0; i < 20; i++) {
            str[2+i*2] = alphabet[uint(uint8(value[i + 12] >> 4))];
            str[3+i*2] = alphabet[uint(uint8(value[i + 12] & 0x0f))];
        }
        return string(str);
    }

    function _toLowerCase(string memory str) private pure returns(string memory) {
        bytes memory bStr = bytes(str);
        for (uint i = 0; i < bStr.length; i++) {
            bStr[i] = bStr[i] >= 0x41 && bStr[i] <= 0x5A ? bytes1(uint8(bStr[i]) + 0x20) : bStr[i];
        }
        return string(bStr);
    }
}

interface IMVDProxy {
    function getStateHolderAddress() external view returns(address);
    function getMVDWalletAddress() external view returns(address);
    function transfer(address receiver, uint256 value, address token) external;
    function flushToWallet(address tokenAddress, bool is721, uint256 tokenId) external;
}

interface IStateHolder {
    function setUint256(string calldata name, uint256 value) external returns(uint256);
    function getUint256(string calldata name) external view returns(uint256);
    function getAddress(string calldata name) external view returns(address);
    function setAddress(string calldata varName, address val) external returns (address);
    function getBool(string calldata varName) external view returns (bool);
    function setBool(string calldata varName, bool val) external returns(bool);
    function clear(string calldata varName) external returns(string memory oldDataType, bytes memory oldVal);
}

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function safeApprove(address spender, uint256 amount) external;
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function decimals() external view returns (uint8);
    function mint(uint256 amount) external;
    function burn(uint256 amount) external;
}

After the extension has received the tokens, the host does not need to also then manually transfer them to the farming contract. This will happen automatically when the setup(s) are activated.

See the Activate/Deactivate section for more details.

Last updated