This commit is contained in:
Laurent Le Houerou 2021-11-15 14:09:43 +04:00
parent 981b42eb24
commit f1729770e3
7 changed files with 299 additions and 23 deletions

View File

@ -1 +1 @@
[{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"},{"internalType":"uint16","name":"referralCode","type":"uint16"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserAccountData","outputs":[{"internalType":"uint256","name":"totalCollateralETH","type":"uint256"},{"internalType":"uint256","name":"totalDebtETH","type":"uint256"},{"internalType":"uint256","name":"availableBorrowsETH","type":"uint256"},{"internalType":"uint256","name":"currentLiquidationThreshold","type":"uint256"},{"internalType":"uint256","name":"ltv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rateMode","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
[{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"interestRateMode","type":"uint256"},{"internalType":"uint16","name":"referralCode","type":"uint16"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint16","name":"referralCode","type":"uint16"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveData","outputs":[{"internalType":"uint256","name":"configuration","type":"uint256"},{"internalType":"uint256","name":"liquidityIndex","type":"uint256"},{"internalType":"uint256","name":"variableBorrowIndex","type":"uint256"},{"internalType":"uint256","name":"currentLiquidityRate","type":"uint256"},{"internalType":"uint256","name":"currentVariableBorrowRate","type":"uint256"},{"internalType":"uint256","name":"currentStableBorrowRate","type":"uint256"},{"internalType":"uint40","name":"lastUpdateTimestamp","type":"uint40"},{"internalType":"address","name":"aTokenAddress","type":"address"},{"internalType":"address","name":"stableDebtTokenAddress","type":"address"},{"internalType":"address","name":"variableDebtTokenAddress","type":"address"},{"internalType":"address","name":"interestRateStrategyAddress","type":"address"},{"internalType":"uint8","name":"id","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserAccountData","outputs":[{"internalType":"uint256","name":"totalCollateralETH","type":"uint256"},{"internalType":"uint256","name":"totalDebtETH","type":"uint256"},{"internalType":"uint256","name":"availableBorrowsETH","type":"uint256"},{"internalType":"uint256","name":"currentLiquidationThreshold","type":"uint256"},{"internalType":"uint256","name":"ltv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rateMode","type":"uint256"},{"internalType":"address","name":"onBehalfOf","type":"address"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

View File

@ -30,7 +30,7 @@ var (
// LendingPoolMetaData contains all meta data concerning the LendingPool contract.
var LendingPoolMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"interestRateMode\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"}],\"name\":\"borrow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"getUserAccountData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalCollateralETH\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtETH\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"availableBorrowsETH\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentLiquidationThreshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ltv\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"healthFactor\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rateMode\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"}],\"name\":\"repay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"interestRateMode\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"}],\"name\":\"borrow\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"referralCode\",\"type\":\"uint16\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"}],\"name\":\"getReserveData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"configuration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"liquidityIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"variableBorrowIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentLiquidityRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentVariableBorrowRate\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentStableBorrowRate\",\"type\":\"uint256\"},{\"internalType\":\"uint40\",\"name\":\"lastUpdateTimestamp\",\"type\":\"uint40\"},{\"internalType\":\"address\",\"name\":\"aTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"stableDebtTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"variableDebtTokenAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"interestRateStrategyAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"id\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"}],\"name\":\"getUserAccountData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalCollateralETH\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"totalDebtETH\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"availableBorrowsETH\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentLiquidationThreshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ltv\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"healthFactor\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rateMode\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"onBehalfOf\",\"type\":\"address\"}],\"name\":\"repay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
}
// LendingPoolABI is the input ABI used to generate the binding from.
@ -179,6 +179,101 @@ func (_LendingPool *LendingPoolTransactorRaw) Transact(opts *bind.TransactOpts,
return _LendingPool.Contract.contract.Transact(opts, method, params...)
}
// GetReserveData is a free data retrieval call binding the contract method 0x35ea6a75.
//
// Solidity: function getReserveData(address asset) view returns(uint256 configuration, uint256 liquidityIndex, uint256 variableBorrowIndex, uint256 currentLiquidityRate, uint256 currentVariableBorrowRate, uint256 currentStableBorrowRate, uint40 lastUpdateTimestamp, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint8 id)
func (_LendingPool *LendingPoolCaller) GetReserveData(opts *bind.CallOpts, asset common.Address) (struct {
Configuration *big.Int
LiquidityIndex *big.Int
VariableBorrowIndex *big.Int
CurrentLiquidityRate *big.Int
CurrentVariableBorrowRate *big.Int
CurrentStableBorrowRate *big.Int
LastUpdateTimestamp *big.Int
ATokenAddress common.Address
StableDebtTokenAddress common.Address
VariableDebtTokenAddress common.Address
InterestRateStrategyAddress common.Address
Id uint8
}, error) {
var out []interface{}
err := _LendingPool.contract.Call(opts, &out, "getReserveData", asset)
outstruct := new(struct {
Configuration *big.Int
LiquidityIndex *big.Int
VariableBorrowIndex *big.Int
CurrentLiquidityRate *big.Int
CurrentVariableBorrowRate *big.Int
CurrentStableBorrowRate *big.Int
LastUpdateTimestamp *big.Int
ATokenAddress common.Address
StableDebtTokenAddress common.Address
VariableDebtTokenAddress common.Address
InterestRateStrategyAddress common.Address
Id uint8
})
if err != nil {
return *outstruct, err
}
outstruct.Configuration = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
outstruct.LiquidityIndex = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int)
outstruct.VariableBorrowIndex = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int)
outstruct.CurrentLiquidityRate = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int)
outstruct.CurrentVariableBorrowRate = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int)
outstruct.CurrentStableBorrowRate = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int)
outstruct.LastUpdateTimestamp = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int)
outstruct.ATokenAddress = *abi.ConvertType(out[7], new(common.Address)).(*common.Address)
outstruct.StableDebtTokenAddress = *abi.ConvertType(out[8], new(common.Address)).(*common.Address)
outstruct.VariableDebtTokenAddress = *abi.ConvertType(out[9], new(common.Address)).(*common.Address)
outstruct.InterestRateStrategyAddress = *abi.ConvertType(out[10], new(common.Address)).(*common.Address)
outstruct.Id = *abi.ConvertType(out[11], new(uint8)).(*uint8)
return *outstruct, err
}
// GetReserveData is a free data retrieval call binding the contract method 0x35ea6a75.
//
// Solidity: function getReserveData(address asset) view returns(uint256 configuration, uint256 liquidityIndex, uint256 variableBorrowIndex, uint256 currentLiquidityRate, uint256 currentVariableBorrowRate, uint256 currentStableBorrowRate, uint40 lastUpdateTimestamp, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint8 id)
func (_LendingPool *LendingPoolSession) GetReserveData(asset common.Address) (struct {
Configuration *big.Int
LiquidityIndex *big.Int
VariableBorrowIndex *big.Int
CurrentLiquidityRate *big.Int
CurrentVariableBorrowRate *big.Int
CurrentStableBorrowRate *big.Int
LastUpdateTimestamp *big.Int
ATokenAddress common.Address
StableDebtTokenAddress common.Address
VariableDebtTokenAddress common.Address
InterestRateStrategyAddress common.Address
Id uint8
}, error) {
return _LendingPool.Contract.GetReserveData(&_LendingPool.CallOpts, asset)
}
// GetReserveData is a free data retrieval call binding the contract method 0x35ea6a75.
//
// Solidity: function getReserveData(address asset) view returns(uint256 configuration, uint256 liquidityIndex, uint256 variableBorrowIndex, uint256 currentLiquidityRate, uint256 currentVariableBorrowRate, uint256 currentStableBorrowRate, uint40 lastUpdateTimestamp, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint8 id)
func (_LendingPool *LendingPoolCallerSession) GetReserveData(asset common.Address) (struct {
Configuration *big.Int
LiquidityIndex *big.Int
VariableBorrowIndex *big.Int
CurrentLiquidityRate *big.Int
CurrentVariableBorrowRate *big.Int
CurrentStableBorrowRate *big.Int
LastUpdateTimestamp *big.Int
ATokenAddress common.Address
StableDebtTokenAddress common.Address
VariableDebtTokenAddress common.Address
InterestRateStrategyAddress common.Address
Id uint8
}, error) {
return _LendingPool.Contract.GetReserveData(&_LendingPool.CallOpts, asset)
}
// GetUserAccountData is a free data retrieval call binding the contract method 0xbf92857c.
//
// Solidity: function getUserAccountData(address user) view returns(uint256 totalCollateralETH, uint256 totalDebtETH, uint256 availableBorrowsETH, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)

