Goober Litepaper

Date: Nov 20, 2022 | Bandit, Alcibiades, Grug

Contents

  1. Introduction
  2. Motivation
  3. Solution
  4. Mechanism
    1. Overview
    2. Optimizing Goo production
    3. GBR
      1. Protocol Fees
    4. Issuance
    5. Redemption
    6. Liquid markets for Goo and Gobblers
      1. Balancing the swap with erroneous Goo
    7. Price impact
    8. Flash swaps
    9. Minting
      1. Definitions
      2. Discounting unrevealed Gobblers
  5. Code
  6. Conclusion

Introduction

Goober is a yield-optimized farm and liquidity engine for Art Gobblers, the experimental, decentralized art factory by Paradigm. Art Gobblers was constructed using two new innovations, GOO (an emission mechanism) and VRGDA (a Dutch auction mechanism). Gobblers continuously produce Goo, an ERC20 token, proportional to their multiplier and how much Goo is already in their "tank", a balance held per Ethereum address.

Goober allows users to permissionlessly swap Gobblers and Goo, as well as deposit Goo and fractionalize their Gobblers in order to maximize their Goo accrual without the need for active management. By depositing Gobblers and/or Goo into the vault, users receive GBR in return, an ERC20 token that gives depositors claim to the assets in the vault.

Motivation

Several months prior to the release of Art Gobblers, the Paradigm team reached out to Grug, now a member of the Goober team, with the goal of gathering a group of MEV searchers to battle test the Art Gobbler issuance mechanism. The Art Gobblers contracts were deployed on the Goerli testnet with parameters sped up 30x in order to simulate 2 years in 2 weeks.

The test imparted a great degree of knowledge about the VRGDA and GOO mechanisms underpinning Art Gobblers onto all the participants. Notably, that participants who did not hit the ground running early would stand little to no chance of keeping up their Goo accrual with those that did.

This has also been true in production the existing Goo and Gobbler distribution heavily favors those with deep pockets or who colluded early on. Since these actors possess the most Gobblers, they produce the most Goo, in turn continuously driving the price of the auction beyond any new entrants' reach - at a quadratically increasing rate. Thus, a user with one or two Gobblers who has just gotten involved will likely never be able to mint another Gobbler with their Goo holdings.

Compounded by the fact that there is also low liquidity for Goo across decentralized exchanges, this dynamic makes it even more difficult for the average user to get the Goo flywheel turning without owning a Gobbler themselves. It has also caused large holders to mint from auction at an irrationally high Goo cost compared to the outstanding Goo supply. Since Gobbler prices on secondary markets inherently trade at a premium to the auction price, the average user has been effectively boxed out of the game.

Solution

Both liquidity and the ability to effectively pool capital with other users are necessary to compete with the quadratic inflation of Goo in the Art Gobblers system. Additionally, determining an optimal minting strategy (given a game-theoretical and probabilistic approach to VRGDA) to automate liquidty management and passive rebalancing.

It is very difficult to account for illiquidity and market fluctuations. In trying to solve for this, it became apparent that a constant function market maker optimizes for Goo production, it matches the shape of the function for optimal Goo production, and in fact, the unit of Goo and Gobbler production is the native unit of liquidity for a Uniswap V2 like automated market maker.

Thus, we set out to buid a special purpose AMM with yield bearing vault-like characteristics and managed minting mechanics. Where Goober diverges from Uniswap V2, aside from bonding an ERC20 with an ERC721, is the fact that the vault can mint new Gobblers when it's profitable to do so, returning them to the pool (shifting the curve) and increasing the rate of Goo emissions.

Mechanism

Overview

Utilizing an amalgamation of Uniswap V2's constant function bonding, an ERC-4626 yield-bearing vault (with GBR as the vault token), and automated VRGDA minting under certain market conditions, Goober aligns incentives to create an optimal balance of Goo and Gobbler multiplier for increased liquidity and Goo production across market conditions.

The rate of growth of Goo happens to be maximized when Goo=MultGoo = Mult. Thus, there is a clear comparison with Uniswap V2's xy=kx * y = k constant product market maker design, where the point of maximization is also the point at which X=YX = Y.

Optimizing Goo production

Art Gobblers NFTs produce GooGoo according to the formula:

GooMult=GooProduction\sqrt{Goo * Mult} = GooProduction

Where MultMult is the multiplier of Art Gobbler NFTs in a wallet and GooGoo is the Goo balance of a wallet.

Goober implements a Uniswap V2 style XY=KX*Y=K automated market maker where XX the total amount of GooGoo in the vault, YY is the vault's total Gobbler MultMult, KK is the constant product of the reserves, K\sqrt{K} is the instantaneous GooProductionGooProduction at some time tt, and GBR is the LP token.

Aligning the proportion of Goo and Gobblers in the vault with market forces using a bonding curve, optimizes for both Goo production and liquidity.

GBR

GBR is the ERC4626-like ERC20 vault token herein referred to as FF, representing fractions of the pool. The issuance and redemption rates of FF are determined by the change in K\sqrt{K}, or ΔK\Delta \sqrt{K}, outlined in detail below.

Protocol Fees

The protocol charges a 22% management in FF from what is minted during deposit() and 1010% on the growth of K\sqrt{K} in FF dilution. For example, if K\sqrt{K} grows by 1010%, the protocol mints another 11% of the total outstanding FF supply for itself.

The pool charges a 30 basis point swap fee, which accrues to GBR holders.

Issuance

Since we want to optimize Goo emission, we create an incentive for increasing K\sqrt{K} upon deposit().

The total rate of emission of the vault is tracked by a constant K\sqrt{K}, where

K=GooMult\sqrt{K} = \sqrt{Goo * Mult}

When a deposit is made to the pool, a new nn value of reserves is calculated based on the amount of each asset added by depositor dd,

Goon=Gooi+GoodGoo_{n} = Goo_{i} + Goo_{d}

and

Multn=Multi+Multd{ Mult }_{n} = { Mult }_{i} + Mult_{d}

Now that we have some increase in our emission,

Kn>Ki{\sqrt{K}}_{n} > \sqrt{K}_{i}

the depositor should be rewarded accordingly. Thus, the vault mints some amount of FdF_{d}, GBR to transfer to the depositor, where

Fd=FiΔK{ F }_{d}={ F }_{i} * \Delta \sqrt{K}

or

Fd=Fi(KnKiKi){ F }_{d}={ F }_{i} ( {\sqrt{K}_{n} - \sqrt{K}_{i} \over \sqrt{K}_{i}})

subsituting for KK, we get

Fd=Fi((GoonMultn)(GooiMulti)(GooiMulti)){ F }_{d}={ F }_{i} ( {\sqrt{(Goo_{n} * Mult_{n})} - \sqrt{(Goo_{i} * Mult_{i})} \over \sqrt{(Goo_{i} * Mult_{i})}})

which can be simplified to

Fd=Fi((GoonMultn)(GooiMulti)1){ F }_{d}={ F }_{i} ({\sqrt{(Goo_{n} * Mult_{n})} \over \sqrt{(Goo_{i} * Mult_{i})}} -1)

our issuance formula.

Redemption

Since FF represents a fixed fraction of the pool. As the pool grows, so to will the assets redeemable by the fraction.

On withdraw, a user exchanges some FF for GooGoo and/or MultMult, burning the respective amount of FF in the process.

Since, a withdrawal decreases the reserves, then post withdraw:

Ki>Kn\sqrt{K}_{i} > \sqrt{K}_{n}

Since the pool's rate of GooGoo emission has decreased, then so too must its supply of outstanding fractions by a proportionate amount.

We can derive the amount of reserves allotted to an amount of FdF_{d} from the inverse of the issuance calculation:

Fd=Fi((GoowMultw)(GooiMulti)1){ F }_{d}={ F }_{i} ({\sqrt{(Goo_{w} * Mult_{w})} \over \sqrt{(Goo_{i} * Mult_{i})}} -1)

which can be rearranged to

Goow=(GooiMulti)(FdFi+1)2Multw{Goo_{w}} = {{(Goo_{i} * Mult_{i})}({{ F }_{d} \over { F }_{i}} + 1)^2 \over Mult_{w}}

where GoowGoo_{w} and MultwMult_{w} represent the respective amounts (each can be solved for interchangeably) that can be withdrawn simultaneously for some amount of fractions FdF_{d} upon withdraw().

Liquid markets for Goo and Gobblers

Creating a liquid, balanced pool of Goo and Gobblers also enables users to swap efficiently between either asset, and for the vault to accrue fees and increase the growth of KK (Goo production).

For some swap() ss with initial reserves GooiGoo_{i} and MultiMult_{i}, the new balances GoonGoo_{n} and MultnMult_{n} are determined by:

Goon=Gooi+GooInGooOutGoo_{n} = Goo_{i} + GooIn - GooOut

and

Multn=Multi+MultInMultOutMult_{n} = Mult_{i} + MultIn - MultOut

we can rewrite the net changes (InOutIn - Out) in Goo and Mult as GooxGoo_x and MultxMult_x respectively.

Balancing the swap with erroneous Goo

Since we know the constant product KK must remain greater than or equal to the original KK after ss, and because MultMult is not unitarily interchangable, if the inequality does not hold, there is some amount of erroneousGoo GooeGoo_e, that must be added to GooxGoo_{x} as additional GooInGooIn or withdrawn from GooxGoo_{x} as additional GooOutGooOut for ss to make the inequality hold such that:

KiKn\sqrt{K}_i \leq \sqrt{K}_n

therefore,

GooiMultiGoonMultn\sqrt{Goo_i * Mult_i} \leq \sqrt{Goo_n * Mult_n}

subsituting we get

GooiMulti(Gooi+Goox)(Multi+Multx)\sqrt{Goo_i * Mult_i} \leq \sqrt{(Goo_i + Goo_x) (Mult_i + Mult_x)}

or at point of equality:

GooiMulti=GooiMulti+GooxMulti+GooiMultx+GooxMultx{Goo_i Mult_i} = {Goo_iMult_i+ Goo_xMult_i + Goo_iMult_x + Goo_xMult_x}

which we can rearrange to

Multx=Goox(Multi+Multx)GooiMult_x = -{Goo_x(Mult_i + Mult_x)\over Goo_i}

or

Goox=Gooi(1+MultiMultx)Goo_x = {-Goo_i \over (1 + {Mult_i \over Mult_x} )}

or

Goox=GooiMultxMultx+MultiGoo_x = -{Goo_iMult_x\over Mult_x + Mult_i}

therefore, the erroneous goo can be expressed as,

Gooe=Gooi(1+MultiMultx)Goox Goo_e = {-Goo_i \over (1 +{Mult_i \over Mult_x})} - Goo_x

which can be used to balance a swap that doesn't have the correct parameters.

Price impact

As with all constant function market makers, there will always be some price impact inherent to swaps, as a function of the size of the order in relation to the size of the pool. In the case of Goober, this is best expressed as a percentage change in the ratio between GooGoo and MultMult.

To solve for some price impact IsI_s for swap() ss, we must first calculate the change between the ratio of GooGoo and MultMult, the price impact for the swap expressed as an absolute change in the ratio of the reserves for ss is:

Is=ΔGooMultI_s = |\Delta{Goo \over Mult}|

or

Is=GooiMultiGoonMultnGooiMultiI_s = |{{Goo_i \over Mult_i} - {Goo_n \over Mult_n}\over {Goo_i \over Mult_i}}|

