diff --git a/.dockerignore b/.dockerignore
index 8e8d2930691c82de499545af58c79784ce15f44b..40c5717ba7f629848b52fc88c6d35eb0e8884b87 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -4,11 +4,10 @@
 LICENSE
 README.md
 CHANGELOG.md
-images/
 docker-compose.yml
 
+images/
 tools/
-client/
 
 # Database directory
 mainnetdb/
diff --git a/.gitignore b/.gitignore
index ed554be4bbe94dca5030e830ec94f18fa5ef4953..f8ae258e1db0cc47075a8c7caa709a56a6404e8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,11 @@
 *.dll
 *.so
 *.dylib
+*.dat
+*.dat.bkp
+
+# cli-wallet config
+tools/cli-wallet/config.json
 
 # Test binary, build with `go test -c`
 *.test
diff --git a/client/wallet/addressmanager.go b/client/wallet/addressmanager.go
new file mode 100644
index 0000000000000000000000000000000000000000..15088cbad7e50ff1f31437563f9ee3275fe50da5
--- /dev/null
+++ b/client/wallet/addressmanager.go
@@ -0,0 +1,174 @@
+package wallet
+
+import (
+	"runtime"
+
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
+	"github.com/iotaledger/hive.go/bitmask"
+)
+
+// AddressManager is an manager struct that allows us to keep track of the used and spent addresses.
+type AddressManager struct {
+	// state of the wallet
+	seed             *walletseed.Seed
+	lastAddressIndex uint64
+	spentAddresses   []bitmask.BitMask
+
+	// internal variables for faster access
+	firstUnspentAddressIndex uint64
+	lastUnspentAddressIndex  uint64
+}
+
+// NewAddressManager is the constructor for the AddressManager type.
+func NewAddressManager(seed *walletseed.Seed, lastAddressIndex uint64, spentAddresses []bitmask.BitMask) (addressManager *AddressManager) {
+	defer runtime.KeepAlive(spentAddresses)
+
+	addressManager = &AddressManager{
+		seed:             seed,
+		lastAddressIndex: lastAddressIndex,
+		spentAddresses:   spentAddresses,
+	}
+	addressManager.updateFirstUnspentAddressIndex()
+	addressManager.updateLastUnspentAddressIndex()
+
+	return
+}
+
+// Address returns the address that belongs to the given index.
+func (addressManager *AddressManager) Address(addressIndex uint64) walletaddr.Address {
+	// update lastUnspentAddressIndex if necessary
+	addressManager.spentAddressIndexes(addressIndex)
+
+	return addressManager.seed.Address(addressIndex)
+}
+
+// Addresses returns a list of all addresses of the wallet.
+func (addressManager *AddressManager) Addresses() (addresses []walletaddr.Address) {
+	addresses = make([]walletaddr.Address, addressManager.lastAddressIndex+1)
+	for i := uint64(0); i <= addressManager.lastAddressIndex; i++ {
+		addresses[i] = addressManager.Address(i)
+	}
+
+	return
+}
+
+// UnspentAddresses returns a list of all unspent addresses of the wallet.
+func (addressManager *AddressManager) UnspentAddresses() (addresses []walletaddr.Address) {
+	addresses = make([]walletaddr.Address, 0)
+	for i := addressManager.firstUnspentAddressIndex; i <= addressManager.lastAddressIndex; i++ {
+		if !addressManager.IsAddressSpent(i) {
+			addresses = append(addresses, addressManager.Address(i))
+		}
+	}
+
+	return
+}
+
+// SpentAddresses returns a list of all spent addresses of the wallet.
+func (addressManager *AddressManager) SpentAddresses() (addresses []walletaddr.Address) {
+	addresses = make([]walletaddr.Address, 0)
+	for i := uint64(0); i <= addressManager.lastAddressIndex; i++ {
+		if addressManager.IsAddressSpent(i) {
+			addresses = append(addresses, addressManager.Address(i))
+		}
+	}
+
+	return
+}
+
+// FirstUnspentAddress returns the first unspent address that we know.
+func (addressManager *AddressManager) FirstUnspentAddress() walletaddr.Address {
+	return addressManager.Address(addressManager.firstUnspentAddressIndex)
+}
+
+// LastUnspentAddress returns the last unspent address that we know.
+func (addressManager *AddressManager) LastUnspentAddress() walletaddr.Address {
+	return addressManager.Address(addressManager.lastUnspentAddressIndex)
+}
+
+// NewAddress generates and returns a new unused address.
+func (addressManager *AddressManager) NewAddress() walletaddr.Address {
+	return addressManager.Address(addressManager.lastAddressIndex + 1)
+}
+
+// MarkAddressSpent marks the given address as spent.
+func (addressManager *AddressManager) MarkAddressSpent(addressIndex uint64) {
+	// determine indexes
+	sliceIndex, bitIndex := addressManager.spentAddressIndexes(addressIndex)
+
+	// mark address as spent
+	addressManager.spentAddresses[sliceIndex] = addressManager.spentAddresses[sliceIndex].SetFlag(uint(bitIndex))
+
+	// update spent address indexes
+	if addressIndex == addressManager.firstUnspentAddressIndex {
+		addressManager.updateFirstUnspentAddressIndex()
+	}
+	if addressIndex == addressManager.lastUnspentAddressIndex {
+		addressManager.updateLastUnspentAddressIndex()
+	}
+}
+
+// IsAddressSpent returns true if the address given by the address index was spent already.
+func (addressManager *AddressManager) IsAddressSpent(addressIndex uint64) bool {
+	sliceIndex, bitIndex := addressManager.spentAddressIndexes(addressIndex)
+
+	return addressManager.spentAddresses[sliceIndex].HasFlag(uint(bitIndex))
+}
+
+// spentAddressIndexes retrieves the indexes for the internal representation of the spend addresses bitmask slice that
+// belongs to the given address index. It automatically increases the capacity and updates the lastAddressIndex and the
+// lastUnspentAddressIndex if a new address is generated for the first time.
+func (addressManager *AddressManager) spentAddressIndexes(addressIndex uint64) (sliceIndex uint64, bitIndex uint64) {
+	// calculate result
+	spentAddressesCapacity := uint64(len(addressManager.spentAddresses))
+	sliceIndex = addressIndex / 8
+	bitIndex = addressIndex % 8
+
+	// extend capacity to make space for the requested index
+	if sliceIndex+1 > spentAddressesCapacity {
+		addressManager.spentAddresses = append(addressManager.spentAddresses, make([]bitmask.BitMask, sliceIndex-spentAddressesCapacity+1)...)
+	}
+
+	// update lastAddressIndex if the index is bigger
+	if addressIndex > addressManager.lastAddressIndex {
+		addressManager.lastAddressIndex = addressIndex
+	}
+
+	// update lastUnspentAddressIndex if necessary
+	if addressIndex > addressManager.lastUnspentAddressIndex && !addressManager.spentAddresses[sliceIndex].HasFlag(uint(bitIndex)) {
+		addressManager.lastUnspentAddressIndex = addressIndex
+	}
+
+	return
+}
+
+// updateFirstUnspentAddressIndex searches for the first unspent address and updates the firstUnspentAddressIndex.
+func (addressManager *AddressManager) updateFirstUnspentAddressIndex() {
+	for i := addressManager.firstUnspentAddressIndex; true; i++ {
+		if !addressManager.IsAddressSpent(i) {
+			addressManager.firstUnspentAddressIndex = i
+
+			return
+		}
+	}
+}
+
+// updateLastUnspentAddressIndex searches for the first unspent address and updates the lastUnspentAddressIndex.
+func (addressManager *AddressManager) updateLastUnspentAddressIndex() {
+	// search for last unspent address
+	for i := addressManager.lastUnspentAddressIndex; true; i-- {
+		if !addressManager.IsAddressSpent(i) {
+			addressManager.lastUnspentAddressIndex = i
+
+			return
+		}
+
+		if i == 0 {
+			break
+		}
+	}
+
+	// or generate a new unspent address
+	addressManager.NewAddress()
+}
diff --git a/client/wallet/asset.go b/client/wallet/asset.go
new file mode 100644
index 0000000000000000000000000000000000000000..c3990e81616bf43bba6460ed7e5cafe29f0b5ee9
--- /dev/null
+++ b/client/wallet/asset.go
@@ -0,0 +1,27 @@
+package wallet
+
+import (
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+)
+
+// Asset represents a container for all the information regarding a colored coin.
+type Asset struct {
+	// Color contains the identifier of this asset
+	Color balance.Color
+
+	// Name of the asset
+	Name string
+
+	// currency symbol of the asset (optional)
+	Symbol string
+
+	// Precision defines how many decimal places are shown when showing this asset in wallets
+	Precision int
+
+	// Address defines the target address where the asset is supposed to be created
+	Address address.Address
+
+	// the amount of tokens that we want to create
+	Amount uint64
+}
diff --git a/client/wallet/assetregistry.go b/client/wallet/assetregistry.go
new file mode 100644
index 0000000000000000000000000000000000000000..aee5cdeedf863f1f1e8e39d5151ca1718ead9a9c
--- /dev/null
+++ b/client/wallet/assetregistry.go
@@ -0,0 +1,158 @@
+package wallet
+
+import (
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/iotaledger/hive.go/typeutils"
+)
+
+// AssetRegistry represents a registry for colored coins, that stores the relevant metadata in a dictionary.
+type AssetRegistry struct {
+	assets map[balance.Color]Asset
+}
+
+// NewAssetRegistry is the constructor for the AssetRegistry.
+func NewAssetRegistry() *AssetRegistry {
+	return &AssetRegistry{
+		make(map[balance.Color]Asset),
+	}
+}
+
+// ParseAssetRegistry is a utility function that can be used to parse a marshaled version of the registry.
+func ParseAssetRegistry(marshalUtil *marshalutil.MarshalUtil) (assetRegistry *AssetRegistry, consumedBytes int, err error) {
+	assetRegistry = &AssetRegistry{
+		assets: make(map[balance.Color]Asset),
+	}
+
+	startingOffset := marshalUtil.ReadOffset()
+
+	assetCount, err := marshalUtil.ReadUint64()
+	if err != nil {
+		return
+	}
+
+	for i := uint64(0); i < assetCount; i++ {
+		asset := Asset{}
+
+		colorBytes, parseErr := marshalUtil.ReadBytes(balance.ColorLength)
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+		color, _, parseErr := balance.ColorFromBytes(colorBytes)
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+
+		nameLength, parseErr := marshalUtil.ReadUint32()
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+		nameBytes, parseErr := marshalUtil.ReadBytes(int(nameLength))
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+
+		symbolLength, parseErr := marshalUtil.ReadUint32()
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+		symbolBytes, parseErr := marshalUtil.ReadBytes(int(symbolLength))
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+
+		precision, parseErr := marshalUtil.ReadUint32()
+		if parseErr != nil {
+			err = parseErr
+
+			return
+		}
+
+		asset.Color = color
+		asset.Name = typeutils.BytesToString(nameBytes)
+		asset.Symbol = typeutils.BytesToString(symbolBytes)
+		asset.Precision = int(precision)
+
+		assetRegistry.assets[color] = asset
+	}
+
+	consumedBytes = marshalUtil.ReadOffset() - startingOffset
+
+	return
+}
+
+// RegisterAsset registers an asset in the registry, so we can look up names and symbol of colored coins.
+func (assetRegistry *AssetRegistry) RegisterAsset(color balance.Color, asset Asset) {
+	assetRegistry.assets[color] = asset
+}
+
+// Name returns the name of the given asset.
+func (assetRegistry *AssetRegistry) Name(color balance.Color) string {
+	if asset, assetExists := assetRegistry.assets[color]; assetExists {
+		return asset.Name
+	}
+
+	if color == balance.ColorIOTA {
+		return "IOTA"
+	}
+
+	return color.String()
+}
+
+// Symbol return the symbol of the token.
+func (assetRegistry *AssetRegistry) Symbol(color balance.Color) string {
+	if asset, assetExists := assetRegistry.assets[color]; assetExists {
+		return asset.Symbol
+	}
+
+	if color == balance.ColorIOTA {
+		return "I"
+	}
+
+	return "cI"
+}
+
+// Precision returns the amount of decimal places that this token uses.
+func (assetRegistry *AssetRegistry) Precision(color balance.Color) int {
+	if asset, assetExists := assetRegistry.assets[color]; assetExists {
+		return asset.Precision
+	}
+
+	return 0
+}
+
+// Bytes marshal the registry into a sequence of bytes.
+func (assetRegistry *AssetRegistry) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	assetCount := len(assetRegistry.assets)
+	marshalUtil.WriteUint64(uint64(assetCount))
+
+	for color, asset := range assetRegistry.assets {
+		marshalUtil.WriteBytes(color.Bytes())
+
+		nameBytes := typeutils.StringToBytes(asset.Name)
+		marshalUtil.WriteUint32(uint32(len(nameBytes)))
+		marshalUtil.WriteBytes(nameBytes)
+
+		symbolBytes := typeutils.StringToBytes(asset.Symbol)
+		marshalUtil.WriteUint32(uint32(len(symbolBytes)))
+		marshalUtil.WriteBytes(symbolBytes)
+
+		marshalUtil.WriteUint32(uint32(asset.Precision))
+	}
+
+	return marshalUtil.Bytes()
+}
diff --git a/client/wallet/connector.go b/client/wallet/connector.go
new file mode 100644
index 0000000000000000000000000000000000000000..c14ef999697476a4f46eb565c3fd6da8093ba5af
--- /dev/null
+++ b/client/wallet/connector.go
@@ -0,0 +1,14 @@
+package wallet
+
+import (
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+)
+
+// Connector represents an interface that defines how the wallet interacts with the network. A wallet can either be used
+// locally on a server or it can connect remotely using the web API.
+type Connector interface {
+	UnspentOutputs(addresses ...walletaddr.Address) (unspentOutputs map[walletaddr.Address]map[transaction.ID]*Output, err error)
+	SendTransaction(tx *transaction.Transaction) (err error)
+	RequestFaucetFunds(addr walletaddr.Address) (err error)
+}
diff --git a/client/wallet/options.go b/client/wallet/options.go
new file mode 100644
index 0000000000000000000000000000000000000000..1fd746dcfc98df88025b6d026165e7913b1e9e38
--- /dev/null
+++ b/client/wallet/options.go
@@ -0,0 +1,42 @@
+package wallet
+
+import (
+	"net/http"
+
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
+	"github.com/iotaledger/hive.go/bitmask"
+)
+
+// Option represents an optional parameter .
+type Option func(*Wallet)
+
+// WebAPI connects the wallet with the remote API of a node.
+func WebAPI(baseURL string, httpClient ...http.Client) Option {
+	return func(wallet *Wallet) {
+		wallet.connector = NewWebConnector(baseURL, httpClient...)
+	}
+}
+
+// Import restores a wallet that has previously been created.
+func Import(seed *walletseed.Seed, lastAddressIndex uint64, spentAddresses []bitmask.BitMask, assetRegistry *AssetRegistry) Option {
+	return func(wallet *Wallet) {
+		wallet.addressManager = NewAddressManager(seed, lastAddressIndex, spentAddresses)
+		wallet.assetRegistry = assetRegistry
+	}
+}
+
+// ReusableAddress configures the wallet to run in "single address" mode where all the funds are always managed on a
+// single reusable address.
+func ReusableAddress(enabled bool) Option {
+	return func(wallet *Wallet) {
+		wallet.reusableAddress = enabled
+	}
+}
+
+// GenericConnector allows us to provide a generic connector to the wallet. It can be used to mock the behavior of a
+// real connector in tests or to provide new connection methods for nodes.
+func GenericConnector(connector Connector) Option {
+	return func(wallet *Wallet) {
+		wallet.connector = connector
+	}
+}
diff --git a/client/wallet/output.go b/client/wallet/output.go
new file mode 100644
index 0000000000000000000000000000000000000000..4000677812c0c59702bb10565174761122881971
--- /dev/null
+++ b/client/wallet/output.go
@@ -0,0 +1,24 @@
+package wallet
+
+import (
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+)
+
+// Output is a wallet specific representation of an output in the IOTA network.
+type Output struct {
+	Address        address.Address
+	TransactionID  transaction.ID
+	Balances       map[balance.Color]uint64
+	InclusionState InclusionState
+}
+
+// InclusionState is a container for the different flags of an output that define if it was accepted in the network.
+type InclusionState struct {
+	Liked       bool
+	Confirmed   bool
+	Rejected    bool
+	Conflicting bool
+	Spent       bool
+}
diff --git a/client/wallet/packages/address/address.go b/client/wallet/packages/address/address.go
new file mode 100644
index 0000000000000000000000000000000000000000..1719169f85129b4aad462e2a10ec64abfb607f44
--- /dev/null
+++ b/client/wallet/packages/address/address.go
@@ -0,0 +1,15 @@
+package address
+
+import (
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+)
+
+// Address represents an address in a wallet. It extends the normal address type with an index number that was used to
+// generate the address from its seed.
+type Address struct {
+	address.Address
+	Index uint64
+}
+
+// AddressEmpty represents the 0-value of an address.
+var AddressEmpty = Address{}
diff --git a/dapps/valuetransfers/packages/wallet/seed.go b/client/wallet/packages/seed/seed.go
similarity index 61%
rename from dapps/valuetransfers/packages/wallet/seed.go
rename to client/wallet/packages/seed/seed.go
index 9e94c3d79eb7a4ed7dffc10c0541767aeececf15..3939464755c09fed4339dffa883d890c210897e1 100644
--- a/dapps/valuetransfers/packages/wallet/seed.go
+++ b/client/wallet/packages/seed/seed.go
@@ -1,8 +1,9 @@
-package wallet
+package seed
 
 import (
 	"github.com/iotaledger/hive.go/crypto/ed25519"
 
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 )
 
