Skip to content

Commit

Permalink
Merge 7688281 into adf9993
Browse files Browse the repository at this point in the history
  • Loading branch information
k1rill-fedoseev committed Apr 8, 2020
2 parents adf9993 + 7688281 commit 0ce7224
Show file tree
Hide file tree
Showing 19 changed files with 1,461 additions and 5 deletions.
5 changes: 5 additions & 0 deletions contracts/interfaces/IMintHandler.sol
@@ -0,0 +1,5 @@
pragma solidity 0.4.24;

interface IMintHandler {
function mint(address _to, uint256 _amount) external returns (bool);
}
28 changes: 28 additions & 0 deletions contracts/mocks/MintHandlerMock.sol
@@ -0,0 +1,28 @@
pragma solidity 0.4.24;

import "../interfaces/IMintHandler.sol";
import "../interfaces/IBurnableMintableERC677Token.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract MintHandlerMock is IMintHandler, Ownable {
IBurnableMintableERC677Token public token;

mapping(address => bool) public isBridge;

constructor(address _token) public {
token = IBurnableMintableERC677Token(_token);
}

function addBridge(address _bridge) external onlyOwner {
isBridge[_bridge] = true;
}

function removeBridge(address _bridge) external onlyOwner {
delete isBridge[_bridge];
}

