Repository: coinbase/rosetta-cli
Pull request #110 (2020-08-17 18:22:52)

Move statefulsyncer, storage, and utils to rosetta-sdk-go
Initially intended only for internal use, these packages have become useful to projects in the Rosetta ecosystem. It seemed strange that using them required referencing the rosetta-cli, so we are moving them to rosetta-sdk-go.

Related PR: https://github.com/coinbase/rosetta-sdk-go/pull/99

### Changes
- [X] Move `statefulsyncer`, `storage`, and `utils` to rosetta-sdk-go


Patch:
diff --git a/Makefile b/Makefile
index 27f3791..bab383f 100644
--- a/Makefile
+++ b/Makefile
@@ -12,9 +12,7 @@ ADDLICENCE_SCRIPT=${ADDLICENSE_CMD} -c "Coinbase, Inc." -l "apache" -v
 GOLINES_CMD=go run github.com/segmentio/golines
 GOVERALLS_CMD=go run github.com/mattn/goveralls
 COVERAGE_TEST_DIRECTORIES=./configuration/... ./pkg/constructor/... \
-	./pkg/logger/... ./pkg/scenario/... \
-	./pkg/statefulsyncer/... ./pkg/storage/... \
-	./pkg/utils/...
+	./pkg/logger/... ./pkg/scenario/...
 TEST_SCRIPT=go test -v ./pkg/... ./configuration/...
 COVERAGE_TEST_SCRIPT=go test -v ${COVERAGE_TEST_DIRECTORIES}
 
