From f1729770e3247ede4dc1c9e59b6cd3d238e49512 Mon Sep 17 00:00:00 2001 From: Laurent Le Houerou Date: Mon, 15 Nov 2021 14:09:43 +0400 Subject: [PATCH] wip --- aave/contracts/ILendingPool.abi | 2 +- aave/contracts/lendingpool.go | 97 ++++++++++++++++++++++++++- aave/contracts/lendingpool.sol | 15 +++++ aave/lendingpool.go | 62 +++++++++++++----- avalanche/tokenmapper.go | 1 + beefy/vaultv6.go | 32 ++++++++- curve/Pool.go | 113 +++++++++++++++++++++++++++++++- 7 files changed, 299 insertions(+), 23 deletions(-) diff --git a/aave/contracts/ILendingPool.abi b/aave/contracts/ILendingPool.abi index 847aae1..82aca7a 100644 --- a/aave/contracts/ILendingPool.abi +++ b/aave/contracts/ILendingPool.abi @@ -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"}] \ No newline at end of file +[{"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"}] \ No newline at end of file diff --git a/aave/contracts/lendingpool.go b/aave/contracts/lendingpool.go index 7f6e2e4..5f0392c 100644 --- a/aave/contracts/lendingpool.go +++ b/aave/contracts/lendingpool.go @@ -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) diff --git a/aave/contracts/lendingpool.sol b/aave/contracts/lendingpool.sol index 42fd8f8..cd93608 100644 --- a/aave/contracts/lendingpool.sol +++ b/aave/contracts/lendingpool.sol @@ -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 + ); } \ No newline at end of file diff --git a/aave/lendingpool.go b/aave/lendingpool.go index 4468550..867ca02 100644 --- a/aave/lendingpool.go +++ b/aave/lendingpool.go @@ -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...) } diff --git a/avalanche/tokenmapper.go b/avalanche/tokenmapper.go index 9a54a6f..61c6a0b 100644 --- a/avalanche/tokenmapper.go +++ b/avalanche/tokenmapper.go @@ -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 } diff --git a/beefy/vaultv6.go b/beefy/vaultv6.go index 1a5eab4..952d2b3 100644 --- a/beefy/vaultv6.go +++ b/beefy/vaultv6.go @@ -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...) +} diff --git a/curve/Pool.go b/curve/Pool.go index ddb53e5..afa9db6 100644 --- a/curve/Pool.go +++ b/curve/Pool.go @@ -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...) +}