Skip to content

Commit b22383f

Browse files
authored
Merge the develop branch to the master branch, preparation to v5.5.0-rc0
2 parents feb0ba5 + 13f8ea6 commit b22383f

File tree

65 files changed

+832
-467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+832
-467
lines changed

contracts/ERC677BridgeToken.sol

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ contract ERC677BridgeToken is IBurnableMintableERC677Token, DetailedERC20, Burna
1616

1717
address internal bridgeContractAddr;
1818

19-
event ContractFallbackCallFailed(address from, address to, uint256 value);
20-
2119
constructor(string _name, string _symbol, uint8 _decimals) public DetailedERC20(_name, _symbol, _decimals) {
2220
// solhint-disable-previous-line no-empty-blocks
2321
}
@@ -67,10 +65,17 @@ contract ERC677BridgeToken is IBurnableMintableERC677Token, DetailedERC20, Burna
6765
return true;
6866
}
6967

68+
/**
69+
* @dev Internal function that calls onTokenTransfer callback on the receiver after the successful transfer.
70+
* Since it is not present in the original ERC677 standard, the callback is only called on the bridge contract,
71+
* in order to simplify UX. In other cases, this token complies with the ERC677/ERC20 standard.
72+
* @param _from tokens sender address.
73+
* @param _to tokens receiver address.
74+
* @param _value amount of sent tokens.
75+
*/
7076
function callAfterTransfer(address _from, address _to, uint256 _value) internal {
71-
if (AddressUtils.isContract(_to) && !contractFallback(_from, _to, _value, new bytes(0))) {
72-
require(!isBridge(_to));
73-
emit ContractFallbackCallFailed(_from, _to, _value);
77+
if (isBridge(_to)) {
78+
require(contractFallback(_from, _to, _value, new bytes(0)));
7479
}
7580
}
7681

@@ -97,7 +102,12 @@ contract ERC677BridgeToken is IBurnableMintableERC677Token, DetailedERC20, Burna
97102
revert();
98103
}
99104

