Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit 9eaf72e

Browse files
authored
Add Code Snippets for Liquidity Pools (#653)
1 parent 8970934 commit 9eaf72e

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed

content/docs/glossary/liquidity-pool.mdx

+189
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,69 @@ function preamble() {
160160
}
161161
```
162162

163+
```python
164+
from decimal import Decimal
165+
from typing import List, Any, Dict
166+
167+
from stellar_sdk import *
168+
169+
server = Server("https://horizon-testnet.stellar.org")
170+
171+
172+
# Preamble
173+
def new_tx_builder(source: str) -> TransactionBuilder:
174+
network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE
175+
base_fee = 100
176+
source_account = server.load_account(source)
177+
builder = TransactionBuilder(
178+
source_account=source_account, network_passphrase=network_passphrase, base_fee=base_fee
179+
).set_timeout(30)
180+
return builder
181+
182+
183+
# Returns the given asset pair in "protocol order."
184+
def order_asset(a: Asset, b: Asset) -> List[Asset]:
185+
return [a, b] if LiquidityPoolAsset.is_valid_lexicographic_order(a, b) else [b, a]
186+
187+
188+
secrets = [
189+
"SBGCD73TK2PTW2DQNWUYZSTCTHHVJPL4GZF3GVZMCDL6GYETYNAYOADN",
190+
"SAAQFHI2FMSIC6OFPWZ3PDIIX3OF64RS3EB52VLYYZBX6GYB54TW3Q4U",
191+
"SCJWYFTBDMDPAABHVJZE3DRMBRTEH4AIC5YUM54QGW57NUBM2XX6433P",
192+
]
193+
kps = [Keypair.from_secret(secret=secret) for secret in secrets]
194+
195+
# kp0 issues the assets
196+
kp0 = kps[0]
197+
asset_a, asset_b = order_asset(Asset("A", kp0.public_key), Asset("B", kp0.public_key))
198+
199+
200+
def distribute_assets(
201+
issuer_kp: Keypair, recipient_kp: Keypair, assets: List[Asset]
202+
) -> Dict[str, Any]:
203+
builder = new_tx_builder(issuer_kp.public_key)
204+
for asset in assets:
205+
builder.append_change_trust_op(
206+
asset=asset, limit="100000", source=recipient_kp.public_key
207+
).append_payment_op(
208+
destination=recipient_kp.public_key,
209+
asset=asset,
210+
amount="100000",
211+
source=issuer_kp.public_key,
212+
)
213+
214+
tx = builder.build()
215+
tx.sign(issuer_kp)
216+
tx.sign(recipient_kp)
217+
resp = server.submit_transaction(tx)
218+
return resp
219+
220+
221+
def preamble() -> None:
222+
resp1 = distribute_assets(kp0, kps[1], [asset_a, asset_b])
223+
resp2 = distribute_assets(kp0, kps[2], [asset_a, asset_b])
224+
# ...
225+
```
163226
</CodeExample>
164227

165228
Here, we use `distributeAssets()` to establish trustlines and set up initial balances of two custom assets (`A` and `B`, issued by `kp1`) for two accounts (`kp2` and `kp3`). For someone to participate in the pool, they must establish trustlines to each of the asset issuers *and* to the pool share asset (explained below).
@@ -188,6 +251,19 @@ function establishPoolTrustline(account, keypair, poolAsset) {
188251
}
189252
```
190253

254+
```python
255+
pool_share_asset = LiquidityPoolAsset(asset_a=asset_a, asset_b=asset_b)
256+
257+
258+
def establish_pool_trustline(source: Keypair, pool_asset: LiquidityPoolAsset) -> Dict[str, Any]:
259+
tx = (
260+
new_tx_builder(source.public_key)
261+
.append_change_trust_op(asset=pool_asset, limit="100000")
262+
.build()
263+
)
264+
tx.sign(source)
265+
return server.submit_transaction(tx)
266+
```
191267
</CodeExample>
192268

193269
This lets the participants hold pool shares (refer to the discussion about [pool shares earlier](#liquidity-pool-participation) for details), which means now they can perform deposits and withdrawals.
@@ -223,6 +299,33 @@ function addLiquidity(source, signer, poolId, maxReserveA, maxReserveB) {
223299
}
224300
```
225301

302+
```python
303+
pool_id = pool_share_asset.liquidity_pool_id
304+
305+
306+
def add_liquidity(
307+
source: Keypair,
308+
pool_id: str,
309+
max_reserve_a: Decimal,
310+
max_reserve_b: Decimal,
311+
) -> dict[str, Any]:
312+
exact_price = max_reserve_a / max_reserve_b
313+
min_price = exact_price - exact_price * Decimal("0.1")
314+
max_price = exact_price + exact_price * Decimal("0.1")
315+
tx = (
316+
new_tx_builder(source.public_key)
317+
.append_liquidity_pool_deposit_op(
318+
liquidity_pool_id=pool_id,
319+
max_amount_a=f"{max_reserve_a:.7f}",
320+
max_amount_b=f"{max_reserve_b:.7f}",
321+
min_price=min_price,
322+
max_price=max_price,
323+
)
324+
.build()
325+
)
326+
tx.sign(source)
327+
return server.submit_transaction(tx)
328+
```
226329
</CodeExample>
227330

228331
When depositing assets into a liquidity pool, you need to define your acceptable price bounds. In the above function, we allow for a +/-10% margin of error from the "spot price". This margin is **by no means a recommendation** and is chosen just for demonstration.
@@ -249,6 +352,38 @@ function removeLiquidity(source, signer, poolId, minReserveA, minReserveB) {
249352
}
250353
```
251354

355+
```python
356+
def remove_liquidity(
357+
source: Keypair, pool_id: str, shares_amount: Decimal
358+
) -> dict[str, Any]:
359+
pool_info = server.liquidity_pools().liquidity_pool(pool_id).call()
360+
total_shares = Decimal(pool_info["total_shares"])
361+
min_reserve_a = (
362+
shares_amount
363+
/ total_shares
364+
* Decimal(pool_info["reserves"][0]["amount"])
365+
* Decimal("0.95")
366+
) #
367+
min_reserve_b = (
368+
shares_amount
369+
/ total_shares
370+
* Decimal(pool_info["reserves"][1]["amount"])
371+
* Decimal("0.95")
372+
)
373+
tx = (
374+
new_tx_builder(source.public_key)
375+
.append_liquidity_pool_withdraw_op(
376+
liquidity_pool_id=pool_id,
377+
amount=f"{shares_amount:.7f}",
378+
min_amount_a=f"{min_reserve_a:.7f}",
379+
min_amount_b=f"{min_reserve_b:.7f}",
380+
)
381+
.build()
382+
)
383+
tx.sign(source)
384+
return server.submit_transaction(tx)
385+
```
386+
252387
</CodeExample>
253388

254389
Notice here that we specify the minimum amount. Much like with a strict-receive path payment, we're specifying that we're not willing to receive less than this amount of each asset from the pool. This effectively defines a minimum withdrawal price.
@@ -291,6 +426,45 @@ function getSpotPrice() {
291426
preamble().then(main);
292427
```
293428

429+
```python
430+
def main():
431+
deposit_a = Decimal(1000)
432+
deposit_b = Decimal(3000) # maintain a 1:3 ratio
433+
establish_pool_trustline(kps[1], pool_share_asset)
434+
add_liquidity(kps[1], pool_id, deposit_a, deposit_b)
435+
get_spot_price()
436+
437+
deposit_a = Decimal(2000)
438+
deposit_b = Decimal(6000) # maintain a 1:3 ratio
439+
establish_pool_trustline(kps[2], pool_share_asset)
440+
add_liquidity(kps[2], pool_id, deposit_a, deposit_b)
441+
get_spot_price()
442+
443+
# kp1 takes all his/her shares out
444+
balance = 0
445+
for b in server.accounts().account_id(kps[1].public_key).call()["balances"]:
446+
if (
447+
b["asset_type"] == "liquidity_pool_shares"
448+
and b["liquidity_pool_id"] == pool_id
449+
):
450+
balance = Decimal(b["balance"])
451+
break
452+
if not balance:
453+
raise
454+
remove_liquidity(kps[1], pool_id, balance)
455+
get_spot_price()
456+
457+
def get_spot_price():
458+
resp = server.liquidity_pools().liquidity_pool(pool_id).call()
459+
amount_a = resp["reserves"][0]["amount"]
460+
amount_b = resp["reserves"][1]["amount"]
461+
spot_price = Decimal(amount_a) / Decimal(amount_b)
462+
print(f"Price: {amount_a}/{amount_b} = {spot_price:.7f}")
463+
464+
if __name__ == '__main__':
465+
preamble()
466+
main()
467+
```
294468
</CodeExample>
295469
296470
### Watching Liquidity Pool Activity
@@ -315,4 +489,19 @@ server.operations()
315489
});
316490
```
317491

492+
```python
493+
def watch_liquidity_pool_activity():
494+
for op in (
495+
server.operations()
496+
.for_liquidity_pool(liquidity_pool_id=pool_id)
497+
.cursor("now")
498+
.stream()
499+
):
500+
if op["type"] == "liquidity_pool_deposit":
501+
print("Reserves deposited:")
502+
for r in op["reserves_deposited"]:
503+
print(f" {r['amount']} of {r['asset']}")
504+
print(f" for pool shares: {op['shares_received']}")
505+
# ...
506+
```
318507
</CodeExample>

0 commit comments

Comments
 (0)