evm/aave/lendingpool.go

90 lines
3.3 KiB
Go

package aave
import (
"context"
"math/big"
"git.lehouerou.net/laurent/evm"
"git.lehouerou.net/laurent/evm/aave/contracts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
)
type LendingPool struct {
client evm.Client
contract *contracts.LendingPool
}
func NewLendingPool(client evm.Client, address common.Address) (*LendingPool, error) {
contract, err := contracts.NewLendingPool(address, client)
if err != nil {
return nil, errors.Wrap(err, "init contract")
}
return &LendingPool{
client: client,
contract: contract,
}, nil
}
type UserAccountData struct {
TotalCollateralETH decimal.Decimal
TotalDebtETH decimal.Decimal
AvailableBorrowsETH decimal.Decimal
CurrentLiquidationThreshold decimal.Decimal
CurrentLtv decimal.Decimal
MaxLtv decimal.Decimal
HealthFactor decimal.Decimal
}
func (uad UserAccountData) BorrowAmountToLtv(ltv decimal.Decimal) decimal.Decimal {
return uad.TotalCollateralETH.Mul(ltv).Div(decimal.NewFromInt(100)).Sub(uad.TotalDebtETH)
}
func (lp *LendingPool) UserAccountDataForAddress(address common.Address) (UserAccountData, error) {
uad, err := lp.contract.GetUserAccountData(&bind.CallOpts{}, address)
if err != nil {
return UserAccountData{}, errors.Wrap(err, "calling contract")
}
res := UserAccountData{
TotalCollateralETH: decimal.NewFromBigInt(uad.TotalCollateralETH, -8),
TotalDebtETH: decimal.NewFromBigInt(uad.TotalDebtETH, -8),
AvailableBorrowsETH: decimal.NewFromBigInt(uad.AvailableBorrowsETH, -8),
CurrentLiquidationThreshold: decimal.NewFromBigInt(uad.CurrentLiquidationThreshold, -2),
MaxLtv: decimal.NewFromBigInt(uad.Ltv, -2),
HealthFactor: decimal.NewFromBigInt(uad.HealthFactor, -18),
}
res.CurrentLtv = res.TotalDebtETH.Div(res.TotalCollateralETH).Mul(decimal.NewFromInt(100))
return res, nil
}
func (lp *LendingPool) Borrow(ctx context.Context, asset common.Address, amount decimal.Decimal) (evm.Transaction, error) {
token, err := lp.client.TokenService().TokenByAddress(asset)
if err != nil {
return nil, errors.Wrap(err, "getting token")
}
return lp.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return lp.contract.Borrow(opts.TransactOpts, asset, token.ValueToBigInt(amount), big.NewInt(2), 0, lp.client.PublicAddress())
})
}
func (lp *LendingPool) Repay(ctx context.Context, asset common.Address, amount decimal.Decimal) (evm.Transaction, error) {
token, err := lp.client.TokenService().TokenByAddress(asset)
if err != nil {
return nil, errors.Wrap(err, "getting token")
}
balance, err := token.Balance()
if err != nil {
return nil, errors.Wrap(err, "getting token balance")
}
if balance.LessThan(amount) {
return nil, errors.Wrapf(err, "balance of %s insufficient. need %s have %s", token.Symbol(), amount, balance)
}
return lp.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return lp.contract.Repay(opts.TransactOpts, asset, token.ValueToBigInt(amount), big.NewInt(2), lp.client.PublicAddress())
})
}