100-
function claimTokens(address _token, address _to) public onlyOwner validAddress(_to) {
105+
/**
106+
* @dev Withdraws the erc20 tokens or native coins from this contract.
107+
* @param _token address of the claimed token or address(0) for native coins.
108+
* @param _to address of the tokens/coins receiver.
109+
*/
110+
function claimTokens(address _token, address _to) external onlyOwner {
101111
claimValues(_token, _to);
102112
}
103113

contracts/ERC677BridgeTokenRewardable.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,29 @@ contract ERC677BridgeTokenRewardable is ERC677MultiBridgeToken {
1313
// solhint-disable-previous-line no-empty-blocks
1414
}
1515

16+
/**
17+
* @dev Updates the address of the used block reward contract.
18+
* Only the token owner can call this method.
19+
* Even though this function is inteded only for the initialization purpose,
20+
* it is still possible to change the already used block reward contract.
21+
* In this case users of the old contract won't lose their accumulated rewards,
22+
* they can proceed with the withdrawal by calling the old block reward contract directly.
23+
* @param _blockRewardContract address of the new block reward contract.
24+
*/
1625
function setBlockRewardContract(address _blockRewardContract) external onlyOwner {
1726
require(AddressUtils.isContract(_blockRewardContract));
1827
blockRewardContract = _blockRewardContract;
1928
}
2029

30+
/**
31+
* @dev Updates the address of the used staking contract.
32+
* Only the token owner can call this method.
33+
* Even though this function is inteded only for the initialization purpose,
34+
* it is still possible to change the already used staking contract.
35+
* In this case users of the old staking contract won't lose their tokens,
36+
* they can proceed with the withdrawal by calling the old staking contract directly.
37+
* @param _stakingContract address of the new staking contract.
38+
*/
2139
function setStakingContract(address _stakingContract) external onlyOwner {
2240
require(AddressUtils.isContract(_stakingContract));
2341
require(balanceOf(_stakingContract) == 0);

contracts/PermittableToken.sol

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ contract PermittableToken is ERC677BridgeToken {
6767
return true;
6868
}
6969

70+
/// @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
71+
/// @param _to The address which will spend the funds.
72+
/// @param _value The amount of tokens to be spent.
73+
function approve(address _to, uint256 _value) public returns (bool result) {
74+
result = super.approve(_to, _value);
75+
if (_value == uint256(-1)) {
76+
delete expirations[msg.sender][_to];
77+
}
78+
}
79+
80+
/// @dev Atomically increases the allowance granted to spender by the caller.
81+
/// @param _to The address which will spend the funds.
82+
/// @param _addedValue The amount of tokens to increase the allowance by.
83+
function increaseAllowance(address _to, uint256 _addedValue) public returns (bool result) {
84+
result = super.increaseAllowance(_to, _addedValue);
85+
if (allowed[msg.sender][_to] == uint256(-1)) {
86+
delete expirations[msg.sender][_to];
87+
}
88+
}
89+
7090
/// @dev An alias for `transfer` function.
7191
/// @param _to The address of the recipient.
7292
/// @param _amount The value to transfer.
@@ -98,6 +118,8 @@ contract PermittableToken is ERC677BridgeToken {
98118
/// @param _nonce The nonce taken from `nonces(_holder)` public getter.
99119
/// @param _expiry The allowance expiration date (unix timestamp in UTC).
100120
/// Can be zero for no expiration. Forced to zero if `_allowed` is `false`.
121+
/// Note that timestamps are not precise, malicious miner/validator can manipulate them to some extend.
122+
/// Assume that there can be a 900 seconds time delta between the desired timestamp and the actual expiration.
101123
/// @param _allowed True to enable unlimited allowance for the spender by the holder. False to disable.
102124
/// @param _v A final byte of signature (ECDSA component).
103125
/// @param _r The first 32 bytes of signature (ECDSA component).
@@ -116,6 +138,9 @@ contract PermittableToken is ERC677BridgeToken {
116138
require(_spender != address(0));
117139
require(_expiry == 0 || _now() <= _expiry);
118140

141+
require(_v == 27 || _v == 28);
142+
require(uint256(_s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);
143+
119144
bytes32 digest = keccak256(
120145
abi.encodePacked(
121146
"\x19\x01",

contracts/interfaces/IBurnableMintableERC677Token.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import "../interfaces/ERC677.sol";
44
contract IBurnableMintableERC677Token is ERC677 {
55
function mint(address _to, uint256 _amount) public returns (bool);
66
function burn(uint256 _value) public;
7-
function claimTokens(address _token, address _to) public;
7+
function claimTokens(address _token, address _to) external;
88
}

contracts/libraries/ArbitraryMessage.sol

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@ library ArbitraryMessage {
1111
* offset 104 : 4 bytes :: uint32 - gasLimit
1212
* offset 108 : 1 bytes :: uint8 - source chain id length (X)
1313
* offset 109 : 1 bytes :: uint8 - destination chain id length (Y)
14-
* offset 110 : 1 bytes :: bytes1 - dataType
15-
* (optional) 111 : 32 bytes :: uint256 - gasPrice
16-
* (optional) 111 : 1 bytes :: bytes1 - gasPriceSpeed
17-
* offset 111/143/112 : X bytes :: bytes - source chain id
18-
* offset 111/143/112 + X : Y bytes :: bytes - destination chain id
14+
* offset 110 : 1 bytes :: uint8 - dataType
15+
* offset 111 : X bytes :: bytes - source chain id
16+
* offset 111 + X : Y bytes :: bytes - destination chain id
1917
2018
* NOTE: when message structure is changed, make sure that MESSAGE_PACKING_VERSION from VersionableAMB is updated as well
2119
* NOTE: assembly code uses calldatacopy, make sure that message is passed as the first argument in the calldata
@@ -29,9 +27,8 @@ library ArbitraryMessage {
2927
address sender,
3028
address executor,
3129
uint32 gasLimit,
32-
bytes1 dataType,
30+
uint8 dataType,
3331
uint256[2] chainIds,
34-
uint256 gasPrice,
3532
bytes memory data
3633
)
3734
{
@@ -50,23 +47,16 @@ library ArbitraryMessage {
5047
executor := shr(96, blob)
5148
gasLimit := and(shr(64, blob), 0xffffffff)
5249

50+
dataType := byte(26, blob)
51+
if gt(dataType, 0) {
52+
// for now, only 0 datatype is supported - regular AMB calls
53+
// other dataType values are kept reserved for future use
54+
revert(0, 0)
55+
}
56+
5357
// load source chain id length
5458
let chainIdLength := byte(24, blob)
5559

56-
dataType := and(shl(208, blob), 0xFF00000000000000000000000000000000000000000000000000000000000000)
57-
switch dataType
58-
case 0x0000000000000000000000000000000000000000000000000000000000000000 {
59-
gasPrice := 0
60-
}
61-
case 0x0100000000000000000000000000000000000000000000000000000000000000 {
62-
gasPrice := mload(add(_data, 111)) // 32
63-
srcdataptr := add(srcdataptr, 32)
64-
}
65-
case 0x0200000000000000000000000000000000000000000000000000000000000000 {
66-
gasPrice := 0
67-
srcdataptr := add(srcdataptr, 1)
68-
}
69-
7060
// at this moment srcdataptr points to sourceChainId
7161

7262
// mask for sourceChainId

contracts/libraries/Message.sol

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,6 @@ pragma solidity 0.4.24;
22
import "../interfaces/IBridgeValidators.sol";
33

44
library Message {
5-
// function uintToString(uint256 inputValue) internal pure returns (string) {
6-
// // figure out the length of the resulting string
7-
// uint256 length = 0;
8-
// uint256 currentValue = inputValue;
9-
// do {
10-
// length++;
11-
// currentValue /= 10;
12-
// } while (currentValue != 0);
13-
// // allocate enough memory
14-
// bytes memory result = new bytes(length);
15-
// // construct the string backwards
16-
// uint256 i = length - 1;
17-
// currentValue = inputValue;
18-
// do {
19-
// result[i--] = byte(48 + currentValue % 10);
20-
// currentValue /= 10;
21-
// } while (currentValue != 0);
22-
// return string(result);
23-
// }
24-
255
function addressArrayContains(address[] array, address value) internal pure returns (bool) {
266
for (uint256 i = 0; i < array.length; i++) {
277
if (array[i] == value) {
@@ -83,6 +63,9 @@ library Message {
8363
s := mload(add(signature, 0x40))
8464
v := mload(add(signature, 0x60))
8565
}
66+
require(uint8(v) == 27 || uint8(v) == 28);
67+
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);
68+
8669
return ecrecover(hashMessage(message, isAMBMessage), uint8(v), r, s);
8770
}
8871

contracts/libraries/TokenReader.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ library TokenReader {
7373
ptr := mload(0x40)
7474
mstore(ptr, 0x95d89b4100000000000000000000000000000000000000000000000000000000) // symbol()
7575
if iszero(staticcall(gas, _token, ptr, 4, ptr, 32)) {
76-
mstore(ptr, 0xf76f8d7800000000000000000000000000000000000000000000000000000000) // SYMBOl()
76+
mstore(ptr, 0xf76f8d7800000000000000000000000000000000000000000000000000000000) // SYMBOL()
7777
staticcall(gas, _token, ptr, 4, ptr, 32)
7878
pop
7979
}

contracts/mocks/MessageTest.sol

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,12 @@ contract MessageTest {
1111
address sender,
1212
address executor,
1313
uint32 gasLimit,
14-
bytes1 dataType,
14+
uint8 dataType,
1515
uint256[2] chainIds,
16-
uint256 gasPrice,
1716
bytes memory data
1817
)
1918
{
20-
(messageId, sender, executor, gasLimit, dataType, chainIds, gasPrice, data) = ArbitraryMessage.unpackData(
21-
_data
22-
);
19+
(messageId, sender, executor, gasLimit, dataType, chainIds, data) = ArbitraryMessage.unpackData(_data);
2320
}
2421

2522
function unpackDataWithExtraParams(
@@ -33,15 +30,12 @@ contract MessageTest {
3330
address sender,
3431
address executor,
3532
uint32 gasLimit,
36-
bytes1 dataType,
33+
uint8 dataType,
3734
uint256[2] chainIds,
38-
uint256 gasPrice,
3935
bytes memory data
4036
)
4137
{
42-
(messageId, sender, executor, gasLimit, dataType, chainIds, gasPrice, data) = ArbitraryMessage.unpackData(
43-
_data
44-
);
38+
(messageId, sender, executor, gasLimit, dataType, chainIds, data) = ArbitraryMessage.unpackData(_data);
4539
}
4640

4741
}

contracts/upgradeable_contracts/BaseFeeManager.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ contract BaseFeeManager is EternalStorage, FeeTypes {
1616
bytes32 internal constant HOME_FEE_STORAGE_KEY = 0xc3781f3cec62d28f56efe98358f59c2105504b194242dbcb2cc0806850c306e7; // keccak256(abi.encodePacked("homeFee"))
1717
bytes32 internal constant FOREIGN_FEE_STORAGE_KEY = 0x68c305f6c823f4d2fa4140f9cf28d32a1faccf9b8081ff1c2de11cf32c733efc; // keccak256(abi.encodePacked("foreignFee"))
1818

19-
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType) public view returns (uint256) {
19+
function calculateFee(uint256 _value, bool _recover, bytes32 _feeType)
20+
public
21+
view
22+
validFeeType(_feeType)
23+
returns (uint256)
24+
{
2025
uint256 fee = _feeType == HOME_FEE ? getHomeFee() : getForeignFee();
2126
if (!_recover) {
2227
return _value.mul(fee).div(MAX_FEE);

contracts/upgradeable_contracts/BaseMediatorFeeManager.sol

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pragma solidity 0.4.24;
22

33
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
44
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
5+
import "openzeppelin-solidity/contracts/AddressUtils.sol";
56

67
/**
78
* @title BaseMediatorFeeManager
@@ -31,9 +32,11 @@ contract BaseMediatorFeeManager is Ownable {
3132
* @dev Stores the initial parameters of the fee manager.
3233
* @param _owner address of the owner of the fee manager contract.
3334
* @param _fee the fee percentage amount.
34-
* @param _rewardAccountList list of addresses that will receive the fee rewards.
35+
* @param _rewardAccountList list of unique addresses that will receive the fee rewards.
36+
* @param _mediatorContract address of the mediator contract used together with this fee manager.
3537
*/
3638
constructor(address _owner, uint256 _fee, address[] _rewardAccountList, address _mediatorContract) public {
39+
require(AddressUtils.isContract(_mediatorContract));
3740
require(_rewardAccountList.length > 0 && _rewardAccountList.length <= MAX_REWARD_ACCOUNTS);
3841
_transferOwnership(_owner);
3942
_setFee(_fee);
@@ -82,7 +85,7 @@ contract BaseMediatorFeeManager is Ownable {
8285
function addRewardAccount(address _account) external onlyOwner {
8386
require(isValidAccount(_account));
8487
require(!isRewardAccount(_account));
85-
require(rewardAccounts.length.add(1) < MAX_REWARD_ACCOUNTS);
88+
require(rewardAccounts.length < MAX_REWARD_ACCOUNTS);
8689
rewardAccounts.push(_account);
8790
}
8891

@@ -154,6 +157,12 @@ contract BaseMediatorFeeManager is Ownable {
154157
*/
155158
function distributeFee(uint256 _fee) internal {
156159
uint256 numOfAccounts = rewardAccountsCount();
160+
if (numOfAccounts == 0) {
161+
// In case there are no reward accounts defined, no actual fee distribution will happen.
162+
// Funds will be kept locked on the contract until some of the reward accounts will be added.
163+
// After it, locked funds ca be distributed by a call to onTokenTransfer() of this contract, which can be done by anyone.
164+
return;
165+
}
157166
uint256 feePerAccount = _fee.div(numOfAccounts);
158167
uint256 randomAccountIndex;
159168
uint256 diff = _fee.sub(feePerAccount.mul(numOfAccounts));

0 commit comments

Comments
 (0)