diff --git a/cmd/check_construction.go b/cmd/check_construction.go
index af577e9..1f1a761 100644
--- a/cmd/check_construction.go
+++ b/cmd/check_construction.go
@@ -21,9 +21,9 @@ import (
 	"time"
 
 	"github.com/coinbase/rosetta-cli/pkg/tester"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 	"github.com/spf13/cobra"
 	"golang.org/x/sync/errgroup"
@@ -64,12 +64,12 @@ func runCheckConstructionCmd(cmd *cobra.Command, args []string) {
 		fetcher.WithTimeout(time.Duration(Config.HTTPTimeout)*time.Second),
 	)
 
-	_, _, err := fetcher.InitializeAsserter(ctx)
-	if err != nil {
-		log.Fatalf("%s: unable to initialize asserter", err.Error())
+	_, _, fetchErr := fetcher.InitializeAsserter(ctx)
+	if fetchErr != nil {
+		log.Fatalf("%s: unable to initialize asserter", fetchErr.Err.Error())
 	}
 
-	_, err = utils.CheckNetworkSupported(ctx, Config.Network, fetcher)
+	_, err := utils.CheckNetworkSupported(ctx, Config.Network, fetcher)
 	if err != nil {
 		log.Fatalf("%s: unable to confirm network is supported", err.Error())
 	}
diff --git a/cmd/check_data.go b/cmd/check_data.go
index 8ff51f6..01de949 100644
--- a/cmd/check_data.go
+++ b/cmd/check_data.go
@@ -20,9 +20,9 @@ import (
 	"time"
 
 	"github.com/coinbase/rosetta-cli/pkg/tester"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/spf13/cobra"
 	"golang.org/x/sync/errgroup"
 )
@@ -80,13 +80,13 @@ func runCheckDataCmd(cmd *cobra.Command, args []string) {
 		fetcher.WithTimeout(time.Duration(Config.HTTPTimeout)*time.Second),
 	)
 
-	_, _, err := fetcher.InitializeAsserter(ctx)
-	if err != nil {
+	_, _, fetchErr := fetcher.InitializeAsserter(ctx)
+	if fetchErr != nil {
 		tester.Exit(
 			Config,
 			nil,
 			nil,
-			fmt.Errorf("%w: unable to initialize asserter", err),
+			fmt.Errorf("%w: unable to initialize asserter", fetchErr.Err),
 			1,
 			"",
 			"",
diff --git a/cmd/configuration_create.go b/cmd/configuration_create.go
index e76ee48..99e56d9 100644
--- a/cmd/configuration_create.go
+++ b/cmd/configuration_create.go
@@ -18,8 +18,8 @@ import (
 	"log"
 
 	"github.com/coinbase/rosetta-cli/configuration"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/spf13/cobra"
 )
 
diff --git a/cmd/root.go b/cmd/root.go
index f83b769..5a3cd06 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -23,8 +23,8 @@ import (
 	"syscall"
 
 	"github.com/coinbase/rosetta-cli/configuration"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 	"github.com/spf13/cobra"
 )
diff --git a/cmd/utils_asserter_configuration.go b/cmd/utils_asserter_configuration.go
index 6cbc12f..c78d0f7 100644
--- a/cmd/utils_asserter_configuration.go
+++ b/cmd/utils_asserter_configuration.go
@@ -19,9 +19,8 @@ import (
 	"log"
 	"time"
 
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/spf13/cobra"
 )
 
@@ -53,9 +52,9 @@ func runCreateConfigurationCmd(cmd *cobra.Command, args []string) {
 	)
 
 	// Initialize the fetcher's asserter
-	_, _, err := newFetcher.InitializeAsserter(ctx)
-	if err != nil {
-		log.Fatalf("%s: failed to initialize asserter", err.Error())
+	_, _, fetchErr := newFetcher.InitializeAsserter(ctx)
+	if fetchErr != nil {
+		log.Fatalf("%s: failed to initialize asserter", fetchErr.Err.Error())
 	}
 
 	configuration, err := newFetcher.Asserter.ClientConfiguration()
diff --git a/cmd/view_account.go b/cmd/view_account.go
index c38fc2c..4a3e49e 100644
--- a/cmd/view_account.go
+++ b/cmd/view_account.go
@@ -22,11 +22,10 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/spf13/cobra"
 )
 
@@ -67,12 +66,12 @@ func runViewAccountCmd(cmd *cobra.Command, args []string) {
 	)
 
 	// Initialize the fetcher's asserter
-	_, _, err := newFetcher.InitializeAsserter(ctx)
-	if err != nil {
-		log.Fatal(err)
+	_, _, fetchErr := newFetcher.InitializeAsserter(ctx)
+	if fetchErr != nil {
+		log.Fatal(fetchErr.Err)
 	}
 
-	_, err = utils.CheckNetworkSupported(ctx, Config.Network, newFetcher)
+	_, err := utils.CheckNetworkSupported(ctx, Config.Network, newFetcher)
 	if err != nil {
 		log.Fatalf("%s: unable to confirm network is supported", err.Error())
 	}
@@ -87,14 +86,14 @@ func runViewAccountCmd(cmd *cobra.Command, args []string) {
 		lookupBlock = &types.PartialBlockIdentifier{Index: &index}
 	}
 
-	block, amounts, coins, metadata, err := newFetcher.AccountBalanceRetry(
+	block, amounts, coins, metadata, fetchErr := newFetcher.AccountBalanceRetry(
 		ctx,
 		Config.Network,
 		account,
 		lookupBlock,
 	)
-	if err != nil {
-		log.Fatal(fmt.Errorf("%w: unable to fetch account %+v", err, account))
+	if fetchErr != nil {
+		log.Fatal(fmt.Errorf("%w: unable to fetch account %+v", fetchErr.Err, account))
 	}
 
 	log.Printf("Amounts: %s\n", types.PrettyPrintStruct(amounts))
diff --git a/cmd/view_block.go b/cmd/view_block.go
index abbd3c8..f4f7d99 100644
--- a/cmd/view_block.go
+++ b/cmd/view_block.go
@@ -21,11 +21,10 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/spf13/cobra"
 )
 
@@ -66,9 +65,9 @@ func runViewBlockCmd(cmd *cobra.Command, args []string) {
 	// Behind the scenes this makes a call to get the
 	// network status and uses the response to inform
 	// the asserter what are valid responses.
-	_, _, err = newFetcher.InitializeAsserter(ctx)
-	if err != nil {
-		log.Fatal(err)
+	_, _, fetchErr := newFetcher.InitializeAsserter(ctx)
+	if fetchErr != nil {
+		log.Fatal(fetchErr.Err)
 	}
 
 	_, err = utils.CheckNetworkSupported(ctx, Config.Network, newFetcher)
@@ -85,15 +84,15 @@ func runViewBlockCmd(cmd *cobra.Command, args []string) {
 	// the client directly, you will need to implement a mechanism
 	// to fully populate the block by fetching all these
 	// transactions.
-	block, err := newFetcher.BlockRetry(
+	block, fetchErr := newFetcher.BlockRetry(
 		ctx,
 		Config.Network,
 		&types.PartialBlockIdentifier{
 			Index: &index,
 		},
 	)
-	if err != nil {
-		log.Fatal(fmt.Errorf("%w: unable to fetch block", err))
+	if fetchErr != nil {
+		log.Fatal(fmt.Errorf("%w: unable to fetch block", fetchErr.Err))
 	}
 
 	log.Printf("Current Block: %s\n", types.PrettyPrintStruct(block))
diff --git a/cmd/view_networks.go b/cmd/view_networks.go
index 0baeaad..74e2d54 100644
--- a/cmd/view_networks.go
+++ b/cmd/view_networks.go
@@ -49,9 +49,9 @@ func runViewNetworksCmd(cmd *cobra.Command, args []string) {
 	)
 
 	// Attempt to fetch network list
-	networkList, err := f.NetworkListRetry(ctx, nil)
-	if err != nil {
-		log.Fatalf("%s: unable to fetch network list", err.Error())
+	networkList, fetchErr := f.NetworkListRetry(ctx, nil)
+	if fetchErr != nil {
+		log.Fatalf("%s: unable to fetch network list", fetchErr.Err.Error())
 	}
 
 	if len(networkList.NetworkIdentifiers) == 0 {
@@ -60,24 +60,24 @@ func runViewNetworksCmd(cmd *cobra.Command, args []string) {
 
 	for _, network := range networkList.NetworkIdentifiers {
 		color.Cyan(types.PrettyPrintStruct(network))
-		networkOptions, err := f.NetworkOptions(
+		networkOptions, fetchErr := f.NetworkOptions(
 			ctx,
 			network,
 			nil,
 		)
-		if err != nil {
-			log.Fatalf("%s: unable to get network options", err.Error())
+		if fetchErr != nil {
+			log.Fatalf("%s: unable to get network options", fetchErr.Err.Error())
 		}
 
 		log.Printf("Network options: %s\n", types.PrettyPrintStruct(networkOptions))
 
-		networkStatus, err := f.NetworkStatusRetry(
+		networkStatus, fetchErr := f.NetworkStatusRetry(
 			ctx,
 			network,
 			nil,
 		)
-		if err != nil {
-			log.Fatalf("%s: unable to get network status", err.Error())
+		if fetchErr != nil {
+			log.Fatalf("%s: unable to get network status", fetchErr.Err.Error())
 		}
 
 		log.Printf("Network status: %s\n", types.PrettyPrintStruct(networkStatus))
diff --git a/configuration/configuration.go b/configuration/configuration.go
index cb0ce14..ef4929c 100644
--- a/configuration/configuration.go
+++ b/configuration/configuration.go
@@ -22,10 +22,11 @@ import (
 	"math/big"
 
 	"github.com/coinbase/rosetta-cli/pkg/scenario"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/asserter"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 )
 
@@ -224,13 +225,7 @@ type ConstructionConfiguration struct {
 
 	// PrefundedAccounts is an array of prefunded accounts
 	// to use while testing.
-	PrefundedAccounts []*PrefundedAccount `json:"prefunded_accounts"`
-}
-
-type PrefundedAccount struct {
-	PrivateKeyHex string          `json:"privkey"`
-	Address       string          `json:"address"`
-	CurveType     types.CurveType `json:"curve_type"`
+	PrefundedAccounts []*storage.PrefundedAccount `json:"prefunded_accounts"`
 }
 
 // DefaultConstructionConfiguration returns the *ConstructionConfiguration
diff --git a/go.mod b/go.mod
index 7690478..57c09a1 100644
--- a/go.mod
+++ b/go.mod
@@ -3,19 +3,11 @@ module github.com/coinbase/rosetta-cli
 go 1.13
 
 require (
-	github.com/coinbase/rosetta-sdk-go v0.3.5-0.20200812042923-73d638d69870
-	github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200811071800-b22eccb04321
-	github.com/ethereum/go-ethereum v1.9.18
+	github.com/coinbase/rosetta-sdk-go v0.3.5-0.20200817175934-2ce9b255ab7d
 	github.com/fatih/color v1.9.0
-	github.com/golang/protobuf v1.4.2 // indirect
 	github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
-	github.com/kr/pretty v0.2.0 // indirect
 	github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c
-	github.com/pkg/errors v0.9.1 // indirect
 	github.com/spf13/cobra v1.0.0
-	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/stretchr/testify v1.6.1
-	github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1
-	golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
-	golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect
+	golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
 )
diff --git a/go.sum b/go.sum
index 2e9c46c..1595705 100644
--- a/go.sum
+++ b/go.sum
@@ -15,6 +15,8 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
 github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
@@ -47,6 +49,7 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
 github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
 github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
 github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -54,9 +57,11 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
+github.com/coinbase/rosetta-cli v0.4.1/go.mod h1:06yp7ELNVihI7vrekRLJ2WVEeEJcVuHBA48YJuuAC5Q=
 github.com/coinbase/rosetta-sdk-go v0.3.4 h1:jWKgajozco/T0FNnZb2TqBsmsUoF6ZuCLnUJkEE+vNg=
-github.com/coinbase/rosetta-sdk-go v0.3.5-0.20200812042923-73d638d69870 h1:eqi+3gj2n7mZkRTbA+zDIhklPq4iPuFknHZJ/cPtwGw=
-github.com/coinbase/rosetta-sdk-go v0.3.5-0.20200812042923-73d638d69870/go.mod h1:Q6dAY0kdG2X3jNaIYnkxnZOb8XEZQar9Q1RcnBgm/wQ=
+github.com/coinbase/rosetta-sdk-go v0.3.4/go.mod h1:Q6dAY0kdG2X3jNaIYnkxnZOb8XEZQar9Q1RcnBgm/wQ=
+github.com/coinbase/rosetta-sdk-go v0.3.5-0.20200817175934-2ce9b255ab7d h1:T3sV3EmPQ94T0nzBRRUpXayI3+9TTfG7SCiRwn/kCVk=
+github.com/coinbase/rosetta-sdk-go v0.3.5-0.20200817175934-2ce9b255ab7d/go.mod h1:UTvOzD4L0TCDUHs3IQYFNMW2BL17tm5HGpFI71o8GtA=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
@@ -77,18 +82,18 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
-github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200811071800-b22eccb04321 h1:LsR9nZv2ijtDtB7kyQum3KKrae/15LazuOpuHUfAriY=
-github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200811071800-b22eccb04321/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE=
 github.com/dgraph-io/badger/v2 v2.0.3 h1:inzdf6VF/NZ+tJ8RwwYMjJMvsOALTHYdozn0qSl6XJI=
 github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM=
 github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
 github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
 github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
-github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA=
-github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
+github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI=
+github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
 github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
+github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
@@ -96,6 +101,8 @@ github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbT
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/ethereum/go-ethereum v1.9.18 h1:+vzvufVD7+OfQa07IJP20Z7AGZsJaw0M6JIA/WQcqy8=
 github.com/ethereum/go-ethereum v1.9.18/go.mod h1:JSSTypSMTkGZtAdAChH2wP5dZEvPGh3nUTuDpH+hNrg=
 github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -122,12 +129,14 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -141,6 +150,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/pprof v0.0.0-20181127221834-b4f47329b966/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -186,6 +197,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
 github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
+github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
 github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
 github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -193,6 +206,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
 github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -230,6 +245,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -314,14 +330,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -331,6 +353,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
+golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -339,6 +363,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -356,9 +382,13 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb h1:ZxSglHghKPYD8WDeRUzRJrUJtDF0PxsTUSxyqr9/5BI=
 golang.org/x/sys v0.0.0-20191024172528-b4ff53e7a1cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
 golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200817155316-9781c653f443 h1:X18bCaipMcoJGm27Nv7zr4XYPKGUy92GtqboKC2Hxaw=
+golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -366,25 +396,38 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181127232545-e782529d0ddd/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20191024220359-3d91e92cde03 h1:4gtJXHJ9ud0q8MNSDxJsRU/WH+afypbe4Vk4zq+8qow=
 golang.org/x/tools v0.0.0-20191024220359-3d91e92cde03/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
@@ -408,3 +451,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/mocks/constructor/handler.go b/mocks/constructor/handler.go
index 71c2e46..8cef173 100644
--- a/mocks/constructor/handler.go
+++ b/mocks/constructor/handler.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Code generated by mockery v2.0.0-alpha.2. DO NOT EDIT.
+// Code generated by mockery v1.0.0. DO NOT EDIT.
 
 package constructor
 
diff --git a/mocks/constructor/helper.go b/mocks/constructor/helper.go
index acc8139..302a220 100644
--- a/mocks/constructor/helper.go
+++ b/mocks/constructor/helper.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Code generated by mockery v2.0.0-alpha.2. DO NOT EDIT.
+// Code generated by mockery v1.0.0. DO NOT EDIT.
 
 package constructor
 
@@ -25,7 +25,7 @@ import (
 
 	mock "github.com/stretchr/testify/mock"
 
-	storage "github.com/coinbase/rosetta-cli/pkg/storage"
+	storage "github.com/coinbase/rosetta-sdk-go/storage"
 
 	types "github.com/coinbase/rosetta-sdk-go/types"
 )
diff --git a/pkg/constructor/constructor.go b/pkg/constructor/constructor.go
index fa740e7..eb0818e 100644
--- a/pkg/constructor/constructor.go
+++ b/pkg/constructor/constructor.go
@@ -25,12 +25,12 @@ import (
 	"github.com/coinbase/rosetta-cli/configuration"
 	"github.com/coinbase/rosetta-cli/pkg/logger"
 	"github.com/coinbase/rosetta-cli/pkg/scenario"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/keys"
 	"github.com/coinbase/rosetta-sdk-go/parser"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 	"github.com/jinzhu/copier"
 )
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index 8bada93..43bc951 100644
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -23,12 +23,12 @@ import (
 	"path"
 
 	"github.com/coinbase/rosetta-cli/pkg/scenario"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 )
 
diff --git a/pkg/processor/balance_storage_handler.go b/pkg/processor/balance_storage_handler.go
index 72cf2dd..b7623b4 100644
--- a/pkg/processor/balance_storage_handler.go
+++ b/pkg/processor/balance_storage_handler.go
@@ -18,10 +18,10 @@ import (
 	"context"
 
 	"github.com/coinbase/rosetta-cli/pkg/logger"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
 
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
diff --git a/pkg/processor/balance_storage_helper.go b/pkg/processor/balance_storage_helper.go
index 17d1578..2e7d976 100644
--- a/pkg/processor/balance_storage_helper.go
+++ b/pkg/processor/balance_storage_helper.go
@@ -18,14 +18,13 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 )
 
 var _ storage.BalanceStorageHelper = (*BalanceStorageHelper)(nil)
diff --git a/pkg/processor/broadcast_storage_handler.go b/pkg/processor/broadcast_storage_handler.go
index f579e33..3564ca8 100644
--- a/pkg/processor/broadcast_storage_handler.go
+++ b/pkg/processor/broadcast_storage_handler.go
@@ -20,9 +20,9 @@ import (
 	"math/big"
 
 	"github.com/coinbase/rosetta-cli/configuration"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
 
 	"github.com/coinbase/rosetta-sdk-go/parser"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
diff --git a/pkg/processor/broadcast_storage_helper.go b/pkg/processor/broadcast_storage_helper.go
index 3849762..f58a6d4 100644
--- a/pkg/processor/broadcast_storage_helper.go
+++ b/pkg/processor/broadcast_storage_helper.go
@@ -18,9 +18,8 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
diff --git a/pkg/processor/coin_storage_helper.go b/pkg/processor/coin_storage_helper.go
index 6761cec..dae4b41 100644
--- a/pkg/processor/coin_storage_helper.go
+++ b/pkg/processor/coin_storage_helper.go
@@ -17,8 +17,7 @@ package processor
 import (
 	"context"
 
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
diff --git a/pkg/processor/constructor_handler.go b/pkg/processor/constructor_handler.go
index 67c0095..d46613d 100644
--- a/pkg/processor/constructor_handler.go
+++ b/pkg/processor/constructor_handler.go
@@ -19,8 +19,8 @@ import (
 	"math/big"
 
 	"github.com/coinbase/rosetta-cli/pkg/constructor"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
 
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
diff --git a/pkg/processor/constructor_helper.go b/pkg/processor/constructor_helper.go
index 1168ce8..548fa6a 100644
--- a/pkg/processor/constructor_helper.go
+++ b/pkg/processor/constructor_helper.go
@@ -20,12 +20,12 @@ import (
 	"math/big"
 
 	"github.com/coinbase/rosetta-cli/pkg/constructor"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/keys"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 )
 
 var _ constructor.Helper = (*ConstructorHelper)(nil)
diff --git a/pkg/processor/reconciler_handler.go b/pkg/processor/reconciler_handler.go
index f932761..95f58ab 100644
--- a/pkg/processor/reconciler_handler.go
+++ b/pkg/processor/reconciler_handler.go
@@ -21,9 +21,9 @@ import (
 	"math/big"
 
 	"github.com/coinbase/rosetta-cli/pkg/logger"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
 
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
diff --git a/pkg/processor/reconciler_helper.go b/pkg/processor/reconciler_helper.go
index 02d40b2..8299ac3 100644
--- a/pkg/processor/reconciler_helper.go
+++ b/pkg/processor/reconciler_helper.go
@@ -18,11 +18,10 @@ import (
 	"context"
 	"errors"
 
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 )
 
 // ReconcilerHelper implements the Reconciler.Helper
diff --git a/pkg/statefulsyncer/stateful_syncer.go b/pkg/statefulsyncer/stateful_syncer.go
deleted file mode 100644
index 11ffa44..0000000
--- a/pkg/statefulsyncer/stateful_syncer.go
+++ /dev/null
@@ -1,181 +0,0 @@
-// 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.
-
-package statefulsyncer
-
-import (
-	"context"
-	"fmt"
-	"math/big"
-
-	"github.com/coinbase/rosetta-cli/pkg/logger"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-
-	"github.com/coinbase/rosetta-sdk-go/fetcher"
-	"github.com/coinbase/rosetta-sdk-go/syncer"
-	"github.com/coinbase/rosetta-sdk-go/types"
-)
-
-var _ syncer.Handler = (*StatefulSyncer)(nil)
-var _ syncer.Helper = (*StatefulSyncer)(nil)
-
-// StatefulSyncer is an abstraction layer over
-// the stateless syncer package. This layer
-// handles sync restarts and provides
-// fully populated blocks during reorgs (not
-// provided by stateless syncer).
-type StatefulSyncer struct {
-	network        *types.NetworkIdentifier
-	fetcher        *fetcher.Fetcher
-	cancel         context.CancelFunc
-	blockStorage   *storage.BlockStorage
-	counterStorage *storage.CounterStorage
-	logger         *logger.Logger
-	workers        []storage.BlockWorker
-
-	concurrency uint64
-}
-
-// New returns a new *StatefulSyncer.
-func New(
-	ctx context.Context,
-	network *types.NetworkIdentifier,
-	fetcher *fetcher.Fetcher,
-	blockStorage *storage.BlockStorage,
-	counterStorage *storage.CounterStorage,
-	logger *logger.Logger,
-	cancel context.CancelFunc,
-	workers []storage.BlockWorker,
-	concurrency uint64,
-) *StatefulSyncer {
-	return &StatefulSyncer{
-		network:        network,
-		fetcher:        fetcher,
-		cancel:         cancel,
-		blockStorage:   blockStorage,
-		counterStorage: counterStorage,
-		workers:        workers,
-		logger:         logger,
-		concurrency:    concurrency,
-	}
-}
-
-// Sync starts a new sync run after properly initializing blockStorage.
-func (s *StatefulSyncer) Sync(ctx context.Context, startIndex int64, endIndex int64) error {
-	s.blockStorage.Initialize(s.workers)
-
-	// Ensure storage is in correct state for starting at index
-	if startIndex != -1 { // attempt to remove blocks from storage (without handling)
-		if err := s.blockStorage.SetNewStartIndex(ctx, startIndex); err != nil {
-			return fmt.Errorf("%w: unable to set new start index", err)
-		}
-	} else { // attempt to load last processed index
-		head, err := s.blockStorage.GetHeadBlockIdentifier(ctx)
-		if err == nil {
-			startIndex = head.Index + 1
-		}
-	}
-
-	// Load in previous blocks into syncer cache to handle reorgs.
-	// If previously processed blocks exist in storage, they are fetched.
-	// Otherwise, none are provided to the cache (the syncer will not attempt
-	// a reorg if the cache is empty).
-	pastBlocks := s.blockStorage.CreateBlockCache(ctx)
-
-	syncer := syncer.New(
-		s.network,
-		s,
-		s,
-		s.cancel,
-		syncer.WithConcurrency(s.concurrency),
-		syncer.WithPastBlocks(pastBlocks),
-	)
-
-	return syncer.Sync(ctx, startIndex, endIndex)
-}
-
-// BlockAdded is called by the syncer when a block is added.
-func (s *StatefulSyncer) BlockAdded(ctx context.Context, block *types.Block) error {
-	err := s.blockStorage.AddBlock(ctx, block)
-	if err != nil {
-		return fmt.Errorf(
-			"%w: unable to add block to storage %s:%d",
-			err,
-			block.BlockIdentifier.Hash,
-			block.BlockIdentifier.Index,
-		)
-	}
-
-	if err := s.logger.AddBlockStream(ctx, block); err != nil {
-		return nil
-	}
-
-	// Update Counters
-	_, _ = s.counterStorage.Update(ctx, storage.BlockCounter, big.NewInt(1))
-	_, _ = s.counterStorage.Update(
-		ctx,
-		storage.TransactionCounter,
-		big.NewInt(int64(len(block.Transactions))),
-	)
-	opCount := int64(0)
-	for _, txn := range block.Transactions {
-		opCount += int64(len(txn.Operations))
-	}
-	_, _ = s.counterStorage.Update(ctx, storage.OperationCounter, big.NewInt(opCount))
-
-	return nil
-}
-
-// BlockRemoved is called by the syncer when a block is removed.
-func (s *StatefulSyncer) BlockRemoved(
-	ctx context.Context,
-	blockIdentifier *types.BlockIdentifier,
-) error {
-	err := s.blockStorage.RemoveBlock(ctx, blockIdentifier)
-	if err != nil {
-		return fmt.Errorf(
-			"%w: unable to remove block from storage %s:%d",
-			err,
-			blockIdentifier.Hash,
-			blockIdentifier.Index,
-		)
-	}
-
-	if err := s.logger.RemoveBlockStream(ctx, blockIdentifier); err != nil {
-		return nil
-	}
-
-	// Update Counters
-	_, _ = s.counterStorage.Update(ctx, storage.OrphanCounter, big.NewInt(1))
-
-	return err
-}
-
-// NetworkStatus is called by the syncer to get the current
-// network status.
-func (s *StatefulSyncer) NetworkStatus(
-	ctx context.Context,
-	network *types.NetworkIdentifier,
-) (*types.NetworkStatusResponse, error) {
-	return s.fetcher.NetworkStatusRetry(ctx, network, nil)
-}
-
-// Block is called by the syncer to fetch a block.
-func (s *StatefulSyncer) Block(
-	ctx context.Context,
-	network *types.NetworkIdentifier,
-	block *types.PartialBlockIdentifier,
-) (*types.Block, error) {
-	return s.fetcher.BlockRetry(ctx, network, block)
-}
diff --git a/pkg/storage/badger_storage.go b/pkg/storage/badger_storage.go
deleted file mode 100644
index 80ab7d7..0000000
--- a/pkg/storage/badger_storage.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"fmt"
-	"sync"
-
-	"github.com/dgraph-io/badger/v2"
-	"github.com/dgraph-io/badger/v2/options"
-)
-
-const (
-	// DefaultCacheSize is 0 MB.
-	DefaultCacheSize = 0
-
-	// DefaultBfCacheSize is 10 MB.
-	DefaultBfCacheSize = 10 << 20
-
-	// DefaultValueLogFileSize is 100 MB.
-	DefaultValueLogFileSize = 100 << 20
-)
-
-// BadgerStorage is a wrapper around Badger DB
-// that implements the Database interface.
-type BadgerStorage struct {
-	db *badger.DB
-
-	writer sync.Mutex
-}
-
-// lowMemoryOptions returns a set of BadgerDB configuration
-// options that significantly reduce memory usage.
-//
-// Inspired by: https://github.com/dgraph-io/badger/issues/1304
-func lowMemoryOptions(dir string) badger.Options {
-	opts := badger.DefaultOptions(dir)
-	opts.Logger = nil
-
-	// Don't load tables into memory.
-	opts.TableLoadingMode = options.FileIO
-	opts.ValueLogLoadingMode = options.FileIO
-
-	// To allow writes at a faster speed, we create a new memtable as soon as
-	// an existing memtable is filled up. This option determines how many
-	// memtables should be kept in memory.
-	opts.NumMemtables = 1
-
-	// This option will have a significant effect the memory. If the level is kept
-	// in-memory, read are faster but the tables will be kept in memory.
-	opts.KeepL0InMemory = false
-
-	// LoadBloomsOnOpen=false will improve the db startup speed
-	opts.LoadBloomsOnOpen = false
-
-	// Bloom filters will be kept in memory if the following option is not set. Each
-	// bloom filter takes up 5 MB of memory. A smaller bf cache would mean that
-	// bloom filters will be evicted quickly from the cache and they will be read from
-	// the disk (which is slow) and inserted into the cache.
-	opts.MaxBfCacheSize = DefaultBfCacheSize
-
-	// Don't cache blocks in memory. All reads should go to disk.
-	opts.MaxCacheSize = DefaultCacheSize
-
-	// Don't keep multiple memtables in memory.
-	opts.NumLevelZeroTables = 1
-	opts.NumLevelZeroTablesStall = 2
-	opts.NumMemtables = 1
-
-	// Limit ValueLogFileSize as log files
-	// must be read into memory during compaction.
-	opts.ValueLogFileSize = DefaultValueLogFileSize
-
-	return opts
-}
-
-// performanceOptions returns a set of BadgerDB configuration
-// options that don't attempt to reduce memory usage (can
-// improve performance).
-func performanceOptions(dir string) badger.Options {
-	opts := badger.DefaultOptions(dir)
-	opts.Logger = nil
-
-	return opts
-}
-
-// NewBadgerStorage creates a new BadgerStorage.
-func NewBadgerStorage(ctx context.Context, dir string, disableMemoryLimit bool) (Database, error) {
-	options := lowMemoryOptions(dir)
-	if disableMemoryLimit {
-		options = performanceOptions(dir)
-	}
-
-	db, err := badger.Open(options)
-	if err != nil {
-		return nil, fmt.Errorf("%w could not open badger database", err)
-	}
-
-	return &BadgerStorage{
-		db: db,
-	}, nil
-}
-
-// Close closes the database to prevent corruption.
-// The caller should defer this in main.
-func (b *BadgerStorage) Close(ctx context.Context) error {
-	if err := b.db.Close(); err != nil {
-		return fmt.Errorf("%w unable to close database", err)
-	}
-
-	return nil
-}
-
-// BadgerTransaction is a wrapper around a Badger
-// DB transaction that implements the DatabaseTransaction
-// interface.
-type BadgerTransaction struct {
-	db  *BadgerStorage
-	txn *badger.Txn
-
-	holdsLock bool
-}
-
-// NewDatabaseTransaction creates a new BadgerTransaction.
-// If the transaction will not modify any values, pass
-// in false for the write parameter (this allows for
-// optimization within the Badger DB).
-func (b *BadgerStorage) NewDatabaseTransaction(
-	ctx context.Context,
-	write bool,
-) DatabaseTransaction {
-	if write {
-		// To avoid database commit conflicts,
-		// we need to lock the writer.
-		//
-		// Because we process blocks serially,
-		// this doesn't lead to much lock contention.
-		b.writer.Lock()
-	}
-
-	return &BadgerTransaction{
-		db:        b,
-		txn:       b.db.NewTransaction(write),
-		holdsLock: write,
-	}
-}
-
-// Commit attempts to commit and discard the transaction.
-func (b *BadgerTransaction) Commit(context.Context) error {
-	err := b.txn.Commit()
-	b.holdsLock = false
-	b.db.writer.Unlock()
-
-	if err != nil {
-		return fmt.Errorf("%w: unable to commit transaction", err)
-	}
-
-	return nil
-}
-
-// Discard discards an open transaction. All transactions
-// must be either discarded or committed.
-func (b *BadgerTransaction) Discard(context.Context) {
-	b.txn.Discard()
-	if b.holdsLock {
-		b.db.writer.Unlock()
-	}
-}
-
-// Set changes the value of the key to the value within a transaction.
-func (b *BadgerTransaction) Set(
-	ctx context.Context,
-	key []byte,
-	value []byte,
-) error {
-	return b.txn.Set(key, value)
-}
-
-// Get accesses the value of the key within a transaction.
-func (b *BadgerTransaction) Get(
-	ctx context.Context,
-	key []byte,
-) (bool, []byte, error) {
-	var value []byte
-	item, err := b.txn.Get(key)
-	if err == badger.ErrKeyNotFound {
-		return false, nil, nil
-	} else if err != nil {
-		return false, nil, err
-	}
-
-	err = item.Value(func(v []byte) error {
-		value = make([]byte, len(v))
-		copy(value, v)
-		return nil
-	})
-	if err != nil {
-		return false, nil, err
-	}
-
-	return true, value, nil
-}
-
-// Delete removes the key and its value within the transaction.
-func (b *BadgerTransaction) Delete(ctx context.Context, key []byte) error {
-	return b.txn.Delete(key)
-}
-
-// Set changes the value of the key to the value in its own transaction.
-func (b *BadgerStorage) Set(
-	ctx context.Context,
-	key []byte,
-	value []byte,
-) error {
-	return b.db.Update(func(txn *badger.Txn) error {
-		return txn.Set(key, value)
-	})
-}
-
-// Get fetches the value of a key in its own transaction.
-func (b *BadgerStorage) Get(
-	ctx context.Context,
-	key []byte,
-) (bool, []byte, error) {
-	var value []byte
-	err := b.db.View(func(txn *badger.Txn) error {
-		item, err := txn.Get(key)
-		if err != nil {
-			return err
-		}
-
-		err = item.Value(func(v []byte) error {
-			value = make([]byte, len(v))
-			copy(value, v)
-			return nil
-		})
-		if err != nil {
-			return err
-		}
-
-		return nil
-	})
-	if err == badger.ErrKeyNotFound {
-		return false, nil, nil
-	} else if err != nil {
-		return false, nil, err
-	}
-
-	return true, value, nil
-}
-
-// Scan fetches all items at a given prefix. This is typically
-// used to get all items in a namespace.
-func (b *BadgerStorage) Scan(
-	ctx context.Context,
-	prefix []byte,
-) ([][]byte, error) {
-	values := [][]byte{}
-	err := b.db.View(func(txn *badger.Txn) error {
-		opts := badger.DefaultIteratorOptions
-		opts.PrefetchValues = false
-
-		it := txn.NewIterator(opts)
-		defer it.Close()
-		for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
-			item := it.Item()
-			key := item.Key()
-
-			// There is some strange issue with BadgerDB where the value returned
-			// on the item is sometimes corrupted (which causes decoding errors).
-			// Until this is fixed, the workaround I found was to fetch the value
-			// in a separate transaction with the key from the scan. Instead of
-			// spending more time investigating these issues, we will work
-			// on prioritizing a switch to a different backend.
-			exists, v, err := b.Get(ctx, key)
-			if err != nil {
-				return fmt.Errorf("%w unable to get key %s", err, string(key))
-			}
-			if !exists {
-				return fmt.Errorf("key %s does not exist", string(key))
-			}
-
-			values = append(values, v)
-		}
-		return nil
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	return values, nil
-}
diff --git a/pkg/storage/balance_storage.go b/pkg/storage/balance_storage.go
deleted file mode 100644
index 1546dec..0000000
--- a/pkg/storage/balance_storage.go
+++ /dev/null
@@ -1,524 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"log"
-	"math/big"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-sdk-go/asserter"
-	"github.com/coinbase/rosetta-sdk-go/parser"
-	"github.com/coinbase/rosetta-sdk-go/reconciler"
-	"github.com/coinbase/rosetta-sdk-go/types"
-)
-
-var _ BlockWorker = (*BalanceStorage)(nil)
-
-const (
-	// balanceNamespace is prepended to any stored balance.
-	balanceNamespace = "balance"
-)
-
-var (
-	// ErrAccountNotFound is returned when an account
-	// is not found in BalanceStorage.
-	ErrAccountNotFound = errors.New("account not found")
-
-	// ErrNegativeBalance is returned when an account
-	// balance goes negative as the result of an operation.
-	ErrNegativeBalance = errors.New("negative balance")
-)
-
-/*
-  Key Construction
-*/
-
-// GetBalanceKey returns a deterministic hash of an types.Account + types.Currency.
-func GetBalanceKey(account *types.AccountIdentifier, currency *types.Currency) []byte {
-	return []byte(
-		fmt.Sprintf("%s/%s/%s", balanceNamespace, types.Hash(account), types.Hash(currency)),
-	)
-}
-
-// BalanceStorageHandler is invoked after balance changes are committed to the database.
-type BalanceStorageHandler interface {
-	BlockAdded(ctx context.Context, block *types.Block, changes []*parser.BalanceChange) error
-	BlockRemoved(ctx context.Context, block *types.Block, changes []*parser.BalanceChange) error
-}
-
-// BalanceStorageHelper functions are used by BalanceStorage to process balances. Defining an
-// interface allows the client to determine if they wish to query the node for
-// certain information or use another datastore.
-type BalanceStorageHelper interface {
-	AccountBalance(
-		ctx context.Context,
-		account *types.AccountIdentifier,
-		currency *types.Currency,
-		block *types.BlockIdentifier,
-	) (*types.Amount, error)
-
-	ExemptFunc() parser.ExemptOperation
-	Asserter() *asserter.Asserter
-}
-
-// BalanceStorage implements block specific storage methods
-// on top of a Database and DatabaseTransaction interface.
-type BalanceStorage struct {
-	db      Database
-	helper  BalanceStorageHelper
-	handler BalanceStorageHandler
-
-	parser *parser.Parser
-}
-
-// NewBalanceStorage returns a new BalanceStorage.
-func NewBalanceStorage(
-	db Database,
-) *BalanceStorage {
-	return &BalanceStorage{
-		db: db,
-	}
-}
-
-// Initialize adds a BalanceStorageHelper and BalanceStorageHandler to BalanceStorage.
-// This must be called prior to syncing!
-func (b *BalanceStorage) Initialize(helper BalanceStorageHelper, handler BalanceStorageHandler) {
-	b.helper = helper
-	b.handler = handler
-	b.parser = parser.New(helper.Asserter(), helper.ExemptFunc())
-}
-
-// AddingBlock is called by BlockStorage when adding a block to storage.
-func (b *BalanceStorage) AddingBlock(
-	ctx context.Context,
-	block *types.Block,
-	transaction DatabaseTransaction,
-) (CommitWorker, error) {
-	changes, err := b.parser.BalanceChanges(ctx, block, false)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to calculate balance changes", err)
-	}
-
-	for _, change := range changes {
-		if err := b.UpdateBalance(ctx, transaction, change, block.ParentBlockIdentifier); err != nil {
-			return nil, err
-		}
-	}
-
-	return func(ctx context.Context) error {
-		return b.handler.BlockAdded(ctx, block, changes)
-	}, nil
-}
-
-// RemovingBlock is called by BlockStorage when removing a block from storage.
-func (b *BalanceStorage) RemovingBlock(
-	ctx context.Context,
-	block *types.Block,
-	transaction DatabaseTransaction,
-) (CommitWorker, error) {
-	changes, err := b.parser.BalanceChanges(ctx, block, true)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to calculate balance changes", err)
-	}
-
-	for _, change := range changes {
-		if err := b.UpdateBalance(ctx, transaction, change, block.BlockIdentifier); err != nil {
-			return nil, err
-		}
-	}
-
-	return func(ctx context.Context) error {
-		return b.handler.BlockRemoved(ctx, block, changes)
-	}, nil
-}
-
-type balanceEntry struct {
-	Account        *types.AccountIdentifier `json:"account"`
-	Amount         *types.Amount            `json:"amount"`
-	Block          *types.BlockIdentifier   `json:"block"`
-	LastReconciled *types.BlockIdentifier   `json:"last_reconciled"`
-}
-
-// SetBalance allows a client to set the balance of an account in a database
-// transaction. This is particularly useful for bootstrapping balances.
-func (b *BalanceStorage) SetBalance(
-	ctx context.Context,
-	dbTransaction DatabaseTransaction,
-	account *types.AccountIdentifier,
-	amount *types.Amount,
-	block *types.BlockIdentifier,
-) error {
-	key := GetBalanceKey(account, amount.Currency)
-
-	serialBal, err := encode(&balanceEntry{
-		Account: account,
-		Amount:  amount,
-		Block:   block,
-	})
-	if err != nil {
-		return err
-	}
-
-	if err := dbTransaction.Set(ctx, key, serialBal); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Reconciled updates the LastReconciled field on a particular
-// balance. Tracking reconciliation coverage is an important
-// end condition.
-func (b *BalanceStorage) Reconciled(
-	ctx context.Context,
-	account *types.AccountIdentifier,
-	currency *types.Currency,
-	block *types.BlockIdentifier,
-) error {
-	dbTransaction := b.db.NewDatabaseTransaction(ctx, true)
-	defer dbTransaction.Discard(ctx)
-
-	key := GetBalanceKey(account, currency)
-	exists, balance, err := dbTransaction.Get(ctx, key)
-	if err != nil {
-		return fmt.Errorf(
-			"%w: unable to get balance entry for account %s:%s",
-			err,
-			types.PrettyPrintStruct(account),
-			types.PrettyPrintStruct(currency),
-		)
-	}
-
-	if !exists {
-		return fmt.Errorf(
-			"balance entry is missing for account %s:%s",
-			types.PrettyPrintStruct(account),
-			types.PrettyPrintStruct(currency),
-		)
-	}
-
-	var bal balanceEntry
-	if err := decode(balance, &bal); err != nil {
-		return fmt.Errorf("%w: unable to decode balance entry", err)
-	}
-
-	bal.LastReconciled = block
-
-	serialBal, err := encode(bal)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encod balance entry", err)
-	}
-
-	if err := dbTransaction.Set(ctx, key, serialBal); err != nil {
-		return fmt.Errorf("%w: unable to set balance entry", err)
-	}
-
-	if err := dbTransaction.Commit(ctx); err != nil {
-		return fmt.Errorf("%w: unable to commit last reconciliation update", err)
-	}
-
-	return nil
-}
-
-// ReconciliationCoverage returns the proportion of accounts [0.0, 1.0] that
-// have been reconciled at an index >= to a minimumIndex.
-func (b *BalanceStorage) ReconciliationCoverage(
-	ctx context.Context,
-	minimumIndex int64,
-) (float64, error) {
-	balances, err := b.getAllBalanceEntries(ctx)
-	if err != nil {
-		return -1, fmt.Errorf("%w: unable to get all balance entries", err)
-	}
-
-	if len(balances) == 0 {
-		return 0, nil
-	}
-
-	validCoverage := 0
-	for _, b := range balances {
-		if b.LastReconciled == nil {
-			continue
-		}
-
-		if b.LastReconciled.Index >= minimumIndex {
-			validCoverage++
-		}
-	}
-
-	return float64(validCoverage) / float64(len(balances)), nil
-}
-
-// UpdateBalance updates a types.AccountIdentifer
-// by a types.Amount and sets the account's most
-// recent accessed block.
-func (b *BalanceStorage) UpdateBalance(
-	ctx context.Context,
-	dbTransaction DatabaseTransaction,
-	change *parser.BalanceChange,
-	parentBlock *types.BlockIdentifier,
-) error {
-	if change.Currency == nil {
-		return errors.New("invalid currency")
-	}
-
-	key := GetBalanceKey(change.Account, change.Currency)
-	// Get existing balance on key
-	exists, balance, err := dbTransaction.Get(ctx, key)
-	if err != nil {
-		return err
-	}
-
-	var existingValue string
-	switch {
-	case exists:
-		// This could happen if balances are bootstrapped and should not be
-		// overridden.
-		var bal balanceEntry
-		err := decode(balance, &bal)
-		if err != nil {
-			return err
-		}
-
-		existingValue = bal.Amount.Value
-	case parentBlock != nil && change.Block.Hash == parentBlock.Hash:
-		// Don't attempt to use the helper if we are going to query the same
-		// block we are processing (causes the duplicate issue).
-		existingValue = "0"
-	default:
-		// Use helper to fetch existing balance.
-		amount, err := b.helper.AccountBalance(ctx, change.Account, change.Currency, parentBlock)
-		if err != nil {
-			return fmt.Errorf("%w: unable to get previous account balance", err)
-		}
-
-		existingValue = amount.Value
-	}
-
-	newVal, err := types.AddValues(change.Difference, existingValue)
-	if err != nil {
-		return err
-	}
-
-	bigNewVal, ok := new(big.Int).SetString(newVal, 10)
-	if !ok {
-		return fmt.Errorf("%s is not an integer", newVal)
-	}
-
-	if bigNewVal.Sign() == -1 {
-		return fmt.Errorf(
-			"%w %s:%+v for %+v at %+v",
-			ErrNegativeBalance,
-			newVal,
-			change.Currency,
-			change.Account,
-			change.Block,
-		)
-	}
-
-	serialBal, err := encode(&balanceEntry{
-		Account: change.Account,
-		Amount: &types.Amount{
-			Value:    newVal,
-			Currency: change.Currency,
-		},
-		Block: change.Block,
-	})
-	if err != nil {
-		return err
-	}
-
-	return dbTransaction.Set(ctx, key, serialBal)
-}
-
-// GetBalance returns all the balances of a types.AccountIdentifier
-// and the types.BlockIdentifier it was last updated at.
-func (b *BalanceStorage) GetBalance(
-	ctx context.Context,
-	account *types.AccountIdentifier,
-	currency *types.Currency,
-	headBlock *types.BlockIdentifier,
-) (*types.Amount, *types.BlockIdentifier, error) {
-	transaction := b.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	key := GetBalanceKey(account, currency)
-	exists, bal, err := transaction.Get(ctx, key)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	// When beginning syncing from an arbitrary height, an account may
-	// not yet have a cached balance when requested. If this is the case,
-	// we fetch the balance from the node for the given height and persist
-	// it. This is particularly useful when monitoring interesting accounts.
-	if !exists {
-		amount, err := b.helper.AccountBalance(ctx, account, currency, headBlock)
-		if err != nil {
-			return nil, nil, fmt.Errorf("%w: unable to get account balance from helper", err)
-		}
-
-		writeTransaction := b.db.NewDatabaseTransaction(ctx, true)
-		defer writeTransaction.Discard(ctx)
-		err = b.SetBalance(
-			ctx,
-			writeTransaction,
-			account,
-			amount,
-			headBlock,
-		)
-		if err != nil {
-			return nil, nil, fmt.Errorf("%w: unable to set account balance", err)
-		}
-
-		err = writeTransaction.Commit(ctx)
-		if err != nil {
-			return nil, nil, fmt.Errorf("%w: unable to commit account balance transaction", err)
-		}
-
-		return amount, headBlock, nil
-	}
-
-	var popBal balanceEntry
-	err = decode(bal, &popBal)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	return popBal.Amount, popBal.Block, nil
-}
-
-// BootstrapBalance represents a balance of
-// a *types.AccountIdentifier and a *types.Currency in the
-// genesis block.
-type BootstrapBalance struct {
-	Account  *types.AccountIdentifier `json:"account_identifier,omitempty"`
-	Currency *types.Currency          `json:"currency,omitempty"`
-	Value    string                   `json:"value,omitempty"`
-}
-
-// BootstrapBalances is utilized to set the balance of
-// any number of AccountIdentifiers at the genesis blocks.
-// This is particularly useful for setting the value of
-// accounts that received an allocation in the genesis block.
-func (b *BalanceStorage) BootstrapBalances(
-	ctx context.Context,
-	bootstrapBalancesFile string,
-	genesisBlockIdentifier *types.BlockIdentifier,
-) error {
-	// Read bootstrap file
-	balances := []*BootstrapBalance{}
-	if err := utils.LoadAndParse(bootstrapBalancesFile, &balances); err != nil {
-		return err
-	}
-
-	// Update balances in database
-	dbTransaction := b.db.NewDatabaseTransaction(ctx, true)
-	defer dbTransaction.Discard(ctx)
-
-	for _, balance := range balances {
-		// Ensure change.Difference is valid
-		amountValue, ok := new(big.Int).SetString(balance.Value, 10)
-		if !ok {
-			return fmt.Errorf("%s is not an integer", balance.Value)
-		}
-
-		if amountValue.Sign() < 1 {
-			return fmt.Errorf("cannot bootstrap zero or negative balance %s", amountValue.String())
-		}
-
-		log.Printf(
-			"Setting account %s balance to %s %+v\n",
-			balance.Account.Address,
-			balance.Value,
-			balance.Currency,
-		)
-
-		err := b.SetBalance(
-			ctx,
-			dbTransaction,
-			balance.Account,
-			&types.Amount{
-				Value:    balance.Value,
-				Currency: balance.Currency,
-			},
-			genesisBlockIdentifier,
-		)
-		if err != nil {
-			return err
-		}
-	}
-
-	err := dbTransaction.Commit(ctx)
-	if err != nil {
-		return err
-	}
-
-	log.Printf("%d Balances Bootstrapped\n", len(balances))
-	return nil
-}
-
-func (b *BalanceStorage) getAllBalanceEntries(ctx context.Context) ([]*balanceEntry, error) {
-	rawBalances, err := b.db.Scan(ctx, []byte(balanceNamespace))
-	if err != nil {
-		return nil, fmt.Errorf("%w: database scan failed", err)
-	}
-
-	balances := make([]*balanceEntry, len(rawBalances))
-	for i, rawBalance := range rawBalances {
-		var deserialBal balanceEntry
-		err := decode(rawBalance, &deserialBal)
-		if err != nil {
-			return nil, fmt.Errorf(
-				"%w: unable to parse balance entry for %s",
-				err,
-				string(rawBalance),
-			)
-		}
-
-		balances[i] = &deserialBal
-	}
-
-	return balances, nil
-}
-
-// GetAllAccountCurrency scans the db for all balances and returns a slice
-// of reconciler.AccountCurrency. This is useful for bootstrapping the reconciler
-// after restart.
-func (b *BalanceStorage) GetAllAccountCurrency(
-	ctx context.Context,
-) ([]*reconciler.AccountCurrency, error) {
-	log.Println("Loading previously seen accounts (this could take a while)...")
-
-	balances, err := b.getAllBalanceEntries(ctx)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get all balance entries", err)
-	}
-
-	accounts := make([]*reconciler.AccountCurrency, len(balances))
-	for i, balance := range balances {
-		accounts[i] = &reconciler.AccountCurrency{
-			Account:  balance.Account,
-			Currency: balance.Amount.Currency,
-		}
-	}
-
-	return accounts, nil
-}
diff --git a/pkg/storage/block_storage.go b/pkg/storage/block_storage.go
deleted file mode 100644
index f5b0084..0000000
--- a/pkg/storage/block_storage.go
+++ /dev/null
@@ -1,618 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"log"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-sdk-go/syncer"
-	"github.com/coinbase/rosetta-sdk-go/types"
-)
-
-const (
-	// headBlockKey is used to lookup the head block identifier.
-	// The head block is the block with the largest index that is
-	// not orphaned.
-	headBlockKey = "head-block"
-
-	// blockNamespace is prepended to any stored block.
-	blockNamespace = "block"
-
-	// blockIndexNamespace is prepended to any stored block index.
-	blockIndexNamespace = "block-index"
-
-	// transactionHashNamespace is prepended to any stored
-	// transaction hash.
-	transactionHashNamespace = "transaction-hash"
-)
-
-var (
-	// ErrHeadBlockNotFound is returned when there is no
-	// head block found in BlockStorage.
-	ErrHeadBlockNotFound = errors.New("head block not found")
-
-	// ErrBlockNotFound is returned when a block is not
-	// found in BlockStorage.
-	ErrBlockNotFound = errors.New("block not found")
-
-	// ErrDuplicateKey is returned when a key
-	// cannot be stored because it is a duplicate.
-	ErrDuplicateKey = errors.New("duplicate key")
-
-	// ErrDuplicateTransactionHash is returned when a transaction
-	// hash cannot be stored because it is a duplicate.
-	ErrDuplicateTransactionHash = errors.New("duplicate transaction hash")
-)
-
-func getHeadBlockKey() []byte {
-	return []byte(headBlockKey)
-}
-
-func getBlockHashKey(hash string) []byte {
-	return []byte(fmt.Sprintf("%s/%s", blockNamespace, hash))
-}
-
-func getBlockIndexKey(index int64) []byte {
-	return []byte(fmt.Sprintf("%s/%d", blockIndexNamespace, index))
-}
-
-func getTransactionHashKey(transactionIdentifier *types.TransactionIdentifier) []byte {
-	return []byte(fmt.Sprintf("%s/%s", transactionHashNamespace, transactionIdentifier.Hash))
-}
-
-// BlockWorker is an interface that allows for work
-// to be done while a block is added/removed from storage
-// in the same database transaction as the change.
-type BlockWorker interface {
-	AddingBlock(context.Context, *types.Block, DatabaseTransaction) (CommitWorker, error)
-	RemovingBlock(context.Context, *types.Block, DatabaseTransaction) (CommitWorker, error)
-}
-
-// CommitWorker is returned by a BlockWorker to be called after
-// changes have been committed. It is common to put logging activities
-// in here (that shouldn't be printed until the block is committed).
-type CommitWorker func(context.Context) error
-
-// BlockStorage implements block specific storage methods
-// on top of a Database and DatabaseTransaction interface.
-type BlockStorage struct {
-	db Database
-
-	workers []BlockWorker
-}
-
-// NewBlockStorage returns a new BlockStorage.
-func NewBlockStorage(
-	db Database,
-) *BlockStorage {
-	return &BlockStorage{
-		db: db,
-	}
-}
-
-// Initialize adds a []BlockWorker to BlockStorage. Usually
-// all block workers are not created by the time block storage
-// is constructed.
-//
-// This must be called prior to syncing!
-func (b *BlockStorage) Initialize(workers []BlockWorker) {
-	b.workers = workers
-}
-
-// GetHeadBlockIdentifier returns the head block identifier,
-// if it exists.
-func (b *BlockStorage) GetHeadBlockIdentifier(
-	ctx context.Context,
-) (*types.BlockIdentifier, error) {
-	transaction := b.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	return b.GetHeadBlockIdentifierTransactional(ctx, transaction)
-}
-
-// GetHeadBlockIdentifierTransactional returns the head block identifier,
-// if it exists, in the context of a DatabaseTransaction.
-func (b *BlockStorage) GetHeadBlockIdentifierTransactional(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-) (*types.BlockIdentifier, error) {
-	exists, block, err := transaction.Get(ctx, getHeadBlockKey())
-	if err != nil {
-		return nil, err
-	}
-
-	if !exists {
-		return nil, ErrHeadBlockNotFound
-	}
-
-	var blockIdentifier types.BlockIdentifier
-	err = decode(block, &blockIdentifier)
-	if err != nil {
-		return nil, err
-	}
-
-	return &blockIdentifier, nil
-}
-
-// StoreHeadBlockIdentifier stores a block identifier
-// or returns an error.
-func (b *BlockStorage) StoreHeadBlockIdentifier(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	blockIdentifier *types.BlockIdentifier,
-) error {
-	buf, err := encode(blockIdentifier)
-	if err != nil {
-		return err
-	}
-
-	return transaction.Set(ctx, getHeadBlockKey(), buf)
-}
-
-// GetBlock returns a block, if it exists.
-func (b *BlockStorage) GetBlock(
-	ctx context.Context,
-	blockIdentifier *types.PartialBlockIdentifier,
-) (*types.Block, error) {
-	transaction := b.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	var exists bool
-	var block []byte
-	var err error
-	switch {
-	case blockIdentifier == nil || (blockIdentifier.Hash == nil && blockIdentifier.Index == nil):
-		// Get current block when no blockIdentifier is provided
-		var head *types.BlockIdentifier
-		head, err = b.GetHeadBlockIdentifierTransactional(ctx, transaction)
-		if err != nil {
-			return nil, fmt.Errorf("%w: cannot get head block identifier", err)
-		}
-
-		exists, block, err = transaction.Get(ctx, getBlockHashKey(head.Hash))
-	case blockIdentifier.Hash != nil:
-		// Get block by hash if provided
-		exists, block, err = transaction.Get(ctx, getBlockHashKey(*blockIdentifier.Hash))
-	default:
-		// Get block by index if hash not provided
-		var blockKey []byte
-		exists, blockKey, err = transaction.Get(ctx, getBlockIndexKey(*blockIdentifier.Index))
-		if exists {
-			exists, block, err = transaction.Get(ctx, blockKey)
-		}
-	}
-
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get block", err)
-	}
-
-	if !exists {
-		return nil, fmt.Errorf("%w: %+v", ErrBlockNotFound, blockIdentifier)
-	}
-
-	var rosettaBlock types.Block
-	err = decode(block, &rosettaBlock)
-	if err != nil {
-		return nil, err
-	}
-
-	return &rosettaBlock, nil
-}
-
-func (b *BlockStorage) storeBlock(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	block *types.Block,
-) error {
-	buf, err := encode(block)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode block", err)
-	}
-
-	if err := b.storeUniqueKey(ctx, transaction, getBlockHashKey(block.BlockIdentifier.Hash), buf); err != nil {
-		return fmt.Errorf("%w: unable to store block", err)
-	}
-
-	if err := b.storeUniqueKey(
-		ctx,
-		transaction,
-		getBlockIndexKey(block.BlockIdentifier.Index),
-		getBlockHashKey(block.BlockIdentifier.Hash),
-	); err != nil {
-		return fmt.Errorf("%w: unable to store block index", err)
-	}
-
-	if err := b.StoreHeadBlockIdentifier(ctx, transaction, block.BlockIdentifier); err != nil {
-		return fmt.Errorf("%w: unable to update head block identifier", err)
-	}
-
-	return nil
-}
-
-// AddBlock stores a block or returns an error.
-func (b *BlockStorage) AddBlock(
-	ctx context.Context,
-	block *types.Block,
-) error {
-	transaction := b.db.NewDatabaseTransaction(ctx, true)
-	defer transaction.Discard(ctx)
-
-	// Store block
-	err := b.storeBlock(ctx, transaction, block)
-	if err != nil {
-		return fmt.Errorf("%w: unable to store block", err)
-	}
-
-	// Store all transaction hashes
-	for _, txn := range block.Transactions {
-		err = b.storeTransactionHash(
-			ctx,
-			transaction,
-			block.BlockIdentifier,
-			txn.TransactionIdentifier,
-		)
-		if err != nil {
-			return fmt.Errorf("%w: unable to store transaction hash", err)
-		}
-	}
-
-	return b.callWorkersAndCommit(ctx, block, transaction, true)
-}
-
-func (b *BlockStorage) deleteBlock(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	block *types.Block,
-) error {
-	blockIdentifier := block.BlockIdentifier
-	if err := transaction.Delete(ctx, getBlockHashKey(blockIdentifier.Hash)); err != nil {
-		return fmt.Errorf("%w: unable to delete block", err)
-	}
-
-	if err := transaction.Delete(ctx, getBlockIndexKey(blockIdentifier.Index)); err != nil {
-		return fmt.Errorf("%w: unable to delete block index", err)
-	}
-
-	if err := b.StoreHeadBlockIdentifier(ctx, transaction, block.ParentBlockIdentifier); err != nil {
-		return fmt.Errorf("%w: unable to update head block identifier", err)
-	}
-
-	return nil
-}
-
-// RemoveBlock removes a block or returns an error.
-// RemoveBlock also removes the block hash and all
-// its transaction hashes to not break duplicate
-// detection. This is called within a re-org.
-func (b *BlockStorage) RemoveBlock(
-	ctx context.Context,
-	blockIdentifier *types.BlockIdentifier,
-) error {
-	block, err := b.GetBlock(ctx, types.ConstructPartialBlockIdentifier(blockIdentifier))
-	if err != nil {
-		return err
-	}
-
-	transaction := b.db.NewDatabaseTransaction(ctx, true)
-	defer transaction.Discard(ctx)
-
-	// Remove all transaction hashes
-	for _, txn := range block.Transactions {
-		err = b.removeTransactionHash(ctx, transaction, blockIdentifier, txn.TransactionIdentifier)
-		if err != nil {
-			return err
-		}
-	}
-
-	// Delete block
-	if err := b.deleteBlock(ctx, transaction, block); err != nil {
-		return fmt.Errorf("%w: unable to delete block", err)
-	}
-
-	return b.callWorkersAndCommit(ctx, block, transaction, false)
-}
-
-func (b *BlockStorage) callWorkersAndCommit(
-	ctx context.Context,
-	block *types.Block,
-	txn DatabaseTransaction,
-	adding bool,
-) error {
-	commitWorkers := make([]CommitWorker, len(b.workers))
-	for i, w := range b.workers {
-		var cw CommitWorker
-		var err error
-		if adding {
-			cw, err = w.AddingBlock(ctx, block, txn)
-		} else {
-			cw, err = w.RemovingBlock(ctx, block, txn)
-		}
-		if err != nil {
-			return err
-		}
-
-		commitWorkers[i] = cw
-	}
-
-	if err := txn.Commit(ctx); err != nil {
-		return err
-	}
-
-	for _, cw := range commitWorkers {
-		if cw == nil {
-			continue
-		}
-
-		if err := cw(ctx); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// SetNewStartIndex attempts to remove all blocks
-// greater than or equal to the startIndex.
-func (b *BlockStorage) SetNewStartIndex(
-	ctx context.Context,
-	startIndex int64,
-) error {
-	head, err := b.GetHeadBlockIdentifier(ctx)
-	if errors.Is(err, ErrHeadBlockNotFound) {
-		return nil
-	}
-	if err != nil {
-		return err
-	}
-
-	if head.Index < startIndex {
-		return fmt.Errorf(
-			"last processed block %d is less than start index %d",
-			head.Index,
-			startIndex,
-		)
-	}
-
-	currBlock := head
-	for currBlock.Index >= startIndex {
-		log.Printf("Removing block %+v\n", currBlock)
-		block, err := b.GetBlock(ctx, types.ConstructPartialBlockIdentifier(currBlock))
-		if err != nil {
-			return err
-		}
-
-		if err := b.RemoveBlock(ctx, block.BlockIdentifier); err != nil {
-			return err
-		}
-
-		currBlock = block.ParentBlockIdentifier
-	}
-
-	return nil
-}
-
-// CreateBlockCache populates a slice of blocks with the most recent
-// ones in storage.
-func (b *BlockStorage) CreateBlockCache(ctx context.Context) []*types.BlockIdentifier {
-	cache := []*types.BlockIdentifier{}
-	head, err := b.GetHeadBlockIdentifier(ctx)
-	if err != nil {
-		return cache
-	}
-
-	for len(cache) < syncer.PastBlockSize {
-		block, err := b.GetBlock(ctx, types.ConstructPartialBlockIdentifier(head))
-		if err != nil {
-			return cache
-		}
-
-		log.Printf("Added %+v to cache\n", block.BlockIdentifier)
-
-		cache = append([]*types.BlockIdentifier{block.BlockIdentifier}, cache...)
-		head = block.ParentBlockIdentifier
-
-		// We should break if we have reached genesis.
-		if head.Index == block.BlockIdentifier.Index {
-			break
-		}
-	}
-
-	return cache
-}
-
-func (b *BlockStorage) storeUniqueKey(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	key []byte,
-	value []byte,
-) error {
-	exists, _, err := transaction.Get(ctx, key)
-	if err != nil {
-		return err
-	}
-
-	if exists {
-		return fmt.Errorf("%w: duplicate key %s found", ErrDuplicateKey, string(key))
-	}
-
-	return transaction.Set(ctx, key, value)
-}
-
-func (b *BlockStorage) storeTransactionHash(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	blockIdentifier *types.BlockIdentifier,
-	transactionIdentifier *types.TransactionIdentifier,
-) error {
-	hashKey := getTransactionHashKey(transactionIdentifier)
-	exists, val, err := transaction.Get(ctx, hashKey)
-	if err != nil {
-		return err
-	}
-
-	var blocks map[string]int64
-	if !exists {
-		blocks = make(map[string]int64)
-	} else {
-		if err := decode(val, &blocks); err != nil {
-			return fmt.Errorf("%w: could not decode transaction hash contents", err)
-		}
-
-		if _, exists := blocks[blockIdentifier.Hash]; exists {
-			return fmt.Errorf(
-				"%w: duplicate transaction %s found in block %s:%d",
-				ErrDuplicateTransactionHash,
-				transactionIdentifier.Hash,
-				blockIdentifier.Hash,
-				blockIdentifier.Index,
-			)
-		}
-	}
-	blocks[blockIdentifier.Hash] = blockIdentifier.Index
-
-	encodedResult, err := encode(blocks)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode transaction data", err)
-	}
-
-	return transaction.Set(ctx, hashKey, encodedResult)
-}
-
-func (b *BlockStorage) removeTransactionHash(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	blockIdentifier *types.BlockIdentifier,
-	transactionIdentifier *types.TransactionIdentifier,
-) error {
-	hashKey := getTransactionHashKey(transactionIdentifier)
-	exists, val, err := transaction.Get(ctx, hashKey)
-	if err != nil {
-		return err
-	}
-
-	if !exists {
-		return fmt.Errorf("could not remove transaction %s", transactionIdentifier.Hash)
-	}
-
-	var blocks map[string]int64
-	if err := decode(val, &blocks); err != nil {
-		return fmt.Errorf("%w: could not decode transaction hash contents", err)
-	}
-
-	if _, exists := blocks[blockIdentifier.Hash]; !exists {
-		return fmt.Errorf("saved blocks at transaction does not contain %s", blockIdentifier.Hash)
-	}
-
-	delete(blocks, blockIdentifier.Hash)
-
-	if len(blocks) == 0 {
-		return transaction.Delete(ctx, hashKey)
-	}
-
-	encodedResult, err := encode(blocks)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode transaction data", err)
-	}
-
-	return transaction.Set(ctx, hashKey, encodedResult)
-}
-
-// FindTransaction returns the most recent *types.BlockIdentifier containing the
-// transaction and the transaction.
-func (b *BlockStorage) FindTransaction(
-	ctx context.Context,
-	transactionIdentifier *types.TransactionIdentifier,
-	txn DatabaseTransaction,
-) (*types.BlockIdentifier, *types.Transaction, error) {
-	txExists, tx, err := txn.Get(ctx, getTransactionHashKey(transactionIdentifier))
-	if err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to query database for transaction", err)
-	}
-
-	if !txExists {
-		return nil, nil, nil
-	}
-
-	var blocks map[string]int64
-	if err := decode(tx, &blocks); err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to decode block data for transaction", err)
-	}
-
-	var newestBlock *types.BlockIdentifier
-	for hash, index := range blocks {
-		b := &types.BlockIdentifier{Hash: hash, Index: index}
-		if newestBlock == nil || b.Index > newestBlock.Index {
-			newestBlock = b
-		}
-	}
-
-	blockExists, block, err := txn.Get(ctx, getBlockHashKey(newestBlock.Hash))
-	if err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to query database for block", err)
-	}
-
-	if !blockExists {
-		return nil, nil, ErrBlockNotFound
-	}
-
-	var parsedBlock *types.Block
-	err = decode(block, &parsedBlock)
-	if err != nil {
-		return nil, nil, fmt.Errorf("%w: could not decode block", err)
-	}
-
-	for _, tx := range parsedBlock.Transactions {
-		if types.Hash(tx.TransactionIdentifier) == types.Hash(transactionIdentifier) {
-			return newestBlock, tx, nil
-		}
-	}
-
-	return nil, nil, fmt.Errorf(
-		"unable to find transaction %s in expected block %s:%d",
-		transactionIdentifier.Hash,
-		newestBlock.Hash,
-		newestBlock.Index,
-	)
-}
-
-// AtTip returns a boolean indicating if we
-// are at tip (provided some acceptable
-// tip delay).
-func (b *BlockStorage) AtTip(
-	ctx context.Context,
-	tipDelay int64,
-) (bool, *types.BlockIdentifier, error) {
-	block, err := b.GetBlock(ctx, nil)
-	if errors.Is(err, ErrHeadBlockNotFound) {
-		return false, nil, nil
-	}
-
-	if err != nil {
-		return false, nil, fmt.Errorf("%w: unable to get head block", err)
-	}
-
-	currentTime := utils.Milliseconds()
-	tipCutoff := currentTime - (tipDelay * utils.MillisecondsInSecond)
-	if block.Timestamp < tipCutoff {
-		return false, nil, nil
-	}
-
-	return true, block.BlockIdentifier, nil
-}
diff --git a/pkg/storage/broadcast_storage.go b/pkg/storage/broadcast_storage.go
deleted file mode 100644
index d153200..0000000
--- a/pkg/storage/broadcast_storage.go
+++ /dev/null
@@ -1,545 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"fmt"
-	"log"
-	"sync"
-
-	"github.com/coinbase/rosetta-sdk-go/types"
-)
-
-var _ BlockWorker = (*BroadcastStorage)(nil)
-
-const (
-	transactionBroadcastNamespace = "transaction-broadcast"
-
-	// depthOffset is used for adjusting depth checks because
-	// depth is "indexed by 1". Meaning, if a transaction is in
-	// tip it has depth 1.
-	depthOffset = 1
-)
-
-func getBroadcastKey(transactionIdentifier *types.TransactionIdentifier) []byte {
-	return []byte(
-		fmt.Sprintf("%s/%s", transactionBroadcastNamespace, transactionIdentifier.Hash),
-	)
-}
-
-// BroadcastStorage implements storage methods for managing
-// transaction broadcast.
-type BroadcastStorage struct {
-	db      Database
-	helper  BroadcastStorageHelper
-	handler BroadcastStorageHandler
-
-	confirmationDepth   int64
-	staleDepth          int64
-	broadcastLimit      int
-	tipDelay            int64
-	broadcastBehindTip  bool
-	blockBroadcastLimit int
-
-	// Running BroadcastAll concurrently
-	// could cause corruption.
-	broadcastAllMutex sync.Mutex
-}
-
-// BroadcastStorageHelper is used by BroadcastStorage to submit transactions
-// and find said transaction in blocks on-chain.
-type BroadcastStorageHelper interface {
-	// CurrentBlockIdentifier is called before transaction broadcast and is used
-	// to determine if a transaction broadcast is stale.
-	CurrentBlockIdentifier(
-		context.Context,
-	) (*types.BlockIdentifier, error) // used to determine if should rebroadcast
-
-	// AtTip is called before transaction broadcast to determine if we are at tip.
-	AtTip(
-		context.Context,
-		int64,
-	) (bool, error)
-
-	// FindTransaction looks for the provided TransactionIdentifier in processed
-	// blocks and returns the block identifier containing the most recent sighting
-	// and the transaction seen in that block.
-	FindTransaction(
-		context.Context,
-		*types.TransactionIdentifier,
-		DatabaseTransaction,
-	) (*types.BlockIdentifier, *types.Transaction, error) // used to confirm
-
-	// BroadcastTransaction broadcasts a transaction to a Rosetta implementation
-	// and returns the *types.TransactionIdentifier returned by the implementation.
-	BroadcastTransaction(
-		context.Context,
-		string,
-	) (*types.TransactionIdentifier, error) // handle initial broadcast + confirm matches provided + rebroadcast if stale
-}
-
-// BroadcastStorageHandler is invoked when a transaction is confirmed on-chain
-// or when a transaction is considered stale.
-type BroadcastStorageHandler interface {
-	// TransactionConfirmed is called when a transaction is observed on-chain for the
-	// last time at a block height < current block height - confirmationDepth.
-	TransactionConfirmed(
-		context.Context,
-		*types.BlockIdentifier,
-		*types.Transaction,
-		[]*types.Operation,
-	) error // can use locked account again + confirm matches intent + update logger
-
-	// TransactionStale is called when a transaction has not yet been
-	// seen on-chain and is considered stale. This occurs when
-	// current block height - last broadcast > staleDepth.
-	TransactionStale(
-		context.Context,
-		*types.TransactionIdentifier,
-	) error // log in counter (rebroadcast should occur here)
-
-	// BroadcastFailed is called when another transaction broadcast would
-	// put it over the provided broadcast limit.
-	BroadcastFailed(
-		context.Context,
-		*types.TransactionIdentifier,
-		[]*types.Operation,
-	) error
-}
-
-// Broadcast is persisted to the db to track transaction broadcast.
-type Broadcast struct {
-	Identifier    *types.TransactionIdentifier `json:"identifier"`
-	Sender        string                       `json:"sender"`
-	Intent        []*types.Operation           `json:"intent"`
-	Payload       string                       `json:"payload"`
-	LastBroadcast *types.BlockIdentifier       `json:"broadcast_at"`
-	Broadcasts    int                          `json:"broadcasts"`
-}
-
-// NewBroadcastStorage returns a new BroadcastStorage.
-func NewBroadcastStorage(
-	db Database,
-	confirmationDepth int64,
-	staleDepth int64,
-	broadcastLimit int,
-	tipDelay int64,
-	broadcastBehindTip bool,
-	blockBroadcastLimit int,
-) *BroadcastStorage {
-	return &BroadcastStorage{
-		db:                  db,
-		confirmationDepth:   confirmationDepth,
-		staleDepth:          staleDepth,
-		broadcastLimit:      broadcastLimit,
-		tipDelay:            tipDelay,
-		broadcastBehindTip:  broadcastBehindTip,
-		blockBroadcastLimit: blockBroadcastLimit,
-	}
-}
-
-// Initialize adds a BroadcastStorageHelper and BroadcastStorageHandler to BroadcastStorage.
-// This must be called prior to syncing!
-func (b *BroadcastStorage) Initialize(
-	helper BroadcastStorageHelper,
-	handler BroadcastStorageHandler,
-) {
-	b.helper = helper
-	b.handler = handler
-}
-
-func (b *BroadcastStorage) addBlockCommitWorker(
-	ctx context.Context,
-	staleTransactions []*types.TransactionIdentifier,
-	confirmedTransactions []*Broadcast,
-	foundTransactions []*types.Transaction,
-	foundBlocks []*types.BlockIdentifier,
-) error {
-	for _, stale := range staleTransactions {
-		if err := b.handler.TransactionStale(ctx, stale); err != nil {
-			return fmt.Errorf("%w: unable to handle stale transaction %s", err, stale.Hash)
-		}
-	}
-
-	for i, broadcast := range confirmedTransactions {
-		err := b.handler.TransactionConfirmed(
-			ctx,
-			foundBlocks[i],
-			foundTransactions[i],
-			broadcast.Intent,
-		)
-		if err != nil {
-			return fmt.Errorf(
-				"%w: unable to handle confirmed transaction %s",
-				err,
-				broadcast.Identifier.Hash,
-			)
-		}
-	}
-
-	if err := b.BroadcastAll(ctx, true); err != nil {
-		return fmt.Errorf("%w: unable to broadcast pending transactions", err)
-	}
-
-	return nil
-}
-
-// AddingBlock is called by BlockStorage when adding a block.
-func (b *BroadcastStorage) AddingBlock(
-	ctx context.Context,
-	block *types.Block,
-	transaction DatabaseTransaction,
-) (CommitWorker, error) {
-	broadcasts, err := b.GetAllBroadcasts(ctx)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get all broadcasts", err)
-	}
-
-	staleTransactions := []*types.TransactionIdentifier{}
-	confirmedTransactions := []*Broadcast{}
-	foundTransactions := []*types.Transaction{}
-	foundBlocks := []*types.BlockIdentifier{}
-
-	for _, broadcast := range broadcasts {
-		if broadcast.LastBroadcast == nil {
-			continue
-		}
-
-		key := getBroadcastKey(broadcast.Identifier)
-
-		// We perform the FindTransaction search in the context of the block database
-		// transaction so we can access any transactions of depth 1 (in the current
-		// block).
-		foundBlock, foundTransaction, err := b.helper.FindTransaction(
-			ctx,
-			broadcast.Identifier,
-			transaction,
-		)
-		if err != nil {
-			return nil, fmt.Errorf("%w: unable to determine if transaction was seen", err)
-		}
-
-		// Check if we should mark the broadcast as stale
-		if foundBlock == nil &&
-			block.BlockIdentifier.Index-broadcast.LastBroadcast.Index >= b.staleDepth-depthOffset {
-			staleTransactions = append(staleTransactions, broadcast.Identifier)
-			broadcast.LastBroadcast = nil
-			bytes, err := encode(broadcast)
-			if err != nil {
-				return nil, fmt.Errorf("%w: unable to encode updated broadcast", err)
-			}
-
-			if err := transaction.Set(ctx, key, bytes); err != nil {
-				return nil, fmt.Errorf("%w: unable to update broadcast", err)
-			}
-
-			continue
-		}
-
-		// Continue if we are still waiting for a broadcast to appear and it isn't stale
-		if foundBlock == nil {
-			continue
-		}
-
-		// Check if we should mark the transaction as confirmed
-		if block.BlockIdentifier.Index-foundBlock.Index >= b.confirmationDepth-depthOffset {
-			confirmedTransactions = append(confirmedTransactions, broadcast)
-			foundTransactions = append(foundTransactions, foundTransaction)
-			foundBlocks = append(foundBlocks, foundBlock)
-
-			if err := transaction.Delete(ctx, key); err != nil {
-				return nil, fmt.Errorf("%w: unable to delete confirmed broadcast", err)
-			}
-		}
-	}
-
-	return func(ctx context.Context) error {
-		return b.addBlockCommitWorker(
-			ctx,
-			staleTransactions,
-			confirmedTransactions,
-			foundTransactions,
-			foundBlocks,
-		)
-	}, nil
-}
-
-// RemovingBlock is called by BlockStorage when removing a block.
-// TODO: error if transaction removed after confirmed (means confirmation depth not deep enough)
-func (b *BroadcastStorage) RemovingBlock(
-	ctx context.Context,
-	block *types.Block,
-	transaction DatabaseTransaction,
-) (CommitWorker, error) {
-	return nil, nil
-}
-
-// Broadcast is called when a caller wants a transaction to be broadcast and tracked.
-// The caller SHOULD NOT broadcast the transaction before calling this function.
-func (b *BroadcastStorage) Broadcast(
-	ctx context.Context,
-	sender string,
-	intent []*types.Operation,
-	transactionIdentifier *types.TransactionIdentifier,
-	payload string,
-) error {
-	txn := b.db.NewDatabaseTransaction(ctx, true)
-	defer txn.Discard(ctx)
-
-	broadcastKey := getBroadcastKey(transactionIdentifier)
-
-	exists, _, err := txn.Get(ctx, broadcastKey)
-	if err != nil {
-		return fmt.Errorf("%w: unable to determine if already broadcasting transaction", err)
-	}
-
-	if exists {
-		return fmt.Errorf("already broadcasting transaction %s", transactionIdentifier.Hash)
-	}
-
-	bytes, err := encode(&Broadcast{
-		Identifier: transactionIdentifier,
-		Sender:     sender,
-		Intent:     intent,
-		Payload:    payload,
-		Broadcasts: 0,
-	})
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode broadcast", err)
-	}
-
-	if err := txn.Set(ctx, broadcastKey, bytes); err != nil {
-		return fmt.Errorf("%w: unable to set broadcast", err)
-	}
-
-	if err := txn.Commit(ctx); err != nil {
-		return fmt.Errorf("%w: unable to commit broadcast", err)
-	}
-
-	// Broadcast all pending transactions (instead of waiting
-	// until after processing the next block).
-	if err := b.BroadcastAll(ctx, true); err != nil {
-		return fmt.Errorf("%w: unable to broadcast pending transactions", err)
-	}
-
-	return nil
-}
-
-// GetAllBroadcasts returns all currently in-process broadcasts.
-func (b *BroadcastStorage) GetAllBroadcasts(ctx context.Context) ([]*Broadcast, error) {
-	rawBroadcasts, err := b.db.Scan(ctx, []byte(transactionBroadcastNamespace))
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to scan for all broadcasts", err)
-	}
-
-	broadcasts := make([]*Broadcast, len(rawBroadcasts))
-	for i, rawBroadcast := range rawBroadcasts {
-		var b Broadcast
-		if err := decode(rawBroadcast, &b); err != nil {
-			return nil, fmt.Errorf("%w: unable to decode broadcast", err)
-		}
-
-		broadcasts[i] = &b
-	}
-
-	return broadcasts, nil
-}
-
-func (b *BroadcastStorage) performBroadcast(
-	ctx context.Context,
-	broadcast *Broadcast,
-	onlyEligible bool,
-) error {
-	bytes, err := encode(broadcast)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode broadcast", err)
-	}
-
-	txn := b.db.NewDatabaseTransaction(ctx, true)
-	defer txn.Discard(ctx)
-
-	if err := txn.Set(ctx, getBroadcastKey(broadcast.Identifier), bytes); err != nil {
-		return fmt.Errorf("%w: unable to update broadcast", err)
-	}
-
-	if err := txn.Commit(ctx); err != nil {
-		return fmt.Errorf("%w: unable to commit broadcast update", err)
-	}
-
-	if !onlyEligible {
-		log.Printf("Broadcasting: %s\n", types.PrettyPrintStruct(broadcast))
-	}
-
-	broadcastIdentifier, err := b.helper.BroadcastTransaction(ctx, broadcast.Payload)
-	if err != nil {
-		// Don't error on broadcast failure, retries will automatically be handled.
-		log.Printf(
-			"%s: unable to broadcast transaction %s",
-			err.Error(),
-			broadcast.Identifier.Hash,
-		)
-
-		return nil
-	}
-
-	if types.Hash(broadcastIdentifier) != types.Hash(broadcast.Identifier) {
-		return fmt.Errorf(
-			"transaction hash returned by broadcast %s does not match expected %s",
-			broadcastIdentifier.Hash,
-			broadcast.Identifier.Hash,
-		)
-	}
-
-	return nil
-}
-
-// BroadcastAll broadcasts all transactions in BroadcastStorage. If onlyEligible
-// is set to true, then only transactions that should be broadcast again
-// are actually broadcast.
-func (b *BroadcastStorage) BroadcastAll(ctx context.Context, onlyEligible bool) error {
-	// Corruption can occur if we run this concurrently.
-	b.broadcastAllMutex.Lock()
-	defer b.broadcastAllMutex.Unlock()
-
-	currBlock, err := b.helper.CurrentBlockIdentifier(ctx)
-	if err != nil {
-		return fmt.Errorf("%w: unable to get current block identifier", err)
-	}
-
-	// We have not yet synced a block and should wait to broadcast
-	// until we do so (otherwise we can't track last broadcast correctly).
-	if currBlock == nil {
-		return nil
-	}
-
-	// Wait to broadcast transaction until close to tip
-	atTip, err := b.helper.AtTip(ctx, b.tipDelay)
-	if err != nil {
-		return fmt.Errorf("%w: unable to determine if at tip", err)
-	}
-
-	if (!atTip && !b.broadcastBehindTip) && onlyEligible {
-		return nil
-	}
-
-	broadcasts, err := b.GetAllBroadcasts(ctx)
-	if err != nil {
-		return fmt.Errorf("%w: unable to get all broadcasts", err)
-	}
-
-	attemptedBroadcasts := 0
-	for _, broadcast := range broadcasts {
-		// When a transaction should be broadcast, its last broadcast field must
-		// be set to nil.
-		if broadcast.LastBroadcast != nil && onlyEligible {
-			continue
-		}
-
-		if broadcast.Broadcasts >= b.broadcastLimit {
-			txn := b.db.NewDatabaseTransaction(ctx, true)
-			defer txn.Discard(ctx)
-
-			if err := txn.Delete(ctx, getBroadcastKey(broadcast.Identifier)); err != nil {
-				return fmt.Errorf("%w: unable to delete broadcast", err)
-			}
-
-			if err := txn.Commit(ctx); err != nil {
-				return fmt.Errorf("%w: unable to commit broadcast delete", err)
-			}
-
-			if err := b.handler.BroadcastFailed(ctx, broadcast.Identifier, broadcast.Intent); err != nil {
-				return fmt.Errorf("%w: unable to handle broadcast failure", err)
-			}
-
-			continue
-		}
-
-		// Limit the number of transactions we attempt to broadcast
-		// at a given block.
-		if attemptedBroadcasts >= b.blockBroadcastLimit {
-			continue
-		}
-		attemptedBroadcasts++
-
-		// We set the last broadcast value before broadcast so we don't accidentally
-		// re-broadcast if exiting between broadcasting the transaction and updating
-		// the value in the database. If the transaction is never really broadcast,
-		// it will be rebroadcast when it is considered stale!
-		broadcast.LastBroadcast = currBlock
-		broadcast.Broadcasts++
-
-		if err := b.performBroadcast(ctx, broadcast, onlyEligible); err != nil {
-			return fmt.Errorf("%w: unable to perform broadcast", err)
-		}
-	}
-
-	return nil
-}
-
-// LockedAddresses returns all addresses currently active in transaction broadcasts.
-// The caller SHOULD NOT broadcast a transaction from an account if it is
-// considered locked!
-func (b *BroadcastStorage) LockedAddresses(ctx context.Context) ([]string, error) {
-	broadcasts, err := b.GetAllBroadcasts(ctx)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get all broadcasts", err)
-	}
-
-	addressMap := map[string]struct{}{}
-	for _, broadcast := range broadcasts {
-		for _, op := range broadcast.Intent {
-			if op.Account == nil {
-				continue
-			}
-
-			addressMap[op.Account.Address] = struct{}{}
-		}
-	}
-
-	addresses := []string{}
-	for k := range addressMap {
-		addresses = append(addresses, k)
-	}
-
-	return addresses, nil
-}
-
-// ClearBroadcasts deletes all in-progress broadcasts from BroadcastStorage. This
-// is useful when there is some construction error and all pending broadcasts
-// will fail and should be cleared instead of re-attempting.
-func (b *BroadcastStorage) ClearBroadcasts(ctx context.Context) ([]*Broadcast, error) {
-	broadcasts, err := b.GetAllBroadcasts(ctx)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get all broadcasts", err)
-	}
-
-	txn := b.db.NewDatabaseTransaction(ctx, true)
-	for _, broadcast := range broadcasts {
-		if err := txn.Delete(ctx, getBroadcastKey(broadcast.Identifier)); err != nil {
-			return nil, fmt.Errorf(
-				"%w: unable to delete broadcast %s",
-				err,
-				broadcast.Identifier.Hash,
-			)
-		}
-	}
-
-	if err := txn.Commit(ctx); err != nil {
-		return nil, fmt.Errorf("%w: unable to delete broadcasts", err)
-	}
-
-	return broadcasts, nil
-}
diff --git a/pkg/storage/coin_storage.go b/pkg/storage/coin_storage.go
deleted file mode 100644
index 1f176b7..0000000
--- a/pkg/storage/coin_storage.go
+++ /dev/null
@@ -1,446 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"math/big"
-
-	"github.com/coinbase/rosetta-sdk-go/asserter"
-	"github.com/coinbase/rosetta-sdk-go/types"
-)
-
-const (
-	coinNamespace        = "coinNamespace"
-	coinAccountNamespace = "coinAccountNamespace"
-)
-
-var _ BlockWorker = (*CoinStorage)(nil)
-
-// CoinStorage implements storage methods for storing
-// UTXOs.
-type CoinStorage struct {
-	db Database
-
-	helper   CoinStorageHelper
-	asserter *asserter.Asserter
-}
-
-// CoinStorageHelper is used by CoinStorage to determine
-// at which block a Coin set is valid.
-type CoinStorageHelper interface {
-	// CurrentBlockIdentifier is called while fetching coins in a single
-	// database transaction to return the *types.BlockIdentifier where
-	// the Coin set is valid.
-	CurrentBlockIdentifier(
-		context.Context,
-		DatabaseTransaction,
-	) (*types.BlockIdentifier, error)
-}
-
-// NewCoinStorage returns a new CoinStorage.
-func NewCoinStorage(
-	db Database,
-	helper CoinStorageHelper,
-	asserter *asserter.Asserter,
-) *CoinStorage {
-	return &CoinStorage{
-		db:       db,
-		helper:   helper,
-		asserter: asserter,
-	}
-}
-
-func getCoinKey(identifier *types.CoinIdentifier) []byte {
-	return []byte(fmt.Sprintf("%s/%s", coinNamespace, identifier.Identifier))
-}
-
-func getCoinAccountKey(accountIdentifier *types.AccountIdentifier) []byte {
-	return []byte(fmt.Sprintf("%s/%s", coinAccountNamespace, types.Hash(accountIdentifier)))
-}
-
-func getAndDecodeCoin(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	coinIdentifier *types.CoinIdentifier,
-) (bool, *types.Coin, error) {
-	exists, val, err := transaction.Get(ctx, getCoinKey(coinIdentifier))
-	if err != nil {
-		return false, nil, fmt.Errorf("%w: unable to query for coin", err)
-	}
-
-	if !exists { // this could occur if coin was created before we started syncing
-		return false, nil, nil
-	}
-
-	var coin types.Coin
-	if err := decode(val, &coin); err != nil {
-		return false, nil, fmt.Errorf("%w: unable to decode coin", err)
-	}
-
-	return true, &coin, nil
-}
-
-type wallets map[string]map[string]struct{}
-
-func (c *CoinStorage) tryAddingCoin(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	operation *types.Operation,
-	action types.CoinAction,
-	cache wallets,
-) error {
-	if operation.CoinChange == nil {
-		return errors.New("coin change cannot be nil")
-	}
-
-	if operation.CoinChange.CoinAction != action {
-		return nil
-	}
-
-	coinIdentifier := operation.CoinChange.CoinIdentifier
-
-	newCoin := &types.Coin{
-		CoinIdentifier: coinIdentifier,
-		Amount:         operation.Amount,
-	}
-
-	encodedResult, err := encode(newCoin)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode coin data", err)
-	}
-
-	if err := transaction.Set(ctx, getCoinKey(coinIdentifier), encodedResult); err != nil {
-		return fmt.Errorf("%w: unable to store coin", err)
-	}
-
-	addressKey := string(getCoinAccountKey(operation.Account))
-	coins, ok := cache[addressKey]
-	if !ok {
-		accountExists, accountCoins, err := getAndDecodeCoins(ctx, transaction, operation.Account)
-		if err != nil {
-			return fmt.Errorf("%w: unable to query coin account", err)
-		}
-
-		if !accountExists {
-			accountCoins = map[string]struct{}{}
-		}
-
-		coins = accountCoins
-	}
-
-	if _, exists := coins[coinIdentifier.Identifier]; exists {
-		return fmt.Errorf(
-			"coin %s already exists in account %s",
-			coinIdentifier,
-			types.PrettyPrintStruct(operation.Account),
-		)
-	}
-
-	coins[coinIdentifier.Identifier] = struct{}{}
-	cache[addressKey] = coins
-
-	return nil
-}
-
-func encodeAndSetCoins(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	accountKey []byte,
-	coins map[string]struct{},
-) error {
-	encodedResult, err := encode(coins)
-	if err != nil {
-		return fmt.Errorf("%w: unable to encode coins", err)
-	}
-
-	if err := transaction.Set(ctx, accountKey, encodedResult); err != nil {
-		return fmt.Errorf("%w: unable to set coin account", err)
-	}
-
-	return nil
-}
-
-func getAndDecodeCoins(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	accountIdentifier *types.AccountIdentifier,
-) (bool, map[string]struct{}, error) {
-	accountExists, val, err := transaction.Get(ctx, getCoinAccountKey(accountIdentifier))
-	if err != nil {
-		return false, nil, fmt.Errorf("%w: unable to query coin account", err)
-	}
-
-	if !accountExists {
-		return false, nil, nil
-	}
-
-	var coins map[string]struct{}
-	if err := decode(val, &coins); err != nil {
-		return false, nil, fmt.Errorf("%w: unable to decode coin account", err)
-	}
-
-	return true, coins, nil
-}
-
-func (c *CoinStorage) tryRemovingCoin(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-	operation *types.Operation,
-	action types.CoinAction,
-	cache wallets,
-) error {
-	if operation.CoinChange == nil {
-		return errors.New("coin change cannot be nil")
-	}
-
-	if operation.CoinChange.CoinAction != action {
-		return nil
-	}
-
-	coinIdentifier := operation.CoinChange.CoinIdentifier
-
-	exists, _, err := transaction.Get(ctx, getCoinKey(coinIdentifier))
-	if err != nil {
-		return fmt.Errorf("%w: unable to query for coin", err)
-	}
-
-	if !exists { // this could occur if coin was created before we started syncing
-		return nil
-	}
-
-	if err := transaction.Delete(ctx, getCoinKey(coinIdentifier)); err != nil {
-		return fmt.Errorf("%w: unable to delete coin", err)
-	}
-
-	addressKey := string(getCoinAccountKey(operation.Account))
-	coins, ok := cache[addressKey]
-	if !ok {
-		accountExists, accountCoins, err := getAndDecodeCoins(ctx, transaction, operation.Account)
-		if err != nil {
-			return fmt.Errorf("%w: unable to query coin account", err)
-		}
-
-		if !accountExists {
-			return fmt.Errorf("%w: unable to find owner of coin", err)
-		}
-
-		coins = accountCoins
-	}
-
-	if _, exists := coins[coinIdentifier.Identifier]; !exists {
-		return fmt.Errorf(
-			"unable to find coin %s in account %s",
-			coinIdentifier,
-			types.PrettyPrintStruct(operation.Account),
-		)
-	}
-
-	delete(coins, coinIdentifier.Identifier)
-	cache[addressKey] = coins
-
-	return nil
-}
-
-// writeCache commits all changes stored in the cache to the database. This ensures
-// that any block only ever contains 1 write per account identifier to update
-// their persisted wallet.
-func writeCache(ctx context.Context, transaction DatabaseTransaction, cache wallets) error {
-	for account, coins := range cache {
-		if err := encodeAndSetCoins(ctx, transaction, []byte(account), coins); err != nil {
-			return fmt.Errorf("%w: unable to set coins", err)
-		}
-	}
-
-	return nil
-}
-
-// AddingBlock is called by BlockStorage when adding a block.
-func (c *CoinStorage) AddingBlock(
-	ctx context.Context,
-	block *types.Block,
-	transaction DatabaseTransaction,
-) (CommitWorker, error) {
-	cache := wallets{}
-	for _, txn := range block.Transactions {
-		for _, operation := range txn.Operations {
-			if operation.CoinChange == nil {
-				continue
-			}
-
-			if operation.Amount == nil {
-				continue
-			}
-
-			success, err := c.asserter.OperationSuccessful(operation)
-			if err != nil {
-				return nil, fmt.Errorf("%w: unable to parse operation success", err)
-			}
-
-			if !success {
-				continue
-			}
-
-			if err := c.tryAddingCoin(ctx, transaction, operation, types.CoinCreated, cache); err != nil {
-				return nil, fmt.Errorf("%w: unable to add coin", err)
-			}
-
-			if err := c.tryRemovingCoin(ctx, transaction, operation, types.CoinSpent, cache); err != nil {
-				return nil, fmt.Errorf("%w: unable to remove coin", err)
-			}
-		}
-	}
-
-	if err := writeCache(ctx, transaction, cache); err != nil {
-		return nil, fmt.Errorf("%w: unable to update wallets", err)
-	}
-
-	return nil, nil
-}
-
-// RemovingBlock is called by BlockStorage when removing a block.
-func (c *CoinStorage) RemovingBlock(
-	ctx context.Context,
-	block *types.Block,
-	transaction DatabaseTransaction,
-) (CommitWorker, error) {
-	cache := wallets{}
-	for _, txn := range block.Transactions {
-		for _, operation := range txn.Operations {
-			if operation.CoinChange == nil {
-				continue
-			}
-
-			if operation.Amount == nil {
-				continue
-			}
-
-			success, err := c.asserter.OperationSuccessful(operation)
-			if err != nil {
-				return nil, fmt.Errorf("%w: unable to parse operation success", err)
-			}
-
-			if !success {
-				continue
-			}
-
-			// We add spent coins and remove created coins during a re-org (opposite of
-			// AddingBlock).
-			if err := c.tryAddingCoin(ctx, transaction, operation, types.CoinSpent, cache); err != nil {
-				return nil, fmt.Errorf("%w: unable to add coin", err)
-			}
-
-			if err := c.tryRemovingCoin(ctx, transaction, operation, types.CoinCreated, cache); err != nil {
-				return nil, fmt.Errorf("%w: unable to remove coin", err)
-			}
-		}
-	}
-
-	if err := writeCache(ctx, transaction, cache); err != nil {
-		return nil, fmt.Errorf("%w: unable to update wallets", err)
-	}
-
-	return nil, nil
-}
-
-// GetCoins returns all unspent coins for a provided *types.AccountIdentifier.
-func (c *CoinStorage) GetCoins(
-	ctx context.Context,
-	accountIdentifier *types.AccountIdentifier,
-) ([]*types.Coin, *types.BlockIdentifier, error) {
-	transaction := c.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	accountExists, coins, err := getAndDecodeCoins(ctx, transaction, accountIdentifier)
-	if err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to query account identifier", err)
-	}
-
-	headBlockIdentifier, err := c.helper.CurrentBlockIdentifier(ctx, transaction)
-	if err != nil {
-		return nil, nil, fmt.Errorf("%w: unable to get current block identifier", err)
-	}
-
-	if !accountExists {
-		return []*types.Coin{}, headBlockIdentifier, nil
-	}
-
-	coinArr := []*types.Coin{}
-	for coinIdentifier := range coins {
-		exists, coin, err := getAndDecodeCoin(
-			ctx,
-			transaction,
-			&types.CoinIdentifier{Identifier: coinIdentifier},
-		)
-		if err != nil {
-			return nil, nil, fmt.Errorf("%w: unable to query coin", err)
-		}
-
-		if !exists {
-			return nil, nil, fmt.Errorf("%w: unable to get coin %s", err, coinIdentifier)
-		}
-
-		coinArr = append(coinArr, coin)
-	}
-
-	return coinArr, headBlockIdentifier, nil
-}
-
-// GetLargestCoin returns the largest Coin for a
-// *types.AccountIdentifier and *types.Currency.
-// If no Coins are available, a 0 balance is returned.
-func (c *CoinStorage) GetLargestCoin(
-	ctx context.Context,
-	accountIdentifier *types.AccountIdentifier,
-	currency *types.Currency,
-) (*big.Int, *types.CoinIdentifier, *types.BlockIdentifier, error) {
-	coins, blockIdentifier, err := c.GetCoins(ctx, accountIdentifier)
-	if err != nil {
-		return nil, nil, nil, fmt.Errorf(
-			"%w: unable to get utxo balance for %s",
-			err,
-			accountIdentifier.Address,
-		)
-	}
-
-	bal := big.NewInt(0)
-	var coinIdentifier *types.CoinIdentifier
-	for _, coin := range coins {
-		if types.Hash(
-			coin.Amount.Currency,
-		) != types.Hash(
-			currency,
-		) {
-			continue
-		}
-
-		val, ok := new(big.Int).SetString(coin.Amount.Value, 10)
-		if !ok {
-			return nil, nil, nil, fmt.Errorf(
-				"could not parse amount for coin %s",
-				coin.CoinIdentifier.Identifier,
-			)
-		}
-
-		if bal.Cmp(val) == -1 {
-			bal = val
-			coinIdentifier = coin.CoinIdentifier
-		}
-	}
-
-	return bal, coinIdentifier, blockIdentifier, nil
-}
diff --git a/pkg/storage/counter_storage.go b/pkg/storage/counter_storage.go
deleted file mode 100644
index 3bdcb1a..0000000
--- a/pkg/storage/counter_storage.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"fmt"
-	"math/big"
-)
-
-const (
-	// BlockCounter is the number of added blocks.
-	BlockCounter = "blocks"
-
-	// OrphanCounter is the number of orphaned blocks.
-	OrphanCounter = "orphans"
-
-	// TransactionCounter is the number of processed transactions.
-	TransactionCounter = "transactions"
-
-	// OperationCounter is the number of processed operations.
-	OperationCounter = "operations"
-
-	// AddressesCreatedCounter is the number of created addresses.
-	AddressesCreatedCounter = "addresses_created"
-
-	// TransactionsCreatedCounter is the number of created transactions.
-	TransactionsCreatedCounter = "transactions_created"
-
-	// TransactionsConfirmedCounter is the number of confirmed transactions.
-	TransactionsConfirmedCounter = "transactions_confirmed"
-
-	// StaleBroadcastsCounter is the number of transaction broadcasts that
-	// never appeared on-chain.
-	StaleBroadcastsCounter = "stale_broadcasts"
-
-	// FailedBroadcastsCounter is the number of transaction broadcasts that
-	// never made it on-chain after retries.
-	FailedBroadcastsCounter = "failed_broadcasts"
-
-	// ActiveReconciliationCounter is the number of active
-	// reconciliations performed.
-	ActiveReconciliationCounter = "active_reconciliations"
-
-	// InactiveReconciliationCounter is the number of inactive
-	// reconciliations performed.
-	InactiveReconciliationCounter = "inactive_reconciliations"
-
-	// counterNamespace is preprended to any counter.
-	counterNamespace = "counter"
-)
-
-// CounterStorage implements counter-specific storage methods
-// on top of a Database and DatabaseTransaction interface.
-type CounterStorage struct {
-	db Database
-}
-
-// NewCounterStorage returns a new CounterStorage.
-func NewCounterStorage(
-	db Database,
-) *CounterStorage {
-	return &CounterStorage{
-		db: db,
-	}
-}
-
-func getCounterKey(counter string) []byte {
-	return []byte(fmt.Sprintf("%s/%s", counterNamespace, counter))
-}
-
-func transactionalGet(
-	ctx context.Context,
-	counter string,
-	txn DatabaseTransaction,
-) (*big.Int, error) {
-	exists, val, err := txn.Get(ctx, getCounterKey(counter))
-	if err != nil {
-		return nil, err
-	}
-
-	if !exists {
-		return big.NewInt(0), nil
-	}
-
-	return new(big.Int).SetBytes(val), nil
-}
-
-// Update updates the value of a counter by amount and returns the new value.
-func (c *CounterStorage) Update(
-	ctx context.Context,
-	counter string,
-	amount *big.Int,
-) (*big.Int, error) {
-	transaction := c.db.NewDatabaseTransaction(ctx, true)
-	defer transaction.Discard(ctx)
-
-	val, err := transactionalGet(ctx, counter, transaction)
-	if err != nil {
-		return nil, err
-	}
-
-	newVal := new(big.Int).Add(val, amount)
-
-	if err := transaction.Set(ctx, getCounterKey(counter), newVal.Bytes()); err != nil {
-		return nil, err
-	}
-
-	if err := transaction.Commit(ctx); err != nil {
-		return nil, err
-	}
-
-	return newVal, nil
-}
-
-// Get returns the current value of a counter.
-func (c *CounterStorage) Get(ctx context.Context, counter string) (*big.Int, error) {
-	transaction := c.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	return transactionalGet(ctx, counter, transaction)
-}
diff --git a/pkg/storage/key_storage.go b/pkg/storage/key_storage.go
deleted file mode 100644
index 5bcaebb..0000000
--- a/pkg/storage/key_storage.go
+++ /dev/null
@@ -1,232 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"fmt"
-	"math/rand"
-	"time"
-
-	"github.com/coinbase/rosetta-cli/configuration"
-	"github.com/coinbase/rosetta-sdk-go/keys"
-	"github.com/coinbase/rosetta-sdk-go/types"
-)
-
-// WARNING: KEY STORAGE USING THIS PACKAGE IS NOT SECURE!!!! ONLY USE
-// FOR TESTING!!!!
-
-func init() {
-	rand.Seed(time.Now().UTC().UnixNano())
-}
-
-const (
-	keyNamespace = "key"
-)
-
-var (
-	ErrAddrExists = errors.New("Address already exists")
-)
-
-func getAddressKey(address string) []byte {
-	return []byte(
-		fmt.Sprintf("%s/%s", keyNamespace, address),
-	)
-}
-
-// KeyStorage implements key storage methods
-// on top of a Database and DatabaseTransaction interface.
-type KeyStorage struct {
-	db Database
-}
-
-// NewKeyStorage returns a new KeyStorage.
-func NewKeyStorage(
-	db Database,
-) *KeyStorage {
-	return &KeyStorage{
-		db: db,
-	}
-}
-
-type Key struct {
-	Address string        `json:"address"`
-	KeyPair *keys.KeyPair `json:"keypair"`
-}
-
-func parseKey(buf []byte) (*Key, error) {
-	var k Key
-	err := getDecoder(bytes.NewReader(buf)).Decode(&k)
-	if err != nil {
-		return nil, err
-	}
-
-	return &k, nil
-}
-
-func serializeKey(k *Key) ([]byte, error) {
-	buf := new(bytes.Buffer)
-	err := getEncoder(buf).Encode(k)
-	if err != nil {
-		return nil, err
-	}
-
-	return buf.Bytes(), nil
-}
-
-// Store saves a keys.KeyPair for a given address. If the address already
-// exists, an error is returned.
-func (k *KeyStorage) Store(ctx context.Context, address string, keyPair *keys.KeyPair) error {
-	transaction := k.db.NewDatabaseTransaction(ctx, true)
-	defer transaction.Discard(ctx)
-
-	exists, _, err := transaction.Get(ctx, getAddressKey(address))
-	if err != nil {
-		return fmt.Errorf("%w: unable to check if address %s exists", err, address)
-	}
-
-	if exists {
-		return fmt.Errorf("%w: address %s already exists", ErrAddrExists, address)
-	}
-
-	val, err := serializeKey(&Key{
-		Address: address,
-		KeyPair: keyPair,
-	})
-	if err != nil {
-		return fmt.Errorf("%w: unable to serialize key", err)
-	}
-
-	err = transaction.Set(ctx, getAddressKey(address), val)
-	if err != nil {
-		return fmt.Errorf("%w: unable to store key", err)
-	}
-
-	if err := transaction.Commit(ctx); err != nil {
-		return fmt.Errorf("%w: unable to commit new key to db", err)
-	}
-
-	return nil
-}
-
-// Get returns a *keys.KeyPair for an address, if it exists.
-func (k *KeyStorage) Get(ctx context.Context, address string) (*keys.KeyPair, error) {
-	transaction := k.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	exists, rawKey, err := transaction.Get(ctx, getAddressKey(address))
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get address %s", err, address)
-	}
-
-	if !exists {
-		return nil, fmt.Errorf("address not found %s", address)
-	}
-
-	key, err := parseKey(rawKey)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to parse saved key", err)
-	}
-
-	return key.KeyPair, nil
-}
-
-// GetAllAddresses returns all addresses in key storage.
-func (k *KeyStorage) GetAllAddresses(ctx context.Context) ([]string, error) {
-	rawKeys, err := k.db.Scan(ctx, []byte(keyNamespace))
-	if err != nil {
-		return nil, fmt.Errorf("%w database scan for keys failed", err)
-	}
-
-	addresses := make([]string, len(rawKeys))
-	for i, rawKey := range rawKeys {
-		kp, err := parseKey(rawKey)
-		if err != nil {
-			return nil, fmt.Errorf("%w: unable to parse key pair", err)
-		}
-
-		addresses[i] = kp.Address
-	}
-
-	return addresses, nil
-}
-
-// Sign attempts to sign a slice of *types.SigningPayload with the keys in KeyStorage.
-func (k *KeyStorage) Sign(
-	ctx context.Context,
-	payloads []*types.SigningPayload,
-) ([]*types.Signature, error) {
-	signatures := make([]*types.Signature, len(payloads))
-	for i, payload := range payloads {
-		keyPair, err := k.Get(ctx, payload.Address)
-		if err != nil {
-			return nil, fmt.Errorf("%w: could not get key for %s", err, payload.Address)
-		}
-
-		signer, err := keyPair.Signer()
-		if err != nil {
-			return nil, fmt.Errorf("%w: unable to create signer", err)
-		}
-
-		if len(payload.SignatureType) == 0 {
-			return nil, fmt.Errorf("cannot determine signature type for payload %d", i)
-		}
-
-		signature, err := signer.Sign(payload, payload.SignatureType)
-		if err != nil {
-			return nil, fmt.Errorf("%w: unable to to sign payload %d", err, i)
-		}
-
-		signatures[i] = signature
-	}
-
-	return signatures, nil
-}
-
-// RandomAddress returns a random address from all addresses.
-func (k *KeyStorage) RandomAddress(ctx context.Context) (string, error) {
-	addresses, err := k.GetAllAddresses(ctx)
-	if err != nil {
-		return "", fmt.Errorf("%w: unable to get addresses", err)
-	}
-
-	if len(addresses) == 0 {
-		return "", errors.New("no addresses available")
-	}
-
-	return addresses[rand.Intn(len(addresses))], nil
-}
-
-func (k *KeyStorage) ImportAccounts(ctx context.Context, accounts []*configuration.PrefundedAccount) error {
-	// Import prefunded account and save to database
-	for _, acc := range accounts {
-		keyPair, err := keys.ImportPrivKey(acc.PrivateKeyHex, acc.CurveType)
-		if err != nil {
-			return fmt.Errorf("%w: unable to import prefunded account", err)
-		}
-
-		// Skip if key already exists
-		err = k.Store(ctx, acc.Address, keyPair)
-		if errors.Is(err, ErrAddrExists) {
-			continue
-		}
-		if err != nil {
-			return fmt.Errorf("%w: unable to store prefunded account", err)
-		}
-	}
-	return nil
-}
diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go
deleted file mode 100644
index 3777db3..0000000
--- a/pkg/storage/storage.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-)
-
-// Database is an interface that provides transactional
-// access to a KV store.
-type Database interface {
-	NewDatabaseTransaction(context.Context, bool) DatabaseTransaction
-	Close(context.Context) error
-	Set(context.Context, []byte, []byte) error
-	Get(context.Context, []byte) (bool, []byte, error)
-	Scan(ctx context.Context, prefix []byte) ([][]byte, error)
-}
-
-// DatabaseTransaction is an interface that provides
-// access to a KV store within some transaction
-// context provided by a Database.
-type DatabaseTransaction interface {
-	Set(context.Context, []byte, []byte) error
-	Get(context.Context, []byte) (bool, []byte, error)
-	Delete(context.Context, []byte) error
-	Commit(context.Context) error
-	Discard(context.Context)
-}
diff --git a/pkg/storage/utils.go b/pkg/storage/utils.go
deleted file mode 100644
index 0f475b7..0000000
--- a/pkg/storage/utils.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-
-	msgpack "github.com/vmihailenco/msgpack/v5"
-)
-
-func getEncoder(w io.Writer) *msgpack.Encoder {
-	enc := msgpack.NewEncoder(w)
-	enc.UseJSONTag(true)
-
-	return enc
-}
-
-func encode(object interface{}) ([]byte, error) {
-	buf := new(bytes.Buffer)
-	err := getEncoder(buf).Encode(object)
-	if err != nil {
-		return nil, fmt.Errorf("%w: could not encode object", err)
-	}
-
-	return buf.Bytes(), nil
-}
-
-func getDecoder(r io.Reader) *msgpack.Decoder {
-	dec := msgpack.NewDecoder(r)
-	dec.UseJSONTag(true)
-
-	return dec
-}
-
-func decode(b []byte, object interface{}) error {
-	err := getDecoder(bytes.NewReader(b)).Decode(&object)
-	if err != nil {
-		return fmt.Errorf("%w: unable to decode bytes", err)
-	}
-
-	return nil
-}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
deleted file mode 100644
index ae85ab9..0000000
--- a/pkg/utils/utils.go
+++ /dev/null
@@ -1,312 +0,0 @@
-// 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.
-
-package utils
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io/ioutil"
-	"log"
-	"math/big"
-	"math/rand"
-	"os"
-	"path"
-	"time"
-
-	"github.com/coinbase/rosetta-sdk-go/fetcher"
-	"github.com/coinbase/rosetta-sdk-go/types"
-	"github.com/fatih/color"
-)
-
-func init() {
-	rand.Seed(time.Now().UTC().UnixNano())
-}
-
-const (
-	// DefaultFilePermissions specifies that the user can
-	// read and write the file.
-	DefaultFilePermissions = 0600
-
-	// AllFilePermissions specifies anyone can do anything
-	// to the file.
-	AllFilePermissions = 0777
-
-	base10            = 10
-	bigFloatPrecision = 512
-
-	// NanosecondsInMillisecond is the number
-	// of nanoseconds in a millisecond.
-	NanosecondsInMillisecond = 1000000
-
-	// MillisecondsInSecond is the number
-	// of milliseconds in a second.
-	MillisecondsInSecond = 1000
-
-	// OneHundred is the number 100.
-	OneHundred = 100
-)
-
-var (
-	// ErrNetworkNotSupported is returned when the network
-	// you are attempting to connect to is not supported.
-	ErrNetworkNotSupported = errors.New("network not supported")
-
-	// OneHundredInt is a big.Int of value 100.
-	OneHundredInt = big.NewInt(OneHundred)
-
-	// ZeroInt is a big.Int of value 0.
-	ZeroInt = big.NewInt(0)
-)
-
-// CreateTempDir creates a directory in
-// /tmp for usage within testing.
-func CreateTempDir() (string, error) {
-	storageDir, err := ioutil.TempDir("", "rosetta-cli")
-	if err != nil {
-		return "", err
-	}
-
-	color.Cyan("Using temporary directory %s", storageDir)
-	return storageDir, nil
-}
-
-// RemoveTempDir deletes a directory at
-// a provided path for usage within testing.
-func RemoveTempDir(dir string) {
-	if err := os.RemoveAll(dir); err != nil {
-		log.Fatal(err)
-	}
-}
-
-// EnsurePathExists creates directories along
-// a path if they do not exist.
-func EnsurePathExists(path string) error {
-	if err := os.MkdirAll(path, os.FileMode(AllFilePermissions)); err != nil {
-		return fmt.Errorf("%w: unable to create data and network directory", err)
-	}
-
-	return nil
-}
-
-// Equal returns a boolean indicating if two
-// interfaces are equal.
-func Equal(a interface{}, b interface{}) bool {
-	return types.Hash(a) == types.Hash(b)
-}
-
-// SerializeAndWrite attempts to serialize the provided object
-// into a file at filePath.
-func SerializeAndWrite(filePath string, object interface{}) error {
-	err := ioutil.WriteFile(
-		filePath,
-		[]byte(types.PrettyPrintStruct(object)),
-		os.FileMode(DefaultFilePermissions),
-	)
-	if err != nil {
-		return fmt.Errorf("%w: unable to write to file path %s", err, filePath)
-	}
-
-	return nil
-}
-
-// LoadAndParse reads the file at the provided path
-// and attempts to unmarshal it into output.
-func LoadAndParse(filePath string, output interface{}) error {
-	b, err := ioutil.ReadFile(path.Clean(filePath))
-	if err != nil {
-		return fmt.Errorf("%w: unable to load file %s", err, filePath)
-	}
-
-	// To prevent silent erroring, we explicitly
-	// reject any unknown fields.
-	dec := json.NewDecoder(bytes.NewReader(b))
-	dec.DisallowUnknownFields()
-
-	if err := dec.Decode(&output); err != nil {
-		return fmt.Errorf("%w: unable to unmarshal", err)
-	}
-
-	return nil
-}
-
-// CreateCommandPath creates a unique path for a command and network within a data directory. This
-// is used to avoid collision when using multiple commands on multiple networks
-// when the same storage resources are used. If the derived path does not exist,
-// we run os.MkdirAll on the path.
-func CreateCommandPath(
-	dataDirectory string,
-	cmd string,
-	network *types.NetworkIdentifier,
-) (string, error) {
-	dataPath := path.Join(dataDirectory, cmd, types.Hash(network))
-	if err := EnsurePathExists(dataPath); err != nil {
-		return "", fmt.Errorf("%w: cannot populate path", err)
-	}
-
-	return dataPath, nil
-}
-
-// CheckNetworkSupported checks if a Rosetta implementation supports a given
-// *types.NetworkIdentifier. If it does, the current network status is returned.
-func CheckNetworkSupported(
-	ctx context.Context,
-	networkIdentifier *types.NetworkIdentifier,
-	fetcher *fetcher.Fetcher,
-) (*types.NetworkStatusResponse, error) {
-	networks, err := fetcher.NetworkList(ctx, nil)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to fetch network list", err)
-	}
-
-	networkMatched := false
-	supportedNetworks := []*types.NetworkIdentifier{}
-	for _, availableNetwork := range networks.NetworkIdentifiers {
-		if types.Hash(availableNetwork) == types.Hash(networkIdentifier) {
-			networkMatched = true
-			break
-		}
-
-		supportedNetworks = append(supportedNetworks, availableNetwork)
-	}
-
-	if !networkMatched {
-		color.Yellow("Supported networks: %s", types.PrettyPrintStruct(supportedNetworks))
-		return nil, fmt.Errorf(
-			"%w: %s is not available",
-			ErrNetworkNotSupported,
-			types.PrettyPrintStruct(networkIdentifier),
-		)
-	}
-
-	status, err := fetcher.NetworkStatusRetry(
-		ctx,
-		networkIdentifier,
-		nil,
-	)
-	if err != nil {
-		return nil, fmt.Errorf("%w: unable to get network status", err)
-	}
-
-	return status, nil
-}
-
-// BigPow10 computes the value of 10^e.
-// Inspired by:
-// https://steemit.com/tutorial/@gopher23/power-and-root-functions-using-big-float-in-golang
-func BigPow10(e int32) *big.Float {
-	a := big.NewFloat(base10)
-	result := Zero().Copy(a)
-	for i := int32(0); i < e-1; i++ {
-		result = Zero().Mul(result, a)
-	}
-	return result
-}
-
-// Zero returns a float with 256 bit precision.
-func Zero() *big.Float {
-	r := big.NewFloat(0)
-	r.SetPrec(bigFloatPrecision)
-	return r
-}
-
-// RandomNumber returns some number in the range [minimum, maximum).
-// Source: https://golang.org/pkg/math/big/#Int.Rand
-func RandomNumber(minimum *big.Int, maximum *big.Int) *big.Int {
-	source := rand.New(rand.NewSource(time.Now().UnixNano()))
-	transformed := new(big.Int).Sub(maximum, minimum)
-	addition := new(big.Int).Rand(source, transformed)
-
-	return new(big.Int).Add(minimum, addition)
-}
-
-// ContainsString returns a boolean indicating
-// whether the string s is in arr.
-func ContainsString(arr []string, s string) bool {
-	for _, v := range arr {
-		if v == s {
-			return true
-		}
-	}
-
-	return false
-}
-
-// PrettyAmount returns a currency amount in native format with
-// its symbol.
-func PrettyAmount(amount *big.Int, currency *types.Currency) string {
-	nativeUnits := new(big.Float).SetInt(amount)
-	precision := currency.Decimals
-	if precision > 0 {
-		divisor := BigPow10(precision)
-		nativeUnits = new(big.Float).Quo(nativeUnits, divisor)
-	}
-
-	return fmt.Sprintf(
-		"%s %s",
-		nativeUnits.Text('f', int(precision)),
-		currency.Symbol,
-	)
-}
-
-// Milliseconds gets the current time in milliseconds.
-func Milliseconds() int64 {
-	nanos := time.Now().UnixNano()
-	return nanos / NanosecondsInMillisecond
-}
-
-// CurrencyBalance returns the balance of an account
-// for a particular currency.
-func CurrencyBalance(
-	ctx context.Context,
-	network *types.NetworkIdentifier,
-	fetcher *fetcher.Fetcher,
-	account *types.AccountIdentifier,
-	currency *types.Currency,
-	block *types.BlockIdentifier,
-) (*types.Amount, *types.BlockIdentifier, error) {
-	var lookupBlock *types.PartialBlockIdentifier
-	if block != nil {
-		lookupBlock = types.ConstructPartialBlockIdentifier(block)
-	}
-
-	liveBlock, liveBalances, _, _, err := fetcher.AccountBalanceRetry(
-		ctx,
-		network,
-		account,
-		lookupBlock,
-	)
-	if err != nil {
-		return nil, nil, fmt.Errorf(
-			"%w: unable to lookup acccount balance for %s",
-			err,
-			types.PrettyPrintStruct(account),
-		)
-	}
-
-	liveAmount, err := types.ExtractAmount(liveBalances, currency)
-	if err != nil {
-		return nil, nil, fmt.Errorf(
-			"%w: could not get %s currency balance for %s",
-			err,
-			types.PrettyPrintStruct(currency),
-			types.PrettyPrintStruct(account),
-		)
-	}
-
-	return liveAmount, liveBlock, nil
-}


