Skip to content

Commit

Permalink
Merge branch 'develop' into 365-amb-native-to-erc
Browse files Browse the repository at this point in the history
# Conflicts:
#	contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol
  • Loading branch information
patitonar committed Apr 21, 2020
2 parents 69a612e + 8bce8f7 commit 8b1172b
Show file tree
Hide file tree
Showing 20 changed files with 241 additions and 284 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import "../Claimable.sol";
import "../VersionableBridge.sol";
import "../TokenBridgeMediator.sol";

/**
* @title BasicAMBErc677ToErc677
* @dev Common functionality for erc677-to-erc677 mediator intended to work on top of AMB bridge.
*/
contract BasicAMBErc677ToErc677 is
Initializable,
ReentrancyGuard,
Expand Down Expand Up @@ -63,9 +67,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, nonce());
bytes memory data = abi.encodeWithSelector(methodSelector, _receiver, _value, nonce());

bytes32 dataHash = keccak256(data);
setMessageHashValue(dataHash, _value);
Expand Down Expand Up @@ -135,6 +146,12 @@ contract BasicAMBErc677ToErc677 is
emit AmountLimitExceeded(_recipient, _value, txHash);
}

/**
* @dev Fixes locked tokens, that were out of execution limits during the call to handleBridgedTokens
* @param txHash 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 txHash, bool unlockOnForeign, uint256 valueToUnlock)
external
onlyIfUpgradeabilityOwner
Expand All @@ -153,7 +170,7 @@ contract BasicAMBErc677ToErc677 is
setFixedAssets(txHash);
}
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 txHash = transactionHash();
erc677token().transfer(_recipient, value);
emit TokensBridged(_recipient, value, txHash);
}

/**
* @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 txHash = transactionHash();
_transferWithOptionalMint(_recipient, value);
emit TokensBridged(_recipient, value, txHash);
}

/**
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 txHash = transactionHash();
IBurnableMintableERC677Token(erc677token()).mint(_recipient, value);
emit TokensBridged(_recipient, value, txHash);
}

/**
* @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 txHash = transactionHash();
getMintHandler().mint(_recipient, value);
emit TokensBridged(_recipient, value, txHash);
}

/**
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
84 changes: 83 additions & 1 deletion test/amb_erc677_to_erc677/AMBErc677ToErc677Behavior.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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 @@ -992,6 +992,88 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
expect(event[0].returnValues.value).to.be.equal(oneEther.toString())
})
})
describe('fixFailedMessage for alternative receiver', () => {
let dataHash
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
const transferTx = 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' })
expect(events.length).to.be.equal(1)
const data = `0x${events[0].returnValues.encodedData.substr(
148,
events[0].returnValues.encodedData.length - 148
)}`

// Bridge calls mediator from other side
await bridgeContract.executeMessageCall(
contract.address,
contract.address,
data,
transferTx.tx,
100
).should.be.fulfilled

expect(await bridgeContract.messageCallStatus(transferTx.tx)).to.be.equal(false)

// mediator from other side should use this dataHash to request fix the failed message
dataHash = await bridgeContract.failedMessageDataHash(transferTx.tx)
})
it('should fix burnt/locked tokens', async () => {
// Given
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)

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

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

// Then
expect(await bridgeContract.messageCallStatus(exampleTxHash)).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.messageHashFixed(dataHash)).to.be.equal(true)

const event = await getEvents(contract, { event: 'FailedMessageFixed' })
expect(event.length).to.be.equal(1)
expect(event[0].returnValues.dataHash).to.be.equal(dataHash)
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
Loading

0 comments on commit 8b1172b

Please sign in to comment.