Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC677 support on foreign bridge in erc20-to-erc20 bridge mode #177

Merged
merged 9 commits into from
May 2, 2019
12 changes: 7 additions & 5 deletions contracts/upgradeable_contracts/ERC677Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pragma solidity 0.4.24;


import "./BasicBridge.sol";
import "../ERC677.sol";
import "../IBurnableMintableERC677Token.sol";

contract ERC677Bridge is BasicBridge {
Expand All @@ -15,16 +16,17 @@ contract ERC677Bridge is BasicBridge {
}

function onTokenTransfer(address _from, uint256 _value, bytes /*_data*/) external returns(bool) {
require(msg.sender == address(erc677token()));
IBurnableMintableERC677Token token = erc677token();
akolotov marked this conversation as resolved.
Show resolved Hide resolved
require(msg.sender == address(token));
require(withinLimit(_value));
setTotalSpentPerDay(getCurrentDay(), totalSpentPerDay(getCurrentDay()).add(_value));
erc677token().burn(_value);
fireEventOnTokenTransfer(_from, _value);
bridgeSpecificActionsOnTokenTransfer(token, _from, _value);
return true;
}

function fireEventOnTokenTransfer(address /*_from */, uint256 /* _value */) internal {
// has to be defined
function bridgeSpecificActionsOnTokenTransfer(IBurnableMintableERC677Token _token, address _from, uint256 _value) internal {
fireEventOnTokenTransfer(_from, _value);
}

function fireEventOnTokenTransfer(address _from, uint256 _value) internal;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pragma solidity 0.4.24;


import "./ERC677Bridge.sol";
import "../IBurnableMintableERC677Token.sol";


contract ERC677BridgeForBurnableMintableToken is ERC677Bridge {
function bridgeSpecificActionsOnTokenTransfer(IBurnableMintableERC677Token _token, address _from, uint256 _value) internal {
_token.burn(_value);
fireEventOnTokenTransfer(_from, _value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
pragma solidity 0.4.24;


import "../BasicBridge.sol";
import "../BasicForeignBridge.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol";


contract BasicForeignBridgeErcToErc is BasicBridge, BasicForeignBridge {
function _initialize(
address _validatorContract,
address _erc20token,
uint256 _requiredBlockConfirmations,
uint256 _gasPrice,
uint256 _maxPerTx,
uint256 _homeDailyLimit,
uint256 _homeMaxPerTx,
address _owner
) internal {
require(!isInitialized());
require(_validatorContract != address(0) && isContract(_validatorContract));
require(_requiredBlockConfirmations != 0);
require(_gasPrice > 0);
require(_homeMaxPerTx < _homeDailyLimit);
require(_owner != address(0));
addressStorage[keccak256(abi.encodePacked("validatorContract"))] = _validatorContract;
setErc20token(_erc20token);
uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))] = block.number;
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _requiredBlockConfirmations;
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _gasPrice;
uintStorage[keccak256(abi.encodePacked("maxPerTx"))] = _maxPerTx;
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _homeDailyLimit;
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _homeMaxPerTx;
setOwner(_owner);
setInitialize(true);
}

function getBridgeMode() public pure returns(bytes4 _data) {
return bytes4(keccak256(abi.encodePacked("erc-to-erc-core")));
}

function claimTokens(address _token, address _to) public onlyIfOwnerOfProxy {
require(_token != address(erc20token()));
super.claimTokens(_token, _to);
}

function onExecuteMessage(address _recipient, uint256 _amount) internal returns(bool){
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_amount));
return erc20token().transfer(_recipient, _amount);
}

function messageWithinLimits(uint256 _amount) internal view returns(bool) {
return withinExecutionLimit(_amount);
}

function onFailedMessage(address, uint256, bytes32) internal {
revert();
}

function erc20token() public view returns(ERC20Basic);

function setErc20token(address _token) internal;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
pragma solidity 0.4.24;


import "./BasicForeignBridgeErcToErc.sol";
import "../ERC677Bridge.sol";


contract ForeignBridgeErc677ToErc677 is ERC677Bridge, BasicForeignBridgeErcToErc {

event UserRequestForAffirmation(address recipient, uint256 value);

function initialize(
address _validatorContract,
address _erc20token,
uint256 _requiredBlockConfirmations,
uint256 _gasPrice,
uint256 _dailyLimit,
uint256 _maxPerTx,
uint256 _minPerTx,
uint256 _homeDailyLimit,
uint256 _homeMaxPerTx,
address _owner
) public returns(bool) {
require(_minPerTx > 0 && _maxPerTx > _minPerTx && _dailyLimit > _maxPerTx);

_initialize(
_validatorContract,
_erc20token,
_requiredBlockConfirmations,
_gasPrice,
_maxPerTx,
_homeDailyLimit,
_homeMaxPerTx,
_owner
);

uintStorage[keccak256(abi.encodePacked("dailyLimit"))] = _dailyLimit;
uintStorage[keccak256(abi.encodePacked("minPerTx"))] = _minPerTx;

return isInitialized();
}

function erc20token() public view returns(ERC20Basic) {
return erc677token();
}

function setErc20token(address _token) internal {
setErc677token(_token);
}

function fireEventOnTokenTransfer(address _from, uint256 _value) internal {
emit UserRequestForAffirmation(_from, _value);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
pragma solidity 0.4.24;
import "../../libraries/SafeMath.sol";
import "../../libraries/Message.sol";
import "../BasicBridge.sol";
import "../BasicForeignBridge.sol";
import "../../IBurnableMintableERC677Token.sol";
import "../../ERC677Receiver.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol";


contract ForeignBridgeErcToErc is BasicBridge, BasicForeignBridge {
import "./BasicForeignBridgeErcToErc.sol";

event RelayedMessage(address recipient, uint value, bytes32 transactionHash);

contract ForeignBridgeErcToErc is BasicForeignBridgeErcToErc {
function initialize(
address _validatorContract,
address _erc20token,
Expand All @@ -22,53 +15,25 @@ contract ForeignBridgeErcToErc is BasicBridge, BasicForeignBridge {
uint256 _homeMaxPerTx,
address _owner
) public returns(bool) {
require(!isInitialized());
require(_validatorContract != address(0) && isContract(_validatorContract));
require(_requiredBlockConfirmations != 0);
require(_gasPrice > 0);
require(_homeMaxPerTx < _homeDailyLimit);
require(_owner != address(0));
addressStorage[keccak256(abi.encodePacked("validatorContract"))] = _validatorContract;
setErc20token(_erc20token);
uintStorage[keccak256(abi.encodePacked("deployedAtBlock"))] = block.number;
uintStorage[keccak256(abi.encodePacked("requiredBlockConfirmations"))] = _requiredBlockConfirmations;
uintStorage[keccak256(abi.encodePacked("gasPrice"))] = _gasPrice;
uintStorage[keccak256(abi.encodePacked("maxPerTx"))] = _maxPerTx;
uintStorage[keccak256(abi.encodePacked("executionDailyLimit"))] = _homeDailyLimit;
uintStorage[keccak256(abi.encodePacked("executionMaxPerTx"))] = _homeMaxPerTx;
setOwner(_owner);
setInitialize(true);
_initialize(
_validatorContract,
_erc20token,
_requiredBlockConfirmations,
_gasPrice,
_maxPerTx,
_homeDailyLimit,
_homeMaxPerTx,
_owner
);
return isInitialized();
}

function getBridgeMode() public pure returns(bytes4 _data) {
return bytes4(keccak256(abi.encodePacked("erc-to-erc-core")));
}

function claimTokens(address _token, address _to) public onlyIfOwnerOfProxy {
require(_token != address(erc20token()));
super.claimTokens(_token, _to);
}

function erc20token() public view returns(ERC20Basic) {
return ERC20Basic(addressStorage[keccak256(abi.encodePacked("erc20token"))]);
}

function onExecuteMessage(address _recipient, uint256 _amount) internal returns(bool){
setTotalExecutedPerDay(getCurrentDay(), totalExecutedPerDay(getCurrentDay()).add(_amount));
return erc20token().transfer(_recipient, _amount);
}

function setErc20token(address _token) private {
function setErc20token(address _token) internal {
require(_token != address(0) && isContract(_token));
addressStorage[keccak256(abi.encodePacked("erc20token"))] = _token;
}

function messageWithinLimits(uint256 _amount) internal view returns(bool) {
return withinExecutionLimit(_amount);
}

function onFailedMessage(address, uint256, bytes32) internal {
revert();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import "../../upgradeability/EternalStorage.sol";
import "../../IBurnableMintableERC677Token.sol";
import "../../ERC677Receiver.sol";
import "../BasicHomeBridge.sol";
import "../ERC677Bridge.sol";
import "../OverdrawManagement.sol";
import "../ERC677BridgeForBurnableMintableToken.sol";


contract HomeBridgeErcToErc is ERC677Receiver, EternalStorage, BasicBridge, BasicHomeBridge, ERC677Bridge, OverdrawManagement {
contract HomeBridgeErcToErc is ERC677Receiver, EternalStorage, BasicBridge, BasicHomeBridge, ERC677BridgeForBurnableMintableToken, OverdrawManagement {

event AmountLimitExceeded(address recipient, uint256 value, bytes32 transactionHash);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import "../../IBurnableMintableERC677Token.sol";
import "../../ERC677Receiver.sol";
import "../BasicForeignBridge.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol";
import "../ERC677Bridge.sol";
import "../ERC677BridgeForBurnableMintableToken.sol";


contract ForeignBridgeNativeToErc is ERC677Receiver, BasicBridge, BasicForeignBridge, ERC677Bridge {
contract ForeignBridgeNativeToErc is ERC677Receiver, BasicBridge, BasicForeignBridge, ERC677BridgeForBurnableMintableToken {

/// Event created on money withdraw.
event UserRequestForAffirmation(address recipient, uint256 value);
Expand Down
2 changes: 2 additions & 0 deletions deploy/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=8
FOREIGN_GAS_PRICE=10000000000
#for bridge erc_to_erc and erc_to_native mode
ERC20_TOKEN_ADDRESS=
# Only for for erc_to_erc mode
ERC20_EXTENDED_BY_ERC677=false

REQUIRED_NUMBER_OF_VALIDATORS=1
#If several validators are used, list them separated by space without quotes
Expand Down
2 changes: 2 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ FOREIGN_GAS_PRICE=10000000000
# The address of the existing ERC20 compatible token in the Foreign network to
# be exchanged to the ERC20/ERC677 token deployed on Home.
ERC20_TOKEN_ADDRESS=0x
# Flag to specify that the existing ERC20 is also ERC677 compatible and want the Foreign bridge to use it as ERC677 to increase security.
ERC20_EXTENDED_BY_ERC677=false

# The minimum number of validators required to send their signatures confirming
# the relay of assets. The same number of validators is expected on both sides
Expand Down
3 changes: 2 additions & 1 deletion deploy/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ async function deployNativeToErc() {
}

async function deployErcToErc() {
const preDeploy = require('./src/erc_to_erc/preDeploy')
const deployHome = require('./src/erc_to_erc/home')
const deployForeign = require('./src/erc_to_erc/foreign')

await preDeploy()
const { homeBridge, erc677 } = await deployHome()
const { foreignBridge } = await deployForeign()
console.log('\nDeployment has been completed.\n\n')
Expand Down
53 changes: 39 additions & 14 deletions deploy/src/erc_to_erc/foreign.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const { web3Foreign, deploymentPrivateKey, FOREIGN_RPC_URL } = require('../web3'
const EternalStorageProxy = require('../../../build/contracts/EternalStorageProxy.json')
const BridgeValidators = require('../../../build/contracts/BridgeValidators.json')
const ForeignBridge = require('../../../build/contracts/ForeignBridgeErcToErc.json')
const ForeignBridgeErc677ToErc677 = require('../../../build/contracts/ForeignBridgeErc677ToErc677.json')

const VALIDATORS = env.VALIDATORS.split(' ')

Expand All @@ -22,7 +23,10 @@ const {
FOREIGN_GAS_PRICE,
FOREIGN_MAX_AMOUNT_PER_TX,
HOME_DAILY_LIMIT,
HOME_MAX_AMOUNT_PER_TX
HOME_MAX_AMOUNT_PER_TX,
FOREIGN_MIN_AMOUNT_PER_TX,
FOREIGN_DAILY_LIMIT,
ERC20_EXTENDED_BY_ERC677
} = env

const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
Expand Down Expand Up @@ -121,7 +125,8 @@ async function deployForeign() {
console.log('[Foreign] ForeignBridge Storage: ', foreignBridgeStorage.options.address)

console.log('\ndeploying foreignBridge implementation\n')
const foreignBridgeImplementation = await deployContract(ForeignBridge, [], {
const bridgeContract = ERC20_EXTENDED_BY_ERC677 ? ForeignBridgeErc677ToErc677 : ForeignBridge
const foreignBridgeImplementation = await deployContract(bridgeContract, [], {
from: DEPLOYMENT_ACCOUNT_ADDRESS,
network: 'foreign',
nonce: foreignNonce
Expand Down Expand Up @@ -154,18 +159,38 @@ async function deployForeign() {
console.log(`Foreign Validators: ${storageValidatorsForeign.options.address},
`)
foreignBridgeImplementation.options.address = foreignBridgeStorage.options.address
const initializeFBridgeData = await foreignBridgeImplementation.methods
.initialize(
storageValidatorsForeign.options.address,
ERC20_TOKEN_ADDRESS,
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS,
FOREIGN_GAS_PRICE,
FOREIGN_MAX_AMOUNT_PER_TX,
HOME_DAILY_LIMIT,
HOME_MAX_AMOUNT_PER_TX,
FOREIGN_BRIDGE_OWNER
)
.encodeABI({ from: DEPLOYMENT_ACCOUNT_ADDRESS })
let initializeFBridgeData

if (ERC20_EXTENDED_BY_ERC677) {
initializeFBridgeData = await foreignBridgeImplementation.methods
.initialize(
storageValidatorsForeign.options.address,
ERC20_TOKEN_ADDRESS,
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS,
FOREIGN_GAS_PRICE,
FOREIGN_DAILY_LIMIT,
FOREIGN_MAX_AMOUNT_PER_TX,
FOREIGN_MIN_AMOUNT_PER_TX,
HOME_DAILY_LIMIT,
HOME_MAX_AMOUNT_PER_TX,
FOREIGN_BRIDGE_OWNER
)
.encodeABI()
} else {
initializeFBridgeData = await foreignBridgeImplementation.methods
.initialize(
storageValidatorsForeign.options.address,
ERC20_TOKEN_ADDRESS,
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS,
FOREIGN_GAS_PRICE,
FOREIGN_MAX_AMOUNT_PER_TX,
HOME_DAILY_LIMIT,
HOME_MAX_AMOUNT_PER_TX,
FOREIGN_BRIDGE_OWNER
)
.encodeABI()
}

const txInitializeBridge = await sendRawTxForeign({
data: initializeFBridgeData,
nonce: foreignNonce,
Expand Down
Loading