Skip to content

Instantly share code, notes, and snippets.

@ngmachado
Last active April 5, 2022 20:02
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 ngmachado/90a2156f7d923d0ee5092528a55646ea to your computer and use it in GitHub Desktop.
Save ngmachado/90a2156f7d923d0ee5092528a55646ea to your computer and use it in GitHub Desktop.
// 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