View File

@ -18,4 +18,19 @@ interface ILendingPool {
uint256 ltv,
uint256 healthFactor
);
function getReserveData(address asset) external view returns (
uint256 configuration,
uint256 liquidityIndex,
uint256 variableBorrowIndex,
uint256 currentLiquidityRate,
uint256 currentVariableBorrowRate,
uint256 currentStableBorrowRate,
uint40 lastUpdateTimestamp,
address aTokenAddress,
address stableDebtTokenAddress,
address variableDebtTokenAddress,
address interestRateStrategyAddress,
uint8 id
);
}

View File

@ -3,6 +3,7 @@ package aave
import (
"context"
"math/big"
"time"
"git.lehouerou.net/laurent/evm"
"git.lehouerou.net/laurent/evm/aave/contracts"
@ -13,6 +14,10 @@ import (
"github.com/shopspring/decimal"
)
const (
SECONDSPERYEAR = 31536000
)
type LendingPool struct {
client evm.Client
contract *contracts.LendingPool
@ -61,29 +66,56 @@ func (lp *LendingPool) UserAccountDataForAddress(address common.Address) (UserAc
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())
})
type ReserveData struct {
LiquidityIndex decimal.Decimal
VariableBorrowIndex decimal.Decimal
CurrentLiquidityRate decimal.Decimal
CurrentVariableBorrowRate decimal.Decimal
CurrentStableBorrowRate decimal.Decimal
LastUpdateTimestamp time.Time
ATokenAddress common.Address
StableDebtTokenAddress common.Address
VariableDebtTokenAddress common.Address
InterestRateStrategyAddress common.Address
Id uint8
}
func (lp *LendingPool) Repay(ctx context.Context, asset common.Address, amount decimal.Decimal) (evm.Transaction, error) {
token, err := lp.client.TokenService().TokenByAddress(asset)
func (lp *LendingPool) ReserveData(asset evm.Token) (ReserveData, error) {
rd, err := lp.contract.GetReserveData(&bind.CallOpts{}, asset.Address())
if err != nil {
return nil, errors.Wrap(err, "getting token")
return ReserveData{}, errors.Wrap(err, "calling contract")
}
balance, err := token.Balance()
res := ReserveData{
LiquidityIndex: decimal.NewFromBigInt(rd.LiquidityIndex, -27),
VariableBorrowIndex: decimal.NewFromBigInt(rd.VariableBorrowIndex, -27),
CurrentLiquidityRate: decimal.NewFromBigInt(rd.CurrentLiquidityRate, -27),
CurrentVariableBorrowRate: decimal.NewFromBigInt(rd.CurrentVariableBorrowRate, -27),
CurrentStableBorrowRate: decimal.NewFromBigInt(rd.CurrentStableBorrowRate, -27),
LastUpdateTimestamp: time.Unix(rd.LastUpdateTimestamp.Int64(), 0),
ATokenAddress: rd.ATokenAddress,
StableDebtTokenAddress: rd.StableDebtTokenAddress,
VariableDebtTokenAddress: rd.VariableDebtTokenAddress,
InterestRateStrategyAddress: rd.InterestRateStrategyAddress,
Id: rd.Id,
}
return res, nil
}
func (lp *LendingPool) Borrow(ctx context.Context, asset evm.Token, amount decimal.Decimal, opts ...evm.ExecutionOption) (evm.Transaction, error) {
return lp.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return lp.contract.Borrow(opts.TransactOpts, asset.Address(), asset.ValueToBigInt(amount), big.NewInt(2), 0, lp.client.PublicAddress())
}, opts...)
}
func (lp *LendingPool) Repay(ctx context.Context, asset evm.Token, amount decimal.Decimal, opts ...evm.ExecutionOption) (evm.Transaction, error) {
balance, err := asset.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 nil, errors.Wrapf(err, "balance of %s insufficient. need %s have %s", asset.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())
})
return lp.contract.Repay(opts.TransactOpts, asset.Address(), asset.ValueToBigInt(amount), big.NewInt(2), lp.client.PublicAddress())
}, opts...)
}

