---
# System prepended metadata

title: Phoenix DeFi Hub - provide/withdraw liquidity instruction

---

Hello!
In this document, I wanted to provide you with a guide on how to provide and withdraw liquidity in Phoenix DeFi Hub liquidity pools.

The first and recommended method is to use our official web UI. Go to the page: app.phoenix-hub.io.

![2024-08-12-142938_983x601_scrot](https://hackmd.io/_uploads/HJILe5wqC.png)

Click on the Pools button on the sidebar.

![2024-08-12-142945_812x500_scrot](https://hackmd.io/_uploads/BkOPg9w90.png)

Click on the pool you are interested in adding liquidity to.

On the right side, you can enter the desired amount of liquidity—the other amount will adjust accordingly to the current ratio.

![2024-08-12-143529_532x690_scrot](https://hackmd.io/_uploads/By0zZ9D5C.png)

After signing the message, you can see your amount of shares and their value in the pool.

![signal-2024-08-12-143928_003](https://hackmd.io/_uploads/B1gSW5v9A.png)


When you want to withdraw the tokens, just click on the Remove liquidity tab.

![signal-2024-08-12-143928_004](https://hackmd.io/_uploads/B19rbcD5C.png)


Enter the amount of LP Share tokens you want to burn (they represent your total stake in the pool).

![signal-2024-08-12-143928_005](https://hackmd.io/_uploads/HJGLW9wcR.png)


Again, after signing the message, you can see that your liquidity has been successfully withdrawn.

![signal-2024-08-12-143928_006](https://hackmd.io/_uploads/H1KIW9w50.png)


# CLI

The other method is to use the soroban-cli command-line tool.

I will refer to the official documentation regarding the installation method: https://github.com/stellar/stellar-cli?tab=readme-ov-file#installation

Then you need to configure the mainnet access. This is the recommended RPC endpoint:
```bash!
$ soroban config network add --global mainnet \
  --rpc-url https://horizon.stellar.org \
  --network-passphrase "Public Global Stellar Network ; September 2015"
```

After you are ready, you need to configure your keys.

```bash!
$ soroban keys add --help
Add a new identity (keypair, ledger, macOS keychain)

Usage: soroban keys add [OPTIONS] <NAME>

Arguments:
  <NAME>  Name of identity

Options:
      --secret-key   Add using secret_key Can provide with SOROBAN_SECRET_KEY
      --seed-phrase  Add using 12 word seed phrase to generate secret_key
      --global       Use global config
  -h, --help         Print help
```

At this point, you should be ready to execute messages on deployed soroban smart contracts.

Let's test it by calling the query_pool_info message on the XLM/USDC pool.
To confirm the address of the pool you want to operate on, you can check the URL at app.phoenix-hub.io:
https://app.phoenix-hub.io/pools/CBHCRSVX3ZZ7EGTSYMKPEFGZNWRVCSESQR3UABET4MIW52N4EVU6BIZX

Let's call it:
```bash!
$ soroban contract invoke \
--id CBHCRSVX3ZZ7EGTSYMKPEFGZNWRVCSESQR3UABET4MIW52N4EVU6BIZX \
--source localaccount \
--network mainnet \
-- \
query_pool_info | jq .
{
  "asset_a": {
    "address": "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA",
    "amount": "3186306147121"
  },
  "asset_b": {
    "address": "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
    "amount": "314998896183"
  },
  "asset_lp_share": {
    "address": "CBTCSVZBJFGMW7E2LKFKRIUARUKZK2DTBUC7X5QPQJLXMAH42DB3ALE5",
    "amount": "1001839774671"
  },
  "stake_address": "CABWEFVXUB3XWYPTWFETEGJR2WRGE2ZKYYLZDLV3EBUVFMOU4ENK4DJC"
}
```

Now you are ready to provide liquidity.
With CLI, it gets a little bit more difficult, since we can't adjust the token ratio automatically.
To see help, you can call:
```bash!
$ soroban contract invoke \
    --id CBHCRSVX3ZZ7EGTSYMKPEFGZNWRVCSESQR3UABET4MIW52N4EVU6BIZX \
    --source localaccount \
    --network mainnet \
    -- \
    provide_liquidity --help
Usage Notes:
Each arg has a corresponding --<arg_name>-file-path which is a path to a file containing the corresponding JSON argument.
Note: The only types which aren't JSON are Bytes and Bytes which are raw bytes

Usage: provide_liquidity [OPTIONS]

Options:
      --desired_a <Option<i128>>
          Example:
            --desired_a -100

      --sender <Address>
          Can be public key (G13..), a contract hash (6c45307) or an identity (alice),
          Example:
            --sender GDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCR4W4

      --custom_slippage_bps <Option<i64>>
          Example:
            --custom_slippage_bps -42

      --min_b <Option<i128>>
          Example:
            --min_b -100

      --desired_b <Option<i128>>
          Example:
            --desired_b -100

      --min_a <Option<i128>>
          Example:
            --min_a -100

  -h, --help
          Print help (see a summary with '-h')
```

In the case of the XLM/USDC pool, desired_a/min_a refers to XLM, while the _b token is USDC. You can confirm it by calling further queries on their addresses (obtained from the query_pool_info called earlier):
```bash!
$ soroban contract invoke \
    --id CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA \
    --source localaccount \
    --network mainnet \
    -- \
    symbol
"native"
$ soroban contract invoke \
    --id CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75 \
    --source localaccount \
    --network mainnet \
    -- \
    symbol
"USDC"
```

Now we can try to provide liquidity. In the best-case scenario (you have the required amount of tokens and you provide them with the correct ratio), it should look like this:
```bash!
$ soroban contract invoke \
    --id CBHCRSVX3ZZ7EGTSYMKPEFGZNWRVCSESQR3UABET4MIW52N4EVU6BIZX \
    --source localaccount \
    --network mainnet \
    -- \
    provide_liquidity --sender GD...RBB --desired_a 1000000000  --desired_b 98860000
    
$ 
```
Please note that the GD...RBB should be your wallet's full public address. You can obtain it through CLI like this:
```bash!
$ soroban config identity address localaccount
GD...RBB
```

Then you can confirm that you received the LP share tokens, which represent your shares in the pool (again, the address is taken from the earlier query_pool_info call):
```bash!
$ soroban contract invoke \
--id CBTCSVZBJFGMW7E2LKFKRIUARUKZK2DTBUC7X5QPQJLXMAH42DB3ALE5 \
--source localaccount \
--network mainnet \
-- \
balance --id GD...RBB
"314419768"
```

If the ratio of tokens you are trying to provide differs too much from the existing ratio, the transaction will fail. The call stack is long and pretty rough, but you will see something along the lines of Error(Contract, #9) (it's unfortunate we can't provide more clarity with the responses right now).

If you provide more tokens than you actually have in your wallet, you should see the error: resulting balance is not within the allowed range.

The process of withdrawing liquidity is very similar and simpler.
This is the message:
```bash!
$ soroban contract invoke \
    --id CBHCRSVX3ZZ7EGTSYMKPEFGZNWRVCSESQR3UABET4MIW52N4EVU6BIZX \
    --source localaccount \
    --network mainnet \
    -- \
    withdraw_liquidity --help
Usage Notes:
Each arg has a corresponding --<arg_name>-file-path which is a path to a file containing the corresponding JSON argument.
Note: The only types which aren't JSON are Bytes and Bytes which are raw bytes

Usage: withdraw_liquidity [OPTIONS]

Options:
      --min_a <i128>
          Example:
            --min_a -100

      --sender <Address>
          Can be public key (G13..), a contract hash (6c45307) or an identity (alice),
          Example:
            --sender GDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCR4W4

      --min_b <i128>
          Example:
            --min_b -100

      --share_amount <i128>
          Example:
            --share_amount -100

  -h, --help
          Print help (see a summary with '-h')
```

And the actual call itself:
```bash!
$ soroban contract invoke \
    --id CBHCRSVX3ZZ7EGTSYMKPEFGZNWRVCSESQR3UABET4MIW52N4EVU6BIZX \
    --source localaccount \
    --network mainnet \
    -- \
    withdraw_liquidity --sender GD...RBB --share_amount 314419768 --min_a 999990000 --min_b 9990000
["999997868","99959999"]
```

Due to subsequent trades on the platform, the ratio constantly changes slightly, hence it's recommended to use a bit smaller amounts for `min_a` and `min_b` arguments.