Two-chain LP accounting: - Mirror liquidity ledger on both chains for symmetry - Outbound deposits/withdraws: update ledger + move tokens - Inbound deposits/withdraws: update ledger but only token movement for withdraws HandleBatchWithdraw() handles local/remote liquidity withdraw requests. local=tr
(batch *lib.DexBatch, counterChainId uint64, x, y *uint64, local bool)
| 373 | // HandleBatchWithdraw() handles local/remote liquidity withdraw requests. |
| 374 | // local=true: x=local pool, y=counter mirror; local=false: x=counter mirror, y=local pool. |
| 375 | func (s *StateMachine) HandleBatchWithdraw(batch *lib.DexBatch, counterChainId uint64, x, y *uint64, local bool) lib.ErrorI { |
| 376 | if len(batch.Withdrawals) == 0 { |
| 377 | return nil |
| 378 | } |
| 379 | // initialize vars |
| 380 | var totalPointsToRemove uint64 |
| 381 | // get liquidity pool |
| 382 | p, err := s.GetPool(counterChainId + LiquidityPoolAddend) |
| 383 | if err != nil { |
| 384 | return err |
| 385 | } |
| 386 | // collect withdrawals |
| 387 | for _, w := range batch.Withdrawals { |
| 388 | initialPoints, e := p.GetPointsFor(w.Address) |
| 389 | if e != nil { |
| 390 | s.log.Errorf("an error occurred retrieving the pool points for: %x, %s", w.Address, e.Error()) |
| 391 | continue // defensive |
| 392 | } |
| 393 | // update the total points to remove |
| 394 | totalPointsToRemove += lib.SafeMulDiv(initialPoints, w.Percent, 100) |
| 395 | } |
| 396 | if totalPointsToRemove == 0 || p.TotalPoolPoints == 0 { |
| 397 | return nil |
| 398 | } |
| 399 | // compute totals; actual paid amounts are tracked below to avoid burning rounding dust |
| 400 | // x = 1000 RONI; totalPoolPoints = 100 ; Pablo has 50 points |
| 401 | // y = 100 CNPY; totalPoolPoints = 100 ; Pablo has 50 points |
| 402 | // Both the X and Y pool should have the same a) pool holders b) pool points per holder c) totalPoolPoints |
| 403 | // Pablo has 50 pool points and Pablo withdrawals 50% = 25 points |
| 404 | // AmountCNPYToReceiveInWithdrawal = 100 x 25 / 100 |
| 405 | // AmountRONIToReceiveInWithdrawal = 1000 x 25 / 100 |
| 406 | totalYWithdrawal := lib.SafeMulDiv(*y, totalPointsToRemove, p.TotalPoolPoints) |
| 407 | totalXWithdraw := lib.SafeMulDiv(*x, totalPointsToRemove, p.TotalPoolPoints) |
| 408 | var paidY, paidX uint64 |
| 409 | // distribute tokens |
| 410 | for _, w := range batch.Withdrawals { |
| 411 | initialPoints, e := p.GetPointsFor(w.Address) |
| 412 | if e != nil { |
| 413 | s.log.Warnf("an error occurred retrieving the pool points for: %x, %s", w.Address, e.Error()) |
| 414 | continue // defensive |
| 415 | } |
| 416 | // calculate points from percent |
| 417 | points := lib.SafeMulDiv(initialPoints, w.Percent, 100) |
| 418 | // calculate share |
| 419 | yShare := lib.SafeMulDiv(totalYWithdrawal, points, totalPointsToRemove) |
| 420 | // calculate virtual share |
| 421 | xShare := lib.SafeMulDiv(totalXWithdraw, points, totalPointsToRemove) |
| 422 | // remove points from pool |
| 423 | if err = p.RemovePoints(w.Address, points); err != nil { |
| 424 | return err |
| 425 | } |
| 426 | payout, counter := yShare, xShare |
| 427 | if local { |
| 428 | payout, counter = xShare, yShare |
| 429 | } |
| 430 | paidY += yShare |
| 431 | paidX += xShare |
| 432 | // credit user and update pool balance |