View File

@ -74,5 +74,6 @@ func NewTokenMapper() evm.TokenMapper {
tm.AddAlias("ETH", "WETH.e")
tm.AddAlias("BTC", "WBTC.e")
tm.AddAlias("USDC", "USDC.e")
tm.AddAlias("DAI", "DAI.e")
return tm
}

View File

@ -1,10 +1,13 @@
package beefy
import (
"context"
"git.lehouerou.net/laurent/evm"
"git.lehouerou.net/laurent/evm/beefy/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"
)
@ -13,6 +16,8 @@ type VaultV6 struct {
evm.Token
client evm.Client
contract *contracts.VaultV6
Underlying evm.Token
}
func NewVaultV6(client evm.Client, address common.Address) (*VaultV6, error) {
@ -24,11 +29,20 @@ func NewVaultV6(client evm.Client, address common.Address) (*VaultV6, error) {
if err != nil {
return nil, errors.Wrap(err, "init token contract")
}
wantaddress, err := contract.Want(&bind.CallOpts{})
if err != nil {
return nil, errors.Wrap(err, "getting underlying token address")
}
want, err := client.TokenService().TokenByAddress(wantaddress)
if err != nil {
return nil, errors.Wrap(err, "init underlying token contract")
}
return &VaultV6{
client: client,
contract: contract,
Token: token,
client: client,
contract: contract,
Token: token,
Underlying: want,
}, nil
}
@ -39,3 +53,15 @@ func (v *VaultV6) PricePerFullShare() (decimal.Decimal, error) {
}
return v.ValueFromBigInt(ppfs), nil
}
func (v *VaultV6) Deposit(ctx context.Context, amount decimal.Decimal, opts ...evm.ExecutionOption) (evm.Transaction, error) {
return v.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return v.contract.Deposit(opts.TransactOpts, v.Underlying.ValueToBigInt(amount))
}, opts...)
}
func (v *VaultV6) Withdraw(ctx context.Context, amount decimal.Decimal, opts ...evm.ExecutionOption) (evm.Transaction, error) {
return v.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return v.contract.Withdraw(opts.TransactOpts, v.ValueToBigInt(amount))
}, opts...)
}

