Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

profitability check general tool #1984

Draft
wants to merge 78 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
21d28ff
initail commit
sparrowDom Jun 14, 2023
ca4b9c7
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Jun 29, 2023
bd17a59
intermediary commit
sparrowDom Jun 30, 2023
e218397
commit research files
sparrowDom Jul 3, 2023
e40539a
balancer booster abi
sparrowDom Jul 4, 2023
fddfc96
intermittent commit
sparrowDom Jul 5, 2023
4d61080
add base balancer contract that implements checkBalance functionality
sparrowDom Jul 11, 2023
c941766
add some additional initial integration
sparrowDom Jul 12, 2023
4e2a0ac
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Jul 12, 2023
a163e8f
intermittent commit
sparrowDom Jul 14, 2023
6322507
add deployment file
sparrowDom Jul 14, 2023
5150986
add fork test fixture
sparrowDom Jul 14, 2023
fa5838b
intermittent commit
sparrowDom Jul 18, 2023
74f1403
prettier
sparrowDom Jul 20, 2023
10b6fdc
add basic withdrawal / deposit functionality
sparrowDom Jul 21, 2023
807e8c4
correct the BPT calculation
sparrowDom Jul 21, 2023
8f16b4e
prettier + lint
sparrowDom Jul 23, 2023
1c782a5
simplify the BPT price calculation
sparrowDom Jul 23, 2023
faa7eb5
add read-only re-entrancy protection
sparrowDom Jul 23, 2023
c6b45c3
add some missing tests and adjust existing. Deparate deposit and with…
sparrowDom Jul 24, 2023
c77a296
fix check balance implementation
sparrowDom Jul 24, 2023
75329db
Balancer review changes (#1726)
naddison36 Jul 31, 2023
ea68019
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
naddison36 Aug 1, 2023
5607b5d
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
naddison36 Aug 1, 2023
012a83d
[ DFD-1 ] Balancer's checkBalance (#1730)
sparrowDom Aug 2, 2023
b120330
Balancer fork tests (#1727)
naddison36 Aug 2, 2023
39a7b3f
Add read-only reentrancy test (#1731)
shahthepro Aug 2, 2023
346d949
Balancer fixes (#1734)
sparrowDom Aug 3, 2023
fbafb80
Balancer withdrawal fix (#1739)
sparrowDom Aug 5, 2023
430fbad
use only 1 safeApprove when applicable
sparrowDom Aug 5, 2023
3ec984c
some renames and more correct application of approves
sparrowDom Aug 5, 2023
ecceeb1
renames, additional requires, move initializer to a better location, …
sparrowDom Aug 6, 2023
eb11498
bug fix
sparrowDom Aug 6, 2023
a25a661
Generated latest Balancer strategy diagrams
naddison36 Aug 7, 2023
b9dd480
re-deploy BPT tokens sitting in the strategy
sparrowDom Aug 7, 2023
3af8527
Merge branch 'master' into sparrowDom/balancer-sfrxETH-stETH-rETH
sparrowDom Aug 9, 2023
3767bb2
fix re-entrancy test
sparrowDom Aug 10, 2023
3cdbdba
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Aug 16, 2023
c69369e
fixture fix
sparrowDom Aug 17, 2023
8a26a4e
bug fix
sparrowDom Aug 17, 2023
afb2d67
prettier
sparrowDom Aug 17, 2023
3ae0892
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Aug 17, 2023
b321712
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Aug 23, 2023
34608a7
L02 improve naming (#1783)
sparrowDom Aug 28, 2023
a323447
do a check that supported assets are being withdrawn (#1784)
sparrowDom Aug 28, 2023
7d8c3fa
set uint256 max instead of magic number (#1782)
sparrowDom Aug 28, 2023
d4eef49
remove unused files (#1785)
sparrowDom Aug 28, 2023
4d36852
fix renaming bug
sparrowDom Aug 29, 2023
d206ce4
correct safe approve all tokens and adjust the documentation (#1776)
sparrowDom Aug 30, 2023
b127355
prettier
sparrowDom Aug 30, 2023
6ad405c
M04 - minBptFunction robustness (#1795)
sparrowDom Aug 30, 2023
04b3010
M02 withdrawal fuzzing tests (#1801)
sparrowDom Aug 30, 2023
852afa4
N02 gas inefficiencies (#1786)
sparrowDom Aug 30, 2023
abf482f
remove todo comments (#1796)
sparrowDom Aug 30, 2023
d159fbe
use a more appropriate array initialisation length (#1800)
sparrowDom Aug 30, 2023
e09a9b9
more consistant function naming (#1797)
sparrowDom Aug 30, 2023
daeab91
fix typo (#1798)
sparrowDom Aug 30, 2023
a2f8fcd
simplify the way we withdrawAll. no need to pass along min amonts (#1…
sparrowDom Aug 30, 2023
cf4122a
M03 - missing or misleading documentation (#1781)
sparrowDom Aug 30, 2023
fcc08f7
M01 More comprehensive test suite (#1780)
sparrowDom Aug 31, 2023
5a338d6
fix bad merge + prettier & lint
sparrowDom Aug 31, 2023
d2c0039
fix fork tests remove .only
sparrowDom Aug 31, 2023
70b4481
remove only
sparrowDom Aug 31, 2023
71332e5
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Aug 31, 2023
900e6d0
lint
sparrowDom Aug 31, 2023
38a7e86
fix unit tests
sparrowDom Aug 31, 2023
3fd8f23
add more tests to see how checkBalance behaves
sparrowDom Aug 31, 2023
88bfd30
remove console log
sparrowDom Aug 31, 2023
b7e0dc3
improve checkBalance test by testing that checkBalance amount doesn't…
sparrowDom Aug 31, 2023
a0cb919
confirm that yield gained by 3rd party tilting the pool can be extrac…
sparrowDom Sep 6, 2023
0f02fad
rename internal functions by prepending them with underscore
sparrowDom Sep 11, 2023
e95315a
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Sep 11, 2023
3019b49
Generated latest Balancer strategy diagrams (#1805)
naddison36 Sep 11, 2023
6b5a2b4
bug fix
sparrowDom Sep 11, 2023
09684fd
bug fix
sparrowDom Sep 11, 2023
c1e1cf2
correct script to balance the pool
sparrowDom Sep 13, 2023
23b8570
Merge remote-tracking branch 'origin/master' into sparrowDom/balancer…
sparrowDom Jan 30, 2024
73a26e0
intermediary work
sparrowDom Feb 1, 2024
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
Prev Previous commit
Next Next commit
Balancer withdrawal fix (#1739)
* fix balancer withdrawals

* lint

* prettier
  • Loading branch information
sparrowDom authored Aug 5, 2023
commit fbafb803749c7f8550b2afc96bbf263b6a99f399
20 changes: 20 additions & 0 deletions brownie/abi/balancerUserData.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,25 @@
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "joinKind",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "minBptAmountout",
"type": "uint256"
}
],
"name": "userDataExactBPTinForTokensOut",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]
66 changes: 52 additions & 14 deletions brownie/balancer_check_balance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,31 +77,30 @@ def redeem(amount):
oeth_vault_core.redeem(amount*1e18, amount*1e18*0.95, WSTD)
oeth_vault_core.rebase(WSTD)

def deposit_withdrawal_test(amount, print_states = False):
def deposit_withdrawal_test(amount, _outputAmount, print_states = False):
print_state("initial state", print_states)

# mint(400, weth)
# print_state("whale minted", print_states)

vault_value_checker.takeSnapshot(STD)

weth_balance_before_whale = weth.balanceOf(weth_whale)
# Enter the pool
ba_vault.joinPool(
amountsIn = [amount * 10**18, amount * 10**18]
tx_join = ba_vault.joinPool(
pool_id,
weth_whale, #sender
weth_whale, #recipient
[
# tokens need to be sorted numerically
[reth.address, weth.address], # assets
# indexes match above assets
[0, amount * 10**18], # min amounts in
balancerUserDataEncoder.userDataExactTokenInForBPTOut.encode_input(1, [0, amount * 10**18], amount * 10**18 * 0.85)[10:],
amountsIn, # min amounts in
balancerUserDataEncoder.userDataExactTokenInForBPTOut.encode_input(1, amountsIn, amount * 10**18 * 0.9)[10:],
False, #fromInternalBalance
],
WSTD
)

print_state("after manipulation", print_states)

## attempt to mint - fails with Bal208 -> BPT_OUT_MIN_AMOUNT (Slippage/front-running protection check failed on a pool join)
Expand All @@ -112,7 +111,38 @@ def deposit_withdrawal_test(amount, print_states = False):
bpt_balance = pool.balanceOf(weth_whale)
pool.approve(ba_vault, 10**50, WSTD)

ba_vault.exitPool(
# EXACT_BPT_IN_FOR_ONE_TOKEN_OUT - single asset exit
# user data [EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, bptAmountIn, exitTokenIndex]
# ba_vault.exitPool(
# pool_id,
# weth_whale, #sender
# weth_whale, #recipient
# [
# # tokens need to be sorted numerically
# # we should account for some slippage here since it comes down to balance amounts in the pool
# [reth.address, weth.address], # assets
# #[0, 177_972 * 10**18], # min amounts out
# [0, 0], # min amounts out - no MEWS on local network no need to calculate exaclty
# balancerUserDataEncoder.userDataTokenInExactBPTOut.encode_input(0, bpt_balance, 1)[10:],
# False, #fromInternalBalance
# ],
# WSTD
# )

# BPT_IN_FOR_EXACT_TOKENS_OUT
# User sends an estimated but unknown (computed at run time) quantity of BPT, and receives precise quantities of specified tokens
# user data [BPT_IN_FOR_EXACT_TOKENS_OUT, amountsOut, maxBPTAmountIn]
# weth_exit_before = weth.balanceOf(weth_whale)
# reth_exit_before = reth.balanceOf(weth_whale)

# bpt_balance = 9742858928635020293
#outputAmounts = [_outputAmount * 10**18, _outputAmount * 10**18]
outputAmounts = [_outputAmount * 10**18, 0]
# print("OUTPUT amounts")
# print(outputAmounts[0])
# print(outputAmounts[1])
bpt_balance = _outputAmount * 10**18 * 1.15
tx_exit = ba_vault.exitPool(
pool_id,
weth_whale, #sender
weth_whale, #recipient
Expand All @@ -121,24 +151,32 @@ def deposit_withdrawal_test(amount, print_states = False):
# we should account for some slippage here since it comes down to balance amounts in the pool
[reth.address, weth.address], # assets
#[0, 177_972 * 10**18], # min amounts out
[0, 0], # min amounts out - no MEWS on local network no need to calculate exaclty
balancerUserDataEncoder.userDataTokenInExactBPTOut.encode_input(0, bpt_balance, 1)[10:],
#outputAmounts, # min amounts out - no MEWS on local network no need to calculate exaclty
[_outputAmount * 10**18 - 10**9,0],
balancerUserDataEncoder.userDataExactTokenInForBPTOut.encode_input(2, outputAmounts, bpt_balance)[10:],
False, #fromInternalBalance
],
WSTD
)
print("user data", balancerUserDataEncoder.userDataExactTokenInForBPTOut.encode_input(2, outputAmounts, bpt_balance)[10:])

weth_balance_diff_whale = (weth_balance_before_whale - weth.balanceOf(weth_whale))/weth_balance_before_whale

vault_value_checker.checkDelta(0, 0.5*10**18, 0, 0.5*10**18, STD)
print_state("after exit", print_states)
print("weth_balance_diff_whale", weth_balance_diff_whale)

# weth_exit_diff = weth.balanceOf(weth_whale) - weth_exit_before
# reth_exit_diff = reth.balanceOf(weth_whale) - reth_exit_before
# print("weth_exit_diff", weth_exit_diff)
# print("reth_exit_diff", reth_exit_diff)
return {
"whale_weth_diff": weth_balance_diff_whale
"whale_weth_diff": weth_balance_diff_whale,
"tx_exit": tx_exit,
"tx_join": tx_join,
}

with TemporaryFork():
stats = deposit_withdrawal_test(200_000, True)
#stats = deposit_withdrawal_test(10, True)
stats = deposit_withdrawal_test(10, 0.8123, True)

# plot results
# import matplotlib.pyplot as plt
Expand Down
8 changes: 0 additions & 8 deletions contracts/contracts/mocks/MockEvilReentrantContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { IERC20 } from "../utils/InitializableAbstractStrategy.sol";

import { StableMath } from "../utils/StableMath.sol";

import "hardhat/console.sol";

contract MockEvilReentrantContract {
using StableMath for uint256;

Expand Down Expand Up @@ -72,7 +70,6 @@ contract MockEvilReentrantContract {
);

uint256 bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));
console.log("BPT Token balance: %s", bptTokenBalance);

// 2. Redeem as ETH
bytes memory exitUserData = abi.encode(
Expand All @@ -94,7 +91,6 @@ contract MockEvilReentrantContract {
exitRequest
);
bptTokenBalance = IERC20(poolAddress).balanceOf(address(this));
console.log("BPT Token balance: %s", bptTokenBalance);
}

function getBPTExpected(address[] memory _assets, uint256[] memory _amounts)
Expand Down Expand Up @@ -131,11 +127,7 @@ contract MockEvilReentrantContract {
}

receive() external payable {
console.log("Received ETH");

// 3. Try to mint OETH
oethVault.mint(address(weth), 1 ether, 0.9 ether);

console.log("You shouldn't see me!!!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,19 @@ contract BalancerMetaPoolStrategy is BaseAuraStrategy {
_amounts[j]
);
wrappedAssetAmounts[j] = poolAmountsOut[i];

/* Because of the potential Balancer rounding error mentioned below
* the contract might receive 1-2 WEI smaller amount than required
* in the withdraw user data encoding. If slightly lesser token amount
* is received the strategy can not unwrap the pool asset as it is
* smaller than expected.
*
* For that reason we `overshoot` the required tokens expected to
* circumvent the error
*/
if (poolAmountsOut[i] > 0) {
poolAmountsOut[i] += 2;
}
}
}
}
Expand All @@ -267,7 +280,19 @@ contract BalancerMetaPoolStrategy is BaseAuraStrategy {
);

IBalancerVault.ExitPoolRequest memory request = IBalancerVault
.ExitPoolRequest(poolAssets, poolAmountsOut, userData, false);
.ExitPoolRequest(
poolAssets,
/* We specify the exact amount of a tokens we are expecting in the encoded
* userData, for that reason we don't need to specify the amountsOut here.
*
* Also Balancer has a rounding issue that can make a transaction fail:
* https://github.com/balancer/balancer-v2-monorepo/issues/2541
* which is an extra reason why this field is empty.
*/
new uint256[](tokens.length),
userData,
false
);

balancerVault.exitPool(
balancerPoolId,
Expand Down
26 changes: 12 additions & 14 deletions contracts/contracts/strategies/balancer/BaseBalancerStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,32 +121,30 @@ abstract contract BaseBalancerStrategy is InitializableAbstractStrategy {
whenNotInVaultContext
returns (uint256 amount)
{
// Get the total balance of each of the Balancer pool assets
(IERC20[] memory tokens, , ) = balancerVault.getPoolTokens(
balancerPoolId
);

uint256 bptBalance = _getBalancerPoolTokens();

/* To calculate the worth of queried asset in accordance with pool token
* rates (provided by asset rateProvider)
* - convert complete balance of BPT to underlying tokens ETH denominated amount
* - take in consideration pool's tokens and their exchangeRates. For the queried asset
* deduce what a share of that asset should be in the pool in case of pool being
* completely balanced. To get to this we are using inverted exchange rate accumulator
* and queried asset inverted exchange rate.
* - divide the amount of the previous step with assetRate to convert the ETH
* denominated representation to asset denominated
/* To calculate the worth of queried asset:
* - assume that all tokens normalized to their ETH value have an equal split balance
* in the pool when it is balanced
* - multiply the BPT amount with the bpt rate to get the ETH denominated amount
* of strategy's holdings
* - divide that by the number of tokens in the pool to get ETH denominated amount
* that is applicable to each token in the pool
*/
amount = (bptBalance.mulTruncate(
IRateProvider(platformAddress).getRate()
) / tokens.length);

/* If pool asset is equals _asset it means a rate provider for that asset
* exists and that asset is not necessarily pegged to a unit (ETH).
/* If the pool asset is equal to (strategy )_asset it means that a rate
* provider for that asset exists and that asset is not necessarily
* pegged to a unit (ETH).
*
* Because this function returns the balance of the asset not denominated in
* ETH units we need to convert the amount to asset amount.
* Because this function returns the balance of the asset and is not denominated in
* ETH units we need to convert the ETH denominated amount to asset amount.
*/
if (toPoolAsset(_asset) == _asset) {
amount = amount.divPrecisely(getRateProviderRate(_asset));
Expand Down
5 changes: 5 additions & 0 deletions contracts/test/_fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,11 @@ function balancerWstEthFixtureSetup() {
);
fixture.balancerWstEthPID = balancer_stETH_WETH_PID;

fixture.auraPool = await ethers.getContractAt(
"IERC4626",
addresses.mainnet.wstETH_WETH_AuraRewards
);

fixture.balancerVault = await ethers.getContractAt(
"IBalancerVault",
addresses.mainnet.balancerVault,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,15 @@ forkOnlyDescribe(
.depositToStrategy(
balancerWstEthStrategy.address,
[weth.address, stETH.address],
[oethUnits("25"), oethUnits("25")]
[units("25", weth), oethUnits("25")]
);

// TODO: Check slippage errors
await balancerWstEthStrategy
.connect(strategist)
.setMaxWithdrawalSlippage(oethUnits("0.01"));
});

it("Should be able to withdraw 10 WETH from the pool", async function () {
const { weth, balancerWstEthStrategy, oethVault } = fixture;

Expand All @@ -614,6 +615,7 @@ forkOnlyDescribe(
);
expect(wethBalanceDiffVault).to.approxEqualTolerance(withdrawAmount, 1);
});

it("Should be able to withdraw 8 stETH from the pool", async function () {
const { stETH, balancerWstEthStrategy, oethVault } = fixture;

Expand Down
4 changes: 2 additions & 2 deletions contracts/utils/funding.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ const fundAccounts = async () => {

const ousdCoins = [dai, usdc, usdt, tusd, ogn];
const oethCoins = [weth, rETH, stETH, frxETH];
const skipOUSDCoins = !!process.env.SKIP_OUSD_COINS;
const skipOETHCoins = !!process.env.SKIP_OETH_COINS;
const skipOUSDCoins = process.env.SKIP_OUSD_COINS == "true";
const skipOETHCoins = process.env.SKIP_OETH_COINS == "true";
let allCoins = [];
if (!skipOUSDCoins) {
allCoins = [...allCoins, ...ousdCoins];
Expand Down