Repository: coinbase/rosetta-sdk-go
Pull request #231 (2020-11-06 17:15:46)

[chore] v1.4.7 Upgrade
This PR updates `rosetta-sdk-go` to use [`rosetta-specifications@v1.4.7`](https://github.com/coinbase/rosetta-specifications/releases/tag/v1.4.7). Notably, this adds support for ["indexer" endpoints](https://github.com/coinbase/rosetta-specifications#indexers)!

### Changelog
- [x] update `codegen.sh`
- [x] add `types.X` methods for `string`, `int64`, and `bool` to make it easier to handle pointers
- [x] pass existing tests

### Future PR
* add new endpoints to `fetcher`
* add assertions for new endpoints (both client + server)


Patch:
diff --git a/asserter/account.go b/asserter/account.go
index 3c2d70e..3ec4911 100644
--- a/asserter/account.go
+++ b/asserter/account.go
@@ -72,12 +72,6 @@ func AccountBalanceResponse(
 		return fmt.Errorf("%w: balance amounts are invalid", err)
 	}
 
-	if response.Coins != nil {
-		if err := Coins(response.Coins); err != nil {
-			return fmt.Errorf("%w: coins are invalid", err)
-		}
-	}
-
 	if requestBlock == nil {
 		return nil
 	}
diff --git a/asserter/asserter.go b/asserter/asserter.go
index a7e3238..7ddd22f 100644
--- a/asserter/asserter.go
+++ b/asserter/asserter.go
@@ -247,9 +247,13 @@ func (a *Asserter) OperationSuccessful(operation *types.Operation) (bool, error)
 		return false, ErrAsserterNotInitialized
 	}
 
-	val, ok := a.operationStatusMap[operation.Status]
+	if operation.Status == nil || len(*operation.Status) == 0 {
+		return false, ErrOperationStatusMissing
+	}
+
+	val, ok := a.operationStatusMap[*operation.Status]
 	if !ok {
-		return false, fmt.Errorf("%s not found", operation.Status)
+		return false, fmt.Errorf("%s not found", *operation.Status)
 	}
 
 	return val, nil
