-
Notifications
You must be signed in to change notification settings - Fork 47
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
BAL Hookathon - Balancer.fun Memecoin Factory #93
base: main
Are you sure you want to change the base?
Conversation
Someone is attempting to deploy a commit to the Matt Pereira's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pushing this!
I like the concept overall. Here's some technical feedback for you to consider around possible improvements I would try to make this more functional, as well as to showcase how it works.
Please consider these comments just as feedback; feel free to address it if it's within your possibilities to polish your submission before we evaluate them.
*/ | ||
constructor(IVault vault, IERC20 _token) VaultGuard(vault) { | ||
token = _token; | ||
maxSwapAmount = 1_000_000 ether * 3 / 100; // 3% of total supply, could use token.totalSupply() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this assume that the supply of the token is fixed when the pool is initialized?
I'd suggest making this configurable (perhaps one maximum value per pool and a default one) by making the hook ownable
and providing a permissioned setter function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need to define whether this is a raw amount (presumably it is), in which case the initialization would depend on token decimals.
uint[] memory, | ||
bytes memory | ||
) public view override onlyVault returns (bool, uint[] memory) { | ||
revert LiquidityIsLocked(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😅 blunt, but interesting concept!
If I'm not mistaken, what most memecoin launches do is just create a pool with a ton of memecoin liquidity and then burn the LP supply manually; is that correct?
If this hook is trying to achieve the same, the problem with this approach is that whoever adds the liquidity still owns the BPT shares, and the liquidity can still be exited via recovery mode. Balancer V3 is designed to be non-custodial, and LPs can always exit their positions proportionally when recovery mode is activated. Therefore, blocking the liquidity is not as simple, unfortunately (or thankfully for LPs?).
There are ways which can get you around this, although you might need a custom router for that. The one that I have in mind is the following:
- modify the current router so that the recipient of the BPT can be configured as an argument when you add liquidity / initialize
- in
onAfterAdd
(and possiblyonAfterInitialize
), check that the router is trusted, and check that the recipient the liquidity is e.g.0x...Dead
.
That way, you don't need to revert: BPT shares are always sent to a dead address, which ensures that the liquidity can never be retrieved back again. Can also be done with just onAfterInitialize
, and just revert onAfterAdd
always.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally the pool creators need to extract the proceeds at some point, though. Balancer pools have to be two-tokens, so there will be a "value token" (like DAI or WETH) that would also be locked. There are lots of options here.
The BPT could be held by the hook contract, and the owner would be able to withdraw it with a permissioned function after a timelock. Then you wouldn't need to do anything in the remove hooks. You'd need an owner then, and also ensure that only the owner can add liquidity (otherwise other people could inadvertently "donate" funds that would go to the owner). LBPs do this as well (except for the timelock withdrawal).
* @notice Tests a swap operation. | ||
* @dev Executes a swap and verifies the balance changes for Alice. | ||
*/ | ||
function testSwap() public { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to see a test that actually triggers one of the new conditions errors e.g. MaximumSwapExceeded
Interesting work. I think there is a lot of design surface around better liquidty for memecoins, especially around launch. This hook focuses about how much people can trade and when. Quite often in the first block of a memecoin launch a bot, or a dev, buys up a large portion of supply in the token. These addresses then represent potential "rug risk", which makes the token undesirable, and usually results in whoever doesn't notice getting rekt. If you could find ways to ensure that not too much of the token was sold in the first 24 hours, and/or that the amount of supply for sale somehow ramped up giving everyone a chance to ape in before someone owned double digit %'s of supply paying almost nothing. Maybe you could also explore limits to try to ensure a wide/fair distribution of a pump.fun token which is often launched into an LP pool with 100% of supply + 0 to almost 0 other liquidity, immediately followed by a dev and some bots buying however much they want of it. Larger swaps are probably ok later on if the token was well distributed at launch, if not it's likely doomed anyway. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this twist on the fair launch concept. Serving the "long tail" of liquidity is one of the goals of Balancer.
*/ | ||
constructor(IVault vault, IERC20 _token) VaultGuard(vault) { | ||
token = _token; | ||
maxSwapAmount = 1_000_000 ether * 3 / 100; // 3% of total supply, could use token.totalSupply() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need to define whether this is a raw amount (presumably it is), in which case the initialization would depend on token decimals.
) public override onlyVault returns (bool, uint) { | ||
if (address(params.tokenIn) == address(token)) { | ||
uint currentBlockSold = blockToTotalSold[block.number]; | ||
if (currentBlockSold + params.amountInScaled18 > maxSwapAmount * 1e18) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assumes the token's 18-decimals; you could integrate the ScalingHelpers library to help here (presumably you don't want to limit it to 18-decimal tokens).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted elsewhere, there could be a "decaying limit" here, or some kind of defined "sale period" during which it would be limited, or perhaps a % distribution. This would have to be some kind of custom pool type anyway (e.g., a regular Weighted Pool doesn't work well for "one-sided" sales, as the price would just go up and up until the sale stopped).
Maybe it returns a constant price or something. It could also have some sort of pricing curve that let you buy as much as you wanted, but at higher and higher prices as the amount went up. There could be a threshold amount, under which you get the "sale price." Or you could sell it in fixed-sized blocks for a defined price, and only let people buy once per block (or 10 blocks, etc.) Arbitrary pool code = limitless design space.
LiquidityManagement calldata | ||
) public override onlyVault returns (bool) { | ||
emit BalancerFunHookRegistered(address(this), pool); | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work with every pool type? The original fair launch pool was an LBP (in v2, and under construction in v3). Does it need to be 2-token, etc.?
uint[] memory, | ||
bytes memory | ||
) public view override onlyVault returns (bool, uint[] memory) { | ||
revert LiquidityIsLocked(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally the pool creators need to extract the proceeds at some point, though. Balancer pools have to be two-tokens, so there will be a "value token" (like DAI or WETH) that would also be locked. There are lots of options here.
The BPT could be held by the hook contract, and the owner would be able to withdraw it with a permissioned function after a timelock. Then you wouldn't need to do anything in the remove hooks. You'd need an owner then, and also ensure that only the owner can add liquidity (otherwise other people could inadvertently "donate" funds that would go to the owner). LBPs do this as well (except for the timelock withdrawal).
For the Balancer V3 Hookathon, we developed Balancer.Fun, a custom Balancer v3 hook designed to enable fair and controlled token distributions within liquidity pools. The goal is to create a sustainable ecosystem around the concept of memecoins (and any other erc20 token) by enforcing liquidity controls that prevent market manipulation and large-scale token dumps. Inspired by the recent wave of memecoin trading on Solana, this project leverages Balancer’s innovative architecture to support a fair launch platform, ensuring that token distribution remains balanced and transparent.
What Does the BalancerFun Hook Do?
The BalancerFun hook implements two key features:
Per-Block Swap Limits A cap is enforced on the amount of tokens that can be swapped out of the pool in any given block. This prevents sudden, large sell-offs by limiting the number of tokens that can be sold within each block. The hook monitors the total token swaps per block and reverts any transaction that exceeds the predefined limit.
Liquidity Locking Liquidity added to the pool is locked indefinitely. This ensures that liquidity cannot be withdrawn once it has been committed, stabilizing the token's liquidity over time and preventing sudden removal of funds that could destabilize the market.
Fair Launch Memecoin Ecosystem
The idea is to capture the current narrative and launch Balancer v3 with a hook which enables fair launch memecoins. If the custom logic within a hook can prevent a rugpulls then it will create a more sustainable and valued ecosystem for memecoin traders.
By implementing Balancer.Fun, the memecoin’s liquidity pool would prevent any single party from dumping large amounts of tokens in one block, thus protecting the token’s price from sudden crashes. With locked liquidity, the ecosystem benefits from a steady, immovable liquidity base, further contributing to the long-term health of the token’s market.
This approach brings fairness and transparency to memecoin launches, ensuring that every participant has an equal opportunity to buy and sell without being affected by malicious rugs and dumps. It serves as a solution for new token creators who want to establish trust and stability in their project and provides Balancer with the potential to address the biggest potential avenue for traction with the launch of v3, given the current memecoin narrative.