Skip to content

Commit 0c365d1

Browse files
authored
Merge pull request #77 from bancorprotocol/deposit-permitted
Add permitted deposit flows + with minor security issue in PendingWithdrawals
2 parents ccc78d5 + ac9594a commit 0c365d1

File tree

8 files changed

+794
-413
lines changed

8 files changed

+794
-413
lines changed

packages/v3/contracts/helpers/TestERC20Token.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
pragma solidity 0.7.6;
33

44
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5+
import { ERC20Permit } from "@openzeppelin/contracts/drafts/ERC20Permit.sol";
56

6-
contract TestERC20Token is ERC20 {
7+
contract TestERC20Token is ERC20Permit {
78
constructor(
89
string memory name,
910
string memory symbol,
1011
uint256 totalSupply
11-
) ERC20(name, symbol) {
12+
) ERC20(name, symbol) ERC20Permit(name) {
1213
_mint(msg.sender, totalSupply);
1314
}
1415

packages/v3/contracts/network/BancorNetwork.sol

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pragma abicoder v2;
55
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
66
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
77
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8+
import { IERC20Permit } from "@openzeppelin/contracts/drafts/IERC20Permit.sol";
89
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
910
import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
1011

@@ -541,8 +542,8 @@ contract BancorNetwork is IBancorNetwork, Upgradeable, OwnedUpgradeable, Reentra
541542
external
542543
payable
543544
override
544-
validAddress(address(pool))
545545
validAddress(provider)
546+
validAddress(address(pool))
546547
greaterThanZero(tokenAmount)
547548
nonReentrant
548549
{
@@ -563,15 +564,42 @@ contract BancorNetwork is IBancorNetwork, Upgradeable, OwnedUpgradeable, Reentra
563564
_depositFor(msg.sender, pool, tokenAmount, msg.sender);
564565
}
565566

567+
/**
568+
* @inheritdoc IBancorNetwork
569+
*/
570+
function depositForPermitted(
571+
address provider,
572+
IReserveToken pool,
573+
uint256 tokenAmount,
574+
uint256 deadline,
575+
uint8 v,
576+
bytes32 r,
577+
bytes32 s
578+
) external override validAddress(provider) validAddress(address(pool)) greaterThanZero(tokenAmount) nonReentrant {
579+
_depositBaseTokenForPermitted(provider, pool, tokenAmount, msg.sender, deadline, v, r, s);
580+
}
581+
582+
/**
583+
* @inheritdoc IBancorNetwork
584+
*/
585+
function depositPermitted(
586+
IReserveToken pool,
587+
uint256 tokenAmount,
588+
uint256 deadline,
589+
uint8 v,
590+
bytes32 r,
591+
bytes32 s
592+
) external override validAddress(address(pool)) greaterThanZero(tokenAmount) nonReentrant {
593+
_depositBaseTokenForPermitted(msg.sender, pool, tokenAmount, msg.sender, deadline, v, r, s);
594+
}
595+
566596
/**
567597
* @inheritdoc IBancorNetwork
568598
*/
569599
function withdraw(uint256 id) external override nonReentrant {
600+
bytes32 contextId = _withdrawContextId(id);
570601
address provider = msg.sender;
571602

572-
// generate context ID for monitoring
573-
bytes32 contextId = keccak256(abi.encodePacked(provider, _time(), id));
574-
575603
// complete the withdrawal and claim the locked pool tokens
576604
CompletedWithdrawal memory completedRequest = _pendingWithdrawals.completeWithdrawal(contextId, provider, id);
577605

@@ -614,6 +642,25 @@ contract BancorNetwork is IBancorNetwork, Upgradeable, OwnedUpgradeable, Reentra
614642
);
615643
}
616644

645+
/**
646+
* @dev generates context ID for a deposit requesst
647+
*/
648+
function _depositContextId(
649+
address provider,
650+
IReserveToken pool,
651+
uint256 tokenAmount,
652+
address sender
653+
) private view returns (bytes32) {
654+
return keccak256(abi.encodePacked(provider, _time(), pool, tokenAmount, sender));
655+
}
656+
657+
/**
658+
* @dev generates context ID for a withdraw request
659+
*/
660+
function _withdrawContextId(uint256 id) private view returns (bytes32) {
661+
return keccak256(abi.encodePacked(msg.sender, _time(), id));
662+
}
663+
617664
/**
618665
* @dev deposits liquidity for the specified provider from sender
619666
*
@@ -627,8 +674,7 @@ contract BancorNetwork is IBancorNetwork, Upgradeable, OwnedUpgradeable, Reentra
627674
uint256 tokenAmount,
628675
address sender
629676
) private {
630-
// generate context ID for monitoring
631-
bytes32 contextId = keccak256(abi.encodePacked(provider, _time(), pool, tokenAmount, sender));
677+
bytes32 contextId = _depositContextId(provider, pool, tokenAmount, sender);
632678

633679
if (pool == IReserveToken(address(_networkToken))) {
634680
_depositNetworkTokenFor(contextId, provider, tokenAmount, sender);
@@ -785,6 +831,28 @@ contract BancorNetwork is IBancorNetwork, Upgradeable, OwnedUpgradeable, Reentra
785831
});
786832
}
787833

834+
function _depositBaseTokenForPermitted(
835+
address provider,
836+
IReserveToken pool,
837+
uint256 tokenAmount,
838+
address sender,
839+
uint256 deadline,
840+
uint8 v,
841+
bytes32 r,
842+
bytes32 s
843+
) private {
844+
// neither the network token nor ETH support EIP2612 permit requests
845+
require(pool != IReserveToken(address(_networkToken)) && !pool.isNativeToken(), "ERR_PERMIT_UNSUPPORTED");
846+
847+
// permit the amount the caller is trying to deposit. Please note, that if the base token doesn't support
848+
// EIP2612 permit - either this call of the inner safeTransferFrom will revert
849+
IERC20Permit(address(pool)).permit(sender, address(this), tokenAmount, deadline, v, r, s);
850+
851+
bytes32 contextId = _depositContextId(provider, pool, tokenAmount, sender);
852+
853+
_depositBaseTokenFor(contextId, provider, pool, tokenAmount, sender);
854+
}
855+
788856
/**
789857
* @dev handles network token withdrawal
790858
*/

packages/v3/contracts/network/PendingWithdrawals.sol

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,17 @@ contract PendingWithdrawals is
250250
/**
251251
* @inheritdoc IPendingWithdrawals
252252
*/
253-
function initWithdrawalDelegated(
253+
function initWithdrawalPermitted(
254254
IPoolToken poolToken,
255255
uint256 poolTokenAmount,
256-
address provider,
257256
uint256 deadline,
258257
uint8 v,
259258
bytes32 r,
260259
bytes32 s
261260
) external override validAddress(address(poolToken)) greaterThanZero(poolTokenAmount) nonReentrant {
262-
poolToken.permit(provider, address(this), poolTokenAmount, deadline, v, r, s);
261+
poolToken.permit(msg.sender, address(this), poolTokenAmount, deadline, v, r, s);
263262

264-
_initWithdrawal(provider, poolToken, poolTokenAmount);
263+
_initWithdrawal(msg.sender, poolToken, poolTokenAmount);
265264
}
266265

267266
/**

packages/v3/contracts/network/interfaces/IBancorNetwork.sol

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,41 @@ interface IBancorNetwork is IUpgradeable {
128128
*/
129129
function deposit(IReserveToken pool, uint256 tokenAmount) external payable;
130130

131+
/**
132+
* @dev deposits liquidity for the specified provider by providing an EIP712 typed signature for an EIP2612 permit
133+
* request
134+
*
135+
* requirements:
136+
*
137+
* - the caller must have provided a valid and unused EIP712 typed signature
138+
*/
139+
function depositForPermitted(
140+
address provider,
141+
IReserveToken pool,
142+
uint256 tokenAmount,
143+
uint256 deadline,
144+
uint8 v,
145+
bytes32 r,
146+
bytes32 s
147+
) external;
148+
149+
/**
150+
* @dev deposits liquidity by providing an EIP712 typed signature for an EIP2612 permit
151+
* request
152+
*
153+
* requirements:
154+
*
155+
* - the caller must have provided a valid and unused EIP712 typed signature
156+
*/
157+
function depositPermitted(
158+
IReserveToken pool,
159+
uint256 tokenAmount,
160+
uint256 deadline,
161+
uint8 v,
162+
bytes32 r,
163+
bytes32 s
164+
) external;
165+
131166
/**
132167
* @dev withdraws liquidity
133168
*

packages/v3/contracts/network/interfaces/IPendingWithdrawals.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,9 @@ interface IPendingWithdrawals is IUpgradeable {
7979
*
8080
* - the caller must have provided a valid and unused EIP712 typed signature
8181
*/
82-
function initWithdrawalDelegated(
82+
function initWithdrawalPermitted(
8383
IPoolToken poolToken,
8484
uint256 poolTokenAmount,
85-
address provider,
8685
uint256 deadline,
8786
uint8 v,
8887
bytes32 r,

packages/v3/test/helpers/Utils.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
2+
import { BaseContract, BigNumber, BigNumberish, ContractTransaction, Wallet } from 'ethers';
3+
import { ethers, waffle } from 'hardhat';
14
import Contracts from '../../components/Contracts';
25
import { TestERC20Token } from '../../typechain';
36
import { NATIVE_TOKEN_ADDRESS } from './Constants';
47
import { toWei } from './Types';
5-
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
6-
import { BigNumber, BigNumberish, ContractTransaction, BaseContract } from 'ethers';
7-
import { ethers } from 'hardhat';
88

99
export type TokenWithAddress = TestERC20Token | { address: string };
1010

@@ -66,3 +66,12 @@ export const createTokenBySymbol = async (symbol: string, networkToken: TestERC2
6666
throw new Error(`Unsupported type ${symbol}`);
6767
}
6868
};
69+
70+
export const createWallet = async () => {
71+
// create a random wallet, connect it to a test provider, and fund it
72+
const wallet = Wallet.createRandom().connect(waffle.provider);
73+
const deployer = (await ethers.getSigners())[0];
74+
await deployer.sendTransaction({ value: toWei(BigNumber.from(10)), to: await wallet.getAddress() });
75+
76+
return wallet;
77+
};

0 commit comments

Comments
 (0)