Omnibridge usage of manual/oracle-driven lanes (#547)
This commit is contained in:
parent
1748f94757
commit
09d228fa72
|
@ -42,6 +42,14 @@ contract AMBMock {
|
|||
}
|
||||
|
||||
function requireToPassMessage(address _contract, bytes _data, uint256 _gas) external returns (bytes32) {
|
||||
return _sendMessage(_contract, _data, _gas, 0x00);
|
||||
}
|
||||
|
||||
function requireToConfirmMessage(address _contract, bytes _data, uint256 _gas) external returns (bytes32) {
|
||||
return _sendMessage(_contract, _data, _gas, 0xf0);
|
||||
}
|
||||
|
||||
function _sendMessage(address _contract, bytes _data, uint256 _gas, uint256 _dataType) internal returns (bytes32) {
|
||||
require(messageId == bytes32(0));
|
||||
bytes32 bridgeId = keccak256(abi.encodePacked(uint16(1337), address(this))) &
|
||||
0x00000000ffffffffffffffffffffffffffffffffffffffff0000000000000000;
|
||||
|
@ -55,7 +63,7 @@ contract AMBMock {
|
|||
uint32(_gas),
|
||||
uint8(2),
|
||||
uint8(2),
|
||||
uint8(0x00),
|
||||
uint8(_dataType),
|
||||
uint16(1337),
|
||||
uint16(1338),
|
||||
_data
|
||||
|
|
|
@ -61,8 +61,8 @@ contract HomeAMBErc20ToNative is BasicAMBErc20ToNative, BlockRewardBridge, HomeF
|
|||
* @param _decimalShift number of decimals shift required to adjust the amount of tokens bridged.
|
||||
* @param _owner address of the owner of the mediator contract
|
||||
* @param _blockReward address of the block reward contract
|
||||
* @param _rewardAddreses list of reward addresses, between whom fees will be distributed
|
||||
* @param _fees array with initial fees for both bridge firections
|
||||
* @param _rewardAddresses list of reward addresses, between whom fees will be distributed
|
||||
* @param _fees array with initial fees for both bridge directions
|
||||
* [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
*/
|
||||
function rewardableInitialize(
|
||||
|
@ -74,10 +74,10 @@ contract HomeAMBErc20ToNative is BasicAMBErc20ToNative, BlockRewardBridge, HomeF
|
|||
int256 _decimalShift,
|
||||
address _owner,
|
||||
address _blockReward,
|
||||
address[] _rewardAddreses,
|
||||
address[] _rewardAddresses,
|
||||
uint256[2] _fees // [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
) external returns (bool) {
|
||||
_setRewardAddressList(_rewardAddreses);
|
||||
_setRewardAddressList(_rewardAddresses);
|
||||
_setFee(HOME_TO_FOREIGN_FEE, _fees[0]);
|
||||
_setFee(FOREIGN_TO_HOME_FEE, _fees[1]);
|
||||
return
|
||||
|
|
|
@ -58,7 +58,7 @@ contract BasicMultiAMBErc20ToErc677 is
|
|||
* @return patch value of the version
|
||||
*/
|
||||
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
|
||||
return (1, 1, 1);
|
||||
return (1, 2, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,13 +4,18 @@ import "./BasicMultiAMBErc20ToErc677.sol";
|
|||
import "./TokenProxy.sol";
|
||||
import "./HomeFeeManagerMultiAMBErc20ToErc677.sol";
|
||||
import "../../interfaces/IBurnableMintableERC677Token.sol";
|
||||
import "./MultiTokenForwardingRules.sol";
|
||||
|
||||
/**
|
||||
* @title HomeMultiAMBErc20ToErc677
|
||||
* @dev Home side implementation for multi-erc20-to-erc677 mediator intended to work on top of AMB bridge.
|
||||
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
|
||||
*/
|
||||
contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManagerMultiAMBErc20ToErc677 {
|
||||
contract HomeMultiAMBErc20ToErc677 is
|
||||
BasicMultiAMBErc20ToErc677,
|
||||
HomeFeeManagerMultiAMBErc20ToErc677,
|
||||
MultiTokenForwardingRules
|
||||
{
|
||||
bytes32 internal constant TOKEN_IMAGE_CONTRACT = 0x20b8ca26cc94f39fab299954184cf3a9bd04f69543e4f454fab299f015b8130f; // keccak256(abi.encodePacked("tokenImageContract"))
|
||||
|
||||
event NewTokenRegistered(address indexed foreignToken, address indexed homeToken);
|
||||
|
@ -26,8 +31,8 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManager
|
|||
* @param _requestGasLimit the gas limit for the message execution.
|
||||
* @param _owner address of the owner of the mediator contract.
|
||||
* @param _tokenImage address of the PermittableToken contract that will be used for deploying of new tokens.
|
||||
* @param _rewardAddreses list of reward addresses, between whom fees will be distributed.
|
||||
* @param _fees array with initial fees for both bridge firections.
|
||||
* @param _rewardAddresses list of reward addresses, between whom fees will be distributed.
|
||||
* @param _fees array with initial fees for both bridge directions.
|
||||
* [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
*/
|
||||
function initialize(
|
||||
|
@ -38,7 +43,7 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManager
|
|||
uint256 _requestGasLimit,
|
||||
address _owner,
|
||||
address _tokenImage,
|
||||
address[] _rewardAddreses,
|
||||
address[] _rewardAddresses,
|
||||
uint256[2] _fees // [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
) external onlyRelevantSender returns (bool) {
|
||||
require(!isInitialized());
|
||||
|
@ -50,8 +55,8 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManager
|
|||
_setRequestGasLimit(_requestGasLimit);
|
||||
_setOwner(_owner);
|
||||
_setTokenImage(_tokenImage);
|
||||
if (_rewardAddreses.length > 0) {
|
||||
_setRewardAddressList(_rewardAddreses);
|
||||
if (_rewardAddresses.length > 0) {
|
||||
_setRewardAddressList(_rewardAddresses);
|
||||
}
|
||||
_setFee(HOME_TO_FOREIGN_FEE, address(0), _fees[0]);
|
||||
_setFee(FOREIGN_TO_HOME_FEE, address(0), _fees[1]);
|
||||
|
@ -242,26 +247,26 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManager
|
|||
* @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 _value requested amount of bridged tokens
|
||||
* @param _data alternative receiver, if specified
|
||||
*/
|
||||
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal {
|
||||
if (!lock()) {
|
||||
bytes32 _messageId = messageId();
|
||||
uint256 valueToBridge = _value;
|
||||
uint256 fee = 0;
|
||||
// Next line disables fee collection in case sender is one of the reward addresses.
|
||||
// It is needed to allow a 100% withdrawal of tokens from the home side.
|
||||
// If fees are not disabled for reward receivers, small fraction of tokens will always
|
||||
// be redistributed between the same set of reward addresses, which is not the desired behaviour.
|
||||
if (!isRewardAddress(_from)) {
|
||||
uint256 fee = _distributeFee(HOME_TO_FOREIGN_FEE, _token, valueToBridge);
|
||||
if (fee > 0) {
|
||||
valueToBridge = valueToBridge.sub(fee);
|
||||
emit FeeDistributed(fee, _token, _messageId);
|
||||
}
|
||||
fee = _distributeFee(HOME_TO_FOREIGN_FEE, _token, valueToBridge);
|
||||
valueToBridge = valueToBridge.sub(fee);
|
||||
}
|
||||
IBurnableMintableERC677Token(_token).burn(valueToBridge);
|
||||
passMessage(_token, _from, chooseReceiver(_from, _data), valueToBridge);
|
||||
bytes32 _messageId = passMessage(_token, _from, chooseReceiver(_from, _data), valueToBridge);
|
||||
if (fee > 0) {
|
||||
emit FeeDistributed(fee, _token, _messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,20 +278,27 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManager
|
|||
* @param _from address of sender, if bridge operation fails, tokens will be returned to this address
|
||||
* @param _receiver address of receiver on the other side, will eventually receive bridged tokens
|
||||
* @param _value bridged amount of tokens
|
||||
* @return id of the created and passed message
|
||||
*/
|
||||
function passMessage(ERC677 _token, address _from, address _receiver, uint256 _value) internal {
|
||||
function passMessage(ERC677 _token, address _from, address _receiver, uint256 _value) internal returns (bytes32) {
|
||||
bytes4 methodSelector = this.handleBridgedTokens.selector;
|
||||
address foreignToken = foreignTokenAddress(_token);
|
||||
bytes memory data = abi.encodeWithSelector(methodSelector, foreignToken, _receiver, _value);
|
||||
|
||||
bytes32 _messageId = bridgeContract().requireToPassMessage(
|
||||
mediatorContractOnOtherSide(),
|
||||
data,
|
||||
requestGasLimit()
|
||||
);
|
||||
address executor = mediatorContractOnOtherSide();
|
||||
uint256 gasLimit = requestGasLimit();
|
||||
IAMB bridge = bridgeContract();
|
||||
|
||||
// Address of the foreign token is used here for determining lane permissions.
|
||||
// Such decision makes it possible to set rules for tokens that are not bridged yet.
|
||||
bytes32 _messageId = destinationLane(foreignToken, _from, _receiver) >= 0
|
||||
? bridge.requireToPassMessage(executor, data, gasLimit)
|
||||
: bridge.requireToConfirmMessage(executor, data, gasLimit);
|
||||
|
||||
setMessageToken(_messageId, _token);
|
||||
setMessageValue(_messageId, _value);
|
||||
setMessageRecipient(_messageId, _from);
|
||||
|
||||
return _messageId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../../upgradeable_contracts/Ownable.sol";
|
||||
|
||||
/**
|
||||
* @title MultiTokenForwardingRules
|
||||
* @dev Multi token mediator functionality for managing destination AMB lanes permissions.
|
||||
*/
|
||||
contract MultiTokenForwardingRules is Ownable {
|
||||
address internal constant ANY_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
|
||||
|
||||
event ForwardingRuleUpdated(address token, address sender, address receiver, int256 lane);
|
||||
|
||||
/**
|
||||
* @dev Tells the destination lane for a particular bridge operation by checking several wildcard forwarding rules.
|
||||
* @param _token address of the token contract on the foreign side of the bridge.
|
||||
* @param _sender address of the tokens sender on the home side of the bridge.
|
||||
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
|
||||
* @return destination lane identifier, where the message should be forwarded to.
|
||||
* 1 - oracle-driven-lane should be used.
|
||||
* 0 - default behaviour should be applied.
|
||||
* -1 - manual lane should be used.
|
||||
*/
|
||||
function destinationLane(address _token, address _sender, address _receiver) public view returns (int256) {
|
||||
int256 defaultLane = forwardingRule(_token, ANY_ADDRESS, ANY_ADDRESS); // specific token for all senders and receivers
|
||||
int256 lane;
|
||||
if (defaultLane < 0) {
|
||||
lane = forwardingRule(_token, _sender, ANY_ADDRESS); // specific token for specific sender
|
||||
if (lane != 0) return lane;
|
||||
lane = forwardingRule(_token, ANY_ADDRESS, _receiver); // specific token for specific receiver
|
||||
if (lane != 0) return lane;
|
||||
return defaultLane;
|
||||
}
|
||||
lane = forwardingRule(ANY_ADDRESS, _sender, ANY_ADDRESS); // all tokens for specific sender
|
||||
if (lane != 0) return lane;
|
||||
return forwardingRule(ANY_ADDRESS, ANY_ADDRESS, _receiver); // all tokens for specific receiver
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the forwarding rule for bridging specific token.
|
||||
* Only owner can call this method.
|
||||
* @param _token address of the token contract on the foreign side.
|
||||
* @param _enable true, if bridge operations for a given token should be forwarded to the manual lane.
|
||||
*/
|
||||
function setTokenForwardingRule(address _token, bool _enable) external {
|
||||
require(_token != ANY_ADDRESS);
|
||||
_setForwardingRule(_token, ANY_ADDRESS, ANY_ADDRESS, _enable ? int256(-1) : int256(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a particular address to send bridge requests to the oracle-driven lane for a particular token.
|
||||
* Only owner can call this method.
|
||||
* @param _token address of the token contract on the foreign side.
|
||||
* @param _sender address of the tokens sender on the home side of the bridge.
|
||||
* @param _enable true, if bridge operations for a given token and sender should be forwarded to the oracle-driven lane.
|
||||
*/
|
||||
function setSenderExceptionForTokenForwardingRule(address _token, address _sender, bool _enable) external {
|
||||
require(_token != ANY_ADDRESS);
|
||||
require(_sender != ANY_ADDRESS);
|
||||
_setForwardingRule(_token, _sender, ANY_ADDRESS, _enable ? int256(1) : int256(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a particular address to receive bridged tokens from the oracle-driven lane for a particular token.
|
||||
* Only owner can call this method.
|
||||
* @param _token address of the token contract on the foreign side.
|
||||
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
|
||||
* @param _enable true, if bridge operations for a given token and receiver should be forwarded to the oracle-driven lane.
|
||||
*/
|
||||
function setReceiverExceptionForTokenForwardingRule(address _token, address _receiver, bool _enable) external {
|
||||
require(_token != ANY_ADDRESS);
|
||||
require(_receiver != ANY_ADDRESS);
|
||||
_setForwardingRule(_token, ANY_ADDRESS, _receiver, _enable ? int256(1) : int256(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the forwarding rule for the specific sender.
|
||||
* Only owner can call this method.
|
||||
* @param _sender address of the tokens sender on the home side.
|
||||
* @param _enable true, if all bridge operations from a given sender should be forwarded to the manual lane.
|
||||
*/
|
||||
function setSenderForwardingRule(address _sender, bool _enable) external {
|
||||
require(_sender != ANY_ADDRESS);
|
||||
_setForwardingRule(ANY_ADDRESS, _sender, ANY_ADDRESS, _enable ? int256(-1) : int256(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the forwarding rule for the specific receiver.
|
||||
* Only owner can call this method.
|
||||
* @param _receiver address of the tokens receiver on the foreign side.
|
||||
* @param _enable true, if all bridge operations to a given receiver should be forwarded to the manual lane.
|
||||
*/
|
||||
function setReceiverForwardingRule(address _receiver, bool _enable) external {
|
||||
require(_receiver != ANY_ADDRESS);
|
||||
_setForwardingRule(ANY_ADDRESS, ANY_ADDRESS, _receiver, _enable ? int256(-1) : int256(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Tells forwarding rule set up for a particular bridge operation.
|
||||
* @param _token address of the token contract on the foreign side of the bridge.
|
||||
* @param _sender address of the tokens sender on the home side of the bridge.
|
||||
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
|
||||
* @return preferred destination lane for the particular bridge operation.
|
||||
*/
|
||||
function forwardingRule(address _token, address _sender, address _receiver) public view returns (int256) {
|
||||
return intStorage[keccak256(abi.encodePacked("forwardTo", _token, _sender, _receiver))];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function for updating the preferred destination lane for the specific wildcard pattern.
|
||||
* Only owner can call this method.
|
||||
* Examples:
|
||||
* _setForwardingRule(tokenA, ANY_ADDRESS, ANY_ADDRESS, -1) - forward all operations on tokenA to the manual lane
|
||||
* _setForwardingRule(tokenA, Alice, ANY_ADDRESS, 1) - allow Alice to use the oracle-driven lane for bridging tokenA
|
||||
* _setForwardingRule(tokenA, ANY_ADDRESS, Bob, 1) - forward all tokenA bridge operations, where Bob is the receiver, to the oracle-driven lane
|
||||
* _setForwardingRule(ANY_ADDRESS, Mallory, ANY_ADDRESS, -1) - forward all bridge operations from Mallory to the manual lane
|
||||
* @param _token address of the token contract on the foreign side of the bridge.
|
||||
* @param _sender address of the tokens sender on the home side of the bridge.
|
||||
* @param _receiver address of the tokens receiver on the foreign side of the bridge.
|
||||
* @param _lane preferred destination lane for the particular sender.
|
||||
* 1 - forward to the oracle-driven lane.
|
||||
* 0 - behaviour is unset, proceed by checking other less-specific rules.
|
||||
* -1 - manual lane should be used.
|
||||
*/
|
||||
function _setForwardingRule(address _token, address _sender, address _receiver, int256 _lane) internal onlyOwner {
|
||||
intStorage[keccak256(abi.encodePacked("forwardTo", _token, _sender, _receiver))] = _lane;
|
||||
|
||||
emit ForwardingRuleUpdated(_token, _sender, _receiver, _lane);
|
||||
}
|
||||
}
|
|
@ -995,6 +995,62 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('oracle driven lane permissions', () => {
|
||||
it('should allow to set/update lane permissions', async () => {
|
||||
expect(await contract.destinationLane(token.address, user, user2)).to.be.bignumber.equal('0')
|
||||
|
||||
await contract.setTokenForwardingRule(token.address, true, { from: user }).should.be.rejected
|
||||
await contract.setTokenForwardingRule(token.address, true, { from: owner }).should.be.fulfilled
|
||||
|
||||
expect(await contract.destinationLane(token.address, user, user2)).to.be.bignumber.equal('-1')
|
||||
|
||||
await contract.setSenderExceptionForTokenForwardingRule(token.address, user, true, { from: user }).should.be
|
||||
.rejected
|
||||
await contract.setSenderExceptionForTokenForwardingRule(token.address, user, true, { from: owner }).should.be
|
||||
.fulfilled
|
||||
|
||||
expect(await contract.destinationLane(token.address, user, user2)).to.be.bignumber.equal('1')
|
||||
expect(await contract.destinationLane(token.address, user2, user2)).to.be.bignumber.equal('-1')
|
||||
|
||||
await contract.setSenderExceptionForTokenForwardingRule(token.address, user, false, { from: owner }).should.be
|
||||
.fulfilled
|
||||
await contract.setReceiverExceptionForTokenForwardingRule(token.address, user, true, { from: user }).should.be
|
||||
.rejected
|
||||
await contract.setReceiverExceptionForTokenForwardingRule(token.address, user, true, { from: owner }).should.be
|
||||
.fulfilled
|
||||
|
||||
expect(await contract.destinationLane(token.address, user, user)).to.be.bignumber.equal('1')
|
||||
expect(await contract.destinationLane(token.address, user, user2)).to.be.bignumber.equal('-1')
|
||||
|
||||
await contract.setTokenForwardingRule(token.address, false, { from: owner }).should.be.fulfilled
|
||||
|
||||
expect(await contract.destinationLane(token.address, user2, user2)).to.be.bignumber.equal('0')
|
||||
|
||||
await contract.setSenderForwardingRule(user2, true, { from: user }).should.be.rejected
|
||||
await contract.setSenderForwardingRule(user2, true, { from: owner }).should.be.fulfilled
|
||||
|
||||
expect(await contract.destinationLane(token.address, user2, user2)).to.be.bignumber.equal('-1')
|
||||
|
||||
await contract.setReceiverForwardingRule(user2, true, { from: user }).should.be.rejected
|
||||
await contract.setReceiverForwardingRule(user2, true, { from: owner }).should.be.fulfilled
|
||||
|
||||
expect(await contract.destinationLane(token.address, user, user2)).to.be.bignumber.equal('-1')
|
||||
})
|
||||
|
||||
it('should send a message to the manual lane', async () => {
|
||||
homeToken = await bridgeToken(token)
|
||||
|
||||
await homeToken.transferAndCall(contract.address, ether('0.1'), '0x', { from: user }).should.be.fulfilled
|
||||
await contract.setTokenForwardingRule(token.address, true, { from: owner }).should.be.fulfilled
|
||||
await homeToken.transferAndCall(contract.address, ether('0.1'), '0x', { from: user }).should.be.fulfilled
|
||||
|
||||
const events = await getEvents(ambBridgeContract, { event: 'MockedEvent' })
|
||||
expect(events.length).to.be.equal(2)
|
||||
expect(strip0x(events[0].returnValues.encodedData).slice(156, 158)).to.be.equal('00')
|
||||
expect(strip0x(events[1].returnValues.encodedData).slice(156, 158)).to.be.equal('f0')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('fees management', () => {
|
||||
|
|
Loading…
Reference in New Issue