Skip to content

Commit

Permalink
implement V5 REST asset create internal transfer endpoint (#164)
Browse files Browse the repository at this point in the history
Co-authored-by: Rostislav Lyupa <>
  • Loading branch information
lyro41 authored Feb 14, 2024
1 parent 7dbac89 commit c07a9e5
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ The following API endpoints have been implemented

#### Asset

- [`/v5/asset/transfer/inter-transfer` Create Internal Transfer](https://bybit-exchange.github.io/docs/v5/asset/create-inter-transfer)
- [`/v5/asset/transfer/query-inter-transfer-list` Get Internal Transfer Records](https://bybit-exchange.github.io/docs/v5/asset/inter-transfer-list)
- [`/v5/asset/transfer/query-account-coins-balance` Get All Coins Balance](https://bybit-exchange.github.io/docs/v5/asset/all-balance)
- [`/v5/asset/deposit/query-record` Get Deposit Records](https://bybit-exchange.github.io/docs/v5/asset/deposit-record)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
17 changes: 17 additions & 0 deletions integrationtest/v5/asset/asset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ import (
"github.com/stretchr/testify/require"
)

func TestCreateInternalTransfer(t *testing.T) {
client := bybit.NewTestClient().WithAuthFromEnv()
res, err := client.V5().Asset().GetInternalTransferRecords(bybit.V5CreateInternalTransferParam{
TransferID: "42c0cfb0-6bca-c242-bc76-4e6df6cbcb16",
Coin: CoinBTC,
Amount: "0.05",
FromAccountType: AccountTypeV5UNIFIED,
ToAccountType: AccountTypeV5CONTRACT,
})
require.NoError(t, err)
{
goldenFilename := "./testdata/v5-asset-create-internal-transfer.json"
testhelper.Compare(t, goldenFilename, testhelper.ConvertToJSON(res.Result))
testhelper.UpdateFile(t, goldenFilename, testhelper.ConvertToJSON(res.Result))
}
}

func TestGetInternalTransferRecords(t *testing.T) {
client := bybit.NewTestClient().WithAuthFromEnv()
limit := 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"transferId": "42c0cfb0-6bca-c242-bc76-4e6df6cbab16"
}
65 changes: 65 additions & 0 deletions v5_asset_service.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package bybit

import (
"encoding/json"
"fmt"
"strconv"
"strings"

"github.com/google/go-querystring/query"
"github.com/google/uuid"
)

// V5AssetServiceI :
type V5AssetServiceI interface {
CreateInternalTransfer(param V5CreateInternalTransferParam) (*V5CreateInternalTransferResponse, error)
GetInternalTransferRecords(V5GetInternalTransferRecordsParam) (*V5GetInternalTransferRecordsResponse, error)
GetDepositRecords(V5GetDepositRecordsParam) (*V5GetDepositRecordsResponse, error)
GetSubDepositRecords(V5GetSubDepositRecordsParam) (*V5GetSubDepositRecordsResponse, error)
Expand All @@ -22,6 +27,66 @@ type V5AssetService struct {
client *Client
}

// V5CreateInternalTransferParam :
type V5CreateInternalTransferParam struct {
TransferID string `json:"transferId"`
Coin Coin `json:"coin"`
Amount string `json:"amount"`
FromAccountType AccountTypeV5 `json:"fromAccountType"`
ToAccountType AccountTypeV5 `json:"toAccountType"`
}

func (p V5CreateInternalTransferParam) validate() error {
if _, err := uuid.Parse(p.TransferID); err != nil {
return fmt.Errorf("%w: transferId must be a valid UUID", err)
}
amount, err := strconv.ParseFloat(p.Amount, 64)
if err != nil {
return fmt.Errorf("%w: parse amount", err)
}
if amount <= 0 {
return fmt.Errorf("amount must be positive")
}
if p.Coin == "" || p.FromAccountType == "" || p.ToAccountType == "" {
return fmt.Errorf("coin, fromAccountType and toAccountType needed")
}
if p.FromAccountType == p.ToAccountType {
return fmt.Errorf("toAccountType and fromAccountType must differ")
}
return nil
}

// V5CreateInternalTransferResponse :
type V5CreateInternalTransferResponse struct {
CommonV5Response `json:",inline"`
Result V5CreateInternalTransferResult `json:"result"`
}

// V5CreateInternalTransferResult :
type V5CreateInternalTransferResult struct {
TransferID string `json:"transferId"`
}

// CreateInternalTransfer :
func (s *V5AssetService) CreateInternalTransfer(param V5CreateInternalTransferParam) (*V5CreateInternalTransferResponse, error) {
var res V5CreateInternalTransferResponse

if err := param.validate(); err != nil {
return nil, fmt.Errorf("validate param: %w", err)
}

body, err := json.Marshal(param)
if err != nil {
return &res, fmt.Errorf("json marshal: %w", err)
}

if err := s.client.postV5JSON("/v5/asset/transfer/inter-transfer", body, &res); err != nil {
return &res, err
}

return &res, nil
}

// V5GetInternalTransferRecordsParam :
type V5GetInternalTransferRecordsParam struct {
TransferID *string `url:"transferId,omitempty"`
Expand Down
71 changes: 71 additions & 0 deletions v5_asset_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bybit

import (
"encoding/json"
"fmt"
"net/http"
"testing"

Expand All @@ -10,6 +11,76 @@ import (
"github.com/stretchr/testify/require"
)

func TestV5Asset_CreateInternalTransfer(t *testing.T) {
t.Run("success", func(t *testing.T) {
param := V5CreateInternalTransferParam{
TransferID: "42c0cfb0-6bca-c242-bc76-4e6df6cbcb16",
Coin: CoinBTC,
Amount: "0.05",
FromAccountType: AccountTypeV5UNIFIED,
ToAccountType: AccountTypeV5CONTRACT,
}

path := "/v5/asset/transfer/inter-transfer"
method := http.MethodPost
status := http.StatusOK
respBody := map[string]interface{}{
"result": map[string]interface{}{
"transferId": "42c0cfb0-6bca-c242-bc76-4e6df6cbcb16",
},
}
bytesBody, err := json.Marshal(respBody)
require.NoError(t, err)

server, teardown := testhelper.NewServer(
testhelper.WithHandlerOption(path, method, status, bytesBody),
)
defer teardown()

client := NewTestClient().
WithBaseURL(server.URL).
WithAuth("test", "test")

resp, err := client.V5().Asset().CreateInternalTransfer(param)
require.NoError(t, err)

require.NotNil(t, resp)
fmt.Println(resp.Result, respBody["result"])
testhelper.Compare(t, respBody["result"], resp.Result)
})
t.Run("authentication required", func(t *testing.T) {
param := V5CreateInternalTransferParam{
TransferID: "42c0cfb0-6bca-c242-bc76-4e6df6cbcb16",
Coin: CoinBTC,
Amount: "0.05",
FromAccountType: AccountTypeV5UNIFIED,
ToAccountType: AccountTypeV5CONTRACT,
}

path := "/v5/asset/transfer/inter-transfer"
method := http.MethodPost
status := http.StatusOK
respBody := map[string]interface{}{
"result": map[string]interface{}{
"transferId": "42c0cfb0-6bca-c242-bc76-4e6df6cbcb16",
},
}
bytesBody, err := json.Marshal(respBody)
require.NoError(t, err)

server, teardown := testhelper.NewServer(
testhelper.WithHandlerOption(path, method, status, bytesBody),
)
defer teardown()

client := NewTestClient().
WithBaseURL(server.URL)

_, err = client.V5().Asset().CreateInternalTransfer(param)
assert.Error(t, err)
})
}

func TestV5Asset_GetInternalTransferRecords(t *testing.T) {
t.Run("success", func(t *testing.T) {
param := V5GetInternalTransferRecordsParam{}
Expand Down

0 comments on commit c07a9e5

Please sign in to comment.