Skip to content

Instantly share code, notes, and snippets.

@luisivan
Last active May 29, 2019 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luisivan/87389ccf51a693d73e89630d529b3e8c to your computer and use it in GitHub Desktop.
Save luisivan/87389ccf51a693d73e89630d529b3e8c to your computer and use it in GitHub Desktop.
Aragon wrapper for ERC20 tokens
/* Based on https://github.com/MyBitFoundation/MyBit-DAO.tech/blob/master/apps/MyTokens/contracts/MyTokens.sol */
/* solium-disable function-order */
pragma solidity 0.4.24;
import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/common/IForwarder.sol";
import "@aragon/os/contracts/lib/token/ERC20.sol";
import "@aragon/os/contracts/lib/math/SafeMath.sol";
import "@aragon/apps-shared-minime/contracts/ITokenController.sol";
import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol";
contract AragonERC20Wrapper is ITokenController, IForwarder, AragonApp {
using SafeMath for uint256;
event TokensLocked(address entity, uint256 amount);
event TokensUnlocked(address entity, uint256 amount);
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
string private constant ERROR_TOKEN_CONTROLLER = "TM_TOKEN_CONTROLLER";
string private constant ERROR_CAN_NOT_FORWARD = "TM_CAN_NOT_FORWARD";
string private constant ERROR_PROXY_PAYMENT_WRONG_SENDER = "TM_PROXY_PAYMENT_WRONG_SENDER";
MiniMeToken public token;
ERC20 public erc20;
mapping(address => uint256) private lockAmount;
/**
* @notice Initialize a new MiniMe token that will be the controller of an ERC20 token
* @param _token The MiniMe token that is used in the DAO
* @param _erc20 The ERC20 token that is locked in order to receive MiniMe tokens
*/
function initialize(
MiniMeToken _token,
address _erc20
)
external
{
initialized();
require(MiniMeToken(_token).controller() == address(this), ERROR_TOKEN_CONTROLLER);
token = _token;
token.enableTransfers(false);
erc20 = ERC20(_erc20);
}
/**
* @notice Lock `_amount` tokens
*/
function lock(uint256 _amount) external {
require(amount > 0, 'Token amount is 0');
require(erc20.transferFrom(msg.sender, address(this), _amount), 'ERC20 not sent');
require(token.generateTokens(msg.sender, _amount), 'Tokens not generated');
lockAmount[msg.sender] = lockAmount[msg.sender].add(_amount);
emit TokensLocked(msg.sender, _amount);
}
/**
* @notice Unlock `_amount` tokens
*/
function unlock(uint256 _amount) external {
require(amount > 0, 'Token amount is 0');
require(amount <= lockAmount[msg.sender], 'Token amount to unlock is larger than amount of tokens locked');
require(token.destroyTokens(msg.sender, token.balanceOf(msg.sender).sub(_amount)), 'Tokens not destroyed');
require(erc20.transfer(msg.sender, _amount), 'ERC20 not returned');
lockAmount[msg.sender] = lockAmount[msg.sender].sub(_amount);
if (lockAmount[msg.sender] == 0) {
delete lockAmount[msg.sender];
}
emit TokensUnlocked(msg.sender, _amount);
}
/**
* @notice Give control of the MiniMe token over to `_newController`
* @param _newController Ethereum address of contract that will control the token
*/
function changeTokenController(address _newController) external auth(MANAGER_ROLE) {
token.changeController(_newController);
}
/**
* @notice Execute desired action as a token holder
* @dev IForwarder interface conformance. Forwards any token holder action.
* @param _evmScript Script being executed
*/
function forward(bytes _evmScript) public {
require(canForward(msg.sender, _evmScript), ERROR_CAN_NOT_FORWARD);
bytes memory input = new bytes(0); // TODO: Consider input for this
// Add the managed token to the blacklist to disallow a token holder from executing actions
// on the token controller's (this contract) behalf
address[] memory blacklist = new address[](2);
blacklist[0] = address(token);
blacklist[1] = address(erc20);
runScript(_evmScript, input, blacklist);
}
function isForwarder() public pure returns (bool) {
return true;
}
function canForward(address _sender, bytes) public view returns (bool) {
return hasInitialized() && token.balanceOf(_sender) > 0;
}
/*
* @dev Notifies the controller about a token transfer allowing the controller to decide whether to allow it or react if desired (only callable from the token)
* @param _from The origin of the transfer
* @param _to The destination of the transfer
* @param _amount The amount of the transfer
* @return False if the controller does not authorize the transfer
*/
function onTransfer(address _from, address _to, uint _amount) public isInitialized returns (bool) {
return false;
}
/**
* @notice Called when ether is sent to the MiniMe Token contract
* @return True if the ether is accepted, false for it to throw
*/
function proxyPayment(address) public payable returns (bool) {
// Sender check is required to avoid anyone sending ETH to the Token Manager through this method
// Even though it is tested, solidity-coverage doesnt get it because
// MiniMeToken is not instrumented and entire tx is reverted
require(msg.sender == address(token), ERROR_PROXY_PAYMENT_WRONG_SENDER);
return false;
}
/**
* @dev Notifies the controller about an approval allowing the controller to react if desired
* @return False if the controller does not authorize the approval
*/
function onApprove(address, address, uint) public returns (bool) {
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment