5/5
# Code Walkthrough: `calc_withdraw_one_coin` In this lesson, we will be covering the function `calc_withdraw_one_coin`. The function `remove_liquidity_one_coin` calls the internal function `calc_withdraw_one_coin` in order to calculate the amount of tokens to send back to a liquidity provider. Let's dive in. First, let's take a look at the function definition: ```javascript def calc_withdraw_one_coin( A_gamma: uint256[2], token_amount: uint256, i: uint256, update_D: bool, ) -> (uint256, uint256[N_COINS], uint256): ``` This function takes in `A_gamma` which is an array of `uint256` with a length of two, `token_amount` which is of type `uint256`, `i` which is also of type `uint256` and `update_D` which is a boolean. This function returns three values. The function calculates the number of tokens that a liquidity provider will receive when burning their LP shares. The variable `i` specifies which token that the liquidity provider wants to receive. Next, we grab the state variable `totalSupply`. This represents the total LP supply. We then perform some basic checks. The first check asserts that the `token_amount` is less than or equal to `token_supply`. The next check asserts that the index `i` is less than `N_COINS`. ```javascript token_supply: uint256 = self.totalSupply assert token_amount <= token_supply # dev: token amount more than supply assert i < N_COINS # dev: coin out of range ``` The code then initializes some more variables: ```javascript xx: uint256[N_COINS] = self.balances precisions: uint256[N_COINS] = self._unpack(self.packed_precisions) xp: uint256[N_COINS] = precisions D0: uint256 = 0 ``` `xx` is initialized with token balances. `precisions` is initialized by unpacking packed precisions and stored in an array. `xp` is a copy of `precisions`. `D0` is an integer initialized to `0`. Next, we see some code that might be difficult to understand. Let's take a look at it step by step: ```javascript price_scale_i: uint256 = PRECISION * precisions[0] packed_prices: uint256 = self.price_scale_packed xp[0] *= xx[0] for k in range(1, N_COINS): p: uint256 = (packed_prices & PRICE_MASK) if i == k: price_scale_i = p * xp[i] # precision * balances[i] * price_scale[i] / PRECISION xp[k] = unsafe_div(xp[k] * xx[k] * p, PRECISION) packed_prices = packed_prices >> PRICE_SIZE ``` `price_scale_i` is initialized with `PRECISION` multiplied by `precisions` at index `0`. `packed_prices` is initialized with the value from the state variable `price_scale_packed`. `xp` at index `0` is multiplied by `xx` at index `0`. Next, a for loop starts. It iterates from `k = 1` to `k < N_COINS`. Inside the loop `p` is initialized with packed prices and `PRICE_MASK`. If `i == k` then `price_scale_i` is updated by `p * xp[i]`. The code comments show how `xp[k]` is being calculated. `xp` at index `k` gets updated with `xp[k] * xx[k] * p` divided by `PRECISION`. Then `packed_prices` is shifted right by the `PRICE_SIZE`. The code is setting the values in the `xp` array with transformed token balances. If `i == k` then `price_scale_i` is updated. After the loop, `D0` is calculated with a conditional: ```javascript if update_D: D0 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0) else: D0 = self.D D: uint256 = D0 ``` If `update_D` which is passed into the function is `true`, then `D0` is updated with `MATH.newton_D` function, otherwise the state variable `D` is used. Then the variable `D` is initialized to the value of `D0`. After that, there is some fee calculation code that is important to cover: ```javascript xp_imprecise: uint256[N_COINS] = xp # balanced withdrawal = from xp[0] -> xp[0] * token_amount / token_supply # from xp[1] -> xp[1] * token_amount / token_supply # from xp[2] -> xp[2] * token_amount / token_supply # imbalanced withdrawal # assumes xp[0] ~= xp[1] ~= xp[2] -> xp[i] * N * token_amount / token_supply xp_correction: uint256 = xp[i] * N_COINS * token_amount / token_supply fee: uint256 = self._unpack(self.packed_fee_params)[1] # <- self.out_fee. # Deduct xp[i] * N * token_amount / token_supply from xp[i] and calculate fee if xp_correction < xp_imprecise[i]: xp_imprecise[i] -= xp_correction fee = self._fee(xp_imprecise) dd: uint256 = unsafe_div(token_amount * D, token_supply) D_fee: uint256 = fee * dd / (2 * 10**10) + 1 D = (D - D_fee) # <- Charge fee on D ``` The code initializes `xp_imprecise` to a copy of `xp`. The comments above `xp_correction` explain the logic being implemented for both balanced and imbalanced withdrawals. `xp_correction` is updated using `xp[i]`, `N_COINS`, `token_amount` and `token_supply`. The `fee` is unpacked using the state variable `packed_fee_params`. Then if `xp_correction` is less than `xp_imprecise[i]` then we deduct `xp_correction` from `xp_imprecise[i]`. Then the `fee` is calculated. Next, `dd` which is the change in `D` is calculated and a `D_fee` is calculated using `fee * dd`. Finally, we subtract the fee from `D`. The last portion of code is used to update `xp[i]` which is the token balances after calculating fees. ```javascript y: uint256 = MATH.get_y(A_gamma[0], A_gamma[1], xp, D0, dd, i)[0] dy: uint256 = (xp[i] - y) * PRECISION / price_scale_i xp[i] = y ``` First, a new variable `y` is created with the help of `MATH.get_y`. `dy` is calculated using `xp[i]`, `y`, `PRECISION` and `price_scale_i`, and finally `xp[i]` is updated to equal `y`. The function returns `dy`, `xp`, and `approx_fee`. ```javascript return dy, xp, approx_fee ``` That is it for the function `calc_withdraw_one_coin` . In the next lesson, we will cover other functions.
calc_withdraw_one_coin
In this lesson, we will be covering the function calc_withdraw_one_coin
. The function remove_liquidity_one_coin
calls the internal function calc_withdraw_one_coin
in order to calculate the amount of tokens to send back to a liquidity provider. Let's dive in.
First, let's take a look at the function definition:
This function takes in A_gamma
which is an array of uint256
with a length of two, token_amount
which is of type uint256
, i
which is also of type uint256
and update_D
which is a boolean. This function returns three values.
The function calculates the number of tokens that a liquidity provider will receive when burning their LP shares. The variable i
specifies which token that the liquidity provider wants to receive.
Next, we grab the state variable totalSupply
. This represents the total LP supply. We then perform some basic checks. The first check asserts that the token_amount
is less than or equal to token_supply
. The next check asserts that the index i
is less than N_COINS
.
The code then initializes some more variables:
xx
is initialized with token balances. precisions
is initialized by unpacking packed precisions and stored in an array. xp
is a copy of precisions
. D0
is an integer initialized to 0
.
Next, we see some code that might be difficult to understand. Let's take a look at it step by step:
price_scale_i
is initialized with PRECISION
multiplied by precisions
at index 0
. packed_prices
is initialized with the value from the state variable price_scale_packed
. xp
at index 0
is multiplied by xx
at index 0
.
Next, a for loop starts. It iterates from k = 1
to k < N_COINS
.
Inside the loop p
is initialized with packed prices and PRICE_MASK
. If i == k
then price_scale_i
is updated by p * xp[i]
. The code comments show how xp[k]
is being calculated. xp
at index k
gets updated with xp[k] * xx[k] * p
divided by PRECISION
. Then packed_prices
is shifted right by the PRICE_SIZE
.
The code is setting the values in the xp
array with transformed token balances. If i == k
then price_scale_i
is updated.
After the loop, D0
is calculated with a conditional:
If update_D
which is passed into the function is true
, then D0
is updated with MATH.newton_D
function, otherwise the state variable D
is used. Then the variable D
is initialized to the value of D0
.
After that, there is some fee calculation code that is important to cover:
The code initializes xp_imprecise
to a copy of xp
. The comments above xp_correction
explain the logic being implemented for both balanced and imbalanced withdrawals. xp_correction
is updated using xp[i]
, N_COINS
, token_amount
and token_supply
. The fee
is unpacked using the state variable packed_fee_params
. Then if xp_correction
is less than xp_imprecise[i]
then we deduct xp_correction
from xp_imprecise[i]
. Then the fee
is calculated. Next, dd
which is the change in D
is calculated and a D_fee
is calculated using fee * dd
. Finally, we subtract the fee from D
.
The last portion of code is used to update xp[i]
which is the token balances after calculating fees.
First, a new variable y
is created with the help of MATH.get_y
. dy
is calculated using xp[i]
, y
, PRECISION
and price_scale_i
, and finally xp[i]
is updated to equal y
. The function returns dy
, xp
, and approx_fee
.
That is it for the function calc_withdraw_one_coin
. In the next lesson, we will cover other functions.
Understand how calc_withdraw_one_coin calculates the exact amount a user receives when withdrawing in one token—factoring balances, fees, and precision.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
AMM math for Curve Cryptoswap
How liquidity is concentrated
Price-repegging
How function calls interact with the AMM
Curve Cryptoswap state variables
How the function exchange works
How to swap tokens
How to add and remove liquidity
Math for Curve Cryptoswap’s internal price oracle
Implicit differentiation
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Blockchain Financial Analyst
$100,000 - $150,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on August 11, 2025
Duration: 4min
Duration: 1h 21min
Duration: 28min
Duration: 26min
Duration: 14min
Duration: 24min
Duration: 59min
Duration: 5min
Course Overview
About the course
AMM math for Curve Cryptoswap
How liquidity is concentrated
Price-repegging
How function calls interact with the AMM
Curve Cryptoswap state variables
How the function exchange works
How to swap tokens
How to add and remove liquidity
Math for Curve Cryptoswap’s internal price oracle
Implicit differentiation
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Blockchain Financial Analyst
$100,000 - $150,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on August 11, 2025