Skip to content

Commit

Permalink
Merge branch 'develop' into amb-multi-requests
Browse files Browse the repository at this point in the history
  • Loading branch information
k1rill-fedoseev committed Apr 27, 2020
2 parents 257fa9f + 8bce8f7 commit 07d10f4
Show file tree
Hide file tree
Showing 21 changed files with 230 additions and 284 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import "../Claimable.sol";
import "../VersionableBridge.sol";
import "../../libraries/Bytes.sol";

/**
* @title BasicAMBErc677ToErc677
* @dev Common functionality for erc677-to-erc677 mediator intended to work on top of AMB bridge.
*/
contract BasicAMBErc677ToErc677 is
Initializable,
Ownable,
Expand All @@ -23,6 +27,7 @@ contract BasicAMBErc677ToErc677 is
BaseERC677Bridge
{
event FailedMessageFixed(bytes32 indexed messageId, address recipient, uint256 value);
event TokensBridged(address indexed recipient, uint256 value, bytes32 indexed messageId);

bytes32 internal constant BRIDGE_CONTRACT = 0x811bbb11e8899da471f0e69a3ed55090fc90215227fc5fb1cb0d6e962ea7b74f; // keccak256(abi.encodePacked("bridgeContract"))
bytes32 internal constant MEDIATOR_CONTRACT = 0x98aa806e31e94a687a31c65769cb99670064dd7f5a87526da075c5fb4eab9880; // keccak256(abi.encodePacked("mediatorContract"))
Expand Down Expand Up @@ -69,9 +74,16 @@ contract BasicAMBErc677ToErc677 is
return mediatorContractOnOtherSide();
}