diff --git a/asserter/block.go b/asserter/block.go
index 562ac07..be15d58 100644
--- a/asserter/block.go
+++ b/asserter/block.go
@@ -138,17 +138,29 @@ func containsInt64(valid []int64, value int64) bool {
 
 // OperationStatus returns an error if an operation.Status
 // is not valid.
-func (a *Asserter) OperationStatus(status string) error {
+func (a *Asserter) OperationStatus(status *string, construction bool) error {
 	if a == nil {
 		return ErrAsserterNotInitialized
 	}
 
-	if status == "" {
+	if status == nil {
+		if construction {
+			return nil
+		}
+
 		return ErrOperationStatusMissing
 	}
 
-	if _, ok := a.operationStatusMap[status]; !ok {
-		return fmt.Errorf("%w: %s", ErrOperationStatusInvalid, status)
+	if construction {
+		return ErrOperationStatusNotEmptyForConstruction
+	}
+
+	if len(*status) == 0 {
+		return ErrOperationStatusMissing
+	}
+
+	if _, ok := a.operationStatusMap[*status]; !ok {
+		return fmt.Errorf("%w: %s", ErrOperationStatusInvalid, *status)
 	}
 
 	return nil
@@ -191,11 +203,7 @@ func (a *Asserter) Operation(
 		return fmt.Errorf("%w: operation type is invalid in operation %d", err, index)
 	}
 
-	if construction && len(operation.Status) != 0 {
-		return ErrOperationStatusNotEmptyForConstruction
-	}
-
-	if err := a.OperationStatus(operation.Status); err != nil && !construction {
+	if err := a.OperationStatus(operation.Status, construction); err != nil {
 		return fmt.Errorf("%w: operation status is invalid in operation %d", err, index)
 	}
 
diff --git a/asserter/server.go b/asserter/server.go
index 7911a0f..f565d0c 100644
--- a/asserter/server.go
+++ b/asserter/server.go
@@ -435,3 +435,36 @@ func (a *Asserter) CallRequest(request *types.CallRequest) error {
 
 	return nil
 }
+
+// AccountCoinsRequest ensures that a types.AccountCoinsRequest
+// is well-formatted.
+func (a *Asserter) AccountCoinsRequest(request *types.AccountCoinsRequest) error {
+	if a == nil {
+		return ErrAsserterNotInitialized
+	}
+
+	// TODO: implement assertion
+	return nil
+}
+
+// EventsBlocksRequest ensures that a types.EventsBlocksRequest
+// is well-formatted.
+func (a *Asserter) EventsBlocksRequest(request *types.EventsBlocksRequest) error {
+	if a == nil {
+		return ErrAsserterNotInitialized
+	}
+
+	// TODO: implement assertion
+	return nil
+}
+
+// SearchTransactionsRequest ensures that a types.SearchTransactionsRequest
+// is well-formatted.
+func (a *Asserter) SearchTransactionsRequest(request *types.SearchTransactionsRequest) error {
+	if a == nil {
+		return ErrAsserterNotInitialized
+	}
+
+	// TODO: implement assertion
+	return nil
+}
diff --git a/client/api_account.go b/client/api_account.go
index 5d25bdf..15ac48f 100644
--- a/client/api_account.go
+++ b/client/api_account.go
@@ -126,3 +126,97 @@ func (a *AccountAPIService) AccountBalance(
 		)
 	}
 }
+
+// AccountCoins Get an array of all unspent coins for an AccountIdentifier and the BlockIdentifier
+// at which the lookup was performed. If your implementation does not support coins (i.e. it is for
+// an account-based blockchain), you do not need to implement this endpoint. If you implementation
+// does support coins (i.e. it is fro a UTXO-based blockchain), you MUST also complete the
+// /account/balance endpoint. It is important to note that making a coins request for an account
+// without populating the SubAccountIdentifier should not result in the coins of all possible
+// SubAccountIdentifiers being returned. Rather, it should result in the coins pertaining to no
+// SubAccountIdentifiers being returned. To get all coins associated with an account, it may be
+// necessary to perform multiple coin requests with unique AccountIdentifiers. Optionally, an
+// implementation may choose to support updating an AccountIdentifier&#39;s unspent coins based on
+// the contents of the mempool. Note, using this functionality breaks any guarantee of idempotency.
+func (a *AccountAPIService) AccountCoins(
+	ctx _context.Context,
+	accountCoinsRequest *types.AccountCoinsRequest,
+) (*types.AccountCoinsResponse, *types.Error, error) {
+	var (
+		localVarPostBody interface{}
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/account/coins"
+	localVarHeaderParams := make(map[string]string)
+
+	// to determine the Content-Type header
+	localVarHTTPContentTypes := []string{"application/json"}
+
+	// set Content-Type header
+	localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
+	if localVarHTTPContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHTTPContentType
+	}
+
+	// to determine the Accept header
+	localVarHTTPHeaderAccepts := []string{"application/json"}
+
+	// set Accept header
+	localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
+	if localVarHTTPHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
+	}
+	// body params
+	localVarPostBody = accountCoinsRequest
+
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarPostBody, localVarHeaderParams)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	localVarHTTPResponse, err := a.client.callAPI(ctx, r)
+	if err != nil || localVarHTTPResponse == nil {
+		return nil, nil, err
+	}
+
+	localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body)
+	defer localVarHTTPResponse.Body.Close()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	switch localVarHTTPResponse.StatusCode {
+	case _nethttp.StatusOK:
+		var v types.AccountCoinsResponse
+		err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+
+		return &v, nil, nil
+	case _nethttp.StatusInternalServerError:
+		var v types.Error
+		err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+
+		return nil, &v, fmt.Errorf("%+v", v)
+	case _nethttp.StatusBadGateway,
+		_nethttp.StatusServiceUnavailable,
+		_nethttp.StatusGatewayTimeout:
+		return nil, nil, fmt.Errorf(
+			"%w: code: %d body: %s",
+			ErrRetriable,
+			localVarHTTPResponse.StatusCode,
+			string(localVarBody),
+		)
+	default:
+		return nil, nil, fmt.Errorf(
+			"invalid status code: %d body: %s",
+			localVarHTTPResponse.StatusCode,
+			string(localVarBody),
+		)
+	}
+}
diff --git a/client/api_events.go b/client/api_events.go
new file mode 100644
index 0000000..82da58a
--- /dev/null
+++ b/client/api_events.go
@@ -0,0 +1,123 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package client
+
+import (
+	_context "context"
+	"fmt"
+	_ioutil "io/ioutil"
+	_nethttp "net/http"
+
+	"github.com/coinbase/rosetta-sdk-go/types"
+)
+
+// Linger please
+var (
+	_ _context.Context
+)
+
+// EventsAPIService EventsAPI service
+type EventsAPIService service
+
+// EventsBlocks /events/blocks allows the caller to query a sequence of BlockEvents indicating which
+// blocks were added and removed from storage to reach the current state. Following BlockEvents
+// allows lightweight clients to update their state without needing to implement their own syncing
+// logic (like finding the common parent in a reorg). /events/blocks is considered an \indexer\
+// endpoint and Rosetta implementations are not required to complete it to adhere to the Rosetta
+// spec. However, any Rosetta \indexer\ MUST support this endpoint.
+func (a *EventsAPIService) EventsBlocks(
+	ctx _context.Context,
+	eventsBlocksRequest *types.EventsBlocksRequest,
+) (*types.EventsBlocksResponse, *types.Error, error) {
+	var (
+		localVarPostBody interface{}
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/events/blocks"
+	localVarHeaderParams := make(map[string]string)
+
+	// to determine the Content-Type header
+	localVarHTTPContentTypes := []string{"application/json"}
+
+	// set Content-Type header
+	localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
+	if localVarHTTPContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHTTPContentType
+	}
+
+	// to determine the Accept header
+	localVarHTTPHeaderAccepts := []string{"application/json"}
+
+	// set Accept header
+	localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
+	if localVarHTTPHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
+	}
+	// body params
+	localVarPostBody = eventsBlocksRequest
+
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarPostBody, localVarHeaderParams)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	localVarHTTPResponse, err := a.client.callAPI(ctx, r)
+	if err != nil || localVarHTTPResponse == nil {
+		return nil, nil, err
+	}
+
+	localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body)
+	defer localVarHTTPResponse.Body.Close()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	switch localVarHTTPResponse.StatusCode {
+	case _nethttp.StatusOK:
+		var v types.EventsBlocksResponse
+		err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+
+		return &v, nil, nil
+	case _nethttp.StatusInternalServerError:
+		var v types.Error
+		err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+
+		return nil, &v, fmt.Errorf("%+v", v)
+	case _nethttp.StatusBadGateway,
+		_nethttp.StatusServiceUnavailable,
+		_nethttp.StatusGatewayTimeout:
+		return nil, nil, fmt.Errorf(
+			"%w: code: %d body: %s",
+			ErrRetriable,
+			localVarHTTPResponse.StatusCode,
+			string(localVarBody),
+		)
+	default:
+		return nil, nil, fmt.Errorf(
+			"invalid status code: %d body: %s",
+			localVarHTTPResponse.StatusCode,
+			string(localVarBody),
+		)
+	}
+}
diff --git a/client/api_search.go b/client/api_search.go
new file mode 100644
index 0000000..3228aee
--- /dev/null
+++ b/client/api_search.go
@@ -0,0 +1,123 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package client
+
+import (
+	_context "context"
+	"fmt"
+	_ioutil "io/ioutil"
+	_nethttp "net/http"
+
+	"github.com/coinbase/rosetta-sdk-go/types"
+)
+
+// Linger please
+var (
+	_ _context.Context
+)
+
+// SearchAPIService SearchAPI service
+type SearchAPIService service
+
+// SearchTransactions /search/transactions allows the caller to search for transactions that meet
+// certain conditions. Some conditions include matching a transaction hash, containing an operation
+// with a certain status, or containing an operation that affects a certain account.
+// /search/transactions is considered an \indexer\ endpoint and Rosetta implementations are not
+// required to complete it to adhere to the Rosetta spec. However, any Rosetta \indexer\ MUST
+// support this endpoint.
+func (a *SearchAPIService) SearchTransactions(
+	ctx _context.Context,
+	searchTransactionsRequest *types.SearchTransactionsRequest,
+) (*types.SearchTransactionsResponse, *types.Error, error) {
+	var (
+		localVarPostBody interface{}
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/search/transactions"
+	localVarHeaderParams := make(map[string]string)
+
+	// to determine the Content-Type header
+	localVarHTTPContentTypes := []string{"application/json"}
+
+	// set Content-Type header
+	localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
+	if localVarHTTPContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHTTPContentType
+	}
+
+	// to determine the Accept header
+	localVarHTTPHeaderAccepts := []string{"application/json"}
+
+	// set Accept header
+	localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
+	if localVarHTTPHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
+	}
+	// body params
+	localVarPostBody = searchTransactionsRequest
+
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarPostBody, localVarHeaderParams)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	localVarHTTPResponse, err := a.client.callAPI(ctx, r)
+	if err != nil || localVarHTTPResponse == nil {
+		return nil, nil, err
+	}
+
+	localVarBody, err := _ioutil.ReadAll(localVarHTTPResponse.Body)
+	defer localVarHTTPResponse.Body.Close()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	switch localVarHTTPResponse.StatusCode {
+	case _nethttp.StatusOK:
+		var v types.SearchTransactionsResponse
+		err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+
+		return &v, nil, nil
+	case _nethttp.StatusInternalServerError:
+		var v types.Error
+		err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+
+		return nil, &v, fmt.Errorf("%+v", v)
+	case _nethttp.StatusBadGateway,
+		_nethttp.StatusServiceUnavailable,
+		_nethttp.StatusGatewayTimeout:
+		return nil, nil, fmt.Errorf(
+			"%w: code: %d body: %s",
+			ErrRetriable,
+			localVarHTTPResponse.StatusCode,
+			string(localVarBody),
+		)
+	default:
+		return nil, nil, fmt.Errorf(
+			"invalid status code: %d body: %s",
+			localVarHTTPResponse.StatusCode,
+			string(localVarBody),
+		)
+	}
+}
diff --git a/client/client.go b/client/client.go
index 5e65631..9784ac4 100644
--- a/client/client.go
+++ b/client/client.go
@@ -41,7 +41,7 @@ var (
 	ErrRetriable = errors.New("retriable http status code received")
 )
 