which in the expanded form yields

Is=MultiGoonGooiMultnI_s = - {Mult_iGoo_n \over Goo_iMult_n}

and since we know that Goon=Gooi+GooxGoo_n = Goo_i + Goo_x, and Multn=Multi+MultxMult_n = Mult_i + Mult_x, then

Is=Multi(Gooi+Goox)Gooi(Multi+Multx)I_s = - {Mult_i(Goo_i + Goo_x) \over Goo_i(Mult_i +Mult_x)}

then we can subsitute MultxMult_x

Is=Multi(GooiGoox)Gooi(MultiGoox(Multi+Multx)Gooi)I_s = - {Mult_i(Goo_i -Goo_x) \over Goo_i(Mult_i -{Goo_x(Mult_i+Mult_x) \over Goo_i})}

or

Is=Multi(GooiGoox)MultiGooiGoox(Multi+Multx)I_s = - {Mult_i(Goo_i -Goo_x) \over Mult_iGoo_i -Goo_x(Mult_i+Mult_x)}

or

Is=GooxGooiGooiGoox+MultxMultiI_s = {Goo_x -Goo_i \over Goo_i -Goo_x+{Mult_x \over Mult_i}}

or

Is=GooxGooiGooiGoox+ΔMultI_s = {Goo_x -Goo_i \over Goo_i -Goo_x+\Delta Mult}

alternatively

Is=Multi(GooxGooi)GooiMultiMultiGoox+MultxI_s = {Mult_i(Goo_x -Goo_i) \over Goo_iMult_i - Mult_iGoo_x +Mult_x }

which gives us the price impact IsI_s in terms of values for GooxGoo_x and MultxMult_x that maintain the exact constant product.

Flash swaps

Goober also allows for flash swaps which allow a user to receive and use some Goo or Gobblers before paying for them, as long as they make the repayment within the same atomic transaction.

The swap() function makes a call to an optional user-specified callback contract in between transferring out the tokens requested by the user and enforcing the invariant KiKnK_i \leq K_n . Once the callback is complete, the contract checks the new balances and confirms that the invariant is satisfied, after adjusting for fees on the amounts paid in. If the contract does not have sufficient funds, it reverts the entire transaction. A user can also repay the Goober pool using the same token, rather than completing the swap(). This is effectively the same as letting anyone flash-borrow any of assets stored in a Goober pool, for the same dynamic fees as Goober charges for regular swaps.

Minting

Since Goo growth of a given tank grows quadratically per added MultMult, it is optimal for the growth of KK to increase MultMult as often as possible. On-chain conditions determine when it is preferable for the pool, or an Externally Owned Account, to mint a new Gobbler from the VRGDA auction rather than buying a Gobbler at the pool's market rate.

Definitions

The weighted average multiplier of a newly minted Gobbler can be derived from the mint probabilities specified in the Art Gobblers smart contract, which yield an average MultMult per mint of

Multavg=7.3294Mult_{avg}= 7.3294

Also we define,

V=AuctionPriceV = AuctionPrice

Where AuctionPriceAuctionPrice follows the logistic VRGDA formula in GooGoo based on the state-of-the-Art Gobblers smart contract at time tit_{i}.

Discounting unrevealed Gobblers

At any time the Goober pool is willing to buy revealed Gobblers for a price PrP_r, which can be calculated as the absolute value of gooErroneous GooeGoo_e required to balance a one-sided swap of MultavgMult_{avg} for 0 GooGoo.

Pr=Gooe=Gooi(1+MultiMultavg) P_r = |Goo_e| = {Goo_i \over (1 +{Mult_i \over Mult_{avg}})}

Given that price PrP_r at which the pool is willing to buy a revealed Gobbler, there exists some price PuP_u at which it is statistically profitable for the pool to buy an unrevealed Gobbler at some time trt_r away from reveal.

