Last active
April 5, 2022 20:02
-
-
Save ngmachado/90a2156f7d923d0ee5092528a55646ea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.13; | |
/* | |
Exemple of upgrader supertoken based on roles. | |
NOTICE: IS ONE EXAMPLE NOT PROD READY | |
*/ | |
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
import { IERC777 } from "@openzeppelin/contracts/token/ERC777/IERC777.sol"; | |
import { IERC1820Registry } from "@openzeppelin/contracts/utils/introspection/IERC1820Registry.sol"; | |
import { IERC777Recipient } from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; | |
import {ISuperToken} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; | |
contract Upgrader is AccessControlEnumerable, IERC777Recipient { | |
using SafeERC20 for IERC20; | |
IERC1820Registry constant internal _ERC1820_REGISTRY = | |
IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); | |
// role identifier for upgrader caller | |
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADE_CALLER"); | |
//If | |
mapping(ISuperToken => bool) public supportedSuperTokens; | |
bool private lock; | |
constructor(address default_admin_role, address[] memory upgraders) { | |
_ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777TokensRecipient"), address(this)); | |
_setupRole(DEFAULT_ADMIN_ROLE, default_admin_role); | |
for (uint256 i = 0; i < upgraders.length; ++i) { | |
require(upgraders[i] != address(0), "Upgrader: upgrader can't be zero"); | |
_setupRole(UPGRADER_ROLE, upgraders[i]); | |
} | |
} | |
/** | |
* @notice The user should ERC20.approve this contract. | |
* @notice SuperToken must be whitelisted before calling this function | |
* @notice User can upgrade self balance | |
* @dev Execute upgrade function in the name of the user. | |
* @param superToken Super Token to upgrade | |
* @param account User address that previous approved this contract. | |
* @param amount Amount value to be upgraded. | |
*/ | |
function upgrade( | |
ISuperToken superToken, | |
address account, | |
uint256 amount | |
) | |
external | |
{ | |
require(msg.sender == account || | |
(hasRole(UPGRADER_ROLE, msg.sender) && | |
supportedSuperTokens[superToken] | |
) | |
, "Upgrader: operation not allowed"); | |
// get underlying token | |
IERC20 token = IERC20(superToken.getUnderlyingToken()); | |
uint256 beforeBalance = superToken.balanceOf(address(this)); | |
// get tokens from user | |
token.safeTransferFrom(account, address(this), amount); | |
//reset approve amount | |
token.safeApprove(address(superToken), 0); | |
token.safeApprove(address(superToken), amount); | |
// upgrade tokens and send back to user | |
superToken.upgrade(amount); | |
//Depends on the decimals of underlying token itself. We send the diff | |
superToken.transfer(account, superToken.balanceOf(address(this)) - beforeBalance); | |
} | |
/** | |
* @notice The user should ERC20.approve this contract. | |
* @notice SuperToken must be whitelisted before calling this function | |
* @notice User can downgrade self balance | |
* @dev Execute upgrade function in the name of the user. | |
* @param superToken Super Token to upgrade | |
* @param account User address that previous approved this contract. | |
* @param amount Amount value to be downgraded (in SuperToken decimals). | |
*/ | |
function downgrade( | |
ISuperToken superToken, | |
address account, | |
uint256 amount | |
) | |
external | |
inTx() | |
{ | |
require(msg.sender == account || | |
(hasRole(UPGRADER_ROLE, msg.sender) && | |
supportedSuperTokens[superToken] | |
) | |
, "Upgrader: operation not allowed"); | |
// get underlying token | |
IERC20 token = IERC20(superToken.getUnderlyingToken()); | |
uint256 beforeBalance = token.balanceOf(address(this)); | |
superToken.transferFrom(account, address(this), amount); | |
// upgrade tokens and send back to user | |
superToken.downgrade(amount); | |
//Depends on the decimals of underlying token itself. We send the diff | |
token.safeTransfer(account, token.balanceOf(address(this)) - beforeBalance); | |
} | |
/** | |
* MANAGE SUPERTOKENS | |
*/ | |
function addSuperToken(ISuperToken superToken) external onlyRole(DEFAULT_ADMIN_ROLE) { | |
require(superToken.getUnderlyingToken() != address(0), "Upgrader: SuperToken without underlying token"); | |
supportedSuperTokens[superToken] = true; | |
} | |
function RemoveSuperToken(ISuperToken superToken) external onlyRole(DEFAULT_ADMIN_ROLE) { | |
delete supportedSuperTokens[superToken]; | |
} | |
/** | |
* ACCESS CONTROL | |
*/ | |
/** | |
* @dev Allows admin to add address to upgrader role | |
* @param upgrader address | |
*/ | |
function addUpgrader(address upgrader) external { | |
require(upgrader != address(0), "Upgrader: operation not allowed"); | |
grantRole(UPGRADER_ROLE, upgrader); | |
} | |
/** | |
* @dev Allows admin to remove address from upgrader role | |
* @param upgrader address | |
*/ | |
function revokeUpgrader(address upgrader) external { | |
revokeRole(UPGRADER_ROLE, upgrader); | |
} | |
/** | |
* ERC777 RECEIVER CALLBACK | |
*/ | |
function tokensReceived( | |
address /*operator*/, | |
address /*from*/, | |
address /*to*/, | |
uint256 /*amount*/, | |
bytes calldata /*userData*/, | |
bytes calldata /*operatorData*/ | |
) override external { | |
require(lock, "Upgrader: use downgrade function"); | |
} | |
modifier inTx() { | |
lock = true; | |
_; | |
lock = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment