-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Balancer pool adaptor * review items 1 * Rework pool ID parsing * Fmt * Clippy
- Loading branch information
Showing
18 changed files
with
26,357 additions
and
689 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Protos for function calls to the Balancer Pool adaptor. | ||
*/ | ||
|
||
syntax = "proto3"; | ||
package steward.v3; | ||
|
||
option go_package = "/steward_proto"; | ||
|
||
import "adaptors/base.proto"; | ||
|
||
// Represents call data for the Balancer Pool adaptor V1, for managing pool positions on Balancer. | ||
message BalancerPoolAdaptorV1 { | ||
oneof function { | ||
/***** BASE ADAPTOR FUNCTIONS *****/ | ||
|
||
// Represents function `revokeApproval(ERC20 asset, address spender)` | ||
RevokeApproval revoke_approval = 1; | ||
|
||
/***** ADAPTOR-SPECIFIC FUNCTIONS *****/ | ||
|
||
// Represents function `relayerJoinPool(ERC20[] tokensIn, uint256[] amountsIn, ERC20 btpOut, bytes[] memory callData)` | ||
JoinPool join_pool = 2; | ||
// Represents function `relayerExitPool(ERC20 bptIn, uint256 amountIn, ERC20[] memory tokensOut, bytes[] memory callData)` | ||
ExitPool exit_pool = 3; | ||
// Represents function `stakeBPT(ERC20 _bpt, address _liquidityGauge, uint256 _amountIn)` | ||
StakeBPT stake_bpt = 4; | ||
// Represents function `unstakeBPT(ERC20 _bpt, address _liquidityGauge, uint256 _amountOut)` | ||
UnstakeBPT unstake_bpt = 5; | ||
// Represents function `claimRewards(address gauge)` | ||
ClaimRewards claim_rewards = 6; | ||
|
||
} | ||
|
||
// Represents the SwapKind enum defined here: | ||
// https://github.com/PeggyJV/cellar-contracts/blob/main/src/interfaces/external/Balancer/IVault.sol | ||
enum SwapKind { | ||
SWAP_KIND_UNSPECIFIED = 0; | ||
SWAP_KIND_GIVEN_IN = 1; | ||
SWAP_KIND_GIVEN_OUT = 2; | ||
} | ||
|
||
// Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on the `kind` value. | ||
// Represents the SingleSwap struct defined here: | ||
// https://github.com/PeggyJV/cellar-contracts/blob/main/src/interfaces/external/Balancer/IVault.sol | ||
message SingleSwap { | ||
// The pool ID (bytes32) | ||
string pool_id = 1; | ||
|
||
// The swap kind (enum) | ||
SwapKind kind = 2; | ||
|
||
// The asset in (address) | ||
string asset_in = 3; | ||
|
||
// The asset out (address) | ||
string asset_out = 4; | ||
|
||
// The amount (uint256) | ||
string amount = 5; | ||
|
||
// The user data (bytes) | ||
bytes user_data = 6; | ||
} | ||
|
||
// Stores each swaps min amount, and deadline | ||
message SwapData { | ||
// The minimum amounts for swaps | ||
repeated string min_amounts_for_swaps = 1; | ||
|
||
// The swap deadlines | ||
repeated string swap_deadlines = 2; | ||
} | ||
|
||
/* | ||
* Allows strategists to join Balancer pools using EXACT_TOKENS_IN_FOR_BPT_OUT joins | ||
* | ||
* Represents function `joinPool(ERC20 targetBpt, IVault.SingleSwap[] memory swapsBeforeJoin, SwapData memory swapData, uint256 minimumBpt)` | ||
*/ | ||
message JoinPool { | ||
// The target pool | ||
string target_bpt = 1; | ||
|
||
// Swap to execute before joining pool | ||
repeated SingleSwap swaps_before_join = 2; | ||
|
||
// Data for swaps | ||
SwapData swap_data = 3; | ||
|
||
// The minimum BPT to mint | ||
string minimum_bpt = 4; | ||
} | ||
|
||
|
||
message ExitPoolRequest { | ||
repeated string assets = 1; | ||
repeated string min_amounts_out = 2; | ||
bytes user_data = 3; | ||
bool to_internal_balance = 4; | ||
} | ||
|
||
/* | ||
* Call `BalancerRelayer` on mainnet to carry out exit txs | ||
* | ||
* Represents function `exitPool(ERC20 targetBpt, IVault.SingleSwap[] memory swapsBeforeJoin, SwapData memory swapData, IVault.ExitPoolRequest request)` | ||
*/ | ||
message ExitPool { | ||
// The target pool | ||
string target_bpt = 1; | ||
|
||
// Swaps to execute after exiting pool | ||
repeated SingleSwap swaps_after_exit = 2; | ||
|
||
// Data for swaps | ||
SwapData swap_data = 3; | ||
|
||
ExitPoolRequest request = 4; | ||
} | ||
|
||
/* | ||
* Stake (deposit) BPTs into respective pool gauge | ||
* | ||
* Represents `function stakeBPT(ERC20 _bpt, address _liquidityGauge, uint256 _amountIn)`` | ||
*/ | ||
message StakeBPT { | ||
// The BPT to stake | ||
string bpt = 1; | ||
|
||
// The liquidity gauge to stake into | ||
string liquidity_gauge = 2; | ||
|
||
// The amount to stake | ||
string amount_in = 3; | ||
} | ||
|
||
/* | ||
* Unstake (withdraw) BPT from respective pool gauge | ||
* | ||
* Represents `function unstakeBPT(ERC20 _bpt, address _liquidityGauge, uint256 _amountOut)`` | ||
*/ | ||
message UnstakeBPT { | ||
// The BPT to unstake | ||
string bpt = 1; | ||
|
||
// The liquidity gauge to unstake from | ||
string liquidity_gauge = 2; | ||
|
||
// The amount to unstake | ||
string amount_out = 3; | ||
} | ||
|
||
/* | ||
* Claim rewards ($BAL) from LP position | ||
* | ||
* Represents `function claimRewards(address gauge)` | ||
*/ | ||
message ClaimRewards { | ||
// The gauge to claim rewards from | ||
string gauge = 1; | ||
} | ||
} | ||
|
||
message BalancerPoolAdaptorV1Calls { | ||
repeated BalancerPoolAdaptorV1 calls = 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
use std::convert::TryInto; | ||
|
||
use ethers::{abi::AbiEncode, types::Bytes}; | ||
use steward_abi::balancer_pool_adaptor_v1::{ | ||
BalancerPoolAdaptorV1Calls, ExitPoolRequest, SingleSwap as AbiSingleSwap, | ||
SwapData as AbiSwapData, | ||
}; | ||
use steward_proto::steward::balancer_pool_adaptor_v1::{self, SingleSwap, SwapData}; | ||
|
||
use crate::{ | ||
error::{Error, ErrorKind}, | ||
utils::{sp_call_error, sp_call_parse_address, string_to_u256}, | ||
}; | ||
|
||
pub(crate) fn balancer_pool_adaptor_v1_calls( | ||
params: steward_proto::steward::BalancerPoolAdaptorV1Calls, | ||
) -> Result<Vec<Bytes>, Error> { | ||
let mut calls = Vec::new(); | ||
for c in params.calls { | ||
let function = c | ||
.function | ||
.ok_or_else(|| sp_call_error("function cannot be empty".to_string()))?; | ||
|
||
match function { | ||
balancer_pool_adaptor_v1::Function::RevokeApproval(p) => { | ||
let call = steward_abi::balancer_pool_adaptor_v1::RevokeApprovalCall { | ||
asset: sp_call_parse_address(p.asset)?, | ||
spender: sp_call_parse_address(p.spender)?, | ||
}; | ||
calls.push( | ||
BalancerPoolAdaptorV1Calls::RevokeApproval(call) | ||
.encode() | ||
.into(), | ||
) | ||
} | ||
balancer_pool_adaptor_v1::Function::JoinPool(p) => { | ||
if !p.swaps_before_join.is_empty() | ||
&& p.swaps_before_join.iter().any(|s| s.kind == 0) | ||
{ | ||
return Err(sp_call_error("invalid swap kind".to_string())); | ||
} | ||
|
||
let swaps_before_join = convert_single_swap(p.swaps_before_join)?; | ||
|
||
if p.swap_data.is_none() { | ||
return Err(sp_call_error("swap data must be set".to_string())); | ||
} | ||
|
||
let swap_data = convert_swap_data(p.swap_data.unwrap())?; | ||
|
||
let call = steward_abi::balancer_pool_adaptor_v1::JoinPoolCall { | ||
target_bpt: sp_call_parse_address(p.target_bpt)?, | ||
swaps_before_join, | ||
swap_data, | ||
minimum_bpt: string_to_u256(p.minimum_bpt)?, | ||
}; | ||
|
||
calls.push(BalancerPoolAdaptorV1Calls::JoinPool(call).encode().into()) | ||
} | ||
balancer_pool_adaptor_v1::Function::ExitPool(p) => { | ||
if !p.swaps_after_exit.is_empty() && p.swaps_after_exit.iter().any(|s| s.kind == 0) | ||
{ | ||
return Err(sp_call_error("invalid swap kind".to_string())); | ||
} | ||
|
||
let swaps_after_exit = convert_single_swap(p.swaps_after_exit)?; | ||
|
||
if p.swap_data.is_none() { | ||
return Err(sp_call_error("swap data must be set".to_string())); | ||
} | ||
|
||
let swap_data = convert_swap_data(p.swap_data.unwrap())?; | ||
|
||
let request = match p.request { | ||
Some(r) => ExitPoolRequest { | ||
assets: r | ||
.assets | ||
.into_iter() | ||
.map(sp_call_parse_address) | ||
.collect::<Result<Vec<_>, Error>>()?, | ||
min_amounts_out: r | ||
.min_amounts_out | ||
.into_iter() | ||
.map(string_to_u256) | ||
.collect::<Result<Vec<_>, Error>>()?, | ||
user_data: r.user_data.into(), | ||
to_internal_balance: r.to_internal_balance, | ||
}, | ||
None => return Err(sp_call_error("exit pool request must be set".to_string())), | ||
}; | ||
|
||
let call = steward_abi::balancer_pool_adaptor_v1::ExitPoolCall { | ||
target_bpt: sp_call_parse_address(p.target_bpt)?, | ||
swaps_after_exit, | ||
swap_data, | ||
request, | ||
}; | ||
|
||
calls.push(BalancerPoolAdaptorV1Calls::ExitPool(call).encode().into()) | ||
} | ||
balancer_pool_adaptor_v1::Function::StakeBpt(p) => { | ||
let call = steward_abi::balancer_pool_adaptor_v1::StakeBPTCall { | ||
bpt: sp_call_parse_address(p.bpt)?, | ||
liquidity_gauge: sp_call_parse_address(p.liquidity_gauge)?, | ||
amount_in: string_to_u256(p.amount_in)?, | ||
}; | ||
calls.push(BalancerPoolAdaptorV1Calls::StakeBPT(call).encode().into()) | ||
} | ||
balancer_pool_adaptor_v1::Function::UnstakeBpt(p) => { | ||
let call = steward_abi::balancer_pool_adaptor_v1::UnstakeBPTCall { | ||
bpt: sp_call_parse_address(p.bpt)?, | ||
liquidity_gauge: sp_call_parse_address(p.liquidity_gauge)?, | ||
amount_out: string_to_u256(p.amount_out)?, | ||
}; | ||
calls.push(BalancerPoolAdaptorV1Calls::UnstakeBPT(call).encode().into()) | ||
} | ||
balancer_pool_adaptor_v1::Function::ClaimRewards(p) => { | ||
let call = steward_abi::balancer_pool_adaptor_v1::ClaimRewardsCall { | ||
gauge: sp_call_parse_address(p.gauge)?, | ||
}; | ||
calls.push( | ||
BalancerPoolAdaptorV1Calls::ClaimRewards(call) | ||
.encode() | ||
.into(), | ||
) | ||
} | ||
} | ||
} | ||
|
||
Ok(calls) | ||
} | ||
|
||
fn convert_single_swap(swaps: Vec<SingleSwap>) -> Result<Vec<AbiSingleSwap>, Error> { | ||
swaps | ||
.into_iter() | ||
.map(|s| { | ||
let pool_id = hex::decode(s.pool_id) | ||
.map_err(|e| { | ||
ErrorKind::SPCallError.context(format!("failed to decode pool_id: {e}")) | ||
})? | ||
.try_into() | ||
.map_err(|_| { | ||
ErrorKind::SPCallError.context("pool ID must be 32 bytes".to_string()) | ||
})?; | ||
|
||
Ok(AbiSingleSwap { | ||
pool_id, | ||
kind: (s.kind - 1) as u8, | ||
asset_in: sp_call_parse_address(s.asset_in)?, | ||
asset_out: sp_call_parse_address(s.asset_out)?, | ||
amount: string_to_u256(s.amount)?, | ||
user_data: s.user_data.into(), | ||
}) | ||
}) | ||
.collect::<Result<Vec<_>, Error>>() | ||
} | ||
|
||
fn convert_swap_data(data: SwapData) -> Result<AbiSwapData, Error> { | ||
Ok(AbiSwapData { | ||
min_amounts_for_swaps: data | ||
.min_amounts_for_swaps | ||
.into_iter() | ||
.map(string_to_u256) | ||
.collect::<Result<Vec<_>, Error>>()?, | ||
swap_deadlines: data | ||
.swap_deadlines | ||
.into_iter() | ||
.map(string_to_u256) | ||
.collect::<Result<Vec<_>, Error>>()?, | ||
}) | ||
} |
Oops, something went wrong.