@@ -20,7 +21,10 @@ func NewSeed(optionalSeedBytes ...[]byte) *Seed {
 	}
 }
 
-// Address returns an ed25519 address which can be used for receiving or sending funds.
-func (seed *Seed) Address(index uint64) address.Address {
-	return address.FromED25519PubKey(seed.Seed.KeyPair(index).PublicKey)
+// Address returns an Address which can be used for receiving or sending funds.
+func (seed *Seed) Address(index uint64) walletaddr.Address {
+	return walletaddr.Address{
+		Address: address.FromED25519PubKey(seed.Seed.KeyPair(index).PublicKey),
+		Index:   index,
+	}
 }
diff --git a/client/wallet/sendfunds_options.go b/client/wallet/sendfunds_options.go
new file mode 100644
index 0000000000000000000000000000000000000000..229919a0282b75d20794c977e6e83c627b272e63
--- /dev/null
+++ b/client/wallet/sendfunds_options.go
@@ -0,0 +1,100 @@
+package wallet
+
+import (
+	"errors"
+
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+)
+
+// SendFundsOption is the type for the optional parameters for the SendFunds call.
+type SendFundsOption func(*sendFundsOptions) error
+
+// Destination is an option for the SendFunds call that defines a destination for funds that are supposed to be moved.
+func Destination(addr address.Address, amount uint64, optionalColor ...balance.Color) SendFundsOption {
+	// determine optional output color
+	var outputColor balance.Color
+	switch len(optionalColor) {
+	case 0:
+		outputColor = balance.ColorIOTA
+	case 1:
+		outputColor = optionalColor[0]
+	default:
+		return optionError(errors.New("providing more than one output color for the destination of funds is forbidden"))
+	}
+
+	// return an error if the amount is less
+	if amount == 0 {
+		return optionError(errors.New("the amount provided in the destinations needs to be larger than 0"))
+	}
+
+	// return Option
+	return func(options *sendFundsOptions) error {
+		// initialize destinations property
+		if options.Destinations == nil {
+			options.Destinations = make(map[address.Address]map[balance.Color]uint64)
+		}
+
+		// initialize address specific destination
+		if _, addressExists := options.Destinations[addr]; !addressExists {
+			options.Destinations[addr] = make(map[balance.Color]uint64)
+		}
+
+		// initialize color specific destination
+		if _, colorExists := options.Destinations[addr][outputColor]; !colorExists {
+			options.Destinations[addr][outputColor] = 0
+		}
+
+		// increase amount
+		options.Destinations[addr][outputColor] += amount
+
+		return nil
+	}
+}
+
+// Remainder is an option for the SendsFunds call that allows us to specify the remainder address that is
+// supposed to be used in the corresponding transaction.
+func Remainder(addr walletaddr.Address) SendFundsOption {
+	return func(options *sendFundsOptions) error {
+		options.RemainderAddress = addr
+
+		return nil
+	}
+}
+
+// sendFundsOptions is a struct that is used to aggregate the optional parameters provided in the SendFunds call.
+type sendFundsOptions struct {
+	Destinations     map[address.Address]map[balance.Color]uint64
+	RemainderAddress walletaddr.Address
+}
+
+// buildSendFundsOptions is a utility function that constructs the sendFundsOptions.
+func buildSendFundsOptions(options ...SendFundsOption) (result *sendFundsOptions, err error) {
+	// create options to collect the arguments provided
+	result = &sendFundsOptions{}
+
+	// apply arguments to our options
+	for _, option := range options {
+		if err = option(result); err != nil {
+			return
+		}
+	}
+
+	// sanitize parameters
+	if len(result.Destinations) == 0 {
+		err = errors.New("you need to provide at least one Destination for a valid transfer to be issued")
+
+		return
+	}
+
+	return
+}
+
+// optionError is a utility function that returns a Option that returns the error provided in the
+// argument.
+func optionError(err error) SendFundsOption {
+	return func(options *sendFundsOptions) error {
+		return err
+	}
+}
diff --git a/client/wallet/serverstatus.go b/client/wallet/serverstatus.go
new file mode 100644
index 0000000000000000000000000000000000000000..fce53b852a599075870a7fb880837d942e86914c
--- /dev/null
+++ b/client/wallet/serverstatus.go
@@ -0,0 +1,8 @@
+package wallet
+
+// ServerStatus defines the information of connected server
+type ServerStatus struct {
+	ID      string
+	Synced  bool
+	Version string
+}
diff --git a/client/wallet/unspentoutputmanager.go b/client/wallet/unspentoutputmanager.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d54c6daeafdf0904ec92f46931b47dc60cd62eb
--- /dev/null
+++ b/client/wallet/unspentoutputmanager.go
@@ -0,0 +1,112 @@
+package wallet
+
+import (
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+)
+
+// UnspentOutputManager is a manager for the unspent outputs of the addresses of a wallet. It allows us to keep track of
+// the spent state of outputs using our local knowledge about outputs that have already been spent and allows us to
+// cache results that would otherwise have to be requested by the server over and over again.
+type UnspentOutputManager struct {
+	addressManager *AddressManager
+	connector      Connector
+	unspentOutputs map[walletaddr.Address]map[transaction.ID]*Output
+}
+
+// NewUnspentOutputManager creates a new UnspentOutputManager.
+func NewUnspentOutputManager(addressManager *AddressManager, connector Connector) (outputManager *UnspentOutputManager) {
+	outputManager = &UnspentOutputManager{
+		addressManager: addressManager,
+		connector:      connector,
+		unspentOutputs: make(map[walletaddr.Address]map[transaction.ID]*Output),
+	}
+
+	outputManager.Refresh(true)
+
+	return
+}
+
+// Refresh retrieves the unspent outputs from the node. If includeSpentAddresses is set to true, then it also scans the
+// addresses from which we previously spent already.
+func (unspentOutputManager *UnspentOutputManager) Refresh(includeSpentAddresses ...bool) (err error) {
+	var addressesToRefresh []walletaddr.Address
+	if len(includeSpentAddresses) >= 1 && includeSpentAddresses[0] {
+		addressesToRefresh = unspentOutputManager.addressManager.Addresses()
+	} else {
+		addressesToRefresh = unspentOutputManager.addressManager.UnspentAddresses()
+	}
+
+	unspentOutputs, err := unspentOutputManager.connector.UnspentOutputs(addressesToRefresh...)
+	if err != nil {
+		return
+	}
+
+	for addr, unspentOutputs := range unspentOutputs {
+		for transactionID, output := range unspentOutputs {
+			if _, addressExists := unspentOutputManager.unspentOutputs[addr]; !addressExists {
+				unspentOutputManager.unspentOutputs[addr] = make(map[transaction.ID]*Output)
+			}
+
+			// mark the output as spent if we already marked it as spent locally
+			if existingOutput, outputExists := unspentOutputManager.unspentOutputs[addr][transactionID]; outputExists && existingOutput.InclusionState.Spent {
+				output.InclusionState.Spent = true
+			}
+
+			unspentOutputManager.unspentOutputs[addr][transactionID] = output
+		}
+	}
+
+	return
+}
+
+// UnspentOutputs returns the outputs that have not been spent, yet.
+func (unspentOutputManager *UnspentOutputManager) UnspentOutputs(addresses ...walletaddr.Address) (unspentOutputs map[walletaddr.Address]map[transaction.ID]*Output) {
+	// prepare result
+	unspentOutputs = make(map[walletaddr.Address]map[transaction.ID]*Output)
+
+	// retrieve the list of addresses from the address manager if none was provided
+	if len(addresses) == 0 {
+		addresses = unspentOutputManager.addressManager.Addresses()
+	}
+
+	// iterate through addresses and scan for unspent outputs
+	for _, addr := range addresses {
+		// skip the address if we have no outputs for it stored
+		unspentOutputsOnAddress, addressExistsInStoredOutputs := unspentOutputManager.unspentOutputs[addr]
+		if !addressExistsInStoredOutputs {
+			continue
+		}
+
+		// iterate through outputs
+		for transactionID, output := range unspentOutputsOnAddress {
+			// skip spent outputs
+			if output.InclusionState.Spent {
+				continue
+			}
+
+			// store unspent outputs in result
+			if _, addressExists := unspentOutputs[addr]; !addressExists {
+				unspentOutputs[addr] = make(map[transaction.ID]*Output)
+			}
+			unspentOutputs[addr][transactionID] = output
+		}
+	}
+
+	return
+}
+
+// MarkOutputSpent marks the output identified by the given parameters as spent.
+func (unspentOutputManager *UnspentOutputManager) MarkOutputSpent(addr walletaddr.Address, transactionID transaction.ID) {
+	// abort if we try to mark an unknown output as spent
+	if _, addressExists := unspentOutputManager.unspentOutputs[addr]; !addressExists {
+		return
+	}
+	output, outputExists := unspentOutputManager.unspentOutputs[addr][transactionID]
+	if !outputExists {
+		return
+	}
+
+	// mark output as spent
+	output.InclusionState.Spent = true
+}
diff --git a/client/wallet/wallet.go b/client/wallet/wallet.go
new file mode 100644
index 0000000000000000000000000000000000000000..533677fec95c2a4bc1ec5f2bd37bf690742f501f
--- /dev/null
+++ b/client/wallet/wallet.go
@@ -0,0 +1,428 @@
+package wallet
+
+import (
+	"errors"
+	"reflect"
+	"time"
+	"unsafe"
+
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+	"github.com/iotaledger/hive.go/bitmask"
+	"github.com/iotaledger/hive.go/marshalutil"
+)
+
+// Wallet represents a simple cryptocurrency wallet for the IOTA tangle. It contains the logic to manage the movement of
+// funds.
+type Wallet struct {
+	addressManager       *AddressManager
+	assetRegistry        *AssetRegistry
+	unspentOutputManager *UnspentOutputManager
+	connector            Connector
+
+	// if this option is enabled the wallet will use a single reusable address instead of changing addresses.
+	reusableAddress bool
+}
+
+// New is the factory method of the wallet. It either creates a new wallet or restores the wallet backup that is handed
+// in as an optional parameter.
+func New(options ...Option) (wallet *Wallet) {
+	// create wallet
+	wallet = &Wallet{
+		assetRegistry: NewAssetRegistry(),
+	}
+
+	// configure wallet
+	for _, option := range options {
+		option(wallet)
+	}
+
+	// initialize wallet with default address manager if we did not import a previous wallet
+	if wallet.addressManager == nil {
+		wallet.addressManager = NewAddressManager(walletseed.NewSeed(), 0, []bitmask.BitMask{})
+	}
+
+	// initialize asset registry if none was provided in the options.
+	if wallet.assetRegistry == nil {
+		wallet.assetRegistry = NewAssetRegistry()
+	}
+
+	// initialize wallet with default connector (server) if none was provided
+	if wallet.connector == nil {
+		panic("you need to provide a connector for your wallet")
+	}
+
+	// initialize output manager
+	wallet.unspentOutputManager = NewUnspentOutputManager(wallet.addressManager, wallet.connector)
+	err := wallet.unspentOutputManager.Refresh(true)
+	if err != nil {
+		panic(err)
+	}
+
+	return
+}
+
+// ServerStatus retrieves the connected server status.
+func (wallet *Wallet) ServerStatus() (status ServerStatus, err error) {
+	return wallet.connector.(*WebConnector).ServerStatus()
+}
+
+// SendFunds issues a payment of the given amount to the given address.
+func (wallet *Wallet) SendFunds(options ...SendFundsOption) (tx *transaction.Transaction, err error) {
+	// build options from the parameters
+	sendFundsOptions, err := buildSendFundsOptions(options...)
+	if err != nil {
+		return
+	}
+
+	// determine which outputs to use for our transfer
+	consumedOutputs, err := wallet.determineOutputsToConsume(sendFundsOptions)
+	if err != nil {
+		return
+	}
+
+	// build transaction
+	inputs, consumedFunds := wallet.buildInputs(consumedOutputs)
+	outputs := wallet.buildOutputs(sendFundsOptions, consumedFunds)
+	tx = transaction.New(inputs, outputs)
+	for addr := range consumedOutputs {
+		tx.Sign(signaturescheme.ED25519(*wallet.Seed().KeyPair(addr.Index)))
+	}
+
+	// mark outputs as spent
+	for addr, outputs := range consumedOutputs {
+		for transactionID := range outputs {
+			wallet.unspentOutputManager.MarkOutputSpent(addr, transactionID)
+		}
+	}
+
+	// mark addresses as spent
+	if !wallet.reusableAddress {
+		for addr := range consumedOutputs {
+			wallet.addressManager.MarkAddressSpent(addr.Index)
+		}
+	}
+
+	// send transaction
+	err = wallet.connector.SendTransaction(tx)
+
+	return
+}
+
+// CreateAsset creates a new colored token with the given details.
+func (wallet *Wallet) CreateAsset(asset Asset) (assetColor balance.Color, err error) {
+	if asset.Amount == 0 {
+		err = errors.New("required to provide the amount when trying to create an asset")
+
+		return
+	}
+
+	if asset.Name == "" {
+		err = errors.New("required to provide a name when trying to create an asset")
+
+		return
+	}
+
+	tx, err := wallet.SendFunds(
+		Destination(wallet.ReceiveAddress().Address, asset.Amount, balance.ColorNew),
+	)
+	if err != nil {
+		return
+	}
+
+	assetColor, _, err = balance.ColorFromBytes(tx.ID().Bytes())
+	if err != nil {
+		return
+	}
+
+	wallet.assetRegistry.RegisterAsset(assetColor, asset)
+
+	return
+}
+
+// AssetRegistry return the internal AssetRegistry instance of the wallet.
+func (wallet *Wallet) AssetRegistry() *AssetRegistry {
+	return wallet.assetRegistry
+}
+
+// ReceiveAddress returns the last receive address of the wallet.
+func (wallet *Wallet) ReceiveAddress() walletaddr.Address {
+	return wallet.addressManager.LastUnspentAddress()
+}
+
+// NewReceiveAddress generates and returns a new unused receive address.
+func (wallet *Wallet) NewReceiveAddress() walletaddr.Address {
+	return wallet.addressManager.NewAddress()
+}
+
+// RemainderAddress returns the address that is used for the remainder of funds.
+func (wallet *Wallet) RemainderAddress() walletaddr.Address {
+	return wallet.addressManager.FirstUnspentAddress()
+}
+
+// UnspentOutputs returns the unspent outputs that are available for spending.
+func (wallet *Wallet) UnspentOutputs() map[walletaddr.Address]map[transaction.ID]*Output {
+	return wallet.unspentOutputManager.UnspentOutputs()
+}
+
+// RequestFaucetFunds requests some funds from the faucet for testing purposes.
+func (wallet *Wallet) RequestFaucetFunds(waitForConfirmation ...bool) (err error) {
+	if len(waitForConfirmation) == 0 || !waitForConfirmation[0] {
+		err = wallet.connector.RequestFaucetFunds(wallet.ReceiveAddress())
+
+		return
+	}
+
+	if err = wallet.Refresh(); err != nil {
+		return
+	}
+	confirmedBalance, _, err := wallet.Balance()
+	if err != nil {
+		return
+	}
+
+	err = wallet.connector.RequestFaucetFunds(wallet.ReceiveAddress())
+	if err != nil {
+		return
+	}
+
+	for {
+		time.Sleep(500 * time.Millisecond)
+
+		if err = wallet.Refresh(); err != nil {
+			return
+		}
+		newConfirmedBalance, _, balanceErr := wallet.Balance()
+		if balanceErr != nil {
+			err = balanceErr
+
+			return
+		}
+
+		if !reflect.DeepEqual(confirmedBalance, newConfirmedBalance) {
+			return
+		}
+	}
+}
+
+// Refresh scans the addresses for incoming transactions. If the optional rescanSpentAddresses parameter is set to true
+// we also scan the spent addresses again (this can take longer).
+func (wallet *Wallet) Refresh(rescanSpentAddresses ...bool) (err error) {
+	err = wallet.unspentOutputManager.Refresh(rescanSpentAddresses...)
+
+	return
+}
+
+// Balance returns the confirmed and pending balance of the funds managed by this wallet.
+func (wallet *Wallet) Balance() (confirmedBalance map[balance.Color]uint64, pendingBalance map[balance.Color]uint64, err error) {
+	err = wallet.unspentOutputManager.Refresh()
+	if err != nil {
+		return
+	}
+
+	confirmedBalance = make(map[balance.Color]uint64)
+	pendingBalance = make(map[balance.Color]uint64)
+
+	// iterate through the unspent outputs
+	for _, outputsOnAddress := range wallet.unspentOutputManager.UnspentOutputs() {
+		for _, output := range outputsOnAddress {
+			// skip if the output was rejected or spent already
+			if output.InclusionState.Spent || output.InclusionState.Rejected {
+				continue
+			}
+
+			// determine target map
+			var targetMap map[balance.Color]uint64
+			if output.InclusionState.Confirmed {
+				targetMap = confirmedBalance
+			} else {
+				targetMap = pendingBalance
+			}
+
+			// store amount
+			for color, amount := range output.Balances {
+				targetMap[color] += amount
+			}
+		}
+	}
+
+	return
+}
+
+// Seed returns the seed of this wallet that is used to generate all of the wallets addresses and private keys.
+func (wallet *Wallet) Seed() *walletseed.Seed {
+	return wallet.addressManager.seed
+}
+
+// AddressManager returns the manager for the addresses of this wallet.
+func (wallet *Wallet) AddressManager() *AddressManager {
+	return wallet.addressManager
+}
+
+// ExportState exports the current state of the wallet to a marshaled version.
+func (wallet *Wallet) ExportState() []byte {
+	marshalUtil := marshalutil.New()
+	marshalUtil.WriteBytes(wallet.Seed().Bytes())
+	marshalUtil.WriteUint64(wallet.AddressManager().lastAddressIndex)
+	marshalUtil.WriteBytes(wallet.assetRegistry.Bytes())
+	marshalUtil.WriteBytes(*(*[]byte)(unsafe.Pointer(&wallet.addressManager.spentAddresses)))
+
+	return marshalUtil.Bytes()
+}
+
+func (wallet *Wallet) determineOutputsToConsume(sendFundsOptions *sendFundsOptions) (outputsToConsume map[walletaddr.Address]map[transaction.ID]*Output, err error) {
+	// initialize return values
+	outputsToConsume = make(map[walletaddr.Address]map[transaction.ID]*Output)
+
+	// aggregate total amount of required funds, so we now what and how many funds we need
+	requiredFunds := make(map[balance.Color]uint64)
+	for _, coloredBalances := range sendFundsOptions.Destinations {
+		for color, amount := range coloredBalances {
+			// if we want to color sth then we need fresh IOTA
+			if color == balance.ColorNew {
+				color = balance.ColorIOTA
+			}
+
+			requiredFunds[color] += amount
+		}
+	}
+
+	// refresh balances so we get the latest changes
+	if err = wallet.unspentOutputManager.Refresh(); err != nil {
+		return
+	}
+
+	// look for the required funds in the available unspent outputs
+	for addr, unspentOutputsOnAddress := range wallet.unspentOutputManager.UnspentOutputs() {
+		// keeps track if outputs from this address are supposed to be spent
+		outputsFromAddressSpent := false
+
+		// scan the outputs on this address for required funds
+		for transactionID, output := range unspentOutputsOnAddress {
+			// keeps track if the output contains any usable funds
+			requiredColorFoundInOutput := false
+
+			// subtract the found matching balances from the required funds
+			for color, availableBalance := range output.Balances {
+				if requiredAmount, requiredColorExists := requiredFunds[color]; requiredColorExists {
+					if requiredAmount > availableBalance {
+						requiredFunds[color] -= availableBalance
+					} else {
+						delete(requiredFunds, color)
+					}
+
+					requiredColorFoundInOutput = true
+				}
+			}
+
+			// if we found required tokens in this output
+			if requiredColorFoundInOutput {
+				// store the output in the outputs to use for the transfer
+				if _, addressEntryExists := outputsToConsume[addr]; !addressEntryExists {
+					outputsToConsume[addr] = make(map[transaction.ID]*Output)
+				}
+				outputsToConsume[addr][transactionID] = output
+
+				// mark address as spent
+				outputsFromAddressSpent = true
+			}
+		}
+
+		// if outputs from this address were spent add the remaining outputs as well (we want to spend only once from
+		// every address if we are not using a reusable address)
+		if !wallet.reusableAddress && outputsFromAddressSpent {
+			for transactionID, output := range unspentOutputsOnAddress {
+				outputsToConsume[addr][transactionID] = output
+			}
+		}
+	}
+
+	// update remainder address with default value (first unspent address) if none was provided
+	if sendFundsOptions.RemainderAddress.Address == address.Empty {
+		sendFundsOptions.RemainderAddress = wallet.RemainderAddress()
+	}
+	if _, remainderAddressInConsumedOutputs := outputsToConsume[sendFundsOptions.RemainderAddress]; remainderAddressInConsumedOutputs && !wallet.reusableAddress {
+		sendFundsOptions.RemainderAddress = wallet.ReceiveAddress()
+	}
+	if _, remainderAddressInConsumedOutputs := outputsToConsume[sendFundsOptions.RemainderAddress]; remainderAddressInConsumedOutputs && !wallet.reusableAddress {
+		sendFundsOptions.RemainderAddress = wallet.NewReceiveAddress()
+	}
+
+	// check if we have found all required funds
+	if len(requiredFunds) != 0 {
+		outputsToConsume = nil
+		err = errors.New("not enough funds to create transaction")
+	}
+
+	return
+}
+
+func (wallet *Wallet) buildInputs(outputsToUseAsInputs map[walletaddr.Address]map[transaction.ID]*Output) (inputs *transaction.Inputs, consumedFunds map[balance.Color]uint64) {
+	consumedInputs := make([]transaction.OutputID, 0)
+	consumedFunds = make(map[balance.Color]uint64)
+	for addr, unspentOutputsOfAddress := range outputsToUseAsInputs {
+		for transactionID, output := range unspentOutputsOfAddress {
+			consumedInputs = append(consumedInputs, transaction.NewOutputID(addr.Address, transactionID))
+
+			for color, amount := range output.Balances {
+				consumedFunds[color] += amount
+			}
+		}
+	}
+	inputs = transaction.NewInputs(consumedInputs...)
+
+	return
+}
+
+func (wallet *Wallet) buildOutputs(sendFundsOptions *sendFundsOptions, consumedFunds map[balance.Color]uint64) (outputs *transaction.Outputs) {
+	// build outputs for destinations
+	outputsByColor := make(map[address.Address]map[balance.Color]uint64)
+	for walletAddress, coloredBalances := range sendFundsOptions.Destinations {
+		if _, addressExists := outputsByColor[walletAddress]; !addressExists {
+			outputsByColor[walletAddress] = make(map[balance.Color]uint64)
+		}
+		for color, amount := range coloredBalances {
+			outputsByColor[walletAddress][color] += amount
+			if color == balance.ColorNew {
+				consumedFunds[balance.ColorIOTA] -= amount
+
+				if consumedFunds[balance.ColorIOTA] == 0 {
+					delete(consumedFunds, balance.ColorIOTA)
+				}
+			} else {
+				consumedFunds[color] -= amount
+
+				if consumedFunds[color] == 0 {
+					delete(consumedFunds, color)
+				}
+			}
+		}
+	}
+
+	// build outputs for remainder
+	if len(consumedFunds) != 0 {
+		if _, addressExists := outputsByColor[sendFundsOptions.RemainderAddress.Address]; !addressExists {
+			outputsByColor[sendFundsOptions.RemainderAddress.Address] = make(map[balance.Color]uint64)
+		}
+
+		for color, amount := range consumedFunds {
+			outputsByColor[sendFundsOptions.RemainderAddress.Address][color] += amount
+		}
+	}
+
+	// construct result
+	outputsBySlice := make(map[address.Address][]*balance.Balance)
+	for addr, outputs := range outputsByColor {
+		outputsBySlice[addr] = make([]*balance.Balance, 0)
+		for color, amount := range outputs {
+			outputsBySlice[addr] = append(outputsBySlice[addr], balance.New(color, int64(amount)))
+		}
+	}
+	outputs = transaction.NewOutputs(outputsBySlice)
+
+	return
+}
diff --git a/client/wallet/wallet_test.go b/client/wallet/wallet_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..34947c3815be59c1f7d705910e26baa9d6c7a150
--- /dev/null
+++ b/client/wallet/wallet_test.go
@@ -0,0 +1,236 @@
+package wallet
+
+import (
+	"crypto/rand"
+	"testing"
+
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+	"github.com/iotaledger/hive.go/bitmask"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestWallet_SendFunds(t *testing.T) {
+	// create test seed
+	senderSeed := walletseed.NewSeed()
+	receiverSeed := walletseed.NewSeed()
+
+	// define sub-tests by providing a list of parameters and a validator function
+	testCases := []struct {
+		name       string
+		parameters []SendFundsOption
+		validator  func(t *testing.T, tx *transaction.Transaction, err error)
+	}{
+		// test if not providing a destination triggers an error
+		{
+			name: "missingDestination",
+			parameters: []SendFundsOption{
+				Remainder(walletaddr.AddressEmpty),
+			},
+			validator: func(t *testing.T, tx *transaction.Transaction, err error) {
+				assert.True(t, tx == nil, "the transaction should be nil")
+				assert.Error(t, err, "calling SendFunds without a Destination should trigger an error")
+				assert.Equal(t, "you need to provide at least one Destination for a valid transfer to be issued", err.Error(), "the error message is wrong")
+			},
+		},
+
+		// test if providing an invalid destination (amount <= 0) triggers an error
+		{
+			name: "zeroAmount",
+			parameters: []SendFundsOption{
+				Destination(address.Empty, 1),
+				Destination(address.Empty, 0),
+				Destination(address.Empty, 123),
+			},
+			validator: func(t *testing.T, tx *transaction.Transaction, err error) {
+				assert.True(t, tx == nil, "the transaction should be nil")
+				assert.Error(t, err, "calling SendFunds without an invalid Destination (amount <= 0) should trigger an error")
+				assert.Equal(t, "the amount provided in the destinations needs to be larger than 0", err.Error(), "the error message is wrong")
+			},
+		},
+
+		// test if a valid transaction can be created
+		{
+			name: "validTransfer",
+			parameters: []SendFundsOption{
+				Destination(receiverSeed.Address(0).Address, 1200),
+			},
+			validator: func(t *testing.T, tx *transaction.Transaction, err error) {
+				assert.False(t, tx == nil, "there should be a transaction created")
+				assert.Nil(t, err)
+			},
+		},
+
+		// test if a valid transaction having a colored coin can be created
+		{
+			name: "validColoredTransfer",
+			parameters: []SendFundsOption{
+				Destination(receiverSeed.Address(0).Address, 1200, balance.ColorNew),
+			},
+			validator: func(t *testing.T, tx *transaction.Transaction, err error) {
+				assert.False(t, tx == nil, "there should be a transaction created")
+				assert.Nil(t, err)
+			},
+		},
+	}
+
+	// execute sub-tests and hand in the results to the validator function
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			// create mocked connector
+			mockedConnector := newMockConnector(
+				&Output{
+					Address:       senderSeed.Address(0).Address,
+					TransactionID: transaction.GenesisID,
+					Balances: map[balance.Color]uint64{
+						balance.ColorIOTA: 1337,
+						{3}:               1338,
+					},
+					InclusionState: InclusionState{
+						Liked:     true,
+						Confirmed: true,
+					},
+				},
+				&Output{
+					Address:       senderSeed.Address(0).Address,
+					TransactionID: transaction.ID{3},
+					Balances: map[balance.Color]uint64{
+						balance.ColorIOTA: 663,
+						{4}:               1338,
+					},
+					InclusionState: InclusionState{
+						Liked:     true,
+						Confirmed: true,
+					},
+				},
+			)
+
+			// create our test wallet
+			wallet := New(
+				Import(senderSeed, 1, []bitmask.BitMask{}, NewAssetRegistry()),
+				GenericConnector(mockedConnector),
+			)
+
+			// validate the result of the function call
+			tx, err := wallet.SendFunds(testCase.parameters...)
+			testCase.validator(t, tx, err)
+		})
+	}
+}
+
+type mockConnector struct {
+	outputs map[address.Address]map[transaction.ID]*Output
+}
+
+func (connector *mockConnector) RequestFaucetFunds(addr walletaddr.Address) (err error) {
+	// generate random transaction id
+	idBytes := make([]byte, transaction.IDLength)
+	_, err = rand.Read(idBytes)
+	if err != nil {
+		return
+	}
+	transactionID, _, err := transaction.IDFromBytes(idBytes)
+	if err != nil {
+		return
+	}
+
+	newOutput := &Output{
+		Address:       addr.Address,
+		TransactionID: transactionID,
+		Balances: map[balance.Color]uint64{
+			balance.ColorIOTA: 1337,
+		},
+		InclusionState: InclusionState{
+			Liked:       true,
+			Confirmed:   true,
+			Rejected:    false,
+			Conflicting: false,
+			Spent:       false,
+		},
+	}
+
+	if _, addressExists := connector.outputs[addr.Address]; !addressExists {
+		connector.outputs[addr.Address] = make(map[transaction.ID]*Output)
+	}
+	connector.outputs[addr.Address][transactionID] = newOutput
+
+	return
+}
+
+func (connector *mockConnector) SendTransaction(tx *transaction.Transaction) (err error) {
+	// mark outputs as spent
+	tx.Inputs().ForEach(func(outputId transaction.OutputID) bool {
+		connector.outputs[outputId.Address()][outputId.TransactionID()].InclusionState.Spent = true
+
+		return true
+	})
+
+	// create new outputs
+	tx.Outputs().ForEach(func(addr address.Address, balances []*balance.Balance) bool {
+		// initialize missing address entry
+		if _, addressExists := connector.outputs[addr]; !addressExists {
+			connector.outputs[addr] = make(map[transaction.ID]*Output)
+		}
+
+		// translate balances to mockConnector specific balances
+		outputBalances := make(map[balance.Color]uint64)
+		for _, coloredBalance := range balances {
+			outputBalances[coloredBalance.Color] += uint64(coloredBalance.Value)
+		}
+
+		// store new output
+		connector.outputs[addr][tx.ID()] = &Output{
+			Address:       addr,
+			TransactionID: tx.ID(),
+			Balances:      outputBalances,
+			InclusionState: InclusionState{
+				Liked:       true,
+				Confirmed:   true,
+				Rejected:    false,
+				Conflicting: false,
+				Spent:       false,
+			},
+		}
+
+		return true
+	})
+
+	return
+}
+
+func newMockConnector(outputs ...*Output) (connector *mockConnector) {
+	connector = &mockConnector{
+		outputs: make(map[address.Address]map[transaction.ID]*Output),
+	}
+
+	for _, output := range outputs {
+		if _, addressExists := connector.outputs[output.Address]; !addressExists {
+			connector.outputs[output.Address] = make(map[transaction.ID]*Output)
+		}
+
+		connector.outputs[output.Address][output.TransactionID] = output
+	}
+
+	return
+}
+
+func (connector *mockConnector) UnspentOutputs(addresses ...walletaddr.Address) (outputs map[walletaddr.Address]map[transaction.ID]*Output, err error) {
+	outputs = make(map[walletaddr.Address]map[transaction.ID]*Output)
+
+	for _, addr := range addresses {
+		for transactionID, output := range connector.outputs[addr.Address] {
+			if !output.InclusionState.Spent {
+				if _, outputsExist := outputs[addr]; !outputsExist {
+					outputs[addr] = make(map[transaction.ID]*Output)
+				}
+
+				outputs[addr][transactionID] = output
+			}
+		}
+	}
+
+	return
+}
diff --git a/client/wallet/webconnector.go b/client/wallet/webconnector.go
new file mode 100644
index 0000000000000000000000000000000000000000..97004737cb7b26d6e40fd94a4c86ccbc93906924
--- /dev/null
+++ b/client/wallet/webconnector.go
@@ -0,0 +1,132 @@
+package wallet
+
+import (
+	"net/http"
+
+	"github.com/iotaledger/goshimmer/client"
+	walletaddr "github.com/iotaledger/goshimmer/client/wallet/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
+)
+
+// WebConnector implements a connector that uses the web API to connect to a node to implement the required functions
+// for the wallet.
+type WebConnector struct {
+	client *client.GoShimmerAPI
+}
+
+// NewWebConnector is the constructor for the WebConnector.
+func NewWebConnector(baseURL string, httpClient ...http.Client) *WebConnector {
+	return &WebConnector{
+		client: client.NewGoShimmerAPI(baseURL, httpClient...),
+	}
+}
+
+// ServerStatus retrieves the connected server status with Info api.
+func (webConnector *WebConnector) ServerStatus() (status ServerStatus, err error) {
+	response, err := webConnector.client.Info()
+	if err != nil {
+		return
+	}
+
+	status.ID = response.IdentityID
+	status.Synced = response.Synced
+	status.Version = response.Version
+
+	return
+}
+
+// RequestFaucetFunds request some funds from the faucet for test purposes.
+func (webConnector *WebConnector) RequestFaucetFunds(addr walletaddr.Address) (err error) {
+	_, err = webConnector.client.SendFaucetRequest(addr.String())
+
+	return
+}
+
+// UnspentOutputs returns the outputs of transactions on the given addresses that have not been spent yet.
+func (webConnector WebConnector) UnspentOutputs(addresses ...walletaddr.Address) (unspentOutputs map[walletaddr.Address]map[transaction.ID]*Output, err error) {
+	// build reverse lookup table + arguments for client call
+	addressReverseLookupTable := make(map[string]walletaddr.Address)
+	base58EncodedAddresses := make([]string, len(addresses))
+	for i, addr := range addresses {
+		base58EncodedAddresses[i] = addr.String()
+		addressReverseLookupTable[addr.String()] = addr
+	}
+
+	// request unspent outputs
+	response, err := webConnector.client.GetUnspentOutputs(base58EncodedAddresses)
+	if err != nil {
+		return
+	}
+
+	// build result
+	unspentOutputs = make(map[walletaddr.Address]map[transaction.ID]*Output)
+	for _, unspentOutput := range response.UnspentOutputs {
+		// lookup wallet address from raw address
+		addr, addressRequested := addressReverseLookupTable[unspentOutput.Address]
+		if !addressRequested {
+			panic("the server returned an unrequested address")
+		}
+
+		// iterate through outputs
+		for _, output := range unspentOutput.OutputIDs {
+			// parse output id
+			outputID, parseErr := transaction.OutputIDFromBase58(output.ID)
+			if parseErr != nil {
+				err = parseErr
+
+				return
+			}
+
+			// build balances map
+			balancesByColor := make(map[balance.Color]uint64)
+			for _, bal := range output.Balances {
+				color := colorFromString(bal.Color)
+				balancesByColor[color] += uint64(bal.Value)
+			}
+
+			// build output
+			walletOutput := &Output{
+				Address:       addr.Address,
+				TransactionID: outputID.TransactionID(),
+				Balances:      balancesByColor,
+				InclusionState: InclusionState{
+					Liked:       output.InclusionState.Liked,
+					Confirmed:   output.InclusionState.Confirmed,
+					Rejected:    output.InclusionState.Rejected,
+					Conflicting: output.InclusionState.Conflicting,
+					Spent:       false,
+				},
+			}
+
+			// store output in result
+			if _, addressExists := unspentOutputs[addr]; !addressExists {
+				unspentOutputs[addr] = make(map[transaction.ID]*Output)
+			}
+			unspentOutputs[addr][walletOutput.TransactionID] = walletOutput
+		}
+	}
+
+	return
+}
+
+// SendTransaction sends a new transaction to the network.
+func (webConnector WebConnector) SendTransaction(tx *transaction.Transaction) (err error) {
+	_, err = webConnector.client.SendTransaction(tx.Bytes())
+
+	return
+}
+
+// colorFromString is an internal utility method that parses the given string into a Color.
+func colorFromString(colorStr string) (color balance.Color) {
+	if colorStr == "IOTA" {
+		color = balance.ColorIOTA
+	} else {
+		t, _ := transaction.IDFromBase58(colorStr)
+		color, _, _ = balance.ColorFromBytes(t.Bytes())
+	}
+	return
+}
+
+// Interface contract: make compiler warn if the interface is not implemented correctly.
+var _ Connector = &WebConnector{}
diff --git a/dapps/faucet/packages/faucet.go b/dapps/faucet/packages/faucet.go
index fbbcc66c034d5d6c556cd978dd7c1fb63c2b8556..b8f40d4f7af64a6f1797fe5e279714640499bbd4 100644
--- a/dapps/faucet/packages/faucet.go
+++ b/dapps/faucet/packages/faucet.go
@@ -6,6 +6,7 @@ import (
 	"sync"
 	"time"
 
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
@@ -13,7 +14,6 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	"github.com/iotaledger/goshimmer/plugins/issuer"
@@ -31,7 +31,7 @@ var (
 func New(seed []byte, tokensPerRequest int64, blacklistCapacity int, maxTxBookedAwaitTime time.Duration) *Faucet {
 	return &Faucet{
 		tokensPerRequest:     tokensPerRequest,
-		wallet:               wallet.New(seed),
+		seed:                 walletseed.NewSeed(seed),
 		maxTxBookedAwaitTime: maxTxBookedAwaitTime,
 		blacklist:            orderedmap.New(),
 		blacklistCapacity:    blacklistCapacity,
@@ -43,8 +43,8 @@ type Faucet struct {
 	sync.Mutex
 	// the amount of tokens to send to every request
 	tokensPerRequest int64
-	// the wallet instance of the faucet holding the tokens
-	wallet *wallet.Wallet
+	// the seed instance of the faucet holding the tokens
+	seed *walletseed.Seed
 	// the time to await for the transaction fulfilling a funding request
 	// to become booked in the value layer
 	maxTxBookedAwaitTime time.Duration
@@ -106,7 +106,7 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin
 	}
 
 	for index := range addrsIndices {
-		tx.Sign(signaturescheme.ED25519(*f.wallet.Seed().KeyPair(index)))
+		tx.Sign(signaturescheme.ED25519(*f.seed.KeyPair(index)))
 	}
 
 	// prepare value payload with value factory
@@ -142,7 +142,7 @@ func (f *Faucet) collectUTXOsForFunding() (outputIds []transaction.OutputID, add
 
 	// get a list of address for inputs
 	for i = 0; total > 0; i++ {
-		addr := f.wallet.Seed().Address(i)
+		addr := f.seed.Address(i).Address
 		valuetransfers.Tangle().OutputsOnAddress(addr).Consume(func(output *tangle.Output) {
 			if output.ConsumerCount() > 0 || total == 0 {
 				return
@@ -172,7 +172,7 @@ func (f *Faucet) collectUTXOsForFunding() (outputIds []transaction.OutputID, add
 func (f *Faucet) nextUnusedAddress() address.Address {
 	var index uint64
 	for index = 0; ; index++ {
-		addr := f.wallet.Seed().Address(index)
+		addr := f.seed.Address(index).Address
 		cachedOutputs := valuetransfers.Tangle().OutputsOnAddress(addr)
 		if len(cachedOutputs) == 0 {
 			// unused address
diff --git a/dapps/valuetransfers/packages/address/address.go b/dapps/valuetransfers/packages/address/address.go
index 16068a88d3e00c40cfe4eacfba4c98c851c28a36..ded68269b6027ac6ac8ec3d3e15738f6d3744371 100644
--- a/dapps/valuetransfers/packages/address/address.go
+++ b/dapps/valuetransfers/packages/address/address.go
@@ -133,5 +133,8 @@ func (address Address) String() string {
 	return base58.Encode(address.Bytes())
 }
 
+// Empty represents the 0-value of an address and therefore represents the "empty" address value
+var Empty = Address{}
+
 // Length contains the length of an address (digest length = 32 + version byte length = 1).
 const Length = 33
diff --git a/dapps/valuetransfers/packages/tangle/signature_filter_test.go b/dapps/valuetransfers/packages/tangle/signature_filter_test.go
index 7f511ac36e2389c3adbae194f11bac2dc901ca36..2ac1a596ed3bcc96dd5cd1afaacdf426fa7f705f 100644
--- a/dapps/valuetransfers/packages/tangle/signature_filter_test.go
+++ b/dapps/valuetransfers/packages/tangle/signature_filter_test.go
@@ -9,7 +9,6 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	valuePayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/messageparser"
@@ -28,7 +27,7 @@ func TestSignatureFilter(t *testing.T) {
 	messageParser := newSyncMessageParser(NewSignatureFilter())
 
 	// create helper instances
-	seed := wallet.NewSeed()
+	seed := newSeed()
 	messageFactory := messagefactory.New(mapdb.NewMapDB(), []byte("sequenceKey"), identity.GenerateLocalIdentity(), tipselector.New())
 
 	// 1. test value message without signatures
diff --git a/dapps/valuetransfers/packages/tangle/snapshot_test.go b/dapps/valuetransfers/packages/tangle/snapshot_test.go
index 7ef8b153864d90fdfebd5034ec22ab676f87f6bb..f1b4696471666e88ae4a3649a558ef6f80ec0f15 100644
--- a/dapps/valuetransfers/packages/tangle/snapshot_test.go
+++ b/dapps/valuetransfers/packages/tangle/snapshot_test.go
@@ -8,7 +8,6 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/hive.go/kvstore/mapdb"
 	"github.com/stretchr/testify/assert"
 )
@@ -44,7 +43,7 @@ func TestLoadSnapshot(t *testing.T) {
 
 func TestSnapshotMarshalUnmarshal(t *testing.T) {
 	const genesisBalance = 1000000000
-	seed := wallet.NewSeed()
+	seed := newSeed()
 	genesisAddr := seed.Address(GENESIS)
 
 	snapshot := Snapshot{
diff --git a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go
index 5d185f9637fef1a7bef59f8e8b69138936f2ac37..542690dd8994a847f383ec96a9be6259165093e7 100644
--- a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go
+++ b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go
@@ -9,7 +9,6 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/hive.go/kvstore/mapdb"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
@@ -34,12 +33,12 @@ const (
 // TODO: clean up create scenario with some helper functions: DRY!
 
 // preparePropagationScenario1 creates a tangle according to `img/scenario1.png`.
-func preparePropagationScenario1(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
+func preparePropagationScenario1(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *seed) {
 	// create tangle
 	tangle := newEventTangle(t, New(mapdb.NewMapDB()))
 
 	// create seed for testing
-	seed := wallet.NewSeed()
+	seed := newSeed()
 
 	// initialize tangle with genesis block (+GENESIS)
 	tangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{
@@ -542,7 +541,7 @@ func preparePropagationScenario1(t *testing.T) (*eventTangle, map[string]*transa
 }
 
 // preparePropagationScenario1 creates a tangle according to `img/scenario2.png`.
-func preparePropagationScenario2(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
+func preparePropagationScenario2(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *seed) {
 	tangle, transactions, valueObjects, branches, seed := preparePropagationScenario1(t)
 
 	// [-C, H+]
diff --git a/dapps/valuetransfers/packages/tangle/test_util.go b/dapps/valuetransfers/packages/tangle/test_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..dfb730318f273ae9c1e4d65812b30bd78c2af5f2
--- /dev/null
+++ b/dapps/valuetransfers/packages/tangle/test_util.go
@@ -0,0 +1,20 @@
+package tangle
+
+import (
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+)
+
+type seed struct {
+	*ed25519.Seed
+}
+
+func newSeed(optionalSeedBytes ...[]byte) *seed {
+	return &seed{
+		ed25519.NewSeed(optionalSeedBytes...),
+	}
+}
+
+func (seed *seed) Address(index uint64) address.Address {
+	return address.FromED25519PubKey(seed.Seed.KeyPair(index).PublicKey)
+}
diff --git a/dapps/valuetransfers/packages/test/tangle_test.go b/dapps/valuetransfers/packages/test/tangle_test.go
index a45803700ff263a6b7d8e293f386c17f04698071..82be2f4c6ef761c993b6f395e0dd741174333b7c 100644
--- a/dapps/valuetransfers/packages/test/tangle_test.go
+++ b/dapps/valuetransfers/packages/test/tangle_test.go
@@ -10,13 +10,13 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/consensus"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 )
 
 func TestTangle_ValueTransfer(t *testing.T) {
@@ -28,31 +28,31 @@ func TestTangle_ValueTransfer(t *testing.T) {
 	ledgerState := tangle.NewLedgerState(valueTangle)
 
 	// initialize seed
-	seed := wallet.NewSeed()
+	seed := walletseed.NewSeed()
 
 	// setup consensus rules
 	consensus.NewFCOB(valueTangle, 0)
 
 	// check if ledger empty first
-	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(0)))
-	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(1)))
+	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(0).Address))
+	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(1).Address))
 
 	// load snapshot
 	valueTangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{
 		transaction.GenesisID: {
-			seed.Address(0): []*balance.Balance{
+			seed.Address(0).Address: []*balance.Balance{
 				balance.New(balance.ColorIOTA, 337),
 			},
 
-			seed.Address(1): []*balance.Balance{
+			seed.Address(1).Address: []*balance.Balance{
 				balance.New(balance.ColorIOTA, 1000),
 			},
 		},
 	})
 
 	// check if balance exists after loading snapshot
-	assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 337}, ledgerState.Balances(seed.Address(0)))
-	assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 1000}, ledgerState.Balances(seed.Address(1)))
+	assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 337}, ledgerState.Balances(seed.Address(0).Address))
+	assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 1000}, ledgerState.Balances(seed.Address(1).Address))
 
 	// introduce logic to record liked payloads
 	recordedLikedPayloads, resetRecordedLikedPayloads := recordLikedPayloads(valueTangle)
@@ -61,8 +61,8 @@ func TestTangle_ValueTransfer(t *testing.T) {
 	outputAddress1 := address.Random()
 	attachedPayload1 := payload.New(payload.GenesisID, payload.GenesisID, transaction.New(
 		transaction.NewInputs(
-			transaction.NewOutputID(seed.Address(0), transaction.GenesisID),
-			transaction.NewOutputID(seed.Address(1), transaction.GenesisID),
+			transaction.NewOutputID(seed.Address(0).Address, transaction.GenesisID),
+			transaction.NewOutputID(seed.Address(1).Address, transaction.GenesisID),
 		),
 
 		transaction.NewOutputs(map[address.Address][]*balance.Balance{
@@ -78,8 +78,8 @@ func TestTangle_ValueTransfer(t *testing.T) {
 	})
 
 	// check if old addresses are empty and new addresses are filled
-	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(0)))
-	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(1)))
+	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(0).Address))
+	assert.Equal(t, map[balance.Color]int64{}, ledgerState.Balances(seed.Address(1).Address))
 	assert.Equal(t, map[balance.Color]int64{balance.ColorIOTA: 1337}, ledgerState.Balances(outputAddress1))
 	assert.Equal(t, 1, len(recordedLikedPayloads))
 	assert.Contains(t, recordedLikedPayloads, attachedPayload1.ID())
@@ -90,8 +90,8 @@ func TestTangle_ValueTransfer(t *testing.T) {
 	outputAddress2 := address.Random()
 	valueTangle.AttachPayloadSync(payload.New(payload.GenesisID, payload.GenesisID, transaction.New(
 		transaction.NewInputs(
-			transaction.NewOutputID(seed.Address(0), transaction.GenesisID),
-			transaction.NewOutputID(seed.Address(1), transaction.GenesisID),
+			transaction.NewOutputID(seed.Address(0).Address, transaction.GenesisID),
+			transaction.NewOutputID(seed.Address(1).Address, transaction.GenesisID),
 		),
 
 		transaction.NewOutputs(map[address.Address][]*balance.Balance{
diff --git a/dapps/valuetransfers/packages/wallet/wallet.go b/dapps/valuetransfers/packages/wallet/wallet.go
deleted file mode 100644
index 88ab072fa649fa6840db5fc2df8c525dc59fa9a3..0000000000000000000000000000000000000000
--- a/dapps/valuetransfers/packages/wallet/wallet.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package wallet
-
-// Wallet represents a simple cryptocurrency wallet for the IOTA tangle. It contains the logic to manage the movement of
-// funds.
-type Wallet struct {
-	seed *Seed
-}
-
-// New is the factory method of the wallet. It either creates a new wallet or restores the wallet backup that is handed
-// in as an optional parameter.
-func New(optionalRecoveryBytes ...[]byte) *Wallet {
-	return &Wallet{
-		seed: NewSeed(optionalRecoveryBytes...),
-	}
-}
-
-// Seed returns the seed of this wallet that is used to generate all of the wallets addresses and private keys.
-func (wallet *Wallet) Seed() *Seed {
-	return wallet.seed
-}
diff --git a/tools/cli-wallet/address.go b/tools/cli-wallet/address.go
new file mode 100644
index 0000000000000000000000000000000000000000..2eb7c4b68bd642996df2ea2462b3f16af49c0a3a
--- /dev/null
+++ b/tools/cli-wallet/address.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"text/tabwriter"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+)
+
+func execAddressCommand(command *flag.FlagSet, cliWallet *wallet.Wallet) {
+	command.Usage = func() {
+		printUsage(command)
+	}
+
+	receivePtr := command.Bool("receive", false, "show the latest receive address")
+	newReceiveAddressPtr := command.Bool("new", false, "generate a new receive address")
+	listPtr := command.Bool("list", false, "list all addresses")
+	listUnspentPtr := command.Bool("listunspent", false, "list all unspent addresses")
+	listSpentPtr := command.Bool("listspent", false, "list all spent addresses")
+	helpPtr := command.Bool("help", false, "display this help screen")
+
+	err := command.Parse(os.Args[2:])
+	if err != nil {
+		printUsage(command, err.Error())
+	}
+	if *helpPtr {
+		printUsage(command)
+	}
+
+	// sanitize flags
+	setFlagCount := 0
+	if *receivePtr {
+		setFlagCount++
+	}
+	if *listPtr {
+		setFlagCount++
+	}
+	if *listUnspentPtr {
+		setFlagCount++
+	}
+	if *listSpentPtr {
+		setFlagCount++
+	}
+	if *newReceiveAddressPtr {
+		setFlagCount++
+	}
+	if setFlagCount == 0 {
+		printUsage(command)
+	}
+	if setFlagCount > 1 {
+		printUsage(command, "please provide only one option at a time")
+	}
+
+	if *receivePtr {
+		fmt.Println()
+		fmt.Println("Latest Receive Address: " + cliWallet.ReceiveAddress().String())
+	}
+
+	if *newReceiveAddressPtr {
+		fmt.Println()
+		fmt.Println("New Receive Address: " + cliWallet.NewReceiveAddress().String())
+	}
+
+	if *listPtr {
+		// initialize tab writer
+		w := new(tabwriter.Writer)
+		w.Init(os.Stdout, 0, 8, 2, '\t', 0)
+		defer w.Flush()
+
+		// print header
+		fmt.Println()
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "INDEX", "ADDRESS", "SPENT")
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "-----", "--------------------------------------------", "-----")
+
+		addressPrinted := false
+		for _, addr := range cliWallet.AddressManager().Addresses() {
+			_, _ = fmt.Fprintf(w, "%d\t%s\t%t\n", addr.Index, addr.String(), cliWallet.AddressManager().IsAddressSpent(addr.Index))
+
+			addressPrinted = true
+		}
+
+		if !addressPrinted {
+			_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "<EMPTY>", "<EMPTY>", "<EMPTY>")
+		}
+	}
+
+	if *listUnspentPtr {
+		// initialize tab writer
+		w := new(tabwriter.Writer)
+		w.Init(os.Stdout, 0, 8, 2, '\t', 0)
+		defer w.Flush()
+
+		// print header
+		fmt.Println()
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "INDEX", "ADDRESS", "SPENT")
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "-------", "--------------------------------------------", "-------")
+
+		addressPrinted := false
+		for _, addr := range cliWallet.AddressManager().UnspentAddresses() {
+			_, _ = fmt.Fprintf(w, "%d\t%s\t%t\n", addr.Index, addr.String(), cliWallet.AddressManager().IsAddressSpent(addr.Index))
+
+			addressPrinted = true
+		}
+
+		if !addressPrinted {
+			_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "<EMPTY>", "<EMPTY>", "<EMPTY>")
+		}
+	}
+
+	if *listSpentPtr {
+		// initialize tab writer
+		w := new(tabwriter.Writer)
+		w.Init(os.Stdout, 0, 8, 2, '\t', 0)
+		defer w.Flush()
+
+		// print header
+		fmt.Println()
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "INDEX", "ADDRESS", "SPENT")
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "-------", "--------------------------------------------", "-------")
+
+		addressPrinted := false
+		for _, addr := range cliWallet.AddressManager().SpentAddresses() {
+			_, _ = fmt.Fprintf(w, "%d\t%s\t%t\n", addr.Index, addr.String(), cliWallet.AddressManager().IsAddressSpent(addr.Index))
+
+			addressPrinted = true
+		}
+
+		if !addressPrinted {
+			_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", "<EMPTY>", "<EMPTY>", "<EMPTY>")
+		}
+	}
+}
diff --git a/tools/cli-wallet/balance.go b/tools/cli-wallet/balance.go
new file mode 100644
index 0000000000000000000000000000000000000000..f4244505b6238efc5ae37adcee706ec67cc0d627
--- /dev/null
+++ b/tools/cli-wallet/balance.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"text/tabwriter"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+)
+
+func execBalanceCommand(command *flag.FlagSet, cliWallet *wallet.Wallet) {
+	err := command.Parse(os.Args[2:])
+	if err != nil {
+		panic(err)
+	}
+
+	confirmedBalance, pendingBalance, err := cliWallet.Balance()
+	if err != nil {
+		printUsage(nil, err.Error())
+	}
+
+	// initialize tab writer
+	w := new(tabwriter.Writer)
+	w.Init(os.Stdout, 0, 8, 2, '\t', 0)
+	defer w.Flush()
+
+	// print header
+	fmt.Println()
+	_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "STATUS", "BALANCE", "COLOR", "TOKEN NAME")
+	_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "------", "---------------", "--------------------------------------------", "-------------------------")
+
+	// print empty if no balances founds
+	if len(confirmedBalance) == 0 && len(pendingBalance) == 0 {
+		_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "<EMPTY>", "<EMPTY>", "<EMPTY>", "<EMPTY>")
+
+		return
+	}
+
+	// print balances
+	for color, amount := range confirmedBalance {
+		_, _ = fmt.Fprintf(w, "%s\t%d %s\t%s\t%s\n", "[ OK ]", amount, cliWallet.AssetRegistry().Symbol(color), color.String(), cliWallet.AssetRegistry().Name(color))
+	}
+	for color, amount := range pendingBalance {
+		_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%s\n", "[PEND]", amount, color.String(), cliWallet.AssetRegistry().Name(color))
+	}
+}
diff --git a/tools/cli-wallet/build.sh b/tools/cli-wallet/build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..09d5182a757bf41b42be8a56e6feffdcd3325546
--- /dev/null
+++ b/tools/cli-wallet/build.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+echo "Building executables..."
+
+GOOS=windows GOARCH=amd64 go build -o cli-wallet_Windows_x86_64.exe
+echo "Windows version created"
+GOOS=linux GOARCH=amd64 go build -o cli-wallet_Linux_x86_64
+echo "Linux version created"
+GOOS=darwin GOARCH=amd64 go build -o cli-wallet_macOS_x86_64
+echo "MAC OSX version created"
+
+echo "All done!"
\ No newline at end of file
diff --git a/tools/cli-wallet/config.go b/tools/cli-wallet/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..781f19bb95bf42e95d91c20e0aa26db5a9b784cc
--- /dev/null
+++ b/tools/cli-wallet/config.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+)
+
+// config type that defines the config structure
+type configuration struct {
+	WebAPI string
+}
+
+// internal variable that holds the config
+var config = configuration{}
+
+// load the config file
+func loadConfig() {
+	// open config file
+	file, err := os.Open("config.json")
+	if err != nil {
+		if !os.IsNotExist(err) {
+			panic(err)
+		}
+
+		if err = ioutil.WriteFile("config.json", []byte("{\n  \"WebAPI\": \"http://127.0.0.1:8080\"\n}"), 0644); err != nil {
+			panic(err)
+		}
+		if file, err = os.Open("config.json"); err != nil {
+			panic(err)
+		}
+	}
+	defer file.Close()
+
+	// decode config file
+	if err = json.NewDecoder(file).Decode(&config); err != nil {
+		panic(err)
+	}
+}
diff --git a/tools/cli-wallet/create_asset.go b/tools/cli-wallet/create_asset.go
new file mode 100644
index 0000000000000000000000000000000000000000..ba187b38aaebc73dff435004c16c8adc4761c483
--- /dev/null
+++ b/tools/cli-wallet/create_asset.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+	"strconv"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+)
+
+func execCreateAssetCommand(command *flag.FlagSet, cliWallet *wallet.Wallet) {
+	command.Usage = func() {
+		printUsage(command)
+	}
+
+	helpPtr := command.Bool("help", false, "show this help screen")
+	amountPtr := command.Uint64("amount", 0, "the amount of tokens to be created")
+	namePtr := command.String("name", "", "the name of the tokens to create")
+	symbolPtr := command.String("symbol", "", "the symbol of the tokens to create")
+
+	err := command.Parse(os.Args[2:])
+	if err != nil {
+		printUsage(command, err.Error())
+	}
+	if *helpPtr {
+		printUsage(command)
+	}
+
+	if *amountPtr == 0 {
+		printUsage(command)
+	}
+
+	if *namePtr == "" {
+		printUsage(command, "you need to provide a name for you asset")
+	}
+
+	assetColor, err := cliWallet.CreateAsset(wallet.Asset{
+		Name:   *namePtr,
+		Symbol: *symbolPtr,
+		Amount: *amountPtr,
+	})
+	if err != nil {
+		printUsage(command, err.Error())
+	}
+
+	fmt.Println()
+	fmt.Println("Creating " + strconv.Itoa(int(*amountPtr)) + " tokens with the color '" + assetColor.String() + "' ...   [DONE]")
+}
diff --git a/tools/cli-wallet/lib.go b/tools/cli-wallet/lib.go
new file mode 100644
index 0000000000000000000000000000000000000000..280960954ca593f10d6d25e3bca9cb669737dce7
--- /dev/null
+++ b/tools/cli-wallet/lib.go
@@ -0,0 +1,166 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"unsafe"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
+	"github.com/iotaledger/hive.go/bitmask"
+	"github.com/iotaledger/hive.go/crypto/ed25519"
+	"github.com/iotaledger/hive.go/marshalutil"
+	"github.com/mr-tron/base58"
+)
+
+func printBanner() {
+	fmt.Println("IOTA Pollen CLI-Wallet 0.1")
+}
+
+func loadWallet() *wallet.Wallet {
+	seed, lastAddressIndex, spentAddresses, assetRegistry, err := importWalletStateFile("wallet.dat")
+	if err != nil {
+		panic(err)
+	}
+
+	return wallet.New(
+		wallet.WebAPI(config.WebAPI),
+		wallet.Import(seed, lastAddressIndex, spentAddresses, assetRegistry),
+	)
+}
+
+func importWalletStateFile(filename string) (seed *walletseed.Seed, lastAddressIndex uint64, spentAddresses []bitmask.BitMask, assetRegistry *wallet.AssetRegistry, err error) {
+	walletStateBytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			return
+		}
+
+		if len(os.Args) < 2 || os.Args[1] != "init" {
+			printUsage(nil, "no wallet file (wallet.dat) found: please call \""+filepath.Base(os.Args[0])+" init\"")
+		}
+
+		seed = walletseed.NewSeed()
+		lastAddressIndex = 0
+		spentAddresses = []bitmask.BitMask{}
+		err = nil
+
+		fmt.Println("GENERATING NEW WALLET ...                                 [DONE]")
+		fmt.Println()
+		fmt.Println("================================================================")
+		fmt.Println("!!!            PLEASE CREATE A BACKUP OF YOUR SEED           !!!")
+		fmt.Println("!!!                                                          !!!")
+		fmt.Println("!!!       " + base58.Encode(seed.Bytes()) + "       !!!")
+		fmt.Println("!!!                                                          !!!")
+		fmt.Println("!!!            PLEASE CREATE A BACKUP OF YOUR SEED           !!!")
+		fmt.Println("================================================================")
+
+		return
+	}
+
+	if len(os.Args) >= 2 && os.Args[1] == "init" {
+		printUsage(nil, "please remove the wallet.dat before trying to create a new wallet")
+	}
+
+	marshalUtil := marshalutil.New(walletStateBytes)
+
+	seedBytes, err := marshalUtil.ReadBytes(ed25519.SeedSize)
+	seed = walletseed.NewSeed(seedBytes)
+	if err != nil {
+		return
+	}
+
+	lastAddressIndex, err = marshalUtil.ReadUint64()
+	if err != nil {
+		return
+	}
+
+	assetRegistry, _, err = wallet.ParseAssetRegistry(marshalUtil)
+
+	spentAddressesBytes := marshalUtil.ReadRemainingBytes()
+	spentAddresses = *(*[]bitmask.BitMask)(unsafe.Pointer(&spentAddressesBytes))
+
+	return
+}
+
+func writeWalletStateFile(wallet *wallet.Wallet, filename string) {
+	var skipRename bool
+	info, err := os.Stat(filename)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			panic(err)
+		}
+
+		skipRename = true
+	}
+	if err == nil && info.IsDir() {
+		panic("found directory instead of file at " + filename)
+	}
+
+	if !skipRename {
+		err = os.Rename(filename, filename+".bkp")
+		if err != nil && os.IsNotExist(err) {
+			panic(err)
+		}
+	}
+
+	err = ioutil.WriteFile(filename, wallet.ExportState(), 0644)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func printUsage(command *flag.FlagSet, optionalErrorMessage ...string) {
+	if len(optionalErrorMessage) >= 1 {
+		_, _ = fmt.Fprintf(os.Stderr, "\n")
+		_, _ = fmt.Fprintf(os.Stderr, "ERROR:\n  "+optionalErrorMessage[0]+"\n")
+	}
+
+	if command == nil {
+		fmt.Println()
+		fmt.Println("USAGE:")
+		fmt.Println("  " + filepath.Base(os.Args[0]) + " [COMMAND]")
+		fmt.Println()
+		fmt.Println("COMMANDS:")
+		fmt.Println("  balance")
+		fmt.Println("        show the balances held by this wallet")
+		fmt.Println("  send-funds")
+		fmt.Println("        initiate a value transfer")
+		fmt.Println("  create-asset")
+		fmt.Println("        create an asset in the form of colored coins")
+		fmt.Println("  address")
+		fmt.Println("        start the address manager of this wallet")
+		fmt.Println("  request-funds")
+		fmt.Println("        request funds from the testnet-faucet")
+		fmt.Println("  init")
+		fmt.Println("        generate a new wallet using a random seed")
+		fmt.Println("  server-status")
+		fmt.Println("        display the server status")
+		fmt.Println("  help")
+		fmt.Println("        display this help screen")
+
+		flag.PrintDefaults()
+
+		if len(optionalErrorMessage) >= 1 {
+			os.Exit(1)
+		}
+
+		os.Exit(0)
+	}
+
+	fmt.Println()
+	fmt.Println("USAGE:")
+	fmt.Println("  " + filepath.Base(os.Args[0]) + " " + command.Name() + " [OPTIONS]")
+	fmt.Println()
+	fmt.Println("OPTIONS:")
+	command.PrintDefaults()
+
+	if len(optionalErrorMessage) >= 1 {
+		os.Exit(1)
+	}
+
+	os.Exit(0)
+}
diff --git a/tools/cli-wallet/main.go b/tools/cli-wallet/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..b69e44465fc716f090d1bfc9c53ae30eaac6d200
--- /dev/null
+++ b/tools/cli-wallet/main.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+)
+
+// entry point for the program
+func main() {
+	defer func() {
+		if r := recover(); r != nil {
+			_, _ = fmt.Fprintf(os.Stderr, "\nFATAL ERROR: "+r.(error).Error())
+			os.Exit(1)
+		}
+	}()
+
+	// print banner + initialize framework
+	printBanner()
+	loadConfig()
+
+	// override Usage to use our custom method
+	flag.Usage = func() {
+		printUsage(nil)
+	}
+
+	// load wallet
+	wallet := loadWallet()
+	defer writeWalletStateFile(wallet, "wallet.dat")
+
+	// check if parameters potentially include sub commands
+	if len(os.Args) < 2 {
+		printUsage(nil)
+	}
+
+	// define sub commands
+	balanceCommand := flag.NewFlagSet("balance", flag.ExitOnError)
+	sendFundsCommand := flag.NewFlagSet("send-funds", flag.ExitOnError)
+	createAssetCommand := flag.NewFlagSet("create-asset", flag.ExitOnError)
+	addressCommand := flag.NewFlagSet("address", flag.ExitOnError)
+	requestFaucetFundsCommand := flag.NewFlagSet("request-funds", flag.ExitOnError)
+	serverStatusCommand := flag.NewFlagSet("server-status", flag.ExitOnError)
+
+	// switch logic according to provided sub command
+	switch os.Args[1] {
+	case "balance":
+		execBalanceCommand(balanceCommand, wallet)
+	case "address":
+		execAddressCommand(addressCommand, wallet)
+	case "send-funds":
+		execSendFundsCommand(sendFundsCommand, wallet)
+	case "create-asset":
+		execCreateAssetCommand(createAssetCommand, wallet)
+	case "request-funds":
+		execRequestFundsCommand(requestFaucetFundsCommand, wallet)
+	case "init":
+		fmt.Println()
+		fmt.Println("CREATING WALLET STATE FILE (wallet.dat) ...               [DONE]")
+	case "server-status":
+		execServerStatusCommand(serverStatusCommand, wallet)
+	case "help":
+		printUsage(nil)
+	default:
+		printUsage(nil, "unknown [COMMAND]: "+os.Args[1])
+	}
+}
diff --git a/tools/cli-wallet/requestfunds.go b/tools/cli-wallet/requestfunds.go
new file mode 100644
index 0000000000000000000000000000000000000000..c392708e412bb3139f1017e8fa26d257f065d633
--- /dev/null
+++ b/tools/cli-wallet/requestfunds.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+)
+
+func execRequestFundsCommand(command *flag.FlagSet, cliWallet *wallet.Wallet) {
+	err := command.Parse(os.Args[2:])
+	if err != nil {
+		printUsage(nil, err.Error())
+	}
+
+	fmt.Println()
+	fmt.Println("Requesting funds from faucet ... [PERFORMING POW]          (this can take a while)")
+
+	// request funds
+	err = cliWallet.RequestFaucetFunds()
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println("Requesting funds from faucet ... [DONE]")
+}
diff --git a/tools/cli-wallet/sendfunds.go b/tools/cli-wallet/sendfunds.go
new file mode 100644
index 0000000000000000000000000000000000000000..617f70e7d72222273761cb4153c5d64c3e2c796a
--- /dev/null
+++ b/tools/cli-wallet/sendfunds.go
@@ -0,0 +1,71 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
+	"github.com/mr-tron/base58"
+)
+
+func execSendFundsCommand(command *flag.FlagSet, cliWallet *wallet.Wallet) {
+	helpPtr := command.Bool("help", false, "show this help screen")
+	addressPtr := command.String("dest-addr", "", "destination address for the transfer")
+	amountPtr := command.Int64("amount", 0, "the amount of tokens that are supposed to be sent")
+	colorPtr := command.String("color", "IOTA", "color of the tokens to transfer (optional)")
+
+	err := command.Parse(os.Args[2:])
+	if err != nil {
+		panic(err)
+	}
+
+	if *helpPtr {
+		printUsage(command)
+	}
+
+	if *addressPtr == "" {
+		printUsage(command, "dest-addr has to be set")
+	}
+	if *amountPtr <= 0 {
+		printUsage(command, "amount has to be set and be bigger than 0")
+	}
+	if *colorPtr == "" {
+		printUsage(command, "color must be set")
+	}
+
+	destinationAddress, err := address.FromBase58(*addressPtr)
+	if err != nil {
+		printUsage(command, err.Error())
+	}
+
+	var color balance.Color
+	switch *colorPtr {
+	case "IOTA":
+		color = balance.ColorIOTA
+	case "NEW":
+		color = balance.ColorNew
+	default:
+		colorBytes, parseErr := base58.Decode(*colorPtr)
+		if parseErr != nil {
+			printUsage(command, parseErr.Error())
+		}
+
+		color, _, parseErr = balance.ColorFromBytes(colorBytes)
+		if parseErr != nil {
+			printUsage(command, parseErr.Error())
+		}
+	}
+
+	_, err = cliWallet.SendFunds(
+		wallet.Destination(destinationAddress, uint64(*amountPtr), color),
+	)
+	if err != nil {
+		printUsage(command, err.Error())
+	}
+
+	fmt.Println()
+	fmt.Println("Sending funds ... [DONE]")
+}
diff --git a/tools/cli-wallet/serverstatus.go b/tools/cli-wallet/serverstatus.go
new file mode 100644
index 0000000000000000000000000000000000000000..573ef0f88cd360482b0e745d37cb3f97066cfa8e
--- /dev/null
+++ b/tools/cli-wallet/serverstatus.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"github.com/iotaledger/goshimmer/client/wallet"
+)
+
+func execServerStatusCommand(command *flag.FlagSet, cliWallet *wallet.Wallet) {
+	err := command.Parse(os.Args[2:])
+	if err != nil {
+		printUsage(nil, err.Error())
+	}
+
+	fmt.Println()
+
+	// request funds
+	status, err := cliWallet.ServerStatus()
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println("Server ID: ", status.ID)
+	fmt.Println("Server Synced: ", status.Synced)
+	fmt.Println("Server Version: ", status.Version)
+}
diff --git a/tools/double-spend/double-spend.go b/tools/double-spend/double-spend.go
index 65f0f56335df8904c79863c92d506c700f8f94a3..c41d95ad3d0f811fdc4e26dc1723a4f3be8c7ebf 100644
--- a/tools/double-spend/double-spend.go
+++ b/tools/double-spend/double-spend.go
@@ -7,12 +7,13 @@ import (
 	"time"
 
 	"github.com/iotaledger/goshimmer/client"
+	"github.com/iotaledger/goshimmer/client/wallet"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 )
 
 func main() {
@@ -79,7 +80,7 @@ func main() {
 	// issue transactions which spend the same output
 	conflictingTxs := make([]*transaction.Transaction, 2)
 	conflictingMsgIDs := make([]string, 2)
-	receiverSeeds := make([]*wallet.Seed, 2)
+	receiverSeeds := make([]*walletseed.Seed, 2)
 
 	var wg sync.WaitGroup
 	for i := range conflictingTxs {
@@ -90,13 +91,13 @@ func main() {
 			fmt.Println(i)
 
 			// create a new receiver wallet for the given conflict
-			receiverSeeds[i] = wallet.NewSeed()
+			receiverSeeds[i] = walletseed.NewSeed()
 			destAddr := receiverSeeds[i].Address(0)
 
 			tx := transaction.New(
 				transaction.NewInputs(out),
 				transaction.NewOutputs(map[address.Address][]*balance.Balance{
-					destAddr: {
+					destAddr.Address: {
 						{Value: 1337, Color: balance.ColorIOTA},
 					},
 				}))
diff --git a/tools/genesis-snapshot/main.go b/tools/genesis-snapshot/main.go
index 1a887e139ba8e606516899e781401e2700361de0..a53c1980bd77cd867e56bb607fe91977016bd5a1 100644
--- a/tools/genesis-snapshot/main.go
+++ b/tools/genesis-snapshot/main.go
@@ -4,10 +4,10 @@ import (
 	"log"
 	"os"
 
+	"github.com/iotaledger/goshimmer/client/wallet"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	flag "github.com/spf13/pflag"
 	"github.com/spf13/viper"
 )
@@ -33,7 +33,7 @@ func main() {
 	log.Printf("creating snapshot %s...", snapshotFileName)
 
 	genesisWallet := wallet.New()
-	genesisAddress := genesisWallet.Seed().Address(0)
+	genesisAddress := genesisWallet.Seed().Address(0).Address
 
 	log.Println("genesis:")
 	log.Printf("-> seed (base58): %s", genesisWallet.Seed().String())
diff --git a/tools/integration-tests/tester/framework/network.go b/tools/integration-tests/tester/framework/network.go
index d72337f1553f376b4dae72247decb02e65ab2181..cc745a378847eb800c97a6a53f1c8c6a5abb52ea 100644
--- a/tools/integration-tests/tester/framework/network.go
+++ b/tools/integration-tests/tester/framework/network.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/iotaledger/hive.go/identity"
 )
@@ -103,11 +103,11 @@ func (n *Network) CreatePeer(c GoShimmerConfig) (*Peer, error) {
 	config.SnapshotFilePath = snapshotFilePath
 
 	// create wallet
-	var nodeWallet *wallet.Wallet
+	var nodeSeed *walletseed.Seed
 	if c.Faucet == true {
-		nodeWallet = wallet.New(genesisSeed)
+		nodeSeed = walletseed.NewSeed(genesisSeed)
 	} else {
-		nodeWallet = wallet.New()
+		nodeSeed = walletseed.NewSeed()
 	}
 
 	// create Docker container
@@ -125,7 +125,7 @@ func (n *Network) CreatePeer(c GoShimmerConfig) (*Peer, error) {
 		return nil, err
 	}
 
-	peer, err := newPeer(name, identity.New(publicKey), container, nodeWallet, n)
+	peer, err := newPeer(name, identity.New(publicKey), container, nodeSeed, n)
 	if err != nil {
 		return nil, err
 	}
diff --git a/tools/integration-tests/tester/framework/peer.go b/tools/integration-tests/tester/framework/peer.go
index 8da31c20ef4634f2dc408034b5046a9b7b589712..e3e540aa0d4d2c89a6db9f02eaac516d4d25fff6 100644
--- a/tools/integration-tests/tester/framework/peer.go
+++ b/tools/integration-tests/tester/framework/peer.go
@@ -6,7 +6,7 @@ import (
 	"time"
 
 	"github.com/iotaledger/goshimmer/client"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	"github.com/iotaledger/goshimmer/plugins/webapi/autopeering"
 	"github.com/iotaledger/hive.go/identity"
 )
@@ -25,8 +25,8 @@ type Peer struct {
 	// the DockerContainer that this peer is running in
 	*DockerContainer
 
-	// Wallet
-	*wallet.Wallet
+	// Seed
+	*walletseed.Seed
 
 	chosen   []autopeering.Neighbor
 	accepted []autopeering.Neighbor
@@ -34,7 +34,7 @@ type Peer struct {
 
 // newPeer creates a new instance of Peer with the given information.
 // dockerContainer needs to be started in order to determine the container's (and therefore peer's) IP correctly.
-func newPeer(name string, identity *identity.Identity, dockerContainer *DockerContainer, wallet *wallet.Wallet, network *Network) (*Peer, error) {
+func newPeer(name string, identity *identity.Identity, dockerContainer *DockerContainer, seed *walletseed.Seed, network *Network) (*Peer, error) {
 	// after container is started we can get its IP
 	ip, err := dockerContainer.IP(network.name)
 	if err != nil {
@@ -47,7 +47,7 @@ func newPeer(name string, identity *identity.Identity, dockerContainer *DockerCo
 		Identity:        identity,
 		GoShimmerAPI:    client.NewGoShimmerAPI(getWebAPIBaseURL(name), http.Client{Timeout: 30 * time.Second}),
 		DockerContainer: dockerContainer,
-		Wallet:          wallet,
+		Seed:            seed,
 	}, nil
 }
 
diff --git a/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go b/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
index 4aec007c066a5feaeab1cb9fe530631228da5f4f..1e56f39a1ed79bf1a27c5f43a47aeadfec7c6006 100644
--- a/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
+++ b/tools/integration-tests/tester/tests/consensus/consensus_conflicts_test.go
@@ -7,11 +7,11 @@ import (
 
 	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/framework"
 
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
 	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests"
 	"github.com/mr-tron/base58/base58"
@@ -57,20 +57,20 @@ func TestConsensusFiftyFiftyOpinionSplit(t *testing.T) {
 	require.NoError(t, err, "couldn't decode genesis seed from base58 seed")
 
 	const genesisBalance = 1000000000
-	genesisWallet := wallet.New(genesisSeedBytes)
-	genesisAddr := genesisWallet.Seed().Address(0)
+	genesisSeed := walletseed.NewSeed(genesisSeedBytes)
+	genesisAddr := genesisSeed.Address(0).Address
 	genesisOutputID := transaction.NewOutputID(genesisAddr, transaction.GenesisID)
 
 	// issue transactions which spend the same genesis output in all partitions
 	conflictingTxs := make([]*transaction.Transaction, len(n.Partitions()))
 	conflictingTxIDs := make([]string, len(n.Partitions()))
-	receiverWallets := make([]*wallet.Wallet, len(n.Partitions()))
+	receiverSeeds := make([]*walletseed.Seed, len(n.Partitions()))
 	for i, partition := range n.Partitions() {
 
 		// create a new receiver wallet for the given partition
-		partitionReceiverWallet := wallet.New()
-		destAddr := partitionReceiverWallet.Seed().Address(0)
-		receiverWallets[i] = partitionReceiverWallet
+		partitionReceiverSeed := walletseed.NewSeed()
+		destAddr := partitionReceiverSeed.Address(0).Address
+		receiverSeeds[i] = partitionReceiverSeed
 		tx := transaction.New(
 			transaction.NewInputs(genesisOutputID),
 			transaction.NewOutputs(map[address.Address][]*balance.Balance{
@@ -78,7 +78,7 @@ func TestConsensusFiftyFiftyOpinionSplit(t *testing.T) {
 					{Value: genesisBalance, Color: balance.ColorIOTA},
 				},
 			}))
-		tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
+		tx = tx.Sign(signaturescheme.ED25519(*genesisSeed.KeyPair(0)))
 		conflictingTxs[i] = tx
 
 		// issue the transaction on the first peer of the partition
diff --git a/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go b/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go
index badf4040180e3135368f021125eca471be2a8f19..efb5bde599bc1ba7228165272f2058c8d9a1c55c 100644
--- a/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go
+++ b/tools/integration-tests/tester/tests/consensus/consensus_noconflicts_test.go
@@ -6,12 +6,12 @@ import (
 	"testing"
 	"time"
 
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
 	"github.com/iotaledger/goshimmer/plugins/webapi/value/utils"
 	"github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests"
 	"github.com/mr-tron/base58/base58"
@@ -32,11 +32,11 @@ func TestConsensusNoConflicts(t *testing.T) {
 	require.NoError(t, err, "couldn't decode genesis seed from base58 seed")
 
 	const genesisBalance = 1000000000
-	genesisWallet := wallet.New(genesisSeedBytes)
-	genesisAddr := genesisWallet.Seed().Address(0)
+	genesisSeed := walletseed.NewSeed(genesisSeedBytes)
+	genesisAddr := genesisSeed.Address(0).Address
 	genesisOutputID := transaction.NewOutputID(genesisAddr, transaction.GenesisID)
 
-	firstReceiver := wallet.New()
+	firstReceiver := walletseed.NewSeed()
 	const depositCount = 10
 	const deposit = genesisBalance / depositCount
 	firstReceiverAddresses := make([]string, depositCount)
@@ -44,7 +44,7 @@ func TestConsensusNoConflicts(t *testing.T) {
 	firstReceiverDepositOutputs := map[address.Address][]*balance.Balance{}
 	firstReceiverExpectedBalances := map[string]map[balance.Color]int64{}
 	for i := 0; i < depositCount; i++ {
-		addr := firstReceiver.Seed().Address(uint64(i))
+		addr := firstReceiver.Address(uint64(i)).Address
 		firstReceiverDepositAddrs[i] = addr
 		firstReceiverAddresses[i] = addr.String()
 		firstReceiverDepositOutputs[addr] = []*balance.Balance{{Value: deposit, Color: balance.ColorIOTA}}
@@ -54,7 +54,7 @@ func TestConsensusNoConflicts(t *testing.T) {
 	// issue transaction spending from the genesis output
 	log.Printf("issuing transaction spending genesis to %d addresses", depositCount)
 	tx := transaction.New(transaction.NewInputs(genesisOutputID), transaction.NewOutputs(firstReceiverDepositOutputs))
-	tx = tx.Sign(signaturescheme.ED25519(*genesisWallet.Seed().KeyPair(0)))
+	tx = tx.Sign(signaturescheme.ED25519(*genesisSeed.KeyPair(0)))
 	utilsTx := utils.ParseTransaction(tx)
 
 	txID, err := n.Peers()[0].SendTransaction(tx.Bytes())
@@ -86,20 +86,20 @@ func TestConsensusNoConflicts(t *testing.T) {
 	tests.CheckBalances(t, n.Peers(), firstReceiverExpectedBalances)
 
 	// issue transactions spending all the outputs which were just created from a random peer
-	secondReceiverWallet := wallet.New()
+	secondReceiverSeed := walletseed.NewSeed()
 	secondReceiverAddresses := make([]string, depositCount)
 	secondReceiverExpectedBalances := map[string]map[balance.Color]int64{}
 	secondReceiverExpectedTransactions := map[string]*tests.ExpectedTransaction{}
 	for i := 0; i < depositCount; i++ {
-		addr := secondReceiverWallet.Seed().Address(uint64(i))
+		addr := secondReceiverSeed.Address(uint64(i)).Address
 		tx := transaction.New(
-			transaction.NewInputs(transaction.NewOutputID(firstReceiver.Seed().Address(uint64(i)), tx.ID())),
+			transaction.NewInputs(transaction.NewOutputID(firstReceiver.Address(uint64(i)).Address, tx.ID())),
 			transaction.NewOutputs(map[address.Address][]*balance.Balance{
 				addr: {{Value: deposit, Color: balance.ColorIOTA}},
 			}),
 		)
 		secondReceiverAddresses[i] = addr.String()
-		tx = tx.Sign(signaturescheme.ED25519(*firstReceiver.Seed().KeyPair(uint64(i))))
+		tx = tx.Sign(signaturescheme.ED25519(*firstReceiver.KeyPair(uint64(i))))
 		txID, err := n.Peers()[rand.Intn(len(n.Peers()))].SendTransaction(tx.Bytes())
 		require.NoError(t, err)
 
diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go
index 49ab041eee8bae05bfc6e73256aef7cc5bd0a6b3..d6b8c98bd62811ec5fd4e86e8b82bd5da0810ae7 100644
--- a/tools/integration-tests/tester/tests/testutil.go
+++ b/tools/integration-tests/tester/tests/testutil.go
@@ -83,7 +83,7 @@ func SendFaucetRequestOnRandomPeer(t *testing.T, peers []*framework.Peer, numMes
 
 	for i := 0; i < numMessages; i++ {
 		peer := peers[rand.Intn(len(peers))]
-		addr := peer.Seed().Address(uint64(i))
+		addr := peer.Seed.Address(uint64(i)).Address
 		id, sent := SendFaucetRequest(t, peer, addr)
 		ids[id] = sent
 		addrBalance[addr.String()] = map[balance.Color]int64{
@@ -150,12 +150,12 @@ func SendTransactionFromFaucet(t *testing.T, peers []*framework.Peer, sentValue
 	// initiate addrBalance map
 	addrBalance = make(map[string]map[balance.Color]int64)
 	for _, p := range peers {
-		addr := p.Seed().Address(0).String()
+		addr := p.Seed.Address(0).String()
 		addrBalance[addr] = make(map[balance.Color]int64)
 	}
 
 	faucetPeer := peers[0]
-	faucetAddrStr := faucetPeer.Seed().Address(0).String()
+	faucetAddrStr := faucetPeer.Seed.Address(0).String()
 
 	// get faucet balances
 	unspentOutputs, err := faucetPeer.GetUnspentOutputs([]string{faucetAddrStr})
@@ -204,9 +204,9 @@ func SendTransactionOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBala
 // SendIotaTransaction sends sentValue amount of IOTA tokens and remainders from and to a given peer and returns the fail flag and the transaction ID.
 // Every peer sends and receives the transaction on the address of index 0.
 func SendIotaTransaction(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64, sentValue int64) (fail bool, txId string) {
-	sigScheme := signaturescheme.ED25519(*from.Seed().KeyPair(0))
-	inputAddr := from.Seed().Address(0)
-	outputAddr := to.Seed().Address(0)
+	sigScheme := signaturescheme.ED25519(*from.Seed.KeyPair(0))
+	inputAddr := from.Seed.Address(0).Address
+	outputAddr := to.Seed.Address(0).Address
 
 	// prepare inputs
 	resp, err := from.GetUnspentOutputs([]string{inputAddr.String()})
@@ -288,9 +288,9 @@ func SendColoredTransactionOnRandomPeer(t *testing.T, peers []*framework.Peer, a
 func SendColoredTransaction(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) {
 	var sentValue int64 = 50
 	var balanceList []*balance.Balance
-	sigScheme := signaturescheme.ED25519(*from.Seed().KeyPair(0))
-	inputAddr := from.Seed().Address(0)
-	outputAddr := to.Seed().Address(0)
+	sigScheme := signaturescheme.ED25519(*from.Seed.KeyPair(0))
+	inputAddr := from.Seed.Address(0).Address
+	outputAddr := to.Seed.Address(0).Address
 
 	// prepare inputs
 	resp, err := from.GetUnspentOutputs([]string{inputAddr.String()})
diff --git a/tools/rand-address/main.go b/tools/rand-address/main.go
index dd13cc419882fb2f1f7408dd9db1462f062b2f79..fb4c06c20c51616ca0c9ab8c796ab898d3563f60 100644
--- a/tools/rand-address/main.go
+++ b/tools/rand-address/main.go
@@ -3,9 +3,9 @@ package main
 import (
 	"fmt"
 
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
+	walletseed "github.com/iotaledger/goshimmer/client/wallet/packages/seed"
 )
 
 func main() {
-	fmt.Println(wallet.New().Seed().Address(0))
+	fmt.Println(walletseed.NewSeed().Address(0))
 }