function mint(address _to, uint256 _amount) external returns (bool) {
require(isBridge[msg.sender]);
return token.mint(_to, _amount);
}
}
Expand Up @@ -38,7 +38,7 @@ contract BasicAMBErc677ToErc677 is
uint256 _requestGasLimit,
uint256 _decimalShift,
address _owner
) external onlyRelevantSender returns (bool) {
) public onlyRelevantSender returns (bool) {
require(!isInitialized());
require(
_dailyLimitMaxPerTxMinPerTxArray[2] > 0 && // _minPerTx > 0
Expand Down
@@ -0,0 +1,13 @@
pragma solidity 0.4.24;

import "./BasicAMBErc677ToErc677.sol";

contract BasicStakeTokenMediator is BasicAMBErc677ToErc677 {
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
return (1, 0, 0);
}

function getBridgeMode() external pure returns (bytes4 _data) {
return 0x16ea01e9; // bytes4(keccak256(abi.encodePacked("stake-erc-to-erc-amb")))
}
}
@@ -0,0 +1,60 @@
pragma solidity 0.4.24;

import "./BasicStakeTokenMediator.sol";
import "../../interfaces/IBurnableMintableERC677Token.sol";

contract ForeignStakeTokenMediator is BasicStakeTokenMediator {
/**
* @dev Executes action on the request to withdraw tokens relayed from the other network
* @param _recipient address of tokens receiver
* @param _value amount of bridged tokens
*/
function executeActionOnBridgedTokens(address _recipient, uint256 _value) internal {
uint256 value = _value.div(10**decimalShift());
_transferWithOptionalMint(_recipient, value);
}

/**
* @dev Executes action on deposit of bridged tokens
* @param _from address of tokens sender
* @param _value requsted amount of bridged tokens
* @param _data alternative receiver, if specified
*/
function bridgeSpecificActionsOnTokenTransfer(
ERC677, /* _token */
address _from,
uint256 _value,
bytes _data
) internal {
if (!lock()) {
passMessage(chooseReceiver(_from, _data), _value);
}
}

/**
* @dev Executes action on relayed request to fix the failed transfer of tokens
* @param _recipient address of tokens receiver
* @param _value amount of fixed tokens
*/
function executeActionOnFixedTokens(address _recipient, uint256 _value) internal {
_transferWithOptionalMint(_recipient, _value);
}

/**
* @dev Internal function for transfer of tokens, with optional minting if current balance is insufficient
* @param _recipient address of tokens receiver
* @param _value amount of fixed tokens
*/
function _transferWithOptionalMint(address _recipient, uint256 _value) internal {
IBurnableMintableERC677Token token = IBurnableMintableERC677Token(erc677token());
uint256 balance = token.balanceOf(address(this));
if (_recipient != address(0) && balance == 0) {
token.mint(_recipient, _value);
} else if (balance < _value) {
token.mint(address(this), _value - balance);
token.transfer(_recipient, _value);
} else {
token.transfer(_recipient, _value);
}
}
}
@@ -0,0 +1,64 @@
pragma solidity 0.4.24;

import "../BlockRewardBridge.sol";
import "../Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

contract HomeStakeTokenFeeManager is BlockRewardBridge, Ownable {
using SafeMath for uint256;

event FeeUpdated(uint256 fee);

uint256 internal constant MAX_FEE = 1 ether;
bytes32 internal constant FEE = 0x241773621b963145d8e249ca69b0240df7de56fca52fb3ec9e2ddd08a968570e; // keccak256(abi.encodePacked("stakeTokenFee"));

/**
* @dev Retrieves currently used block reward contract
* @return configured block reward contract address
*/
function blockRewardContract() external view returns (IBlockReward) {
return _blockRewardContract();
}

/**
* @dev Updates address of currently used block reward contract
* @param _blockReward address of a new contract
*/
function setBlockRewardContract(address _blockReward) external onlyOwner {
_setBlockRewardContract(_blockReward);
}

/**
* @dev Retrieves current fee value
* @return current value of fee, 1e18 is 100%
*/
function getFee() public view returns (uint256) {
return uintStorage[FEE];
}

/**
* @dev Calculates the fee amount to be subtracted from the value.
* @param _value the base value from which fees are calculated
*/
function calculateFee(uint256 _value) public view returns (uint256) {
return _value.mul(getFee()).div(MAX_FEE);
}

/**
* @dev Sets the fee percentage amount for the mediator operations. Only the owner can call this method.
* @param _fee the fee percentage
*/
function setFee(uint256 _fee) external onlyOwner {
_setFee(_fee);
}

/**
* @dev Internal setter for fee
* @param _fee the fee percentage
*/
function _setFee(uint256 _fee) internal {
require(_fee < MAX_FEE);
uintStorage[FEE] = _fee;
emit FeeUpdated(_fee);
}
}
@@ -0,0 +1,160 @@
pragma solidity 0.4.24;

import "../../interfaces/IMintHandler.sol";
import "./BasicStakeTokenMediator.sol";
import "../BlockRewardBridge.sol";
import "./HomeStakeTokenFeeManager.sol";
import "../../interfaces/IBurnableMintableERC677Token.sol";

contract HomeStakeTokenMediator is BasicStakeTokenMediator, HomeStakeTokenFeeManager {
bytes32 internal constant MINT_HANDLER = 0x8a8236f871f2bbb44f59e8c68b82f7587d19c987e09aba39148cc97ea004a32e; // keccak256(abi.encodePacked("mintHandler"))

/**
* Initializes home mediator
* @param _bridgeContract HomeAMB bridge contract
* @param _mediatorContract address of the mediator contract in the Foreign chain
* @param _erc677token address of STAKE token in the Home chain
* @param _dailyLimitMaxPerTxMinPerTxArray Home limits for outgoing transfers
* @param _executionDailyLimitExecutionMaxPerTxArray Home execution limits for incoming transfers
* @param _requestGasLimit gas limit used for AMB operations
* @param _decimalShift decimal shift for bridged TAKE token
* @param _owner address of new bridge owner
*/
function initialize(
address _bridgeContract,
address _mediatorContract,
address _erc677token,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256[] _executionDailyLimitExecutionMaxPerTxArray, // [ 0 = _executionDailyLimit, 1 = _executionMaxPerTx ]
uint256 _requestGasLimit,
uint256 _decimalShift,
address _owner
) public returns (bool) {
addressStorage[MINT_HANDLER] = _erc677token;
return
super.initialize(
_bridgeContract,
_mediatorContract,
_erc677token,
_dailyLimitMaxPerTxMinPerTxArray,
_executionDailyLimitExecutionMaxPerTxArray,
_requestGasLimit,
_decimalShift,
_owner
);
}

/**
* Initializes rewardable home mediator
* @param _bridgeContract HomeAMB bridge contract
* @param _mediatorContract address of the mediator contract in the Foreign chain
* @param _erc677token address of STAKE token in the Home chain
* @param _dailyLimitMaxPerTxMinPerTxArray Home limits for outgoing transfers
* @param _executionDailyLimitExecutionMaxPerTxArray Home execution limits for incoming transfers
* @param _requestGasLimit gas limit used for AMB operations
* @param _decimalShift decimal shift for bridged TAKE token
* @param _owner address of new bridge owner
* @param _blockReward address of block reward contract used for fee distribution
* @param _fee initial home fee
*/
function rewardableInitialize(
address _bridgeContract,
address _mediatorContract,
address _erc677token,
uint256[] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
uint256[] _executionDailyLimitExecutionMaxPerTxArray, // [ 0 = _executionDailyLimit, 1 = _executionMaxPerTx ]
uint256 _requestGasLimit,
uint256 _decimalShift,
address _owner,
address _blockReward,
uint256 _fee
) external returns (bool) {
_setFee(_fee);
_setBlockRewardContract(_blockReward);
return
initialize(
_bridgeContract,
_mediatorContract,
_erc677token,
_dailyLimitMaxPerTxMinPerTxArray,
_executionDailyLimitExecutionMaxPerTxArray,
_requestGasLimit,
_decimalShift,
_owner
);
}

/**
* @dev Allows to transfer token ownership to different proxy contract.
* Can be called only once, when mediator is the current owner of a token.
* All subsequent calls to erc677 token will be done through new proxy contract.
* @param _owner token proxy contract address
*/
function transferTokenOwnership(address _owner) external onlyOwner {
Ownable(erc677token()).transferOwnership(_owner);
}

/**
* @dev Updates address of contract used for handling mint operations,
* all subsequent mint operations will be called through this contract
* @param _mintHandler address of new contract
*/
function setMintHandler(address _mintHandler) external onlyOwner {
require(AddressUtils.isContract(_mintHandler));
addressStorage[MINT_HANDLER] = _mintHandler;
}

/**
* @dev Retrieves currently used contract for handling mint operations, defaults to token itself
* @return address of mint handler contract
*/
function getMintHandler() public view returns (IMintHandler) {
return IMintHandler(addressStorage[MINT_HANDLER]);
}

/**
* @dev Executes action on the request to deposit tokens relayed from the other network
* @param _recipient address of tokens receiver
* @param _value amount of bridged tokens
*/
function executeActionOnBridgedTokens(address _recipient, uint256 _value) internal {
uint256 value = _value.mul(10**decimalShift());
getMintHandler().mint(_recipient, value);
}

/**
* @dev Executes action on withdrawal of bridged tokens
* @param _token address of token contract
* @param _from address of tokens sender
* @param _value requsted amount of bridged tokens
* @param _data alternative receiver, if specified
*/
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal {
if (!lock()) {
// burn all incoming tokens
IBurnableMintableERC677Token(_token).burn(_value);

if (address(_blockRewardContract()) == address(0)) {
// in case if block reward contract is not configured, the fee is not collected
passMessage(chooseReceiver(_from, _data), _value);
} else {
// when block reward contract is defined, the calculated fee is subtracted from the original value
uint256 fee = calculateFee(_value);
passMessage(chooseReceiver(_from, _data), _value.sub(fee));
if (fee > 0) {
// the fee itself is distributed later in the block reward contract
_blockRewardContract().addBridgeTokenRewardReceivers(fee);
}
}
}
}

/**
* @dev Executes action on relayed request to fix the failed transfer of tokens
* @param _recipient address of tokens receiver
* @param _value amount of fixed tokens
*/
function executeActionOnFixedTokens(address _recipient, uint256 _value) internal {
getMintHandler().mint(_recipient, _value);
}
}
4 changes: 4 additions & 0 deletions deploy/.env.example
Expand Up @@ -90,3 +90,7 @@ FOREIGN_EXPLORER_API_KEY=
DEPLOY_INTEREST_RECEIVER=false
# if DEPLOY_INTEREST_RECEIVER set to true, address of interest receiver contract owner should be specified
FOREIGN_INTEREST_RECEIVER_OWNER=0x

# for stake token mediators
HOME_STAKE_TOKEN_ADDRESS=0x
FOREIGN_STAKE_TOKEN_ADDRESS=0x

0 comments on commit 0ce7224

Please sign in to comment.