Skip to content

Commit b3511bf

Browse files
authored
Merge the develop branch to the master branch, preparation to v6.0.0-rc1
This set of changes includes the following improvements and fixes: * [Improvement] EIP2612 permit (#618) * [Fix] Make _sendMessage internal (#613) * [Other] Fix coverage (#606) * [Other] Remove unused upgrade dir (#339 * [Other] Bump package version before 6.0.0-rc1 (#619)
2 parents 004d466 + fb66e2a commit b3511bf

Some content is hidden

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

41 files changed

+14011
-24361
lines changed

.dockerignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,4 @@ Dockerfile
1111
flats
1212
contracts.sublime-project
1313
contracts.sublime-workspace
14-
upgrade/*.env*
15-
!upgrade/.env.example
1614
build/

.eslintignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
node_modules
22
deploy
3-
upgrade
3+
coverage

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
CC_SECRET: ${{ secrets.CC_SECRET }}
5454
coverage:
5555
runs-on: ubuntu-latest
56-
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags')
56+
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/tags') || contains(github.event.head_commit.message, 'coverage')
5757
steps:
5858
- uses: actions/setup-node@v1
5959
with:

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ flats
44
.node*
55
.idea
66
coverage
7+
coverage.json
78
*.sublime-*
89
.0x-artifacts
910
deploy/*.config
1011
deploy/*.env*
1112
!deploy/.env.example
12-
upgrade/*.env*
13-
!upgrade/.env.example
1413
.vscode

.solcover.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const Web3 = require('web3')
2+
3+
async function waitCompoundDeploy(config) {
4+
const web3 = new Web3(config.provider)
5+
const abi = [{
6+
inputs: [{ name: "", type: "address"}],
7+
outputs: [{ name: "", type: "uint256" }],
8+
name: "balanceOf",
9+
stateMutability: "view",
10+
type: "function"
11+
}]
12+
const cDai = new web3.eth.Contract(abi, '0x615cba17EE82De39162BB87dBA9BcfD6E8BcF298')
13+
const faucet = (await web3.eth.getAccounts())[6]
14+
while (true) {
15+
try {
16+
if (await cDai.methods.balanceOf(faucet).call() !== '0') break
17+
} catch (e) {
18+
await new Promise(res => setTimeout(res, 1000))
19+
}
20+
}
21+
}
22+
23+
module.exports = {
24+
skipFiles: [
25+
'Migrations.sol',
26+
'mocks',
27+
'interfaces',
28+
'helpers'
29+
],
30+
providerOptions: {
31+
port: 8545,
32+
_chainId: 1337,
33+
network_id: 1337,
34+
seed: 'TestRPC is awesome!',
35+
default_balance_ether: 1000000
36+
},
37+
onServerReady: waitCompoundDeploy,
38+
istanbulReporter: ['lcov']
39+
}

Dockerfile

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ COPY ./deploy/package.json ./deploy/
2323
COPY ./deploy/package-lock.json ./deploy/
2424
RUN cd ./deploy; npm install --only=prod; cd ..
2525

26-
COPY ./upgrade/package.json ./upgrade/
27-
COPY ./upgrade/package-lock.json ./upgrade/
28-
RUN cd ./upgrade; npm install --only=prod; cd ..
29-
30-
COPY ./upgrade ./upgrade
3126
COPY deploy.sh deploy.sh
3227
COPY ./deploy ./deploy
3328

Dockerfile.dev

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ COPY ./deploy/package.json ./deploy/
1414
COPY ./deploy/package-lock.json ./deploy/
1515
RUN cd ./deploy; npm install; cd ..
1616

17-
COPY ./upgrade/package.json ./upgrade/
18-
COPY ./upgrade/package-lock.json ./upgrade/
19-
RUN cd ./upgrade; npm install; cd ..
20-
2117
COPY truffle-config.js truffle-config.js
2218
COPY ./contracts ./contracts
2319
RUN npm run compile
@@ -29,7 +25,6 @@ COPY .eslintignore .eslintignore
2925
COPY .eslintrc .eslintrc
3026
COPY .prettierrc .prettierrc
3127

32-
COPY ./upgrade ./upgrade
3328
COPY deploy.sh deploy.sh
3429
COPY ./deploy ./deploy
3530
COPY .solhint.json .solhint.json

contracts/ERC677BridgeToken.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ contract ERC677BridgeToken is IBurnableMintableERC677Token, DetailedERC20, Burna
4646
}
4747

4848
function getTokenInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
49-
return (2, 4, 0);
49+
return (2, 5, 0);
5050
}
5151

5252
function superTransfer(address _to, uint256 _value) internal returns (bool) {

contracts/PermittableToken.sol

Lines changed: 102 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ contract PermittableToken is ERC677BridgeToken {
77

88
// EIP712 niceties
99
bytes32 public DOMAIN_SEPARATOR;
10-
// bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
11-
bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
10+
// bytes32 public constant PERMIT_TYPEHASH_LEGACY = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
11+
bytes32 public constant PERMIT_TYPEHASH_LEGACY = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
12+
// bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
13+
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
1214

1315
mapping(address => uint256) public nonces;
1416
mapping(address => mapping(address => uint256)) public expirations;
@@ -56,7 +58,7 @@ contract PermittableToken is ERC677BridgeToken {
5658
} else {
5759
// If allowance is unlimited by `permit`, `approve`, or `increaseAllowance`
5860
// function, don't adjust it. But the expiration date must be empty or in the future
59-
require(expirations[_sender][msg.sender] == 0 || expirations[_sender][msg.sender] >= _now());
61+
require(expirations[_sender][msg.sender] == 0 || expirations[_sender][msg.sender] >= now);
6062
}
6163
} else {
6264
// If `_sender` is `msg.sender`,
@@ -71,20 +73,16 @@ contract PermittableToken is ERC677BridgeToken {
7173
/// @param _to The address which will spend the funds.
7274
/// @param _value The amount of tokens to be spent.
7375
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-
}
76+
_approveAndResetExpirations(msg.sender, _to, _value);
77+
return true;
7878
}
7979

8080
/// @dev Atomically increases the allowance granted to spender by the caller.
8181
/// @param _to The address which will spend the funds.
8282
/// @param _addedValue The amount of tokens to increase the allowance by.
8383
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-
}
84+
_approveAndResetExpirations(msg.sender, _to, allowed[msg.sender][_to].add(_addedValue));
85+
return true;
8886
}
8987

9088
/// @dev An alias for `transfer` function.
@@ -134,33 +132,107 @@ contract PermittableToken is ERC677BridgeToken {
134132
bytes32 _r,
135133
bytes32 _s
136134
) external {
137-
require(_holder != address(0));
138-
require(_spender != address(0));
139-
require(_expiry == 0 || _now() <= _expiry);
140-
141-
require(_v == 27 || _v == 28);
142-
require(uint256(_s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0);
143-
144-
bytes32 digest = keccak256(
145-
abi.encodePacked(
146-
"\x19\x01",
147-
DOMAIN_SEPARATOR,
148-
keccak256(abi.encode(PERMIT_TYPEHASH, _holder, _spender, _nonce, _expiry, _allowed))
149-
)
150-
);
135+
require(_expiry == 0 || now <= _expiry);
151136

152-
require(_holder == ecrecover(digest, _v, _r, _s));
137+
bytes32 digest = _digest(abi.encode(PERMIT_TYPEHASH_LEGACY, _holder, _spender, _nonce, _expiry, _allowed));
138+
139+
require(_holder == _recover(digest, _v, _r, _s));
153140
require(_nonce == nonces[_holder]++);
154141

155142
uint256 amount = _allowed ? uint256(-1) : 0;
156143

157-
allowed[_holder][_spender] = amount;
158144
expirations[_holder][_spender] = _allowed ? _expiry : 0;
159145

160-
emit Approval(_holder, _spender, amount);
146+
_approve(_holder, _spender, amount);
161147
}
162148

163-
function _now() internal view returns (uint256) {
164-
return now;
149+
/** @dev Allows to spend holder's unlimited amount by the specified spender according to EIP2612.
150+
* The function can be called by anyone, but requires having allowance parameters
151+
* signed by the holder according to EIP712.
152+
* @param _holder The holder's address.
153+
* @param _spender The spender's address.
154+
* @param _value Allowance value to set as a result of the call.
155+
* @param _deadline The deadline timestamp to call the permit function. Must be a timestamp in the future.
156+
* Note that timestamps are not precise, malicious miner/validator can manipulate them to some extend.
157+
* Assume that there can be a 900 seconds time delta between the desired timestamp and the actual expiration.
158+
* @param _v A final byte of signature (ECDSA component).
159+
* @param _r The first 32 bytes of signature (ECDSA component).
160+
* @param _s The second 32 bytes of signature (ECDSA component).
161+
*/
162+
function permit(
163+
address _holder,
164+
address _spender,
165+
uint256 _value,
166+
uint256 _deadline,
167+
uint8 _v,
168+
bytes32 _r,
169+
bytes32 _s
170+
) external {
171+
require(now <= _deadline);
172+
173+
uint256 nonce = nonces[_holder]++;
174+
bytes32 digest = _digest(abi.encode(PERMIT_TYPEHASH, _holder, _spender, _value, nonce, _deadline));
175+
176+
require(_holder == _recover(digest, _v, _r, _s));
177+
178+
_approveAndResetExpirations(_holder, _spender, _value);
179+
}
180+
181+
/**
182+
* @dev Sets a new allowance value for the given owner and spender addresses.
183+
* Resets expiration timestamp in case of unlimited approval.
184+
* @param _owner address tokens holder.
185+
* @param _spender address of tokens spender.
186+
* @param _amount amount of approved tokens.
187+
*/
188+
function _approveAndResetExpirations(address _owner, address _spender, uint256 _amount) internal {
189+
_approve(_owner, _spender, _amount);
190+
191+
// it is not necessary to reset _expirations in other cases, since it is only used together with infinite allowance
192+
if (_amount == uint256(-1)) {
193+
delete expirations[_owner][_spender];
194+
}
195+
}
196+
197+
/**
198+
* @dev Internal function for issuing an allowance.
199+
* @param _owner address of the tokens owner.
200+
* @param _spender address of the approved tokens spender.
201+
* @param _amount amount of the approved tokens.
202+
*/
203+
function _approve(address _owner, address _spender, uint256 _amount) internal {
204+
require(_owner != address(0), "ERC20: approve from the zero address");
205+
require(_spender != address(0), "ERC20: approve to the zero address");
206+
207+
allowed[_owner][_spender] = _amount;
208+
emit Approval(_owner, _spender, _amount);
209+
}
210+
211+
/**
212+
* @dev Calculates the message digest for encoded EIP712 typed struct.
213+
* @param _typedStruct encoded payload.
214+
*/
215+
function _digest(bytes memory _typedStruct) internal view returns (bytes32) {
216+
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, keccak256(_typedStruct)));
217+
}
218+
219+
/**
220+
* @dev Derives the signer address for the given message digest and ECDSA signature params.
221+
* @param _digest signed message digest.
222+
* @param _v a final byte of signature (ECDSA component).
223+
* @param _r the first 32 bytes of the signature (ECDSA component).
224+
* @param _s the second 32 bytes of the signature (ECDSA component).
225+
*/
226+
function _recover(bytes32 _digest, uint8 _v, bytes32 _r, bytes32 _s) internal pure returns (address) {
227+
require(_v == 27 || _v == 28, "ECDSA: invalid signature 'v' value");
228+
require(
229+
uint256(_s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
230+
"ECDSA: invalid signature 's' value"
231+
);
232+
233+
address signer = ecrecover(_digest, _v, _r, _s);
234+
require(signer != address(0), "ECDSA: invalid signature");
235+
236+
return signer;
165237
}
166238
}

0 commit comments

Comments
 (0)