View File

@ -1,6 +1,11 @@
package curve
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"git.lehouerou.net/laurent/evm"
"git.lehouerou.net/laurent/evm/curve/contracts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@ -31,11 +36,35 @@ func NewPool(client evm.Client, address common.Address) (*Pool, error) {
if err != nil {
return nil, errors.Wrap(err, "getting lp token")
}
return &Pool{
p := &Pool{
client: client,
contract: contract,
lptoken: lptoken,
}, nil
}
tokens, err := p.getTokens()
if err != nil {
return nil, errors.Wrap(err, "getting underlying tokens")
}
p.tokens = tokens
return p, nil
}
func (p *Pool) getTokens() ([]evm.Token, error) {
var res []evm.Token
var index int64
for {
t, err := p.contract.UnderlyingCoins(&bind.CallOpts{}, big.NewInt(index))
if err != nil {
break
}
token, err := p.client.TokenService().TokenByAddress(t)
if err != nil {
return nil, errors.Wrapf(err, "getting token %s", t)
}
res = append(res, token)
index++
}
return res, nil
}
func (p *Pool) VirtualPrice() (decimal.Decimal, error) {
@ -47,4 +76,82 @@ func (p *Pool) VirtualPrice() (decimal.Decimal, error) {
return decimal.NewFromBigInt(price, -18), nil
}
//func (p *Pool) CalculateTokenAmount(amounts []decimal.Decimal)
func (p *Pool) CalculateTokenAmountForOneUnderlying(token evm.Token, amount decimal.Decimal, isdeposit bool) (decimal.Decimal, error) {
tokenindex, err := p.getTokenIndex(token)
if err != nil {
return decimal.Zero, errors.Wrap(err, "getting token index")
}
var amounts [3]*big.Int
for i, _ := range p.tokens {
if i == tokenindex {
amounts[i] = token.ValueToBigInt(amount)
} else {
amounts[i] = big.NewInt(0)
}
}
raw, err := p.contract.CalcTokenAmount(&bind.CallOpts{}, amounts, isdeposit)
if err != nil {
return decimal.Zero, errors.Wrap(err, "calling contract")
}
return p.lptoken.ValueFromBigInt(raw), nil
}
func (p *Pool) CalculateWithdrawOneCoin(token evm.Token, amount decimal.Decimal) (decimal.Decimal, error) {
tokenindex, err := p.getTokenIndex(token)
if err != nil {
return decimal.Zero, errors.Wrap(err, "getting token index")
}
raw, err := p.contract.CalcWithdrawOneCoin(&bind.CallOpts{}, p.lptoken.ValueToBigInt(amount), big.NewInt(int64(tokenindex)))
if err != nil {
return decimal.Zero, errors.Wrap(err, "calling contract")
}
return token.ValueFromBigInt(raw), nil
}
func (p *Pool) getTokenIndex(token evm.Token) (int, error) {
tokenindex := -1
for i, t := range p.tokens {
if t.Address() == token.Address() {
tokenindex = i
break
}
}
if tokenindex == -1 {
return -1, errors.Errorf("token %s not found in pool", token.Symbol())
}
return tokenindex, nil
}
func (p *Pool) AddLiquidity(ctx context.Context, token evm.Token, amount decimal.Decimal, opts ...evm.ExecutionOption) (evm.Transaction, error) {
tokenindex, err := p.getTokenIndex(token)
if err != nil {
return nil, errors.Wrap(err, "getting token index")
}
amounts := [3]*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)}
amounts[tokenindex] = token.ValueToBigInt(amount)
minMintAmount, err := p.CalculateTokenAmountForOneUnderlying(token, amount, true)
if err != nil {
return nil, errors.Wrap(err, "getting mint amount")
}
minMintAmount = minMintAmount.Sub(minMintAmount.Mul(decimal.NewFromFloat(0.01))).Round(int32(p.lptoken.Decimals()))
return p.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return p.contract.AddLiquidity0(opts.TransactOpts, amounts, p.lptoken.ValueToBigInt(minMintAmount), true)
}, opts...)
}
func (p *Pool) RemoveLiquidity(ctx context.Context, token evm.Token, amount decimal.Decimal, opts ...evm.ExecutionOption) (evm.Transaction, error) {
tokenindex, err := p.getTokenIndex(token)
if err != nil {
return nil, errors.Wrap(err, "getting token index")
}
minamount, err := p.CalculateWithdrawOneCoin(token, amount)
if err != nil {
return nil, errors.Wrap(err, "calculate withdraw one coin")
}
minamount = minamount.Sub(minamount.Mul(decimal.NewFromFloat(0.002))).Round(int32(token.Decimals()))
return p.client.Execute(ctx, func(ctx context.Context, opts *evm.TransactOpts) (*types.Transaction, error) {
return p.contract.RemoveLiquidityOneCoin0(opts.TransactOpts, p.lptoken.ValueToBigInt(amount), big.NewInt(int64(tokenindex)), token.ValueToBigInt(minamount), true)
}, opts...)
}