Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dto/apiRequests.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ type SharedApiReq struct {

type GetFeeRequest struct {
*SharedApiReq
ReceiversCount int `json:"receiversCount"`
ReceiversCount int `json:"receiversCount"`
Speed string `json:"speed,omitempty"`
}

type GetTokenFeeRequest struct {
*SharedApiReq
TokenAddress string `json:"tokenAddress"`
Speed string `json:"speed,omitempty"`
}
145 changes: 109 additions & 36 deletions services/feeCalculator/feeCalculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ import (
"sync"
)

type Params struct {
Address string
Amount string
ReceiversCount int
Speed string
TokenAddress string
}

type feeCalculator struct {
CalcFee func(int, int, int) int
MinFeePerByte int
FeePerByte int
}

func GetBitcoinFee(address string, amount string, receiversCount int) (dto.GetFeeResponse, responses.ResponseError, error) {
func GetBitcoinFee(params *Params) (dto.GetFeeResponse, responses.ResponseError, error) {

var utxos responses.UtxoResponse
var apiUtxoErr, apiFeeErr responses.ResponseError
Expand All @@ -25,7 +33,7 @@ func GetBitcoinFee(address string, amount string, receiversCount int) (dto.GetFe
wg.Add(2)
go func() {
defer wg.Done()
utxos, apiUtxoErr = api.GetUtxo(address, "btc")
utxos, apiUtxoErr = api.GetUtxo(params.Address, "btc")
}()

go func() {
Expand All @@ -40,19 +48,17 @@ func GetBitcoinFee(address string, amount string, receiversCount int) (dto.GetFe
return dto.GetFeeResponse{}, apiUtxoErr, nil
}

fee, apiErr := calcUtxoFee(utxos.Utxo, amount, receiversCount, feeCalculator{
CalcFee: calcBitcoinFee,
MinFeePerByte: 10,
FeePerByte: feePerByte.HalfHourFee,
})
feeCounted, _ := speedControl(feePerByte, params.Speed)

fee, apiErr := calcUtxoFee(utxos.Utxo, params.Amount, params.ReceiversCount, *feeCounted)
if apiErr.Error != nil || apiErr.ApiError != nil {
return dto.GetFeeResponse{}, apiErr, nil
}

return fee, responses.ResponseError{}, nil
}

func GetLitecoinFee(address string, amount string, receiversCount int) (dto.GetFeeResponse, responses.ResponseError, error) {
func GetLitecoinFee(params *Params) (dto.GetFeeResponse, responses.ResponseError, error) {

var utxos responses.UtxoResponse
var apiUtxoErr, apiFeeErr responses.ResponseError
Expand All @@ -63,7 +69,7 @@ func GetLitecoinFee(address string, amount string, receiversCount int) (dto.GetF
wg.Add(2)
go func() {
defer wg.Done()
utxos, apiUtxoErr = api.GetUtxo(address, "ltc")
utxos, apiUtxoErr = api.GetUtxo(params.Address, "ltc")
}()

go func() {
Expand All @@ -78,23 +84,23 @@ func GetLitecoinFee(address string, amount string, receiversCount int) (dto.GetF
return dto.GetFeeResponse{}, apiUtxoErr, nil
}

fee, apiErr := calcUtxoFee(utxos.Utxo, amount, receiversCount, feeCalculator{
CalcFee: calcLitecoinFee,
MinFeePerByte: 8,
FeePerByte: feePerByte.MediumFeePerKb / 1024,
})
feeCounted, _ := speedControl(feePerByte, params.Speed)
// todo : complete
feeCounted.MinFeePerByte = 8

fee, apiErr := calcUtxoFee(utxos.Utxo, params.Amount, params.ReceiversCount, *feeCounted)
if apiErr.Error != nil || apiErr.ApiError != nil {
return dto.GetFeeResponse{}, apiErr, nil
}
return fee, responses.ResponseError{}, nil
}

func GetBitcoinCashFee(address string, amount string, receiversCount int) (dto.GetFeeResponse, responses.ResponseError, error) {
utxos, apiErr := api.GetUtxo(address, "bch")
func GetBitcoinCashFee(params *Params) (dto.GetFeeResponse, responses.ResponseError, error) {
utxos, apiErr := api.GetUtxo(params.Address, "bch")
if apiErr.Error != nil || apiErr.ApiError != nil {
return dto.GetFeeResponse{}, apiErr, nil
}
fee, apiErr := calcUtxoFee(utxos.Utxo, amount, receiversCount, feeCalculator{
fee, apiErr := calcUtxoFee(utxos.Utxo, params.Amount, params.ReceiversCount, feeCalculator{
CalcFee: calcBitcoinCashFee,
MinFeePerByte: 1,
FeePerByte: 3,
Expand All @@ -105,7 +111,7 @@ func GetBitcoinCashFee(address string, amount string, receiversCount int) (dto.G
return fee, responses.ResponseError{}, nil
}

func GetEthereumFee(address string, amount string) (dto.GetEthFeeResponse, responses.ResponseError, error) {
func GetEthereumFee(params *Params) (dto.GetEthFeeResponse, responses.ResponseError, error) {

var fee responses.EthereumFeeResponse
var feeErr, balanceErr responses.ResponseError
Expand All @@ -121,7 +127,7 @@ func GetEthereumFee(address string, amount string) (dto.GetEthFeeResponse, respo

go func() {
defer wg.Done()
balance, balanceErr = api.GetEthereumBalance(address)
balance, balanceErr = api.GetEthereumBalance(params.Address)
}()
wg.Wait()

Expand All @@ -131,14 +137,16 @@ func GetEthereumFee(address string, amount string) (dto.GetEthFeeResponse, respo
return dto.GetEthFeeResponse{}, balanceErr, nil
}

fr, err := CalculateEthBasedFee(balance.Balance, fee.GasPrice, 21000, amount)
_, gasPrice := speedControl(fee, params.Speed)

fr, err := CalculateEthBasedFee(balance.Balance, gasPrice, 21000, params.Amount)
if err != nil {
return dto.GetEthFeeResponse{}, responses.ResponseError{}, err
}
return fr, responses.ResponseError{}, nil
}

func GetEthereumClassicFee(address string, amount string) (dto.GetEthFeeResponse, responses.ResponseError, error) {
func GetEthereumClassicFee(params *Params) (dto.GetEthFeeResponse, responses.ResponseError, error) {
var fee responses.EthereumFeeResponse
var feeErr, balanceErr responses.ResponseError
var balance responses.CurrencyBalanceResponse
Expand All @@ -153,7 +161,7 @@ func GetEthereumClassicFee(address string, amount string) (dto.GetEthFeeResponse

go func() {
defer wg.Done()
balance, balanceErr = api.GetEthereumClassicBalance(address)
balance, balanceErr = api.GetEthereumClassicBalance(params.Address)
}()
wg.Wait()

Expand All @@ -163,14 +171,16 @@ func GetEthereumClassicFee(address string, amount string) (dto.GetEthFeeResponse
return dto.GetEthFeeResponse{}, balanceErr, nil
}

fr, err := CalculateEthBasedFee(balance.Balance, fee.GasPrice, 21000, amount)
_, gasPrice := speedControl(fee, params.Speed)

fr, err := CalculateEthBasedFee(balance.Balance, gasPrice, 21000, params.Amount)
if err != nil {
return dto.GetEthFeeResponse{}, responses.ResponseError{}, err
}
return fr, responses.ResponseError{}, nil
}

func GetTokenFee(address, tokenAddress, amount string) (dto.GetTokenFeeResponse, responses.ResponseError, error) {
func GetTokenFee(params *Params) (dto.GetTokenFeeResponse, responses.ResponseError, error) {
var gasLimitErr, feeErr, balanceErr, tokenBalanceErr responses.ResponseError
var gasLimit responses.TokenFeeResponse
var fee responses.EthereumFeeResponse
Expand All @@ -182,9 +192,9 @@ func GetTokenFee(address, tokenAddress, amount string) (dto.GetTokenFeeResponse,
go func() {
defer wg.Done()
gasLimit, gasLimitErr = api.GetTokenGasLimit(requests.TokenGasLimitRequest{
TokenAddress: tokenAddress,
ToAddress: address,
Amount: amount,
TokenAddress: params.TokenAddress,
ToAddress: params.Address,
Amount: params.Amount,
})
}()

Expand All @@ -195,12 +205,12 @@ func GetTokenFee(address, tokenAddress, amount string) (dto.GetTokenFeeResponse,

go func() {
defer wg.Done()
balance, balanceErr = api.GetEthereumBalance(address)
balance, balanceErr = api.GetEthereumBalance(params.Address)
}()

go func() {
defer wg.Done()
tokenBalance, tokenBalanceErr = api.GetTokenBalance(address, tokenAddress)
tokenBalance, tokenBalanceErr = api.GetTokenBalance(params.Address, params.TokenAddress)
if tokenBalance.Balance == "" {
tokenBalance.Balance = "0"
}
Expand All @@ -220,21 +230,23 @@ func GetTokenFee(address, tokenAddress, amount string) (dto.GetTokenFeeResponse,
return dto.GetTokenFeeResponse{}, tokenBalanceErr, nil
}

fr, err := CalculateTokenFee(balance.Balance, tokenBalance.Balance, fee.GasPrice, gasLimit.GasLimit, amount)
_, gasPrice := speedControl(fee, params.Speed)

fr, err := CalculateTokenFee(balance.Balance, tokenBalance.Balance, gasPrice, gasLimit.GasLimit, params.Amount)
if err != nil {
return dto.GetTokenFeeResponse{}, responses.ResponseError{}, err
}
return fr, responses.ResponseError{}, nil
}

func GetWavesFee(address string, amount string) (dto.GetWavesAndStellarFeeResponse, responses.ResponseError, error) {
balance, apiErr := api.GetWavesBalance(address)
func GetWavesFee(params *Params) (dto.GetWavesAndStellarFeeResponse, responses.ResponseError, error) {
balance, apiErr := api.GetWavesBalance(params.Address)
if apiErr.Error != nil || apiErr.ApiError != nil {
return dto.GetWavesAndStellarFeeResponse{}, apiErr, nil
}
maxAmount := balance.Balance - 300000
isEnough := true
if stringAmountToSatoshi(amount) > maxAmount {
if stringAmountToSatoshi(params.Amount) > maxAmount {
isEnough = false
if maxAmount < 0 {
maxAmount = 0
Expand All @@ -250,12 +262,12 @@ func GetWavesFee(address string, amount string) (dto.GetWavesAndStellarFeeRespon
}}, responses.ResponseError{}, nil
}

func GetStellarFee(address string, amount string) (dto.GetWavesAndStellarFeeResponse, responses.ResponseError, error) {
balance, apiErr := api.GetStellarBalance(address)
func GetStellarFee(params *Params) (dto.GetWavesAndStellarFeeResponse, responses.ResponseError, error) {
balance, apiErr := api.GetStellarBalance(params.Address)
if apiErr.Error != nil || apiErr.ApiError != nil {
return dto.GetWavesAndStellarFeeResponse{}, apiErr, nil
}
fr := CalcStellarFee(balance.Balance, amount, 100)
fr := CalcStellarFee(balance.Balance, params.Amount, 100)
return fr, responses.ResponseError{}, nil
}

Expand All @@ -270,3 +282,64 @@ func calcLitecoinFee(inputCount, outputCount, feePerByte int) int {
func calcBitcoinCashFee(inputCount, outputCount, feePerByte int) int {
return (inputCount*148 + outputCount*34 + 10) * feePerByte
}

func speedControl(t interface{}, speed string) (*feeCalculator, int) {
var (
f feeCalculator
gasPrice int
)

switch t.(type) {
case responses.BitcoinFeeResponse:
typed := t.(responses.BitcoinFeeResponse)
f.CalcFee = calcBitcoinFee
switch speed {
case "slow":
f.FeePerByte = typed.HourFee
case "average":
f.FeePerByte = typed.HalfHourFee
case "fast":
f.FeePerByte = typed.FastestFee
default:
f.FeePerByte = typed.HourFee
}
if f.FeePerByte <= 10 {
f.MinFeePerByte = f.FeePerByte - 1
} else {
f.MinFeePerByte = 10
}
case responses.LitecoinFeeResponse:
typed := t.(responses.LitecoinFeeResponse)
f.CalcFee = calcLitecoinFee
switch speed {
case "slow":
f.FeePerByte = typed.LowFeePerKb
case "average":
f.FeePerByte = typed.MediumFeePerKb
case "fast":
f.FeePerByte = typed.HighFeePerKb
default:
f.FeePerByte = typed.MediumFeePerKb
}
f.FeePerByte = f.FeePerByte / 1024
if f.FeePerByte <= 8 {
f.MinFeePerByte = f.FeePerByte - 1
} else {
f.MinFeePerByte = 8
}
case responses.EthereumFeeResponse:
typed := t.(responses.EthereumFeeResponse)
switch speed {
case "slow":
gasPrice = typed.GasPrice * 10 / 8
case "average":
gasPrice = typed.GasPrice
case "fast":
gasPrice = typed.GasPrice + (typed.GasPrice * 10 / 5)
default:
gasPrice = typed.GasPrice
}
}

return &f, gasPrice
}
4 changes: 2 additions & 2 deletions services/feeCalculator/utxoBlockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func calcUtxoFee(utxos []responses.Utxo, amount string, receiversCount int, feeC
MaxAmount: uint64(ux.MaxAmount),
MaxAmountWithOptimalFee: uint64(ux.MaxUsefulAmount),
},
Inputs: []responses.Utxo{},
Inputs: []responses.Utxo{},
FeePerByte: feeCalculator.FeePerByte,
}
result.Input = ux.MinInputs - 1
Expand All @@ -62,7 +62,7 @@ func calcUtxoFee(utxos []responses.Utxo, amount string, receiversCount int, feeC
result.IsEnough = true
result.Output = receiversCount
iterationBalance := ux.LastIterationBalance
maxIterations := len(ux.UsefulUtxos)+len(ux.UselessUtxos)
maxIterations := len(ux.UsefulUtxos) + len(ux.UselessUtxos)

for i := ux.MinInputs - 1; i < maxIterations; i++ {
result.Input++
Expand Down
Loading