Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e0b6753
Remove eslint addons
Jan 12, 2021
d96a5c0
Bump ganache-cli
Jan 12, 2021
f44bc96
Move test contracts into common/v1
Jan 12, 2021
0dcfa09
Remove unused function arg in migration scripts
Jan 12, 2021
0fb14a6
Add exitPool to AbstractPool contract
Jan 12, 2021
c6a2e59
Add v2 BPool and Vault mocks
Jan 12, 2021
eff3dd9
Add 'migrate' action
Jan 12, 2021
32af8ad
Test migration
Jan 12, 2021
bbad60a
Remove var docstring
Jan 14, 2021
cb6fd24
Switch Vault token balances to uint256
Jan 14, 2021
71c9f7d
Add LogExpMath lib
Jan 14, 2021
513b9c2
Use unproportional join for migration
Jan 14, 2021
fbf7759
Handle different token order in migration pool
Jan 22, 2021
293383d
Use uint128 for v2 pool amounts
Jan 27, 2021
9bc25a4
Remove redundant whitespace
Feb 3, 2021
a9f04af
Switch to latest v2 interface
Feb 3, 2021
c8e55ee
Add proportional migration
Feb 3, 2021
7ee60c1
Fix v1 tokens used instead of v2
Feb 3, 2021
11d5bbb
Send missing tokens back during full migration
Feb 10, 2021
7fb638f
Change Vault's pool token interface
Feb 12, 2021
c71c6b2
Fix "exact token in join" param encoding
Feb 16, 2021
88320a2
Update Vault joinPool interface
Apr 2, 2021
139dcb2
Update join pool vault interface
Apr 15, 2021
8c30c71
Remove unused variable
Apr 22, 2021
24216bf
Keep original amount for bottleneck to avoid dust
Apr 26, 2021
60b5344
Don't try to send change back for full migration
May 10, 2021
22daf7e
Check that tokens on both pools are the same
May 10, 2021
6f304ea
Strip out some revert reasons to reduce bytecode
May 10, 2021
c5a6e91
Check that both pools have 2 tokens
May 10, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"extends": [
"airbnb"
"eslint:recommended"
],
"rules": {
"indent": ["error", 4],
Expand Down
7 changes: 4 additions & 3 deletions .solcover.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module.exports = {
port: 8555,
skipFiles: [
'Migrations.sol',
'DSProxyFactory.sol',
'test'
'test',
'common',
'v1',
'v2',
],
testrpcOptions: "-p 8555 -d"
};
137 changes: 135 additions & 2 deletions contracts/BActions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ abstract contract AbstractPool is ERC20, BalancerOwnable {
function joinswapExternAmountIn(
address tokenIn, uint tokenAmountIn, uint minPoolAmountOut
) external virtual returns (uint poolAmountOut);
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external virtual;
}

abstract contract BPool is AbstractPool {
Expand All @@ -51,6 +52,29 @@ abstract contract BFactory {
function newBPool() external virtual returns (BPool);
}

abstract contract BalancerPool is ERC20 {
function getPoolId() external view virtual returns (bytes32);

enum JoinKind { INIT, EXACT_TOKENS_IN_FOR_BPT_OUT }
}

abstract contract Vault {
struct JoinPoolRequest {
address[] assets;
uint256[] maxAmountsIn;
bytes userData;
bool fromInternalBalance;
}

function joinPool(
bytes32 poolId,
address sender,
address recipient,
JoinPoolRequest calldata request
) external virtual;
function getPoolTokens(bytes32 poolId) external view virtual returns (address[] memory, uint[] memory, uint256);
}

abstract contract ConfigurableRightsPool is AbstractPool {
struct PoolParams {
string poolTokenSymbol;
Expand Down Expand Up @@ -347,16 +371,125 @@ contract BActions {
) external {
crp.removeWhitelistedLiquidityProvider(provider);
}

// --- Migration ---

function migrateProportionally(
Vault vault,
BPool poolIn,
uint poolInAmount,
uint[] calldata tokenOutAmountsMin,
BalancerPool poolOut,
uint poolOutAmountMin
) external {
address[] memory tokens = poolIn.getFinalTokens();
(address[] memory outTokens, uint[] memory tokenInAmounts,) =
vault.getPoolTokens(poolOut.getPoolId());
require(tokens.length == 2);
require(outTokens.length == 2);
require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1]));
require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1]));
// Transfer v1 BPTs to proxy
poolIn.transferFrom(msg.sender, address(this), poolInAmount);
// Exit v1 pool
poolIn.exitPool(poolInAmount, tokenOutAmountsMin);
// Approve each token to v2 vault
for (uint i = 0; i < tokens.length; i++) {
_safeApprove(ERC20(tokens[i]), address(vault), uint(-1));
}
// Calculate amounts for even join
// 1) find the lowest UserBalance-to-PoolBalance ratio
// 2) multiply by this ratio to get in amounts
uint lowestRatio = uint(-1);
uint lowestRatioToken = 0;
for (uint i = 0; i < outTokens.length; ++i) {
uint ratio = 1 ether * ERC20(outTokens[i]).balanceOf(address(this)) / tokenInAmounts[i];
if (ratio < lowestRatio) {
lowestRatio = ratio;
lowestRatioToken = i;
}
}
for (uint i = 0; i < outTokens.length; ++i) {
// Keep original amount for "bottleneck" token to avoid dust
if (lowestRatioToken == i) {
tokenInAmounts[i] = ERC20(outTokens[i]).balanceOf(address(this));
} else {
tokenInAmounts[i] = tokenInAmounts[i] * lowestRatio / 1 ether;
}
}
// Join v2 pool and transfer v2 BPTs to user
bytes memory userData = abi.encode(
BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,
tokenInAmounts,
poolOutAmountMin
);
Vault.JoinPoolRequest memory request = Vault.JoinPoolRequest(outTokens, tokenInAmounts, userData, false);
vault.joinPool(
poolOut.getPoolId(),
address(this),
msg.sender,
request
);
// Send "change" back
for (uint i = 0; i < tokens.length; i++) {
ERC20 token = ERC20(tokens[i]);
if (token.balanceOf(address(this)) > 0) {
require(token.transfer(msg.sender, token.balanceOf(address(this))), "ERR_TRANSFER_FAILED");
}
}
}

