diff --git a/protocols/horizon/main.go b/protocols/horizon/main.go index 10a0bccc15..94622d28f5 100644 --- a/protocols/horizon/main.go +++ b/protocols/horizon/main.go @@ -445,10 +445,13 @@ type Transaction struct { Account string `json:"source_account"` AccountSequence string `json:"source_account_sequence"` FeeAccount string `json:"fee_account"` - // Action needed in release: horizonclient-v3.0.0 - // Change FeeCharged and MaxFee types to string - // because JSON doesn't support 64 bit integers - FeeCharged int64 `json:"fee_charged"` + // Action needed in release: horizon-v1.3.0 + // set json tag to `json:"fee_charged,string"` so max_fee can be marshalled + // as a string in the JSON response + FeeCharged int64 `json:"fee_charged"` + // Action needed in release: horizon-v1.3.0 + // set json tag to `json:"max_fee,string"` so max_fee can be marshalled + // as a string in the JSON response MaxFee int64 `json:"max_fee"` OperationCount int32 `json:"operation_count"` EnvelopeXdr string `json:"envelope_xdr"` @@ -495,9 +498,36 @@ func (t Transaction) MarshalJSON() ([]byte, error) { return json.Marshal(v) } +// UnmarshalJSON implements a custom unmarshaler for Transaction +// which can handle a max_fee field which can be a string of int +func (t *Transaction) UnmarshalJSON(data []byte) error { + type Alias Transaction // we define Alias to avoid infinite recursion when calling UnmarshalJSON() + v := &struct { + FeeCharged json.Number `json:"fee_charged"` + MaxFee json.Number `json:"max_fee"` + *Alias + }{ + Alias: (*Alias)(t), + } + err := json.Unmarshal(data, &v) + if err != nil { + return err + } + + t.FeeCharged, err = v.FeeCharged.Int64() + if err != nil { + return err + } + t.MaxFee, err = v.MaxFee.Int64() + if err != nil { + return err + } + return nil +} + // PagingToken implementation for hal.Pageable -func (res Transaction) PagingToken() string { - return res.PT +func (t Transaction) PagingToken() string { + return t.PT } // TransactionResultCodes represent a summary of result codes returned from diff --git a/protocols/horizon/main_test.go b/protocols/horizon/main_test.go index 688d579af5..e0d761dd98 100644 --- a/protocols/horizon/main_test.go +++ b/protocols/horizon/main_test.go @@ -91,3 +91,120 @@ func TestTransactionMemoTypeNone(t *testing.T) { json.Unmarshal(marshaledTransaction, &result) assert.Nil(t, result.Memo, "no memo field is present when memo input type was `none`") } + +func TestTransactionUnmarshalJSON(t *testing.T) { + const feesAsInt64s = `{ + "memo": "MzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTA=", + "_links": { + "self": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033" + }, + "account": { + "href": "https://horizon.stellar.org/accounts/GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3" + }, + "ledger": { + "href": "https://horizon.stellar.org/ledgers/29113108" + }, + "operations": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/operations{?cursor,limit,order}", + "templated": true + }, + "effects": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/effects{?cursor,limit,order}", + "templated": true + }, + "precedes": { + "href": "https://horizon.stellar.org/transactions?order=asc\u0026cursor=125039846745419776" + }, + "succeeds": { + "href": "https://horizon.stellar.org/transactions?order=desc\u0026cursor=125039846745419776" + }, + "transaction": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033" + } + }, + "id": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033", + "paging_token": "125039846745419776", + "successful": true, + "hash": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033", + "ledger": 29113108, + "created_at": "2020-04-10T17:03:18Z", + "source_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3", + "source_account_sequence": "113942901088600162", + "fee_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3", + "fee_charged": 3000000000, + "max_fee": 2500000000, + "operation_count": 1, + "envelope_xdr": "AAAAAGmBpPsDnlK0e194Og7IO5mFUc0deRAdxxha3Q+t4F77AAAAZAGUzncAEEBiAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADMzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTAAAAABAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAEAAAAAP29uBulc9ouSoH62BRypPhD6zcLWoS5sj7CHf5SJ15MAAAABTk9ETAAAAAB1jYLXrFzNBOWCoPnZSHI3PJAhHtc1TrCaiPuZwSf5pgAAAAAAAAABAAAAAAAAAALw9Tl2AAAAQOknEHs7ZaPNVlXMU0uOtT+0TVo9kW/jDuNxN40FdJDic0p23V4lxOfPGCgQwBgTehqCIEzCMQ4LkbfzkdgkFAut4F77AAAAQKtFmT73srS8RHeQgWWia8mb+TrLCr1CJbK+MAKGdUnb4s4JBOKUjHhqQLrs7GCkJ3wOpgTbtW8VpwNedCJhFQ0=", + "result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=", + "result_meta_xdr": "AAAAAQAAAAIAAAADAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBhAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBiAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJIASptjhEiAAAAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJMASptjhEiAAAAAAAEAAAAAAAAAAAAAAAMBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU7B//////////wAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU69//////////wAAAAEAAAAAAAAAAA==", + "fee_meta_xdr": "AAAAAgAAAAMBvDsTAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEGcAZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBvDsUAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEE4AZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==", + "memo_type": "hash", + "signatures": [ + "6ScQeztlo81WVcxTS461P7RNWj2Rb+MO43E3jQV0kOJzSnbdXiXE588YKBDAGBN6GoIgTMIxDguRt/OR2CQUCw==", + "q0WZPveytLxEd5CBZaJryZv5OssKvUIlsr4wAoZ1SdvizgkE4pSMeGpAuuzsYKQnfA6mBNu1bxWnA150ImEVDQ==" + ], + "valid_after": "1970-01-01T00:00:00Z" + }` + + const feesAsStrings = `{ + "memo": "MzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTA=", + "_links": { + "self": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033" + }, + "account": { + "href": "https://horizon.stellar.org/accounts/GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3" + }, + "ledger": { + "href": "https://horizon.stellar.org/ledgers/29113108" + }, + "operations": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/operations{?cursor,limit,order}", + "templated": true + }, + "effects": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/effects{?cursor,limit,order}", + "templated": true + }, + "precedes": { + "href": "https://horizon.stellar.org/transactions?order=asc\u0026cursor=125039846745419776" + }, + "succeeds": { + "href": "https://horizon.stellar.org/transactions?order=desc\u0026cursor=125039846745419776" + }, + "transaction": { + "href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033" + } + }, + "id": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033", + "paging_token": "125039846745419776", + "successful": true, + "hash": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033", + "ledger": 29113108, + "created_at": "2020-04-10T17:03:18Z", + "source_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3", + "source_account_sequence": "113942901088600162", + "fee_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3", + "fee_charged": "3000000000", + "max_fee": "2500000000", + "operation_count": 1, + "envelope_xdr": "AAAAAGmBpPsDnlK0e194Og7IO5mFUc0deRAdxxha3Q+t4F77AAAAZAGUzncAEEBiAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADMzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTAAAAABAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAEAAAAAP29uBulc9ouSoH62BRypPhD6zcLWoS5sj7CHf5SJ15MAAAABTk9ETAAAAAB1jYLXrFzNBOWCoPnZSHI3PJAhHtc1TrCaiPuZwSf5pgAAAAAAAAABAAAAAAAAAALw9Tl2AAAAQOknEHs7ZaPNVlXMU0uOtT+0TVo9kW/jDuNxN40FdJDic0p23V4lxOfPGCgQwBgTehqCIEzCMQ4LkbfzkdgkFAut4F77AAAAQKtFmT73srS8RHeQgWWia8mb+TrLCr1CJbK+MAKGdUnb4s4JBOKUjHhqQLrs7GCkJ3wOpgTbtW8VpwNedCJhFQ0=", + "result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=", + "result_meta_xdr": "AAAAAQAAAAIAAAADAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBhAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBiAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJIASptjhEiAAAAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJMASptjhEiAAAAAAAEAAAAAAAAAAAAAAAMBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU7B//////////wAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU69//////////wAAAAEAAAAAAAAAAA==", + "fee_meta_xdr": "AAAAAgAAAAMBvDsTAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEGcAZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBvDsUAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEE4AZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==", + "memo_type": "hash", + "signatures": [ + "6ScQeztlo81WVcxTS461P7RNWj2Rb+MO43E3jQV0kOJzSnbdXiXE588YKBDAGBN6GoIgTMIxDguRt/OR2CQUCw==", + "q0WZPveytLxEd5CBZaJryZv5OssKvUIlsr4wAoZ1SdvizgkE4pSMeGpAuuzsYKQnfA6mBNu1bxWnA150ImEVDQ==" + ], + "valid_after": "1970-01-01T00:00:00Z" + }` + + var parsedFeesAsInts, parsedFeesAsStrings Transaction + assert.NoError(t, json.Unmarshal([]byte(feesAsInt64s), &parsedFeesAsInts)) + assert.NoError(t, json.Unmarshal([]byte(feesAsStrings), &parsedFeesAsStrings)) + assert.Equal(t, parsedFeesAsInts, parsedFeesAsStrings) + assert.Equal(t, int64(2500000000), parsedFeesAsInts.MaxFee) + assert.Equal(t, int64(3000000000), parsedFeesAsInts.FeeCharged) +}