go-ethereum/XDCxlending/lendingstate/settle_balance.go

257 lines
11 KiB
Go

package lendingstate
import (
"encoding/json"
"errors"
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/log"
)
const DefaultFeeRate = 100 // 100 / XDCXBaseFee = 100 / 10000 = 1%
var (
ErrQuantityTradeTooSmall = errors.New("quantity trade too small")
ErrInvalidCollateralPrice = errors.New("unable to retrieve price of this collateral. Please try another collateral")
)
type TradeResult struct {
Fee *big.Int
InToken common.Address
InTotal *big.Int
OutToken common.Address
OutTotal *big.Int
}
type LendingSettleBalance struct {
Taker TradeResult
Maker TradeResult
CollateralLockedAmount *big.Int
}
func (settleBalance *LendingSettleBalance) String() string {
jsonData, _ := json.Marshal(settleBalance)
return string(jsonData)
}
func GetSettleBalance(isXDCXLendingFork bool,
takerSide string,
lendTokenXDCPrice,
collateralPrice,
depositRate,
borrowFeeRate *big.Int,
lendingToken,
collateralToken common.Address,
lendTokenDecimal,
collateralTokenDecimal *big.Int,
quantityToLend *big.Int) (*LendingSettleBalance, error) {
log.Debug("GetSettleBalance", "takerSide", takerSide, "borrowFeeRate", borrowFeeRate, "lendingToken", lendingToken, "collateralToken", collateralToken, "quantityToLend", quantityToLend)
if collateralPrice == nil || collateralPrice.Sign() <= 0 {
return nil, ErrInvalidCollateralPrice
}
//use the defaultFee to validate small orders
defaultFee := new(big.Int).Mul(quantityToLend, new(big.Int).SetUint64(DefaultFeeRate))
defaultFee = new(big.Int).Div(defaultFee, common.XDCXBaseFee)
var result *LendingSettleBalance
//result = map[common.Address]map[string]interface{}{}
if !isXDCXLendingFork {
if takerSide == Borrowing {
// taker = Borrower : takerOutTotal = CollateralLockedAmount = quantityToLend * collateral Token Decimal/ CollateralPrice * deposit rate
takerOutTotal := new(big.Int).Mul(quantityToLend, collateralTokenDecimal)
takerOutTotal = new(big.Int).Mul(takerOutTotal, depositRate) // eg: depositRate = 150%
takerOutTotal = new(big.Int).Div(takerOutTotal, big.NewInt(100))
takerOutTotal = new(big.Int).Div(takerOutTotal, collateralPrice)
// Fee
// takerFee = quantityToLend*borrowFeeRate/baseFee
takerFee := new(big.Int).Mul(quantityToLend, borrowFeeRate)
takerFee = new(big.Int).Div(takerFee, common.XDCXBaseFee)
if quantityToLend.Cmp(takerFee) <= 0 || quantityToLend.Cmp(defaultFee) <= 0 {
log.Debug("quantity lending too small", "quantityToLend", quantityToLend, "takerFee", takerFee)
return result, ErrQuantityTradeTooSmall
}
if lendingToken != common.XDCNativeAddressBinary && lendTokenXDCPrice != nil && lendTokenXDCPrice.Cmp(common.Big0) > 0 {
exTakerReceivedFee := new(big.Int).Mul(takerFee, lendTokenXDCPrice)
exTakerReceivedFee = new(big.Int).Div(exTakerReceivedFee, lendTokenDecimal)
defaultFeeInXDC := new(big.Int).Mul(defaultFee, lendTokenXDCPrice)
defaultFeeInXDC = new(big.Int).Div(defaultFeeInXDC, lendTokenDecimal)
if (exTakerReceivedFee.Cmp(common.RelayerLendingFee) <= 0 && exTakerReceivedFee.Sign() > 0) || defaultFeeInXDC.Cmp(common.RelayerLendingFee) <= 0 {
log.Debug("takerFee too small", "quantityToLend", quantityToLend, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "borrowFeeRate", borrowFeeRate, "defaultFeeInXDC", defaultFeeInXDC)
return result, ErrQuantityTradeTooSmall
}
} else if lendingToken == common.XDCNativeAddressBinary {
exTakerReceivedFee := takerFee
if (exTakerReceivedFee.Cmp(common.RelayerLendingFee) <= 0 && exTakerReceivedFee.Sign() > 0) || defaultFee.Cmp(common.RelayerLendingFee) <= 0 {
log.Debug("takerFee too small", "quantityToLend", quantityToLend, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "borrowFeeRate", borrowFeeRate, "defaultFee", defaultFee)
return result, ErrQuantityTradeTooSmall
}
}
result = &LendingSettleBalance{
//Borrower
Taker: TradeResult{
Fee: takerFee,
InToken: lendingToken,
InTotal: new(big.Int).Sub(quantityToLend, takerFee),
OutToken: collateralToken,
OutTotal: takerOutTotal,
},
// Investor : makerOutTotal = quantityToLend
Maker: TradeResult{
Fee: common.Big0,
InToken: common.Address{},
InTotal: common.Big0,
OutToken: lendingToken,
OutTotal: quantityToLend,
},
CollateralLockedAmount: takerOutTotal,
}
} else {
// maker = Borrower : makerOutTotal = CollateralLockedAmount = quantityToLend * collateral Token Decimal / CollateralPrice * deposit rate
makerOutTotal := new(big.Int).Mul(quantityToLend, collateralTokenDecimal)
makerOutTotal = new(big.Int).Mul(makerOutTotal, depositRate) // eg: depositRate = 150%
makerOutTotal = new(big.Int).Div(makerOutTotal, big.NewInt(100))
makerOutTotal = new(big.Int).Div(makerOutTotal, collateralPrice)
// Fee
makerFee := new(big.Int).Mul(quantityToLend, borrowFeeRate)
makerFee = new(big.Int).Div(makerFee, common.XDCXBaseFee)
if quantityToLend.Cmp(makerFee) <= 0 || quantityToLend.Cmp(defaultFee) <= 0 {
log.Debug("quantity lending too small", "quantityToLend", quantityToLend, "makerFee", makerFee)
return result, ErrQuantityTradeTooSmall
}
if lendingToken != common.XDCNativeAddressBinary && lendTokenXDCPrice != nil && lendTokenXDCPrice.Cmp(common.Big0) > 0 {
exMakerReceivedFee := new(big.Int).Mul(makerFee, lendTokenXDCPrice)
exMakerReceivedFee = new(big.Int).Div(exMakerReceivedFee, lendTokenDecimal)
defaultFeeInXDC := new(big.Int).Mul(defaultFee, lendTokenXDCPrice)
defaultFeeInXDC = new(big.Int).Div(defaultFeeInXDC, lendTokenDecimal)
if (exMakerReceivedFee.Cmp(common.RelayerLendingFee) <= 0 && exMakerReceivedFee.Sign() > 0) || defaultFeeInXDC.Cmp(common.RelayerLendingFee) <= 0 {
log.Debug("makerFee too small", "quantityToLend", quantityToLend, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "borrowFeeRate", borrowFeeRate, "defaultFeeInXDC", defaultFeeInXDC)
return result, ErrQuantityTradeTooSmall
}
} else if lendingToken == common.XDCNativeAddressBinary {
exMakerReceivedFee := makerFee
if (exMakerReceivedFee.Cmp(common.RelayerLendingFee) <= 0 && exMakerReceivedFee.Sign() > 0) || defaultFee.Cmp(common.RelayerLendingFee) <= 0 {
log.Debug("makerFee too small", "quantityToLend", quantityToLend, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "borrowFeeRate", borrowFeeRate, "defaultFee", defaultFee)
return result, ErrQuantityTradeTooSmall
}
}
result = &LendingSettleBalance{
Taker: TradeResult{
Fee: common.Big0,
InToken: common.Address{},
InTotal: common.Big0,
OutToken: lendingToken,
OutTotal: quantityToLend,
},
Maker: TradeResult{
Fee: makerFee,
InToken: lendingToken,
InTotal: new(big.Int).Add(quantityToLend, makerFee),
OutToken: collateralToken,
OutTotal: makerOutTotal,
},
CollateralLockedAmount: makerOutTotal,
}
}
} else {
collateralQuantity := new(big.Int).Mul(quantityToLend, collateralTokenDecimal)
collateralQuantity = new(big.Int).Mul(collateralQuantity, depositRate) // eg: depositRate = 150%
collateralQuantity = new(big.Int).Div(collateralQuantity, big.NewInt(100))
collateralQuantity = new(big.Int).Div(collateralQuantity, collateralPrice)
borrowFee := new(big.Int).Mul(quantityToLend, borrowFeeRate)
borrowFee = new(big.Int).Div(borrowFee, common.XDCXBaseFee)
if quantityToLend.Cmp(borrowFee) <= 0 || quantityToLend.Cmp(defaultFee) <= 0 {
log.Debug("quantity lending too small", "quantityToLend", quantityToLend, "borrowFee", borrowFee)
return result, ErrQuantityTradeTooSmall
}
if lendingToken != common.XDCNativeAddressBinary && lendTokenXDCPrice != nil && lendTokenXDCPrice.Cmp(common.Big0) > 0 {
// exReceivedFee: the fee amount which borrowingRelayer will receive
exReceivedFee := new(big.Int).Mul(borrowFee, lendTokenXDCPrice)
exReceivedFee = new(big.Int).Div(exReceivedFee, lendTokenDecimal)
defaultFeeInXDC := new(big.Int).Mul(defaultFee, lendTokenXDCPrice)
defaultFeeInXDC = new(big.Int).Div(defaultFeeInXDC, lendTokenDecimal)
if (exReceivedFee.Cmp(common.RelayerLendingFee) <= 0 && exReceivedFee.Sign() > 0) || defaultFeeInXDC.Cmp(common.RelayerLendingFee) <= 0 {
log.Debug("takerFee too small", "quantityToLend", quantityToLend, "borrowFee", borrowFee, "exReceivedFee", exReceivedFee, "borrowFeeRate", borrowFeeRate, "defaultFeeInXDC", defaultFeeInXDC)
return result, ErrQuantityTradeTooSmall
}
} else if lendingToken == common.XDCNativeAddressBinary {
exReceivedFee := borrowFee
if (exReceivedFee.Cmp(common.RelayerLendingFee) <= 0 && exReceivedFee.Sign() > 0) || defaultFee.Cmp(common.RelayerLendingFee) <= 0 {
log.Debug("takerFee too small", "quantityToLend", quantityToLend, "borrowFee", borrowFee, "exReceivedFee", exReceivedFee, "borrowFeeRate", borrowFeeRate, "defaultFee", defaultFee)
return result, ErrQuantityTradeTooSmall
}
}
borrowerReceivedQuantity := new(big.Int).Sub(quantityToLend, borrowFee)
borrowerTradeResult := TradeResult{
Fee: borrowFee,
InToken: lendingToken,
InTotal: borrowerReceivedQuantity,
OutToken: collateralToken,
OutTotal: collateralQuantity,
}
investorTradeResult := TradeResult{
Fee: common.Big0,
InToken: common.Address{},
InTotal: common.Big0,
OutToken: lendingToken,
OutTotal: quantityToLend,
}
if takerSide == Borrowing {
result = &LendingSettleBalance{
Taker: borrowerTradeResult,
Maker: investorTradeResult,
CollateralLockedAmount: collateralQuantity,
}
} else {
result = &LendingSettleBalance{
Taker: investorTradeResult,
Maker: borrowerTradeResult,
CollateralLockedAmount: collateralQuantity,
}
}
}
return result, nil
}
// apr: annual percentage rate
// this function returns actual interest rate base on borrowing time and apr
// I = APR *(T + T1) / 2 / 365
// T: term
// T1: borrowingTime
func CalculateInterestRate(finalizeTime, liquidationTime, term uint64, apr uint64) *big.Int {
startBorrowingTime := liquidationTime - term
borrowingTime := finalizeTime - startBorrowingTime
// the time interval which borrower have to pay interest
// (T + T1) / 2
timeToPayInterest := new(big.Int).Add(new(big.Int).SetUint64(term), new(big.Int).SetUint64(borrowingTime))
timeToPayInterest = new(big.Int).Div(timeToPayInterest, new(big.Int).SetUint64(2))
interestRate := new(big.Int).SetUint64(apr)
interestRate = new(big.Int).Mul(interestRate, timeToPayInterest)
interestRate = new(big.Int).Div(interestRate, new(big.Int).SetUint64(common.OneYear))
return interestRate
}
func CalculateTotalRepayValue(finalizeTime, liquidationTime, term uint64, apr uint64, tradeAmount *big.Int) *big.Int {
interestRate := CalculateInterestRate(finalizeTime, liquidationTime, term, apr)
// interest 10%
// user should send: 10 * common.BaseLendingInterest
// decimal = common.BaseLendingInterest * 100
baseInterestDecimal := new(big.Int).Mul(common.BaseLendingInterest, new(big.Int).SetUint64(100))
paymentBalance := new(big.Int).Mul(tradeAmount, new(big.Int).Add(baseInterestDecimal, interestRate))
paymentBalance = new(big.Int).Div(paymentBalance, baseInterestDecimal)
return paymentBalance
}