Skip to content

Commit

Permalink
feat: support balancer-v1
Browse files Browse the repository at this point in the history
  • Loading branch information
0xh9n committed Nov 30, 2023
1 parent 065cdb5 commit fbbde53
Show file tree
Hide file tree
Showing 13 changed files with 1,129 additions and 0 deletions.
28 changes: 28 additions & 0 deletions pkg/liquidity-source/balancer-v1/abis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package balancerv1

import (
"bytes"

"github.com/ethereum/go-ethereum/accounts/abi"
)

var (
bPoolABI abi.ABI
)

func init() {
builder := []struct {
ABI *abi.ABI
data []byte
}{
{&bPoolABI, bPoolJson},
}

for _, b := range builder {
var err error
*b.ABI, err = abi.JSON(bytes.NewReader(b.data))
if err != nil {
panic(err)
}
}
}
1 change: 1 addition & 0 deletions pkg/liquidity-source/balancer-v1/abis/BPool.json

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions pkg/liquidity-source/balancer-v1/b_const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package balancerv1

import (
"github.com/KyberNetwork/blockchain-toolkit/number"
"github.com/holiman/uint256"
)

var BConst *bConst

type bConst struct {
BONE *uint256.Int
MIN_BPOW_BASE *uint256.Int
MAX_BPOW_BASE *uint256.Int
BPOW_PRECISION *uint256.Int
MAX_IN_RATIO *uint256.Int
}

func init() {
BConst = &bConst{
BONE: number.Number_1e18,
MIN_BPOW_BASE: number.Number_1,
MAX_BPOW_BASE: new(uint256.Int).Sub(new(uint256.Int).Mul(number.Number_2, number.Number_1e18), number.Number_1),
BPOW_PRECISION: new(uint256.Int).Div(number.Number_1e18, number.TenPow(10)),
MAX_IN_RATIO: new(uint256.Int).Div(number.Number_1e18, number.Number_2),
}
}
96 changes: 96 additions & 0 deletions pkg/liquidity-source/balancer-v1/b_math.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package balancerv1

import (
"github.com/holiman/uint256"
)

var BMath *bMath

type bMath struct{}