function passMessage(address _from, uint256 _value) internal {
/**
* @dev Constructs and passes a message to the AMB bridge contract.
* Message represents a call of handleBridgedTokens(receiver, value, nonce) on the other side mediator contract.
* @param _from adddress of sender, if bridge operation failes, tokens will be returned to this address
* @param _receiver adddress of receiver on the other side, will eventually receive bridged tokens
* @param _value bridged amount of tokens
*/
function passMessage(address _from, address _receiver, uint256 _value) internal {
bytes4 methodSelector = this.handleBridgedTokens.selector;
bytes memory data = abi.encodeWithSelector(methodSelector, _from, _value);
bytes memory data = abi.encodeWithSelector(methodSelector, _receiver, _value);

bytes32 messageId = bridgeContract().requireToPassMessage(
mediatorContractOnOtherSide(),
Expand Down Expand Up @@ -219,6 +231,12 @@ contract BasicAMBErc677ToErc677 is
}
}

/**
* @dev Fixes locked tokens, that were out of execution limits during the call to handleBridgedTokens
* @param messageId reference transaction hash for bridge operation that was out of execution limits
* @param unlockOnForeign true if fixed tokens should be unlocked to the other side of the bridge
* @param valueToUnlock unlocked amount of tokens, should be less than maxPerTx() and saved txAboveLimitsValue
*/
function fixAssetsAboveLimits(bytes32 messageId, bool unlockOnForeign, uint256 valueToUnlock)
external
onlyIfUpgradeabilityOwner
Expand All @@ -237,7 +255,7 @@ contract BasicAMBErc677ToErc677 is
setFixedAssets(messageId);
}
if (unlockOnForeign) {
passMessage(recipient, valueToUnlock);
passMessage(recipient, recipient, valueToUnlock);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,38 @@ pragma solidity 0.4.24;

import "./BasicAMBErc677ToErc677.sol";

/**
* @title ForeignAMBErc677ToErc677
* @dev Foreign side implementation for erc677-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 ForeignAMBErc677ToErc677 is BasicAMBErc677ToErc677 {
/**
* @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());
bytes32 messageId = _messageId();
erc677token().transfer(_recipient, value);
emit TokensBridged(_recipient, value, messageId);
}

/**
* @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);
passMessage(_from, chooseReceiver(_from, _data), _value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ pragma solidity 0.4.24;
import "./BasicStakeTokenMediator.sol";
import "../../interfaces/IBurnableMintableERC677Token.sol";

/**
* @title ForeignStakeTokenMediator
* @dev Foreign side implementation for stake token mediator intended to work on top of AMB bridge.
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
*/
contract ForeignStakeTokenMediator is BasicStakeTokenMediator {
/**
* @dev Executes action on the request to withdraw tokens relayed from the other network
Expand All @@ -11,7 +16,9 @@ contract ForeignStakeTokenMediator is BasicStakeTokenMediator {
*/
function executeActionOnBridgedTokens(address _recipient, uint256 _value) internal {
uint256 value = _value.div(10**decimalShift());
bytes32 messageId = _messageId();
_transferWithOptionalMint(_recipient, value);
emit TokensBridged(_recipient, value, messageId);
}

/**
Expand All @@ -27,7 +34,7 @@ contract ForeignStakeTokenMediator is BasicStakeTokenMediator {
bytes _data
) internal {
if (!lock()) {
passMessage(chooseReceiver(_from, _data), _value);
passMessage(_from, chooseReceiver(_from, _data), _value);
}
}

Expand All @@ -48,7 +55,7 @@ contract ForeignStakeTokenMediator is BasicStakeTokenMediator {
function _transferWithOptionalMint(address _recipient, uint256 _value) internal {
IBurnableMintableERC677Token token = IBurnableMintableERC677Token(erc677token());
uint256 balance = token.balanceOf(address(this));
if (_recipient != address(0) && balance == 0) {
if (balance == 0) {
token.mint(_recipient, _value);
} else if (balance < _value) {
token.mint(address(this), _value - balance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,35 @@ pragma solidity 0.4.24;
import "./BasicAMBErc677ToErc677.sol";
import "../../interfaces/IBurnableMintableERC677Token.sol";

/**
* @title HomeAMBErc677ToErc677
* @dev Home side implementation for erc677-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 HomeAMBErc677ToErc677 is BasicAMBErc677ToErc677 {
/**
* @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());
bytes32 messageId = _messageId();
IBurnableMintableERC677Token(erc677token()).mint(_recipient, value);
emit TokensBridged(_recipient, value, messageId);
}

/**
* @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()) {
IBurnableMintableERC677Token(_token).burn(_value);
passMessage(chooseReceiver(_from, _data), _value);
passMessage(_from, chooseReceiver(_from, _data), _value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import "../BlockRewardBridge.sol";
import "./HomeStakeTokenFeeManager.sol";
import "../../interfaces/IBurnableMintableERC677Token.sol";

/**
* @title HomeStakeTokenMediator
* @dev Home side implementation for stake token mediator intended to work on top of AMB bridge.
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
*/
contract HomeStakeTokenMediator is BasicStakeTokenMediator, HomeStakeTokenFeeManager {
bytes32 internal constant MINT_HANDLER = 0x8a8236f871f2bbb44f59e8c68b82f7587d19c987e09aba39148cc97ea004a32e; // keccak256(abi.encodePacked("mintHandler"))

Expand Down Expand Up @@ -119,7 +124,9 @@ contract HomeStakeTokenMediator is BasicStakeTokenMediator, HomeStakeTokenFeeMan
*/
function executeActionOnBridgedTokens(address _recipient, uint256 _value) internal {
uint256 value = _value.mul(10**decimalShift());
bytes32 messageId = _messageId();
getMintHandler().mint(_recipient, value);
emit TokensBridged(_recipient, value, messageId);
}

/**
Expand All @@ -136,11 +143,11 @@ contract HomeStakeTokenMediator is BasicStakeTokenMediator, HomeStakeTokenFeeMan

if (address(_blockRewardContract()) == address(0)) {
// in case if block reward contract is not configured, the fee is not collected
passMessage(chooseReceiver(_from, _data), _value);
passMessage(_from, 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));
passMessage(_from, chooseReceiver(_from, _data), _value.sub(fee));
if (fee > 0) {
// the fee itself is distributed later in the block reward contract
_blockRewardContract().addBridgeTokenRewardReceivers(fee);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,30 +104,14 @@ contract ForeignBridgeErcToNative is BasicForeignBridge, ERC20Bridge, OtherSideB
revert();
}

function migrateToMCD() external {
bytes32 storageAddress = 0x3378953eb16363e06fd9ea9701d36ed7285d206d9de7df55b778462d74596a89; // keccak256(abi.encodePacked("migrationToMcdCompleted"))
require(!boolStorage[storageAddress]);

address mcdContract = IDaiAdapter(migrationContract().daiJoin()).dai();
setErc20token(mcdContract);

uintStorage[MIN_HDTOKEN_BALANCE] = 10 ether;

swapTokens();

boolStorage[storageAddress] = true;
}

function saiTopContract() internal pure returns (ISaiTop) {
return ISaiTop(0x9b0ccf7C8994E19F39b2B4CF708e0A7DF65fA8a3);
}

function isTokenSwapAllowed(uint256 _ts) public view returns (bool) {
uint256 esTs = saiTopContract().caged();
if (esTs > 0 && _ts > esTs) {
return false;
}
return true;
function isTokenSwapAllowed(
uint256 /* _ts */
) public pure returns (bool) {
return false;
}

function halfDuplexErc20token() public pure returns (ERC20) {
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "token-bridge-contracts",
"version": "4.1.0",
"version": "4.1.1",
"description": "Bridge",
"main": "index.js",
"scripts": {
Expand Down
66 changes: 65 additions & 1 deletion test/amb_erc677_to_erc677/AMBErc677ToErc677Behavior.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
let erc677Token
const owner = accounts[0]
const user = accounts[1]
const user2 = accounts[2]
describe('initialize', () => {
beforeEach(async () => {
bridgeContract = await AMBMock.new()
Expand Down Expand Up @@ -498,7 +499,6 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
describe('relayTokens', () => {
let contract
let erc20Token
const user2 = accounts[2]
beforeEach(async function() {
bridgeContract = await AMBMock.new()
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
Expand Down Expand Up @@ -981,6 +981,70 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
expect(event[0].returnValues.value).to.be.equal(oneEther.toString())
})
})
describe('fixFailedMessage for alternative receiver', () => {
let transferMessageId
let contract
beforeEach(async function() {
bridgeContract = await AMBMock.new()
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
mediatorContract = await otherSideMediatorContract.new()
erc677Token = await ERC677BridgeToken.new('test', 'TST', 18)
await erc677Token.mint(user, twoEthers, { from: owner }).should.be.fulfilled

contract = this.bridge

await contract.initialize(
bridgeContract.address,
mediatorContract.address,
erc677Token.address,
[dailyLimit, maxPerTx, minPerTx],
[executionDailyLimit, executionMaxPerTx],
maxGasPerTx,
decimalShiftZero,
owner
).should.be.fulfilled
await erc677Token.transferOwnership(contract.address)

expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(twoEthers)
expect(await erc677Token.totalSupply()).to.be.bignumber.equal(twoEthers)

// User transfer tokens
await erc677Token.transferAndCall(contract.address, oneEther, user2, { from: user }).should.be.fulfilled

expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(oneEther)

const events = await getEvents(bridgeContract, { event: 'MockedEvent' })
transferMessageId = events[0].returnValues.messageId
expect(events.length).to.be.equal(1)
})
it('should fix burnt/locked tokens', async () => {
// Given
expect(await contract.messageFixed(transferMessageId)).to.be.equal(false)

// When
const fixData = await contract.contract.methods.fixFailedMessage(transferMessageId).encodeABI()

await bridgeContract.executeMessageCall(
contract.address,
mediatorContract.address,
fixData,
exampleMessageId,
1000000
).should.be.fulfilled

// Then
expect(await bridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(twoEthers)
expect(await erc677Token.totalSupply()).to.be.bignumber.equal(twoEthers)
expect(await contract.messageFixed(transferMessageId)).to.be.equal(true)

const event = await getEvents(contract, { event: 'FailedMessageFixed' })
expect(event.length).to.be.equal(1)
expect(event[0].returnValues.messageId).to.be.equal(transferMessageId)
expect(event[0].returnValues.recipient).to.be.equal(user)
expect(event[0].returnValues.value).to.be.equal(oneEther.toString())
})
})
describe('#claimTokens', () => {
it('should be able to claim tokens', async function() {
const contract = this.proxyContract
Expand Down
15 changes: 15 additions & 0 deletions test/amb_erc677_to_erc677/foreign_bridge.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ contract('ForeignAMBErc677ToErc677', async accounts => {
expect(await foreignBridge.totalExecutedPerDay(currentDay)).to.be.bignumber.equal(oneEther)
expect(await erc677Token.balanceOf(foreignBridge.address)).to.be.bignumber.equal(oneEther)
expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(oneEther)

const TokensBridgedEvent = await getEvents(foreignBridge, { event: 'TokensBridged' })
expect(TokensBridgedEvent.length).to.be.equal(1)
expect(TokensBridgedEvent[0].returnValues.recipient).to.be.equal(user)
expect(TokensBridgedEvent[0].returnValues.value).to.be.equal(oneEther.toString())
expect(TokensBridgedEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
})
it('should transfer locked tokens on message from amb with decimal shift of two', async () => {
// Given
Expand Down Expand Up @@ -225,6 +231,12 @@ contract('ForeignAMBErc677ToErc677', async accounts => {
expect(await foreignBridge.totalExecutedPerDay(currentDay)).to.be.bignumber.equal(valueOnHome)
expect(await erc677Token.balanceOf(foreignBridge.address)).to.be.bignumber.equal(twoEthers.sub(valueOnForeign))
expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(valueOnForeign)

const TokensBridgedEvent = await getEvents(foreignBridge, { event: 'TokensBridged' })
expect(TokensBridgedEvent.length).to.be.equal(1)
expect(TokensBridgedEvent[0].returnValues.recipient).to.be.equal(user)
expect(TokensBridgedEvent[0].returnValues.value).to.be.equal(valueOnForeign.toString())
expect(TokensBridgedEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
})
it('should emit AmountLimitExceeded and not transfer tokens when out of execution limits', async () => {
// Given
Expand Down Expand Up @@ -261,6 +273,9 @@ contract('ForeignAMBErc677ToErc677', async accounts => {
expect(outOfLimitEvent[0].returnValues.recipient).to.be.equal(user)
expect(outOfLimitEvent[0].returnValues.value).to.be.equal(twoEthers.toString())
expect(outOfLimitEvent[0].returnValues.transactionHash).to.be.equal(exampleMessageId)

const TokensBridgedEvent = await getEvents(foreignBridge, { event: 'TokensBridged' })
expect(TokensBridgedEvent.length).to.be.equal(0)
})
})
})
Loading

0 comments on commit 07d10f4

Please sign in to comment.