Degen Code

Degen Code

Share this post

Degen Code
Degen Code
Convex Optimization — Scaling for EVM

Convex Optimization — Scaling for EVM

Part IV: Dealing With Large Numbers

Aug 17, 2024
∙ Paid
2

Share this post

Degen Code
Degen Code
Convex Optimization — Scaling for EVM
1
Share

Most ERC-20 tokens, taking advantage of EVM’s 32 byte word size, use high bit integers for balances. An 18 decimal place token with a nominal value of “1” is represented within EVM as 1,000,000,000,000,000,000.

Parts I-III of the Convex Optimization series have used relatively small values to illustrate the concept.

Convex Optimization — Introduction

Convex Optimization — Introduction

BowTiedDevil
·
July 24, 2024
Read full story
Convex Optimization — Uniswap

Convex Optimization — Uniswap

BowTiedDevil
·
July 27, 2024
Read full story
Convex Optimization — Bounded Liquidity

Convex Optimization — Bounded Liquidity

BowTiedDevil
·
August 5, 2024
Read full story

However these lessons have hidden some complexity for the sake of instruction, which we must now deal with.

We return to the two-pool vectorized convex optimization example from Part II:

uniswap_v2_convex_2pool_vector.py

import cvxpy
import numpy
from fractions import Fraction


NUM_TOKENS = 2

fees = [
    Fraction(997, 1000),  # Pool A
    Fraction(997, 1000),  # Pool B
]
reserves_starting = [
    numpy.array([100_000, 50_000]),  # Pool A
    numpy.array([110_000, 50_000]),  # Pool B
]
deposits = [
    cvxpy.Variable(NUM_TOKENS, nonneg=True),  # Pool A
    cvxpy.Variable(NUM_TOKENS, nonneg=True),  # Pool B
]
withdrawals = [
    cvxpy.Variable(NUM_TOKENS, nonneg=True),  # Pool A
    cvxpy.Variable(NUM_TOKENS, nonneg=True),  # Pool B
]
reserves_after_swap = [
    starting_reserves + fee * deposit - withdrawal
    for starting_reserves, fee, deposit, withdrawal in zip(
        reserves_starting,
        fees,
        deposits,
        withdrawals,
    )
]
final_reserves = [
    starting_reserves + deposit - withdrawal
    for starting_reserves, deposit, withdrawal in zip(
        reserves_starting,
        deposits,
        withdrawals,
    )
]

profit = (
    # swap Y->X at pool B, swap X->Y at pool A, keep difference dY
    withdrawals[0][1] - deposits[1][1]
)

constraints = [
    # Received amount at PoolB is deposited at PoolA
    withdrawals[1][0] == deposits[0][0],
    # Only swap one token at a time
    deposits[0][1] == 0,
    deposits[1][0] == 0,
    withdrawals[0][0] == 0,
    withdrawals[1][1] == 0,
    # Enforce x*y=k invariants
    cvxpy.geo_mean(
        reserves_after_swap[0]
    ) >= cvxpy.geo_mean(reserves_starting[0]),
    cvxpy.geo_mean(
        reserves_after_swap[1]
    ) >= cvxpy.geo_mean(reserves_starting[1]),
]

# Define and solve the problem
problem = cvxpy.Problem(
    objective=cvxpy.Maximize(profit),
    constraints=constraints,
)
problem.solve(verbose=False)
print(f"Solved. profit={profit.value}")
print(f"Pool A reserves (after swap): {reserves_after_swap[0].value}")
print(f"Pool B reserves (after swap): {reserves_after_swap[1].value}")
print(f"Forward token amount (X): {deposits[0][0].value}")

Now consider that the pools in question are holding 18 decimal place ERC-20 tokens. For example, instead of a 50,000 balance of token Y in each pool, the balance is instead 50,000,000,000,000,000,000,000, or 50,000 * 10**18.

Changing only this block:

DECIMALS = 18
reserves_starting = [
    numpy.array([
        100_000 * 10**DECIMALS, 
        50_000 * 10**DECIMALS,
    ]),  # Pool A
    numpy.array([
        110_000 * 10**DECIMALS, 
        50_000 * 10**DECIMALS,
    ]),  # Pool B
]

Then running, we get a disturbing error!

Traceback (most recent call last):
  File "/home/btd/code/convex/uniswap_v2_convex_2pool_vector_scaled.py", line 74, in <module>
    raise ValueError("Problem could not be solved optimally.")
ValueError: Problem could not be solved optimally.

Adding the verbose=True option to problem.solve(), we get some more useful information:

This post is for paid subscribers

Already a paid subscriber? Sign in
© 2025 BowTiedDevil
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share