function migrateAll(
Vault vault,
BPool poolIn,
uint poolInAmount,
uint[] calldata tokenOutAmountsMin,
BalancerPool poolOut,
uint poolOutAmountMin
) external {
address[] memory tokens = poolIn.getFinalTokens();
(address[] memory outTokens,,) = vault.getPoolTokens(poolOut.getPoolId());
require(tokens.length == 2);
require(outTokens.length == 2);
require((tokens[0] == outTokens[0]) || (tokens[0] == outTokens[1]));
require((tokens[1] == outTokens[0]) || (tokens[1] == outTokens[1]));
// Transfer v1 BPTs to proxy
poolIn.transferFrom(msg.sender, address(this), poolInAmount);
// Exit v1 pool
poolIn.exitPool(poolInAmount, tokenOutAmountsMin);
// Approve each token to v2 vault
for (uint i = 0; i < tokens.length; i++) {
_safeApprove(ERC20(tokens[i]), address(vault), uint(-1));
}
// Join v2 pool and transfer v2 BPTs to user
uint[] memory tokenInAmounts = new uint[](outTokens.length);
for (uint i = 0; i < outTokens.length; ++i) {
tokenInAmounts[i] = ERC20(outTokens[i]).balanceOf(address(this));
}

bytes memory userData = abi.encode(
BalancerPool.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,
tokenInAmounts,
poolOutAmountMin
);
Vault.JoinPoolRequest memory request = Vault.JoinPoolRequest(outTokens, tokenInAmounts, userData, false);
vault.joinPool(
poolOut.getPoolId(),
address(this),
msg.sender,
request
);
}

// --- Internals ---

function _safeApprove(ERC20 token, address spender, uint amount) internal {
if (token.allowance(address(this), spender) > 0) {
token.approve(spender, 0);
}
token.approve(spender, amount);
}

function _join(
AbstractPool pool,
address[] memory tokens,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 0 additions & 4 deletions contracts/test/BMath.sol → contracts/v1/BMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ contract BMath is BBronze, BConst, BNum {
public pure
returns (uint poolAmountOut)
{
// Charge the trading fee for the proportion of tokenAi
/// which is implicitly traded to the other pool tokens.
// That proportion is (1- weightTokenIn)
// tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import "./BalancerOwnable.sol";
// Interfaces

// Libraries
import "../common/SafeApprove.sol";
import { RightsManager } from "./RightsManager.sol";
import "./SmartPoolManager.sol";
import "./SafeApprove.sol";

// Contracts

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion contracts/test/PCToken.sol → contracts/v1/PCToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ pragma solidity 0.6.12;

// Imports

import "../common/IERC20.sol";
import "./BalancerSafeMath.sol";
import "./BalancerConstants.sol";
import "./IERC20.sol";

// Contracts

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ pragma experimental ABIEncoderV2;

// Imports

import "./IERC20.sol";
import "../common/IERC20.sol";
import "../common/SafeApprove.sol";
import "./ConfigurableRightsPool.sol";
import "./IBFactory.sol";
import "./BalancerConstants.sol";
import "./BalancerSafeMath.sol";
import "./SafeApprove.sol";


/**
Expand Down
Loading