From 0a6b621646228373a3dcf361cecf380dbb9a74d3 Mon Sep 17 00:00:00 2001 From: 0x0001 <2239315+0x0001@users.noreply.github.com> Date: Sat, 7 Sep 2024 00:54:22 +0800 Subject: [PATCH 1/3] add usds-futures convert service --- v2/futures/client.go | 20 +++ v2/futures/convert_service.go | 238 +++++++++++++++++++++++++++++ v2/futures/convert_service_test.go | 138 +++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 v2/futures/convert_service.go create mode 100644 v2/futures/convert_service_test.go diff --git a/v2/futures/client.go b/v2/futures/client.go index 67ec79a4..34af7ddf 100644 --- a/v2/futures/client.go +++ b/v2/futures/client.go @@ -698,3 +698,23 @@ func (c *Client) NewGetFeeBurnService() *GetFeeBurnService { func (c *Client) NewFeeBurnService() *FeeBurnService { return &FeeBurnService{c: c} } + +// NewListConvertAssetsService init list convert assets service +func (c *Client) NewListConvertExchangeInfoService() *ListConvertExchangeInfoService { + return &ListConvertExchangeInfoService{c: c} +} + +// NewCreateConvertQuoteService init create convert quote service +func (c *Client) NewCreateConvertQuoteService() *CreateConvertQuoteService { + return &CreateConvertQuoteService{c: c} +} + +// NewCreateConvertService init accept convert quote service +func (c *Client) NewConvertAcceptService() *ConvertAcceptService { + return &ConvertAcceptService{c: c} +} + +// NewGetConvertStatusService init get convert status service +func (c *Client) NewGetConvertStatusService() *ConvertStatusService { + return &ConvertStatusService{c: c} +} diff --git a/v2/futures/convert_service.go b/v2/futures/convert_service.go new file mode 100644 index 00000000..98c0ba76 --- /dev/null +++ b/v2/futures/convert_service.go @@ -0,0 +1,238 @@ +package futures + +import ( + "context" + "encoding/json" + "net/http" +) + +type ListConvertExchangeInfoService struct { + c *Client + fromAsset string + toAsset string +} + +type ConvertExchangeInfo struct { + FromAsset string `json:"fromAsset"` + ToAsset string `json:"toAsset"` + FromAssetMinAmount string `json:"fromAssetMinAmount"` + FromAssetMaxAmount string `json:"fromAssetMaxAmount"` + ToAssetMinAmount string `json:"toAssetMinAmount"` + ToAssetMaxAmount string `json:"toAssetMaxAmount"` +} + +func (l *ListConvertExchangeInfoService) FromAsset(fromAsset string) *ListConvertExchangeInfoService { + l.fromAsset = fromAsset + return l +} + +func (l *ListConvertExchangeInfoService) ToAsset(toAsset string) *ListConvertExchangeInfoService { + l.toAsset = toAsset + return l +} + +func (l *ListConvertExchangeInfoService) Do(ctx context.Context, opts ...RequestOption) (res []ConvertExchangeInfo, err error) { + r := &request{ + method: http.MethodGet, + endpoint: "/fapi/v1/convert/exchangeInfo", + secType: secTypeNone, + } + if l.fromAsset != "" { + r.setParam("fromAsset", l.fromAsset) + } + if l.toAsset != "" { + r.setParam("toAsset", l.toAsset) + } + + data, _, err := l.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = make([]ConvertExchangeInfo, 0, 50) + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return res, nil +} + +type ConvertValidTime string + +const ( + ConvertValidTime10S ConvertValidTime = "10s" + ConvertValidTime30S ConvertValidTime = "30s" + ConvertValidTime1M ConvertValidTime = "1m" + ConvertValidTime2M ConvertValidTime = "2m" +) + +type CreateConvertQuoteService struct { + c *Client + fromAsset string + toAsset string + fromAmount string + toAmount string + validTime ConvertValidTime +} + +func (c *CreateConvertQuoteService) FromAsset(fromAsset string) *CreateConvertQuoteService { + c.fromAsset = fromAsset + return c +} + +func (c *CreateConvertQuoteService) ToAsset(toAsset string) *CreateConvertQuoteService { + c.toAsset = toAsset + return c +} + +func (c *CreateConvertQuoteService) FromAmount(fromAmount string) *CreateConvertQuoteService { + c.fromAmount = fromAmount + return c +} + +func (c *CreateConvertQuoteService) ToAmount(toAmount string) *CreateConvertQuoteService { + c.toAmount = toAmount + return c +} + +func (c *CreateConvertQuoteService) ValidTime(validTime ConvertValidTime) *CreateConvertQuoteService { + c.validTime = validTime + return c +} + +type ConvertQuote struct { + QuoteId string `json:"quoteId"` + Ratio string `json:"ratio"` + InverseRatio string `json:"inverseRatio"` + ValidTimestamp int64 `json:"validTimestamp"` + ToAmount string `json:"toAmount"` + FromAmount string `json:"fromAmount"` +} + +func (c *CreateConvertQuoteService) Do(ctx context.Context, opts ...RequestOption) (res *ConvertQuote, err error) { + r := &request{ + method: http.MethodPost, + endpoint: "/fapi/v1/convert/getQuote", + secType: secTypeSigned, + } + m := params{ + "fromAsset": c.fromAsset, + "toAsset": c.toAsset, + } + if c.fromAmount != "" { + m["fromAmount"] = c.fromAmount + } + if c.toAmount != "" { + m["toAmount"] = c.toAmount + } + if c.validTime != "" { + m["validTime"] = c.validTime + } + r.setFormParams(m) + data, _, err := c.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ConvertQuote) + if err := json.Unmarshal(data, res); err != nil { + return nil, err + } + return res, nil +} + +type ConvertAcceptService struct { + c *Client + quoteId string +} + +func (c *ConvertAcceptService) QuoteId(quoteId string) *ConvertAcceptService { + c.quoteId = quoteId + return c +} + +type ConvertAcceptStatus string + +const ( + ConvertAcceptStatusProcess ConvertAcceptStatus = "PROCESS" + ConvertAcceptStatusAcceptSuccess ConvertAcceptStatus = "ACCEPT_SUCCESS" + ConvertAcceptStatusSuccess ConvertAcceptStatus = "SUCCESS" + ConvertAcceptStatusFailed ConvertAcceptStatus = "FAILED" +) + +type ConvertResult struct { + OrderId string `json:"orderId"` + CreateTime int64 `json:"createTime"` + OrderStatus ConvertAcceptStatus `json:"orderStatus"` +} + +func (c *ConvertAcceptService) Do(ctx context.Context, opts ...RequestOption) (res *ConvertResult, err error) { + r := &request{ + method: http.MethodPost, + endpoint: "/fapi/v1/convert", + secType: secTypeSigned, + } + m := params{ + "quoteId": c.quoteId, + } + r.setFormParams(m) + data, _, err := c.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ConvertResult) + if err := json.Unmarshal(data, res); err != nil { + return nil, err + } + return res, nil +} + +type ConvertStatusService struct { + c *Client + quoteId string + orderId string +} + +func (c *ConvertStatusService) QuoteId(quoteId string) *ConvertStatusService { + c.quoteId = quoteId + return c +} + +func (c *ConvertStatusService) OrderId(orderId string) *ConvertStatusService { + c.orderId = orderId + return c +} + +type ConvertStatusResult struct { + OrderId int64 `json:"orderId"` + OrderStatus ConvertAcceptStatus `json:"orderStatus"` + FromAsset string `json:"fromAsset"` + FromAmount string `json:"fromAmount"` + ToAsset string `json:"toAsset"` + ToAmount string `json:"toAmount"` + Ratio string `json:"ratio"` + InverseRatio string `json:"inverseRatio"` + CreateTime int64 `json:"createTime"` +} + +func (c *ConvertStatusService) Do(ctx context.Context, opts ...RequestOption) (res *ConvertStatusResult, err error) { + r := &request{ + method: http.MethodGet, + endpoint: "/fapi/v1/convert", + secType: secTypeSigned, + } + m := params{ + "quoteId": c.quoteId, + } + if c.orderId != "" { + m["orderId"] = c.orderId + } + r.setParam("quoteId", c.quoteId) + r.setFormParams(m) + data, _, err := c.c.callAPI(ctx, r, opts...) + if err != nil { + return nil, err + } + res = new(ConvertStatusResult) + if err := json.Unmarshal(data, res); err != nil { + return nil, err + } + return res, nil +} diff --git a/v2/futures/convert_service_test.go b/v2/futures/convert_service_test.go new file mode 100644 index 00000000..1910b96d --- /dev/null +++ b/v2/futures/convert_service_test.go @@ -0,0 +1,138 @@ +package futures + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type convertServiceTestSuite struct { + baseTestSuite +} + +func TestConvertService(t *testing.T) { + suite.Run(t, new(convertServiceTestSuite)) +} + +func (s *convertServiceTestSuite) TestListConvertExchangeInfo() { + data := []byte(`[ + { + "fromAsset":"BTC", + "toAsset":"USDT", + "fromAssetMinAmount":"0.0004", + "fromAssetMaxAmount":"50", + "toAssetMinAmount":"20", + "toAssetMaxAmount":"2500000" + } +]`) + s.mockDo(data, nil) + defer s.assertDo() + + fromAsset := "BTC" + toAsset := "USDT" + s.assertReq(func(r *request) { + e := newRequest().setParams(params{ + "fromAsset": fromAsset, + "toAsset": toAsset, + }) + s.assertRequestEqual(e, r) + }) + + res, err := s.client.NewListConvertExchangeInfoService().FromAsset(fromAsset).ToAsset(toAsset).Do(newContext()) + s.r().NoError(err) + e := []ConvertExchangeInfo{{ + FromAsset: fromAsset, + ToAsset: toAsset, + FromAssetMinAmount: "0.0004", + FromAssetMaxAmount: "50", + ToAssetMinAmount: "20", + ToAssetMaxAmount: "2500000", + }} + s.assertConvertExchangeInfoEqual(e, res) +} + +func (s *convertServiceTestSuite) TestCreateConvertQuote() { + data := []byte(`{ + "quoteId":"12415572564", + "ratio":"38163.7", + "inverseRatio":"0.0000262", + "validTimestamp":1623319461670, + "toAmount":"3816.37", + "fromAmount":"0.1" +}`) + s.mockDo(data, nil) + defer s.assertDo() + + fromAsset := "BTC" + toAsset := "USDT" + fromAmount := "0.0004" + toAmount := "20" + res, err := s.client.NewCreateConvertQuoteService().FromAsset(fromAsset).ToAsset(toAsset).FromAmount(fromAmount).ToAmount(toAmount).Do(newContext()) + s.r().NoError(err) + s.r().Equal("12415572564", res.QuoteId) + s.r().Equal("38163.7", res.Ratio) + s.r().Equal("0.0000262", res.InverseRatio) + s.r().Equal(int64(1623319461670), res.ValidTimestamp) + s.r().Equal("3816.37", res.ToAmount) + s.r().Equal("0.1", res.FromAmount) +} + +func (s *convertServiceTestSuite) TestAcceptQuote() { + data := []byte(`{ + "orderId":"933256278426274426", + "createTime":1623381330472, + "orderStatus":"PROCESS" +}`) + s.mockDo(data, nil) + defer s.assertDo() + + quoteId := "12415572564" + res, err := s.client.NewConvertAcceptService().QuoteId(quoteId).Do(newContext()) + s.r().NoError(err) + s.r().Equal("933256278426274426", res.OrderId) + s.r().Equal(int64(1623381330472), res.CreateTime) + s.r().Equal(ConvertAcceptStatusProcess, res.OrderStatus) +} + +func (s *convertServiceTestSuite) TestGetConvertStatus() { + data := []byte(`{ + "orderId":933256278426274426, + "orderStatus":"SUCCESS", + "fromAsset":"BTC", + "fromAmount":"0.00054414", + "toAsset":"USDT", + "toAmount":"20", + "ratio":"36755", + "inverseRatio":"0.00002721", + "createTime":1623381330472 +}`) + s.mockDo(data, nil) + defer s.assertDo() + + orderId := "933256278426274426" + res, err := s.client.NewGetConvertStatusService().OrderId(orderId).Do(newContext()) + s.r().NoError(err) + s.r().Equal(int64(933256278426274426), res.OrderId) + s.r().Equal(ConvertAcceptStatusSuccess, res.OrderStatus) + s.r().Equal("BTC", res.FromAsset) + s.r().Equal("0.00054414", res.FromAmount) + s.r().Equal("USDT", res.ToAsset) + s.r().Equal("20", res.ToAmount) + s.r().Equal("36755", res.Ratio) + s.r().Equal("0.00002721", res.InverseRatio) + s.r().Equal(int64(1623381330472), res.CreateTime) + +} + +func (s *convertServiceTestSuite) assertConvertExchangeInfoEqual(e, a []ConvertExchangeInfo) { + r := s.r() + r.Len(a, len(e)) + for i := range e { + r.Equal(e[i].FromAsset, a[i].FromAsset, "FromAsset") + r.Equal(e[i].ToAsset, a[i].ToAsset, "ToAsset") + r.Equal(e[i].FromAssetMinAmount, a[i].FromAssetMinAmount, "FromAssetMinAmount") + r.Equal(e[i].FromAssetMaxAmount, a[i].FromAssetMaxAmount, "FromAssetMaxAmount") + r.Equal(e[i].ToAssetMinAmount, a[i].ToAssetMinAmount, "ToAssetMinAmount") + r.Equal(e[i].ToAssetMaxAmount, a[i].ToAssetMaxAmount, "ToAssetMaxAmount") + } +} From cccce407ecf0325e6a2f8e221d568b5345d2f6cc Mon Sep 17 00:00:00 2001 From: 0x0001 <2239315+0x0001@users.noreply.github.com> Date: Sat, 7 Sep 2024 09:17:21 +0800 Subject: [PATCH 2/3] fix 2 endpoint --- v2/futures/convert_service.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/futures/convert_service.go b/v2/futures/convert_service.go index 98c0ba76..91369371 100644 --- a/v2/futures/convert_service.go +++ b/v2/futures/convert_service.go @@ -166,7 +166,7 @@ type ConvertResult struct { func (c *ConvertAcceptService) Do(ctx context.Context, opts ...RequestOption) (res *ConvertResult, err error) { r := &request{ method: http.MethodPost, - endpoint: "/fapi/v1/convert", + endpoint: "/fapi/v1/convert/acceptQuote", secType: secTypeSigned, } m := params{ @@ -215,7 +215,7 @@ type ConvertStatusResult struct { func (c *ConvertStatusService) Do(ctx context.Context, opts ...RequestOption) (res *ConvertStatusResult, err error) { r := &request{ method: http.MethodGet, - endpoint: "/fapi/v1/convert", + endpoint: "/fapi/v1/convert/orderStatus", secType: secTypeSigned, } m := params{ From 81e3790871ee35dcb07bbb0451d470e119c0d9bb Mon Sep 17 00:00:00 2001 From: 0x0001 <2239315+0x0001@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:06:05 +0800 Subject: [PATCH 3/3] fix mismatch between document and actual returned content types --- v2/futures/convert_service.go | 2 +- v2/futures/convert_service_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/futures/convert_service.go b/v2/futures/convert_service.go index 91369371..9430a094 100644 --- a/v2/futures/convert_service.go +++ b/v2/futures/convert_service.go @@ -201,7 +201,7 @@ func (c *ConvertStatusService) OrderId(orderId string) *ConvertStatusService { } type ConvertStatusResult struct { - OrderId int64 `json:"orderId"` + OrderId string `json:"orderId"` OrderStatus ConvertAcceptStatus `json:"orderStatus"` FromAsset string `json:"fromAsset"` FromAmount string `json:"fromAmount"` diff --git a/v2/futures/convert_service_test.go b/v2/futures/convert_service_test.go index 1910b96d..8695aaa5 100644 --- a/v2/futures/convert_service_test.go +++ b/v2/futures/convert_service_test.go @@ -96,7 +96,7 @@ func (s *convertServiceTestSuite) TestAcceptQuote() { func (s *convertServiceTestSuite) TestGetConvertStatus() { data := []byte(`{ - "orderId":933256278426274426, + "orderId":"933256278426274426", "orderStatus":"SUCCESS", "fromAsset":"BTC", "fromAmount":"0.00054414", @@ -112,7 +112,7 @@ func (s *convertServiceTestSuite) TestGetConvertStatus() { orderId := "933256278426274426" res, err := s.client.NewGetConvertStatusService().OrderId(orderId).Do(newContext()) s.r().NoError(err) - s.r().Equal(int64(933256278426274426), res.OrderId) + s.r().Equal("933256278426274426", res.OrderId) s.r().Equal(ConvertAcceptStatusSuccess, res.OrderStatus) s.r().Equal("BTC", res.FromAsset) s.r().Equal("0.00054414", res.FromAmount)