func init() {
BMath = &bMath{}
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BMath.sol#L28
func (l *bMath) CalcSpotPrice(
tokenBalanceIn *uint256.Int,
tokenWeightIn *uint256.Int,
tokenBalanceOut *uint256.Int,
tokenWeightOut *uint256.Int,
swapFee *uint256.Int,
) (*uint256.Int, error) {
numer, err := BNum.BDiv(tokenBalanceIn, tokenWeightIn)
if err != nil {
return nil, err
}

denom, err := BNum.BDiv(tokenBalanceOut, tokenWeightOut)
if err != nil {
return nil, err
}

ratio, err := BNum.BDiv(numer, denom)
if err != nil {
return nil, err
}

bSubBONEAndSwapFee, err := BNum.BSub(BConst.BONE, swapFee)
if err != nil {
return nil, err
}

scale, err := BNum.BDiv(BConst.BONE, bSubBONEAndSwapFee)
if err != nil {
return nil, err
}

return BNum.BMul(ratio, scale)
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BMath.sol#L55
func (l *bMath) CalcOutGivenIn(
tokenBalanceIn *uint256.Int,
tokenWeightIn *uint256.Int,
tokenBalanceOut *uint256.Int,
tokenWeightOut *uint256.Int,
tokenAmountIn *uint256.Int,
swapFee *uint256.Int,
) (*uint256.Int, error) {
weightRatio, err := BNum.BDiv(tokenWeightIn, tokenWeightOut)
if err != nil {
return nil, err
}

adjustedIn, err := BNum.BSub(BConst.BONE, swapFee)
if err != nil {
return nil, err
}

adjustedIn, err = BNum.BMul(tokenAmountIn, adjustedIn)
if err != nil {
return nil, err
}

bAddTokenBalanceInAndAdjustedIn, err := BNum.BAdd(tokenBalanceIn, adjustedIn)
if err != nil {
return nil, err
}

y, err := BNum.BDiv(tokenBalanceIn, bAddTokenBalanceInAndAdjustedIn)
if err != nil {
return nil, err
}

foo, err := BNum.BPow(y, weightRatio)
if err != nil {
return nil, err
}

bar, err := BNum.BSub(BConst.BONE, foo)
if err != nil {
return nil, err
}

return BNum.BMul(tokenBalanceOut, bar)
}
234 changes: 234 additions & 0 deletions pkg/liquidity-source/balancer-v1/b_num.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
package balancerv1

import (
"errors"

"github.com/KyberNetwork/blockchain-toolkit/number"
"github.com/holiman/uint256"
)

var (
ErrDivZero = errors.New("ERR_DIV_ZERO")
ErrDivInternal = errors.New("ERR_DIV_INTERNAL")
ErrSubUnderflow = errors.New("ERR_SUB_UNDERFLOW")
ErrMulOverflow = errors.New("ERR_MUL_OVERFLOW")
ErrAddOverFlow = errors.New("ERR_ADD_OVERFLOW")
ErrBPowBaseTooLow = errors.New("ERR_BPOW_BASE_TOO_LOW")
ErrBPowBaseTooHigh = errors.New("ERR_BPOW_BASE_TOO_HIGH")
)

var BNum *bNum

type bNum struct {
}

func init() {
BNum = &bNum{}
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L20
func (l *bNum) BToI(a *uint256.Int) *uint256.Int {
return new(uint256.Int).Div(a, BConst.BONE)
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L27
func (l *bNum) BFloor(a *uint256.Int) *uint256.Int {
return new(uint256.Int).Mul(l.BToI(a), BConst.BONE)
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L34
func (l *bNum) BAdd(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) {
c := new(uint256.Int).Add(a, b)

if c.Cmp(a) < 0 {
return nil, ErrAddOverFlow
}

return c, nil
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L43
func (l *bNum) BSub(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) {
c, flag := l.BSubSign(a, b)

if flag {
return nil, ErrSubUnderflow
}

return c, nil
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L52
func (l *bNum) BSubSign(a *uint256.Int, b *uint256.Int) (*uint256.Int, bool) {
if a.Cmp(b) >= 0 {
return new(uint256.Int).Sub(a, b), false
}

return new(uint256.Int).Sub(b, a), true
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L63
func (l *bNum) BMul(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) {
c0 := new(uint256.Int).Mul(a, b)

if a.Cmp(number.Zero) != 0 && new(uint256.Int).Div(c0, a).Cmp(b) != 0 {
return nil, ErrMulOverflow
}

c1 := new(uint256.Int).Add(c0, new(uint256.Int).Div(BConst.BONE, number.Number_2))

if c1.Cmp(c0) < 0 {
return nil, ErrMulOverflow
}

c2 := new(uint256.Int).Div(c1, BConst.BONE)

return c2, nil
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L75
func (l *bNum) BDiv(a *uint256.Int, b *uint256.Int) (*uint256.Int, error) {
if b.Cmp(number.Zero) == 0 {
return nil, ErrDivZero
}

c0 := new(uint256.Int).Mul(a, BConst.BONE)

if a.Cmp(number.Zero) != 0 && new(uint256.Int).Div(c0, a).Cmp(BConst.BONE) != 0 {
return nil, ErrDivInternal
}

c1 := new(uint256.Int).Add(c0, new(uint256.Int).Div(b, number.Number_2))

if c1.Cmp(c0) < 0 {
return nil, ErrDivInternal
}

c2 := new(uint256.Int).Div(c1, b)

return c2, nil
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L89
func (l *bNum) BPowI(a *uint256.Int, n *uint256.Int) (*uint256.Int, error) {
var (
z *uint256.Int
err error
)

if new(uint256.Int).Mod(n, number.Number_2).Cmp(number.Zero) != 0 {
z = a
} else {
z = BConst.BONE
}

for n = new(uint256.Int).Div(n, number.Number_2); n.Cmp(number.Zero) != 0; n = new(uint256.Int).Div(n, number.Number_2) {
a, err = l.BMul(a, a)
if err != nil {
return nil, err
}

if new(uint256.Int).Mod(n, number.Number_2).Cmp(number.Zero) != 0 {
z, err = l.BMul(z, a)
if err != nil {
return nil, err
}
}
}

return z, nil
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L128C14-L128C24
func (l *bNum) BPowApprox(base *uint256.Int, exp *uint256.Int, precision *uint256.Int) (*uint256.Int, error) {
a := new(uint256.Int).Set(exp)
x, xneg := l.BSubSign(base, BConst.BONE)
term := new(uint256.Int).Set(BConst.BONE)
sum := new(uint256.Int).Set(term)
negative := false

for i := number.Number_1; term.Cmp(precision) >= 0; i = new(uint256.Int).Add(i, number.Number_1) {
bigK := new(uint256.Int).Mul(i, BConst.BONE)

bsubBigKAndBone, err := l.BSub(bigK, BConst.BONE)
if err != nil {
return nil, err
}

c, cneg := l.BSubSign(a, bsubBigKAndBone)

bmulCAndX, err := l.BMul(c, x)
if err != nil {
return nil, err
}

term, err := l.BMul(term, bmulCAndX)
if err != nil {
return nil, err
}

term, err = l.BDiv(term, bigK)
if err != nil {
return nil, err
}

if term.Cmp(number.Zero) == 0 {
break
}

if xneg {
negative = !negative
}

if cneg {
negative = !negative
}

if negative {
sum, err = l.BSub(sum, term)
if err != nil {
return nil, err
}
} else {
sum, err = l.BAdd(sum, term)
if err != nil {
return nil, err
}
}
}

return sum, nil
}

// https://github.com/balancer/balancer-core/blob/f4ed5d65362a8d6cec21662fb6eae233b0babc1f/contracts/BNum.sol#L108
func (l *bNum) BPow(base *uint256.Int, exp *uint256.Int) (*uint256.Int, error) {
if base.Cmp(BConst.MIN_BPOW_BASE) < 0 {
return nil, ErrBPowBaseTooLow
}

if base.Cmp(BConst.MAX_BPOW_BASE) > 0 {
return nil, ErrBPowBaseTooHigh
}

whole := l.BFloor(exp)
remain, err := l.BSub(exp, whole)
if err != nil {
return nil, err
}

wholePow, err := l.BPowI(base, l.BToI(whole))
if err != nil {
return nil, err
}

if remain.Cmp(number.Zero) == 0 {
return wholePow, nil
}

partialResult, err := l.BPowApprox(base, remain, BConst.BPOW_PRECISION)
if err != nil {
return nil, err
}

return l.BMul(wholePow, partialResult)
}
Loading

0 comments on commit fbbde53

Please sign in to comment.