The Boosted Pools factories were deployed to mainnet last week, and the Balancer app now supports them too.
The authorizations to be ratified by this proposal would enable Gauntlet and a multisig controlled by Ballers to control certain parameters of the Boosted Pools so as to increase their capital efficiency and returns to liquidity providers.
Motivation
The Boosted Pools smart contracts ( AaveLinearPool
and StablePhantomPool
) contain a series of parameters that affect their efficiency and can be tuned depending on market conditions:
- Boosted Stable Pools (
StablePhantomPool
):- swap fee
- amplification parameter
- token rate cache duration
- Boosted Linear Pools (
AaveLinearPool
):- swap fee
- lower target
- upper target
Gauntlet has been managing fees and amplification factor in weighted and stable pools, and would be able to do so for Boosted Stable Pools if this proposal is approved by the community.
The duration of the token rate cache can have an impact on a boosted pool’s capital efficiency. If the duration is too long, the pool can be arbed for all the interest accrued on Aave since the last update of the cache. A short duration mitigates this risk, with the downside of increasing gas costs for trades.
On Linear Pools, swap fees are not collected by liquidity providers, but rather act as an incentive for arbitragers/keepers to:
- withdraw main tokens from the pool, deposit them in Aave and deposit the aTokens in the pool when it has main tokens in excess;
- withdraw aTokens from the pool, withdraw the underlying from Aave and deposit the main token to the pool when it has a low balance of main tokens;
The lower and upper targets determine a range within which no fee in imposed on trades between main tokens and Aave tokens. A trader moving the balance of main tokens away from this range pays a fee to the pool, which the pool then returns to traders that move the balance back towards the range.
While we’re confident that Gauntlet can hit the ground running with respect to optimizing the swap fee and amplification factor of Boosted Stable Pools, there’s still much to be learned when it comes to the other four parameters described above. For this reason, this proposal would authorize a multisig held by Ballers to set those parameters. Balancer Labs would monitor the activiy on the pools and the market conditions and advise on the the best course of action, but Ballers would be the ultimate decision makers, acting on behalf of BAL holders. All updates would be announced in Discord beforehand.
Specification
[1] The Balancer governance multisig on Ethereum Mainnet ( 0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f
) would submit a transaction to the Authorizer ( 0xA331D84eC860Bf466b4CdCcFb4aC09a1B43F3aE6
) with the following data to authorize the GAUNTLET_FEE_SETTER at 0xE4a8ed6c1D8d048bD29A00946BFcf2DB10E7923B
to set swap fees on Boosted Stable Pools
0xfcd7627e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e4a8ed6c1d8d048bd29a00946bfcf2db10e7923b000000000000000000000000000000000000000000000000000000000000000136e042f590f2c5d0d8959cc373c8b1681f70f84e9656be8dd0eae652e01de4eb
Which is the ABI-encoded calldata for:
authorizer.grantRoles(
[
0x36e042f590f2c5d0d8959cc373c8b1681f70f84e9656be8dd0eae652e01de4eb,
],
0xE4a8ed6c1D8d048bD29A00946BFcf2DB10E7923B
);
[2] The Balancer governance multisig on Ethereum Mainnet ( 0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f
) would submit a transaction to the Authorizer ( 0xA331D84eC860Bf466b4CdCcFb4aC09a1B43F3aE6
) with the following data to authorize the GAUNTLET_SAFE at 0xf4A80929163C5179Ca042E1B292F5EFBBE3D89e6
to set the amplification factor on Boosted Stable Pools
0xfcd7627e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f4a80929163c5179ca042e1b292f5efbbe3d89e60000000000000000000000000000000000000000000000000000000000000002fe1bd34ab8503474f86b5b36c5ea3e3575d3f1ea45eb1fb759b91b5cc4eac1e14f37434f57ce76a752b6a952570d046ec875f494e05243dab1f3c92f673d0cb2
Which is the ABI-encoded calldata for:
authorizer.grantRoles(
[
0xfe1bd34ab8503474f86b5b36c5ea3e3575d3f1ea45eb1fb759b91b5cc4eac1e1,
0x4f37434f57ce76a752b6a952570d046ec875f494e05243dab1f3c92f673d0cb2,
],
0xf4A80929163C5179Ca042E1B292F5EFBBE3D89e6
);
[3] The Balancer governance multisig on Ethereum Mainnet ( 0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f
) would submit a transaction to the Authorizer ( 0xA331D84eC860Bf466b4CdCcFb4aC09a1B43F3aE6
) with the following data to authorize the BALLERS_MULTISIG at 0x75a52c0e32397A3FC0c052E2CeB3479802713Cf4
to set the token rate cache durations on Boosted Stable Pools and swap fees and targets on Boosted Linear Pools
0xfcd7627e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000075a52c0e32397a3fc0c052e2ceb3479802713cf40000000000000000000000000000000000000000000000000000000000000003e4814396e9db5314024c424f43d6a129829efad6c545df373b226431cbcadbd31e3ce02b9d143fb44dc00c908d6b454553cf1c8c48e54090fa1f5fdd18a8e6b92256d78edacd087428321791a930d4f9fd7acf56e8862187466f1caf179c1a08
Which is the ABI-encoded calldata for:
authorizer.grantRoles(
[
0xe4814396e9db5314024c424f43d6a129829efad6c545df373b226431cbcadbd3,
0x1e3ce02b9d143fb44dc00c908d6b454553cf1c8c48e54090fa1f5fdd18a8e6b9,
0x2256d78edacd087428321791a930d4f9fd7acf56e8862187466f1caf179c1a08,
],
0x75a52c0e32397A3FC0c052E2CeB3479802713Cf4
);
For transparency’s sake, a developer could reproduce the bytes specifying the roles above using this code:
const ethers = require("ethers")
const authorizer = new ethers.utils.Interface([
"function grantRoles(bytes32[] memory roles, address account)",
]);
const stable_pool = new ethers.utils.Interface([
"function setSwapFeePercentage(uint256)",
"function startAmplificationParameterUpdate(uint256, uint256)",
"function stopAmplificationParameterUpdate()",
"function setTokenRateCacheDuration(address, uint256)"
]);
const linear_pool = new ethers.utils.Interface([
"function setSwapFeePercentage(uint256)",
"function setTargets(uint256, uint256)",
]);
const GAUNTLET_FEE_SETTER = "0xE4a8ed6c1D8d048bD29A00946BFcf2DB10E7923B";
const GAUNTLET_AMP_SETTER = "0xf4A80929163C5179Ca042E1B292F5EFBBE3D89e6";
const BALLERS_MULTISIG = "0x75a52c0e32397A3FC0c052E2CeB3479802713Cf4";
const AAVE_LINEAR_FACTORY = "0xD7FAD3bd59D6477cbe1BE7f646F7f1BA25b230f8";
const STABLE_PHANTOM_FACTORY = "0xb08E16cFc07C684dAA2f93C70323BAdb2A6CBFd2";
function roleId(address, sighash) {
return ethers.utils.solidityKeccak256(["uint256", "bytes4"], [address, sighash])
}
// 1
let roles = ["setSwapFeePercentage"]
.map(name => roleId(STABLE_PHANTOM_FACTORY, stable_pool.getSighash(name)));
let data = authorizer.encodeFunctionData("grantRoles", [roles, GAUNTLET_FEE_SETTER]);
console.log(`\n\n\n-*-*-*-*-*-*-1-*-*-*-*-*-*-`);
console.log(`authorizer.grantRoles(
[
${roles.map(role => `${role},`).join("\n ")}
],
${GAUNTLET_FEE_SETTER}
);`);
console.log(data);
// 2
roles = ["startAmplificationParameterUpdate", "stopAmplificationParameterUpdate"]
.map(name => roleId(STABLE_PHANTOM_FACTORY, stable_pool.getSighash(name)));
data = authorizer.encodeFunctionData("grantRoles", [roles, GAUNTLET_AMP_SETTER]);
console.log(`\n\n\n-*-*-*-*-*-*-2-*-*-*-*-*-*-`);
console.log(`authorizer.grantRoles(
[
${roles.map(role => `${role},`).join("\n ")}
],
${GAUNTLET_AMP_SETTER}
);`);
console.log(data);
// 3
let spf_roles = ["setTokenRateCacheDuration"]
.map(name => roleId(STABLE_PHANTOM_FACTORY, stable_pool.getSighash(name)));
let alf_roles = ["setTargets", "setSwapFeePercentage"]
.map(name => roleId(AAVE_LINEAR_FACTORY, linear_pool.getSighash(name)));
roles = spf_roles.concat(alf_roles);
data = authorizer.encodeFunctionData("grantRoles", [roles, BALLERS_MULTISIG]);
console.log(`\n\n\n-*-*-*-*-*-*-3-*-*-*-*-*-*-`);
console.log(`authorizer.grantRoles(
[
${roles.map(role => `${role},`).join("\n ")}
],
${BALLERS_MULTISIG}
);`);
console.log(data);