-// APIClient manages communication with the Rosetta API v1.4.6
+// APIClient manages communication with the Rosetta API v1.4.7
 // In most cases there should be only one, shared, APIClient.
 type APIClient struct {
 	cfg    *Configuration
@@ -57,9 +57,13 @@ type APIClient struct {
 
 	ConstructionAPI *ConstructionAPIService
 
+	EventsAPI *EventsAPIService
+
 	MempoolAPI *MempoolAPIService
 
 	NetworkAPI *NetworkAPIService
+
+	SearchAPI *SearchAPIService
 }
 
 type service struct {
@@ -82,8 +86,10 @@ func NewAPIClient(cfg *Configuration) *APIClient {
 	c.BlockAPI = (*BlockAPIService)(&c.common)
 	c.CallAPI = (*CallAPIService)(&c.common)
 	c.ConstructionAPI = (*ConstructionAPIService)(&c.common)
+	c.EventsAPI = (*EventsAPIService)(&c.common)
 	c.MempoolAPI = (*MempoolAPIService)(&c.common)
 	c.NetworkAPI = (*NetworkAPIService)(&c.common)
+	c.SearchAPI = (*SearchAPIService)(&c.common)
 
 	return c
 }
diff --git a/codegen.sh b/codegen.sh
index fd4c6a7..d6681c2 100755
--- a/codegen.sh
+++ b/codegen.sh
@@ -53,7 +53,7 @@ done
 rm -rf tmp;
 
 # Download spec file from releases
-ROSETTA_SPEC_VERSION=1.4.6
+ROSETTA_SPEC_VERSION=1.4.7
 curl -L https://github.com/coinbase/rosetta-specifications/releases/download/v${ROSETTA_SPEC_VERSION}/api.json -o api.json;
 
 # Generate client + types code
@@ -119,8 +119,10 @@ sed "${SED_IFLAG[@]}" 's/*CurveType/CurveType/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/*SignatureType/SignatureType/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/*CoinAction/CoinAction/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/*ExemptionType/ExemptionType/g' client/* server/*;
+sed "${SED_IFLAG[@]}" 's/*Operator/Operator/g' client/* server/*;
+sed "${SED_IFLAG[@]}" 's/*BlockEventType/BlockEventType/g' client/* server/*;
 
-# Fix CurveTypes, SignatureTypes, CoinActions, and ExemptionTypes
+# Fix CurveTypes, SignatureTypes, CoinActions, ExemptionTypes 
 sed "${SED_IFLAG[@]}" 's/SECP256K1/Secp256k1/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/SECP256R1/Secp256r1/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/EDWARDS25519/Edwards25519/g' client/* server/*;
diff --git a/examples/server/services/block_service.go b/examples/server/services/block_service.go
index 0c01a29..8c5c2a4 100644
--- a/examples/server/services/block_service.go
+++ b/examples/server/services/block_service.go
@@ -84,7 +84,7 @@ func (s *BlockAPIService) Block(
 								Index: 0,
 							},
 							Type:   "Transfer",
-							Status: "Success",
+							Status: types.String("Success"),
 							Account: &types.AccountIdentifier{
 								Address: "account 0",
 							},
@@ -106,7 +106,7 @@ func (s *BlockAPIService) Block(
 								},
 							},
 							Type:   "Transfer",
-							Status: "Reverted",
+							Status: types.String("Reverted"),
 							Account: &types.AccountIdentifier{
 								Address: "account 1",
 							},
@@ -146,7 +146,7 @@ func (s *BlockAPIService) BlockTransaction(
 						Index: 0,
 					},
 					Type:   "Reward",
-					Status: "Success",
+					Status: types.String("Success"),
 					Account: &types.AccountIdentifier{
 						Address: "account 2",
 					},
diff --git a/fetcher/account.go b/fetcher/account.go
index d336b42..d4c8239 100644
--- a/fetcher/account.go
+++ b/fetcher/account.go
@@ -30,9 +30,9 @@ func (f *Fetcher) AccountBalance(
 	network *types.NetworkIdentifier,
 	account *types.AccountIdentifier,
 	block *types.PartialBlockIdentifier,
-) (*types.BlockIdentifier, []*types.Amount, []*types.Coin, map[string]interface{}, *Error) {
+) (*types.BlockIdentifier, []*types.Amount, map[string]interface{}, *Error) {
 	if err := f.connectionSemaphore.Acquire(ctx, semaphoreRequestWeight); err != nil {
-		return nil, nil, nil, nil, &Error{
+		return nil, nil, nil, &Error{
 			Err: fmt.Errorf("%w: %s", ErrCouldNotAcquireSemaphore, err.Error()),
 		}
 	}
@@ -46,7 +46,7 @@ func (f *Fetcher) AccountBalance(
 		},
 	)
 	if err != nil {
-		return nil, nil, nil, nil, f.RequestFailedError(clientErr, err, "/account/balance")
+		return nil, nil, nil, f.RequestFailedError(clientErr, err, "/account/balance")
 	}
 
 	if err := asserter.AccountBalanceResponse(
@@ -59,10 +59,10 @@ func (f *Fetcher) AccountBalance(
 				err,
 			),
 		}
-		return nil, nil, nil, nil, fetcherErr
+		return nil, nil, nil, fetcherErr
 	}
 
-	return response.BlockIdentifier, response.Balances, response.Coins, response.Metadata, nil
+	return response.BlockIdentifier, response.Balances, response.Metadata, nil
 }
 
 // AccountBalanceRetry retrieves the validated AccountBalance
@@ -73,25 +73,25 @@ func (f *Fetcher) AccountBalanceRetry(
 	network *types.NetworkIdentifier,
 	account *types.AccountIdentifier,
 	block *types.PartialBlockIdentifier,
-) (*types.BlockIdentifier, []*types.Amount, []*types.Coin, map[string]interface{}, *Error) {
+) (*types.BlockIdentifier, []*types.Amount, map[string]interface{}, *Error) {
 	backoffRetries := backoffRetries(
 		f.retryElapsedTime,
 		f.maxRetries,
 	)
 
 	for {
-		responseBlock, balances, coins, metadata, err := f.AccountBalance(
+		responseBlock, balances, metadata, err := f.AccountBalance(
 			ctx,
 			network,
 			account,
 			block,
 		)
 		if err == nil {
-			return responseBlock, balances, coins, metadata, nil
+			return responseBlock, balances, metadata, nil
 		}
 
 		if ctx.Err() != nil {
-			return nil, nil, nil, nil, &Error{
+			return nil, nil, nil, &Error{
 				Err: ctx.Err(),
 			}
 		}
@@ -101,7 +101,7 @@ func (f *Fetcher) AccountBalanceRetry(
 				Err:       fmt.Errorf("%w: /account/balance not attempting retry", err.Err),
 				ClientErr: err.ClientErr,
 			}
-			return nil, nil, nil, nil, fetcherErr
+			return nil, nil, nil, fetcherErr
 		}
 
 		if err := tryAgain(
@@ -109,7 +109,7 @@ func (f *Fetcher) AccountBalanceRetry(
 			backoffRetries,
 			err,
 		); err != nil {
-			return nil, nil, nil, nil, err
+			return nil, nil, nil, err
 		}
 	}
 }
diff --git a/server/api.go b/server/api.go
index 635983b..2b5e770 100644
--- a/server/api.go
+++ b/server/api.go
@@ -30,6 +30,7 @@ import (
 // results to the http response.
 type AccountAPIRouter interface {
 	AccountBalance(http.ResponseWriter, *http.Request)
+	AccountCoins(http.ResponseWriter, *http.Request)
 }
 
 // BlockAPIRouter defines the required methods for binding the api requests to a responses for the
@@ -68,6 +69,15 @@ type ConstructionAPIRouter interface {
 	ConstructionSubmit(http.ResponseWriter, *http.Request)
 }
 
+// EventsAPIRouter defines the required methods for binding the api requests to a responses for the
+// EventsAPI
+// The EventsAPIRouter implementation should parse necessary information from the http request,
+// pass the data to a EventsAPIServicer to perform the required actions, then write the service
+// results to the http response.
+type EventsAPIRouter interface {
+	EventsBlocks(http.ResponseWriter, *http.Request)
+}
+
 // MempoolAPIRouter defines the required methods for binding the api requests to a responses for the
 // MempoolAPI
 // The MempoolAPIRouter implementation should parse necessary information from the http request,
@@ -89,6 +99,15 @@ type NetworkAPIRouter interface {
 	NetworkStatus(http.ResponseWriter, *http.Request)
 }
 
+// SearchAPIRouter defines the required methods for binding the api requests to a responses for the
+// SearchAPI
+// The SearchAPIRouter implementation should parse necessary information from the http request,
+// pass the data to a SearchAPIServicer to perform the required actions, then write the service
+// results to the http response.
+type SearchAPIRouter interface {
+	SearchTransactions(http.ResponseWriter, *http.Request)
+}
+
 // AccountAPIServicer defines the api actions for the AccountAPI service
 // This interface intended to stay up to date with the openapi yaml used to generate it,
 // while the service implementation can ignored with the .openapi-generator-ignore file
@@ -98,6 +117,10 @@ type AccountAPIServicer interface {
 		context.Context,
 		*types.AccountBalanceRequest,
 	) (*types.AccountBalanceResponse, *types.Error)
+	AccountCoins(
+		context.Context,
+		*types.AccountCoinsRequest,
+	) (*types.AccountCoinsResponse, *types.Error)
 }
 
 // BlockAPIServicer defines the api actions for the BlockAPI service
@@ -159,6 +182,17 @@ type ConstructionAPIServicer interface {
 	) (*types.TransactionIdentifierResponse, *types.Error)
 }
 
+// EventsAPIServicer defines the api actions for the EventsAPI service
+// This interface intended to stay up to date with the openapi yaml used to generate it,
+// while the service implementation can ignored with the .openapi-generator-ignore file
+// and updated with the logic required for the API.
+type EventsAPIServicer interface {
+	EventsBlocks(
+		context.Context,
+		*types.EventsBlocksRequest,
+	) (*types.EventsBlocksResponse, *types.Error)
+}
+
 // MempoolAPIServicer defines the api actions for the MempoolAPI service
 // This interface intended to stay up to date with the openapi yaml used to generate it,
 // while the service implementation can ignored with the .openapi-generator-ignore file
@@ -186,3 +220,14 @@ type NetworkAPIServicer interface {
 		*types.NetworkRequest,
 	) (*types.NetworkStatusResponse, *types.Error)
 }
+
+// SearchAPIServicer defines the api actions for the SearchAPI service
+// This interface intended to stay up to date with the openapi yaml used to generate it,
+// while the service implementation can ignored with the .openapi-generator-ignore file
+// and updated with the logic required for the API.
+type SearchAPIServicer interface {
+	SearchTransactions(
+		context.Context,
+		*types.SearchTransactionsRequest,
+	) (*types.SearchTransactionsResponse, *types.Error)
+}
diff --git a/server/api_account.go b/server/api_account.go
index 0eedece..0f447af 100644
--- a/server/api_account.go
+++ b/server/api_account.go
@@ -52,10 +52,16 @@ func (c *AccountAPIController) Routes() Routes {
 			"/account/balance",
 			c.AccountBalance,
 		},
+		{
+			"AccountCoins",
+			strings.ToUpper("Post"),
+			"/account/coins",
+			c.AccountCoins,
+		},
 	}
 }
 
-// AccountBalance - Get an Account Balance
+// AccountBalance - Get an Account's Balance
 func (c *AccountAPIController) AccountBalance(w http.ResponseWriter, r *http.Request) {
 	accountBalanceRequest := &types.AccountBalanceRequest{}
 	if err := json.NewDecoder(r.Body).Decode(&accountBalanceRequest); err != nil {
@@ -84,3 +90,33 @@ func (c *AccountAPIController) AccountBalance(w http.ResponseWriter, r *http.Req
 
 	EncodeJSONResponse(result, http.StatusOK, w)
 }
+
+// AccountCoins - Get an Account's Unspent Coins
+func (c *AccountAPIController) AccountCoins(w http.ResponseWriter, r *http.Request) {
+	accountCoinsRequest := &types.AccountCoinsRequest{}
+	if err := json.NewDecoder(r.Body).Decode(&accountCoinsRequest); err != nil {
+		EncodeJSONResponse(&types.Error{
+			Message: err.Error(),
+		}, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	// Assert that AccountCoinsRequest is correct
+	if err := c.asserter.AccountCoinsRequest(accountCoinsRequest); err != nil {
+		EncodeJSONResponse(&types.Error{
+			Message: err.Error(),
+		}, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	result, serviceErr := c.service.AccountCoins(r.Context(), accountCoinsRequest)
+	if serviceErr != nil {
+		EncodeJSONResponse(serviceErr, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	EncodeJSONResponse(result, http.StatusOK, w)
+}
diff --git a/server/api_events.go b/server/api_events.go
new file mode 100644
index 0000000..8b0d4ea
--- /dev/null
+++ b/server/api_events.go
@@ -0,0 +1,86 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package server
+
+import (
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/coinbase/rosetta-sdk-go/asserter"
+	"github.com/coinbase/rosetta-sdk-go/types"
+)
+
+// A EventsAPIController binds http requests to an api service and writes the service results to the
+// http response
+type EventsAPIController struct {
+	service  EventsAPIServicer
+	asserter *asserter.Asserter
+}
+
+// NewEventsAPIController creates a default api controller
+func NewEventsAPIController(
+	s EventsAPIServicer,
+	asserter *asserter.Asserter,
+) Router {
+	return &EventsAPIController{
+		service:  s,
+		asserter: asserter,
+	}
+}
+
+// Routes returns all of the api route for the EventsAPIController
+func (c *EventsAPIController) Routes() Routes {
+	return Routes{
+		{
+			"EventsBlocks",
+			strings.ToUpper("Post"),
+			"/events/blocks",
+			c.EventsBlocks,
+		},
+	}
+}
+
+// EventsBlocks - [INDEXER] Get a range of BlockEvents
+func (c *EventsAPIController) EventsBlocks(w http.ResponseWriter, r *http.Request) {
+	eventsBlocksRequest := &types.EventsBlocksRequest{}
+	if err := json.NewDecoder(r.Body).Decode(&eventsBlocksRequest); err != nil {
+		EncodeJSONResponse(&types.Error{
+			Message: err.Error(),
+		}, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	// Assert that EventsBlocksRequest is correct
+	if err := c.asserter.EventsBlocksRequest(eventsBlocksRequest); err != nil {
+		EncodeJSONResponse(&types.Error{
+			Message: err.Error(),
+		}, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	result, serviceErr := c.service.EventsBlocks(r.Context(), eventsBlocksRequest)
+	if serviceErr != nil {
+		EncodeJSONResponse(serviceErr, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	EncodeJSONResponse(result, http.StatusOK, w)
+}
diff --git a/server/api_search.go b/server/api_search.go
new file mode 100644
index 0000000..eee79ac
--- /dev/null
+++ b/server/api_search.go
@@ -0,0 +1,86 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package server
+
+import (
+	"encoding/json"
+	"net/http"
+	"strings"
+
+	"github.com/coinbase/rosetta-sdk-go/asserter"
+	"github.com/coinbase/rosetta-sdk-go/types"
+)
+
+// A SearchAPIController binds http requests to an api service and writes the service results to the
+// http response
+type SearchAPIController struct {
+	service  SearchAPIServicer
+	asserter *asserter.Asserter
+}
+
+// NewSearchAPIController creates a default api controller
+func NewSearchAPIController(
+	s SearchAPIServicer,
+	asserter *asserter.Asserter,
+) Router {
+	return &SearchAPIController{
+		service:  s,
+		asserter: asserter,
+	}
+}
+
+// Routes returns all of the api route for the SearchAPIController
+func (c *SearchAPIController) Routes() Routes {
+	return Routes{
+		{
+			"SearchTransactions",
+			strings.ToUpper("Post"),
+			"/search/transactions",
+			c.SearchTransactions,
+		},
+	}
+}
+
+// SearchTransactions - [INDEXER] Search for Transactions
+func (c *SearchAPIController) SearchTransactions(w http.ResponseWriter, r *http.Request) {
+	searchTransactionsRequest := &types.SearchTransactionsRequest{}
+	if err := json.NewDecoder(r.Body).Decode(&searchTransactionsRequest); err != nil {
+		EncodeJSONResponse(&types.Error{
+			Message: err.Error(),
+		}, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	// Assert that SearchTransactionsRequest is correct
+	if err := c.asserter.SearchTransactionsRequest(searchTransactionsRequest); err != nil {
+		EncodeJSONResponse(&types.Error{
+			Message: err.Error(),
+		}, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	result, serviceErr := c.service.SearchTransactions(r.Context(), searchTransactionsRequest)
+	if serviceErr != nil {
+		EncodeJSONResponse(serviceErr, http.StatusInternalServerError, w)
+
+		return
+	}
+
+	EncodeJSONResponse(result, http.StatusOK, w)
+}
diff --git a/types/account_balance_request.go b/types/account_balance_request.go
index ba8729c..289b89d 100644
--- a/types/account_balance_request.go
+++ b/types/account_balance_request.go
@@ -23,4 +23,8 @@ type AccountBalanceRequest struct {
 	NetworkIdentifier *NetworkIdentifier      `json:"network_identifier"`
 	AccountIdentifier *AccountIdentifier      `json:"account_identifier"`
 	BlockIdentifier   *PartialBlockIdentifier `json:"block_identifier,omitempty"`
+	// In some cases, the caller may not want to retrieve all available balances for an
+	// AccountIdentifier. If the currencies field is populated, only balances for the specified
+	// currencies will be returned. If not populated, all available balances will be returned.
+	Currencies []*Currency `json:"currencies,omitempty"`
 }
diff --git a/types/account_balance_response.go b/types/account_balance_response.go
index 9578b6f..7ad1e81 100644
--- a/types/account_balance_response.go
+++ b/types/account_balance_response.go
@@ -18,16 +18,12 @@ package types
 
 // AccountBalanceResponse An AccountBalanceResponse is returned on the /account/balance endpoint. If
 // an account has a balance for each AccountIdentifier describing it (ex: an ERC-20 token balance on
-// a few smart contracts), an account balance request must be made with each AccountIdentifier.
+// a few smart contracts), an account balance request must be made with each AccountIdentifier. The
+// `coins` field was removed and replaced by by `/account/coins` in `v1.4.7`.
 type AccountBalanceResponse struct {
 	BlockIdentifier *BlockIdentifier `json:"block_identifier"`
 	// A single account may have a balance in multiple currencies.
 	Balances []*Amount `json:"balances"`
-	// If a blockchain is UTXO-based, all unspent Coins owned by an account_identifier should be
-	// returned alongside the balance. It is highly recommended to populate this field so that users
-	// of the Rosetta API implementation don't need to maintain their own indexer to track their
-	// UTXOs.
-	Coins []*Coin `json:"coins,omitempty"`
 	// Account-based blockchains that utilize a nonce or sequence number should include that number
 	// in the metadata. This number could be unique to the identifier or global across the account
 	// address.
diff --git a/types/account_coins_request.go b/types/account_coins_request.go
new file mode 100644
index 0000000..81e4470
--- /dev/null
+++ b/types/account_coins_request.go
@@ -0,0 +1,31 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// AccountCoinsRequest AccountCoinsRequest is utilized to make a request on the /account/coins
+// endpoint.
+type AccountCoinsRequest struct {
+	NetworkIdentifier *NetworkIdentifier `json:"network_identifier"`
+	AccountIdentifier *AccountIdentifier `json:"account_identifier"`
+	// Include state from the mempool when looking up an account's unspent coins. Note, using this
+	// functionality breaks any guarantee of idempotency.
+	IncludeMempool bool `json:"include_mempool"`
+	// In some cases, the caller may not want to retrieve coins for all currencies for an
+	// AccountIdentifier. If the currencies field is populated, only coins for the specified
+	// currencies will be returned. If not populated, all unspent coins will be returned.
+	Currencies []*Currency `json:"currencies,omitempty"`
+}
diff --git a/types/account_coins_response.go b/types/account_coins_response.go
new file mode 100644
index 0000000..ea4a232
--- /dev/null
+++ b/types/account_coins_response.go
@@ -0,0 +1,32 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// AccountCoinsResponse AccountCoinsResponse is returned on the /account/coins endpoint and includes
+// all unspent Coins owned by an AccountIdentifier.
+type AccountCoinsResponse struct {
+	BlockIdentifier *BlockIdentifier `json:"block_identifier"`
+	// If a blockchain is UTXO-based, all unspent Coins owned by an account_identifier should be
+	// returned alongside the balance. It is highly recommended to populate this field so that users
+	// of the Rosetta API implementation don't need to maintain their own indexer to track their
+	// UTXOs.
+	Coins []*Coin `json:"coins"`
+	// Account-based blockchains that utilize a nonce or sequence number should include that number
+	// in the metadata. This number could be unique to the identifier or global across the account
+	// address.
+	Metadata map[string]interface{} `json:"metadata,omitempty"`
+}
diff --git a/types/allow.go b/types/allow.go
index 16597fb..8b00f13 100644
--- a/types/allow.go
+++ b/types/allow.go
@@ -48,4 +48,8 @@ type Allow struct {
 	// balance changes. If your implementation relies on any BalanceExemptions, you MUST implement
 	// historical balance lookup (the ability to query an account balance at any BlockIdentifier).
 	BalanceExemptions []*BalanceExemption `json:"balance_exemptions"`
+	// Any Rosetta implementation that can update an AccountIdentifier's unspent coins based on the
+	// contents of the mempool should populate this field as true. If false, requests to
+	// `/account/coins` that set `include_mempool` as true will be automatically rejected.
+	MempoolCoins bool `json:"mempool_coins"`
 }
diff --git a/types/block_event.go b/types/block_event.go
new file mode 100644
index 0000000..035ec7a
--- /dev/null
+++ b/types/block_event.go
@@ -0,0 +1,27 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// BlockEvent BlockEvent represents the addition or removal of a BlockIdentifier from storage.
+// Streaming BlockEvents allows lightweight clients to update their own state without needing to
+// implement their own syncing logic.
+type BlockEvent struct {
+	// sequence is the unique identifier of a BlockEvent within the context of a NetworkIdentifier.
+	Sequence        int64            `json:"sequence"`
+	BlockIdentifier *BlockIdentifier `json:"block_identifier"`
+	Type            BlockEventType   `json:"type"`
+}
diff --git a/types/block_event_type.go b/types/block_event_type.go
new file mode 100644
index 0000000..163757e
--- /dev/null
+++ b/types/block_event_type.go
@@ -0,0 +1,27 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// BlockEventType BlockEventType determines if a BlockEvent represents the addition or removal of a
+// block.
+type BlockEventType string
+
+// List of BlockEventType
+const (
+	ADDED   BlockEventType = "block_added"
+	REMOVED BlockEventType = "block_removed"
+)
diff --git a/types/block_transaction.go b/types/block_transaction.go
new file mode 100644
index 0000000..0a5f1d5
--- /dev/null
+++ b/types/block_transaction.go
@@ -0,0 +1,24 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// BlockTransaction BlockTransaction contains a populated Transaction and the BlockIdentifier that
+// contains it.
+type BlockTransaction struct {
+	BlockIdentifier *BlockIdentifier `json:"block_identifier"`
+	Transaction     *Transaction     `json:"transaction"`
+}
diff --git a/types/events_blocks_request.go b/types/events_blocks_request.go
new file mode 100644
index 0000000..53071e9
--- /dev/null
+++ b/types/events_blocks_request.go
@@ -0,0 +1,30 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// EventsBlocksRequest EventsBlocksRequest is utilized to fetch a sequence of BlockEvents indicating
+// which blocks were added and removed from storage to reach the current state.
+type EventsBlocksRequest struct {
+	NetworkIdentifier *NetworkIdentifier `json:"network_identifier"`
+	// offset is the offset into the event stream to sync events from. If this field is not
+	// populated, we return the limit events backwards from tip. If this is set to 0, we start from
+	// the beginning.
+	Offset *int64 `json:"offset,omitempty"`
+	// limit is the maximum number of events to fetch in one call. The implementation may return <=
+	// limit events.
+	Limit *int64 `json:"limit,omitempty"`
+}
diff --git a/types/events_blocks_response.go b/types/events_blocks_response.go
new file mode 100644
index 0000000..6bd9ac9
--- /dev/null
+++ b/types/events_blocks_response.go
@@ -0,0 +1,28 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// EventsBlocksResponse EventsBlocksResponse contains an ordered collection of BlockEvents and the
+// max retrievable sequence.
+type EventsBlocksResponse struct {
+	// max_sequence is the maximum available sequence number to fetch.
+	MaxSequence int64 `json:"max_sequence"`
+	// events is an array of BlockEvents indicating the order to add and remove blocks to maintain a
+	// canonical view of blockchain state. Lightweight clients can use this event stream to update
+	// state without implementing their own block syncing logic.
+	Events []*BlockEvent `json:"events"`
+}
diff --git a/types/operation.go b/types/operation.go
index cbe0b68..0cb8085 100644
--- a/types/operation.go
+++ b/types/operation.go
@@ -18,7 +18,9 @@ package types
 
 // Operation Operations contain all balance-changing information within a transaction. They are
 // always one-sided (only affect 1 AccountIdentifier) and can succeed or fail independently from a
-// Transaction.
+// Transaction. Operations are used both to represent on-chain data (Data API) and to construct new
+// transactions (Construction API), creating a standard interface for reading and writing to
+// blockchains.
 type Operation struct {
 	OperationIdentifier *OperationIdentifier `json:"operation_identifier"`
 	// Restrict referenced related_operations to identifier indexes < the current
@@ -26,15 +28,20 @@ type Operation struct {
 	// Since operations are one-sided, one could imagine relating operations in a single transfer or
 	// linking operations in a call tree.
 	RelatedOperations []*OperationIdentifier `json:"related_operations,omitempty"`
-	// The network-specific type of the operation. Ensure that any type that can be returned here is
-	// also specified in the NetworkOptionsResponse. This can be very useful to downstream consumers
-	// that parse all block data.
+	// Type is the network-specific type of the operation. Ensure that any type that can be returned
+	// here is also specified in the NetworkOptionsResponse. This can be very useful to downstream
+	// consumers that parse all block data.
 	Type string `json:"type"`
-	// The network-specific status of the operation. Status is not defined on the transaction object
-	// because blockchains with smart contracts may have transactions that partially apply.
-	// Blockchains with atomic transactions (all operations succeed or all operations fail) will
-	// have the same status for each operation.
-	Status     string                 `json:"status"`
+	// Status is the network-specific status of the operation. Status is not defined on the
+	// transaction object because blockchains with smart contracts may have transactions that
+	// partially apply (some operations are successful and some are not). Blockchains with atomic
+	// transactions (all operations succeed or all operations fail) will have the same status for
+	// each operation. On-chain operations (operations retrieved in the `/block` and
+	// `/block/transaction` endpoints) MUST have a populated status field (anything on-chain must
+	// have succeeded or failed). However, operations provided during transaction construction
+	// (often times called \"intent\" in the documentation) MUST NOT have a populated status field
+	// (operations yet to be included on-chain have not yet succeeded or failed).
+	Status     *string                `json:"status,omitempty"`
 	Account    *AccountIdentifier     `json:"account,omitempty"`
 	Amount     *Amount                `json:"amount,omitempty"`
 	CoinChange *CoinChange            `json:"coin_change,omitempty"`
diff --git a/types/operator.go b/types/operator.go
new file mode 100644
index 0000000..47e4c80
--- /dev/null
+++ b/types/operator.go
@@ -0,0 +1,26 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// Operator Operator is used by query-related endpoints to determine how to apply conditions.
+type Operator string
+
+// List of Operator
+const (
+	OR  Operator = "or"
+	AND Operator = "and"
+)
diff --git a/types/search_transactions_request.go b/types/search_transactions_request.go
new file mode 100644
index 0000000..4d36935
--- /dev/null
+++ b/types/search_transactions_request.go
@@ -0,0 +1,50 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// SearchTransactionsRequest SearchTransactionsRequest is used to search for transactions matching a
+// set of provided conditions in canonical blocks.
+type SearchTransactionsRequest struct {
+	NetworkIdentifier *NetworkIdentifier `json:"network_identifier"`
+	Operator          Operator           `json:"operator"`
+	// max_block is the largest block index to consider when searching for transactions. If this
+	// field is not populated, the current block is considered the max_block. If you do not specify
+	// a max_block, it is possible a newly synced block will interfere with paginated transaction
+	// queries (as the offset could become invalid with newly added rows).
+	MaxBlock *int64 `json:"max_block,omitempty"`
+	// offset is the offset into the query result to start returning transactions. If any search
+	// conditions are changed, the query offset will change and you must restart your search
+	// iteration.
+	Offset *int64 `json:"offset,omitempty"`
+	// limit is the maximum number of transactions to return in one call. The implementation may
+	// return <= limit transactions.
+	Limit                 *int64                 `json:"limit,omitempty"`
+	TransactionIdentifier *TransactionIdentifier `json:"transaction_identifier,omitempty"`
+	AccountIdentifier     *AccountIdentifier     `json:"account_identifier,omitempty"`
+	CoinIdentifier        *CoinIdentifier        `json:"coin_identifier,omitempty"`
+	Currency              *Currency              `json:"currency,omitempty"`
+	// status is the network-specific operation type.
+	Status *string `json:"status,omitempty"`
+	// type is the network-specific operation type.
+	Type *string `json:"type,omitempty"`
+	// address is AccountIdentifier.Address. This is used to get all transactions related to an
+	// AccountIdentifier.Address, regardless of SubAccountIdentifier.
+	Address *string `json:"address,omitempty"`
+	// success is a synthetic condition populated by parsing network-specific operation statuses
+	// (using the mapping provided in `/network/options`).
+	Success *bool `json:"success,omitempty"`
+}
diff --git a/types/search_transactions_response.go b/types/search_transactions_response.go
new file mode 100644
index 0000000..48639c5
--- /dev/null
+++ b/types/search_transactions_response.go
@@ -0,0 +1,31 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Generated by: OpenAPI Generator (https://openapi-generator.tech)
+
+package types
+
+// SearchTransactionsResponse SearchTransactionsResponse contains an ordered collection of
+// BlockTransactions that match the query in SearchTransactionsRequest. These BlockTransactions are
+// sorted from most recent block to oldest block.
+type SearchTransactionsResponse struct {
+	// next_offset is the next offset to use when paginating through transaction results. If this
+	// field is not populated, there are no more transactions to query.
+	NextOffset *int64 `json:"next_offset,omitempty"`
+	// transactions is an array of BlockTransactions sorted by most recent BlockIdentifier (meaning
+	// that transactions in recent blocks appear first). If there are many transactions for a
+	// particular search, transactions may not contain all matching transactions. It is up to the
+	// caller to paginate these transactions using the max_block field.
+	Transactions []*BlockTransaction `json:"transactions"`
+}
diff --git a/types/types.go b/types/types.go
index 157dfb6..aa9f65f 100644
--- a/types/types.go
+++ b/types/types.go
@@ -18,5 +18,5 @@ const (
 	// RosettaAPIVersion is the version of the Rosetta API
 	// specification used to generate code for this release
 	// of the SDK.
-	RosettaAPIVersion = "1.4.6"
+	RosettaAPIVersion = "1.4.7"
 )
diff --git a/types/utils.go b/types/utils.go
index 10e24e1..8a1535d 100644
--- a/types/utils.go
+++ b/types/utils.go
@@ -279,3 +279,21 @@ func ExtractAmount(
 		PrettyPrintStruct(currency),
 	)
 }
+
+// String returns a pointer to the
+// string passed as an argument.
+func String(s string) *string {
+	return &s
+}
+
+// Int64 returns a pointer to the
+// int64 passed as an argument.
+func Int64(i int64) *int64 {
+	return &i
+}
+
+// Bool returns a pointer to the
+// bool passed as an argument.
+func Bool(b bool) *bool {
+	return &b
+}


Test patch:
diff --git a/asserter/block_test.go b/asserter/block_test.go
index 1db3b86..4d79d46 100644
--- a/asserter/block_test.go
+++ b/asserter/block_test.go
@@ -296,7 +296,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -310,7 +310,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:   "PAYMENT",
-				Status: "SUCCESS",
+				Status: types.String("SUCCESS"),
 			},
 			index:      int64(1),
 			successful: true,
@@ -327,7 +327,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:   "PAYMENT",
-				Status: "SUCCESS",
+				Status: types.String("SUCCESS"),
 				Amount: validAmount,
 			},
 			index: int64(1),
@@ -339,7 +339,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: &types.AccountIdentifier{},
 				Amount:  validAmount,
 			},
@@ -352,7 +352,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:   "PAYMENT",
-				Status: "SUCCESS",
+				Status: types.String("SUCCESS"),
 			},
 			index: int64(2),
 			err:   ErrOperationIdentifierIndexOutOfOrder,
@@ -363,7 +363,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:   "STAKE",
-				Status: "SUCCESS",
+				Status: types.String("SUCCESS"),
 			},
 			index: int64(1),
 			err:   ErrOperationTypeInvalid,
@@ -374,7 +374,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:   "PAYMENT",
-				Status: "FAILURE",
+				Status: types.String("FAILURE"),
 			},
 			index:      int64(1),
 			successful: false,
@@ -386,7 +386,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:   "PAYMENT",
-				Status: "DEFERRED",
+				Status: types.String("DEFERRED"),
 			},
 			index: int64(1),
 			err:   ErrOperationStatusInvalid,
@@ -411,7 +411,7 @@ func TestOperation(t *testing.T) {
 					Index: int64(1),
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -523,7 +523,7 @@ func TestBlock(t *testing.T) {
 					Index: int64(0),
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -537,7 +537,7 @@ func TestBlock(t *testing.T) {
 					},
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -558,7 +558,7 @@ func TestBlock(t *testing.T) {
 					},
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -579,7 +579,7 @@ func TestBlock(t *testing.T) {
 					},
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -588,7 +588,7 @@ func TestBlock(t *testing.T) {
 					Index: int64(0),
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -609,7 +609,7 @@ func TestBlock(t *testing.T) {
 					},
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -623,7 +623,7 @@ func TestBlock(t *testing.T) {
 					},
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -639,7 +639,7 @@ func TestBlock(t *testing.T) {
 					Index: int64(0),
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
@@ -656,7 +656,7 @@ func TestBlock(t *testing.T) {
 					},
 				},
 				Type:    "PAYMENT",
-				Status:  "SUCCESS",
+				Status:  types.String("SUCCESS"),
 				Account: validAccount,
 				Amount:  validAmount,
 			},
diff --git a/asserter/server_test.go b/asserter/server_test.go
index cddf3c4..cb28130 100644
--- a/asserter/server_test.go
+++ b/asserter/server_test.go
@@ -126,7 +126,7 @@ var (
 				Index: int64(0),
 			},
 			Type:    "PAYMENT",
-			Status:  "SUCCESS",
+			Status:  types.String("SUCCESS"),
 			Account: validAccount,
 			Amount:  validAmount,
 		},
@@ -140,7 +140,7 @@ var (
 				},
 			},
 			Type:    "PAYMENT",
-			Status:  "SUCCESS",
+			Status:  types.String("SUCCESS"),
 			Account: validAccount,
 			Amount:  validAmount,
 		},
diff --git a/constructor/coordinator/coordinator_test.go b/constructor/coordinator/coordinator_test.go
index 369afc8..4ec64ba 100644
--- a/constructor/coordinator/coordinator_test.go
+++ b/constructor/coordinator/coordinator_test.go
@@ -191,7 +191,7 @@ func TestProcess(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
@@ -693,7 +693,7 @@ func TestProcess(t *testing.T) {
 			OperationIdentifier: &types.OperationIdentifier{
 				Index: 0,
 			},
-			Status: "success",
+			Status: types.String("success"),
 			Type:   "Vin",
 			Account: &types.AccountIdentifier{
 				Address: "address1",
@@ -707,7 +707,7 @@ func TestProcess(t *testing.T) {
 			OperationIdentifier: &types.OperationIdentifier{
 				Index: 1,
 			},
-			Status: "success",
+			Status: types.String("success"),
 			Type:   "Vout",
 			Account: &types.AccountIdentifier{
 				Address: "address2",
@@ -833,7 +833,7 @@ func TestProcess_Failed(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
@@ -1332,7 +1332,7 @@ func TestProcess_DryRun(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{"address":"sender"},"amount":{"value":"-10","currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{"address":"recipient"},"amount":{"value":"5","currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{"address":"sender"},"amount":{"value":"-10","currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{"address":"recipient"},"amount":{"value":"5","currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer_1.operations",
 						},
 						{
@@ -1362,7 +1362,7 @@ func TestProcess_DryRun(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{"address":"sender"},"amount":{"value":"-10","currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{"address":"recipient"},"amount":{{recipient_amount}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{"address":"sender"},"amount":{"value":"-10","currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{"address":"recipient"},"amount":{{recipient_amount}}}]`, // nolint
 							OutputPath: "transfer_2.operations",
 						},
 					},
@@ -1645,7 +1645,7 @@ func TestReturnFunds_NoBalance(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
@@ -1977,7 +1977,7 @@ func TestReturnFunds(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{"address":"address2"},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{"address":"address2"},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
@@ -2239,7 +2239,7 @@ func TestReturnFunds(t *testing.T) {
 			OperationIdentifier: &types.OperationIdentifier{
 				Index: 0,
 			},
-			Status: "success",
+			Status: types.String("success"),
 			Type:   "Vin",
 			Account: &types.AccountIdentifier{
 				Address: "address1",
@@ -2253,7 +2253,7 @@ func TestReturnFunds(t *testing.T) {
 			OperationIdentifier: &types.OperationIdentifier{
 				Index: 1,
 			},
-			Status: "success",
+			Status: types.String("success"),
 			Type:   "Vout",
 			Account: &types.AccountIdentifier{
 				Address: "address2",
@@ -2395,7 +2395,7 @@ func TestNoReservedWorkflows(t *testing.T) {
 						},
 						{
 							Type:       job.SetVariable,
-							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","status":"","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","status":"","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
+							Input:      `[{"operation_identifier":{"index":0},"type":"Vin","account":{{sender.account_identifier}},"amount":{"value":{{sender_amount}},"currency":{{currency}}}},{"operation_identifier":{"index":1},"type":"Vout","account":{{recipient.account_identifier}},"amount":{"value":{{recipient_amount}},"currency":{{currency}}}}]`, // nolint
 							OutputPath: "transfer.operations",
 						},
 					},
diff --git a/constructor/worker/worker_test.go b/constructor/worker/worker_test.go
index c952c64..54a769f 100644
--- a/constructor/worker/worker_test.go
+++ b/constructor/worker/worker_test.go
@@ -1034,7 +1034,7 @@ func TestJob_ComplicatedTransfer(t *testing.T) {
 			},
 			{
 				Type:       job.SetVariable,
-				Input:      `[{"operation_identifier":{"index":0},"type":"","status":"","account":{{account.account_identifier}},"amount":{"value":"-90","currency":{{default_curr}}}},{"operation_identifier":{"index":1},"type":"","status":"","account":{"address":{{random_address}}},"amount":{"value":"100","currency":{{default_curr}}}}]`, // nolint
+				Input:      `[{"operation_identifier":{"index":0},"type":"","account":{{account.account_identifier}},"amount":{"value":"-90","currency":{{default_curr}}}},{"operation_identifier":{"index":1},"type":"","account":{"address":{{random_address}}},"amount":{"value":"100","currency":{{default_curr}}}}]`, // nolint
 				OutputPath: "create_send.operations",
 			},
 			{
diff --git a/fetcher/account_test.go b/fetcher/account_test.go
index 9b2b450..276d79b 100644
--- a/fetcher/account_test.go
+++ b/fetcher/account_test.go
@@ -176,7 +176,7 @@ func TestAccountBalanceRetry(t *testing.T) {
 				WithRetryElapsedTime(5*time.Second),
 				WithMaxRetries(test.fetcherMaxRetries),
 			)
-			block, amounts, _, metadata, err := f.AccountBalanceRetry(
+			block, amounts, metadata, err := f.AccountBalanceRetry(
 				ctx,
 				test.network,
 				test.account,
diff --git a/parser/balance_changes_test.go b/parser/balance_changes_test.go
index a4e2f3e..4480517 100644
--- a/parser/balance_changes_test.go
+++ b/parser/balance_changes_test.go
@@ -45,7 +45,7 @@ func TestBalanceChanges(t *testing.T) {
 				Index: 0,
 			},
 			Type:   "Transfer",
-			Status: "Success",
+			Status: types.String("Success"),
 		}
 
 		emptyAmount = &types.Operation{
@@ -53,7 +53,7 @@ func TestBalanceChanges(t *testing.T) {
 				Index: 0,
 			},
 			Type:    "Transfer",
-			Status:  "Success",
+			Status:  types.String("Success"),
 			Account: recipient,
 		}
 
@@ -62,7 +62,7 @@ func TestBalanceChanges(t *testing.T) {
 				Index: 0,
 			},
 			Type:    "Transfer",
-			Status:  "Success",
+			Status:  types.String("Success"),
 			Account: recipient,
 			Amount:  recipientAmount,
 		}
@@ -72,7 +72,7 @@ func TestBalanceChanges(t *testing.T) {
 				Index: 1,
 			},
 			Type:    "Transfer",
-			Status:  "Failure",
+			Status:  types.String("Failure"),
 			Account: recipient,
 			Amount:  recipientAmount,
 		}
@@ -295,7 +295,7 @@ func simpleTransactionFactory(
 					Index: 0,
 				},
 				Type:   "Transfer",
-				Status: "Success",
+				Status: types.String("Success"),
 				Account: &types.AccountIdentifier{
 					Address: address,
 				},
diff --git a/parser/intent_test.go b/parser/intent_test.go
index b10b5dc..e1f5e33 100644
--- a/parser/intent_test.go
+++ b/parser/intent_test.go
@@ -50,7 +50,7 @@ func TestExpectedOperation(t *testing.T) {
 						Index: 2,
 					},
 				},
-				Status: "success",
+				Status: types.String("success"),
 				Type:   "transfer",
 				Account: &types.AccountIdentifier{
 					Address: "addr1",
@@ -82,7 +82,7 @@ func TestExpectedOperation(t *testing.T) {
 						Index: 2,
 					},
 				},
-				Status: "success",
+				Status: types.String("success"),
 				Type:   "transfer",
 				Account: &types.AccountIdentifier{
 					Address: "addr2",
@@ -115,7 +115,7 @@ func TestExpectedOperation(t *testing.T) {
 						Index: 2,
 					},
 				},
-				Status: "success",
+				Status: types.String("success"),
 				Type:   "transfer",
 				Account: &types.AccountIdentifier{
 					Address: "addr1",
@@ -148,7 +148,7 @@ func TestExpectedOperation(t *testing.T) {
 						Index: 2,
 					},
 				},
-				Status: "success",
+				Status: types.String("success"),
 				Type:   "reward",
 				Account: &types.AccountIdentifier{
 					Address: "addr1",
@@ -214,7 +214,7 @@ func TestExpectedOperations(t *testing.T) {
 					OperationIdentifier: &types.OperationIdentifier{
 						Index: 2,
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "fee",
 					Account: &types.AccountIdentifier{
 						Address: "addr2",
@@ -232,7 +232,7 @@ func TestExpectedOperations(t *testing.T) {
 							Index: 2,
 						},
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "transfer",
 					Account: &types.AccountIdentifier{
 						Address: "addr1",
@@ -334,7 +334,7 @@ func TestExpectedOperations(t *testing.T) {
 					OperationIdentifier: &types.OperationIdentifier{
 						Index: 2,
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "fee",
 					Account: &types.AccountIdentifier{
 						Address: "addr2",
@@ -352,7 +352,7 @@ func TestExpectedOperations(t *testing.T) {
 							Index: 2,
 						},
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "transfer",
 					Account: &types.AccountIdentifier{
 						Address: "addr1",
@@ -396,7 +396,7 @@ func TestExpectedOperations(t *testing.T) {
 					OperationIdentifier: &types.OperationIdentifier{
 						Index: 2,
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "fee",
 					Account: &types.AccountIdentifier{
 						Address: "addr2",
@@ -414,7 +414,7 @@ func TestExpectedOperations(t *testing.T) {
 							Index: 2,
 						},
 					},
-					Status: "failure",
+					Status: types.String("failure"),
 					Type:   "transfer",
 					Account: &types.AccountIdentifier{
 						Address: "addr1",
@@ -447,7 +447,7 @@ func TestExpectedOperations(t *testing.T) {
 					OperationIdentifier: &types.OperationIdentifier{
 						Index: 2,
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "fee",
 					Account: &types.AccountIdentifier{
 						Address: "addr2",
@@ -465,7 +465,7 @@ func TestExpectedOperations(t *testing.T) {
 							Index: 2,
 						},
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "transfer",
 					Account: &types.AccountIdentifier{
 						Address: "addr1",
@@ -515,7 +515,7 @@ func TestExpectedOperations(t *testing.T) {
 							Index: 2,
 						},
 					},
-					Status: "success",
+					Status: types.String("success"),
 					Type:   "transfer",
 					Account: &types.AccountIdentifier{
 						Address: "addr1",
diff --git a/storage/balance_storage_test.go b/storage/balance_storage_test.go
index eb425cc..65c086b 100644
--- a/storage/balance_storage_test.go
+++ b/storage/balance_storage_test.go
@@ -1265,7 +1265,7 @@ func TestBlockSyncing(t *testing.T) {
 							Index: 0,
 						},
 						Account: addr1,
-						Status:  "Success",
+						Status:  types.String("Success"),
 						Type:    "Transfer",
 						Amount: &types.Amount{
 							Value:    "100",
@@ -1298,7 +1298,7 @@ func TestBlockSyncing(t *testing.T) {
 							Index: 0,
 						},
 						Account: addr1,
-						Status:  "Success",
+						Status:  types.String("Success"),
 						Type:    "Transfer",
 						Amount: &types.Amount{
 							Value:    "-50",
@@ -1310,7 +1310,7 @@ func TestBlockSyncing(t *testing.T) {
 							Index: 1,
 						},
 						Account: addr2,
-						Status:  "Success",
+						Status:  types.String("Success"),
 						Type:    "Transfer",
 						Amount: &types.Amount{
 							Value:    "50",
@@ -1322,7 +1322,7 @@ func TestBlockSyncing(t *testing.T) {
 							Index: 2,
 						},
 						Account: addr1,
-						Status:  "Success",
+						Status:  types.String("Success"),
 						Type:    "Transfer",
 						Amount: &types.Amount{
 							Value:    "-1",
@@ -1355,7 +1355,7 @@ func TestBlockSyncing(t *testing.T) {
 							Index: 0,
 						},
 						Account: addr1,
-						Status:  "Success",
+						Status:  types.String("Success"),
 						Type:    "Transfer",
 						Amount: &types.Amount{
 							Value:    "-100",
@@ -1367,7 +1367,7 @@ func TestBlockSyncing(t *testing.T) {
 							Index: 1,
 						},
 						Account: addr2,
-						Status:  "Success",
+						Status:  types.String("Success"),
 						Type:    "Transfer",
 						Amount: &types.Amount{
 							Value:    "100",
diff --git a/storage/block_storage_test.go b/storage/block_storage_test.go
index 2207ae4..cece21f 100644
--- a/storage/block_storage_test.go
+++ b/storage/block_storage_test.go
@@ -113,7 +113,7 @@ func simpleTransactionFactory(
 					Index: 0,
 				},
 				Type:   "Transfer",
-				Status: "Success",
+				Status: types.String("Success"),
 				Account: &types.AccountIdentifier{
 					Address: address,
 				},
@@ -213,7 +213,7 @@ var (
 							Index: 0,
 						},
 						Type:   "Transfer",
-						Status: "Success",
+						Status: types.String("Success"),
 						Account: &types.AccountIdentifier{
 							Address: "addr1",
 							SubAccount: &types.SubAccountIdentifier{
diff --git a/storage/coin_storage_test.go b/storage/coin_storage_test.go
index b23051d..66a4947 100644
--- a/storage/coin_storage_test.go
+++ b/storage/coin_storage_test.go
@@ -120,8 +120,8 @@ var (
 		},
 	}
 
-	successStatus = "success"
-	failureStatus = "failure"
+	successStatus = types.String("success")
+	failureStatus = types.String("failure")
 
 	currency = &types.Currency{
 		Symbol:   "sym",
@@ -407,11 +407,11 @@ func TestCoinStorage(t *testing.T) {
 		[]string{"Transfer"},
 		[]*types.OperationStatus{
 			{
-				Status:     successStatus,
+				Status:     *successStatus,
 				Successful: true,
 			},
 			{
-				Status:     failureStatus,
+				Status:     *failureStatus,
 				Successful: false,
 			},
 		},
diff --git a/syncer/syncer_test.go b/syncer/syncer_test.go
index 1cac11a..72f1263 100644
--- a/syncer/syncer_test.go
+++ b/syncer/syncer_test.go
@@ -53,7 +53,7 @@ var (
 			Index: 0,
 		},
 		Type:    "Transfer",
-		Status:  "Success",
+		Status:  types.String("Success"),
 		Account: recipient,
 		Amount:  recipientAmount,
 	}
@@ -63,7 +63,7 @@ var (
 			Index: 1,
 		},
 		Type:    "Transfer",
-		Status:  "Failure",
+		Status:  types.String("Failure"),
 		Account: recipient,
 		Amount:  recipientAmount,
 	}
@@ -92,7 +92,7 @@ var (
 			Index: 0,
 		},
 		Type:    "Transfer",
-		Status:  "Success",
+		Status:  types.String("Success"),
 		Account: sender,
 		Amount:  senderAmount,
 	}