Test patch:
diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go
index 138176f..cef51e4 100644
--- a/configuration/configuration_test.go
+++ b/configuration/configuration_test.go
@@ -19,9 +19,9 @@ import (
 	"os"
 	"testing"
 
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -108,8 +108,8 @@ var (
 
 	invalidPrefundedAccounts = &Configuration{
 		Construction: &ConstructionConfiguration{
-			PrefundedAccounts: []*PrefundedAccount{
-				&PrefundedAccount{
+			PrefundedAccounts: []*storage.PrefundedAccount{
+				{
 					PrivateKeyHex: "hello",
 				},
 			},
diff --git a/pkg/constructor/constructor_test.go b/pkg/constructor/constructor_test.go
index f0d8f0c..0dab0aa 100644
--- a/pkg/constructor/constructor_test.go
+++ b/pkg/constructor/constructor_test.go
@@ -24,10 +24,10 @@ import (
 	"github.com/coinbase/rosetta-cli/configuration"
 	mocks "github.com/coinbase/rosetta-cli/mocks/constructor"
 	"github.com/coinbase/rosetta-cli/pkg/scenario"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
 
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/parser"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
diff --git a/pkg/storage/badger_storage_test.go b/pkg/storage/badger_storage_test.go
deleted file mode 100644
index 8b44ac1..0000000
--- a/pkg/storage/badger_storage_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"fmt"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestDatabase(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	t.Run("No key exists", func(t *testing.T) {
-		exists, value, err := database.Get(ctx, []byte("hello"))
-		assert.False(t, exists)
-		assert.Nil(t, value)
-		assert.NoError(t, err)
-	})
-
-	t.Run("Set key", func(t *testing.T) {
-		err := database.Set(ctx, []byte("hello"), []byte("hola"))
-		assert.NoError(t, err)
-	})
-
-	t.Run("Get key", func(t *testing.T) {
-		exists, value, err := database.Get(ctx, []byte("hello"))
-		assert.True(t, exists)
-		assert.Equal(t, []byte("hola"), value)
-		assert.NoError(t, err)
-	})
-
-	t.Run("Scan", func(t *testing.T) {
-		storedValues := make([][]byte, 100)
-		for i := 0; i < 100; i++ {
-			v := []byte(fmt.Sprintf("%d", i))
-			err := database.Set(ctx, []byte(fmt.Sprintf("test/%d", i)), v)
-			assert.NoError(t, err)
-
-			storedValues[i] = v
-		}
-
-		values, err := database.Scan(ctx, []byte("test/"))
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, storedValues, values)
-	})
-}
-
-func TestDatabaseTransaction(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	t.Run("Set and get within a transaction", func(t *testing.T) {
-		txn := database.NewDatabaseTransaction(ctx, true)
-		assert.NoError(t, txn.Set(ctx, []byte("hello"), []byte("hola")))
-
-		// Ensure tx does not affect db
-		exists, value, err := database.Get(ctx, []byte("hello"))
-		assert.False(t, exists)
-		assert.Nil(t, value)
-		assert.NoError(t, err)
-
-		assert.NoError(t, txn.Commit(ctx))
-
-		exists, value, err = database.Get(ctx, []byte("hello"))
-		assert.True(t, exists)
-		assert.Equal(t, []byte("hola"), value)
-		assert.NoError(t, err)
-	})
-
-	t.Run("Discard transaction", func(t *testing.T) {
-		txn := database.NewDatabaseTransaction(ctx, true)
-		assert.NoError(t, txn.Set(ctx, []byte("hello"), []byte("world")))
-
-		txn.Discard(ctx)
-
-		exists, value, err := database.Get(ctx, []byte("hello"))
-		assert.True(t, exists)
-		assert.Equal(t, []byte("hola"), value)
-		assert.NoError(t, err)
-	})
-
-	t.Run("Delete within a transaction", func(t *testing.T) {
-		txn := database.NewDatabaseTransaction(ctx, true)
-		assert.NoError(t, txn.Delete(ctx, []byte("hello")))
-		assert.NoError(t, txn.Commit(ctx))
-
-		exists, value, err := database.Get(ctx, []byte("hello"))
-		assert.False(t, exists)
-		assert.Nil(t, value)
-		assert.NoError(t, err)
-	})
-}
diff --git a/pkg/storage/balance_storage_test.go b/pkg/storage/balance_storage_test.go
deleted file mode 100644
index ca855fb..0000000
--- a/pkg/storage/balance_storage_test.go
+++ /dev/null
@@ -1,822 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"encoding/json"
-	"errors"
-	"io/ioutil"
-	"path"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-sdk-go/asserter"
-	"github.com/coinbase/rosetta-sdk-go/parser"
-	"github.com/coinbase/rosetta-sdk-go/reconciler"
-	"github.com/coinbase/rosetta-sdk-go/types"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestBalance(t *testing.T) {
-	var (
-		genesisAccount = &types.AccountIdentifier{
-			Address: "genesis",
-		}
-		account = &types.AccountIdentifier{
-			Address: "blah",
-		}
-		account2 = &types.AccountIdentifier{
-			Address: "blah2",
-		}
-		account3 = &types.AccountIdentifier{
-			Address: "blah3",
-		}
-		subAccount = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-			},
-		}
-		subAccountNewPointer = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-			},
-		}
-		subAccountMetadata = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-				Metadata: map[string]interface{}{
-					"cool": "hello",
-				},
-			},
-		}
-		subAccountMetadataNewPointer = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-				Metadata: map[string]interface{}{
-					"cool": "hello",
-				},
-			},
-		}
-		subAccountMetadata2 = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-				Metadata: map[string]interface{}{
-					"cool": float64(10),
-				},
-			},
-		}
-		subAccountMetadata2NewPointer = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-				Metadata: map[string]interface{}{
-					"cool": float64(10),
-				},
-			},
-		}
-		currency = &types.Currency{
-			Symbol:   "BLAH",
-			Decimals: 2,
-		}
-		amount = &types.Amount{
-			Value:    "100",
-			Currency: currency,
-		}
-		amountWithPrevious = &types.Amount{
-			Value:    "110",
-			Currency: currency,
-		}
-		amountNilCurrency = &types.Amount{
-			Value: "100",
-		}
-		genesisBlock = &types.BlockIdentifier{
-			Hash:  "0",
-			Index: 0,
-		}
-		newBlock = &types.BlockIdentifier{
-			Hash:  "kdasdj",
-			Index: 123890,
-		}
-		newBlock2 = &types.BlockIdentifier{
-			Hash:  "pkdasdj",
-			Index: 123890,
-		}
-		result = &types.Amount{
-			Value:    "200",
-			Currency: currency,
-		}
-		newBlock3 = &types.BlockIdentifier{
-			Hash:  "pkdgdj",
-			Index: 123891,
-		}
-		largeDeduction = &types.Amount{
-			Value:    "-1000",
-			Currency: currency,
-		}
-		mockHelper = &MockBalanceStorageHelper{
-			AccountBalances: map[string]string{
-				"genesis": "100",
-			},
-		}
-	)
-
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBalanceStorage(database)
-	storage.Initialize(mockHelper, nil)
-
-	t.Run("Get unset balance", func(t *testing.T) {
-		amount, block, err := storage.GetBalance(ctx, account, currency, newBlock)
-		assert.NoError(t, err)
-		assert.Equal(t, &types.Amount{
-			Value:    "0",
-			Currency: currency,
-		}, amount)
-		assert.Equal(t, newBlock, block)
-	})
-
-	t.Run("Set and get genesis balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    genesisAccount,
-				Currency:   currency,
-				Block:      genesisBlock,
-				Difference: amount.Value,
-			},
-			genesisBlock,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		amount, block, err := storage.GetBalance(ctx, genesisAccount, currency, genesisBlock)
-		assert.NoError(t, err)
-		assert.Equal(t, &types.Amount{
-			Value:    "100",
-			Currency: currency,
-		}, amount)
-		assert.Equal(t, genesisBlock, block)
-	})
-
-	t.Run("Set and get balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   currency,
-				Block:      newBlock,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		retrievedAmount, block, err := storage.GetBalance(ctx, account, currency, newBlock)
-		assert.NoError(t, err)
-		assert.Equal(t, amount, retrievedAmount)
-		assert.Equal(t, newBlock, block)
-	})
-
-	t.Run("Set and get balance with storage helper", func(t *testing.T) {
-		mockHelper.AccountBalanceAmount = "10"
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account3,
-				Currency:   currency,
-				Block:      newBlock,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		retrievedAmount, block, err := storage.GetBalance(ctx, account3, currency, newBlock)
-		assert.NoError(t, err)
-		assert.Equal(t, amountWithPrevious, retrievedAmount)
-		assert.Equal(t, newBlock, block)
-
-		mockHelper.AccountBalanceAmount = ""
-	})
-
-	t.Run("Set balance with nil currency", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   nil,
-				Block:      newBlock,
-				Difference: amountNilCurrency.Value,
-			},
-			nil,
-		)
-		assert.EqualError(t, err, "invalid currency")
-		txn.Discard(ctx)
-
-		retrievedAmount, block, err := storage.GetBalance(ctx, account, currency, newBlock)
-		assert.NoError(t, err)
-		assert.Equal(t, amount, retrievedAmount)
-		assert.Equal(t, newBlock, block)
-	})
-
-	t.Run("Modify existing balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   currency,
-				Block:      newBlock2,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		retrievedAmount, block, err := storage.GetBalance(ctx, account, currency, newBlock2)
-		assert.NoError(t, err)
-		assert.Equal(t, result, retrievedAmount)
-		assert.Equal(t, newBlock2, block)
-	})
-
-	t.Run("Discard transaction", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   currency,
-				Block:      newBlock3,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-
-		// Get balance during transaction
-		retrievedAmount, block, err := storage.GetBalance(ctx, account, currency, newBlock2)
-		assert.NoError(t, err)
-		assert.Equal(t, result, retrievedAmount)
-		assert.Equal(t, newBlock2, block)
-
-		txn.Discard(ctx)
-	})
-
-	t.Run("Attempt modification to push balance negative on existing account", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   largeDeduction.Currency,
-				Block:      newBlock3,
-				Difference: largeDeduction.Value,
-			},
-			nil,
-		)
-		assert.True(t, errors.Is(err, ErrNegativeBalance))
-		txn.Discard(ctx)
-	})
-
-	t.Run("Attempt modification to push balance negative on new acct", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account2,
-				Currency:   largeDeduction.Currency,
-				Block:      newBlock2,
-				Difference: largeDeduction.Value,
-			},
-			nil,
-		)
-		assert.Error(t, err)
-		assert.True(t, errors.Is(err, ErrNegativeBalance))
-		txn.Discard(ctx)
-	})
-
-	t.Run("sub account set and get balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    subAccount,
-				Currency:   amount.Currency,
-				Block:      newBlock,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		retrievedAmount, block, err := storage.GetBalance(
-			ctx,
-			subAccountNewPointer,
-			amount.Currency,
-			newBlock,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, amount, retrievedAmount)
-		assert.Equal(t, newBlock, block)
-	})
-
-	t.Run("sub account metadata set and get balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    subAccountMetadata,
-				Currency:   amount.Currency,
-				Block:      newBlock,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		retrievedAmount, block, err := storage.GetBalance(
-			ctx,
-			subAccountMetadataNewPointer,
-			amount.Currency,
-			newBlock,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, amount, retrievedAmount)
-		assert.Equal(t, newBlock, block)
-	})
-
-	t.Run("sub account unique metadata set and get balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    subAccountMetadata2,
-				Currency:   amount.Currency,
-				Block:      newBlock,
-				Difference: amount.Value,
-			},
-			nil,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		retrievedAmount, block, err := storage.GetBalance(
-			ctx,
-			subAccountMetadata2NewPointer,
-			amount.Currency,
-			newBlock,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, amount, retrievedAmount)
-		assert.Equal(t, newBlock, block)
-	})
-
-	t.Run("get all set AccountCurrency", func(t *testing.T) {
-		accounts, err := storage.GetAllAccountCurrency(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*reconciler.AccountCurrency{
-			{
-				Account:  genesisAccount,
-				Currency: currency,
-			},
-			{
-				Account:  account,
-				Currency: currency,
-			},
-			{
-				Account:  account3,
-				Currency: currency,
-			},
-			{
-				Account:  subAccount,
-				Currency: currency,
-			},
-			{
-				Account:  subAccountMetadata,
-				Currency: currency,
-			},
-			{
-				Account:  subAccountMetadata2,
-				Currency: currency,
-			},
-		}, accounts)
-	})
-}
-
-func TestBootstrapBalances(t *testing.T) {
-	var (
-		genesisBlockIdentifier = &types.BlockIdentifier{
-			Index: 0,
-			Hash:  "0",
-		}
-
-		account = &types.AccountIdentifier{
-			Address: "hello",
-		}
-	)
-
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBalanceStorage(database)
-	bootstrapBalancesFile := path.Join(newDir, "balances.csv")
-
-	t.Run("File doesn't exist", func(t *testing.T) {
-		err = storage.BootstrapBalances(
-			ctx,
-			bootstrapBalancesFile,
-			genesisBlockIdentifier,
-		)
-		assert.Contains(t, err.Error(), "no such file or directory")
-	})
-
-	t.Run("Set balance successfully", func(t *testing.T) {
-		amount := &types.Amount{
-			Value: "10",
-			Currency: &types.Currency{
-				Symbol:   "BTC",
-				Decimals: 8,
-			},
-		}
-
-		file, err := json.MarshalIndent([]*BootstrapBalance{
-			{
-				Account:  account,
-				Value:    amount.Value,
-				Currency: amount.Currency,
-			},
-		}, "", " ")
-		assert.NoError(t, err)
-
-		assert.NoError(
-			t,
-			ioutil.WriteFile(bootstrapBalancesFile, file, utils.DefaultFilePermissions),
-		)
-
-		err = storage.BootstrapBalances(
-			ctx,
-			bootstrapBalancesFile,
-			genesisBlockIdentifier,
-		)
-		assert.NoError(t, err)
-
-		retrievedAmount, blockIdentifier, err := storage.GetBalance(
-			ctx,
-			account,
-			amount.Currency,
-			genesisBlockIdentifier,
-		)
-
-		assert.Equal(t, amount, retrievedAmount)
-		assert.Equal(t, genesisBlockIdentifier, blockIdentifier)
-		assert.NoError(t, err)
-	})
-
-	t.Run("Invalid file contents", func(t *testing.T) {
-		assert.NoError(
-			t,
-			ioutil.WriteFile(
-				bootstrapBalancesFile,
-				[]byte("bad file"),
-				utils.DefaultFilePermissions,
-			),
-		)
-
-		err := storage.BootstrapBalances(
-			ctx,
-			bootstrapBalancesFile,
-			genesisBlockIdentifier,
-		)
-		assert.Error(t, err)
-	})
-
-	t.Run("Invalid account balance", func(t *testing.T) {
-		amount := &types.Amount{
-			Value: "-10",
-			Currency: &types.Currency{
-				Symbol:   "BTC",
-				Decimals: 8,
-			},
-		}
-
-		file, err := json.MarshalIndent([]*BootstrapBalance{
-			{
-				Account:  account,
-				Value:    amount.Value,
-				Currency: amount.Currency,
-			},
-		}, "", " ")
-		assert.NoError(t, err)
-
-		assert.NoError(
-			t,
-			ioutil.WriteFile(bootstrapBalancesFile, file, utils.DefaultFilePermissions),
-		)
-
-		err = storage.BootstrapBalances(
-			ctx,
-			bootstrapBalancesFile,
-			genesisBlockIdentifier,
-		)
-		assert.EqualError(t, err, "cannot bootstrap zero or negative balance -10")
-	})
-
-	t.Run("Invalid account value", func(t *testing.T) {
-		amount := &types.Amount{
-			Value: "goodbye",
-			Currency: &types.Currency{
-				Symbol:   "BTC",
-				Decimals: 8,
-			},
-		}
-
-		file, err := json.MarshalIndent([]*BootstrapBalance{
-			{
-				Account:  account,
-				Value:    amount.Value,
-				Currency: amount.Currency,
-			},
-		}, "", " ")
-		assert.NoError(t, err)
-
-		assert.NoError(
-			t,
-			ioutil.WriteFile(bootstrapBalancesFile, file, utils.DefaultFilePermissions),
-		)
-
-		err = storage.BootstrapBalances(
-			ctx,
-			bootstrapBalancesFile,
-			genesisBlockIdentifier,
-		)
-		assert.EqualError(t, err, "goodbye is not an integer")
-	})
-}
-
-func TestBalanceReconciliation(t *testing.T) {
-	var (
-		account = &types.AccountIdentifier{
-			Address: "blah",
-		}
-		subAccountMetadata2 = &types.AccountIdentifier{
-			Address: "blah",
-			SubAccount: &types.SubAccountIdentifier{
-				Address: "stake",
-				Metadata: map[string]interface{}{
-					"cool": float64(10),
-				},
-			},
-		}
-		currency = &types.Currency{
-			Symbol:   "BLAH",
-			Decimals: 2,
-		}
-		currency2 = &types.Currency{
-			Symbol:   "BLAH2",
-			Decimals: 4,
-		}
-		genesisBlock = &types.BlockIdentifier{
-			Hash:  "0",
-			Index: 0,
-		}
-		newBlock = &types.BlockIdentifier{
-			Hash:  "kdasdj",
-			Index: 123890,
-		}
-		mockHelper = &MockBalanceStorageHelper{
-			AccountBalances: map[string]string{
-				"genesis": "100",
-			},
-		}
-	)
-
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBalanceStorage(database)
-	storage.Initialize(mockHelper, nil)
-
-	t.Run("attempt to store reconciliation for non-existent account", func(t *testing.T) {
-		err := storage.Reconciled(ctx, account, currency, genesisBlock)
-		assert.Error(t, err)
-
-		coverage, err := storage.ReconciliationCoverage(ctx, 0)
-		assert.NoError(t, err)
-		assert.Equal(t, 0.0, coverage)
-	})
-
-	t.Run("set balance", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err := storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   currency,
-				Block:      genesisBlock,
-				Difference: "100",
-			},
-			genesisBlock,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		coverage, err := storage.ReconciliationCoverage(ctx, 0)
-		assert.NoError(t, err)
-		assert.Equal(t, 0.0, coverage)
-	})
-
-	t.Run("store reconciliation", func(t *testing.T) {
-		err := storage.Reconciled(ctx, account, currency, genesisBlock)
-		assert.NoError(t, err)
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err = storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    account,
-				Currency:   currency2,
-				Block:      genesisBlock,
-				Difference: "200",
-			},
-			genesisBlock,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		coverage, err := storage.ReconciliationCoverage(ctx, 0)
-		assert.NoError(t, err)
-		assert.Equal(t, 0.5, coverage)
-
-		coverage, err = storage.ReconciliationCoverage(ctx, 1)
-		assert.NoError(t, err)
-		assert.Equal(t, 0.0, coverage)
-	})
-
-	t.Run("update reconciliation", func(t *testing.T) {
-		err := storage.Reconciled(ctx, account, currency, newBlock)
-		assert.NoError(t, err)
-
-		coverage, err := storage.ReconciliationCoverage(ctx, 0)
-		assert.NoError(t, err)
-		assert.Equal(t, 0.5, coverage)
-
-		coverage, err = storage.ReconciliationCoverage(ctx, 1)
-		assert.NoError(t, err)
-		assert.Equal(t, 0.5, coverage)
-	})
-
-	t.Run("add unreconciled", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		err = storage.UpdateBalance(
-			ctx,
-			txn,
-			&parser.BalanceChange{
-				Account:    subAccountMetadata2,
-				Currency:   currency2,
-				Block:      newBlock,
-				Difference: "200",
-			},
-			newBlock,
-		)
-		assert.NoError(t, err)
-		assert.NoError(t, txn.Commit(ctx))
-
-		coverage, err := storage.ReconciliationCoverage(ctx, 1)
-		assert.NoError(t, err)
-		assert.Equal(t, float64(1)/float64(3), coverage)
-	})
-}
-
-var _ BalanceStorageHelper = (*MockBalanceStorageHelper)(nil)
-
-type MockBalanceStorageHelper struct {
-	AccountBalanceAmount string
-	AccountBalances      map[string]string
-	ExemptAccounts       []*reconciler.AccountCurrency
-}
-
-func (h *MockBalanceStorageHelper) AccountBalance(
-	ctx context.Context,
-	account *types.AccountIdentifier,
-	currency *types.Currency,
-	block *types.BlockIdentifier,
-) (*types.Amount, error) {
-	if balance, ok := h.AccountBalances[account.Address]; ok {
-		return &types.Amount{
-			Value:    balance,
-			Currency: currency,
-		}, nil
-	}
-
-	value := "0"
-	if len(h.AccountBalanceAmount) > 0 {
-		value = h.AccountBalanceAmount
-	}
-
-	return &types.Amount{
-		Value:    value,
-		Currency: currency,
-	}, nil
-}
-
-func (h *MockBalanceStorageHelper) Asserter() *asserter.Asserter {
-	a, _ := asserter.NewClientWithOptions(
-		&types.NetworkIdentifier{
-			Blockchain: "bitcoin",
-			Network:    "mainnet",
-		},
-		&types.BlockIdentifier{
-			Hash:  "block 0",
-			Index: 0,
-		},
-		[]string{"Transfer"},
-		[]*types.OperationStatus{
-			{
-				Status:     "Success",
-				Successful: true,
-			},
-		},
-		[]*types.Error{},
-	)
-	return a
-}
-
-func (h *MockBalanceStorageHelper) ExemptFunc() parser.ExemptOperation {
-	return func(op *types.Operation) bool {
-		thisAcct := &reconciler.AccountCurrency{
-			Account:  op.Account,
-			Currency: op.Amount.Currency,
-		}
-
-		for _, acct := range h.ExemptAccounts {
-			if types.Hash(acct) == types.Hash(thisAcct) {
-				return true
-			}
-		}
-
-		return false
-	}
-}
diff --git a/pkg/storage/block_storage_test.go b/pkg/storage/block_storage_test.go
deleted file mode 100644
index 3f50d8a..0000000
--- a/pkg/storage/block_storage_test.go
+++ /dev/null
@@ -1,601 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"fmt"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-sdk-go/types"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestHeadBlockIdentifier(t *testing.T) {
-	var (
-		newBlockIdentifier = &types.BlockIdentifier{
-			Hash:  "blah",
-			Index: 0,
-		}
-		newBlockIdentifier2 = &types.BlockIdentifier{
-			Hash:  "blah2",
-			Index: 1,
-		}
-	)
-
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBlockStorage(database)
-
-	t.Run("No head block set", func(t *testing.T) {
-		blockIdentifier, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.EqualError(t, err, ErrHeadBlockNotFound.Error())
-		assert.Nil(t, blockIdentifier)
-	})
-
-	t.Run("Set and get head block", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		assert.NoError(t, storage.StoreHeadBlockIdentifier(ctx, txn, newBlockIdentifier))
-		assert.NoError(t, txn.Commit(ctx))
-
-		blockIdentifier, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlockIdentifier, blockIdentifier)
-	})
-
-	t.Run("Discard head block update", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		assert.NoError(t, storage.StoreHeadBlockIdentifier(ctx, txn,
-			&types.BlockIdentifier{
-				Hash:  "no blah",
-				Index: 10,
-			}),
-		)
-		txn.Discard(ctx)
-
-		blockIdentifier, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlockIdentifier, blockIdentifier)
-	})
-
-	t.Run("Multiple updates to head block", func(t *testing.T) {
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		assert.NoError(t, storage.StoreHeadBlockIdentifier(ctx, txn, newBlockIdentifier2))
-		assert.NoError(t, txn.Commit(ctx))
-
-		blockIdentifier, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		txn.Discard(ctx)
-		assert.Equal(t, newBlockIdentifier2, blockIdentifier)
-	})
-}
-
-func simpleTransactionFactory(
-	hash string,
-	address string,
-	value string,
-	currency *types.Currency,
-) *types.Transaction {
-	return &types.Transaction{
-		TransactionIdentifier: &types.TransactionIdentifier{
-			Hash: hash,
-		},
-		Operations: []*types.Operation{
-			{
-				OperationIdentifier: &types.OperationIdentifier{
-					Index: 0,
-				},
-				Type:   "Transfer",
-				Status: "Success",
-				Account: &types.AccountIdentifier{
-					Address: address,
-				},
-				Amount: &types.Amount{
-					Value:    value,
-					Currency: currency,
-				},
-			},
-		},
-	}
-}
-
-var (
-	genesisBlock = &types.Block{
-		BlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 0",
-			Index: 0,
-		},
-		ParentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 0",
-			Index: 0,
-		},
-		Timestamp: 1,
-	}
-
-	newBlock = &types.Block{
-		BlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 1",
-			Index: 1,
-		},
-		ParentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 0",
-			Index: 0,
-		},
-		Timestamp: 1,
-		Transactions: []*types.Transaction{
-			simpleTransactionFactory("blahTx", "addr1", "100", &types.Currency{Symbol: "hello"}),
-		},
-	}
-
-	badBlockIdentifier = &types.BlockIdentifier{
-		Hash:  "missing blah",
-		Index: 0,
-	}
-
-	newBlock2 = &types.Block{
-		BlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 2",
-			Index: 2,
-		},
-		ParentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 1",
-			Index: 1,
-		},
-		Timestamp: 1,
-		Transactions: []*types.Transaction{
-			simpleTransactionFactory("blahTx", "addr1", "100", &types.Currency{Symbol: "hello"}),
-		},
-	}
-
-	complexBlock = &types.Block{
-		BlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 3",
-			Index: 3,
-		},
-		ParentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 2",
-			Index: 2,
-		},
-		Timestamp: 1,
-		Transactions: []*types.Transaction{
-			{
-				TransactionIdentifier: &types.TransactionIdentifier{
-					Hash: "blahTx 2",
-				},
-				Operations: []*types.Operation{
-					{
-						OperationIdentifier: &types.OperationIdentifier{
-							Index: 0,
-						},
-						Type:   "Transfer",
-						Status: "Success",
-						Account: &types.AccountIdentifier{
-							Address: "addr1",
-							SubAccount: &types.SubAccountIdentifier{
-								Address: "staking",
-								Metadata: map[string]interface{}{
-									"other_complex_stuff": []interface{}{
-										map[string]interface{}{
-											"neat": "test",
-											"more complex": map[string]interface{}{
-												"neater": "testier",
-											},
-										},
-										map[string]interface{}{
-											"i love": "ice cream",
-										},
-									},
-								},
-							},
-						},
-						Amount: &types.Amount{
-							Value: "100",
-							Currency: &types.Currency{
-								Symbol: "hello",
-							},
-						},
-					},
-				},
-				Metadata: map[string]interface{}{
-					"other_stuff":  []interface{}{"stuff"},
-					"simple_stuff": "abc",
-					"super_complex_stuff": map[string]interface{}{
-						"neat": "test",
-						"more complex": map[string]interface{}{
-							"neater": "testier",
-						},
-					},
-				},
-			},
-		},
-	}
-
-	duplicateTxBlock = &types.Block{
-		BlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 4",
-			Index: 4,
-		},
-		ParentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 3",
-			Index: 3,
-		},
-		Timestamp: 1,
-		Transactions: []*types.Transaction{
-			simpleTransactionFactory("blahTx3", "addr2", "200", &types.Currency{Symbol: "hello"}),
-			simpleTransactionFactory("blahTx3", "addr2", "200", &types.Currency{Symbol: "hello"}),
-		},
-	}
-
-	gapBlock = &types.Block{
-		BlockIdentifier: &types.BlockIdentifier{
-			Hash:  "block 10",
-			Index: 10,
-		},
-		ParentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "blah 3",
-			Index: 3,
-		},
-	}
-)
-
-func findTransactionWithDbTransaction(
-	ctx context.Context,
-	storage *BlockStorage,
-	transactionIdentifier *types.TransactionIdentifier,
-) (*types.BlockIdentifier, *types.Transaction, error) {
-	txn := storage.db.NewDatabaseTransaction(ctx, false)
-	defer txn.Discard(ctx)
-
-	return storage.FindTransaction(
-		ctx,
-		transactionIdentifier,
-		txn,
-	)
-}
-
-func TestBlock(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBlockStorage(database)
-
-	t.Run("Get non-existent tx", func(t *testing.T) {
-		newestBlock, transaction, err := findTransactionWithDbTransaction(
-			ctx,
-			storage,
-			newBlock.Transactions[0].TransactionIdentifier,
-		)
-		assert.NoError(t, err)
-		assert.Nil(t, newestBlock)
-		assert.Nil(t, transaction)
-	})
-
-	t.Run("Set and get block", func(t *testing.T) {
-		err := storage.AddBlock(ctx, newBlock)
-		assert.NoError(t, err)
-
-		block, err := storage.GetBlock(
-			ctx,
-			types.ConstructPartialBlockIdentifier(newBlock.BlockIdentifier),
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock, block)
-
-		block, err = storage.GetBlock(
-			ctx,
-			&types.PartialBlockIdentifier{Index: &newBlock.BlockIdentifier.Index},
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock, block)
-
-		block, err = storage.GetBlock(ctx, nil)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock, block)
-
-		head, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock.BlockIdentifier, head)
-
-		newestBlock, transaction, err := findTransactionWithDbTransaction(
-			ctx,
-			storage,
-			newBlock.Transactions[0].TransactionIdentifier,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock.BlockIdentifier, newestBlock)
-		assert.Equal(t, newBlock.Transactions[0], transaction)
-	})
-
-	t.Run("Get non-existent block", func(t *testing.T) {
-		identifier := types.ConstructPartialBlockIdentifier(badBlockIdentifier)
-		block, err := storage.GetBlock(ctx, identifier)
-		assert.EqualError(
-			t,
-			err,
-			fmt.Errorf("%w: %+v", ErrBlockNotFound, identifier).Error(),
-		)
-		assert.Nil(t, block)
-	})
-
-	t.Run("Get non-existent block index", func(t *testing.T) {
-		badIndex := int64(100000)
-		identifier := &types.PartialBlockIdentifier{Index: &badIndex}
-		block, err := storage.GetBlock(ctx, identifier)
-		assert.EqualError(
-			t,
-			err,
-			fmt.Errorf("%w: %+v", ErrBlockNotFound, identifier).Error(),
-		)
-		assert.Nil(t, block)
-	})
-
-	t.Run("Set duplicate block hash", func(t *testing.T) {
-		err = storage.AddBlock(ctx, newBlock)
-		assert.Contains(t, err.Error(), ErrDuplicateKey.Error())
-	})
-
-	t.Run("Set duplicate transaction hash (from prior block)", func(t *testing.T) {
-		err = storage.AddBlock(ctx, newBlock2)
-		assert.NoError(t, err)
-
-		block, err := storage.GetBlock(
-			ctx,
-			types.ConstructPartialBlockIdentifier(newBlock2.BlockIdentifier),
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2, block)
-
-		block, err = storage.GetBlock(
-			ctx,
-			&types.PartialBlockIdentifier{Index: &newBlock2.BlockIdentifier.Index},
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2, block)
-
-		block, err = storage.GetBlock(ctx, nil)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2, block)
-
-		head, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2.BlockIdentifier, head)
-
-		newestBlock, transaction, err := findTransactionWithDbTransaction(
-			ctx,
-			storage,
-			newBlock.Transactions[0].TransactionIdentifier,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2.BlockIdentifier, newestBlock)
-		assert.Equal(t, newBlock2.Transactions[0], transaction)
-	})
-
-	t.Run("Remove block and re-set block of same hash", func(t *testing.T) {
-		err := storage.RemoveBlock(ctx, newBlock2.BlockIdentifier)
-		assert.NoError(t, err)
-
-		head, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2.ParentBlockIdentifier, head)
-
-		err = storage.AddBlock(ctx, newBlock2)
-		assert.NoError(t, err)
-
-		head, err = storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2.BlockIdentifier, head)
-
-		newestBlock, transaction, err := findTransactionWithDbTransaction(
-			ctx,
-			storage,
-			newBlock.Transactions[0].TransactionIdentifier,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2.BlockIdentifier, newestBlock)
-		assert.Equal(t, newBlock2.Transactions[0], transaction)
-	})
-
-	t.Run("Add block with complex metadata", func(t *testing.T) {
-		err := storage.AddBlock(ctx, complexBlock)
-		assert.NoError(t, err)
-
-		block, err := storage.GetBlock(
-			ctx,
-			types.ConstructPartialBlockIdentifier(complexBlock.BlockIdentifier),
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, complexBlock, block)
-
-		head, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, complexBlock.BlockIdentifier, head)
-
-		newestBlock, transaction, err := findTransactionWithDbTransaction(
-			ctx,
-			storage,
-			newBlock.Transactions[0].TransactionIdentifier,
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, newBlock2.BlockIdentifier, newestBlock)
-		assert.Equal(t, newBlock2.Transactions[0], transaction)
-	})
-
-	t.Run("Set duplicate transaction hash (same block)", func(t *testing.T) {
-		err = storage.AddBlock(ctx, duplicateTxBlock)
-		assert.Contains(t, err.Error(), ErrDuplicateTransactionHash.Error())
-
-		head, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, complexBlock.BlockIdentifier, head)
-	})
-
-	t.Run("Add block after omitted", func(t *testing.T) {
-		err := storage.AddBlock(ctx, gapBlock)
-		assert.NoError(t, err)
-
-		block, err := storage.GetBlock(
-			ctx,
-			types.ConstructPartialBlockIdentifier(gapBlock.BlockIdentifier),
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, gapBlock, block)
-
-		head, err := storage.GetHeadBlockIdentifier(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, gapBlock.BlockIdentifier, head)
-	})
-}
-
-func TestCreateBlockCache(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBlockStorage(database)
-
-	t.Run("no blocks processed", func(t *testing.T) {
-		assert.Equal(t, []*types.BlockIdentifier{}, storage.CreateBlockCache(ctx))
-	})
-
-	t.Run("1 block processed", func(t *testing.T) {
-		err = storage.AddBlock(ctx, genesisBlock)
-		assert.NoError(t, err)
-		assert.Equal(
-			t,
-			[]*types.BlockIdentifier{genesisBlock.BlockIdentifier},
-			storage.CreateBlockCache(ctx),
-		)
-	})
-
-	t.Run("2 blocks processed", func(t *testing.T) {
-		err = storage.AddBlock(ctx, newBlock)
-		assert.NoError(t, err)
-		assert.Equal(
-			t,
-			[]*types.BlockIdentifier{genesisBlock.BlockIdentifier, newBlock.BlockIdentifier},
-			storage.CreateBlockCache(ctx),
-		)
-	})
-
-	t.Run("3 blocks processed (with gap)", func(t *testing.T) {
-		simpleGap := &types.Block{
-			BlockIdentifier: &types.BlockIdentifier{
-				Hash:  "block 100",
-				Index: 100,
-			},
-			ParentBlockIdentifier: newBlock.BlockIdentifier,
-		}
-
-		err = storage.AddBlock(ctx, simpleGap)
-		assert.NoError(t, err)
-		assert.Equal(
-			t,
-			[]*types.BlockIdentifier{
-				genesisBlock.BlockIdentifier,
-				newBlock.BlockIdentifier,
-				simpleGap.BlockIdentifier,
-			},
-			storage.CreateBlockCache(ctx),
-		)
-	})
-}
-
-func TestAtTip(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBlockStorage(database)
-	tipDelay := int64(100)
-
-	t.Run("no blocks processed", func(t *testing.T) {
-		atTip, blockIdentifier, err := storage.AtTip(ctx, tipDelay)
-		assert.NoError(t, err)
-		assert.False(t, atTip)
-		assert.Nil(t, blockIdentifier)
-	})
-
-	t.Run("Add old block", func(t *testing.T) {
-		err := storage.AddBlock(ctx, &types.Block{
-			BlockIdentifier: &types.BlockIdentifier{
-				Hash:  "block 0",
-				Index: 0,
-			},
-			ParentBlockIdentifier: &types.BlockIdentifier{
-				Hash:  "block 0",
-				Index: 0,
-			},
-			Timestamp: utils.Milliseconds() - (3 * tipDelay * utils.MillisecondsInSecond),
-		})
-		assert.NoError(t, err)
-
-		atTip, blockIdentifier, err := storage.AtTip(ctx, tipDelay)
-		assert.NoError(t, err)
-		assert.False(t, atTip)
-		assert.Nil(t, blockIdentifier)
-	})
-
-	t.Run("Add new block", func(t *testing.T) {
-		err := storage.AddBlock(ctx, &types.Block{
-			BlockIdentifier: &types.BlockIdentifier{
-				Hash:  "block 1",
-				Index: 1,
-			},
-			ParentBlockIdentifier: &types.BlockIdentifier{
-				Hash:  "block 0",
-				Index: 0,
-			},
-			Timestamp: utils.Milliseconds(),
-		})
-		assert.NoError(t, err)
-
-		atTip, blockIdentifier, err := storage.AtTip(ctx, tipDelay)
-		assert.NoError(t, err)
-		assert.True(t, atTip)
-		assert.Equal(t, &types.BlockIdentifier{
-			Hash:  "block 1",
-			Index: 1,
-		}, blockIdentifier)
-	})
-}
diff --git a/pkg/storage/broadcast_storage_test.go b/pkg/storage/broadcast_storage_test.go
deleted file mode 100644
index 99eb749..0000000
--- a/pkg/storage/broadcast_storage_test.go
+++ /dev/null
@@ -1,968 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-sdk-go/types"
-	"github.com/stretchr/testify/assert"
-)
-
-const (
-	confirmationDepth   = int64(2)
-	staleDepth          = int64(3)
-	broadcastLimit      = 3
-	broadcastTipDelay   = 10
-	broadcastBehindTip  = false
-	blockBroadcastLimit = 10
-)
-
-func blockFiller(start int64, end int64) []*types.Block {
-	blocks := []*types.Block{}
-	for i := start; i < end; i++ {
-		parentIndex := i - 1
-		if parentIndex < 0 {
-			parentIndex = 0
-		}
-		blocks = append(blocks, &types.Block{
-			BlockIdentifier: &types.BlockIdentifier{
-				Index: i,
-				Hash:  fmt.Sprintf("block %d", i),
-			},
-			ParentBlockIdentifier: &types.BlockIdentifier{
-				Index: parentIndex,
-				Hash:  fmt.Sprintf("block %d", parentIndex),
-			},
-		})
-	}
-
-	return blocks
-}
-
-func opFiller(sender string, opNumber int) []*types.Operation {
-	ops := make([]*types.Operation, opNumber)
-	for i := 0; i < opNumber; i++ {
-		ops[i] = &types.Operation{
-			OperationIdentifier: &types.OperationIdentifier{
-				Index: int64(i),
-			},
-			Account: &types.AccountIdentifier{
-				Address: sender,
-				SubAccount: &types.SubAccountIdentifier{
-					Address: sender,
-				},
-			},
-		}
-	}
-
-	return ops
-}
-
-func TestBroadcastStorageBroadcastSuccess(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBroadcastStorage(
-		database,
-		confirmationDepth,
-		staleDepth,
-		broadcastLimit,
-		broadcastTipDelay,
-		broadcastBehindTip,
-		blockBroadcastLimit,
-	)
-	mockHelper := &MockBroadcastStorageHelper{}
-	mockHandler := &MockBroadcastStorageHandler{}
-	storage.Initialize(mockHelper, mockHandler)
-
-	send1 := opFiller("addr 1", 11)
-	send2 := opFiller("addr 2", 13)
-	mockHelper.Transactions = map[string]*types.TransactionIdentifier{
-		"payload 1": {Hash: "tx 1"},
-		"payload 2": {Hash: "tx 2"},
-	}
-
-	mockHelper.AtSyncTip = true
-
-	t.Run("broadcast send 1 before block exists", func(t *testing.T) {
-		err := storage.Broadcast(
-			ctx,
-			"addr 1",
-			send1,
-			&types.TransactionIdentifier{Hash: "tx 1"},
-			"payload 1",
-		)
-		assert.NoError(t, err)
-
-		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 1"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:     "addr 1",
-				Intent:     send1,
-				Payload:    "payload 1",
-			},
-		}, broadcasts)
-	})
-
-	blocks := blockFiller(0, 6)
-	t.Run("add block 0", func(t *testing.T) {
-		block := blocks[0]
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		commitWorker, err := storage.AddingBlock(ctx, block, txn)
-		assert.NoError(t, err)
-		err = txn.Commit(ctx)
-		assert.NoError(t, err)
-
-		mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-		err = commitWorker(ctx)
-		assert.NoError(t, err)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 1"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:        "addr 1",
-				Intent:        send1,
-				Payload:       "payload 1",
-				LastBroadcast: blocks[0].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-	})
-
-	t.Run("add block 1", func(t *testing.T) {
-		block := blocks[1]
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		commitWorker, err := storage.AddingBlock(ctx, block, txn)
-		assert.NoError(t, err)
-		err = txn.Commit(ctx)
-		assert.NoError(t, err)
-
-		mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-		err = commitWorker(ctx)
-		assert.NoError(t, err)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 1"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:        "addr 1",
-				Intent:        send1,
-				Payload:       "payload 1",
-				LastBroadcast: blocks[0].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-	})
-
-	t.Run("broadcast send 2 after adding a block", func(t *testing.T) {
-		err := storage.Broadcast(
-			ctx,
-			"addr 2",
-			send2,
-			&types.TransactionIdentifier{Hash: "tx 2"},
-			"payload 2",
-		)
-		assert.NoError(t, err)
-
-		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:        "addr 1",
-				Intent:        send1,
-				Payload:       "payload 1",
-				LastBroadcast: blocks[0].BlockIdentifier,
-				Broadcasts:    1,
-			},
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:        "addr 2",
-				Intent:        send2,
-				Payload:       "payload 2",
-				LastBroadcast: blocks[1].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-	})
-
-	t.Run("add block 2", func(t *testing.T) {
-		block := blocks[2]
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		commitWorker, err := storage.AddingBlock(ctx, block, txn)
-		assert.NoError(t, err)
-		err = txn.Commit(ctx)
-		assert.NoError(t, err)
-
-		mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-		err = commitWorker(ctx)
-		assert.NoError(t, err)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:        "addr 1",
-				Intent:        send1,
-				Payload:       "payload 1",
-				LastBroadcast: blocks[2].BlockIdentifier,
-				Broadcasts:    2,
-			},
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:        "addr 2",
-				Intent:        send2,
-				Payload:       "payload 2",
-				LastBroadcast: blocks[1].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{
-			{
-				Hash: "tx 1",
-			},
-		}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-	})
-
-	tx1 := &types.Transaction{
-		TransactionIdentifier: &types.TransactionIdentifier{Hash: "tx 1"},
-		Operations:            send1,
-	}
-	tx2 := &types.Transaction{
-		TransactionIdentifier: &types.TransactionIdentifier{Hash: "tx 2"},
-		Operations:            send2,
-	}
-	mockHelper.FindTransactions = map[string]*findTx{
-		tx1.TransactionIdentifier.Hash: {
-			blockIdentifier: blocks[3].BlockIdentifier,
-			transaction:     tx1,
-		},
-		tx2.TransactionIdentifier.Hash: {
-			blockIdentifier: blocks[4].BlockIdentifier,
-			transaction:     tx2,
-		},
-	}
-	t.Run("add block 3", func(t *testing.T) {
-		block := blocks[3]
-		block.Transactions = []*types.Transaction{tx1}
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		commitWorker, err := storage.AddingBlock(ctx, block, txn)
-		assert.NoError(t, err)
-		err = txn.Commit(ctx)
-		assert.NoError(t, err)
-
-		mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-		err = commitWorker(ctx)
-		assert.NoError(t, err)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:        "addr 1",
-				Intent:        send1,
-				Payload:       "payload 1",
-				LastBroadcast: blocks[2].BlockIdentifier,
-				Broadcasts:    2,
-			},
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:        "addr 2",
-				Intent:        send2,
-				Payload:       "payload 2",
-				LastBroadcast: blocks[1].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{
-			{
-				Hash: "tx 1",
-			},
-		}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-		assert.ElementsMatch(t, []*confirmedTx{}, mockHandler.Confirmed)
-	})
-
-	t.Run("add block 4", func(t *testing.T) {
-		block := blocks[4]
-		block.Transactions = []*types.Transaction{tx2}
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		commitWorker, err := storage.AddingBlock(ctx, block, txn)
-		assert.NoError(t, err)
-		err = txn.Commit(ctx)
-		assert.NoError(t, err)
-
-		mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-		err = commitWorker(ctx)
-		assert.NoError(t, err)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 1)
-		assert.ElementsMatch(t, []string{"addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:        "addr 2",
-				Intent:        send2,
-				Payload:       "payload 2",
-				LastBroadcast: blocks[1].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{
-			{
-				Hash: "tx 1",
-			},
-		}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-		assert.ElementsMatch(t, []*confirmedTx{
-			{
-				blockIdentifier: blocks[3].BlockIdentifier,
-				transaction:     tx1,
-				intent:          send1,
-			},
-		}, mockHandler.Confirmed)
-	})
-
-	t.Run("add block 5", func(t *testing.T) {
-		block := blocks[5]
-
-		txn := storage.db.NewDatabaseTransaction(ctx, true)
-		commitWorker, err := storage.AddingBlock(ctx, block, txn)
-		assert.NoError(t, err)
-		err = txn.Commit(ctx)
-		assert.NoError(t, err)
-
-		mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-		err = commitWorker(ctx)
-		assert.NoError(t, err)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{}, broadcasts)
-
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{
-			{
-				Hash: "tx 1",
-			},
-		}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-		assert.ElementsMatch(t, []*confirmedTx{
-			{
-				blockIdentifier: blocks[3].BlockIdentifier,
-				transaction:     tx1,
-				intent:          send1,
-			},
-			{
-				blockIdentifier: blocks[4].BlockIdentifier,
-				transaction:     tx2,
-				intent:          send2,
-			},
-		}, mockHandler.Confirmed)
-	})
-}
-
-func TestBroadcastStorageBroadcastFailure(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBroadcastStorage(
-		database,
-		confirmationDepth,
-		staleDepth,
-		broadcastLimit,
-		broadcastTipDelay,
-		broadcastBehindTip,
-		blockBroadcastLimit,
-	)
-	mockHelper := &MockBroadcastStorageHelper{}
-	mockHandler := &MockBroadcastStorageHandler{}
-	storage.Initialize(mockHelper, mockHandler)
-
-	t.Run("locked addresses with no broadcasts", func(t *testing.T) {
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
-	})
-
-	send1 := opFiller("addr 1", 11)
-	send1 = append(send1, &types.Operation{
-		OperationIdentifier: &types.OperationIdentifier{
-			Index: int64(len(send1)),
-		},
-		Account: &types.AccountIdentifier{
-			Address: "addr 3",
-		},
-	})
-
-	send2 := opFiller("addr 2", 13)
-	t.Run("broadcast", func(t *testing.T) {
-		err := storage.Broadcast(
-			ctx,
-			"addr 1",
-			send1,
-			&types.TransactionIdentifier{Hash: "tx 1"},
-			"payload 1",
-		)
-		assert.NoError(t, err)
-
-		err = storage.Broadcast(
-			ctx,
-			"addr 2",
-			send2,
-			&types.TransactionIdentifier{Hash: "tx 2"},
-			"payload 2",
-		)
-		assert.NoError(t, err)
-
-		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 3)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2", "addr 3"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:     "addr 1",
-				Intent:     send1,
-				Payload:    "payload 1",
-			},
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:     "addr 2",
-				Intent:     send2,
-				Payload:    "payload 2",
-			},
-		}, broadcasts)
-	})
-
-	t.Run("add blocks and expire", func(t *testing.T) {
-		mockHelper.Transactions = map[string]*types.TransactionIdentifier{
-			"payload 1": {Hash: "tx 1"},
-			// payload 2 will fail
-		}
-		blocks := blockFiller(0, 10)
-		mockHelper.AtSyncTip = true
-		for _, block := range blocks {
-			txn := storage.db.NewDatabaseTransaction(ctx, true)
-			commitWorker, err := storage.AddingBlock(ctx, block, txn)
-			assert.NoError(t, err)
-			err = txn.Commit(ctx)
-			assert.NoError(t, err)
-
-			mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-			err = commitWorker(ctx)
-			assert.NoError(t, err)
-		}
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{}, broadcasts)
-
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{
-			{Hash: "tx 1"},
-			{Hash: "tx 1"},
-			{Hash: "tx 1"},
-			{Hash: "tx 2"},
-			{Hash: "tx 2"},
-			{Hash: "tx 2"},
-		}, mockHandler.Stale)
-
-		assert.ElementsMatch(t, []*failedTx{
-			{
-				transaction: &types.TransactionIdentifier{Hash: "tx 1"},
-				intent:      send1,
-			},
-			{
-				transaction: &types.TransactionIdentifier{Hash: "tx 2"},
-				intent:      send2,
-			},
-		}, mockHandler.Failed)
-
-		assert.ElementsMatch(t, []*confirmedTx{}, mockHandler.Confirmed)
-	})
-}
-
-func TestBroadcastStorageBehindTip(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBroadcastStorage(
-		database,
-		confirmationDepth,
-		100,
-		broadcastLimit,
-		broadcastTipDelay,
-		broadcastBehindTip,
-		blockBroadcastLimit,
-	)
-	mockHelper := &MockBroadcastStorageHelper{}
-	mockHandler := &MockBroadcastStorageHandler{}
-	storage.Initialize(mockHelper, mockHandler)
-
-	send1 := opFiller("addr 1", 1)
-	send2 := opFiller("addr 2", 1)
-	t.Run("broadcast", func(t *testing.T) {
-		err := storage.Broadcast(
-			ctx,
-			"addr 1",
-			send1,
-			&types.TransactionIdentifier{Hash: "tx 1"},
-			"payload 1",
-		)
-		assert.NoError(t, err)
-
-		err = storage.Broadcast(
-			ctx,
-			"addr 2",
-			send2,
-			&types.TransactionIdentifier{Hash: "tx 2"},
-			"payload 2",
-		)
-		assert.NoError(t, err)
-
-		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:     "addr 1",
-				Intent:     send1,
-				Payload:    "payload 1",
-			},
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:     "addr 2",
-				Intent:     send2,
-				Payload:    "payload 2",
-			},
-		}, broadcasts)
-	})
-
-	blocks := blockFiller(0, 81)
-	mockHelper.AtSyncTip = false
-	mockHelper.Transactions = map[string]*types.TransactionIdentifier{
-		"payload 1": {Hash: "tx 1"},
-		"payload 2": {Hash: "tx 2"},
-	}
-
-	t.Run("add blocks behind tip", func(t *testing.T) {
-		for _, block := range blocks[:60] {
-			txn := storage.db.NewDatabaseTransaction(ctx, true)
-			commitWorker, err := storage.AddingBlock(ctx, block, txn)
-			assert.NoError(t, err)
-			err = txn.Commit(ctx)
-			assert.NoError(t, err)
-
-			mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-			err = commitWorker(ctx)
-			assert.NoError(t, err)
-		}
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:     "addr 1",
-				Intent:     send1,
-				Payload:    "payload 1",
-			},
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:     "addr 2",
-				Intent:     send2,
-				Payload:    "payload 2",
-			},
-		}, broadcasts)
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-		assert.ElementsMatch(t, []*confirmedTx{}, mockHandler.Confirmed)
-	})
-
-	mockHelper.AtSyncTip = true
-	t.Run("add blocks close to tip", func(t *testing.T) {
-		for _, block := range blocks[60:71] {
-			txn := storage.db.NewDatabaseTransaction(ctx, true)
-			commitWorker, err := storage.AddingBlock(ctx, block, txn)
-			assert.NoError(t, err)
-			err = txn.Commit(ctx)
-			assert.NoError(t, err)
-
-			mockHelper.SyncedBlockIdentifier = block.BlockIdentifier
-			err = commitWorker(ctx)
-			assert.NoError(t, err)
-		}
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:        "addr 1",
-				Intent:        send1,
-				Payload:       "payload 1",
-				LastBroadcast: blocks[60].BlockIdentifier,
-				Broadcasts:    1,
-			},
-			{
-				Identifier:    &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:        "addr 2",
-				Intent:        send2,
-				Payload:       "payload 2",
-				LastBroadcast: blocks[60].BlockIdentifier,
-				Broadcasts:    1,
-			},
-		}, broadcasts)
-		assert.ElementsMatch(t, []*types.TransactionIdentifier{}, mockHandler.Stale)
-		assert.ElementsMatch(t, []*failedTx{}, mockHandler.Failed)
-		assert.ElementsMatch(t, []*confirmedTx{}, mockHandler.Confirmed)
-	})
-}
-
-func TestBroadcastStorageClearBroadcasts(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	storage := NewBroadcastStorage(
-		database,
-		confirmationDepth,
-		staleDepth,
-		broadcastLimit,
-		broadcastTipDelay,
-		broadcastBehindTip,
-		blockBroadcastLimit,
-	)
-	mockHelper := &MockBroadcastStorageHelper{}
-	mockHandler := &MockBroadcastStorageHandler{}
-	storage.Initialize(mockHelper, mockHandler)
-
-	t.Run("locked addresses with no broadcasts", func(t *testing.T) {
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
-	})
-
-	send1 := opFiller("addr 1", 11)
-	send2 := opFiller("addr 2", 13)
-	t.Run("broadcast", func(t *testing.T) {
-		err := storage.Broadcast(
-			ctx,
-			"addr 1",
-			send1,
-			&types.TransactionIdentifier{Hash: "tx 1"},
-			"payload 1",
-		)
-		assert.NoError(t, err)
-
-		err = storage.Broadcast(
-			ctx,
-			"addr 2",
-			send2,
-			&types.TransactionIdentifier{Hash: "tx 2"},
-			"payload 2",
-		)
-		assert.NoError(t, err)
-
-		// Check to make sure duplicate instances of address aren't reported
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 2)
-		assert.ElementsMatch(t, []string{"addr 1", "addr 2"}, addresses)
-
-		broadcasts, err := storage.GetAllBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:     "addr 1",
-				Intent:     send1,
-				Payload:    "payload 1",
-			},
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:     "addr 2",
-				Intent:     send2,
-				Payload:    "payload 2",
-			},
-		}, broadcasts)
-	})
-
-	t.Run("clear broadcasts", func(t *testing.T) {
-		broadcasts, err := storage.ClearBroadcasts(ctx)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, []*Broadcast{
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 1"},
-				Sender:     "addr 1",
-				Intent:     send1,
-				Payload:    "payload 1",
-			},
-			{
-				Identifier: &types.TransactionIdentifier{Hash: "tx 2"},
-				Sender:     "addr 2",
-				Intent:     send2,
-				Payload:    "payload 2",
-			},
-		}, broadcasts)
-
-		addresses, err := storage.LockedAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addresses, 0)
-		assert.ElementsMatch(t, []string{}, addresses)
-	})
-}
-
-var _ BroadcastStorageHelper = (*MockBroadcastStorageHelper)(nil)
-
-type findTx struct {
-	blockIdentifier *types.BlockIdentifier
-	transaction     *types.Transaction
-}
-
-type MockBroadcastStorageHelper struct {
-	AtSyncTip             bool
-	SyncedBlockIdentifier *types.BlockIdentifier
-	Transactions          map[string]*types.TransactionIdentifier
-	FindTransactions      map[string]*findTx
-}
-
-func (m *MockBroadcastStorageHelper) AtTip(
-	ctx context.Context,
-	tipDelay int64,
-) (bool, error) {
-	return m.AtSyncTip, nil
-}
-
-func (m *MockBroadcastStorageHelper) CurrentBlockIdentifier(
-	ctx context.Context,
-) (*types.BlockIdentifier, error) {
-	return m.SyncedBlockIdentifier, nil
-}
-
-func (m *MockBroadcastStorageHelper) FindTransaction(
-	ctx context.Context,
-	transactionIdentifier *types.TransactionIdentifier,
-	txn DatabaseTransaction,
-) (*types.BlockIdentifier, *types.Transaction, error) {
-	val, exists := m.FindTransactions[transactionIdentifier.Hash]
-	if !exists {
-		return nil, nil, nil
-	}
-
-	return val.blockIdentifier, val.transaction, nil
-}
-
-func (m *MockBroadcastStorageHelper) BroadcastTransaction(
-	ctx context.Context,
-	payload string,
-) (*types.TransactionIdentifier, error) {
-	val, exists := m.Transactions[payload]
-	if !exists {
-		return nil, errors.New("broadcast error")
-	}
-
-	return val, nil
-}
-
-var _ BroadcastStorageHandler = (*MockBroadcastStorageHandler)(nil)
-
-type confirmedTx struct {
-	blockIdentifier *types.BlockIdentifier
-	transaction     *types.Transaction
-	intent          []*types.Operation
-}
-
-type failedTx struct {
-	transaction *types.TransactionIdentifier
-	intent      []*types.Operation
-}
-
-type MockBroadcastStorageHandler struct {
-	Confirmed []*confirmedTx
-	Stale     []*types.TransactionIdentifier
-	Failed    []*failedTx
-}
-
-func (m *MockBroadcastStorageHandler) TransactionConfirmed(
-	ctx context.Context,
-	blockIdentifier *types.BlockIdentifier,
-	transaction *types.Transaction,
-	intent []*types.Operation,
-) error {
-	if m.Confirmed == nil {
-		m.Confirmed = []*confirmedTx{}
-	}
-
-	m.Confirmed = append(m.Confirmed, &confirmedTx{
-		blockIdentifier: blockIdentifier,
-		transaction:     transaction,
-		intent:          intent,
-	})
-
-	return nil
-}
-
-func (m *MockBroadcastStorageHandler) TransactionStale(
-	ctx context.Context,
-	transactionIdentifier *types.TransactionIdentifier,
-) error {
-	if m.Stale == nil {
-		m.Stale = []*types.TransactionIdentifier{}
-	}
-
-	m.Stale = append(m.Stale, transactionIdentifier)
-
-	return nil
-}
-
-func (m *MockBroadcastStorageHandler) BroadcastFailed(
-	ctx context.Context,
-	transactionIdentifier *types.TransactionIdentifier,
-	intent []*types.Operation,
-) error {
-	if m.Failed == nil {
-		m.Failed = []*failedTx{}
-	}
-
-	m.Failed = append(m.Failed, &failedTx{
-		transaction: transactionIdentifier,
-		intent:      intent,
-	})
-
-	return nil
-}
diff --git a/pkg/storage/coin_storage_test.go b/pkg/storage/coin_storage_test.go
deleted file mode 100644
index 0525c0c..0000000
--- a/pkg/storage/coin_storage_test.go
+++ /dev/null
@@ -1,387 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"math/big"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-sdk-go/asserter"
-	"github.com/coinbase/rosetta-sdk-go/types"
-	"github.com/stretchr/testify/assert"
-)
-
-var (
-	blockIdentifier = &types.BlockIdentifier{
-		Hash:  "block",
-		Index: 1,
-	}
-	account = &types.AccountIdentifier{
-		Address: "blah",
-	}
-
-	account2 = &types.AccountIdentifier{
-		Address: "blah2",
-	}
-
-	account3 = &types.AccountIdentifier{
-		Address: "blah",
-		SubAccount: &types.SubAccountIdentifier{
-			Address: "extra account",
-		},
-	}
-
-	accountCoins = []*types.Coin{
-		{
-			CoinIdentifier: &types.CoinIdentifier{Identifier: "coin1"},
-			Amount:         coinBlock.Transactions[0].Operations[0].Amount,
-		},
-	}
-
-	account2Coins = []*types.Coin{
-		{
-			CoinIdentifier: &types.CoinIdentifier{Identifier: "coin2"},
-			Amount:         coinBlock.Transactions[0].Operations[1].Amount,
-		},
-	}
-
-	account3Coins = []*types.Coin{
-		{
-			CoinIdentifier: &types.CoinIdentifier{Identifier: "coin3"},
-			Amount:         coinBlock3.Transactions[0].Operations[0].Amount,
-		},
-		{
-			CoinIdentifier: &types.CoinIdentifier{Identifier: "coin4"},
-			Amount:         coinBlock3.Transactions[1].Operations[0].Amount,
-		},
-	}
-
-	successStatus = "success"
-	failureStatus = "failure"
-
-	currency = &types.Currency{
-		Symbol:   "sym",
-		Decimals: 12,
-	}
-
-	currency2 = &types.Currency{
-		Symbol:   "sym2",
-		Decimals: 12,
-	}
-
-	coinBlock = &types.Block{
-		Transactions: []*types.Transaction{
-			{
-				Operations: []*types.Operation{
-					{
-						Account: account,
-						Status:  successStatus,
-						Amount: &types.Amount{
-							Value:    "10",
-							Currency: currency,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinCreated,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin1",
-							},
-						},
-					},
-					{
-						Account: account2,
-						Status:  successStatus,
-						Amount: &types.Amount{
-							Value:    "15",
-							Currency: currency,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinSpent,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin2",
-							},
-						},
-					},
-					{
-						Account: account2,
-						Status:  failureStatus,
-						Amount: &types.Amount{
-							Value:    "20",
-							Currency: currency,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinSpent,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin2",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-
-	coinBlock2 = &types.Block{
-		Transactions: []*types.Transaction{
-			{
-				Operations: []*types.Operation{
-					{
-						Account: account,
-						Status:  successStatus,
-						Amount: &types.Amount{
-							Value:    "-10",
-							Currency: currency,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinSpent,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin1",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-
-	coinBlock3 = &types.Block{
-		Transactions: []*types.Transaction{
-			{
-				Operations: []*types.Operation{
-					{
-						Account: account3,
-						Status:  successStatus,
-						Amount: &types.Amount{
-							Value:    "4",
-							Currency: currency,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinCreated,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin3",
-							},
-						},
-					},
-				},
-			},
-			{
-				Operations: []*types.Operation{
-					{
-						Account: account3,
-						Status:  successStatus,
-						Amount: &types.Amount{
-							Value:    "6",
-							Currency: currency2,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinCreated,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin4",
-							},
-						},
-					},
-				},
-			},
-			{
-				Operations: []*types.Operation{
-					{
-						Account: account3,
-						Status:  failureStatus,
-						Amount: &types.Amount{
-							Value:    "12",
-							Currency: currency,
-						},
-						CoinChange: &types.CoinChange{
-							CoinAction: types.CoinCreated,
-							CoinIdentifier: &types.CoinIdentifier{
-								Identifier: "coin5",
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-)
-
-func TestCoinStorage(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	a, err := asserter.NewClientWithOptions(
-		&types.NetworkIdentifier{
-			Blockchain: "bitcoin",
-			Network:    "mainnet",
-		},
-		&types.BlockIdentifier{
-			Hash:  "block 0",
-			Index: 0,
-		},
-		[]string{"Transfer"},
-		[]*types.OperationStatus{
-			{
-				Status:     successStatus,
-				Successful: true,
-			},
-			{
-				Status:     failureStatus,
-				Successful: false,
-			},
-		},
-		[]*types.Error{},
-	)
-	assert.NoError(t, err)
-	assert.NotNil(t, a)
-
-	mockHelper := &MockCoinStorageHelper{}
-
-	c := NewCoinStorage(database, mockHelper, a)
-
-	t.Run("get coins of unset account", func(t *testing.T) {
-		coins, block, err := c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, []*types.Coin{}, coins)
-		assert.Equal(t, blockIdentifier, block)
-
-		bal, coinIdentifier, block, err := c.GetLargestCoin(ctx, account, currency)
-		assert.NoError(t, err)
-		assert.Equal(t, big.NewInt(0), bal)
-		assert.Nil(t, coinIdentifier)
-		assert.Equal(t, blockIdentifier, block)
-	})
-
-	t.Run("add block", func(t *testing.T) {
-		tx := c.db.NewDatabaseTransaction(ctx, true)
-		commitFunc, err := c.AddingBlock(ctx, coinBlock, tx)
-		assert.Nil(t, commitFunc)
-		assert.NoError(t, err)
-		assert.NoError(t, tx.Commit(ctx))
-
-		coins, block, err := c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, accountCoins, coins)
-		assert.Equal(t, blockIdentifier, block)
-	})
-
-	t.Run("add duplicate coin", func(t *testing.T) {
-		tx := c.db.NewDatabaseTransaction(ctx, true)
-		commitFunc, err := c.AddingBlock(ctx, coinBlock, tx)
-		assert.Nil(t, commitFunc)
-		assert.Error(t, err)
-		tx.Discard(ctx)
-
-		coins, block, err := c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, accountCoins, coins)
-		assert.Equal(t, blockIdentifier, block)
-	})
-
-	t.Run("remove block", func(t *testing.T) {
-		tx := c.db.NewDatabaseTransaction(ctx, true)
-		commitFunc, err := c.RemovingBlock(ctx, coinBlock, tx)
-		assert.Nil(t, commitFunc)
-		assert.NoError(t, err)
-		assert.NoError(t, tx.Commit(ctx))
-
-		coins, block, err := c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, []*types.Coin{}, coins)
-		assert.Equal(t, blockIdentifier, block)
-
-		coins, block, err = c.GetCoins(ctx, account2)
-		assert.NoError(t, err)
-		assert.Equal(t, account2Coins, coins)
-		assert.Equal(t, blockIdentifier, block)
-	})
-
-	t.Run("spend coin", func(t *testing.T) {
-		tx := c.db.NewDatabaseTransaction(ctx, true)
-		commitFunc, err := c.AddingBlock(ctx, coinBlock, tx)
-		assert.Nil(t, commitFunc)
-		assert.NoError(t, err)
-		assert.NoError(t, tx.Commit(ctx))
-
-		coins, block, err := c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, accountCoins, coins)
-		assert.Equal(t, blockIdentifier, block)
-
-		tx = c.db.NewDatabaseTransaction(ctx, true)
-		commitFunc, err = c.AddingBlock(ctx, coinBlock2, tx)
-		assert.Nil(t, commitFunc)
-		assert.NoError(t, err)
-		assert.NoError(t, tx.Commit(ctx))
-
-		coins, block, err = c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, []*types.Coin{}, coins)
-		assert.Equal(t, blockIdentifier, block)
-
-		coins, block, err = c.GetCoins(ctx, account2)
-		assert.NoError(t, err)
-		assert.Equal(t, []*types.Coin{}, coins)
-		assert.Equal(t, blockIdentifier, block)
-	})
-
-	t.Run("add block with multiple outputs for 1 account", func(t *testing.T) {
-		tx := c.db.NewDatabaseTransaction(ctx, true)
-		commitFunc, err := c.AddingBlock(ctx, coinBlock3, tx)
-		assert.Nil(t, commitFunc)
-		assert.NoError(t, err)
-		assert.NoError(t, tx.Commit(ctx))
-
-		coins, block, err := c.GetCoins(ctx, account)
-		assert.NoError(t, err)
-		assert.Equal(t, []*types.Coin{}, coins)
-		assert.Equal(t, blockIdentifier, block)
-
-		coins, block, err = c.GetCoins(ctx, account3)
-		assert.NoError(t, err)
-		assert.ElementsMatch(t, account3Coins, coins)
-		assert.Equal(t, blockIdentifier, block)
-
-		bal, coinIdentifier, block, err := c.GetLargestCoin(ctx, account3, currency)
-		assert.NoError(t, err)
-		assert.Equal(t, big.NewInt(4), bal)
-		assert.Equal(t, &types.CoinIdentifier{Identifier: "coin3"}, coinIdentifier)
-		assert.Equal(t, blockIdentifier, block)
-
-		bal, coinIdentifier, block, err = c.GetLargestCoin(ctx, account3, currency2)
-		assert.NoError(t, err)
-		assert.Equal(t, big.NewInt(6), bal)
-		assert.Equal(t, &types.CoinIdentifier{Identifier: "coin4"}, coinIdentifier)
-		assert.Equal(t, blockIdentifier, block)
-	})
-}
-
-type MockCoinStorageHelper struct{}
-
-var _ CoinStorageHelper = (*MockCoinStorageHelper)(nil)
-
-func (h *MockCoinStorageHelper) CurrentBlockIdentifier(
-	ctx context.Context,
-	transaction DatabaseTransaction,
-) (*types.BlockIdentifier, error) {
-	return blockIdentifier, nil
-}
diff --git a/pkg/storage/counter_storage_test.go b/pkg/storage/counter_storage_test.go
deleted file mode 100644
index 565dd88..0000000
--- a/pkg/storage/counter_storage_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"math/big"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestCounterStorage(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	c := NewCounterStorage(database)
-
-	t.Run("get unset counter", func(t *testing.T) {
-		v, err := c.Get(ctx, "blah")
-		assert.NoError(t, err)
-		assert.Equal(t, v, big.NewInt(0))
-	})
-
-	t.Run("increase counter", func(t *testing.T) {
-		v, err := c.Update(ctx, "blah", big.NewInt(100))
-		assert.NoError(t, err)
-		assert.Equal(t, v, big.NewInt(100))
-
-		v, err = c.Get(ctx, "blah")
-		assert.NoError(t, err)
-		assert.Equal(t, v, big.NewInt(100))
-	})
-
-	t.Run("decrement counter", func(t *testing.T) {
-		v, err := c.Update(ctx, "blah", big.NewInt(-50))
-		assert.NoError(t, err)
-		assert.Equal(t, v, big.NewInt(50))
-
-		v, err = c.Get(ctx, "blah")
-		assert.NoError(t, err)
-		assert.Equal(t, v, big.NewInt(50))
-	})
-
-	t.Run("get unset counter after update", func(t *testing.T) {
-		v, err := c.Get(ctx, "blah2")
-		assert.NoError(t, err)
-		assert.Equal(t, v, big.NewInt(0))
-	})
-}
diff --git a/pkg/storage/key_storage_test.go b/pkg/storage/key_storage_test.go
deleted file mode 100644
index 4e09f35..0000000
--- a/pkg/storage/key_storage_test.go
+++ /dev/null
@@ -1,200 +0,0 @@
-// 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.
-
-package storage
-
-import (
-	"context"
-	"testing"
-
-	"github.com/coinbase/rosetta-cli/pkg/utils"
-
-	"github.com/coinbase/rosetta-cli/configuration"
-	"github.com/coinbase/rosetta-sdk-go/keys"
-	"github.com/coinbase/rosetta-sdk-go/types"
-	"github.com/ethereum/go-ethereum/common"
-	"github.com/stretchr/testify/assert"
-)
-
-func hash(message string) []byte {
-	messageHashBytes := common.BytesToHash([]byte(message)).Bytes()
-	return messageHashBytes
-}
-
-func TestKeyStorage(t *testing.T) {
-	ctx := context.Background()
-
-	newDir, err := utils.CreateTempDir()
-	assert.NoError(t, err)
-	defer utils.RemoveTempDir(newDir)
-
-	database, err := NewBadgerStorage(ctx, newDir, false)
-	assert.NoError(t, err)
-	defer database.Close(ctx)
-
-	k := NewKeyStorage(database)
-
-	kp1, err := keys.GenerateKeypair(types.Edwards25519)
-	assert.NoError(t, err)
-
-	kp2, err := keys.GenerateKeypair(types.Secp256k1)
-	assert.NoError(t, err)
-
-	t.Run("get non-existent key", func(t *testing.T) {
-		v, err := k.Get(ctx, "blah")
-		assert.Error(t, err)
-		assert.Nil(t, v)
-
-		addrs, err := k.GetAllAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Len(t, addrs, 0)
-	})
-
-	t.Run("store and get key", func(t *testing.T) {
-		err = k.Store(ctx, "addr1", kp1)
-		assert.NoError(t, err)
-
-		v, err := k.Get(ctx, "addr1")
-		assert.NoError(t, err)
-		assert.Equal(t, kp1, v)
-
-		addrs, err := k.GetAllAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, []string{"addr1"}, addrs)
-	})
-
-	t.Run("attempt overwrite", func(t *testing.T) {
-		err = k.Store(ctx, "addr1", kp2)
-		assert.Error(t, err)
-
-		v, err := k.Get(ctx, "addr1")
-		assert.NoError(t, err)
-		assert.Equal(t, kp1, v)
-	})
-
-	t.Run("store and get another key", func(t *testing.T) {
-		err = k.Store(ctx, "addr2", kp2)
-		assert.NoError(t, err)
-
-		v, err := k.Get(ctx, "addr2")
-		assert.NoError(t, err)
-		assert.Equal(t, kp2, v)
-
-		addrs, err := k.GetAllAddresses(ctx)
-		assert.NoError(t, err)
-		assert.Equal(t, []string{"addr1", "addr2"}, addrs)
-	})
-
-	t.Run("sign payloads", func(t *testing.T) {
-		payloads := []*types.SigningPayload{
-			{
-				Address:       "addr1",
-				Bytes:         hash("msg1"),
-				SignatureType: types.Ed25519,
-			},
-			{
-				Address:       "addr2",
-				Bytes:         hash("msg2"),
-				SignatureType: types.Ecdsa,
-			},
-		}
-
-		sigs, err := k.Sign(ctx, payloads)
-		assert.NoError(t, err)
-		assert.Len(t, sigs, 2)
-		assert.NoError(t, (&keys.SignerEdwards25519{}).Verify(sigs[0]))
-		assert.NoError(t, (&keys.SignerSecp256k1{}).Verify(sigs[1]))
-	})
-
-	t.Run("missing address in sign", func(t *testing.T) {
-		payloads := []*types.SigningPayload{
-			{
-				Address:       "addr3",
-				Bytes:         hash("msg3"),
-				SignatureType: types.Ed25519,
-			},
-		}
-
-		sigs, err := k.Sign(ctx, payloads)
-		assert.Error(t, err)
-		assert.Nil(t, sigs)
-	})
-
-	t.Run("missing signature type in sign", func(t *testing.T) {
-		payloads := []*types.SigningPayload{
-			{
-				Address: "addr1",
-				Bytes:   hash("msg1"),
-			},
-		}
-
-		sigs, err := k.Sign(ctx, payloads)
-		assert.Error(t, err)
-		assert.Nil(t, sigs)
-	})
-
-	t.Run("imports accounts", func(t *testing.T) {
-		addresses, err := k.GetAllAddresses(ctx)
-		assert.NoError(t, err)
-		startingLen := len(addresses)
-
-		prefundedAccs := []*configuration.PrefundedAccount{
-			&configuration.PrefundedAccount{
-				PrivateKeyHex: "0e842a16b2d39a4dff5c63688513cb2109e30c3c30bc4eb502cc54f4614493f6",
-				Address:       "add1",
-				CurveType:     types.Edwards25519,
-			},
-			&configuration.PrefundedAccount{
-				PrivateKeyHex: "42efc44bdf7b2d4d45ddd6ddb727ed498c91e7070914c9ed0d80af680ff42b3e",
-				Address:       "add2",
-				CurveType:     types.Edwards25519,
-			},
-			&configuration.PrefundedAccount{
-				PrivateKeyHex: "01ea48249742650907004331e85536f868e2d3959434ba751d8aa230138a9707",
-				Address:       "add3",
-				CurveType:     types.Edwards25519,
-			},
-		}
-
-		err = k.ImportAccounts(ctx, prefundedAccs)
-		assert.NoError(t, err)
-		addresses, _ = k.GetAllAddresses(ctx)
-		endLen := len(addresses)
-		assert.Equal(t, endLen, startingLen+len(prefundedAccs))
-	})
-
-	t.Run("does not import same key twice", func(t *testing.T) {
-		prefundedAccs := []*configuration.PrefundedAccount{
-			&configuration.PrefundedAccount{
-				PrivateKeyHex: "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
-				Address:       "badadd",
-				CurveType:     types.Edwards25519,
-			},
-		}
-
-		// No error when importing key for the first time
-		err = k.ImportAccounts(ctx, prefundedAccs)
-		assert.NoError(t, err)
-		addresses, _ := k.GetAllAddresses(ctx)
-		startingLen := len(addresses)
-
-		// No error when trying to import the key again
-		err = k.ImportAccounts(ctx, prefundedAccs)
-		assert.NoError(t, err)
-		addresses, _ = k.GetAllAddresses(ctx)
-		endLen := len(addresses)
-
-		assert.Equal(t, endLen, startingLen)
-	})
-}
diff --git a/pkg/tester/construction.go b/pkg/tester/construction.go
index 4ae3d75..26b04f7 100644
--- a/pkg/tester/construction.go
+++ b/pkg/tester/construction.go
@@ -25,13 +25,13 @@ import (
 	"github.com/coinbase/rosetta-cli/pkg/constructor"
 	"github.com/coinbase/rosetta-cli/pkg/logger"
 	"github.com/coinbase/rosetta-cli/pkg/processor"
-	"github.com/coinbase/rosetta-cli/pkg/statefulsyncer"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/parser"
+	"github.com/coinbase/rosetta-sdk-go/statefulsyncer"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 )
 
@@ -247,9 +247,9 @@ func (t *ConstructionTester) StartSyncer(
 	if errors.Is(err, storage.ErrHeadBlockNotFound) {
 		// If a block has yet to be synced, start syncing from tip.
 		// TODO: make configurable
-		status, err := t.onlineFetcher.NetworkStatusRetry(ctx, t.network, nil)
-		if err != nil {
-			return fmt.Errorf("%w: unable to fetch network status", err)
+		status, fetchErr := t.onlineFetcher.NetworkStatusRetry(ctx, t.network, nil)
+		if fetchErr != nil {
+			return fmt.Errorf("%w: unable to fetch network status", fetchErr.Err)
 		}
 
 		startIndex = status.CurrentBlockIdentifier.Index
diff --git a/pkg/tester/data.go b/pkg/tester/data.go
index c94c88f..1670d30 100644
--- a/pkg/tester/data.go
+++ b/pkg/tester/data.go
@@ -25,13 +25,13 @@ import (
 	"github.com/coinbase/rosetta-cli/configuration"
 	"github.com/coinbase/rosetta-cli/pkg/logger"
 	"github.com/coinbase/rosetta-cli/pkg/processor"
-	"github.com/coinbase/rosetta-cli/pkg/statefulsyncer"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
+	"github.com/coinbase/rosetta-sdk-go/statefulsyncer"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 	"golang.org/x/sync/errgroup"
 )
diff --git a/pkg/tester/data_results.go b/pkg/tester/data_results.go
index 36a8e5e..f4d92bd 100644
--- a/pkg/tester/data_results.go
+++ b/pkg/tester/data_results.go
@@ -24,11 +24,11 @@ import (
 
 	"github.com/coinbase/rosetta-cli/configuration"
 	"github.com/coinbase/rosetta-cli/pkg/processor"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
 	"github.com/olekukonko/tablewriter"
 )
diff --git a/pkg/tester/data_results_test.go b/pkg/tester/data_results_test.go
index 74f2352..25f768c 100644
--- a/pkg/tester/data_results_test.go
+++ b/pkg/tester/data_results_test.go
@@ -23,12 +23,12 @@ import (
 
 	"github.com/coinbase/rosetta-cli/configuration"
 	"github.com/coinbase/rosetta-cli/pkg/processor"
-	"github.com/coinbase/rosetta-cli/pkg/storage"
-	"github.com/coinbase/rosetta-cli/pkg/utils"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
+	"github.com/coinbase/rosetta-sdk-go/storage"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
 	"github.com/coinbase/rosetta-sdk-go/types"
+	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go
deleted file mode 100644
index 504cce7..0000000
--- a/pkg/utils/utils_test.go
+++ /dev/null
@@ -1,190 +0,0 @@
-// 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.
-
-package utils
-
-import (
-	"math/big"
-	"os"
-	"path"
-	"testing"
-
-	"github.com/coinbase/rosetta-sdk-go/asserter"
-	"github.com/coinbase/rosetta-sdk-go/types"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestCreateAndRemoveTempDir(t *testing.T) {
-	dir, err := CreateTempDir()
-	assert.NoError(t, err)
-
-	_, err = os.Stat(dir)
-	assert.NoError(t, err)
-
-	customPath := path.Join(dir, "test", "test2")
-	_, err = os.Stat(customPath)
-	assert.True(t, os.IsNotExist(err))
-
-	assert.NoError(t, EnsurePathExists(customPath))
-	_, err = os.Stat(path.Join(dir, "test"))
-	assert.NoError(t, err)
-
-	_, err = os.Stat(customPath)
-	assert.NoError(t, err)
-
-	// Write to path
-	curr := &types.Currency{
-		Symbol:   "BTC",
-		Decimals: 8,
-	}
-
-	currPath := path.Join(customPath, "curr.json")
-	err = SerializeAndWrite(currPath, curr)
-	assert.NoError(t, err)
-
-	_, err = os.Stat(currPath)
-	assert.NoError(t, err)
-
-	// Check write equal to read
-	var newCurr types.Currency
-	err = LoadAndParse(currPath, &newCurr)
-	assert.NoError(t, err)
-	assert.Equal(t, curr, &newCurr)
-
-	// Test that we error when unknown fields
-	var newBlock types.Block
-	err = LoadAndParse(currPath, &newBlock)
-	assert.Error(t, err)
-	assert.Equal(t, types.Block{}, newBlock)
-
-	RemoveTempDir(dir)
-
-	_, err = os.Stat(dir)
-	assert.True(t, os.IsNotExist(err))
-}
-
-func TestCreateCommandPath(t *testing.T) {
-	dir, err := CreateTempDir()
-	assert.NoError(t, err)
-
-	_, err = os.Stat(dir)
-	assert.NoError(t, err)
-
-	net := &types.NetworkIdentifier{
-		Blockchain: "Bitcoin",
-		Network:    "Mainnet",
-	}
-
-	dp, err := CreateCommandPath(dir, "test", net)
-	assert.NoError(t, err)
-
-	customPath := path.Join(dir, "test", types.Hash(net))
-	assert.Equal(t, customPath, dp)
-	_, err = os.Stat(customPath)
-	assert.NoError(t, err)
-
-	RemoveTempDir(dir)
-
-	_, err = os.Stat(dir)
-	assert.True(t, os.IsNotExist(err))
-}
-
-func TestContainsString(t *testing.T) {
-	var tests = map[string]struct {
-		arr []string
-		s   string
-
-		contains bool
-	}{
-		"empty arr": {
-			s: "hello",
-		},
-		"single arr": {
-			arr:      []string{"hello"},
-			s:        "hello",
-			contains: true,
-		},
-		"single arr no elem": {
-			arr: []string{"hello"},
-			s:   "test",
-		},
-		"multiple arr with elem": {
-			arr:      []string{"hello", "test"},
-			s:        "test",
-			contains: true,
-		},
-	}
-
-	for name, test := range tests {
-		t.Run(name, func(t *testing.T) {
-			assert.Equal(t, test.contains, ContainsString(test.arr, test.s))
-		})
-	}
-}
-
-func TestBigPow10(t *testing.T) {
-	e := int32(12)
-	v := big.NewFloat(10)
-
-	for i := int32(0); i < e-1; i++ {
-		v = new(big.Float).Mul(v, big.NewFloat(10))
-	}
-
-	assert.Equal(t, 0, new(big.Float).Sub(v, BigPow10(e)).Sign())
-}
-
-func TestPrettyAmount(t *testing.T) {
-	var tests = map[string]struct {
-		amount   *big.Int
-		currency *types.Currency
-
-		result string
-	}{
-		"no decimals": {
-			amount:   big.NewInt(100),
-			currency: &types.Currency{Symbol: "blah", Decimals: 0},
-			result:   "100 blah",
-		},
-		"10 decimal": {
-			amount:   big.NewInt(100),
-			currency: &types.Currency{Symbol: "other", Decimals: 10},
-			result:   "0.0000000100 other",
-		},
-	}
-
-	for name, test := range tests {
-		t.Run(name, func(t *testing.T) {
-			assert.Equal(t, test.result, PrettyAmount(test.amount, test.currency))
-		})
-	}
-}
-
-func TestMilliseconds(t *testing.T) {
-	assert.True(t, Milliseconds() > asserter.MinUnixEpoch)
-	assert.True(t, Milliseconds() < asserter.MaxUnixEpoch)
-}
-
-func TestRandomNumber(t *testing.T) {
-	minAmount := big.NewInt(10)
-	maxAmount := big.NewInt(13)
-
-	// somewhat crude but its fast (should be infinitely small chance we don't get all possible
-	// values in small range)
-	for i := 0; i < 10000; i++ {
-		result := RandomNumber(minAmount, maxAmount)
-		assert.NotEqual(t, -1, new(big.Int).Sub(result, minAmount).Sign())
-		assert.Equal(t, 1, new(big.Int).Sub(maxAmount, result).Sign())
-	}
-}