diff --git a/clients/horizonclient/effect_request_test.go b/clients/horizonclient/effect_request_test.go index 6afd89a9ce..a0819274a4 100644 --- a/clients/horizonclient/effect_request_test.go +++ b/clients/horizonclient/effect_request_test.go @@ -143,7 +143,7 @@ func TestNextEffectsPage(t *testing.T) { efp, err := client.Effects(effectRequest) if assert.NoError(t, err) { - assert.Equal(t, len(efp.Embedded.Records), 2) + assert.Len(t, efp.Embedded.Records, 2) } hmock.On( @@ -153,7 +153,88 @@ func TestNextEffectsPage(t *testing.T) { nextPage, err := client.NextEffectsPage(efp) if assert.NoError(t, err) { - assert.Equal(t, len(nextPage.Embedded.Records), 0) + assert.Len(t, nextPage.Embedded.Records, 0) + } +} + +func TestSequenceBumpedNewSeq(t *testing.T) { + hmock := httptest.NewClient() + client := &Client{ + HorizonURL: "https://localhost/", + HTTP: hmock, + } + effectRequest := EffectRequest{ForAccount: "GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD"} + testCases := []struct { + desc string + payload string + }{ + { + desc: "new_seq as a string", + payload: sequenceBumpedAsStringPage, + }, + { + desc: "new_seq as a number", + payload: sequenceBumpedAsNumberPage, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + hmock.On( + "GET", + "https://localhost/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects", + ).ReturnString(200, tc.payload) + + efp, err := client.Effects(effectRequest) + + if assert.NoError(t, err) { + assert.Len(t, efp.Embedded.Records, 1) + } + + effect, ok := efp.Embedded.Records[0].(effects.SequenceBumped) + assert.True(t, ok) + assert.Equal(t, int64(300000000000), effect.NewSeq) + + }) + } +} + +func TestTradeEffectOfferID(t *testing.T) { + hmock := httptest.NewClient() + client := &Client{ + HorizonURL: "https://localhost/", + HTTP: hmock, + } + effectRequest := EffectRequest{ForAccount: "GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD"} + testCases := []struct { + desc string + payload string + }{ + { + desc: "offer_id as a number", + payload: tradeEffectNumberOfferID, + }, + { + desc: "offer_id as a string", + payload: tradeEffectStringOfferID, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + hmock.On( + "GET", + "https://localhost/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects", + ).ReturnString(200, tc.payload) + + efp, err := client.Effects(effectRequest) + + if assert.NoError(t, err) { + assert.Len(t, efp.Embedded.Records, 1) + } + + effect, ok := efp.Embedded.Records[0].(effects.Trade) + assert.True(t, ok) + assert.Equal(t, int64(127538672), effect.OfferID) + }) } } @@ -215,11 +296,159 @@ var firstEffectsPage = `{ "weight": 1, "public_key": "GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD", "key": "" - } + } ] } }` +var sequenceBumpedAsNumberPage = `{ + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects?cursor=&limit=10&order=asc" + }, + "next": { + "href": "https://horizon-testnet.stellar.org/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects?cursor=1557363731492865-3&limit=10&order=asc" + }, + "prev": { + "href": "https://horizon-testnet.stellar.org/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects?cursor=1557363731492865-1&limit=10&order=desc" + } + }, + "_embedded": { + "records": [ + { + "_links": { + "operation": { + "href": "https://horizon-testnet.stellar.org/operations/249108107265" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc\u0026cursor=249108107265-1" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc\u0026cursor=249108107265-1" + } + }, + "id": "0000000249108107265-0000000001", + "paging_token": "249108107265-1", + "account": "GCQZP3IU7XU6EJ63JZXKCQOYT2RNXN3HB5CNHENNUEUHSMA4VUJJJSEN", + "type": "sequence_bumped", + "type_i": 43, + "created_at": "2019-06-03T16:36:24Z", + "new_seq": 300000000000 + } + ] + } + }` + +var sequenceBumpedAsStringPage = `{ + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects?cursor=&limit=10&order=asc" + }, + "next": { + "href": "https://horizon-testnet.stellar.org/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects?cursor=1557363731492865-3&limit=10&order=asc" + }, + "prev": { + "href": "https://horizon-testnet.stellar.org/accounts/GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD/effects?cursor=1557363731492865-1&limit=10&order=desc" + } + }, + "_embedded": { + "records": [ + { + "_links": { + "operation": { + "href": "https://horizon-testnet.stellar.org/operations/249108107265" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc\u0026cursor=249108107265-1" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc\u0026cursor=249108107265-1" + } + }, + "id": "0000000249108107265-0000000001", + "paging_token": "249108107265-1", + "account": "GCQZP3IU7XU6EJ63JZXKCQOYT2RNXN3HB5CNHENNUEUHSMA4VUJJJSEN", + "type": "sequence_bumped", + "type_i": 43, + "created_at": "2019-06-03T16:36:24Z", + "new_seq": "300000000000" + } + ] + } + }` + +var tradeEffectNumberOfferID = ` +{ + "_embedded": { + "records": [ + { + "_links": { + "operation": { + "href": "https://horizon-testnet.stellar.org/operations/224209713045979100" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc&cursor=224209713045979100-3" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc&cursor=224209713045979100-3" + } + }, + "id": "2214209713045979100-0000000003", + "paging_token": "224209713045979100-3", + "account": "GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD", + "type": "trade", + "type_i": 33, + "created_at": "2019-11-01T23:05:58Z", + "seller": "GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX", + "offer_id": 127538672, + "sold_amount": "14.5984123", + "sold_asset_type": "native", + "bought_amount": "1.0000000", + "bought_asset_type": "credit_alphanum4", + "bought_asset_code": "USD", + "bought_asset_issuer": "GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX" + } + ] + } +} +` + +var tradeEffectStringOfferID = ` +{ + "_embedded": { + "records": [ + { + "_links": { + "operation": { + "href": "https://horizon-testnet.stellar.org/operations/224209713045979100" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc&cursor=224209713045979100-3" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc&cursor=224209713045979100-3" + } + }, + "id": "2214209713045979100-0000000003", + "paging_token": "224209713045979100-3", + "account": "GCDIZFWLOTBWHTPODXCBH6XNXPFMSQFRVIDRP3JLEKQZN66G7NF3ANOD", + "type": "trade", + "type_i": 33, + "created_at": "2019-11-01T23:05:58Z", + "seller": "GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX", + "offer_id": "127538672", + "sold_amount": "14.5984123", + "sold_asset_type": "native", + "bought_amount": "1.0000000", + "bought_asset_type": "credit_alphanum4", + "bought_asset_code": "USD", + "bought_asset_issuer": "GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX" + } + ] + } +} +` + var emptyEffectsPage = `{ "_links": { "self": { diff --git a/clients/horizonclient/offer_request_test.go b/clients/horizonclient/offer_request_test.go index 4a150fb953..cc01f05aef 100644 --- a/clients/horizonclient/offer_request_test.go +++ b/clients/horizonclient/offer_request_test.go @@ -47,6 +47,8 @@ func TestNextOffersPage(t *testing.T) { assert.Equal(t, len(offers.Embedded.Records), 2) } + assert.Equal(t, int64(2946580), offers.Embedded.Records[0].ID) + hmock.On( "GET", "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946581&limit=2&order=asc", @@ -105,8 +107,47 @@ func TestOfferRequestStreamOffers(t *testing.T) { } } +func TestStringOfferID(t *testing.T) { + hmock := httptest.NewClient() + client := &Client{ + HorizonURL: "https://localhost/", + HTTP: hmock, + } + + offerRequest := OfferRequest{ForAccount: "GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG", Limit: 1} + + hmock.On( + "GET", + "https://localhost/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?limit=1", + ).ReturnString(200, stringOffersPage) + + offers, err := client.Offers(offerRequest) + + if assert.NoError(t, err) { + assert.Equal(t, len(offers.Embedded.Records), 1) + } + + assert.Equal(t, int64(2946580), offers.Embedded.Records[0].ID) +} + var offerStreamResponse = `data: {"_links":{"self":{"href":"https://horizon-testnet.stellar.org/offers/5269100"},"offer_maker":{"href":"https://horizon-testnet.stellar.org/accounts/GAQHWQYBBW272OOXNQMMLCA5WY2XAZPODGB7Q3S5OKKIXVESKO55ZQ7C"}},"id":5269100,"paging_token":"5269100","seller":"GAQHWQYBBW272OOXNQMMLCA5WY2XAZPODGB7Q3S5OKKIXVESKO55ZQ7C","selling":{"asset_type":"credit_alphanum4","asset_code":"DSQ","asset_issuer":"GBDQPTQJDATT7Z7EO4COS4IMYXH44RDLLI6N6WIL5BZABGMUOVMLWMQF"},"buying":{"asset_type":"credit_alphanum4","asset_code":"XCS6","asset_issuer":"GBH2V47NOZRC56QAYCPV5JUBG5NVFJQF5AQTUNFNWNDHSWWTKH2MWR2L"},"amount":"20.4266087","price_r":{"n":24819,"d":10000000},"price":"0.0024819","last_modified_ledger":674449,"last_modified_time":"2019-04-08T11:56:41Z"} ` +var emptyOffersPage = `{ + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946581&limit=2&order=asc" + }, + "next": { + "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946583&limit=2&order=asc" + }, + "prev": { + "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946582&limit=2&order=desc" + } + }, + "_embedded": { + "records": [] + } +}` var firstOffersPage = `{ "_links": { @@ -188,19 +229,40 @@ var firstOffersPage = `{ } }` -var emptyOffersPage = `{ - "_links": { - "self": { - "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946581&limit=2&order=asc" - }, - "next": { - "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946583&limit=2&order=asc" - }, - "prev": { - "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG/offers?cursor=2946582&limit=2&order=desc" - } - }, +var stringOffersPage = `{ "_embedded": { - "records": [] + "records": [ + { + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/offers/2946580" + }, + "offer_maker": { + "href": "https://horizon-testnet.stellar.org/accounts/GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG" + } + }, + "id": "2946580", + "paging_token": "2946580", + "seller": "GBZ5OD56VRTRQKMNADD6VUZUG3FCILMAMYQY5ZSC3AW3GBXNEPIK76IG", + "selling": { + "asset_type": "credit_alphanum4", + "asset_code": "HT", + "asset_issuer": "GCNSGHUCG5VMGLT5RIYYZSO7VQULQKAJ62QA33DBC5PPBSO57LFWVV6P" + }, + "buying": { + "asset_type": "credit_alphanum4", + "asset_code": "BTC", + "asset_issuer": "GCNSGHUCG5VMGLT5RIYYZSO7VQULQKAJ62QA33DBC5PPBSO57LFWVV6P" + }, + "amount": "33.7252478", + "price_r": { + "n": 15477, + "d": 43975000 + }, + "price": "0.0003519", + "last_modified_ledger": 363492, + "last_modified_time": "2019-05-16T08:35:22Z" + } + ] } }` diff --git a/clients/horizonclient/operation_request_test.go b/clients/horizonclient/operation_request_test.go index 925bbfc705..cec5c284b9 100644 --- a/clients/horizonclient/operation_request_test.go +++ b/clients/horizonclient/operation_request_test.go @@ -175,6 +175,50 @@ func TestOperationRequestStreamOperations(t *testing.T) { } } +func TestManageSellManageBuyOfferOfferID(t *testing.T) { + hmock := httptest.NewClient() + client := &Client{ + HorizonURL: "https://localhost/", + HTTP: hmock, + } + + testCases := []struct { + desc string + payload string + }{ + { + desc: "offer_id as a number", + payload: numberManageSellBuyOfferOperations, + }, + { + desc: "offer_id as a string", + payload: stringManageSellBuyOfferOperations, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + hmock.On( + "GET", + "https://localhost/operations", + ).ReturnString(200, tc.payload) + operationRequest := OperationRequest{} + ops, err := client.Operations(operationRequest) + + if assert.NoError(t, err) { + assert.Equal(t, len(ops.Embedded.Records), 2) + } + + mso, ok := ops.Embedded.Records[0].(operations.ManageSellOffer) + assert.True(t, ok) + assert.Equal(t, int64(127538671), mso.OfferID) + + mbo, ok := ops.Embedded.Records[1].(operations.ManageBuyOffer) + assert.True(t, ok) + assert.Equal(t, int64(127538672), mbo.OfferID) + }) + } +} + var operationStreamResponse = `data: {"_links":{"self":{"href":"https://horizon-testnet.stellar.org/operations/4934917427201"},"transaction":{"href":"https://horizon-testnet.stellar.org/transactions/1c1449106a54cccd8a2ec2094815ad9db30ae54c69c3309dd08d13fdb8c749de"},"effects":{"href":"https://horizon-testnet.stellar.org/operations/4934917427201/effects"},"succeeds":{"href":"https://horizon-testnet.stellar.org/effects?order=desc\u0026cursor=4934917427201"},"precedes":{"href":"https://horizon-testnet.stellar.org/effects?order=asc\u0026cursor=4934917427201"}},"id":"4934917427201","paging_token":"4934917427201","transaction_successful":true,"source_account":"GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR","type":"create_account","type_i":0,"created_at":"2019-02-27T11:32:39Z","transaction_hash":"1c1449106a54cccd8a2ec2094815ad9db30ae54c69c3309dd08d13fdb8c749de","starting_balance":"10000.0000000","funder":"GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR","account":"GDBLBBDIUULY3HGIKXNK6WVBISY7DCNCDA45EL7NTXWX5R4UZ26HGMGS"} ` @@ -192,7 +236,7 @@ var firstOperationsPage = `{ }, "_embedded": { "records": [ - { + { "_links": { "self": { "href": "https://horizon-testnet.stellar.org/operations/661424967681" @@ -272,3 +316,191 @@ var emptyOperationsPage = `{ "records": [] } }` + +var numberManageSellBuyOfferOperations = `{ + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/operations?cursor=661424967682&limit=2&order=asc" + }, + "next": { + "href": "https://horizon-testnet.stellar.org/operations?cursor=661424967684&limit=2&order=asc" + }, + "prev": { + "href": "https://horizon-testnet.stellar.org/operations?cursor=661424967683&limit=2&order=desc" + } + }, + "_embedded": { + "records": [ + { + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/operations/972702718365697" + }, + "transaction": { + "href": "https://horizon-testnet.stellar.org/transactions/cfe9eba317025dd0cff111967a3709358153e9ee97472e67c17e42837dd50a52" + }, + "effects": { + "href": "https://horizon-testnet.stellar.org/operations/972702718365697/effects" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc\u0026cursor=972702718365697" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc\u0026cursor=972702718365697" + } + }, + "id": "972702718365697", + "paging_token": "972702718365697", + "transaction_successful": true, + "source_account": "GBPPEHGF322UNA62WHRHBCUBCVOIT3SLUY7U7XQEEISZ5B2JLZ3AYTDC", + "type": "manage_offer", + "type_i": 3, + "created_at": "2019-11-13T16:46:36Z", + "transaction_hash": "cfe9eba317025dd0cff111967a3709358153e9ee97472e67c17e42837dd50a52", + "amount": "1000.0000000", + "price": "0.1312531", + "price_r": { + "n": 265, + "d": 2019 + }, + "buying_asset_type": "credit_alphanum4", + "buying_asset_code": "BAT", + "buying_asset_issuer": "GBBJMSXCTLXVOYRL7SJ5ABLJ3GGCUFQXCFIXYUOHZZUDAZJKLXCO32AU", + "selling_asset_type": "native", + "offer_id": 127538671 + }, + { + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/operations/158041911595009" + }, + "transaction": { + "href": "https://horizon-testnet.stellar.org/transactions/8a4db87e4749130ba32924943c2f219de497fe2d4f3e074187c5d2159ca2d134" + }, + "effects": { + "href": "https://horizon-testnet.stellar.org/operations/158041911595009/effects" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc&cursor=158041911595009" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc&cursor=158041911595009" + } + }, + "id": "158041911595009", + "paging_token": "158041911595009", + "transaction_successful": true, + "source_account": "GBBXM7GVMXZMQWDEKSWGEW6GT6XMPBLEVEPLYWIQF3SRS43AIJVU3QES", + "type": "manage_buy_offer", + "type_i": 12, + "created_at": "2019-11-01T17:06:47Z", + "transaction_hash": "8a4db87e4749130ba32924943c2f219de497fe2d4f3e074187c5d2159ca2d134", + "amount": "1.0000000", + "price": "0.5000000", + "price_r": { + "n": 1, + "d": 2 + }, + "buying_asset_type": "credit_alphanum12", + "buying_asset_code": "MosaiRMBA", + "buying_asset_issuer": "GBBWA24VLGPVMMFMF2OJHW3QHFVSILK2UJSNTORRC6QHK6EPTUADAJFA", + "selling_asset_type": "native", + "offer_id": 127538672 + } + ] + } + }` + +var stringManageSellBuyOfferOperations = `{ + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/operations?cursor=661424967682&limit=2&order=asc" + }, + "next": { + "href": "https://horizon-testnet.stellar.org/operations?cursor=661424967684&limit=2&order=asc" + }, + "prev": { + "href": "https://horizon-testnet.stellar.org/operations?cursor=661424967683&limit=2&order=desc" + } + }, + "_embedded": { + "records": [ + { + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/operations/972702718365697" + }, + "transaction": { + "href": "https://horizon-testnet.stellar.org/transactions/cfe9eba317025dd0cff111967a3709358153e9ee97472e67c17e42837dd50a52" + }, + "effects": { + "href": "https://horizon-testnet.stellar.org/operations/972702718365697/effects" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc\u0026cursor=972702718365697" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc\u0026cursor=972702718365697" + } + }, + "id": "972702718365697", + "paging_token": "972702718365697", + "transaction_successful": true, + "source_account": "GBPPEHGF322UNA62WHRHBCUBCVOIT3SLUY7U7XQEEISZ5B2JLZ3AYTDC", + "type": "manage_offer", + "type_i": 3, + "created_at": "2019-11-13T16:46:36Z", + "transaction_hash": "cfe9eba317025dd0cff111967a3709358153e9ee97472e67c17e42837dd50a52", + "amount": "1000.0000000", + "price": "0.1312531", + "price_r": { + "n": 265, + "d": 2019 + }, + "buying_asset_type": "credit_alphanum4", + "buying_asset_code": "BAT", + "buying_asset_issuer": "GBBJMSXCTLXVOYRL7SJ5ABLJ3GGCUFQXCFIXYUOHZZUDAZJKLXCO32AU", + "selling_asset_type": "native", + "offer_id": "127538671" + }, + { + "_links": { + "self": { + "href": "https://horizon-testnet.stellar.org/operations/158041911595009" + }, + "transaction": { + "href": "https://horizon-testnet.stellar.org/transactions/8a4db87e4749130ba32924943c2f219de497fe2d4f3e074187c5d2159ca2d134" + }, + "effects": { + "href": "https://horizon-testnet.stellar.org/operations/158041911595009/effects" + }, + "succeeds": { + "href": "https://horizon-testnet.stellar.org/effects?order=desc&cursor=158041911595009" + }, + "precedes": { + "href": "https://horizon-testnet.stellar.org/effects?order=asc&cursor=158041911595009" + } + }, + "id": "158041911595009", + "paging_token": "158041911595009", + "transaction_successful": true, + "source_account": "GBBXM7GVMXZMQWDEKSWGEW6GT6XMPBLEVEPLYWIQF3SRS43AIJVU3QES", + "type": "manage_buy_offer", + "type_i": 12, + "created_at": "2019-11-01T17:06:47Z", + "transaction_hash": "8a4db87e4749130ba32924943c2f219de497fe2d4f3e074187c5d2159ca2d134", + "amount": "1.0000000", + "price": "0.5000000", + "price_r": { + "n": 1, + "d": 2 + }, + "buying_asset_type": "credit_alphanum12", + "buying_asset_code": "MosaiRMBA", + "buying_asset_issuer": "GBBWA24VLGPVMMFMF2OJHW3QHFVSILK2UJSNTORRC6QHK6EPTUADAJFA", + "selling_asset_type": "native", + "offer_id": "127538672" + } + ] + } + }` diff --git a/clients/horizonclient/trade_aggregation_request_test.go b/clients/horizonclient/trade_aggregation_request_test.go index a2dcd9a21f..4d614412d3 100644 --- a/clients/horizonclient/trade_aggregation_request_test.go +++ b/clients/horizonclient/trade_aggregation_request_test.go @@ -119,9 +119,12 @@ func TestNextTradeAggregationsPage(t *testing.T) { tradeAggs, err := client.TradeAggregations(taRequest) if assert.NoError(t, err) { - assert.Equal(t, len(tradeAggs.Embedded.Records), 2) + assert.Len(t, tradeAggs.Embedded.Records, 2) } + assert.Equal(t, int64(1565026860000), tradeAggs.Embedded.Records[0].Timestamp) + assert.Equal(t, int64(3), tradeAggs.Embedded.Records[0].TradeCount) + hmock.On( "GET", "https://horizon-testnet.stellar.org/trade_aggregations?base_asset_code=USD&base_asset_issuer=GDLEUZYDSFMWA5ZLQIOCYS7DMLYDKFS2KWJ5M3RQ3P3WS4L75ZTWKELP&base_asset_type=credit_alphanum4&counter_asset_code=BTC&counter_asset_issuer=GDLEUZYDSFMWA5ZLQIOCYS7DMLYDKFS2KWJ5M3RQ3P3WS4L75ZTWKELP&counter_asset_type=credit_alphanum4&limit=2&resolution=60000&start_time=0", @@ -172,6 +175,38 @@ func TestPrevTradeAggregationsPage(t *testing.T) { } } +func TestTradeAggregationsPageStringPayload(t *testing.T) { + hmock := httptest.NewClient() + client := &Client{ + HorizonURL: "https://localhost/", + HTTP: hmock, + } + + taRequest := TradeAggregationRequest{ + StartTime: testTime, + EndTime: testTime, + Resolution: DayResolution, + BaseAssetType: AssetTypeNative, + CounterAssetType: AssetType4, + CounterAssetCode: "SLT", + CounterAssetIssuer: "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP", + Order: OrderDesc, + } + + hmock.On( + "GET", + "https://localhost/trade_aggregations?base_asset_type=native&counter_asset_code=SLT&counter_asset_issuer=GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP&counter_asset_type=credit_alphanum4&end_time=1517521726000&offset=0&order=desc&resolution=86400000&start_time=1517521726000", + ).ReturnString(200, stringTradeAggsPage) + tradeAggs, err := client.TradeAggregations(taRequest) + + if assert.NoError(t, err) { + assert.Len(t, tradeAggs.Embedded.Records, 1) + } + + assert.Equal(t, int64(1565026860000), tradeAggs.Embedded.Records[0].Timestamp) + assert.Equal(t, int64(3), tradeAggs.Embedded.Records[0].TradeCount) +} + var tradeAggsResponse = `{ "_links": { "self": { @@ -329,3 +364,37 @@ var emptyTradeAggsPage = `{ "records": [] } }` + +var stringTradeAggsPage = `{ + "_embedded": { + "records": [ + { + "timestamp": "1565026860000", + "trade_count": "3", + "base_volume": "23781.2128418", + "counter_volume": "2.0000000", + "avg": "0.0000841", + "high": "0.0000841", + "high_r": { + "N": 841, + "D": 10000000 + }, + "low": "0.0000841", + "low_r": { + "N": 841, + "D": 10000000 + }, + "open": "0.0000841", + "open_r": { + "N": 841, + "D": 10000000 + }, + "close": "0.0000841", + "close_r": { + "N": 841, + "D": 10000000 + } + } + ] + } +}` diff --git a/protocols/horizon/base/main.go b/protocols/horizon/base/main.go index e52deb76cc..d0ee50c479 100644 --- a/protocols/horizon/base/main.go +++ b/protocols/horizon/base/main.go @@ -1,5 +1,7 @@ package base +import "encoding/json" + type Price struct { N int32 `json:"n"` D int32 `json:"d"` @@ -21,3 +23,22 @@ type Asset struct { type Rehydratable interface { Rehydrate() error } + +// ExtractOfferID extracts offer_id from a JSON payload +// Action needed in release: horizon-v0.25.0: remove +func ExtractOfferID(data []byte) (int64, error) { + var temp struct { + OfferID json.Number `json:"offer_id"` + } + + if err := json.Unmarshal(data, &temp); err != nil { + return 0, err + } + + offerID, err := temp.OfferID.Int64() + if err != nil { + return 0, err + } + + return offerID, nil +} diff --git a/protocols/horizon/effects/main.go b/protocols/horizon/effects/main.go index 2f45418cc5..030f6344d4 100644 --- a/protocols/horizon/effects/main.go +++ b/protocols/horizon/effects/main.go @@ -198,9 +198,35 @@ type AccountFlagsUpdated struct { type SequenceBumped struct { Base + // Action needed in release: horizon-v0.25.0: make new_seq as string NewSeq int64 `json:"new_seq"` } +// UnmarshalJSON is the custom unmarshal method for SequenceBumped. It allows +// parsing of new_seq as a string or an int64. +// Action needed in release: horizon-v0.25.0: Delete +func (effect *SequenceBumped) UnmarshalJSON(data []byte) error { + var temp struct { + NewSeq json.Number `json:"new_seq"` + } + + if err := json.Unmarshal(data, &effect.Base); err != nil { + return err + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + newSeq, err := temp.NewSeq.Int64() + if err != nil { + return err + } + effect.NewSeq = newSeq + + return nil +} + type SignerCreated struct { Base Weight int32 `json:"weight"` @@ -254,11 +280,10 @@ type TrustlineDeauthorized struct { AssetCode string `json:"asset_code,omitempty"` } -type Trade struct { - Base - Seller string `json:"seller"` - // Action needed in release: horizon-v0.23.0 - OfferID int64 `json:"offer_id"` +// Action needed in release: horizon-v0.25.0: move back to Trade, remove +// embedded struct +type tradeBase struct { + Seller string `json:"seller"` SoldAmount string `json:"sold_amount"` SoldAssetType string `json:"sold_asset_type"` SoldAssetCode string `json:"sold_asset_code,omitempty"` @@ -269,6 +294,37 @@ type Trade struct { BoughtAssetIssuer string `json:"bought_asset_issuer,omitempty"` } +type Trade struct { + Base + tradeBase + // Action needed in release: horizon-v0.25.0: Make offer_id a string + OfferID int64 `json:"offer_id"` +} + +// UnmarshalJSON is the custom unmarshal method for Trade. It allows +// parsing of offer_id as a string or an int64. +// Action needed in release: horizon-v0.25.0: Delete +func (effect *Trade) UnmarshalJSON(data []byte) error { + var tradeWithoutOfferID struct { + Base + tradeBase + } + if err := json.Unmarshal(data, &tradeWithoutOfferID); err != nil { + return err + } + + offerID, err := base.ExtractOfferID(data) + if err != nil { + return err + } + + effect.Base = tradeWithoutOfferID.Base + effect.tradeBase = tradeWithoutOfferID.tradeBase + effect.OfferID = offerID + + return nil +} + // Effect contains methods that are implemented by all effect types. type Effect interface { PagingToken() string diff --git a/protocols/horizon/main.go b/protocols/horizon/main.go index 687fffb5e3..7de04e0627 100644 --- a/protocols/horizon/main.go +++ b/protocols/horizon/main.go @@ -217,15 +217,13 @@ func (l Ledger) PagingToken() string { return l.PT } -// Offer is the display form of an offer to trade currency. -type Offer struct { +// Action needed in release: horizon-v0.25.0: Move back to Offer, remove embedded struct +type offerBase struct { Links struct { Self hal.Link `json:"self"` OfferMaker hal.Link `json:"offer_maker"` } `json:"_links"` - // Action needed in release: horizon-v0.23.0 - ID int64 `json:"id"` PT string `json:"paging_token"` Seller string `json:"seller"` Selling Asset `json:"selling"` @@ -237,10 +235,43 @@ type Offer struct { LastModifiedTime *time.Time `json:"last_modified_time"` } +// Offer is the display form of an offer to trade currency. +type Offer struct { + offerBase + // Action needed in release: horizon-v0.25.0: Make id a string + ID int64 `json:"id"` +} + func (o Offer) PagingToken() string { return o.PT } +// UnmarshalJSON is the custom unmarshal method for Offer. It allows +// parsing of id as a string or an int64. +// Action needed in release: horizon-v0.25.0: Delete +func (o *Offer) UnmarshalJSON(data []byte) error { + var temp struct { + ID json.Number `json:"id"` + } + + if err := json.Unmarshal(data, &o.offerBase); err != nil { + return err + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + offerID, err := temp.ID.Int64() + if err != nil { + return err + } + + o.ID = offerID + + return nil +} + // OrderBookSummary represents a snapshot summary of a given order book type OrderBookSummary struct { Bids []PriceLevel `json:"bids"` @@ -374,12 +405,9 @@ type TradeEffect struct { LedgerCloseTime time.Time `json:"created_at"` } -// TradeAggregation represents trade data aggregation over a period of time -type TradeAggregation struct { - // Action needed in release: horizon-v0.22.0 - Timestamp int64 `json:"timestamp"` - // Action needed in release: horizon-v0.22.0 - TradeCount int64 `json:"trade_count"` +// Action needed in release: horizon-v0.25.0: Move back to TradeAggregation, +// remove embedded struct +type tradeAggregationBase struct { BaseVolume string `json:"base_volume"` CounterVolume string `json:"counter_volume"` Average string `json:"avg"` @@ -393,11 +421,53 @@ type TradeAggregation struct { CloseR xdr.Price `json:"close_r"` } +// TradeAggregation represents trade data aggregation over a period of time +type TradeAggregation struct { + tradeAggregationBase + // Action needed in release: horizon-v0.25.0: Make timestamp a string + Timestamp int64 `json:"timestamp"` + // Action needed in release: horizon-v0.25.0: Make trade_count a string + TradeCount int64 `json:"trade_count"` +} + // PagingToken implementation for hal.Pageable. Not actually used func (res TradeAggregation) PagingToken() string { return string(res.Timestamp) } +// UnmarshalJSON is the custom unmarshal method for TradeAggregation. It allows +// parsing of timestamp and trade_count as a string or an int64. +// Action needed in release: horizon-v0.25.0: Delete +func (res *TradeAggregation) UnmarshalJSON(data []byte) error { + var temp struct { + Timestamp json.Number `json:"timestamp"` + TradeCount json.Number `json:"trade_count"` + } + + if err := json.Unmarshal(data, &res.tradeAggregationBase); err != nil { + return err + } + + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + timestamp, err := temp.Timestamp.Int64() + if err != nil { + return err + } + + tradeCount, err := temp.TradeCount.Int64() + if err != nil { + return err + } + + res.Timestamp = timestamp + res.TradeCount = tradeCount + + return nil +} + // Transaction represents a single, successful transaction type Transaction struct { Links struct { diff --git a/protocols/horizon/operations/main.go b/protocols/horizon/operations/main.go index 26aae2bd3e..2c5f9dfe2f 100644 --- a/protocols/horizon/operations/main.go +++ b/protocols/horizon/operations/main.go @@ -151,18 +151,57 @@ type CreatePassiveSellOffer struct { // is ManageSellOffer. type ManageSellOffer struct { Offer - // Action needed in release: horizon-v0.23.0 + // Action needed in release: horizon-v0.25.0: Make offer_id a string OfferID int64 `json:"offer_id"` } +// UnmarshalJSON is the custom unmarshal method for ManageSellOffer. It allows +// parsing of offer_id as a string or an int64. +// Action needed in release: horizon-v0.25.0: Delete +func (operation *ManageSellOffer) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &operation.Offer); err != nil { + return err + } + + offerID, err := base.ExtractOfferID(data) + + if err != nil { + return err + } + + operation.OfferID = offerID + + return nil +} + // ManageBuyOffer is the json resource representing a single operation whose type // is ManageBuyOffer. type ManageBuyOffer struct { Offer - // Action needed in release: horizon-v0.23.0 + + // Action needed in release: horizon-v0.25.0: Make offer_id a string OfferID int64 `json:"offer_id"` } +// UnmarshalJSON is the custom unmarshal method for ManageBuyOffer. It allows +// parsing of offer_id as a string or an int64. +// Action needed in release: horizon-v0.25.0: Delete +func (operation *ManageBuyOffer) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &operation.Offer); err != nil { + return err + } + + offerID, err := base.ExtractOfferID(data) + + if err != nil { + return err + } + + operation.OfferID = offerID + + return nil +} + // SetOptions is the json resource representing a single operation whose type is // SetOptions. type SetOptions struct {