From 5d412a92d361af21a51ee49484234577e65c490e Mon Sep 17 00:00:00 2001 From: DenrianWeiss Date: Wed, 2 Apr 2025 14:54:12 +0800 Subject: [PATCH 1/3] WIP: feat(client): add vip loan apis support --- v2/client.go | 7 + v2/vip_loan_service.go | 844 ++++++++++++++++++++++++++++++++++++ v2/vip_loan_service_test.go | 590 +++++++++++++++++++++++++ 3 files changed, 1441 insertions(+) create mode 100644 v2/vip_loan_service.go create mode 100644 v2/vip_loan_service_test.go diff --git a/v2/client.go b/v2/client.go index ad39a7713..068582d52 100644 --- a/v2/client.go +++ b/v2/client.go @@ -1398,3 +1398,10 @@ func (c *Client) NewSimpleEarnService() *SimpleEarnService { } // ----- end simple earn service ----- + +// ----- vip loan service ----- +func (c *Client) NewVipLoanService() *VipLoanService { + return &VipLoanService{c: c} +} + +// ----- end vip loan service ----- diff --git a/v2/vip_loan_service.go b/v2/vip_loan_service.go new file mode 100644 index 000000000..deccb73e0 --- /dev/null +++ b/v2/vip_loan_service.go @@ -0,0 +1,844 @@ +package binance + +import ( + "context" + "encoding/json" + "net/http" +) + +type VipLoanService struct { + c *Client +} + +func (s *VipLoanService) InterestRate() *VipLoanInterestRateService { + return &VipLoanInterestRateService{c: s.c} +} + +func (s *VipLoanService) InterestRateHistory() *VipLoanInterestRateHistoryService { + return &VipLoanInterestRateHistoryService{c: s.c} +} + +func (s *VipLoanService) LoanableAssetData() *VipLoanLoanableAssetDataService { + return &VipLoanLoanableAssetDataService{c: s.c} +} + +func (s *VipLoanService) CollateralAssetData() *VipLoanCollateralAssetDataService { + return &VipLoanCollateralAssetDataService{c: s.c} +} + +func (s *VipLoanService) OngoingOrders() *VipLoanOngoingOrdersService { + return &VipLoanOngoingOrdersService{c: s.c} +} + +func (s *VipLoanService) RepaymentHistory() *VipLoanRepaymentHistoryService { + return &VipLoanRepaymentHistoryService{c: s.c} +} + +func (s *VipLoanService) AccruedInterest() *VipLoanAccruedInterestService { + return &VipLoanAccruedInterestService{c: s.c} +} + +func (s *VipLoanService) CollateralAccount() *VipLoanCollateralAccountService { + return &VipLoanCollateralAccountService{c: s.c} +} + +func (s *VipLoanService) ApplicationStatus() *VipLoanApplicationStatusService { + return &VipLoanApplicationStatusService{c: s.c} +} + +func (s *VipLoanService) Renew() *VipLoanRenewService { + return &VipLoanRenewService{c: s.c} +} + +func (s *VipLoanService) Repay() *VipLoanRepayService { + return &VipLoanRepayService{c: s.c} +} + +func (s *VipLoanService) Borrow() *VipLoanBorrowService { + return &VipLoanBorrowService{c: s.c} +} + +type VipLoanInterestRateService struct { + c *Client + loanCoin *string // Mandatory: YES, max 10 assets split by comma +} + +func (s *VipLoanInterestRateService) LoanCoin(loanCoin string) *VipLoanInterestRateService { + s.loanCoin = &loanCoin + return s +} + +func (s *VipLoanInterestRateService) Do(ctx context.Context) (*VipLoanInterestRate, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/request/interestRate", + secType: secTypeSigned, + } + if s.loanCoin != nil { + r.setParam("loanCoin", *s.loanCoin) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanInterestRate) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanInterestRate []*VipLoanInterestRateElement + +type VipLoanInterestRateElement struct { + Asset string `json:"asset"` + FlexibleDailyInterestRate string `json:"flexibleDailyInterestRate"` + FlexibleYearlyInterestRate string `json:"flexibleYearlyInterestRate"` + Time int64 `json:"time"` +} + +type VipLoanInterestRateHistoryService struct { + c *Client + coin *string + startTime *int64 + endTime *int64 + current *int64 // Mandatory: NO, default 1 + limit *int64 // Mandatory: NO, default 10, max 100 +} + +func (s *VipLoanInterestRateHistoryService) Coin(coin string) *VipLoanInterestRateHistoryService { + s.coin = &coin + return s +} + +func (s *VipLoanInterestRateHistoryService) StartTime(startTime int64) *VipLoanInterestRateHistoryService { + s.startTime = &startTime + return s +} + +func (s *VipLoanInterestRateHistoryService) EndTime(endTime int64) *VipLoanInterestRateHistoryService { + s.endTime = &endTime + return s +} + +func (s *VipLoanInterestRateHistoryService) Current(current int64) *VipLoanInterestRateHistoryService { + s.current = ¤t + return s +} + +func (s *VipLoanInterestRateHistoryService) Limit(limit int64) *VipLoanInterestRateHistoryService { + s.limit = &limit + return s +} + +func (s *VipLoanInterestRateHistoryService) Do(ctx context.Context) (*VipLoanInterestRateHistoryResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/interestRateHistory", + secType: secTypeSigned, + } + if s.coin != nil { + r.setParam("coin", *s.coin) + } + if s.startTime != nil { + r.setParam("startTime", *s.startTime) + } + if s.endTime != nil { + r.setParam("endTime", *s.endTime) + } + if s.current != nil { + r.setParam("current", *s.current) + } + if s.limit != nil { + r.setParam("limit", *s.limit) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanInterestRateHistoryResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanInterestRateHistoryResponse struct { + Rows []VipLoanInterestRateHistory `json:"rows"` + Total int64 `json:"total"` +} + +type VipLoanInterestRateHistory struct { + Coin string `json:"coin"` + AnnualizedInterestRate string `json:"annualizedInterestRate"` + Time int64 `json:"time"` +} + +type VipLoanLoanableAssetDataService struct { + c *Client + loanCoin *string + vipLevel *int64 // Mandatory: NO, default to user vip level +} + +func (s *VipLoanLoanableAssetDataService) LoanCoin(loanCoin string) *VipLoanLoanableAssetDataService { + s.loanCoin = &loanCoin + return s +} + +func (s *VipLoanLoanableAssetDataService) VipLevel(vipLevel int64) *VipLoanLoanableAssetDataService { + s.vipLevel = &vipLevel + return s +} + +func (s *VipLoanLoanableAssetDataService) Do(ctx context.Context) (*VipLoanLoanableAssetDataResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/loanable/data", + secType: secTypeSigned, + } + if s.loanCoin != nil { + r.setParam("loanCoin", *s.loanCoin) + } + if s.vipLevel != nil { + r.setParam("vipLevel", *s.vipLevel) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanLoanableAssetDataResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanLoanableAssetDataResponse struct { + Rows []VipLoanLoanableAssetData `json:"rows"` + Total int `json:"total"` +} + +type VipLoanLoanableAssetData struct { + LoanCoin string `json:"loanCoin"` + FlexibleDailyInterestRate string `json:"_flexibleDailyInterestRate"` + FlexibleYearlyInterestRate string `json:"_flexibleYearlyInterestRate"` + ThirtyDayDailyInterestRate string `json:"_30dDailyInterestRate"` + ThirtyDayYearlyInterestRate string `json:"_30dYearlyInterestRate"` + SixtyDayDailyInterestRate string `json:"_60dDailyInterestRate"` + SixtyDayYearlyInterestRate string `json:"_60dYearlyInterestRate"` + MinLimit string `json:"minLimit"` + MaxLimit string `json:"maxLimit"` + VipLevel int `json:"vipLevel"` +} + +type VipLoanCollateralAssetDataService struct { + c *Client + collateralCoin *string // Mandatory: NO +} + +func (s *VipLoanCollateralAssetDataService) CollateralCoin(collateralCoin string) *VipLoanCollateralAssetDataService { + s.collateralCoin = &collateralCoin + return s +} + +func (s *VipLoanCollateralAssetDataService) Do(ctx context.Context) (*VipLoanCollateralAssetDataResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/collateral/data", + secType: secTypeSigned, + } + if s.collateralCoin != nil { + r.setParam("collateralCoin", *s.collateralCoin) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanCollateralAssetDataResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanCollateralAssetDataResponse struct { + Rows []VipLoanCollateralAssetData `json:"rows"` + Total int `json:"total"` +} + +type VipLoanCollateralAssetData struct { + CollateralCoin string `json:"collateralCoin"` + FirstCollateralRatio string `json:"_1stCollateralRatio"` + FirstCollateralRange string `json:"_1stCollateralRange"` + SecondCollateralRatio string `json:"_2ndCollateralRatio"` + SecondCollateralRange string `json:"_2ndCollateralRange"` + ThirdCollateralRatio string `json:"_3rdCollateralRatio"` + ThirdCollateralRange string `json:"_3rdCollateralRange"` + FourthCollateralRatio string `json:"_4thCollateralRatio"` + FourthCollateralRange string `json:"_4thCollateralRange"` +} + +type VipLoanOngoingOrdersService struct { + c *Client + orderId *int64 // Mandatory: NO + collateralAccountId *int64 // Mandatory: NO + loanCoin *string // Mandatory: NO + collateralCoin *string // Mandatory: NO + current *int64 // Mandatory: NO, default 1, max 100 + limit *int64 // Mandatory: NO, default 10, max 100 +} + +func (s *VipLoanOngoingOrdersService) OrderId(orderId int64) *VipLoanOngoingOrdersService { + s.orderId = &orderId + return s +} + +func (s *VipLoanOngoingOrdersService) CollateralAccountId(collateralAccountId int64) *VipLoanOngoingOrdersService { + s.collateralAccountId = &collateralAccountId + return s +} + +func (s *VipLoanOngoingOrdersService) LoanCoin(loanCoin string) *VipLoanOngoingOrdersService { + s.loanCoin = &loanCoin + return s +} + +func (s *VipLoanOngoingOrdersService) CollateralCoin(collateralCoin string) *VipLoanOngoingOrdersService { + s.collateralCoin = &collateralCoin + return s +} + +func (s *VipLoanOngoingOrdersService) Current(current int64) *VipLoanOngoingOrdersService { + s.current = ¤t + return s +} + +func (s *VipLoanOngoingOrdersService) Limit(limit int64) *VipLoanOngoingOrdersService { + s.limit = &limit + return s +} + +func (s *VipLoanOngoingOrdersService) Do(ctx context.Context) (*VipLoanOngoingOrderResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/ongoing/orders", + secType: secTypeSigned, + } + if s.orderId != nil { + r.setParam("orderId", *s.orderId) + } + if s.collateralAccountId != nil { + r.setParam("collateralAccountId", *s.collateralAccountId) + } + if s.loanCoin != nil { + r.setParam("loanCoin", *s.loanCoin) + } + if s.collateralCoin != nil { + r.setParam("collateralCoin", *s.collateralCoin) + } + if s.current != nil { + r.setParam("current", *s.current) + } + if s.limit != nil { + r.setParam("limit", *s.limit) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanOngoingOrderResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanOngoingOrderResponse struct { + Rows []VipLoanOngoingOrder `json:"rows"` + Total int `json:"total"` +} + +type VipLoanOngoingOrder struct { + OrderId int `json:"orderId"` + LoanCoin string `json:"loanCoin"` + TotalDebt string `json:"totalDebt"` + LoanRate string `json:"loanRate"` + ResidualInterest string `json:"residualInterest"` + CollateralAccountId string `json:"collateralAccountId"` + CollateralCoin string `json:"collateralCoin"` + TotalCollateralValueAfterHaircut string `json:"totalCollateralValueAfterHaircut"` + LockedCollateralValue string `json:"lockedCollateralValue"` + CurrentLTV string `json:"currentLTV"` + ExpirationTime int64 `json:"expirationTime"` + LoanDate string `json:"loanDate"` + LoanTerm string `json:"loanTerm"` +} + +type VipLoanRepaymentHistoryService struct { + c *Client + orderId *int64 + loanCoin *string + startTime *int64 + endTime *int64 + current *int64 + limit *int64 +} + +func (s *VipLoanRepaymentHistoryService) OrderId(orderId int64) *VipLoanRepaymentHistoryService { + s.orderId = &orderId + return s +} + +func (s *VipLoanRepaymentHistoryService) LoanCoin(loanCoin string) *VipLoanRepaymentHistoryService { + s.loanCoin = &loanCoin + return s +} + +func (s *VipLoanRepaymentHistoryService) StartTime(startTime int64) *VipLoanRepaymentHistoryService { + s.startTime = &startTime + return s +} + +func (s *VipLoanRepaymentHistoryService) EndTime(endTime int64) *VipLoanRepaymentHistoryService { + s.endTime = &endTime + return s +} + +func (s *VipLoanRepaymentHistoryService) Current(current int64) *VipLoanRepaymentHistoryService { + s.current = ¤t + return s +} + +func (s *VipLoanRepaymentHistoryService) Limit(limit int64) *VipLoanRepaymentHistoryService { + s.limit = &limit + return s +} + +func (s *VipLoanRepaymentHistoryService) Do(ctx context.Context) (*VipLoanRepaymentHistoryResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/repayment/history", + secType: secTypeSigned, + } + if s.orderId != nil { + r.setParam("orderId", *s.orderId) + } + if s.loanCoin != nil { + r.setParam("loanCoin", *s.loanCoin) + } + if s.startTime != nil { + r.setParam("startTime", *s.startTime) + } + if s.endTime != nil { + r.setParam("endTime", *s.endTime) + } + if s.current != nil { + r.setParam("current", *s.current) + } + if s.limit != nil { + r.setParam("limit", *s.limit) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanRepaymentHistoryResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanRepaymentHistoryResponse struct { + Rows []VipLoanRepaymentHistory `json:"rows"` + Total int `json:"total"` +} + +type VipLoanRepaymentHistory struct { + LoanCoin string `json:"loanCoin"` + RepayAmount string `json:"repayAmount"` + CollateralCoin string `json:"collateralCoin"` + RepayStatus string `json:"repayStatus"` + LoanDate string `json:"loanDate"` + RepayTime string `json:"repayTime"` + OrderId string `json:"orderId"` +} + +type VipLoanAccruedInterestService struct { + c *Client + orderId *int64 + loanCoin *string + startTime *int64 + endTime *int64 + current *int64 + limit *int64 +} + +func (s *VipLoanAccruedInterestService) OrderId(orderId int64) *VipLoanAccruedInterestService { + s.orderId = &orderId + return s +} + +func (s *VipLoanAccruedInterestService) LoanCoin(loanCoin string) *VipLoanAccruedInterestService { + s.loanCoin = &loanCoin + return s +} + +func (s *VipLoanAccruedInterestService) StartTime(startTime int64) *VipLoanAccruedInterestService { + s.startTime = &startTime + return s +} + +func (s *VipLoanAccruedInterestService) EndTime(endTime int64) *VipLoanAccruedInterestService { + s.endTime = &endTime + return s +} + +func (s *VipLoanAccruedInterestService) Current(current int64) *VipLoanAccruedInterestService { + s.current = ¤t + return s +} + +func (s *VipLoanAccruedInterestService) Limit(limit int64) *VipLoanAccruedInterestService { + s.limit = &limit + return s +} + +func (s *VipLoanAccruedInterestService) Do(ctx context.Context) (*VipLoanAccruedInterestResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/accruedInterest", + secType: secTypeSigned, + } + if s.loanCoin != nil { + r.setParam("loanCoin", *s.loanCoin) + } + if s.startTime != nil { + r.setParam("startTime", *s.startTime) + } + if s.endTime != nil { + r.setParam("endTime", *s.endTime) + } + if s.current != nil { + r.setParam("current", *s.current) + } + if s.limit != nil { + r.setParam("limit", *s.limit) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanAccruedInterestResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanAccruedInterestResponse struct { + Rows []VipLoanAccruedInterest `json:"rows"` + Total int `json:"total"` +} + +type VipLoanAccruedInterest struct { + LoanCoin string `json:"loanCoin"` + PrincipalAmount string `json:"principalAmount"` + InterestAmount string `json:"interestAmount"` + AnnualInterestRate string `json:"annualInterestRate"` + AccrualTime int64 `json:"accrualTime"` + OrderId int64 `json:"orderId"` +} + +type VipLoanCollateralAccountService struct { + c *Client + orderId *int64 + collateralAccountId *int64 +} + +func (s *VipLoanCollateralAccountService) OrderId(orderId int64) *VipLoanCollateralAccountService { + s.orderId = &orderId + return s +} + +func (s *VipLoanCollateralAccountService) CollateralAccountId(collateralAccountId int64) *VipLoanCollateralAccountService { + s.collateralAccountId = &collateralAccountId + return s +} + +func (s *VipLoanCollateralAccountService) Do(ctx context.Context) (*VipLoanCollateralAccountResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/collateral/account", + secType: secTypeSigned, + } + if s.orderId != nil { + r.setParam("orderId", *s.orderId) + } + if s.collateralAccountId != nil { + r.setParam("collateralAccountId", *s.collateralAccountId) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanCollateralAccountResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanCollateralAccountResponse struct { + Rows []VipLoanCollateralAccount `json:"rows"` + Total int `json:"total"` +} + +type VipLoanCollateralAccount struct { + CollateralAccountId string `json:"collateralAccountId"` + CollateralCoin string `json:"collateralCoin"` +} + +type VipLoanApplicationStatusService struct { + c *Client + current *int64 // Mandatory: NO, default 1 + limit *int64 // Mandatory: NO, default 10, Max 100 +} + +func (s *VipLoanApplicationStatusService) Current(current int64) *VipLoanApplicationStatusService { + s.current = ¤t + return s +} + +func (s *VipLoanApplicationStatusService) Limit(limit int64) *VipLoanApplicationStatusService { + s.limit = &limit + return s +} + +func (s *VipLoanApplicationStatusService) Do(ctx context.Context) (*VipLoanApplicationStatusResponse, error) { + r := &request{ + method: http.MethodGet, + endpoint: "/sapi/v1/loan/vip/request/data", + secType: secTypeSigned, + } + if s.limit != nil { + r.setParam("limit", *s.limit) + } + data, err := s.c.callAPI(ctx, r) + if err != nil { + return nil, err + } + res := new(VipLoanApplicationStatusResponse) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanApplicationStatusResponse struct { + Rows []VipLoanApplicationStatus `json:"rows"` + Total int `json:"total"` +} + +type VipLoanApplicationStatus struct { + LoanAccountId string `json:"loanAccountId"` + OrderId string `json:"orderId"` + RequestId string `json:"requestId"` + LoanCoin string `json:"loanCoin"` + LoanAmount string `json:"loanAmount"` + CollateralAccountId string `json:"collateralAccountId"` + CollateralCoin string `json:"collateralCoin"` + LoanTerm string `json:"loanTerm"` + Status string `json:"status"` + LoanDate string `json:"loanDate"` +} + +type VipLoanRenewService struct { + c *Client + orderId *int64 // Mandatory: YES + loanTerm *int64 // Mandatory: YES, 30 or 60 days +} + +func (s *VipLoanRenewService) OrderId(orderId int64) *VipLoanRenewService { + s.orderId = &orderId + return s +} + +func (s *VipLoanRenewService) LoanTerm(loanTerm int64) *VipLoanRenewService { + s.loanTerm = &loanTerm + return s +} + +func (s *VipLoanRenewService) Do(ctx context.Context, opts ...RequestOption) (*VipLoanRenew, error) { + r := &request{ + method: http.MethodPost, + endpoint: "/sapi/v1/loan/vip/renew", + secType: secTypeSigned, + } + m := params{ + "orderId": s.orderId, + "loanTerm": s.loanTerm, + } + r.setFormParams(m) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res := new(VipLoanRenew) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanRenew struct { + LoanAccountId string `json:"loanAccountId"` + LoanCoin string `json:"loanCoin"` + LoanAmount string `json:"loanAmount"` + CollateralAccountId string `json:"collateralAccountId"` + CollateralCoin string `json:"collateralCoin"` + LoanTerm string `json:"loanTerm"` +} + +type VipLoanRepayService struct { + c *Client + orderId *int64 + amount *float64 +} + +func (s *VipLoanRepayService) OrderId(orderId int64) *VipLoanRepayService { + s.orderId = &orderId + return s +} + +func (s *VipLoanRepayService) Amount(amount float64) *VipLoanRepayService { + s.amount = &amount + return s +} + +func (s *VipLoanRepayService) Do(ctx context.Context, opts ...RequestOption) (*VipLoanRepay, error) { + r := &request{ + method: http.MethodPost, + endpoint: "/sapi/v1/loan/vip/repay", + secType: secTypeSigned, + } + m := params{ + "orderId": s.orderId, + "amount": s.amount, + } + r.setFormParams(m) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res := new(VipLoanRepay) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanRepay struct { + LoanCoin string `json:"loanCoin"` + RepayAmount string `json:"repayAmount"` + RemainingPrincipal string `json:"remainingPrincipal"` + RemainingInterest string `json:"remainingInterest"` + CollateralCoin string `json:"collateralCoin"` + CurrentLTV string `json:"currentLTV"` + RepayStatus string `json:"repayStatus"` +} + +type VipLoanBorrowService struct { + c *Client + loanAccountId *int64 + loanCoin string // Mandatory: YES + loanAmount *float64 + collateralAccountId *string // Mandatory: YES, accounts split by comma + collateralCoin *string // Mandatory: YES, coins split by comma + isFlexibleRate *bool // Mandatory: YES, TRUE: flexible rate; FALSE: fixed rate + loanTerm *int64 // Mandatory: YES for fixed interest, No for floating interest, 30 or 60 +} + +func (s *VipLoanBorrowService) LoanAccountId(loanAccountId int64) *VipLoanBorrowService { + s.loanAccountId = &loanAccountId + return s +} + +func (s *VipLoanBorrowService) LoanCoin(loanCoin string) *VipLoanBorrowService { + s.loanCoin = loanCoin + return s +} + +func (s *VipLoanBorrowService) LoanAmount(loanAmount float64) *VipLoanBorrowService { + s.loanAmount = &loanAmount + return s +} + +func (s *VipLoanBorrowService) CollateralAccountId(collateralAccountId string) *VipLoanBorrowService { + s.collateralAccountId = &collateralAccountId + return s +} + +func (s *VipLoanBorrowService) CollateralCoin(collateralCoin string) *VipLoanBorrowService { + s.collateralCoin = &collateralCoin + return s +} + +func (s *VipLoanBorrowService) IsFlexibleRate(isFlexibleRate bool) *VipLoanBorrowService { + s.isFlexibleRate = &isFlexibleRate + return s +} + +func (s *VipLoanBorrowService) LoanTerm(loanTerm int64) *VipLoanBorrowService { + s.loanTerm = &loanTerm + return s +} + +func (s *VipLoanBorrowService) Do(ctx context.Context, opts ...RequestOption) (*VipLoanBorrow, error) { + r := &request{ + method: http.MethodPost, + endpoint: "/sapi/v1/loan/vip/borrow", + secType: secTypeSigned, + } + m := params{ + "loanAccountId": s.loanAccountId, + "loanAmount": s.loanAmount, + "collateralAccountId": s.collateralAccountId, + "collateralCoin": s.collateralCoin, + "isFlexibleRate": s.isFlexibleRate, + "loanTerm": s.loanTerm, + } + r.setFormParams(m) + data, err := s.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res := new(VipLoanBorrow) + err = json.Unmarshal(data, res) + if err != nil { + return nil, err + } + return res, nil +} + +type VipLoanBorrow struct { + LoanAccountId string `json:"loanAccountId"` + RequestId string `json:"requestId"` + LoanCoin string `json:"loanCoin"` + IsFlexibleRate string `json:"isFlexibleRate"` + LoanAmount string `json:"loanAmount"` + CollateralAccountId string `json:"collateralAccountId"` + CollateralCoin string `json:"collateralCoin"` + LoanTerm string `json:"loanTerm"` +} diff --git a/v2/vip_loan_service_test.go b/v2/vip_loan_service_test.go new file mode 100644 index 000000000..086f7cd31 --- /dev/null +++ b/v2/vip_loan_service_test.go @@ -0,0 +1,590 @@ +package binance + +import ( + "github.com/stretchr/testify/suite" + "testing" +) + +type vipLoanServiceTestSuite struct { + baseTestSuite +} + +func TestVipLoanService(t *testing.T) { + suite.Run(t, new(vipLoanServiceTestSuite)) +} + +func (s *vipLoanServiceTestSuite) TestVipLoanInterestRateService() { + data := []byte(`[ + { + "asset": "BUSD", + "flexibleDailyInterestRate": "0.001503", + "flexibleYearlyInterestRate": "0.548595", + "time": 1577233578000 + }, + { + "asset": "BTC", + "flexibleDailyInterestRate": "0.001503", + "flexibleYearlyInterestRate": "0.548595", + "time": 1577233562000 + } +]`) + s.mockDo(data, nil) + defer s.assertDo() + + s.assertReq(func(r *request) { + e := newSignedRequest().setParam("loanCoin", "BTC,BUSD") + s.assertRequestEqual(e, r) + }) + res, err := s.client.NewVipLoanService().InterestRate().LoanCoin("BTC,BUSD").Do(newContext()) + s.r().NoError(err) + e := &VipLoanInterestRate{ + { + Asset: "BUSD", + FlexibleDailyInterestRate: "0.001503", + FlexibleYearlyInterestRate: "0.548595", + Time: 1577233578000, + }, + { + Asset: "BTC", + FlexibleDailyInterestRate: "0.001503", + FlexibleYearlyInterestRate: "0.548595", + Time: 1577233562000, + }, + } + s.assertVipLoanInterestRateEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanInterestRateEqual(e, a VipLoanInterestRate) { + r := s.r() + for i := 0; i < len(e); i++ { + r.Equal(e[i].Asset, a[i].Asset, "Asset") + r.Equal(e[i].FlexibleDailyInterestRate, a[i].FlexibleDailyInterestRate, "FlexibleDailyInterestRate") + r.Equal(e[i].FlexibleYearlyInterestRate, a[i].FlexibleYearlyInterestRate, "FlexibleYearlyInterestRate") + r.Equal(e[i].Time, a[i].Time, "Time") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanInterestRateHistory() { + data := []byte(`{ + "rows": [ + { + "coin": "USDT", + "annualizedInterestRate": "0.0647", + "time": 1575018510000 + } + ], + "total": 1 +}`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().InterestRateHistory().Coin("USDT").Do(newContext()) + s.r().NoError(err) + e := &VipLoanInterestRateHistoryResponse{ + Rows: []VipLoanInterestRateHistory{ + { + Coin: "USDT", + AnnualizedInterestRate: "0.0647", + Time: 1575018510000, + }, + }, + Total: 1, + } + s.assertVipLoanInterestRateHistoryResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanInterestRateHistoryResponseEqual(e, a VipLoanInterestRateHistoryResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].Coin, a.Rows[i].Coin, "Coin") + r.Equal(e.Rows[i].AnnualizedInterestRate, a.Rows[i].AnnualizedInterestRate, "AnnualizedInterestRate") + r.Equal(e.Rows[i].Time, a.Rows[i].Time, "Time") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanLoanableAssetData() { + data := []byte(`{ + "rows": [ + { + "loanCoin": "BUSD", + "_flexibleDailyInterestRate": "0.001503", + "_flexibleYearlyInterestRate": "0.548595", + "_30dDailyInterestRate": "0.000136", + "_30dYearlyInterestRate": "0.03450", + "_60dDailyInterestRate": "0.000145", + "_60dYearlyInterestRate": "0.04103", + "minLimit": "100", + "maxLimit": "1000000", + "vipLevel": 1 + } + ], + "total": 1 +} +`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().LoanableAssetData().LoanCoin("BUSD").VipLevel(1).Do(newContext()) + s.r().NoError(err) + e := &VipLoanLoanableAssetDataResponse{ + Rows: []VipLoanLoanableAssetData{ + { + LoanCoin: "BUSD", + FlexibleDailyInterestRate: "0.001503", + FlexibleYearlyInterestRate: "0.548595", + ThirtyDayDailyInterestRate: "0.000136", + ThirtyDayYearlyInterestRate: "0.03450", + SixtyDayDailyInterestRate: "0.000145", + SixtyDayYearlyInterestRate: "0.04103", + }, + }, + Total: 1, + } + s.assertVipLoanLoanableAssetDataResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanLoanableAssetDataResponseEqual(e, a VipLoanLoanableAssetDataResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].LoanCoin, a.Rows[i].LoanCoin, "LoanCoin") + r.Equal(e.Rows[i].FlexibleYearlyInterestRate, a.Rows[i].FlexibleYearlyInterestRate, "FlexibleYearlyInterestRate") + r.Equal(e.Rows[i].FlexibleDailyInterestRate, a.Rows[i].FlexibleDailyInterestRate, "FlexibleDailyInterestRate") + r.Equal(e.Rows[i].ThirtyDayYearlyInterestRate, a.Rows[i].ThirtyDayYearlyInterestRate, "ThirtyDayYearlyInterestRate") + r.Equal(e.Rows[i].ThirtyDayDailyInterestRate, a.Rows[i].ThirtyDayDailyInterestRate, "ThirtyDayDailyInterestRate") + r.Equal(e.Rows[i].SixtyDayYearlyInterestRate, a.Rows[i].SixtyDayYearlyInterestRate, "SixtyDayYearlyInterestRate") + r.Equal(e.Rows[i].SixtyDayDailyInterestRate, a.Rows[i].SixtyDayDailyInterestRate, "SixtyDayDailyInterestRate") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanLoanCollateralData() { + data := []byte(`{ + "rows": [ + { + "collateralCoin": "BUSD", + "_1stCollateralRatio": "100%", + "_1stCollateralRange": "1-10000000", + "_2ndCollateralRatio": "80%", + "_2ndCollateralRange": "10000000-100000000", + "_3rdCollateralRatio": "60%", + "_3rdCollateralRange": "100000000-1000000000", + "_4thCollateralRatio": "0%", + "_4thCollateralRange": ">10000000000" + } + ], + "total": 1 +}`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().CollateralAssetData().CollateralCoin("BUSD").Do(newContext()) + s.r().NoError(err) + e := &VipLoanCollateralAssetDataResponse{ + Rows: []VipLoanCollateralAssetData{ + { + CollateralCoin: "BUSD", + FirstCollateralRatio: "100%", + FirstCollateralRange: "1-10000000", + SecondCollateralRatio: "80%", + SecondCollateralRange: "10000000-100000000", + ThirdCollateralRatio: "60%", + ThirdCollateralRange: "100000000-1000000000", + FourthCollateralRatio: "0%", + FourthCollateralRange: ">10000000000", + }, + }, + Total: 1, + } + + s.assertVipLoanCollateralAssetDataResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanCollateralAssetDataResponseEqual(e, a VipLoanCollateralAssetDataResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].CollateralCoin, a.Rows[i].CollateralCoin, "CollateralCoin") + r.Equal(e.Rows[i].FirstCollateralRatio, a.Rows[i].FirstCollateralRatio, "FirstCollateralRatio") + r.Equal(e.Rows[i].FirstCollateralRange, a.Rows[i].FirstCollateralRange, "FirstCollateralRange") + r.Equal(e.Rows[i].SecondCollateralRatio, a.Rows[i].SecondCollateralRatio, "SecondCollateralRatio") + r.Equal(e.Rows[i].SecondCollateralRange, a.Rows[i].SecondCollateralRange, "SecondCollateralRange") + r.Equal(e.Rows[i].ThirdCollateralRatio, a.Rows[i].ThirdCollateralRatio, "ThirdCollateralRatio") + r.Equal(e.Rows[i].ThirdCollateralRange, a.Rows[i].ThirdCollateralRange, "ThirdCollateralRange") + r.Equal(e.Rows[i].FourthCollateralRatio, a.Rows[i].FourthCollateralRatio, "FourthCollateralRatio") + r.Equal(e.Rows[i].FourthCollateralRange, a.Rows[i].FourthCollateralRange, "FourthCollateralRange") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanOngoingOrders() { + data := []byte(`{ + "rows": [ + { + "orderId": 100000001, + "loanCoin": "BUSD", + "totalDebt": "10000", + "loanRate": "0.0123", + "residualInterest": "10.27687923", + "collateralAccountId": "12345678,23456789", + "collateralCoin": "BNB,BTC,ETH", + "totalCollateralValueAfterHaircut": "25000.27565492", + "lockedCollateralValue": "25000.27565492", + "currentLTV": "0.57", + "expirationTime": 1575018510000, + "loanDate": "1676851200000", + "loanTerm": "30days" + } + ], + "total": 1 +}`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().OngoingOrders().LoanCoin("BUSD").OrderId(100000001).Do(newContext()) + s.r().NoError(err) + e := &VipLoanOngoingOrderResponse{ + Rows: []VipLoanOngoingOrder{ + { + OrderId: 100000001, + LoanCoin: "BUSD", + TotalDebt: "10000", + LoanRate: "0.0123", + ResidualInterest: "10.27687923", + CollateralAccountId: "12345678,23456789", + CollateralCoin: "BNB,BTC,ETH", + TotalCollateralValueAfterHaircut: "25000.27565492", + LockedCollateralValue: "25000.27565492", + CurrentLTV: "0.57", + ExpirationTime: 1575018510000, + LoanDate: "1676851200000", + LoanTerm: "30days", + }, + }, + Total: 1, + } + s.assertVipLoanOngoingOrderResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanOngoingOrderResponseEqual(e, a VipLoanOngoingOrderResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].OrderId, a.Rows[i].OrderId, "OrderId") + r.Equal(e.Rows[i].LoanCoin, a.Rows[i].LoanCoin, "LoanCoin") + r.Equal(e.Rows[i].TotalDebt, a.Rows[i].TotalDebt, "TotalDebt") + r.Equal(e.Rows[i].LoanRate, a.Rows[i].LoanRate, "LoanRate") + r.Equal(e.Rows[i].ResidualInterest, a.Rows[i].ResidualInterest, "ResidualInterest") + r.Equal(e.Rows[i].CollateralAccountId, a.Rows[i].CollateralAccountId, "CollateralAccountId") + r.Equal(e.Rows[i].CollateralCoin, a.Rows[i].CollateralCoin, "CollateralCoin") + r.Equal(e.Rows[i].TotalCollateralValueAfterHaircut, a.Rows[i].TotalCollateralValueAfterHaircut, "TotalCollateralValueAfterHaircut") + r.Equal(e.Rows[i].LockedCollateralValue, a.Rows[i].LockedCollateralValue, "LockedCollateralValue") + r.Equal(e.Rows[i].CurrentLTV, a.Rows[i].CurrentLTV, "CurrentLTV") + r.Equal(e.Rows[i].ExpirationTime, a.Rows[i].ExpirationTime, "ExpirationTime") + r.Equal(e.Rows[i].LoanDate, a.Rows[i].LoanDate, "LoanDate") + r.Equal(e.Rows[i].LoanTerm, a.Rows[i].LoanTerm, "LoanTerm") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanRepaymentHistory() { + data := []byte(`{ + "rows": [ + { + "loanCoin": "BUSD", + "repayAmount": "10000", + "collateralCoin": "BNB,BTC,ETH", + "repayStatus": "Repaid", + "loanDate": "1676851200000", + "repayTime": "1575018510000", + "orderId": "756783308056935434" + } + ], + "total": 1 +}`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().RepaymentHistory().LoanCoin("BUSD").OrderId(756783308056935434).Do(newContext()) + s.r().NoError(err) + e := &VipLoanRepaymentHistoryResponse{ + Rows: []VipLoanRepaymentHistory{ + { + LoanCoin: "BUSD", + RepayAmount: "10000", + CollateralCoin: "BNB,BTC,ETH", + RepayStatus: "Repaid", + LoanDate: "1676851200000", + RepayTime: "1575018510000", + OrderId: "756783308056935434", + }, + }, + Total: 1, + } + s.assertVipLoanRepaymentRecordResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanRepaymentRecordResponseEqual(e, a VipLoanRepaymentHistoryResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].LoanCoin, a.Rows[i].LoanCoin, "LoanCoin") + r.Equal(e.Rows[i].RepayAmount, a.Rows[i].RepayAmount, "RepayAmount") + r.Equal(e.Rows[i].CollateralCoin, a.Rows[i].CollateralCoin, "CollateralCoin") + r.Equal(e.Rows[i].RepayStatus, a.Rows[i].RepayStatus, "RepayStatus") + r.Equal(e.Rows[i].LoanDate, a.Rows[i].LoanDate, "LoanDate") + r.Equal(e.Rows[i].RepayTime, a.Rows[i].RepayTime, "RepayTime") + r.Equal(e.Rows[i].OrderId, a.Rows[i].OrderId, "OrderId") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanAccruedInterest() { + data := []byte(`{ + "rows": [ + { + "loanCoin": "USDT", + "principalAmount": "10000", + "interestAmount": "1.2", + "annualInterestRate": "0.001273", + "accrualTime": 1575018510000, + "orderId": 756783308056935434 + } + ], + "total": 1 +}`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().AccruedInterest().LoanCoin("USDT").OrderId(756783308056935434).Do(newContext()) + s.r().NoError(err) + e := &VipLoanAccruedInterestResponse{ + Rows: []VipLoanAccruedInterest{ + { + LoanCoin: "USDT", + PrincipalAmount: "10000", + InterestAmount: "1.2", + AnnualInterestRate: "0.001273", + AccrualTime: 1575018510000, + OrderId: 756783308056935434, + }, + }, + Total: 1, + } + s.assertVipLoanAccruedInterestResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanAccruedInterestResponseEqual(e, a VipLoanAccruedInterestResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].LoanCoin, a.Rows[i].LoanCoin, "LoanCoin") + r.Equal(e.Rows[i].PrincipalAmount, a.Rows[i].PrincipalAmount, "PrincipalAmount") + r.Equal(e.Rows[i].InterestAmount, a.Rows[i].InterestAmount, "InterestAmount") + r.Equal(e.Rows[i].AnnualInterestRate, a.Rows[i].AnnualInterestRate, "AnnualInterestRate") + r.Equal(e.Rows[i].AccrualTime, a.Rows[i].AccrualTime, "AccrualTime") + r.Equal(e.Rows[i].OrderId, a.Rows[i].OrderId, "OrderId") + } +} + +func (s *vipLoanServiceTestSuite) TestVipCollateralAccount() { + data := []byte(`{ + "rows": [ + { + "collateralAccountId": "12345678", + "collateralCoin": "BNB,BTC,ETH" + }, + { + "collateralAccountId": "23456789", + "collateralCoin": "BNB,BTC,ETH" + } + ], + "total": 2 +}`) + s.mockDo(data, nil) + defer s.assertDo() + res, err := s.client.NewVipLoanService().CollateralAccount().Do(newContext()) + s.r().NoError(err) + e := &VipLoanCollateralAccountResponse{ + Rows: []VipLoanCollateralAccount{ + { + CollateralAccountId: "12345678", + CollateralCoin: "BNB,BTC,ETH", + }, + { + CollateralAccountId: "23456789", + CollateralCoin: "BNB,BTC,ETH", + }, + }, + Total: 2, + } + s.assertVipLoanCollateralAccountResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanCollateralAccountResponseEqual(e, a VipLoanCollateralAccountResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].CollateralAccountId, a.Rows[i].CollateralAccountId, "CollateralAccountId") + r.Equal(e.Rows[i].CollateralCoin, a.Rows[i].CollateralCoin, "CollateralCoin") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanApplicationStatus() { + data := []byte(`{ + "rows": [ + { + "loanAccountId": "12345678", + "orderId": "12345678", + "requestId": "12345678", + "loanCoin": "BTC", + "loanAmount": "100.55", + "collateralAccountId": "12345678,12345678,12345678", + "collateralCoin": "BUSD,USDT,ETH", + "loanTerm": "30", + "status": "Repaid", + "loanDate": "1676851200000" + } + ], + "total": 1 +}`) + s.mockDo(data, nil) + defer s.assertDo() + + res, err := s.client.NewVipLoanService().ApplicationStatus().Do(newContext()) + s.r().NoError(err) + e := &VipLoanApplicationStatusResponse{ + Rows: []VipLoanApplicationStatus{ + { + LoanAccountId: "12345678", + OrderId: "12345678", + RequestId: "12345678", + LoanCoin: "BTC", + LoanAmount: "100.55", + LoanTerm: "30", + Status: "Repaid", + LoanDate: "1676851200000", + }, + }, + Total: 1, + } + s.assertVipLoanApplicationStatusResponseEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanApplicationStatusResponseEqual(e, a VipLoanApplicationStatusResponse) { + r := s.r() + r.Equal(e.Total, a.Total) + for i := 0; i < len(e.Rows); i++ { + r.Equal(e.Rows[i].LoanAccountId, a.Rows[i].LoanAccountId, "LoanAccountId") + r.Equal(e.Rows[i].OrderId, a.Rows[i].OrderId, "OrderId") + r.Equal(e.Rows[i].RequestId, a.Rows[i].RequestId, "RequestId") + r.Equal(e.Rows[i].LoanCoin, a.Rows[i].LoanCoin, "LoanCoin") + r.Equal(e.Rows[i].LoanAmount, a.Rows[i].LoanAmount, "LoanAmount") + r.Equal(e.Rows[i].LoanTerm, a.Rows[i].LoanTerm, "LoanTerm") + r.Equal(e.Rows[i].Status, a.Rows[i].Status, "Status") + r.Equal(e.Rows[i].LoanDate, a.Rows[i].LoanDate, "LoanDate") + } +} + +func (s *vipLoanServiceTestSuite) TestVipLoanRenew() { + data := []byte(`{ + "loanAccountId": "12345678", + "loanCoin": "BTC", + "loanAmount": "100.55", + "collateralAccountId": "12345677,12345678,12345679", + "collateralCoin": "BUSD,USDT,ETH", + "loanTerm": "30" +}`) + s.mockDo(data, nil) + defer s.assertDo() + + res, err := s.client.NewVipLoanService().Renew().OrderId(12345678).LoanTerm(30).Do(newContext()) + s.r().NoError(err) + e := &VipLoanRenew{LoanCoin: "BTC", LoanAmount: "100.55", LoanTerm: "30", CollateralAccountId: "12345677,12345678,12345679", CollateralCoin: "BUSD,USDT,ETH"} + s.assertVipLoanRenewEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertVipLoanRenewEqual(e, a VipLoanRenew) { + r := s.r() + r.Equal(e.LoanCoin, a.LoanCoin, "LoanCoin") + r.Equal(e.LoanAmount, a.LoanAmount, "LoanAmount") + r.Equal(e.LoanTerm, a.LoanTerm, "LoanTerm") + r.Equal(e.CollateralAccountId, a.CollateralAccountId, "CollateralAccountId") + r.Equal(e.CollateralCoin, a.CollateralCoin, "CollateralCoin") +} + +func (s *vipLoanServiceTestSuite) TestVipLoanRepay() { + data := []byte(`{ + "loanCoin": "BUSD", + "repayAmount": "200.5", + "remainingPrincipal": "100.5", + "remainingInterest": "0", + "collateralCoin": "BNB,BTC,ETH", + "currentLTV": "0.25", + "repayStatus": "Repaid" +}`) + s.mockDo(data, nil) + defer s.assertDo() + + res, err := s.client.NewVipLoanService().Repay().OrderId(12345678).Amount(200.5).Do(newContext()) + s.r().NoError(err) + + e := &VipLoanRepay{ + LoanCoin: "BUSD", + RepayAmount: "200.5", + RemainingPrincipal: "100.5", + RemainingInterest: "0", + CollateralCoin: "BNB,BTC,ETH", + CurrentLTV: "0.25", + RepayStatus: "Repaid", + } + + s.assertRepayEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertRepayEqual(e, a VipLoanRepay) { + r := s.r() + r.Equal(e.LoanCoin, a.LoanCoin, "LoanCoin") + r.Equal(e.RepayAmount, a.RepayAmount, "RepayAmount") + r.Equal(e.RemainingPrincipal, a.RemainingPrincipal, "RemainingPrincipal") + r.Equal(e.RemainingInterest, a.RemainingInterest, "RemainingInterest") + r.Equal(e.CollateralCoin, a.CollateralCoin, "CollateralCoin") + r.Equal(e.CurrentLTV, a.CurrentLTV, "CurrentLTV") + r.Equal(e.RepayStatus, a.RepayStatus, "RepayStatus") +} + +func (s *vipLoanServiceTestSuite) TestVipLoanBorrow() { + data := []byte(`{ + "loanAccountId": "12345678", + "requestId": "12345678", + "loanCoin": "BTC", + "isFlexibleRate": "No", + "loanAmount": "100.55", + "collateralAccountId": "12345678,12345678,12345678", + "collateralCoin": "BUSD,USDT,ETH", + "loanTerm": "30" +}`) + s.mockDo(data, nil) + defer s.assertDo() + + res, err := s.client.NewVipLoanService().Borrow().LoanAccountId(12345678).LoanCoin("BTC"). + CollateralAccountId("12345678,12345678,12345678").CollateralCoin("BUSD,USDT,ETH"). + IsFlexibleRate(false).LoanTerm(30).Do(newContext()) + s.r().NoError(err) + + e := &VipLoanBorrow{ + LoanAccountId: "12345678", + RequestId: "12345678", + LoanCoin: "BTC", + IsFlexibleRate: "No", + LoanAmount: "100.55", + CollateralAccountId: "12345678,12345678,12345678", + CollateralCoin: "BUSD,USDT,ETH", + LoanTerm: "30", + } + + s.assertBorrowEqual(*e, *res) +} + +func (s *vipLoanServiceTestSuite) assertBorrowEqual(e, a VipLoanBorrow) { + r := s.r() + r.Equal(e.LoanAccountId, a.LoanAccountId, "LoanAccountId") + r.Equal(e.RequestId, a.RequestId, "RequestId") + r.Equal(e.LoanCoin, a.LoanCoin, "LoanCoin") + r.Equal(e.IsFlexibleRate, a.IsFlexibleRate, "IsFlexibleRate") + r.Equal(e.LoanAmount, a.LoanAmount, "LoanAmount") + r.Equal(e.CollateralAccountId, a.CollateralAccountId, "CollateralAccountId") + r.Equal(e.CollateralCoin, a.CollateralCoin, "CollateralCoin") + r.Equal(e.LoanTerm, a.LoanTerm, "LoanTerm") +} From 22b70a1acff79d588e3899ea6224c36491862ed3 Mon Sep 17 00:00:00 2001 From: DenrianWeiss Date: Thu, 10 Apr 2025 11:00:36 +0800 Subject: [PATCH 2/3] feat(client): add vip loan apis support --- v2/vip_loan_service.go | 66 ++++++++++++++++++++++++------------- v2/vip_loan_service_test.go | 12 +++---- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/v2/vip_loan_service.go b/v2/vip_loan_service.go index deccb73e0..897c4abaf 100644 --- a/v2/vip_loan_service.go +++ b/v2/vip_loan_service.go @@ -95,7 +95,7 @@ type VipLoanInterestRateElement struct { Asset string `json:"asset"` FlexibleDailyInterestRate string `json:"flexibleDailyInterestRate"` FlexibleYearlyInterestRate string `json:"flexibleYearlyInterestRate"` - Time int64 `json:"time"` + Time string `json:"time"` } type VipLoanInterestRateHistoryService struct { @@ -173,7 +173,7 @@ type VipLoanInterestRateHistoryResponse struct { type VipLoanInterestRateHistory struct { Coin string `json:"coin"` AnnualizedInterestRate string `json:"annualizedInterestRate"` - Time int64 `json:"time"` + Time string `json:"time"` } type VipLoanLoanableAssetDataService struct { @@ -271,15 +271,25 @@ type VipLoanCollateralAssetDataResponse struct { } type VipLoanCollateralAssetData struct { - CollateralCoin string `json:"collateralCoin"` - FirstCollateralRatio string `json:"_1stCollateralRatio"` - FirstCollateralRange string `json:"_1stCollateralRange"` - SecondCollateralRatio string `json:"_2ndCollateralRatio"` - SecondCollateralRange string `json:"_2ndCollateralRange"` - ThirdCollateralRatio string `json:"_3rdCollateralRatio"` - ThirdCollateralRange string `json:"_3rdCollateralRange"` - FourthCollateralRatio string `json:"_4thCollateralRatio"` - FourthCollateralRange string `json:"_4thCollateralRange"` + CollateralCoin string `json:"collateralCoin"` + FirstCollateralRatio string `json:"_1stCollateralRatio"` + FirstCollateralRange string `json:"_1stCollateralRange"` + SecondCollateralRatio string `json:"_2ndCollateralRatio"` + SecondCollateralRange string `json:"_2ndCollateralRange"` + ThirdCollateralRatio string `json:"_3rdCollateralRatio"` + ThirdCollateralRange string `json:"_3rdCollateralRange"` + FourthCollateralRatio string `json:"_4thCollateralRatio"` + FourthCollateralRange string `json:"_4thCollateralRange"` + FifthCollateralRatio string `json:"_5thCollateralRatio"` + FifthCollateralRange string `json:"_5thCollateralRange"` + SixthCollateralRatio string `json:"_6thCollateralRatio"` + SixthCollateralRange string `json:"_6thCollateralRange"` + SeventhCollateralRatio string `json:"_7thCollateralRatio"` + SeventhCollateralRange string `json:"_7thCollateralRange"` + EighthCollateralRatio string `json:"_8thCollateralRatio"` + EighthCollateralRange string `json:"_8thCollateralRange"` + NinthCollateralRatio string `json:"_9thCollateralRatio"` + NinthCollateralRange string `json:"_9thCollateralRange"` } type VipLoanOngoingOrdersService struct { @@ -688,7 +698,7 @@ func (s *VipLoanRenewService) Do(ctx context.Context, opts ...RequestOption) (*V "orderId": s.orderId, "loanTerm": s.loanTerm, } - r.setFormParams(m) + r.setParams(m) data, err := s.c.callAPI(ctx, r, opts...) if err != nil { return nil, err @@ -736,7 +746,7 @@ func (s *VipLoanRepayService) Do(ctx context.Context, opts ...RequestOption) (*V "orderId": s.orderId, "amount": s.amount, } - r.setFormParams(m) + r.setParams(m) data, err := s.c.callAPI(ctx, r, opts...) if err != nil { return nil, err @@ -811,15 +821,27 @@ func (s *VipLoanBorrowService) Do(ctx context.Context, opts ...RequestOption) (* endpoint: "/sapi/v1/loan/vip/borrow", secType: secTypeSigned, } - m := params{ - "loanAccountId": s.loanAccountId, - "loanAmount": s.loanAmount, - "collateralAccountId": s.collateralAccountId, - "collateralCoin": s.collateralCoin, - "isFlexibleRate": s.isFlexibleRate, - "loanTerm": s.loanTerm, - } - r.setFormParams(m) + if s.loanAccountId != nil { + r.setParam("loanAccountId", *s.loanAccountId) + } + if s.loanCoin != "" { + r.setParam("loanCoin", s.loanCoin) + } + if s.loanAmount != nil { + r.setParam("loanAmount", *s.loanAmount) + } + if s.collateralAccountId != nil { + r.setParam("collateralAccountId", *s.collateralAccountId) + } + if s.collateralCoin != nil { + r.setParam("collateralCoin", *s.collateralCoin) + } + if s.isFlexibleRate != nil { + r.setParam("isFlexibleRate", *s.isFlexibleRate) + } + if s.loanTerm != nil { + r.setParam("loanTerm", *s.loanTerm) + } data, err := s.c.callAPI(ctx, r, opts...) if err != nil { return nil, err diff --git a/v2/vip_loan_service_test.go b/v2/vip_loan_service_test.go index 086f7cd31..594fb0c2f 100644 --- a/v2/vip_loan_service_test.go +++ b/v2/vip_loan_service_test.go @@ -19,13 +19,13 @@ func (s *vipLoanServiceTestSuite) TestVipLoanInterestRateService() { "asset": "BUSD", "flexibleDailyInterestRate": "0.001503", "flexibleYearlyInterestRate": "0.548595", - "time": 1577233578000 + "time": "1577233578000" }, { "asset": "BTC", "flexibleDailyInterestRate": "0.001503", "flexibleYearlyInterestRate": "0.548595", - "time": 1577233562000 + "time": "1577233562000" } ]`) s.mockDo(data, nil) @@ -42,13 +42,13 @@ func (s *vipLoanServiceTestSuite) TestVipLoanInterestRateService() { Asset: "BUSD", FlexibleDailyInterestRate: "0.001503", FlexibleYearlyInterestRate: "0.548595", - Time: 1577233578000, + Time: "1577233578000", }, { Asset: "BTC", FlexibleDailyInterestRate: "0.001503", FlexibleYearlyInterestRate: "0.548595", - Time: 1577233562000, + Time: "1577233562000", }, } s.assertVipLoanInterestRateEqual(*e, *res) @@ -70,7 +70,7 @@ func (s *vipLoanServiceTestSuite) TestVipLoanInterestRateHistory() { { "coin": "USDT", "annualizedInterestRate": "0.0647", - "time": 1575018510000 + "time": "1575018510000" } ], "total": 1 @@ -84,7 +84,7 @@ func (s *vipLoanServiceTestSuite) TestVipLoanInterestRateHistory() { { Coin: "USDT", AnnualizedInterestRate: "0.0647", - Time: 1575018510000, + Time: "1575018510000", }, }, Total: 1, From f3ba2a05b10539795118b801a43f2591df71f870 Mon Sep 17 00:00:00 2001 From: DenrianWeiss Date: Thu, 10 Apr 2025 15:12:05 +0800 Subject: [PATCH 3/3] feat(client): vip-loan: make mandatory args no pointer --- v2/vip_loan_service.go | 89 ++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/v2/vip_loan_service.go b/v2/vip_loan_service.go index 897c4abaf..bc39c5356 100644 --- a/v2/vip_loan_service.go +++ b/v2/vip_loan_service.go @@ -60,11 +60,11 @@ func (s *VipLoanService) Borrow() *VipLoanBorrowService { type VipLoanInterestRateService struct { c *Client - loanCoin *string // Mandatory: YES, max 10 assets split by comma + loanCoin string // Mandatory: YES, max 10 assets split by comma } func (s *VipLoanInterestRateService) LoanCoin(loanCoin string) *VipLoanInterestRateService { - s.loanCoin = &loanCoin + s.loanCoin = loanCoin return s } @@ -74,9 +74,9 @@ func (s *VipLoanInterestRateService) Do(ctx context.Context) (*VipLoanInterestRa endpoint: "/sapi/v1/loan/vip/request/interestRate", secType: secTypeSigned, } - if s.loanCoin != nil { - r.setParam("loanCoin", *s.loanCoin) - } + + r.setParam("loanCoin", s.loanCoin) + data, err := s.c.callAPI(ctx, r) if err != nil { return nil, err @@ -100,7 +100,7 @@ type VipLoanInterestRateElement struct { type VipLoanInterestRateHistoryService struct { c *Client - coin *string + coin string startTime *int64 endTime *int64 current *int64 // Mandatory: NO, default 1 @@ -108,7 +108,7 @@ type VipLoanInterestRateHistoryService struct { } func (s *VipLoanInterestRateHistoryService) Coin(coin string) *VipLoanInterestRateHistoryService { - s.coin = &coin + s.coin = coin return s } @@ -138,9 +138,9 @@ func (s *VipLoanInterestRateHistoryService) Do(ctx context.Context) (*VipLoanInt endpoint: "/sapi/v1/loan/vip/interestRateHistory", secType: secTypeSigned, } - if s.coin != nil { - r.setParam("coin", *s.coin) - } + + r.setParam("coin", s.coin) + if s.startTime != nil { r.setParam("startTime", *s.startTime) } @@ -674,17 +674,17 @@ type VipLoanApplicationStatus struct { type VipLoanRenewService struct { c *Client - orderId *int64 // Mandatory: YES - loanTerm *int64 // Mandatory: YES, 30 or 60 days + orderId int64 // Mandatory: YES + loanTerm int64 // Mandatory: YES, 30 or 60 days } func (s *VipLoanRenewService) OrderId(orderId int64) *VipLoanRenewService { - s.orderId = &orderId + s.orderId = orderId return s } func (s *VipLoanRenewService) LoanTerm(loanTerm int64) *VipLoanRenewService { - s.loanTerm = &loanTerm + s.loanTerm = loanTerm return s } @@ -722,17 +722,17 @@ type VipLoanRenew struct { type VipLoanRepayService struct { c *Client - orderId *int64 - amount *float64 + orderId int64 + amount float64 } func (s *VipLoanRepayService) OrderId(orderId int64) *VipLoanRepayService { - s.orderId = &orderId + s.orderId = orderId return s } func (s *VipLoanRepayService) Amount(amount float64) *VipLoanRepayService { - s.amount = &amount + s.amount = amount return s } @@ -771,17 +771,17 @@ type VipLoanRepay struct { type VipLoanBorrowService struct { c *Client - loanAccountId *int64 + loanAccountId int64 loanCoin string // Mandatory: YES - loanAmount *float64 - collateralAccountId *string // Mandatory: YES, accounts split by comma - collateralCoin *string // Mandatory: YES, coins split by comma - isFlexibleRate *bool // Mandatory: YES, TRUE: flexible rate; FALSE: fixed rate - loanTerm *int64 // Mandatory: YES for fixed interest, No for floating interest, 30 or 60 + loanAmount float64 + collateralAccountId string // Mandatory: YES, accounts split by comma + collateralCoin string // Mandatory: YES, coins split by comma + isFlexibleRate bool // Mandatory: YES, TRUE: flexible rate; FALSE: fixed rate + loanTerm *int64 // Mandatory: YES for fixed interest, No for floating interest, 30 or 60 } func (s *VipLoanBorrowService) LoanAccountId(loanAccountId int64) *VipLoanBorrowService { - s.loanAccountId = &loanAccountId + s.loanAccountId = loanAccountId return s } @@ -791,22 +791,22 @@ func (s *VipLoanBorrowService) LoanCoin(loanCoin string) *VipLoanBorrowService { } func (s *VipLoanBorrowService) LoanAmount(loanAmount float64) *VipLoanBorrowService { - s.loanAmount = &loanAmount + s.loanAmount = loanAmount return s } func (s *VipLoanBorrowService) CollateralAccountId(collateralAccountId string) *VipLoanBorrowService { - s.collateralAccountId = &collateralAccountId + s.collateralAccountId = collateralAccountId return s } func (s *VipLoanBorrowService) CollateralCoin(collateralCoin string) *VipLoanBorrowService { - s.collateralCoin = &collateralCoin + s.collateralCoin = collateralCoin return s } func (s *VipLoanBorrowService) IsFlexibleRate(isFlexibleRate bool) *VipLoanBorrowService { - s.isFlexibleRate = &isFlexibleRate + s.isFlexibleRate = isFlexibleRate return s } @@ -821,24 +821,19 @@ func (s *VipLoanBorrowService) Do(ctx context.Context, opts ...RequestOption) (* endpoint: "/sapi/v1/loan/vip/borrow", secType: secTypeSigned, } - if s.loanAccountId != nil { - r.setParam("loanAccountId", *s.loanAccountId) - } - if s.loanCoin != "" { - r.setParam("loanCoin", s.loanCoin) - } - if s.loanAmount != nil { - r.setParam("loanAmount", *s.loanAmount) - } - if s.collateralAccountId != nil { - r.setParam("collateralAccountId", *s.collateralAccountId) - } - if s.collateralCoin != nil { - r.setParam("collateralCoin", *s.collateralCoin) - } - if s.isFlexibleRate != nil { - r.setParam("isFlexibleRate", *s.isFlexibleRate) - } + + r.setParam("loanAccountId", s.loanAccountId) + + r.setParam("loanCoin", s.loanCoin) + + r.setParam("loanAmount", s.loanAmount) + + r.setParam("collateralAccountId", s.collateralAccountId) + + r.setParam("collateralCoin", s.collateralCoin) + + r.setParam("isFlexibleRate", s.isFlexibleRate) + if s.loanTerm != nil { r.setParam("loanTerm", *s.loanTerm) }