PuP_u also happens to be the price it makes sense for the pool to pay for minting a new Gobbler from the VRGDA, which is functionally the same as buying an unrevealed Gobbler. Because of the Art Gobblers implementation, the value of trt_r can be found using modulo division on the present tblockt_{block} unix timestamp as follows

tday=86400t_{day} = 86400
treveal=40800t_{reveal} = 40800
tr=tblockmodtdaymodtrevealt_r = t_{block} \bmod t_{day} \bmod t_{reveal}

Emitted GooGoo over some time tt is determined by the integral of the instantaneous rate of goo growth

g(Goo,Mult,t)=14(Multt)2+Goo+tMultGoog(Goo, Mult, t) = {1 \over 4}(Mult * t)^2 + Goo + t \sqrt{Mult * Goo}

by calculating the pool's new reserves after buying an revealed Gobbler GoonGoo_n

Goon=GooiPrGoo_n = Goo_i - P_r

and the new MultMult after reveal, or if the pool had bought a similar revealed Gobbler

Multn=Multi+MultavgMult_n = Mult_i + Mult_{avg}

since an unrevealed Gobbler has Mult=0Mult = 0 until trt_r, there will be a difference in emission GooriGoo_{r-i} between the reserves of the pool had it bought a revealed Gobbler for PrP_r and had it bought an unrevealed Gobbler for PrP_r at time tit_i over the time interval trt_r - tit_i, which can be expressed as:

Goori=g(Goon,Multn,tr)g(Goon,Multi,tr)Goo_{r-i} = g(Goo_n, Mult_{n}, t_r) - g(Goo_n, Mult_i, t_r)

Therefore, we can express some discounted value of an unrevealed Gobbler in Goo at time tit_i as the extra reserves GooxGoo_x that must be present to satisfy Goor1=0Goo_{r-1} = 0

or

g(Goon,Multn,tr)=g(Goon+Goox,Multi,tr)g(Goo_n, Mult_{n}, t_r) = g(Goo_n + Goo_x, Mult_i, t_r)

or

g(GooiPr,Multi+Multavg,tr)=g(GooiPr+Goox,Multi,tr)g(Goo_i - P_r, Mult_{i} + Mult_{avg}, t_r) = g(Goo_i-P_r + Goo_x, Mult_i, t_r)


subsituting, rearranging and simplifying, we get

Goox=t22(MultiMultavg+Multi+2GooiMultit+Multavg22)+Goox2Goo_x = {t^2\over2}({Mult_iMult_{avg}+Mult_i+{{2\sqrt{Goo_iMult_i}\over t}} + {{Mult_{avg}}^2\over 2}}) + Goo_{x_2}

where

Goox2=Multit4GooiMultavg+Multi+2Multavg+t2+4tGooiMulti+Multavg2t2MultiGoo_{x_2} = {Mult_i\over t}{{\sqrt{{{4Goo_i\over Mult_{avg} + Mult_i} + 2Mult_{avg} + {t}^2 + {4t\sqrt{Goo_iMult_i} + Mult_{avg}^2 {t}^2\over Mult_i}} }}}

We can then subtract the discount GooxGoo_x from the known revealed Gobbler price PrP_r to arrive at the price we can pay for an unrevealed Gobbler

Pu=PrGooxP_u = P_r - Goo_x

therefore, an unrevealed Gobbler should be purchased from the VRGDA at time tit_i when

V<PuV \lt P_u

Code

A experimental and permissively licensed (MIT) implementation of Goober can be found at gooberxyz/goobervault. Pull requests with improvements are welcome.

Conclusion

We believe Goober will democratize access to the Art Gobblers game, and make the GOO and VRGDA mechanics of the game function more smoothly over time.

If you are interested in integrating Goober into your project, we’d love to hear from you.

You can reach us on Twitter at @mevbandit, @0xAlcibiades, and @CapitalGrug.