Repository: decred/dcrd
Pull request #2625 (2021-03-16 18:07:38)

multi: Convert to use new stdaddr package.
**This requires #2619, #2624, and #2620**.

~**NOTE: I've left this marked as a draft for now because the various `go.mod` files in the affected modules will need to be updated to use the latest `txscript` module once the aforementioned prerequisite PRs have landed to avoid issues for external consumers, however, this is otherwise ready for review.**~

---

This converts all packages in the repository to make use of the new `stdaddr` package for addresses instead of `dcrutil.Address`+`txscript.PayToX` methods as well as to support and make use of the script version returned by the new addresses accordingly.  It also removes the aforementioned `txscript.PayToX` methods once they are no longer used.

Since this involves modifications to a large number of packages in the repository, this PR is split into a series of individual commits such that everything builds and passes all tests each step of the way.  In order to achieve that goal, each commit updates a single package to use the new addresses while simultaneously updating all consumers of that package in the repository to deal with any fallout of API changes.  Typically, this means some temporary address conversion code is introduced and then removed in a later commit once it's no longer needed.  Finally, all of the unused methods and any associated tests are then removed one by one.

See each individual commit for more details about each individual change, but the following is a high level overview:

- Converts the following packages to use the new `stdaddr` package:
  - `internal/mining`
  - `internal/mempool`
  - `blockchain/stake`
  - `blockchain/indexers`
  - `blockchain`
  - `blockchain/fullblocktests`
  - `blockchain/chaingen`
  - `rpcclient`
  - `rpctest`
  - `internal/rpcserver`
  - `hdkeychain`
  - `txscript`
- Updates all call sites that deal with addresses to properly use the script version returned by the address script generation methods
- Makes minor modifications for consistency and other cleanup while converting
- Removes the following methods:
  - `chaingen.PurchaseCommitmentScript`
  - `txscript.PayToSStx`
  - `txscript.PayToSStxChange`
  - `txscript.PayToSSGen`
  - `txscript.PayToSSGenSHDirect`
  - `txscript.PayToSSGenPKHDirect`
  - `txscript.PayToSSRtx`
  - `txscript.PayToSSRtxPKHDirect`
  - `txscript.PayToSSRtxSHDirect`
  - `txscript.PayToAddrScript`
  - `txscript.PayToScriptHashScript`
  - `txscript.payToSchnorrPubKeyScript`
  - `txscript.payToEdwardsPubKeyScript`
  - `txscript.payToPubKeyScript`
  - `txscript.payToScriptHashScript`
  - `txscript.payToPubKeyHashSchnorrScript`
  - `txscript.payToPubKeyHashEdwardsScript`
  - `txscript.payToPubKeyHashScript`
  - `txscript.GenerateSStxAddrPush`

dnldd: Changes look good so far.
davecgh: I've updated the various `go.mod` files in the affected modules to use the latest `txscript` module to avoid issues for external consumers now that the prerequisite PRs have landed.
matheusd: Builds are failing on the following commits:

`b01536a` stake: Convert to use new stdaddr package
`4f53263 ` indexers: Convert to use new stdaddr package

There are linting errors after commit `7080a66` due to unused stuff that will be removed in the following commits. This makes `run_tests.sh` fail for those commits. Wanted to bring this up because I'm not sure whether that's critical or not.


Patch:
diff --git a/blockchain/chaingen/generator.go b/blockchain/chaingen/generator.go
index f3707649..02c40df5 100644
--- a/blockchain/chaingen/generator.go
+++ b/blockchain/chaingen/generator.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2020 The Decred developers
+// Copyright (c) 2016-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -19,6 +19,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -158,15 +159,16 @@ func (t stakeTicketSorter) Less(i, j int) bool {
 // available spendable outputs and generic payment scripts used throughout the
 // tests.
 type Generator struct {
-	params           *chaincfg.Params
-	tip              *wire.MsgBlock
-	tipName          string
-	blocks           map[chainhash.Hash]*wire.MsgBlock
-	blockHeights     map[chainhash.Hash]uint32
-	blocksByName     map[string]*wire.MsgBlock
-	blockNames       map[chainhash.Hash]string
-	p2shOpTrueAddr   dcrutil.Address
-	p2shOpTrueScript []byte
+	params              *chaincfg.Params
+	tip                 *wire.MsgBlock
+	tipName             string
+	blocks              map[chainhash.Hash]*wire.MsgBlock
+	blockHeights        map[chainhash.Hash]uint32
+	blocksByName        map[string]*wire.MsgBlock
+	blockNames          map[chainhash.Hash]string
+	p2shOpTrueAddr      stdaddr.StakeAddress
+	p2shOpTrueScriptVer uint16
+	p2shOpTrueScript    []byte
 
 	// Used for tracking spendable coinbase outputs.
 	spendableOuts     [][]SpendableOut
@@ -188,31 +190,29 @@ func MakeGenerator(params *chaincfg.Params) (Generator, error) {
 	// Generate a generic pay-to-script-hash script that is a simple
 	// OP_TRUE.  This allows the tests to avoid needing to generate and
 	// track actual public keys and signatures.
-	p2shOpTrueAddr, err := dcrutil.NewAddressScriptHash(opTrueScript, params)
-	if err != nil {
-		return Generator{}, err
-	}
-	p2shOpTrueScript, err := txscript.PayToAddrScript(p2shOpTrueAddr)
+	p2shOpTrueAddr, err := stdaddr.NewAddressScriptHashV0(opTrueScript, params)
 	if err != nil {
 		return Generator{}, err
 	}
+	p2shOpTrueScriptVer, p2shOpTrueScript := p2shOpTrueAddr.PaymentScript()
 
 	genesis := params.GenesisBlock
 	genesisHash := genesis.BlockHash()
 	return Generator{
-		params:           params,
-		tip:              genesis,
-		tipName:          "genesis",
-		blocks:           map[chainhash.Hash]*wire.MsgBlock{genesisHash: genesis},
-		blockHeights:     map[chainhash.Hash]uint32{genesisHash: 0},
-		blocksByName:     map[string]*wire.MsgBlock{"genesis": genesis},
-		blockNames:       map[chainhash.Hash]string{genesisHash: "genesis"},
-		p2shOpTrueAddr:   p2shOpTrueAddr,
-		p2shOpTrueScript: p2shOpTrueScript,
-		originalParents:  make(map[chainhash.Hash]chainhash.Hash),
-		wonTickets:       make(map[chainhash.Hash][]*stakeTicket),
-		revokedTickets:   make(map[chainhash.Hash][]*stakeTicket),
-		missedVotes:      make(map[chainhash.Hash]*stakeTicket),
+		params:              params,
+		tip:                 genesis,
+		tipName:             "genesis",
+		blocks:              map[chainhash.Hash]*wire.MsgBlock{genesisHash: genesis},
+		blockHeights:        map[chainhash.Hash]uint32{genesisHash: 0},
+		blocksByName:        map[string]*wire.MsgBlock{"genesis": genesis},
+		blockNames:          map[chainhash.Hash]string{genesisHash: "genesis"},
+		p2shOpTrueAddr:      p2shOpTrueAddr,
+		p2shOpTrueScriptVer: p2shOpTrueScriptVer,
+		p2shOpTrueScript:    p2shOpTrueScript,
+		originalParents:     make(map[chainhash.Hash]chainhash.Hash),
+		wonTickets:          make(map[chainhash.Hash][]*stakeTicket),
+		revokedTickets:      make(map[chainhash.Hash][]*stakeTicket),
+		missedVotes:         make(map[chainhash.Hash]*stakeTicket),
 	}, nil
 }
 
@@ -233,7 +233,7 @@ func (g *Generator) TipName() string {
 
 // P2shOpTrueAddr returns the generator p2sh script that is composed with
 // a single OP_TRUE.
-func (g *Generator) P2shOpTrueAddr() dcrutil.Address {
+func (g *Generator) P2shOpTrueAddr() stdaddr.StakeAddress {
 	return g.p2shOpTrueAddr
 }
 
@@ -395,6 +395,15 @@ func standardCoinbaseOpReturnScript(blockHeight uint32) []byte {
 	return opReturnScript(data)
 }
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 // addCoinbaseTxOutputs adds the following outputs to the provided transaction
 // which is assumed to be a coinbase transaction:
 // - First output pays the development subsidy portion to the dev org
@@ -423,7 +432,8 @@ func (g *Generator) addCoinbaseTxOutputs(tx *wire.MsgTx, blockHeight uint32, dev
 		if i == numPoWOutputs-1 {
 			amount = powSubsidy - amount*(numPoWOutputs-1)
 		}
-		tx.AddTxOut(wire.NewTxOut(int64(amount), g.p2shOpTrueScript))
+		tx.AddTxOut(newTxOut(int64(amount), g.p2shOpTrueScriptVer,
+			g.p2shOpTrueScript))
 	}
 }
 
@@ -458,41 +468,6 @@ func (g *Generator) CreateCoinbaseTx(blockHeight uint32, numVotes uint16) *wire.
 	return tx
 }
 
-// PurchaseCommitmentScript returns a standard provably-pruneable OP_RETURN
-// commitment script suitable for use in a ticket purchase tx (sstx) using the
-// provided target address, amount, and fee limits.
-func PurchaseCommitmentScript(addr dcrutil.Address, amount, voteFeeLimit, revocationFeeLimit dcrutil.Amount) []byte {
-	// The limits are defined in terms of the closest base 2 exponent and
-	// a bit that must be set to specify the limit is to be applied.  The
-	// vote fee exponent is in the bottom 8 bits, while the revocation fee
-	// exponent is in the upper 8 bits.
-	limits := uint16(0)
-	if voteFeeLimit != 0 {
-		exp := uint16(math.Ceil(math.Log2(float64(voteFeeLimit))))
-		limits |= (exp | 0x40)
-	}
-	if revocationFeeLimit != 0 {
-		exp := uint16(math.Ceil(math.Log2(float64(revocationFeeLimit))))
-		limits |= ((exp | 0x40) << 8)
-	}
-
-	// The data consists of the 20-byte raw script address for the given
-	// address, 8 bytes for the amount to commit to (with the upper bit flag
-	// set to indicate a pay-to-script-hash address), and 2 bytes for the
-	// fee limits.
-	var data [30]byte
-	copy(data[:], addr.ScriptAddress())
-	binary.LittleEndian.PutUint64(data[20:], uint64(amount))
-	data[27] |= 1 << 7
-	binary.LittleEndian.PutUint16(data[28:], limits)
-	script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_RETURN).
-		AddData(data[:]).Script()
-	if err != nil {
-		panic(err)
-	}
-	return script
-}
-
 // CreateTicketPurchaseTx creates a new transaction that spends the provided
 // output to purchase a stake submission ticket (sstx) at the given ticket
 // price.  Both the ticket and the change will go to a p2sh script that is
@@ -505,21 +480,15 @@ func PurchaseCommitmentScript(addr dcrutil.Address, amount, voteFeeLimit, revoca
 func (g *Generator) CreateTicketPurchaseTx(spend *SpendableOut, ticketPrice, fee dcrutil.Amount) *wire.MsgTx {
 	// The first output is the voting rights address.  This impl uses the
 	// standard pay-to-script-hash to an OP_TRUE.
-	pkScript, err := txscript.PayToSStx(g.p2shOpTrueAddr)
-	if err != nil {
-		panic(err)
-	}
+	voteScriptVer, voteScript := g.p2shOpTrueAddr.VotingRightsScript()
 
 	// Generate the commitment script.
-	commitScript := PurchaseCommitmentScript(g.p2shOpTrueAddr,
-		ticketPrice+fee, 0, ticketPrice)
+	commitScriptVer, commitScript := g.p2shOpTrueAddr.RewardCommitmentScript(
+		int64(ticketPrice+fee), 0, int64(ticketPrice))
 
 	// Calculate change and generate script to deliver it.
 	change := spend.amount - ticketPrice - fee
-	changeScript, err := txscript.PayToSStxChange(g.p2shOpTrueAddr)
-	if err != nil {
-		panic(err)
-	}
+	changeScriptVer, changeScript := g.p2shOpTrueAddr.StakeChangeScript()
 
 	// Generate and return the transaction spending from the provided
 	// spendable output with the previously described outputs.
@@ -532,37 +501,32 @@ func (g *Generator) CreateTicketPurchaseTx(spend *SpendableOut, ticketPrice, fee
 		BlockIndex:       spend.blockIndex,
 		SignatureScript:  opTrueRedeemScript,
 	})
-	tx.AddTxOut(wire.NewTxOut(int64(ticketPrice), pkScript))
-	tx.AddTxOut(wire.NewTxOut(0, commitScript))
-	tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
+	tx.AddTxOut(newTxOut(int64(ticketPrice), voteScriptVer, voteScript))
+	tx.AddTxOut(newTxOut(0, commitScriptVer, commitScript))
+	tx.AddTxOut(newTxOut(int64(change), changeScriptVer, changeScript))
 	return tx
 }
 
-// CreateTreasuryTAdd creates a new transaction that spends the provided output
-// to the treasury. If the amount minus fee is zero the returned transaction
-// does not have a change output.
+// CreateTreasuryTAddChange creates a new transaction that spends the provided
+// output to the treasury.  If the amount minus fee is zero the returned
+// transaction does not have a change output.
 //
 // The transaction consists of the following outputs:
 // - First output is an OP_TADD
 // - Second output is optional and when used it is an OP_SSTXCHANGE paying to
 // the provided changeAddr
 func (g *Generator) CreateTreasuryTAddChange(spend *SpendableOut,
-	amount, fee dcrutil.Amount, changeAddr dcrutil.Address) *wire.MsgTx {
+	amount, fee dcrutil.Amount, changeAddr stdaddr.StakeAddress) *wire.MsgTx {
 
 	// Calculate change and generate script to deliver it.
-	var (
-		changeScript []byte
-		err          error
-	)
+	var changeScriptVer uint16
+	var changeScript []byte
 	change := spend.amount - amount - fee
 	if change < 0 {
 		panic(fmt.Sprintf("negative change %v", change))
 	}
 	if change > 0 {
-		changeScript, err = txscript.PayToSStxChange(changeAddr)
-		if err != nil {
-			panic(err)
-		}
+		changeScriptVer, changeScript = changeAddr.StakeChangeScript()
 	}
 
 	// Generate and return the transaction spending from the provided
@@ -576,10 +540,9 @@ func (g *Generator) CreateTreasuryTAddChange(spend *SpendableOut,
 		BlockIndex:       spend.blockIndex,
 		SignatureScript:  opTrueRedeemScript,
 	})
-	tx.AddTxOut(wire.NewTxOut(int64(amount),
-		[]byte{txscript.OP_TADD}))
+	tx.AddTxOut(wire.NewTxOut(int64(amount), []byte{txscript.OP_TADD}))
 	if len(changeScript) > 0 {
-		tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
+		tx.AddTxOut(newTxOut(int64(change), changeScriptVer, changeScript))
 	}
 	return tx
 }
@@ -593,7 +556,7 @@ func (g *Generator) CreateTreasuryTAdd(spend *SpendableOut, amount, fee dcrutil.
 // AddressAmountTuple wraps address+amount in a tuple for easy parameter
 // passing.
 type AddressAmountTuple struct {
-	Address dcrutil.Address
+	Address stdaddr.Address
 	Amount  dcrutil.Amount
 }
 
@@ -624,13 +587,7 @@ func (g *Generator) CreateTreasuryTSpend(privKey []byte, payouts []AddressAmount
 	if err != nil {
 		panic(err)
 	}
-	builder := txscript.NewScriptBuilder()
-	builder.AddOp(txscript.OP_RETURN)
-	builder.AddData(payload[:])
-	opretScript, err := builder.Script()
-	if err != nil {
-		panic(err)
-	}
+	opretScript := opReturnScript(payload[:])
 	msgTx := wire.NewMsgTx()
 	msgTx.Version = wire.TxVersionTreasury
 	msgTx.Expiry = expiry
@@ -638,18 +595,15 @@ func (g *Generator) CreateTreasuryTSpend(privKey []byte, payouts []AddressAmount
 
 	// OP_TGEN
 	for _, v := range payouts {
-		addr := g.p2shOpTrueAddr
+		addr := g.p2shOpTrueAddr.(stdaddr.Address)
 		if v.Address != nil {
 			addr = v.Address
 		}
-		script, err := txscript.PayToAddrScript(addr)
-		if err != nil {
-			panic(err)
-		}
+		scriptVer, script := addr.PaymentScript()
 		tgenScript := make([]byte, len(script)+1)
 		tgenScript[0] = txscript.OP_TGEN
 		copy(tgenScript[1:], script)
-		msgTx.AddTxOut(wire.NewTxOut(int64(v.Amount), tgenScript))
+		msgTx.AddTxOut(newTxOut(int64(v.Amount), scriptVer, tgenScript))
 	}
 
 	// Treasury spend transactions have no inputs since the funds are
@@ -799,10 +753,7 @@ func (g *Generator) CreateVoteTx(voteBlock *wire.MsgBlock, ticketTx *wire.MsgTx,
 	// The third and subsequent outputs pay the original commitment amounts
 	// along with the appropriate portion of the vote subsidy.  This impl
 	// uses the standard pay-to-script-hash to an OP_TRUE.
-	stakeGenScript, err := txscript.PayToSSGen(g.p2shOpTrueAddr)
-	if err != nil {
-		panic(err)
-	}
+	genScriptVer, genScript := g.p2shOpTrueAddr.PayVoteCommitmentScript()
 
 	// Generate and return the transaction with the proof-of-stake subsidy
 	// coinbase and spending from the provided ticket along with the
@@ -829,7 +780,7 @@ func (g *Generator) CreateVoteTx(voteBlock *wire.MsgBlock, ticketTx *wire.MsgTx,
 	})
 	tx.AddTxOut(wire.NewTxOut(0, blockScript))
 	tx.AddTxOut(wire.NewTxOut(0, voteScript))
-	tx.AddTxOut(wire.NewTxOut(int64(voteSubsidy+ticketPrice), stakeGenScript))
+	tx.AddTxOut(newTxOut(int64(voteSubsidy+ticketPrice), genScriptVer, genScript))
 	return tx
 }
 
@@ -851,10 +802,7 @@ func (g *Generator) createVoteTxFromTicket(voteBlock *wire.MsgBlock, ticket *sta
 func (g *Generator) CreateRevocationTx(ticketTx *wire.MsgTx, ticketBlockHeight, ticketBlockIndex uint32) *wire.MsgTx {
 	// The outputs pay the original commitment amounts.  This impl uses the
 	// standard pay-to-script-hash to an OP_TRUE.
-	revokeScript, err := txscript.PayToSSRtx(g.p2shOpTrueAddr)
-	if err != nil {
-		panic(err)
-	}
+	revokeScrVer, revokeScript := g.p2shOpTrueAddr.PayRevokeCommitmentScript()
 
 	// Generate and return the transaction spending from the provided ticket
 	// along with the previously described outputs.
@@ -870,7 +818,7 @@ func (g *Generator) CreateRevocationTx(ticketTx *wire.MsgTx, ticketBlockHeight,
 		BlockIndex:      ticketBlockIndex,
 		SignatureScript: opTrueRedeemScript,
 	})
-	tx.AddTxOut(wire.NewTxOut(ticketPrice, revokeScript))
+	tx.AddTxOut(newTxOut(ticketPrice, revokeScrVer, revokeScript))
 	return tx
 }
 
@@ -1773,8 +1721,7 @@ func (g *Generator) CreateSpendTx(spend *SpendableOut, fee dcrutil.Amount) *wire
 		BlockIndex:       spend.blockIndex,
 		SignatureScript:  opTrueRedeemScript,
 	})
-	spendTx.AddTxOut(wire.NewTxOut(int64(spend.amount-fee),
-		g.p2shOpTrueScript))
+	spendTx.AddTxOut(wire.NewTxOut(int64(spend.amount-fee), g.p2shOpTrueScript))
 	spendTx.AddTxOut(wire.NewTxOut(0, UniqueOpReturnScript()))
 	return spendTx
 }
diff --git a/blockchain/go.mod b/blockchain/go.mod
index fe0aca00..070d57e5 100644
--- a/blockchain/go.mod
+++ b/blockchain/go.mod
@@ -13,7 +13,7 @@ require (
 	github.com/decred/dcrd/dcrutil/v4 v4.0.0-20210129181600-6ae0142d3b28
 	github.com/decred/dcrd/gcs/v3 v3.0.0-20210129195202-a4265d63b619
 	github.com/decred/dcrd/lru v1.1.0
-	github.com/decred/dcrd/txscript/v4 v4.0.0-20210129190127-4ebd135a82f1
+	github.com/decred/dcrd/txscript/v4 v4.0.0-20210330065944-a2366e6e0b3b
 	github.com/decred/dcrd/wire v1.4.0
 	github.com/decred/slog v1.1.0
 )
diff --git a/blockchain/indexers/addrindex.go b/blockchain/indexers/addrindex.go
index bf84ea23..24951016 100644
--- a/blockchain/indexers/addrindex.go
+++ b/blockchain/indexers/addrindex.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2016 The btcsuite developers
-// Copyright (c) 2016-2020 The Decred developers
+// Copyright (c) 2016-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -15,9 +15,9 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/database/v2"
-	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -533,49 +533,35 @@ func dbRemoveAddrIndexEntries(bucket internalBucket, addrKey [addrKeySize]byte,
 
 // addrToKey converts known address types to an addrindex key.  An error is
 // returned for unsupported types.
-func addrToKey(addr dcrutil.Address) ([addrKeySize]byte, error) {
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		switch addr.DSA() {
-		case dcrec.STEcdsaSecp256k1:
-			var result [addrKeySize]byte
-			result[0] = addrKeyTypePubKeyHash
-			copy(result[1:], addr.Hash160()[:])
-			return result, nil
-		case dcrec.STEd25519:
-			var result [addrKeySize]byte
-			result[0] = addrKeyTypePubKeyHashEdwards
-			copy(result[1:], addr.Hash160()[:])
-			return result, nil
-		case dcrec.STSchnorrSecp256k1:
-			var result [addrKeySize]byte
-			result[0] = addrKeyTypePubKeyHashSchnorr
-			copy(result[1:], addr.Hash160()[:])
-			return result, nil
-		}
+func addrToKey(addr stdaddr.Address) ([addrKeySize]byte, error) {
+	// Convert public key addresses to public key hash variants.
+	if addrPKH, ok := addr.(stdaddr.AddressPubKeyHasher); ok {
+		addr = addrPKH.AddressPubKeyHash()
+	}
 
-	case *dcrutil.AddressScriptHash:
+	switch addr := addr.(type) {
+	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
 		var result [addrKeySize]byte
-		result[0] = addrKeyTypeScriptHash
+		result[0] = addrKeyTypePubKeyHash
 		copy(result[1:], addr.Hash160()[:])
 		return result, nil
 
-	case *dcrutil.AddressSecpPubKey:
+	case *stdaddr.AddressPubKeyHashEd25519V0:
 		var result [addrKeySize]byte
-		result[0] = addrKeyTypePubKeyHash
-		copy(result[1:], addr.AddressPubKeyHash().Hash160()[:])
+		result[0] = addrKeyTypePubKeyHashEdwards
+		copy(result[1:], addr.Hash160()[:])
 		return result, nil
 
-	case *dcrutil.AddressEdwardsPubKey:
+	case *stdaddr.AddressPubKeyHashSchnorrSecp256k1V0:
 		var result [addrKeySize]byte
-		result[0] = addrKeyTypePubKeyHashEdwards
-		copy(result[1:], addr.AddressPubKeyHash().Hash160()[:])
+		result[0] = addrKeyTypePubKeyHashSchnorr
+		copy(result[1:], addr.Hash160()[:])
 		return result, nil
 
-	case *dcrutil.AddressSecSchnorrPubKey:
+	case *stdaddr.AddressScriptHashV0:
 		var result [addrKeySize]byte
-		result[0] = addrKeyTypePubKeyHashSchnorr
-		copy(result[1:], addr.AddressPubKeyHash().Hash160()[:])
+		result[0] = addrKeyTypeScriptHash
+		copy(result[1:], addr.Hash160()[:])
 		return result, nil
 	}
 
@@ -897,7 +883,7 @@ func (idx *AddrIndex) DisconnectBlock(dbTx database.Tx, block, parent *dcrutil.B
 // that involve a given address.
 //
 // This function is safe for concurrent access.
-func (idx *AddrIndex) EntriesForAddress(dbTx database.Tx, addr dcrutil.Address, numToSkip, numRequested uint32, reverse bool) ([]TxIndexEntry, uint32, error) {
+func (idx *AddrIndex) EntriesForAddress(dbTx database.Tx, addr stdaddr.Address, numToSkip, numRequested uint32, reverse bool) ([]TxIndexEntry, uint32, error) {
 	addrKey, err := addrToKey(addr)
 	if err != nil {
 		return nil, 0, err
@@ -933,8 +919,8 @@ func (idx *AddrIndex) indexUnconfirmedAddresses(scriptVersion uint16, pkScript [
 	// The error is ignored here since the only reason it can fail is if the
 	// script fails to parse and it was already validated before being
 	// admitted to the mempool.
-	class, addresses, _, _ := txscript.ExtractPkScriptAddrs(scriptVersion,
-		pkScript, idx.chainParams, isTreasuryEnabled)
+	class, addrs, _, _ := txscript.ExtractPkScriptAddrs(scriptVersion, pkScript,
+		idx.chainParams, isTreasuryEnabled)
 
 	if isSStx && class == txscript.NullDataTy {
 		addr, err := stake.AddrFromSStxPkScrCommitment(pkScript, idx.chainParams)
@@ -943,10 +929,10 @@ func (idx *AddrIndex) indexUnconfirmedAddresses(scriptVersion uint16, pkScript [
 			return
 		}
 
-		addresses = append(addresses, addr)
+		addrs = append(addrs, addr)
 	}
 
-	for _, addr := range addresses {
+	for _, addr := range addrs {
 		// Ignore unsupported address types.
 		addrKey, err := addrToKey(addr)
 		if err != nil {
@@ -1042,7 +1028,7 @@ func (idx *AddrIndex) RemoveUnconfirmedTx(hash *chainhash.Hash) {
 // Unsupported address types are ignored and will result in no results.
 //
 // This function is safe for concurrent access.
-func (idx *AddrIndex) UnconfirmedTxnsForAddress(addr dcrutil.Address) []*dcrutil.Tx {
+func (idx *AddrIndex) UnconfirmedTxnsForAddress(addr stdaddr.Address) []*dcrutil.Tx {
 	// Ignore unsupported address types.
 	addrKey, err := addrToKey(addr)
 	if err != nil {
diff --git a/blockchain/indexers/existsaddrindex.go b/blockchain/indexers/existsaddrindex.go
index f4219011..ed2f982e 100644
--- a/blockchain/indexers/existsaddrindex.go
+++ b/blockchain/indexers/existsaddrindex.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2020 The Decred developers
+// Copyright (c) 2016-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -13,6 +13,7 @@ import (
 	"github.com/decred/dcrd/database/v2"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -144,7 +145,7 @@ func (idx *ExistsAddrIndex) existsAddress(bucket internalBucket, k [addrKeySize]
 
 // ExistsAddress is the concurrency safe, exported function that returns
 // whether or not an address has been seen before.
-func (idx *ExistsAddrIndex) ExistsAddress(addr dcrutil.Address) (bool, error) {
+func (idx *ExistsAddrIndex) ExistsAddress(addr stdaddr.Address) (bool, error) {
 	k, err := addrToKey(addr)
 	if err != nil {
 		return false, err
@@ -174,7 +175,7 @@ func (idx *ExistsAddrIndex) ExistsAddress(addr dcrutil.Address) (bool, error) {
 
 // ExistsAddresses is the concurrency safe, exported function that returns
 // whether or not each address in a slice of addresses has been seen before.
-func (idx *ExistsAddrIndex) ExistsAddresses(addrs []dcrutil.Address) ([]bool, error) {
+func (idx *ExistsAddrIndex) ExistsAddresses(addrs []stdaddr.Address) ([]bool, error) {
 	exists := make([]bool, len(addrs))
 	addrKeys := make([][addrKeySize]byte, len(addrs))
 	for i := range addrKeys {
@@ -244,8 +245,7 @@ func (idx *ExistsAddrIndex) ConnectBlock(dbTx database.Tx, block, parent *dcruti
 				rs := txscript.MultisigRedeemScriptFromScriptSig(
 					txIn.SignatureScript)
 				class, addrs, _, err := txscript.ExtractPkScriptAddrs(
-					scriptVersion, rs, idx.chainParams,
-					isTreasuryEnabled)
+					scriptVersion, rs, idx.chainParams, isTreasuryEnabled)
 				if err != nil {
 					// Non-standard outputs are skipped.
 					continue
@@ -267,9 +267,8 @@ func (idx *ExistsAddrIndex) ConnectBlock(dbTx database.Tx, block, parent *dcruti
 		}
 
 		for _, txOut := range tx.MsgTx().TxOut {
-			class, addrs, _, err := txscript.ExtractPkScriptAddrs(
-				txOut.Version, txOut.PkScript, idx.chainParams,
-				isTreasuryEnabled)
+			class, addrs, _, err := txscript.ExtractPkScriptAddrs(txOut.Version,
+				txOut.PkScript, idx.chainParams, isTreasuryEnabled)
 			if err != nil {
 				// Non-standard outputs are skipped.
 				continue
@@ -364,9 +363,8 @@ func (idx *ExistsAddrIndex) addUnconfirmedTx(tx *wire.MsgTx, isTreasuryEnabled b
 			const scriptVersion = 0
 			rs := txscript.MultisigRedeemScriptFromScriptSig(
 				txIn.SignatureScript)
-			class, addrs, _, err := txscript.ExtractPkScriptAddrs(
-				scriptVersion, rs, idx.chainParams,
-				isTreasuryEnabled)
+			class, addrs, _, err := txscript.ExtractPkScriptAddrs(scriptVersion,
+				rs, idx.chainParams, isTreasuryEnabled)
 			if err != nil {
 				// Non-standard outputs are skipped.
 				continue
diff --git a/blockchain/stake/go.mod b/blockchain/stake/go.mod
index ce2053a2..31cf94e6 100644
--- a/blockchain/stake/go.mod
+++ b/blockchain/stake/go.mod
@@ -6,10 +6,9 @@ require (
 	github.com/decred/dcrd/chaincfg/chainhash v1.0.2
 	github.com/decred/dcrd/chaincfg/v3 v3.0.0
 	github.com/decred/dcrd/database/v2 v2.0.3-0.20210129190127-4ebd135a82f1
-	github.com/decred/dcrd/dcrec v1.0.0
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210127014238-b33b46cf1a24
 	github.com/decred/dcrd/dcrutil/v4 v4.0.0-20210129181600-6ae0142d3b28
-	github.com/decred/dcrd/txscript/v4 v4.0.0-20210129190127-4ebd135a82f1
+	github.com/decred/dcrd/txscript/v4 v4.0.0-20210330065944-a2366e6e0b3b
 	github.com/decred/dcrd/wire v1.4.0
 	github.com/decred/slog v1.1.0
 )
diff --git a/blockchain/stake/staketx.go b/blockchain/stake/staketx.go
index 1184bb19..4b0510ac 100644
--- a/blockchain/stake/staketx.go
+++ b/blockchain/stake/staketx.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 //
@@ -15,9 +15,9 @@ import (
 	"math/big"
 
 	"github.com/decred/dcrd/chaincfg/chainhash"
-	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -395,7 +395,7 @@ func TxSStxStakeOutputInfo(tx *wire.MsgTx) ([]bool, [][]byte, []int64, []int64,
 
 // AddrFromSStxPkScrCommitment extracts a P2SH or P2PKH address from a ticket
 // commitment pkScript.
-func AddrFromSStxPkScrCommitment(pkScript []byte, params dcrutil.AddressParams) (dcrutil.Address, error) {
+func AddrFromSStxPkScrCommitment(pkScript []byte, params stdaddr.AddressParams) (stdaddr.Address, error) {
 	if len(pkScript) < SStxPKHMinOutSize {
 		str := "short read of sstx commit pkscript"
 		return nil, stakeRuleError(ErrSStxBadCommitAmount, str)
@@ -417,10 +417,9 @@ func AddrFromSStxPkScrCommitment(pkScript []byte, params dcrutil.AddressParams)
 
 	// Return the correct address type.
 	if isP2SH {
-		return dcrutil.NewAddressScriptHashFromHash(hashBytes, params)
+		return stdaddr.NewAddressScriptHashV0FromHash(hashBytes, params)
 	}
-	return dcrutil.NewAddressPubKeyHash(hashBytes, params,
-		dcrec.STEcdsaSecp256k1)
+	return stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(hashBytes, params)
 }
 
 // AmountFromSStxPkScrCommitment extracts a commitment amount from a
diff --git a/blockchain/stakeext.go b/blockchain/stakeext.go
index 19b7166a..8c6948a5 100644
--- a/blockchain/stakeext.go
+++ b/blockchain/stakeext.go
@@ -10,6 +10,7 @@ import (
 	"github.com/decred/dcrd/database/v2"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -101,13 +102,14 @@ func (b *BlockChain) MissedTickets() ([]chainhash.Hash, error) {
 // corresponding to the given address.
 //
 // This function is safe for concurrent access.
-func (b *BlockChain) TicketsWithAddress(address dcrutil.Address, isTreasuryEnabled bool) ([]chainhash.Hash, error) {
+func (b *BlockChain) TicketsWithAddress(address stdaddr.Address, isTreasuryEnabled bool) ([]chainhash.Hash, error) {
 	b.chainLock.RLock()
 	sn := b.bestChain.Tip().stakeNode
 	b.chainLock.RUnlock()
 
 	tickets := sn.LiveTickets()
 
+	encodedAddr := address.Address()
 	var ticketsWithAddr []chainhash.Hash
 	err := b.db.View(func(dbTx database.Tx) error {
 		for _, hash := range tickets {
@@ -122,7 +124,7 @@ func (b *BlockChain) TicketsWithAddress(address dcrutil.Address, isTreasuryEnabl
 			if err != nil {
 				return err
 			}
-			if addrs[0].Address() == address.Address() {
+			if addrs[0].Address() == encodedAddr {
 				ticketsWithAddr = append(ticketsWithAddr, hash)
 			}
 		}
diff --git a/config.go b/config.go
index 477684c3..056cfffd 100644
--- a/config.go
+++ b/config.go
@@ -31,6 +31,7 @@ import (
 	"github.com/decred/dcrd/internal/version"
 	"github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/sampleconfig"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/go-socks/socks"
 	"github.com/decred/slog"
 	flags "github.com/jessevdk/go-flags"
@@ -232,7 +233,7 @@ type config struct {
 	lookup        func(string) ([]net.IP, error)
 	oniondial     func(context.Context, string, string) (net.Conn, error)
 	dial          func(context.Context, string, string) (net.Conn, error)
-	miningAddrs   []dcrutil.Address
+	miningAddrs   []stdaddr.Address
 	minRelayTxFee dcrutil.Amount
 	whitelists    []*net.IPNet
 	ipv4NetInfo   types.NetworksResult
@@ -1084,9 +1085,9 @@ func loadConfig(appName string) (*config, []string, error) {
 	}
 
 	// Check mining addresses are valid and saved parsed versions.
-	cfg.miningAddrs = make([]dcrutil.Address, 0, len(cfg.MiningAddrs))
+	cfg.miningAddrs = make([]stdaddr.Address, 0, len(cfg.MiningAddrs))
 	for _, strAddr := range cfg.MiningAddrs {
-		addr, err := dcrutil.DecodeAddress(strAddr, cfg.params.Params)
+		addr, err := stdaddr.DecodeAddress(strAddr, cfg.params.Params)
 		if err != nil {
 			str := "%s: mining address '%s' failed to decode: %w"
 			err := fmt.Errorf(str, funcName, strAddr, err)
diff --git a/gcs/go.mod b/gcs/go.mod
index 2265c486..653d40c9 100644
--- a/gcs/go.mod
+++ b/gcs/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/decred/dcrd/blockchain/stake/v4 v4.0.0-20210129192908-660d0518b4cf
 	github.com/decred/dcrd/chaincfg/chainhash v1.0.2
 	github.com/decred/dcrd/crypto/blake256 v1.0.0
-	github.com/decred/dcrd/txscript/v4 v4.0.0-20210129190127-4ebd135a82f1
+	github.com/decred/dcrd/txscript/v4 v4.0.0-20210330065944-a2366e6e0b3b
 	github.com/decred/dcrd/wire v1.4.0
 )
 
diff --git a/go.mod b/go.mod
index ebd8699b..6c8e1ce3 100644
--- a/go.mod
+++ b/go.mod
@@ -27,7 +27,7 @@ require (
 	github.com/decred/dcrd/peer/v3 v3.0.0
 	github.com/decred/dcrd/rpc/jsonrpc/types/v3 v3.0.0-20210129200153-14fd1a785bf2
 	github.com/decred/dcrd/rpcclient/v7 v7.0.0-20210129214723-fc227a05904d
-	github.com/decred/dcrd/txscript/v4 v4.0.0-20210129190127-4ebd135a82f1
+	github.com/decred/dcrd/txscript/v4 v4.0.0-20210330065944-a2366e6e0b3b
 	github.com/decred/dcrd/wire v1.4.0
 	github.com/decred/go-socks v1.1.0
 	github.com/decred/slog v1.2.0
diff --git a/hdkeychain/go.mod b/hdkeychain/go.mod
index 7e88bf33..741771f1 100644
--- a/hdkeychain/go.mod
+++ b/hdkeychain/go.mod
@@ -7,12 +7,11 @@ require (
 	github.com/decred/dcrd/chaincfg/v3 v3.0.0
 	github.com/decred/dcrd/crypto/blake256 v1.0.0
 	github.com/decred/dcrd/crypto/ripemd160 v1.0.1
-	github.com/decred/dcrd/dcrec v1.0.0
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210127014238-b33b46cf1a24
-	github.com/decred/dcrd/dcrutil/v4 v4.0.0-20210129181600-6ae0142d3b28
+	github.com/decred/dcrd/txscript/v4 v4.0.0-20210330065944-a2366e6e0b3b
 )
 
 replace (
 	github.com/decred/dcrd/dcrec/secp256k1/v4 => ../dcrec/secp256k1
-	github.com/decred/dcrd/dcrutil/v4 => ../dcrutil
+	github.com/decred/dcrd/txscript/v4 => ../txscript
 )
diff --git a/hdkeychain/go.sum b/hdkeychain/go.sum
index f8fd4e9b..fb2083ca 100644
--- a/hdkeychain/go.sum
+++ b/hdkeychain/go.sum
@@ -2,6 +2,7 @@ github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7I
 github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
 github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI=
 github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E=
 github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU=
@@ -16,5 +17,7 @@ github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J
 github.com/decred/dcrd/dcrec v1.0.0/go.mod h1:HIaqbEJQ+PDzQcORxnqen5/V1FR3B4VpIfmePklt8Q8=
 github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1 h1:V6eqU1crZzuoFT4KG2LhaU5xDSdkHuvLQsj25wd7Wb4=
 github.com/decred/dcrd/dcrec/edwards/v2 v2.0.1/go.mod h1:d0H8xGMWbiIQP7gN3v2rByWUcuZPm9YsgmnfoxgbINc=
+github.com/decred/dcrd/dcrutil/v4 v4.0.0-20210129181600-6ae0142d3b28/go.mod h1:xe59jKcMx5G/dbRmsZ8+FzY+WQDE/7YBP3k3uzJTtmI=
 github.com/decred/dcrd/wire v1.4.0 h1:KmSo6eTQIvhXS0fLBQ/l7hG7QLcSJQKSwSyzSqJYDk0=
 github.com/decred/dcrd/wire v1.4.0/go.mod h1:WxC/0K+cCAnBh+SKsRjIX9YPgvrjhmE+6pZlel1G7Ro=
+github.com/decred/slog v1.1.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0=
diff --git a/internal/mining/bgblktmplgenerator.go b/internal/mining/bgblktmplgenerator.go
index 3b2c239a..d978f9c4 100644
--- a/internal/mining/bgblktmplgenerator.go
+++ b/internal/mining/bgblktmplgenerator.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2020 The Decred developers
+// Copyright (c) 2019-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -17,6 +17,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/lru"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -323,7 +324,7 @@ type BgBlkTmplConfig struct {
 
 	// MiningAddrs specifies the addresses to choose from when paying mining
 	// rewards in generated templates.
-	MiningAddrs []dcrutil.Address
+	MiningAddrs []stdaddr.Address
 
 	// AllowUnsyncedMining indicates block templates should be created even when
 	// the chain is not fully synced.
diff --git a/internal/mining/mining.go b/internal/mining/mining.go
index 3b403f37..d7b26f38 100644
--- a/internal/mining/mining.go
+++ b/internal/mining/mining.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -21,6 +21,7 @@ import (
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/gcs/v3/blockcf2"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -502,7 +503,7 @@ func calcBlockCommitmentRootV1(block *wire.MsgBlock, prevScripts blockcf2.PrevSc
 //
 // See the comment for NewBlockTemplate for more information about why the nil
 // address handling is useful.
-func createCoinbaseTx(subsidyCache *standalone.SubsidyCache, coinbaseScript []byte, opReturnPkScript []byte, nextBlockHeight int64, addr dcrutil.Address, voters uint16, params *chaincfg.Params, isTreasuryEnabled bool) (*dcrutil.Tx, error) {
+func createCoinbaseTx(subsidyCache *standalone.SubsidyCache, coinbaseScript []byte, opReturnPkScript []byte, nextBlockHeight int64, addr stdaddr.Address, voters uint16, params *chaincfg.Params, isTreasuryEnabled bool) (*dcrutil.Tx, error) {
 	// Coinbase transactions have no inputs, so previous outpoint is zero hash
 	// and max index.
 	coinbaseInput := &wire.TxIn{
@@ -532,18 +533,6 @@ func createCoinbaseTx(subsidyCache *standalone.SubsidyCache, coinbaseScript []by
 		return dcrutil.NewTx(tx), nil
 	}
 
-	// Create the script to pay to the provided payment address if one was
-	// specified.  Otherwise create a script that allows the coinbase to be
-	// redeemable by anyone.
-	workSubsidyScript := opTrueScript
-	if addr != nil {
-		var err error
-		workSubsidyScript, err = txscript.PayToAddrScript(addr)
-		if err != nil {
-			return nil, err
-		}
-	}
-
 	// Prior to the decentralized treasury agenda, the transaction version must
 	// be 1 and there is an additional output that either pays to organization
 	// associated with the treasury or a provably pruneable zero-value output
@@ -578,6 +567,15 @@ func createCoinbaseTx(subsidyCache *standalone.SubsidyCache, coinbaseScript []by
 		txVersion = wire.TxVersionTreasury
 	}
 
+	// Create the script to pay to the provided payment address if one was
+	// specified.  Otherwise create a script that allows the coinbase to be
+	// redeemable by anyone.
+	workSubsidyScriptVer := uint16(0)
+	workSubsidyScript := opTrueScript
+	if addr != nil {
+		workSubsidyScriptVer, workSubsidyScript = addr.PaymentScript()
+	}
+
 	// Create a coinbase with expected inputs and outputs.
 	//
 	// Inputs:
@@ -602,6 +600,7 @@ func createCoinbaseTx(subsidyCache *standalone.SubsidyCache, coinbaseScript []by
 	})
 	tx.AddTxOut(&wire.TxOut{
 		Value:    workSubsidy,
+		Version:  workSubsidyScriptVer,
 		PkScript: workSubsidyScript,
 	})
 	return dcrutil.NewTx(tx), nil
@@ -765,7 +764,7 @@ func (g *BlkTmplGenerator) maybeInsertStakeTx(stx *dcrutil.Tx, treeValid bool, i
 // work off of is present, it will return a copy of that template to pass to the
 // miner.
 // Safe for concurrent access.
-func (g *BlkTmplGenerator) handleTooFewVoters(nextHeight int64, miningAddress dcrutil.Address, isTreasuryEnabled bool) (*BlockTemplate, error) {
+func (g *BlkTmplGenerator) handleTooFewVoters(nextHeight int64, miningAddress stdaddr.Address, isTreasuryEnabled bool) (*BlockTemplate, error) {
 	stakeValidationHeight := g.cfg.ChainParams.StakeValidationHeight
 
 	// Handle not enough voters being present if we're set to mine aggressively
@@ -1010,7 +1009,7 @@ func calcFeePerKb(txDesc *TxDesc, ancestorStats *TxAncestorStats) float64 {
 //
 //  This function returns nil, nil if there are not enough voters on any of
 //  the current top blocks to create a new block template.
-func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress dcrutil.Address) (*BlockTemplate, error) {
+func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress stdaddr.Address) (*BlockTemplate, error) {
 	// All transaction scripts are verified using the more strict standard
 	// flags.
 	scriptFlags, err := g.cfg.Policy.StandardVerifyFlags()
diff --git a/internal/rpcserver/interface.go b/internal/rpcserver/interface.go
index 5b8cc080..e0c4b250 100644
--- a/internal/rpcserver/interface.go
+++ b/internal/rpcserver/interface.go
@@ -22,6 +22,7 @@ import (
 	"github.com/decred/dcrd/internal/mining"
 	"github.com/decred/dcrd/peer/v3"
 	"github.com/decred/dcrd/rpc/jsonrpc/types/v3"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -372,7 +373,7 @@ type Chain interface {
 
 	// TicketsWithAddress returns a slice of ticket hashes that are currently
 	// live corresponding to the given address.
-	TicketsWithAddress(address dcrutil.Address, isTreasuryEnabled bool) ([]chainhash.Hash, error)
+	TicketsWithAddress(address stdaddr.Address, isTreasuryEnabled bool) ([]chainhash.Hash, error)
 
 	// TipGeneration returns the entire generation of blocks stemming from the
 	// parent of the current tip.
@@ -552,11 +553,11 @@ type FiltererV2 interface {
 // ExistsAddresser before calling methods associated with it.
 type ExistsAddresser interface {
 	// ExistsAddress returns whether or not an address has been seen before.
-	ExistsAddress(addr dcrutil.Address) (bool, error)
+	ExistsAddress(addr stdaddr.Address) (bool, error)
 
 	// ExistsAddresses returns whether or not each address in a slice of
 	// addresses has been seen before.
-	ExistsAddresses(addrs []dcrutil.Address) ([]bool, error)
+	ExistsAddresses(addrs []stdaddr.Address) ([]bool, error)
 }
 
 // TxMempooler represents a source of mempool transaction data for the RPC
@@ -607,13 +608,13 @@ type AddrIndexer interface {
 	// NOTE: These results only include transactions confirmed in blocks.  See the
 	// UnconfirmedTxnsForAddress method for obtaining unconfirmed transactions
 	// that involve a given address.
-	EntriesForAddress(dbTx database.Tx, addr dcrutil.Address, numToSkip,
+	EntriesForAddress(dbTx database.Tx, addr stdaddr.Address, numToSkip,
 		numRequested uint32, reverse bool) ([]indexers.TxIndexEntry, uint32, error)
 
 	// UnconfirmedTxnsForAddress returns all transactions currently in the
 	// unconfirmed (memory-only) address index that involve the passed address.
 	// Unsupported address types are ignored and will result in no results.
-	UnconfirmedTxnsForAddress(addr dcrutil.Address) []*dcrutil.Tx
+	UnconfirmedTxnsForAddress(addr stdaddr.Address) []*dcrutil.Tx
 }
 
 // TxIndexer provides an interface for retrieving details for a given
diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go
index 8843b38a..75d005ab 100644
--- a/internal/rpcserver/rpcserver.go
+++ b/internal/rpcserver/rpcserver.go
@@ -50,6 +50,7 @@ import (
 	"github.com/decred/dcrd/internal/version"
 	"github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 	"github.com/jrick/bitset"
 )
@@ -634,6 +635,15 @@ func (s *Server) messageToHex(msg wire.Message) (string, error) {
 	return hex.EncodeToString(buf.Bytes()), nil
 }
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 // handleCreateRawTransaction handles createrawtransaction commands.
 func handleCreateRawTransaction(_ context.Context, s *Server, cmd interface{}) (interface{}, error) {
 	c := cmd.(*types.CreateRawTransactionCmd)
@@ -699,28 +709,19 @@ func handleCreateRawTransaction(_ context.Context, s *Server, cmd interface{}) (
 
 		// Decode the provided address.  This also ensures the network encoded
 		// with the address matches the network the server is currently on.
-		addr, err := dcrutil.DecodeAddress(encodedAddr, s.cfg.ChainParams)
+		addr, err := stdaddr.DecodeAddress(encodedAddr, s.cfg.ChainParams)
 		if err != nil {
 			return nil, rpcAddressKeyError("Could not decode address: %v", err)
 		}
 
 		// Ensure the address is one of the supported types.
-		switch addr.(type) {
-		case *dcrutil.AddressPubKeyHash:
-		case *dcrutil.AddressScriptHash:
-		default:
+		if _, ok := addr.(stdaddr.StakeAddress); !ok {
 			return nil, rpcAddressKeyError("Invalid type: %T", addr)
 		}
 
 		// Create a new script which pays to the provided address.
-		pkScript, err := txscript.PayToAddrScript(addr)
-		if err != nil {
-			return nil, rpcInternalError(err.Error(),
-				"Pay to address script")
-		}
-
-		txOut := wire.NewTxOut(int64(atoms), pkScript)
-		mtx.AddTxOut(txOut)
+		pkScriptVer, pkScript := addr.PaymentScript()
+		mtx.AddTxOut(newTxOut(int64(atoms), pkScriptVer, pkScript))
 	}
 
 	// Set the Locktime, if given.
@@ -793,30 +794,20 @@ func handleCreateRawSStx(_ context.Context, s *Server, cmd interface{}) (interfa
 
 		// Decode the provided address.  This also ensures the network encoded
 		// with the address matches the network the server is currently on.
-		addr, err := dcrutil.DecodeAddress(encodedAddr, s.cfg.ChainParams)
+		addr, err := stdaddr.DecodeAddress(encodedAddr, s.cfg.ChainParams)
 		if err != nil {
 			return nil, rpcAddressKeyError("Could not decode address: %v", err)
 		}
 
 		// Ensure the address is one of the supported types.
-		switch addr.(type) {
-		case *dcrutil.AddressPubKeyHash:
-		case *dcrutil.AddressScriptHash:
-		default:
-			return nil, rpcAddressKeyError("Invalid address type: "+
-				"%T", addr)
+		stakeAddr, ok := addr.(stdaddr.StakeAddress)
+		if !ok {
+			return nil, rpcAddressKeyError("Invalid address type: %T", addr)
 		}
 
-		// Create a new script which pays to the provided address with an
-		// SStx tagged output.
-		pkScript, err := txscript.PayToSStx(addr)
-		if err != nil {
-			return nil, rpcInternalError(err.Error(),
-				"Could not create TX script")
-		}
-
-		txOut := wire.NewTxOut(amount, pkScript)
-		mtx.AddTxOut(txOut)
+		// Create the necessary voting rights script.
+		pkScriptVer, pkScript := stakeAddr.VotingRightsScript()
+		mtx.AddTxOut(newTxOut(amount, pkScriptVer, pkScript))
 
 		amtTicket += amount
 	}
@@ -854,32 +845,25 @@ func handleCreateRawSStx(_ context.Context, s *Server, cmd interface{}) (interfa
 		// Append future commitment output.  This also ensures the network
 		// encoded with the address matches the network the server is currently
 		// on.
-		addr, err := dcrutil.DecodeAddress(cout.Addr, s.cfg.ChainParams)
+		addr, err := stdaddr.DecodeAddress(cout.Addr, s.cfg.ChainParams)
 		if err != nil {
 			return nil, rpcAddressKeyError("Could not decode address: %v", err)
 		}
 
 		// Ensure the address is one of the supported types.
-		switch addr.(type) {
-		case *dcrutil.AddressPubKeyHash:
-			break
-		case *dcrutil.AddressScriptHash:
-			break
-		default:
+		stakeAddr, ok := addr.(stdaddr.StakeAddress)
+		if !ok {
 			return nil, rpcAddressKeyError("Invalid type: %T", addr)
 		}
 
-		// Create an OP_RETURN push containing the pubkeyhash to send
-		// rewards to.  TODO Replace 0x0000 fee limits with an argument
-		// passed to the RPC call.
-		pkScript, err := txscript.GenerateSStxAddrPush(addr,
-			dcrutil.Amount(amountsCommitted[i]), 0x0000)
-		if err != nil {
-			return nil, rpcInternalError(err.Error(),
-				"Could not create SStx script")
-		}
-		txout := wire.NewTxOut(int64(0), pkScript)
-		mtx.AddTxOut(txout)
+		// Create the reward commitment script.
+		//
+		// TODO: Allow fee limits to be specified with an argument.
+		const voteFeeLimit = 0
+		const revokeFeeLimit = 0
+		cmtScriptVer, cmtScript := stakeAddr.RewardCommitmentScript(
+			amountsCommitted[i], voteFeeLimit, revokeFeeLimit)
+		mtx.AddTxOut(newTxOut(0, cmtScriptVer, cmtScript))
 
 		// 2. Append change output.
 
@@ -891,34 +875,21 @@ func handleCreateRawSStx(_ context.Context, s *Server, cmd interface{}) (interfa
 
 		// Decode the provided address.  This also ensures the network encoded
 		// with the address matches the network the server is currently on.
-		addr, err = dcrutil.DecodeAddress(cout.ChangeAddr, s.cfg.ChainParams)
+		addr, err = stdaddr.DecodeAddress(cout.ChangeAddr, s.cfg.ChainParams)
 		if err != nil {
 			return nil, rpcAddressKeyError("Wrong network: %v",
 				addr)
 		}
 
-		// Ensure the address is one of the supported types and that
-		// the network encoded with the address matches the network the
-		// server is currently on.
-		switch addr.(type) {
-		case *dcrutil.AddressPubKeyHash:
-			break
-		case *dcrutil.AddressScriptHash:
-			break
-		default:
+		// Ensure the address is one of the supported types.
+		stakeAddr, ok = addr.(stdaddr.StakeAddress)
+		if !ok {
 			return nil, rpcAddressKeyError("Invalid type: %T", addr)
 		}
 
-		// Create a new script which pays to the provided address with
-		// an SStx change tagged output.
-		pkScript, err = txscript.PayToSStxChange(addr)
-		if err != nil {
-			return nil, rpcInternalError(err.Error(),
-				"Could not create SStx change script")
-		}
-
-		txOut := wire.NewTxOut(cout.ChangeAmt, pkScript)
-		mtx.AddTxOut(txOut)
+		// Create a new script which pays change to the provided address.
+		changeScriptVer, changeScript := stakeAddr.StakeChangeScript()
+		mtx.AddTxOut(newTxOut(cout.ChangeAmt, changeScriptVer, changeScript))
 	}
 
 	// Make sure we generated a valid SStx.
@@ -1028,17 +999,21 @@ func handleCreateRawSSRtx(_ context.Context, s *Server, cmd interface{}) (interf
 		var ssrtxOutScript []byte
 		switch ssrtxPayTypes[i] {
 		case false: // P2PKH
-			ssrtxOutScript, err = txscript.PayToSSRtxPKHDirect(ssrtxPkh)
+			addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(ssrtxPkh,
+				s.cfg.ChainParams)
 			if err != nil {
 				return nil, rpcInvalidError("Could not "+
 					"generate P2PKH script: %v", err)
 			}
+			_, ssrtxOutScript = addr.PayRevokeCommitmentScript()
 		case true: // P2SH
-			ssrtxOutScript, err = txscript.PayToSSRtxSHDirect(ssrtxPkh)
+			addr, err := stdaddr.NewAddressScriptHashV0FromHash(ssrtxPkh,
+				s.cfg.ChainParams)
 			if err != nil {
 				return nil, rpcInvalidError("Could not "+
 					"generate P2SH script: %v", err)
 			}
+			_, ssrtxOutScript = addr.PayRevokeCommitmentScript()
 		}
 
 		// Add the txout to our SSGen tx.
@@ -1179,7 +1154,7 @@ func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap
 		// the case of stake submission transactions, the odd outputs
 		// contain a commitment address, so detect that case
 		// accordingly.
-		var addrs []dcrutil.Address
+		var addrs []stdaddr.Address
 		var scriptClass string
 		var reqSigs int
 		var commitAmt *dcrutil.Amount
@@ -1192,7 +1167,7 @@ func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap
 					"commitment addr output for tx hash "+
 					"%v, output idx %v", mtx.TxHash(), i)
 			} else {
-				addrs = []dcrutil.Address{addr}
+				addrs = []stdaddr.Address{addr}
 			}
 			amt, err := stake.AmountFromSStxPkScrCommitment(v.PkScript)
 			if err != nil {
@@ -1207,9 +1182,8 @@ func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap
 			// couldn't parse and there is no additional information
 			// about it anyways.
 			var sc txscript.ScriptClass
-			sc, addrs, reqSigs, _ = txscript.ExtractPkScriptAddrs(
-				v.Version, v.PkScript, chainParams,
-				isTreasuryEnabled)
+			sc, addrs, reqSigs, _ = txscript.ExtractPkScriptAddrs(v.Version,
+				v.PkScript, chainParams, isTreasuryEnabled)
 			scriptClass = sc.String()
 		}
 
@@ -1372,11 +1346,15 @@ func handleDecodeScript(_ context.Context, s *Server, cmd interface{}) (interfac
 		scriptVersion, script, s.cfg.ChainParams, isTreasuryEnabled)
 	addresses := make([]string, len(addrs))
 	for i, addr := range addrs {
+		if pkHasher, ok := addr.(stdaddr.AddressPubKeyHasher); ok {
+			addr = pkHasher.AddressPubKeyHash()
+		}
 		addresses[i] = addr.Address()
 	}
 
 	// Convert the script itself to a pay-to-script-hash address.
-	p2sh, err := dcrutil.NewAddressScriptHash(script, s.cfg.ChainParams)
+	p2sh, err := stdaddr.NewAddressScriptHash(scriptVersion, script,
+		s.cfg.ChainParams)
 	if err != nil {
 		return nil, rpcInternalError(err.Error(),
 			"Failed to convert script to pay-to-script-hash")
@@ -1511,7 +1489,7 @@ func handleExistsAddress(_ context.Context, s *Server, cmd interface{}) (interfa
 
 	// Decode the provided address.  This also ensures the network encoded with
 	// the address matches the network the server is currently on.
-	addr, err := dcrutil.DecodeAddress(c.Address, s.cfg.ChainParams)
+	addr, err := stdaddr.DecodeAddress(c.Address, s.cfg.ChainParams)
 	if err != nil {
 		return nil, rpcAddressKeyError("Could not decode address: %v",
 			err)
@@ -1536,11 +1514,11 @@ func handleExistsAddresses(_ context.Context, s *Server, cmd interface{}) (inter
 	}
 
 	c := cmd.(*types.ExistsAddressesCmd)
-	addresses := make([]dcrutil.Address, len(c.Addresses))
+	addresses := make([]stdaddr.Address, len(c.Addresses))
 	for i := range c.Addresses {
 		// Decode the provided address.  This also ensures the network encoded
 		// with the address matches the network the server is currently on.
-		addr, err := dcrutil.DecodeAddress(c.Addresses[i], s.cfg.ChainParams)
+		addr, err := stdaddr.DecodeAddress(c.Addresses[i], s.cfg.ChainParams)
 		if err != nil {
 			return nil, rpcAddressKeyError("Could not decode address: %v", err)
 		}
@@ -4306,7 +4284,7 @@ func createVinListPrevOut(s *Server, mtx *wire.MsgTx,
 // fetchMempoolTxnsForAddress queries the address index for all unconfirmed
 // transactions that involve the provided address.  The results will be limited
 // by the number to skip and the number requested.
-func fetchMempoolTxnsForAddress(s *Server, addr dcrutil.Address, numToSkip, numRequested uint32) ([]*dcrutil.Tx, uint32) {
+func fetchMempoolTxnsForAddress(s *Server, addr stdaddr.Address, numToSkip, numRequested uint32) ([]*dcrutil.Tx, uint32) {
 	// There are no entries to return when there are less available than
 	// the number being skipped.
 	mpTxns := s.cfg.AddrIndexer.UnconfirmedTxnsForAddress(addr)
@@ -4351,7 +4329,7 @@ func handleSearchRawTransactions(_ context.Context, s *Server, cmd interface{})
 
 	// Attempt to decode the supplied address.  This also ensures the network
 	// encoded with the address matches the network the server is currently on.
-	addr, err := dcrutil.DecodeAddress(c.Address, s.cfg.ChainParams)
+	addr, err := stdaddr.DecodeAddress(c.Address, s.cfg.ChainParams)
 	if err != nil {
 		return nil, rpcAddressKeyError("Could not decode address: %v",
 			err)
@@ -5075,7 +5053,7 @@ func handleTicketsForAddress(_ context.Context, s *Server, cmd interface{}) (int
 
 	// Decode the provided address.  This also ensures the network encoded
 	// with the address matches the network the server is currently on.
-	addr, err := dcrutil.DecodeAddress(c.Address, s.cfg.ChainParams)
+	addr, err := stdaddr.DecodeAddress(c.Address, s.cfg.ChainParams)
 	if err != nil {
 		return nil, rpcInvalidError("Invalid address: %v", err)
 	}
@@ -5253,7 +5231,7 @@ func handleTxFeeInfo(_ context.Context, s *Server, cmd interface{}) (interface{}
 func handleValidateAddress(_ context.Context, s *Server, cmd interface{}) (interface{}, error) {
 	c := cmd.(*types.ValidateAddressCmd)
 	result := types.ValidateAddressChainResult{}
-	addr, err := dcrutil.DecodeAddress(c.Address, s.cfg.ChainParams)
+	addr, err := stdaddr.DecodeAddress(c.Address, s.cfg.ChainParams)
 	if err != nil {
 		// Return the default value (false) for IsValid.
 		return result, nil
@@ -5320,14 +5298,15 @@ func handleVerifyMessage(_ context.Context, s *Server, cmd interface{}) (interfa
 
 	// Decode the provided address.  This also ensures the network encoded with
 	// the address matches the network the server is currently on.
-	addr, err := dcrutil.DecodeAddress(c.Address, s.cfg.ChainParams)
+	params := s.cfg.ChainParams
+	addr, err := stdaddr.DecodeAddress(c.Address, params)
 	if err != nil {
 		return nil, rpcAddressKeyError("Could not decode address: %v",
 			err)
 	}
 
-	// Only P2PKH addresses are valid for signing.
-	if _, ok := addr.(*dcrutil.AddressPubKeyHash); !ok {
+	// Only version 0 P2PKH addresses are valid for signing.
+	if _, ok := addr.(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0); !ok {
 		return nil, &dcrjson.RPCError{
 			Code:    dcrjson.ErrRPCType,
 			Message: "Address is not a pay-to-pubkey-hash address",
@@ -5349,27 +5328,22 @@ func handleVerifyMessage(_ context.Context, s *Server, cmd interface{}) (interfa
 	wire.WriteVarString(&buf, 0, "Decred Signed Message:\n")
 	wire.WriteVarString(&buf, 0, c.Message)
 	expectedMessageHash := chainhash.HashB(buf.Bytes())
-	pk, wasCompressed, err := ecdsa.RecoverCompact(sig,
-		expectedMessageHash)
+	pk, wasCompressed, err := ecdsa.RecoverCompact(sig, expectedMessageHash)
 	if err != nil {
-		// Mirror Bitcoin Core behavior, which treats error in
-		// RecoverCompact as invalid signature.
+		// Treat errors in RecoverCompact as an invalid signature.
 		return false, nil
 	}
 
 	// Reconstruct the pubkey hash.
-	dcrPK := pk
-	var serializedPK []byte
+	var pkHash []byte
 	if wasCompressed {
-		serializedPK = dcrPK.SerializeCompressed()
+		pkHash = stdaddr.Hash160(pk.SerializeCompressed())
 	} else {
-		serializedPK = dcrPK.SerializeUncompressed()
+		pkHash = stdaddr.Hash160(pk.SerializeUncompressed())
 	}
-	address, err := dcrutil.NewAddressSecpPubKey(serializedPK,
-		s.cfg.ChainParams)
+	address, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, params)
 	if err != nil {
-		// Again mirror Bitcoin Core behavior, which treats error in
-		// public key reconstruction as invalid signature.
+		// Treat error in reconstruction as an invalid signature.
 		return false, nil
 	}
 
@@ -6219,7 +6193,7 @@ type Config struct {
 	TestNet bool
 
 	// MiningAddrs is a list of payment addresses to use for the generated blocks.
-	MiningAddrs []dcrutil.Address
+	MiningAddrs []stdaddr.Address
 
 	// AllowUnsyncedMining indicates whether block templates should be created even
 	// when the chain is not fully synced.
diff --git a/internal/rpcserver/rpcwebsocket.go b/internal/rpcserver/rpcwebsocket.go
index 5c70c3c6..4bce4fdf 100644
--- a/internal/rpcserver/rpcwebsocket.go
+++ b/internal/rpcserver/rpcwebsocket.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -33,6 +33,7 @@ import (
 	"github.com/decred/dcrd/internal/mining"
 	"github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -302,7 +303,7 @@ type wsClientFilter struct {
 	mu sync.Mutex
 
 	// Parameter for address decoding.
-	params dcrutil.AddressParams
+	params stdaddr.AddressParams
 
 	// Implemented fast paths for address lookup.
 	pubKeyHashes      map[[ripemd160.Size]byte]struct{}
@@ -318,7 +319,7 @@ type wsClientFilter struct {
 	unspent map[wire.OutPoint]struct{}
 }
 
-func makeWSClientFilter(addresses []string, unspentOutPoints []*wire.OutPoint, params dcrutil.AddressParams) *wsClientFilter {
+func makeWSClientFilter(addresses []string, unspentOutPoints []*wire.OutPoint, params stdaddr.AddressParams) *wsClientFilter {
 	filter := &wsClientFilter{
 		params:            params,
 		pubKeyHashes:      map[[ripemd160.Size]byte]struct{}{},
@@ -338,16 +339,16 @@ func makeWSClientFilter(addresses []string, unspentOutPoints []*wire.OutPoint, p
 	return filter
 }
 
-func (f *wsClientFilter) addAddress(a dcrutil.Address) {
+func (f *wsClientFilter) addAddress(a stdaddr.Address) {
 	switch a := a.(type) {
-	case *dcrutil.AddressPubKeyHash:
+	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
 		f.pubKeyHashes[*a.Hash160()] = struct{}{}
 		return
-	case *dcrutil.AddressScriptHash:
+	case *stdaddr.AddressScriptHashV0:
 		f.scriptHashes[*a.Hash160()] = struct{}{}
 		return
-	case *dcrutil.AddressSecpPubKey:
-		serializedPubKey := a.ScriptAddress()
+	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
+		serializedPubKey := a.SerializedPubKey()
 		switch len(serializedPubKey) {
 		case 33: // compressed
 			var compressedPubKey [33]byte
@@ -361,33 +362,34 @@ func (f *wsClientFilter) addAddress(a dcrutil.Address) {
 }
 
 func (f *wsClientFilter) addAddressStr(s string) {
-	a, err := dcrutil.DecodeAddress(s, f.params)
-	// If address can't be decoded, no point in saving it since it should also
-	// impossible to create the address from an inspected transaction output
-	// script.
+	a, err := stdaddr.DecodeAddress(s, f.params)
 	if err != nil {
+		// There is no point in saving the address if it can't be decoded since
+		// it should also be impossible to create the address from an inspected
+		// transaction output script.
 		return
 	}
 	f.addAddress(a)
 }
 
-func (f *wsClientFilter) existsAddress(a dcrutil.Address) bool {
+func (f *wsClientFilter) existsAddress(a stdaddr.Address) bool {
 	switch a := a.(type) {
-	case *dcrutil.AddressPubKeyHash:
+	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
 		_, ok := f.pubKeyHashes[*a.Hash160()]
 		return ok
-	case *dcrutil.AddressScriptHash:
+	case *stdaddr.AddressScriptHashV0:
 		_, ok := f.scriptHashes[*a.Hash160()]
 		return ok
-	case *dcrutil.AddressSecpPubKey:
-		serializedPubKey := a.ScriptAddress()
+	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
+		serializedPubKey := a.SerializedPubKey()
 		switch len(serializedPubKey) {
 		case 33: // compressed
 			var compressedPubKey [33]byte
 			copy(compressedPubKey[:], serializedPubKey)
 			_, ok := f.compressedPubKeys[compressedPubKey]
 			if !ok {
-				_, ok = f.pubKeyHashes[*a.AddressPubKeyHash().Hash160()]
+				h160 := a.AddressPubKeyHash().(stdaddr.Hash160er).Hash160()
+				_, ok = f.pubKeyHashes[*h160]
 			}
 			return ok
 		}
@@ -659,10 +661,13 @@ func (m *wsNotificationManager) subscribedClients(tx *dcrutil.Tx, clients map[ch
 	subscribed := make(map[chan struct{}]struct{})
 
 	// Reusable backing array for a slice of a single address.
-	var scratchAddress [1]dcrutil.Address
+	var scratchAddress [1]stdaddr.Address
 
 	const isTreasuryEnabled = true // No need to look it up here.
 
+	// Local for convenience.
+	params := m.server.cfg.ChainParams
+
 	msgTx := tx.MsgTx()
 	var isTicket bool // lazily set
 	for q, c := range clients {
@@ -683,8 +688,7 @@ func (m *wsNotificationManager) subscribedClients(tx *dcrutil.Tx, clients map[ch
 		for i, output := range msgTx.TxOut {
 			watchOutput := true
 			sc, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version,
-				output.PkScript, m.server.cfg.ChainParams,
-				isTreasuryEnabled)
+				output.PkScript, params, isTreasuryEnabled)
 			if err != nil {
 				// Clients are not able to subscribe to
 				// nonstandard or non-address outputs.
@@ -699,7 +703,7 @@ func (m *wsNotificationManager) subscribedClients(tx *dcrutil.Tx, clients map[ch
 				// These outputs cannot be spent and do not need to
 				// be watched.
 				addr, err := stake.AddrFromSStxPkScrCommitment(
-					output.PkScript, m.server.cfg.ChainParams)
+					output.PkScript, params)
 				if err != nil {
 					log.Errorf("Failed to read commitment from "+
 						"previously-validated ticket: %v", err)
@@ -1193,8 +1197,7 @@ func (m *wsNotificationManager) notifyRelevantTxAccepted(tx *dcrutil.Tx,
 
 		for i, output := range msgTx.TxOut {
 			_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version,
-				output.PkScript, m.server.cfg.ChainParams,
-				isTreasuryEnabled)
+				output.PkScript, m.server.cfg.ChainParams, isTreasuryEnabled)
 			if err != nil {
 				continue
 			}
@@ -2314,9 +2317,8 @@ func rescanBlock(filter *wsClientFilter, block *dcrutil.Block, params *chaincfg.
 
 	LoopOutputs:
 		for i, output := range tx.TxOut {
-			_, addrs, _, err := txscript.ExtractPkScriptAddrs(
-				output.Version, output.PkScript, params,
-				isTreasuryEnabled)
+			_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version,
+				output.PkScript, params, isTreasuryEnabled)
 			if err != nil {
 				continue
 			}
diff --git a/rpcclient/chain.go b/rpcclient/chain.go
index 21817075..382106fb 100644
--- a/rpcclient/chain.go
+++ b/rpcclient/chain.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -17,6 +17,7 @@ import (
 	"github.com/decred/dcrd/gcs/v3"
 	"github.com/decred/dcrd/gcs/v3/blockcf2"
 	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -628,14 +629,14 @@ func (r *FutureValidateAddressResult) Receive() (*chainjson.ValidateAddressChain
 // the returned instance.
 //
 // See ValidateAddress for the blocking version and more details.
-func (c *Client) ValidateAddressAsync(ctx context.Context, address dcrutil.Address) *FutureValidateAddressResult {
+func (c *Client) ValidateAddressAsync(ctx context.Context, address stdaddr.Address) *FutureValidateAddressResult {
 	addr := address.Address()
 	cmd := chainjson.NewValidateAddressCmd(addr)
 	return (*FutureValidateAddressResult)(c.sendCmd(ctx, cmd))
 }
 
 // ValidateAddress returns information about the given Decred address.
-func (c *Client) ValidateAddress(ctx context.Context, address dcrutil.Address) (*chainjson.ValidateAddressChainResult, error) {
+func (c *Client) ValidateAddress(ctx context.Context, address stdaddr.Address) (*chainjson.ValidateAddressChainResult, error) {
 	return c.ValidateAddressAsync(ctx, address).Receive()
 }
 
@@ -666,7 +667,7 @@ func (r *FutureVerifyMessageResult) Receive() (bool, error) {
 // returned instance.
 //
 // See VerifyMessage for the blocking version and more details.
-func (c *Client) VerifyMessageAsync(ctx context.Context, address dcrutil.Address, signature, message string) *FutureVerifyMessageResult {
+func (c *Client) VerifyMessageAsync(ctx context.Context, address stdaddr.Address, signature, message string) *FutureVerifyMessageResult {
 	addr := address.Address()
 	cmd := chainjson.NewVerifyMessageCmd(addr, signature, message)
 	return (*FutureVerifyMessageResult)(c.sendCmd(ctx, cmd))
@@ -676,7 +677,7 @@ func (c *Client) VerifyMessageAsync(ctx context.Context, address dcrutil.Address
 //
 // NOTE: This function requires to the wallet to be unlocked.  See the
 // WalletPassphrase function for more details.
-func (c *Client) VerifyMessage(ctx context.Context, address dcrutil.Address, signature, message string) (bool, error) {
+func (c *Client) VerifyMessage(ctx context.Context, address stdaddr.Address, signature, message string) (bool, error) {
 	return c.VerifyMessageAsync(ctx, address, signature, message).Receive()
 }
 
diff --git a/rpcclient/extensions.go b/rpcclient/extensions.go
index efbfc5e6..b9442c18 100644
--- a/rpcclient/extensions.go
+++ b/rpcclient/extensions.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014-2015 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -12,6 +12,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/dcrutil/v4"
 	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -134,7 +135,7 @@ func (r *FutureExistsAddressResult) Receive() (bool, error) {
 // ExistsAddressAsync returns an instance of a type that can be used to get the
 // result of the RPC at some future time by invoking the Receive function on the
 // returned instance.
-func (c *Client) ExistsAddressAsync(ctx context.Context, address dcrutil.Address) *FutureExistsAddressResult {
+func (c *Client) ExistsAddressAsync(ctx context.Context, address stdaddr.Address) *FutureExistsAddressResult {
 	cmd := chainjson.NewExistsAddressCmd(address.Address())
 	return (*FutureExistsAddressResult)(c.sendCmd(ctx, cmd))
 }
@@ -143,7 +144,7 @@ func (c *Client) ExistsAddressAsync(ctx context.Context, address dcrutil.Address
 // used on the main chain or in mempool.
 //
 // NOTE: This is a dcrd extension.
-func (c *Client) ExistsAddress(ctx context.Context, address dcrutil.Address) (bool, error) {
+func (c *Client) ExistsAddress(ctx context.Context, address stdaddr.Address) (bool, error) {
 	return c.ExistsAddressAsync(ctx, address).Receive()
 }
 
@@ -172,7 +173,7 @@ func (r *FutureExistsAddressesResult) Receive() (string, error) {
 // ExistsAddressesAsync returns an instance of a type that can be used to get the
 // result of the RPC at some future time by invoking the Receive function on the
 // returned instance.
-func (c *Client) ExistsAddressesAsync(ctx context.Context, addresses []dcrutil.Address) *FutureExistsAddressesResult {
+func (c *Client) ExistsAddressesAsync(ctx context.Context, addresses []stdaddr.Address) *FutureExistsAddressesResult {
 	addrsStr := make([]string, len(addresses))
 	for i := range addresses {
 		addrsStr[i] = addresses[i].Address()
@@ -186,7 +187,7 @@ func (c *Client) ExistsAddressesAsync(ctx context.Context, addresses []dcrutil.A
 // in the blockchain or memory pool.
 //
 // NOTE: This is a dcrd extension.
-func (c *Client) ExistsAddresses(ctx context.Context, addresses []dcrutil.Address) (string, error) {
+func (c *Client) ExistsAddresses(ctx context.Context, addresses []stdaddr.Address) (string, error) {
 	return c.ExistsAddressesAsync(ctx, addresses).Receive()
 }
 
diff --git a/rpcclient/go.mod b/rpcclient/go.mod
index 9a421566..72863a51 100644
--- a/rpcclient/go.mod
+++ b/rpcclient/go.mod
@@ -8,6 +8,7 @@ require (
 	github.com/decred/dcrd/dcrutil/v4 v4.0.0-20210129181600-6ae0142d3b28
 	github.com/decred/dcrd/gcs/v3 v3.0.0-20210129195202-a4265d63b619
 	github.com/decred/dcrd/rpc/jsonrpc/types/v3 v3.0.0-20210129200153-14fd1a785bf2
+	github.com/decred/dcrd/txscript/v4 v4.0.0-20210330065944-a2366e6e0b3b
 	github.com/decred/dcrd/wire v1.4.0
 	github.com/decred/go-socks v1.1.0
 	github.com/decred/slog v1.1.0
diff --git a/rpcclient/notify.go b/rpcclient/notify.go
index 974b3581..77ed0e04 100644
--- a/rpcclient/notify.go
+++ b/rpcclient/notify.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -16,6 +16,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/dcrutil/v4"
 	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -1139,7 +1140,7 @@ func (r *FutureLoadTxFilterResult) Receive() error {
 // See LoadTxFilter for the blocking version and more details.
 //
 // NOTE: This is a dcrd extension and requires a websocket connection.
-func (c *Client) LoadTxFilterAsync(ctx context.Context, reload bool, addresses []dcrutil.Address,
+func (c *Client) LoadTxFilterAsync(ctx context.Context, reload bool, addresses []stdaddr.Address,
 	outPoints []wire.OutPoint) *FutureLoadTxFilterResult {
 
 	addrStrs := make([]string, len(addresses))
@@ -1164,6 +1165,6 @@ func (c *Client) LoadTxFilterAsync(ctx context.Context, reload bool, addresses [
 // during mempool acceptance, block acceptance, and for all rescanned blocks.
 //
 // NOTE: This is a dcrd extension and requires a websocket connection.
-func (c *Client) LoadTxFilter(ctx context.Context, reload bool, addresses []dcrutil.Address, outPoints []wire.OutPoint) error {
+func (c *Client) LoadTxFilter(ctx context.Context, reload bool, addresses []stdaddr.Address, outPoints []wire.OutPoint) error {
 	return c.LoadTxFilterAsync(ctx, reload, addresses, outPoints).Receive()
 }
diff --git a/rpcclient/rawtransactions.go b/rpcclient/rawtransactions.go
index 57fc6210..4bd759c3 100644
--- a/rpcclient/rawtransactions.go
+++ b/rpcclient/rawtransactions.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -15,6 +15,7 @@ import (
 	"github.com/decred/dcrd/dcrjson/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -199,11 +200,11 @@ func (r *FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) {
 //
 // See CreateRawTransaction for the blocking version and more details.
 func (c *Client) CreateRawTransactionAsync(ctx context.Context, inputs []chainjson.TransactionInput,
-	amounts map[dcrutil.Address]dcrutil.Amount, lockTime *int64, expiry *int64) *FutureCreateRawTransactionResult {
+	amounts map[stdaddr.Address]dcrutil.Amount, lockTime *int64, expiry *int64) *FutureCreateRawTransactionResult {
 
 	convertedAmts := make(map[string]float64, len(amounts))
 	for addr, amount := range amounts {
-		convertedAmts[addr.String()] = amount.ToCoin()
+		convertedAmts[addr.Address()] = amount.ToCoin()
 	}
 	cmd := chainjson.NewCreateRawTransactionCmd(inputs, convertedAmts, lockTime, expiry)
 	return (*FutureCreateRawTransactionResult)(c.sendCmd(ctx, cmd))
@@ -212,7 +213,7 @@ func (c *Client) CreateRawTransactionAsync(ctx context.Context, inputs []chainjs
 // CreateRawTransaction returns a new transaction spending the provided inputs
 // and sending to the provided addresses.
 func (c *Client) CreateRawTransaction(ctx context.Context, inputs []chainjson.TransactionInput,
-	amounts map[dcrutil.Address]dcrutil.Amount, lockTime *int64, expiry *int64) (*wire.MsgTx, error) {
+	amounts map[stdaddr.Address]dcrutil.Amount, lockTime *int64, expiry *int64) (*wire.MsgTx, error) {
 
 	return c.CreateRawTransactionAsync(ctx, inputs, amounts, lockTime, expiry).Receive()
 }
@@ -255,9 +256,9 @@ func (r *FutureCreateRawSStxResult) Receive() (*wire.MsgTx, error) {
 // a commitment address and amount, and a change address and amount. Same
 // name as the JSON lib, but different internal structures.
 type SStxCommitOut struct {
-	Addr       dcrutil.Address
+	Addr       stdaddr.Address
 	CommitAmt  dcrutil.Amount
-	ChangeAddr dcrutil.Address
+	ChangeAddr stdaddr.Address
 	ChangeAmt  dcrutil.Amount
 }
 
@@ -267,18 +268,18 @@ type SStxCommitOut struct {
 //
 // See CreateRawSStx for the blocking version and more details.
 func (c *Client) CreateRawSStxAsync(ctx context.Context, inputs []chainjson.SStxInput,
-	amount map[dcrutil.Address]dcrutil.Amount,
+	amount map[stdaddr.Address]dcrutil.Amount,
 	couts []SStxCommitOut) *FutureCreateRawSStxResult {
 
 	convertedAmt := make(map[string]int64, len(amount))
 	for addr, amt := range amount {
-		convertedAmt[addr.String()] = int64(amt)
+		convertedAmt[addr.Address()] = int64(amt)
 	}
 	convertedCouts := make([]chainjson.SStxCommitOut, len(couts))
 	for i, cout := range couts {
-		convertedCouts[i].Addr = cout.Addr.String()
+		convertedCouts[i].Addr = cout.Addr.Address()
 		convertedCouts[i].CommitAmt = int64(cout.CommitAmt)
-		convertedCouts[i].ChangeAddr = cout.ChangeAddr.String()
+		convertedCouts[i].ChangeAddr = cout.ChangeAddr.Address()
 		convertedCouts[i].ChangeAmt = int64(cout.ChangeAmt)
 	}
 
@@ -289,7 +290,7 @@ func (c *Client) CreateRawSStxAsync(ctx context.Context, inputs []chainjson.SStx
 // CreateRawSStx returns a new transaction spending the provided inputs
 // and sending to the provided addresses.
 func (c *Client) CreateRawSStx(ctx context.Context, inputs []chainjson.SStxInput,
-	amount map[dcrutil.Address]dcrutil.Amount,
+	amount map[stdaddr.Address]dcrutil.Amount,
 	couts []SStxCommitOut) (*wire.MsgTx, error) {
 
 	return c.CreateRawSStxAsync(ctx, inputs, amount, couts).Receive()
@@ -440,7 +441,7 @@ func (r *FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) {
 //
 // See SearchRawTransactions for the blocking version and more details.
 func (c *Client) SearchRawTransactionsAsync(ctx context.Context,
-	address dcrutil.Address, skip, count int, reverse bool,
+	address stdaddr.Address, skip, count int, reverse bool,
 	filterAddrs []string) *FutureSearchRawTransactionsResult {
 
 	addr := address.Address()
@@ -459,7 +460,7 @@ func (c *Client) SearchRawTransactionsAsync(ctx context.Context,
 // See SearchRawTransactionsVerbose to retrieve a list of data structures with
 // information about the transactions instead of the transactions themselves.
 func (c *Client) SearchRawTransactions(ctx context.Context,
-	address dcrutil.Address, skip, count int,
+	address stdaddr.Address, skip, count int,
 	reverse bool, filterAddrs []string) ([]*wire.MsgTx, error) {
 
 	return c.SearchRawTransactionsAsync(ctx, address, skip, count, reverse,
@@ -495,7 +496,7 @@ func (r *FutureSearchRawTransactionsVerboseResult) Receive() ([]*chainjson.Searc
 //
 // See SearchRawTransactionsVerbose for the blocking version and more details.
 func (c *Client) SearchRawTransactionsVerboseAsync(ctx context.Context,
-	address dcrutil.Address, skip, count int, includePrevOut bool, reverse bool,
+	address stdaddr.Address, skip, count int, includePrevOut bool, reverse bool,
 	filterAddrs *[]string) *FutureSearchRawTransactionsVerboseResult {
 
 	addr := address.Address()
@@ -517,7 +518,7 @@ func (c *Client) SearchRawTransactionsVerboseAsync(ctx context.Context,
 //
 // See SearchRawTransactions to retrieve a list of raw transactions instead.
 func (c *Client) SearchRawTransactionsVerbose(ctx context.Context,
-	address dcrutil.Address, skip, count int, includePrevOut, reverse bool,
+	address stdaddr.Address, skip, count int, includePrevOut, reverse bool,
 	filterAddrs []string) ([]*chainjson.SearchRawTransactionsResult, error) {
 
 	return c.SearchRawTransactionsVerboseAsync(ctx, address, skip, count,
diff --git a/txscript/error.go b/txscript/error.go
index 4c75ceb2..7076af78 100644
--- a/txscript/error.go
+++ b/txscript/error.go
@@ -27,10 +27,6 @@ const (
 	// index that is greater than or equal to the number of outputs.
 	ErrInvalidSigHashSingleIndex = ErrorKind("ErrInvalidSigHashSingleIndex")
 
-	// ErrUnsupportedAddress is returned when a concrete type that
-	// implements a dcrutil.Address is not a supported type.
-	ErrUnsupportedAddress = ErrorKind("ErrUnsupportedAddress")
-
 	// ErrNotMultisigScript is returned from CalcMultiSigStats when the
 	// provided script is not a multisig script.
 	ErrNotMultisigScript = ErrorKind("ErrNotMultisigScript")
diff --git a/txscript/sign.go b/txscript/sign.go
index 1576760f..40ab4e49 100644
--- a/txscript/sign.go
+++ b/txscript/sign.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2015 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -14,7 +14,7 @@ import (
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr"
-	"github.com/decred/dcrd/dcrutil/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -118,7 +118,7 @@ func p2pkSignatureScript(tx *wire.MsgTx, idx int, subScript []byte,
 // arguably legal to not be able to sign any of the outputs, no error is
 // returned.
 func signMultiSig(tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType,
-	addresses []dcrutil.Address, nRequired int, kdb KeyDB) ([]byte, bool) {
+	addresses []stdaddr.Address, nRequired int, kdb KeyDB) ([]byte, bool) {
 
 	// No need to add dummy in Decred.
 	builder := NewScriptBuilder()
@@ -149,8 +149,8 @@ func signMultiSig(tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashTyp
 // sign. It handles the signing of stake outputs.
 func handleStakeOutSign(tx *wire.MsgTx, idx int, subScript []byte,
 	hashType SigHashType, kdb KeyDB, sdb ScriptDB,
-	addresses []dcrutil.Address, class ScriptClass, subClass ScriptClass,
-	nrequired int) ([]byte, ScriptClass, []dcrutil.Address, int, error) {
+	addresses []stdaddr.Address, class ScriptClass, subClass ScriptClass,
+	nrequired int) ([]byte, ScriptClass, []stdaddr.Address, int, error) {
 
 	// look up key for address
 	switch subClass {
@@ -182,7 +182,7 @@ func handleStakeOutSign(tx *wire.MsgTx, idx int, subScript []byte,
 // its input index, a database of keys, a database of scripts, and information
 // about the type of signature and returns a signature, script class, the
 // addresses involved, and the number of signatures required.
-func sign(chainParams dcrutil.AddressParams, tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, isTreasuryEnabled bool) ([]byte, ScriptClass, []dcrutil.Address, int, error) {
+func sign(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, subScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, isTreasuryEnabled bool) ([]byte, ScriptClass, []stdaddr.Address, int, error) {
 
 	const scriptVersion = 0
 	class, addresses, nrequired, err := ExtractPkScriptAddrs(scriptVersion,
@@ -319,7 +319,7 @@ func sign(chainParams dcrutil.AddressParams, tx *wire.MsgTx, idx int, subScript
 // NOTE: This function is only valid for version 0 scripts.  Since the function
 // does not accept a script version, the results are undefined for other script
 // versions.
-func mergeMultiSig(tx *wire.MsgTx, idx int, addresses []dcrutil.Address,
+func mergeMultiSig(tx *wire.MsgTx, idx int, addresses []stdaddr.Address,
 	nRequired int, pkScript, sigScript, prevScript []byte) []byte {
 
 	// Nothing to merge if either the new or previous signature scripts are
@@ -393,9 +393,11 @@ sigLoop:
 			// All multisig addresses should be pubkey addresses
 			// it is an error to call this internal function with
 			// bad input.
-			pkaddr := addr.(*dcrutil.AddressSecpPubKey)
-
-			pubKey := pkaddr.PubKey()
+			pkAddr := addr.(stdaddr.SerializedPubKeyer)
+			pubKey, err := secp256k1.ParsePubKey(pkAddr.SerializedPubKey())
+			if err != nil {
+				continue
+			}
 
 			// If it matches we put it in the map. We only
 			// can take one signature per public key so if we
@@ -444,7 +446,7 @@ sigLoop:
 // NOTE: This function is only valid for version 0 scripts.  Since the function
 // does not accept a script version, the results are undefined for other script
 // versions.
-func mergeScripts(chainParams dcrutil.AddressParams, tx *wire.MsgTx, idx int, pkScript []byte, class ScriptClass, addresses []dcrutil.Address, nRequired int, sigScript, prevScript []byte, isTreasuryEnabled bool) []byte {
+func mergeScripts(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pkScript []byte, class ScriptClass, addresses []stdaddr.Address, nRequired int, sigScript, prevScript []byte, isTreasuryEnabled bool) []byte {
 
 	// TODO(oga) the scripthash and multisig paths here are overly
 	// inefficient in that they will recompute already known data.
@@ -511,28 +513,28 @@ func mergeScripts(chainParams dcrutil.AddressParams, tx *wire.MsgTx, idx int, pk
 // KeyDB is an interface type provided to SignTxOutput, it encapsulates
 // any user state required to get the private keys for an address.
 type KeyDB interface {
-	GetKey(dcrutil.Address) ([]byte, dcrec.SignatureType, bool, error)
+	GetKey(stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error)
 }
 
 // KeyClosure implements KeyDB with a closure.
-type KeyClosure func(dcrutil.Address) ([]byte, dcrec.SignatureType, bool, error)
+type KeyClosure func(stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error)
 
 // GetKey implements KeyDB by returning the result of calling the closure.
-func (kc KeyClosure) GetKey(address dcrutil.Address) ([]byte, dcrec.SignatureType, bool, error) {
+func (kc KeyClosure) GetKey(address stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
 	return kc(address)
 }
 
 // ScriptDB is an interface type provided to SignTxOutput, it encapsulates any
 // user state required to get the scripts for a pay-to-script-hash address.
 type ScriptDB interface {
-	GetScript(dcrutil.Address) ([]byte, error)
+	GetScript(stdaddr.Address) ([]byte, error)
 }
 
 // ScriptClosure implements ScriptDB with a closure.
-type ScriptClosure func(dcrutil.Address) ([]byte, error)
+type ScriptClosure func(stdaddr.Address) ([]byte, error)
 
 // GetScript implements ScriptDB by returning the result of calling the closure.
-func (sc ScriptClosure) GetScript(address dcrutil.Address) ([]byte, error) {
+func (sc ScriptClosure) GetScript(address stdaddr.Address) ([]byte, error) {
 	return sc(address)
 }
 
@@ -547,7 +549,7 @@ func (sc ScriptClosure) GetScript(address dcrutil.Address) ([]byte, error) {
 // NOTE: This function is only valid for version 0 scripts.  Since the function
 // does not accept a script version, the results are undefined for other script
 // versions.
-func SignTxOutput(chainParams dcrutil.AddressParams, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, previousScript []byte, isTreasuryEnabled bool) ([]byte, error) {
+func SignTxOutput(chainParams stdaddr.AddressParams, tx *wire.MsgTx, idx int, pkScript []byte, hashType SigHashType, kdb KeyDB, sdb ScriptDB, previousScript []byte, isTreasuryEnabled bool) ([]byte, error) {
 
 	sigScript, class, addresses, nrequired, err := sign(chainParams, tx,
 		idx, pkScript, hashType, kdb, sdb, isTreasuryEnabled)
diff --git a/txscript/standard.go b/txscript/standard.go
index 4b15e1f8..098e1231 100644
--- a/txscript/standard.go
+++ b/txscript/standard.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2017 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -12,18 +12,13 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
-	"github.com/decred/dcrd/dcrutil/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 )
 
 const (
 	// MaxDataCarrierSize is the maximum number of bytes allowed in pushed
 	// data to be considered a nulldata transaction.
 	MaxDataCarrierSize = 256
-
-	// nilAddrErrStr is the common error string to use for attempts to
-	// generate payment scripts to nil addresses embedded within a
-	// dcrutil.Address interface.
-	nilAddrErrStr = "unable to generate payment script for nil address"
 )
 
 // ScriptClass is an enumeration for the list of standard types of script.
@@ -743,327 +738,6 @@ func MultisigRedeemScriptFromScriptSig(script []byte) []byte {
 	return finalOpcodeData(scriptVersion, script)
 }
 
-// payToPubKeyHashScript creates a new script to pay a transaction
-// output to a 20-byte pubkey hash. It is expected that the input is a valid
-// hash.
-func payToPubKeyHashScript(pubKeyHash []byte) ([]byte, error) {
-	return NewScriptBuilder().AddOp(OP_DUP).AddOp(OP_HASH160).
-		AddData(pubKeyHash).AddOp(OP_EQUALVERIFY).AddOp(OP_CHECKSIG).
-		Script()
-}
-
-// payToPubKeyHashEdwardsScript creates a new script to pay a transaction
-// output to a 20-byte pubkey hash of an Edwards public key. It is expected
-// that the input is a valid hash.
-func payToPubKeyHashEdwardsScript(pubKeyHash []byte) ([]byte, error) {
-	edwardsData := []byte{byte(dcrec.STEd25519)}
-	return NewScriptBuilder().AddOp(OP_DUP).AddOp(OP_HASH160).
-		AddData(pubKeyHash).AddOp(OP_EQUALVERIFY).AddData(edwardsData).
-		AddOp(OP_CHECKSIGALT).Script()
-}
-
-// payToPubKeyHashSchnorrScript creates a new script to pay a transaction
-// output to a 20-byte pubkey hash of a secp256k1 public key, but expecting
-// a schnorr signature instead of a classic secp256k1 signature. It is
-// expected that the input is a valid hash.
-func payToPubKeyHashSchnorrScript(pubKeyHash []byte) ([]byte, error) {
-	schnorrData := []byte{byte(dcrec.STSchnorrSecp256k1)}
-	return NewScriptBuilder().AddOp(OP_DUP).AddOp(OP_HASH160).
-		AddData(pubKeyHash).AddOp(OP_EQUALVERIFY).AddData(schnorrData).
-		AddOp(OP_CHECKSIGALT).Script()
-}
-
-// payToScriptHashScript creates a new script to pay a transaction output to a
-// script hash. It is expected that the input is a valid hash.
-func payToScriptHashScript(scriptHash []byte) ([]byte, error) {
-	return NewScriptBuilder().AddOp(OP_HASH160).AddData(scriptHash).
-		AddOp(OP_EQUAL).Script()
-}
-
-// PayToScriptHashScript is the exported version of payToScriptHashScript.
-func PayToScriptHashScript(scriptHash []byte) ([]byte, error) {
-	return payToScriptHashScript(scriptHash)
-}
-
-// payToPubkeyScript creates a new script to pay a transaction output to a
-// public key. It is expected that the input is a valid pubkey.
-func payToPubKeyScript(serializedPubKey []byte) ([]byte, error) {
-	return NewScriptBuilder().AddData(serializedPubKey).
-		AddOp(OP_CHECKSIG).Script()
-}
-
-// payToEdwardsPubKeyScript creates a new script to pay a transaction output
-// to an Ed25519 public key. It is expected that the input is a valid pubkey.
-func payToEdwardsPubKeyScript(serializedPubKey []byte) ([]byte, error) {
-	edwardsData := []byte{byte(dcrec.STEd25519)}
-	return NewScriptBuilder().AddData(serializedPubKey).AddData(edwardsData).
-		AddOp(OP_CHECKSIGALT).Script()
-}
-
-// payToSchnorrPubKeyScript creates a new script to pay a transaction output
-// to a secp256k1 public key, but to be signed by Schnorr type signature. It
-// is expected that the input is a valid pubkey.
-func payToSchnorrPubKeyScript(serializedPubKey []byte) ([]byte, error) {
-	schnorrData := []byte{byte(dcrec.STSchnorrSecp256k1)}
-	return NewScriptBuilder().AddData(serializedPubKey).AddData(schnorrData).
-		AddOp(OP_CHECKSIGALT).Script()
-}
-
-// PayToSStx creates a new script to pay a transaction output to a script hash or
-// public key hash, but tags the output with OP_SSTX. For use in constructing
-// valid SStxs.
-func PayToSStx(addr dcrutil.Address) ([]byte, error) {
-	// Only pay to pubkey hash and pay to script hash are
-	// supported.
-	scriptType := PubKeyHashTy
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		if addr.DSA() != dcrec.STEcdsaSecp256k1 {
-			str := "unable to generate payment script for " +
-				"unsupported digital signature algorithm"
-			return nil, scriptError(ErrUnsupportedAddress, str)
-		}
-
-	case *dcrutil.AddressScriptHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		scriptType = ScriptHashTy
-
-	default:
-		str := fmt.Sprintf("unable to generate payment script for "+
-			"unsupported address type %T", addr)
-		return nil, scriptError(ErrUnsupportedAddress, str)
-	}
-
-	hash := addr.ScriptAddress()
-
-	if scriptType == PubKeyHashTy {
-		return NewScriptBuilder().AddOp(OP_SSTX).AddOp(OP_DUP).
-			AddOp(OP_HASH160).AddData(hash).AddOp(OP_EQUALVERIFY).
-			AddOp(OP_CHECKSIG).Script()
-	}
-	return NewScriptBuilder().AddOp(OP_SSTX).AddOp(OP_HASH160).
-		AddData(hash).AddOp(OP_EQUAL).Script()
-}
-
-// PayToSStxChange creates a new script to pay a transaction output to a
-// public key hash, but tags the output with OP_SSTXCHANGE. For use in constructing
-// valid SStxs.
-func PayToSStxChange(addr dcrutil.Address) ([]byte, error) {
-	// Only pay to pubkey hash and pay to script hash are
-	// supported.
-	scriptType := PubKeyHashTy
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		if addr.DSA() != dcrec.STEcdsaSecp256k1 {
-			str := "unable to generate payment script for " +
-				"unsupported digital signature algorithm"
-			return nil, scriptError(ErrUnsupportedAddress, str)
-		}
-
-	case *dcrutil.AddressScriptHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		scriptType = ScriptHashTy
-
-	default:
-		str := fmt.Sprintf("unable to generate payment script for "+
-			"unsupported address type %T", addr)
-		return nil, scriptError(ErrUnsupportedAddress, str)
-	}
-
-	hash := addr.ScriptAddress()
-
-	if scriptType == PubKeyHashTy {
-		return NewScriptBuilder().AddOp(OP_SSTXCHANGE).AddOp(OP_DUP).
-			AddOp(OP_HASH160).AddData(hash).AddOp(OP_EQUALVERIFY).
-			AddOp(OP_CHECKSIG).Script()
-	}
-	return NewScriptBuilder().AddOp(OP_SSTXCHANGE).AddOp(OP_HASH160).
-		AddData(hash).AddOp(OP_EQUAL).Script()
-}
-
-// PayToSSGen creates a new script to pay a transaction output to a public key
-// hash or script hash, but tags the output with OP_SSGEN. For use in constructing
-// valid SSGen txs.
-func PayToSSGen(addr dcrutil.Address) ([]byte, error) {
-	// Only pay to pubkey hash and pay to script hash are
-	// supported.
-	scriptType := PubKeyHashTy
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		if addr.DSA() != dcrec.STEcdsaSecp256k1 {
-			str := "unable to generate payment script for " +
-				"unsupported digital signature algorithm"
-			return nil, scriptError(ErrUnsupportedAddress, str)
-		}
-
-	case *dcrutil.AddressScriptHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		scriptType = ScriptHashTy
-
-	default:
-		str := fmt.Sprintf("unable to generate payment script for "+
-			"unsupported address type %T", addr)
-		return nil, scriptError(ErrUnsupportedAddress, str)
-	}
-
-	hash := addr.ScriptAddress()
-
-	if scriptType == PubKeyHashTy {
-		return NewScriptBuilder().AddOp(OP_SSGEN).AddOp(OP_DUP).
-			AddOp(OP_HASH160).AddData(hash).AddOp(OP_EQUALVERIFY).
-			AddOp(OP_CHECKSIG).Script()
-	}
-	return NewScriptBuilder().AddOp(OP_SSGEN).AddOp(OP_HASH160).
-		AddData(hash).AddOp(OP_EQUAL).Script()
-}
-
-// PayToSSGenPKHDirect creates a new script to pay a transaction output to a
-// public key hash, but tags the output with OP_SSGEN. For use in constructing
-// valid SSGen txs. Unlike PayToSSGen, this function directly uses the HASH160
-// pubkeyhash (instead of an address).
-func PayToSSGenPKHDirect(pkh []byte) ([]byte, error) {
-	return NewScriptBuilder().AddOp(OP_SSGEN).AddOp(OP_DUP).
-		AddOp(OP_HASH160).AddData(pkh).AddOp(OP_EQUALVERIFY).
-		AddOp(OP_CHECKSIG).Script()
-}
-
-// PayToSSGenSHDirect creates a new script to pay a transaction output to a
-// script hash, but tags the output with OP_SSGEN. For use in constructing
-// valid SSGen txs. Unlike PayToSSGen, this function directly uses the HASH160
-// script hash (instead of an address).
-func PayToSSGenSHDirect(sh []byte) ([]byte, error) {
-	return NewScriptBuilder().AddOp(OP_SSGEN).AddOp(OP_HASH160).
-		AddData(sh).AddOp(OP_EQUAL).Script()
-}
-
-// PayToSSRtx creates a new script to pay a transaction output to a
-// public key hash, but tags the output with OP_SSRTX. For use in constructing
-// valid SSRtx.
-func PayToSSRtx(addr dcrutil.Address) ([]byte, error) {
-	// Only pay to pubkey hash and pay to script hash are
-	// supported.
-	scriptType := PubKeyHashTy
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		if addr.DSA() != dcrec.STEcdsaSecp256k1 {
-			str := "unable to generate payment script for " +
-				"unsupported digital signature algorithm"
-			return nil, scriptError(ErrUnsupportedAddress, str)
-		}
-
-	case *dcrutil.AddressScriptHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		scriptType = ScriptHashTy
-
-	default:
-		str := fmt.Sprintf("unable to generate payment script for "+
-			"unsupported address type %T", addr)
-		return nil, scriptError(ErrUnsupportedAddress, str)
-	}
-
-	hash := addr.ScriptAddress()
-
-	if scriptType == PubKeyHashTy {
-		return NewScriptBuilder().AddOp(OP_SSRTX).AddOp(OP_DUP).
-			AddOp(OP_HASH160).AddData(hash).AddOp(OP_EQUALVERIFY).
-			AddOp(OP_CHECKSIG).Script()
-	}
-	return NewScriptBuilder().AddOp(OP_SSRTX).AddOp(OP_HASH160).
-		AddData(hash).AddOp(OP_EQUAL).Script()
-}
-
-// PayToSSRtxPKHDirect creates a new script to pay a transaction output to a
-// public key hash, but tags the output with OP_SSRTX. For use in constructing
-// valid SSRtx. Unlike PayToSSRtx, this function directly uses the HASH160
-// pubkeyhash (instead of an address).
-func PayToSSRtxPKHDirect(pkh []byte) ([]byte, error) {
-	return NewScriptBuilder().AddOp(OP_SSRTX).AddOp(OP_DUP).
-		AddOp(OP_HASH160).AddData(pkh).AddOp(OP_EQUALVERIFY).
-		AddOp(OP_CHECKSIG).Script()
-}
-
-// PayToSSRtxSHDirect creates a new script to pay a transaction output to a
-// script hash, but tags the output with OP_SSRTX. For use in constructing
-// valid SSRtx. Unlike PayToSSRtx, this function directly uses the HASH160
-// script hash (instead of an address).
-func PayToSSRtxSHDirect(sh []byte) ([]byte, error) {
-	return NewScriptBuilder().AddOp(OP_SSRTX).AddOp(OP_HASH160).
-		AddData(sh).AddOp(OP_EQUAL).Script()
-}
-
-// GenerateSStxAddrPush generates an OP_RETURN push for SSGen payment addresses in
-// an SStx.
-func GenerateSStxAddrPush(addr dcrutil.Address, amount dcrutil.Amount, limits uint16) ([]byte, error) {
-	// Only pay to pubkey hash and pay to script hash are
-	// supported.
-	scriptType := PubKeyHashTy
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		if addr.DSA() != dcrec.STEcdsaSecp256k1 {
-			str := "unable to generate payment script for " +
-				"unsupported digital signature algorithm"
-			return nil, scriptError(ErrUnsupportedAddress, str)
-		}
-
-	case *dcrutil.AddressScriptHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		scriptType = ScriptHashTy
-
-	default:
-		str := fmt.Sprintf("unable to generate payment script for "+
-			"unsupported address type %T", addr)
-		return nil, scriptError(ErrUnsupportedAddress, str)
-	}
-
-	// Concatenate the prefix, pubkeyhash, and amount.
-	adBytes := make([]byte, 20+8+2)
-	copy(adBytes[0:20], addr.ScriptAddress())
-	binary.LittleEndian.PutUint64(adBytes[20:28], uint64(amount))
-	binary.LittleEndian.PutUint16(adBytes[28:30], limits)
-
-	// Set the bit flag indicating pay to script hash.
-	if scriptType == ScriptHashTy {
-		adBytes[27] |= 1 << 7
-	}
-
-	return NewScriptBuilder().AddOp(OP_RETURN).AddData(adBytes).Script()
-}
-
 // GenerateSSGenBlockRef generates an OP_RETURN push for the block header hash and
 // height which the block votes on.
 func GenerateSSGenBlockRef(blockHash chainhash.Hash, height uint32) ([]byte, error) {
@@ -1097,58 +771,6 @@ func GenerateProvablyPruneableOut(data []byte) ([]byte, error) {
 	return NewScriptBuilder().AddOp(OP_RETURN).AddData(data).Script()
 }
 
-// PayToAddrScript creates a new script to pay a transaction output to a the
-// specified address.
-func PayToAddrScript(addr dcrutil.Address) ([]byte, error) {
-	switch addr := addr.(type) {
-	case *dcrutil.AddressPubKeyHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		switch addr.DSA() {
-		case dcrec.STEcdsaSecp256k1:
-			return payToPubKeyHashScript(addr.ScriptAddress())
-		case dcrec.STEd25519:
-			return payToPubKeyHashEdwardsScript(addr.ScriptAddress())
-		case dcrec.STSchnorrSecp256k1:
-			return payToPubKeyHashSchnorrScript(addr.ScriptAddress())
-		}
-
-	case *dcrutil.AddressScriptHash:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		return payToScriptHashScript(addr.ScriptAddress())
-
-	case *dcrutil.AddressSecpPubKey:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		return payToPubKeyScript(addr.ScriptAddress())
-
-	case *dcrutil.AddressEdwardsPubKey:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		return payToEdwardsPubKeyScript(addr.ScriptAddress())
-
-	case *dcrutil.AddressSecSchnorrPubKey:
-		if addr == nil {
-			return nil, scriptError(ErrUnsupportedAddress,
-				nilAddrErrStr)
-		}
-		return payToSchnorrPubKeyScript(addr.ScriptAddress())
-	}
-
-	str := fmt.Sprintf("unable to generate payment script for unsupported "+
-		"address type %T", addr)
-	return nil, scriptError(ErrUnsupportedAddress, str)
-}
-
 // MultiSigScript returns a valid script for a multisignature redemption where
 // the specified threshold number of the keys in the given public keys are
 // required to have signed the transaction for success.
@@ -1209,11 +831,10 @@ func PushedData(script []byte) ([][]byte, error) {
 // pubKeyHashToAddrs is a convenience function to attempt to convert the
 // passed hash to a pay-to-pubkey-hash address housed within an address
 // slice.  It is used to consolidate common code.
-func pubKeyHashToAddrs(hash []byte, params dcrutil.AddressParams) []dcrutil.Address {
+func pubKeyHashToAddrs(hash []byte, params stdaddr.AddressParams) []stdaddr.Address {
 	// Skip the pubkey hash if it's invalid for some reason.
-	var addrs []dcrutil.Address
-	addr, err := dcrutil.NewAddressPubKeyHash(hash, params,
-		dcrec.STEcdsaSecp256k1)
+	var addrs []stdaddr.Address
+	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(hash, params)
 	if err == nil {
 		addrs = append(addrs, addr)
 	}
@@ -1223,10 +844,10 @@ func pubKeyHashToAddrs(hash []byte, params dcrutil.AddressParams) []dcrutil.Addr
 // scriptHashToAddrs is a convenience function to attempt to convert the passed
 // hash to a pay-to-script-hash address housed within an address slice.  It is
 // used to consolidate common code.
-func scriptHashToAddrs(hash []byte, params dcrutil.AddressParams) []dcrutil.Address {
+func scriptHashToAddrs(hash []byte, params stdaddr.AddressParams) []stdaddr.Address {
 	// Skip the hash if it's invalid for some reason.
-	var addrs []dcrutil.Address
-	addr, err := dcrutil.NewAddressScriptHashFromHash(hash, params)
+	var addrs []stdaddr.Address
+	addr, err := stdaddr.NewAddressScriptHashV0FromHash(hash, params)
 	if err == nil {
 		addrs = append(addrs, addr)
 	}
@@ -1241,7 +862,7 @@ func scriptHashToAddrs(hash []byte, params dcrutil.AddressParams) []dcrutil.Addr
 // NOTE: This function only attempts to identify version 0 scripts.  The return
 // value will indicate a nonstandard script type for other script versions along
 // with an invalid script version error.
-func ExtractPkScriptAddrs(version uint16, pkScript []byte, chainParams dcrutil.AddressParams, isTreasuryEnabled bool) (ScriptClass, []dcrutil.Address, int, error) {
+func ExtractPkScriptAddrs(version uint16, pkScript []byte, chainParams stdaddr.AddressParams, isTreasuryEnabled bool) (ScriptClass, []stdaddr.Address, int, error) {
 	if version != 0 {
 		return NonStandardTy, nil, 0, fmt.Errorf("invalid script version")
 	}
@@ -1258,20 +879,32 @@ func ExtractPkScriptAddrs(version uint16, pkScript []byte, chainParams dcrutil.A
 
 	// Check for pay-to-alt-pubkey-hash script.
 	if data, sigType := extractPubKeyHashAltDetails(pkScript); data != nil {
-		var addrs []dcrutil.Address
-		addr, err := dcrutil.NewAddressPubKeyHash(data, chainParams, sigType)
-		if err == nil {
-			addrs = append(addrs, addr)
+		var addrs []stdaddr.Address
+		switch sigType {
+		case dcrec.STEd25519:
+			addr, err := stdaddr.NewAddressPubKeyHashEd25519(version, data,
+				chainParams)
+			if err == nil {
+				addrs = append(addrs, addr)
+			}
+
+		case dcrec.STSchnorrSecp256k1:
+			addr, err := stdaddr.NewAddressPubKeyHashSchnorrSecp256k1(version,
+				data, chainParams)
+			if err == nil {
+				addrs = append(addrs, addr)
+			}
 		}
 		return PubkeyHashAltTy, addrs, 1, nil
 	}
 
 	// Check for pay-to-pubkey script.
 	if data := extractPubKey(pkScript); data != nil {
-		var addrs []dcrutil.Address
+		var addrs []stdaddr.Address
 		pk, err := secp256k1.ParsePubKey(data)
 		if err == nil {
-			addr, err := dcrutil.NewAddressSecpPubKeyCompressed(pk, chainParams)
+			addr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1(version, pk,
+				chainParams)
 			if err == nil {
 				addrs = append(addrs, addr)
 			}
@@ -1281,16 +914,18 @@ func ExtractPkScriptAddrs(version uint16, pkScript []byte, chainParams dcrutil.A
 
 	// Check for pay-to-alt-pubkey script.
 	if pk, sigType := extractPubKeyAltDetails(pkScript); pk != nil {
-		var addrs []dcrutil.Address
+		var addrs []stdaddr.Address
 		switch sigType {
 		case dcrec.STEd25519:
-			addr, err := dcrutil.NewAddressEdwardsPubKey(pk, chainParams)
+			addr, err := stdaddr.NewAddressPubKeyEd25519Raw(version, pk,
+				chainParams)
 			if err == nil {
 				addrs = append(addrs, addr)
 			}
 
 		case dcrec.STSchnorrSecp256k1:
-			addr, err := dcrutil.NewAddressSecSchnorrPubKey(pk, chainParams)
+			addr, err := stdaddr.NewAddressPubKeySchnorrSecp256k1Raw(version,
+				pk, chainParams)
 			if err == nil {
 				addrs = append(addrs, addr)
 			}
@@ -1303,12 +938,12 @@ func ExtractPkScriptAddrs(version uint16, pkScript []byte, chainParams dcrutil.A
 	details := extractMultisigScriptDetails(version, pkScript, true)
 	if details.valid {
 		// Convert the public keys while skipping any that are invalid.
-		addrs := make([]dcrutil.Address, 0, details.numPubKeys)
+		addrs := make([]stdaddr.Address, 0, details.numPubKeys)
 		for i := 0; i < details.numPubKeys; i++ {
 			pubkey, err := secp256k1.ParsePubKey(details.pubKeys[i])
 			if err == nil {
-				addr, err := dcrutil.NewAddressSecpPubKeyCompressed(pubkey,
-					chainParams)
+				addr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1(version,
+					pubkey, chainParams)
 				if err == nil {
 					addrs = append(addrs, addr)
 				}


Test patch:
diff --git a/blockchain/chain_test.go b/blockchain/chain_test.go
index 61bd361d..124fafe5 100644
--- a/blockchain/chain_test.go
+++ b/blockchain/chain_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -21,6 +21,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -125,7 +126,7 @@ func TestBlockchainFunctions(t *testing.T) {
 			"want %v, got %v", expectedVal, val)
 	}
 
-	a, _ := dcrutil.DecodeAddress("SsbKpMkPnadDcZFFZqRPY8nvdFagrktKuzB", params)
+	a, _ := stdaddr.DecodeAddress("SsbKpMkPnadDcZFFZqRPY8nvdFagrktKuzB", params)
 	hs, err := chain.TicketsWithAddress(a, noTreasury)
 	if err != nil {
 		t.Errorf("Failed to do TicketsWithAddress: %v", err)
diff --git a/blockchain/fullblocktests/generate.go b/blockchain/fullblocktests/generate.go
index 3e6a01fe..64ea868c 100644
--- a/blockchain/fullblocktests/generate.go
+++ b/blockchain/fullblocktests/generate.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2016 The btcsuite developers
-// Copyright (c) 2016-2020 The Decred developers
+// Copyright (c) 2016-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -20,6 +20,7 @@ import (
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -41,11 +42,6 @@ const (
 	// whether or not to approve the previous block.
 	voteBitNo  = 0x0000
 	voteBitYes = 0x0001
-
-	// noTreasury signifies the treasury agenda should be treated as though
-	// it is inactive.  It is used to increase the readability of the
-	// tests.
-	noTreasury = false
 )
 
 var (
@@ -158,7 +154,7 @@ func (b RejectedNonCanonicalBlock) FullBlockTestInstance() {}
 // payToScriptHashScript returns a standard pay-to-script-hash for the provided
 // redeem script.
 func payToScriptHashScript(redeemScript []byte) []byte {
-	redeemScriptHash := dcrutil.Hash160(redeemScript)
+	redeemScriptHash := stdaddr.Hash160(redeemScript)
 	script, err := txscript.NewScriptBuilder().
 		AddOp(txscript.OP_HASH160).AddData(redeemScriptHash).
 		AddOp(txscript.OP_EQUAL).Script()
@@ -825,21 +821,15 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
 	g.SetTip("bf6")
 	g.NextBlock("bbadtaxscript", outs[4], ticketOuts[4],
 		func(b *wire.MsgBlock) {
-			taxOutput := b.Transactions[0].TxOut[0]
-			_, addrs, _, _ := txscript.ExtractPkScriptAddrs(
-				g.Params().OrganizationPkScriptVersion,
-				taxOutput.PkScript, g.Params(), noTreasury)
-			p2shTaxAddr := addrs[0].(*dcrutil.AddressScriptHash)
-			p2pkhTaxAddr, err := dcrutil.NewAddressPubKeyHash(
-				p2shTaxAddr.Hash160()[:], g.Params(),
-				dcrec.STEcdsaSecp256k1)
-			if err != nil {
-				panic(err)
-			}
-			p2pkhScript, err := txscript.PayToAddrScript(p2pkhTaxAddr)
+			h160 := g.Params().OrganizationPkScript[2:22]
+			p2pkhTaxAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+				h160, g.Params())
 			if err != nil {
 				panic(err)
 			}
+			p2pkhScriptVer, p2pkhScript := p2pkhTaxAddr.PaymentScript()
+			taxOutput := b.Transactions[0].TxOut[0]
+			taxOutput.Version = p2pkhScriptVer
 			taxOutput.PkScript = p2pkhScript
 		})
 	rejected(blockchain.ErrNoTax)
@@ -1280,12 +1270,12 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
 	//                 \-> bv15(9)
 	g.SetTip("bsl5")
 	g.NextBlock("bv15", outs[9], ticketOuts[9], func(b *wire.MsgBlock) {
-		ticketFee := dcrutil.Amount(2)
-		ticketPrice := dcrutil.Amount(g.CalcNextReqStakeDifficulty(g.Tip()))
+		ticketFee := int64(2)
+		ticketPrice := g.CalcNextReqStakeDifficulty(g.Tip())
 		ticketPrice--
-		b.STransactions[5].TxOut[1].PkScript =
-			chaingen.PurchaseCommitmentScript(g.P2shOpTrueAddr(),
-				ticketPrice+ticketFee, 0, ticketPrice)
+		commitTxOut := b.STransactions[5].TxOut[1]
+		commitTxOut.Version, commitTxOut.PkScript = g.P2shOpTrueAddr().
+			RewardCommitmentScript(ticketPrice+ticketFee, 0, ticketPrice)
 	})
 	rejected(blockchain.ErrTicketCommitment)
 
diff --git a/blockchain/stake/scripttype_test.go b/blockchain/stake/scripttype_test.go
index 773d7be1..4c96f532 100644
--- a/blockchain/stake/scripttype_test.go
+++ b/blockchain/stake/scripttype_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Decred developers
+// Copyright (c) 2020-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -7,12 +7,12 @@ package stake
 import (
 	"testing"
 
-	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 )
 
 var (
-	hash160 = dcrutil.Hash160([]byte("test"))
+	hash160 = stdaddr.Hash160([]byte("test"))
 )
 
 func TestIsRevocationScript(t *testing.T) {
diff --git a/blockchain/stake/staketx_test.go b/blockchain/stake/staketx_test.go
index 24eca653..c82d85c0 100644
--- a/blockchain/stake/staketx_test.go
+++ b/blockchain/stake/staketx_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -14,6 +14,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -1260,7 +1261,7 @@ func TestGetStakeRewards(t *testing.T) {
 }
 
 func TestIsNullDataScript(t *testing.T) {
-	var hash160 = dcrutil.Hash160([]byte("test"))
+	var hash160 = stdaddr.Hash160([]byte("test"))
 	var overMaxDataCarrierSize = make([]byte, txscript.MaxDataCarrierSize+1)
 	var underMaxDataCarrierSize = make([]byte, txscript.MaxDataCarrierSize/2)
 	rand.Read(overMaxDataCarrierSize)
diff --git a/blockchain/stake/treasury_test.go b/blockchain/stake/treasury_test.go
index c5324d91..e8ef3c47 100644
--- a/blockchain/stake/treasury_test.go
+++ b/blockchain/stake/treasury_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Decred developers
+// Copyright (c) 2020-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -14,10 +14,10 @@ import (
 
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
-	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -162,6 +162,15 @@ var (
 //	panic("x")
 //}
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 // TestTreasuryIsFunctions goes through all valid treasury opcode combinations.
 func TestTreasuryIsFunctions(t *testing.T) {
 	tests := []struct {
@@ -264,17 +273,15 @@ func TestTreasuryIsFunctions(t *testing.T) {
 			msgTx.Version = wire.TxVersionTreasury
 			msgTx.AddTxOut(wire.NewTxOut(0, script))
 
-			p2shOpTrueAddr, err := dcrutil.NewAddressScriptHash([]byte{txscript.OP_TRUE},
+			opTrueScript := []byte{txscript.OP_TRUE}
+			p2shOpTrueAddr, err := stdaddr.NewAddressScriptHashV0(opTrueScript,
 				chaincfg.MainNetParams())
 			if err != nil {
 				panic(err)
 			}
-			changeScript, err := txscript.PayToSStxChange(p2shOpTrueAddr)
-			if err != nil {
-				panic(err)
-			}
-			msgTx.AddTxOut(wire.NewTxOut(1, changeScript))
-			msgTx.AddTxIn(&wire.TxIn{}) // On input required
+			changeScriptVer, changeScript := p2shOpTrueAddr.StakeChangeScript()
+			msgTx.AddTxOut(newTxOut(1, changeScriptVer, changeScript))
+			msgTx.AddTxIn(&wire.TxIn{}) // One input required
 			return msgTx
 		},
 		is:       IsTAdd,
@@ -392,19 +399,14 @@ func TestTreasuryIsFunctions(t *testing.T) {
 			msgTx.AddTxOut(wire.NewTxOut(0, opretScript))
 
 			// OP_TGEN
-			p2shOpTrueAddr, err := dcrutil.NewAddressScriptHash([]byte{txscript.OP_TRUE},
+			opTrueScript := []byte{txscript.OP_TRUE}
+			p2shOpTrueAddr, err := stdaddr.NewAddressScriptHashV0(opTrueScript,
 				chaincfg.MainNetParams())
 			if err != nil {
 				panic(err)
 			}
-			p2shOpTrueScript, err := txscript.PayToAddrScript(p2shOpTrueAddr)
-			if err != nil {
-				panic(err)
-			}
-			script := make([]byte, len(p2shOpTrueScript)+1)
-			script[0] = txscript.OP_TGEN
-			copy(script[1:], p2shOpTrueScript)
-			msgTx.AddTxOut(wire.NewTxOut(0, script))
+			genScriptVer, genScript := p2shOpTrueAddr.PayFromTreasuryScript()
+			msgTx.AddTxOut(newTxOut(0, genScriptVer, genScript))
 
 			// tspend
 			builder = txscript.NewScriptBuilder()
@@ -456,19 +458,16 @@ func TestTreasuryIsFunctions(t *testing.T) {
 			// OP_TGEN
 			privKey := secp256k1.NewPrivateKey(new(secp256k1.ModNScalar).SetInt(1))
 			pubKey := privKey.PubKey().SerializeCompressed()
-			p2pkhAddr, err := dcrutil.NewAddressSecpPubKey(pubKey,
+			p2pkAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(pubKey,
 				chaincfg.MainNetParams())
 			if err != nil {
 				panic(err)
 			}
-			p2pkhOpTrueScript, err := txscript.PayToAddrScript(p2pkhAddr)
-			if err != nil {
-				panic(err)
-			}
-			script := make([]byte, len(p2pkhOpTrueScript)+1)
+			p2pkScriptVer, p2pkScript := p2pkAddr.PaymentScript()
+			script := make([]byte, len(p2pkScript)+1)
 			script[0] = txscript.OP_TGEN
-			copy(script[1:], p2pkhOpTrueScript)
-			msgTx.AddTxOut(wire.NewTxOut(0, script))
+			copy(script[1:], p2pkScript)
+			msgTx.AddTxOut(newTxOut(0, p2pkScriptVer, script))
 
 			// tspend
 			builder = txscript.NewScriptBuilder()
@@ -520,21 +519,14 @@ func TestTreasuryIsFunctions(t *testing.T) {
 			// OP_TGEN
 			privKey := secp256k1.NewPrivateKey(new(secp256k1.ModNScalar).SetInt(1))
 			pubKey := privKey.PubKey()
-			pkHash := dcrutil.Hash160(pubKey.SerializeCompressed())
-			p2pkhAddr, err := dcrutil.NewAddressPubKeyHash(pkHash,
-				chaincfg.MainNetParams(), dcrec.STEcdsaSecp256k1)
-
+			pkHash := stdaddr.Hash160(pubKey.SerializeCompressed())
+			p2pkhAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+				pkHash, chaincfg.MainNetParams())
 			if err != nil {
 				panic(err)
 			}
-			p2shOpTrueScript, err := txscript.PayToAddrScript(p2pkhAddr)
-			if err != nil {
-				panic(err)
-			}
-			script := make([]byte, len(p2shOpTrueScript)+1)
-			script[0] = txscript.OP_TGEN
-			copy(script[1:], p2shOpTrueScript)
-			msgTx.AddTxOut(wire.NewTxOut(0, script))
+			genScriptVer, genScript := p2pkhAddr.PayFromTreasuryScript()
+			msgTx.AddTxOut(newTxOut(0, genScriptVer, genScript))
 
 			// tspend
 			builder = txscript.NewScriptBuilder()
diff --git a/blockchain/treasury_test.go b/blockchain/treasury_test.go
index e7d825af..46b0305a 100644
--- a/blockchain/treasury_test.go
+++ b/blockchain/treasury_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Decred developers
+// Copyright (c) 2020-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -27,6 +27,7 @@ import (
 	"github.com/decred/dcrd/gcs/v3/blockcf2"
 	"github.com/decred/dcrd/lru"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -1873,14 +1874,14 @@ func TestSpendableTreasuryTxs(t *testing.T) {
 	// mining these txs.
 	spendPrivKey := secp256k1.NewPrivateKey(new(secp256k1.ModNScalar).SetInt(1))
 	pubKey := spendPrivKey.PubKey().SerializeCompressed()
-	pubKeyHash := dcrutil.Hash160(pubKey)
-	spendP2pkhAddr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, params,
-		dcrec.STEcdsaSecp256k1)
+	pubKeyHash := stdaddr.Hash160(pubKey)
+	spendP2pkhAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+		pubKeyHash, params)
 	if err != nil {
 		t.Fatal(err)
 	}
 	spendP2shScript := []byte{txscript.OP_NOP, txscript.OP_TRUE}
-	spendP2shAddr, err := dcrutil.NewAddressScriptHash(spendP2shScript,
+	spendP2shAddr, err := stdaddr.NewAddressScriptHashV0(spendP2shScript,
 		params)
 	if err != nil {
 		t.Fatal(err)
@@ -1888,14 +1889,14 @@ func TestSpendableTreasuryTxs(t *testing.T) {
 
 	addPrivKey := secp256k1.NewPrivateKey(new(secp256k1.ModNScalar).SetInt(2))
 	pubKey = addPrivKey.PubKey().SerializeCompressed()
-	pubKeyHash = dcrutil.Hash160(pubKey)
-	addP2pkhAddr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, params,
-		dcrec.STEcdsaSecp256k1)
+	pubKeyHash = stdaddr.Hash160(pubKey)
+	addP2pkhAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+		pubKeyHash, params)
 	if err != nil {
 		t.Fatal(err)
 	}
 	addP2shScript := []byte{txscript.OP_NOP, txscript.OP_NOP, txscript.OP_TRUE}
-	addP2shAddr, err := dcrutil.NewAddressScriptHash(addP2shScript, params)
+	addP2shAddr, err := stdaddr.NewAddressScriptHashV0(addP2shScript, params)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -3357,15 +3358,23 @@ func TestTreasuryBalance(t *testing.T) {
 	}
 }
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 func createTAdd(spend chaingen.SpendableOut, changeDivisor dcrutil.Amount, params *chaincfg.Params) func(b *wire.MsgBlock) {
-	p2shOpTrueAddr, err := dcrutil.NewAddressScriptHash([]byte{txscript.OP_TRUE},
-		params)
+	redeemScript := []byte{txscript.OP_TRUE}
+	p2shOpTrueAddr, err := stdaddr.NewAddressScriptHashV0(redeemScript, params)
 	if err != nil {
 		panic(err)
 	}
 
 	return func(b *wire.MsgBlock) {
-		// Add TADD
 		amount := spend.Amount()
 		tx := wire.NewMsgTx()
 		tx.AddTxIn(&wire.TxIn{
@@ -3374,25 +3383,20 @@ func createTAdd(spend chaingen.SpendableOut, changeDivisor dcrutil.Amount, param
 			ValueIn:          int64(amount),
 			BlockHeight:      spend.BlockHeight(),
 			BlockIndex:       spend.BlockIndex(),
-			SignatureScript: []byte{txscript.OP_DATA_1,
-				txscript.OP_TRUE},
+			SignatureScript:  []byte{txscript.OP_DATA_1, txscript.OP_TRUE},
 		})
 
 		// Add negative change.
+		trsyAddScript := []byte{txscript.OP_TADD}
 		if changeDivisor != 0 {
-			changeScript, err := txscript.PayToSStxChange(p2shOpTrueAddr)
-			if err != nil {
-				panic(err)
-			}
+			changeScriptVer, changeScript := p2shOpTrueAddr.StakeChangeScript()
 
 			change := spend.Amount() / changeDivisor
 			amount := spend.Amount() - change
-			tx.AddTxOut(wire.NewTxOut(int64(amount),
-				[]byte{txscript.OP_TADD}))
-			tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
+			tx.AddTxOut(wire.NewTxOut(int64(amount), trsyAddScript))
+			tx.AddTxOut(newTxOut(int64(change), changeScriptVer, changeScript))
 		} else {
-			tx.AddTxOut(wire.NewTxOut(int64(amount),
-				[]byte{txscript.OP_TADD}))
+			tx.AddTxOut(wire.NewTxOut(int64(amount), trsyAddScript))
 		}
 		tx.Version = wire.TxVersionTreasury
 		b.AddSTransaction(tx)
diff --git a/hdkeychain/example_test.go b/hdkeychain/example_test.go
index f7d543fa..bd407d02 100644
--- a/hdkeychain/example_test.go
+++ b/hdkeychain/example_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -9,9 +9,8 @@ import (
 	"fmt"
 
 	"github.com/decred/dcrd/chaincfg/v3"
-	"github.com/decred/dcrd/dcrec"
-	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/hdkeychain/v3"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 )
 
 // This example demonstrates how to generate a cryptographically random seed
@@ -127,9 +126,8 @@ func Example_defaultWalletLayout() {
 	// pubKeyHashAddr is a convenience function to convert an extended
 	// pubkey to a standard pay-to-pubkey-hash address.
 	pubKeyHashAddr := func(extKey *hdkeychain.ExtendedKey) (string, error) {
-		pkHash := dcrutil.Hash160(extKey.SerializedPubKey())
-		addr, err := dcrutil.NewAddressPubKeyHash(pkHash, net,
-			dcrec.STEcdsaSecp256k1)
+		pkHash := stdaddr.Hash160(extKey.SerializedPubKey())
+		addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, net)
 		if err != nil {
 			fmt.Println(err)
 			return "", err
diff --git a/internal/mempool/mempool_test.go b/internal/mempool/mempool_test.go
index 7ffcac9b..9d254e6b 100644
--- a/internal/mempool/mempool_test.go
+++ b/internal/mempool/mempool_test.go
@@ -19,7 +19,6 @@ import (
 	"github.com/decred/dcrd/blockchain/stake/v4"
 	"github.com/decred/dcrd/blockchain/standalone/v2"
 	"github.com/decred/dcrd/blockchain/v4"
-	"github.com/decred/dcrd/blockchain/v4/chaingen"
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrec"
@@ -27,6 +26,7 @@ import (
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/internal/mining"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -341,9 +341,13 @@ type poolHarness struct {
 	//
 	// payAddr is the p2sh address for the signing key and is used for the
 	// payment address throughout the tests.
+	//
+	// payScriptVer and payScript are the script version and script to pay the
+	// aforementioned payAddr.
 	signKey        []byte
 	sigType        dcrec.SignatureType
-	payAddr        dcrutil.Address
+	payAddr        stdaddr.StakeAddress
+	payScriptVer   uint16
 	payScript      []byte
 	chainParams    *chaincfg.Params
 	treasuryActive bool
@@ -355,13 +359,13 @@ type poolHarness struct {
 // GetScript is the pool harness' implementation of the ScriptDB interface.
 // It returns the pool harness' payment redeem script for any address
 // passed in.
-func (p *poolHarness) GetScript(addr dcrutil.Address) ([]byte, error) {
+func (p *poolHarness) GetScript(addr stdaddr.Address) ([]byte, error) {
 	return p.payScript, nil
 }
 
 // GetKey is the pool harness' implementation of the KeyDB interface.
 // It returns the pool harness' signature key for any address passed in.
-func (p *poolHarness) GetKey(addr dcrutil.Address) ([]byte, dcrec.SignatureType, bool, error) {
+func (p *poolHarness) GetKey(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
 	return p.signKey, p.sigType, true, nil
 }
 
@@ -527,22 +531,15 @@ func (p *poolHarness) CreateTx(out spendableOutput) (*dcrutil.Tx, error) {
 // CreateTicketPurchase creates a ticket purchase spending the first output of
 // the provided transaction.
 func (p *poolHarness) CreateTicketPurchase(sourceTx *dcrutil.Tx, cost int64) (*dcrutil.Tx, error) {
-	ticketFee := dcrutil.Amount(singleInputTicketSize)
-	ticketPrice := dcrutil.Amount(cost)
+	ticketFee := singleInputTicketSize
+	ticketPrice := cost
 
-	// Generate the p2sh, commitment and change scripts of the ticket.
-	pkScript, err := txscript.PayToSStx(p.payAddr)
-	if err != nil {
-		return nil, err
-	}
-	commitScript := chaingen.PurchaseCommitmentScript(p.payAddr,
+	// Generate the voting rights, commitment, and change scripts of the ticket.
+	voteScriptVer, voteScript := p.payAddr.VotingRightsScript()
+	commitScriptVer, commitScript := p.payAddr.RewardCommitmentScript(
 		ticketPrice+ticketFee, 0, ticketPrice)
-	change := dcrutil.Amount(sourceTx.MsgTx().TxOut[0].Value) -
-		ticketPrice - ticketFee
-	changeScript, err := txscript.PayToSStxChange(p.payAddr)
-	if err != nil {
-		return nil, err
-	}
+	change := sourceTx.MsgTx().TxOut[0].Value - ticketPrice - ticketFee
+	changeScriptVer, changeScript := p.payAddr.StakeChangeScript()
 
 	// Generate the ticket purchase.
 	tx := wire.NewMsgTx()
@@ -557,9 +554,9 @@ func (p *poolHarness) CreateTicketPurchase(sourceTx *dcrutil.Tx, cost int64) (*d
 		BlockHeight: uint32(p.chain.BestHeight()),
 	})
 
-	tx.AddTxOut(wire.NewTxOut(int64(ticketPrice), pkScript))
-	tx.AddTxOut(wire.NewTxOut(0, commitScript))
-	tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
+	tx.AddTxOut(newTxOut(ticketPrice, voteScriptVer, voteScript))
+	tx.AddTxOut(newTxOut(0, commitScriptVer, commitScript))
+	tx.AddTxOut(newTxOut(change, changeScriptVer, changeScript))
 
 	// Sign the ticket purchase.
 	sigScript, err := txscript.SignatureScript(tx, 0,
@@ -582,6 +579,15 @@ func newVoteScript(voteBits stake.VoteBits) ([]byte, error) {
 	return txscript.GenerateProvablyPruneableOut(b)
 }
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 // CreateVote creates a vote transaction using the provided ticket.  The vote
 // will vote on the current best block hash and height associated with the
 // harness.
@@ -627,15 +633,18 @@ func (p *poolHarness) CreateVote(ticket *dcrutil.Tx, mungers ...func(*wire.MsgTx
 	}
 	vote.AddTxOut(wire.NewTxOut(0, voteScript))
 
-	// Create P2SH scripts for the ticket outputs.
-	for i, hash160 := range ticketHash160s {
-		scriptFn := txscript.PayToSSGenPKHDirect
+	// Create payment scripts for the ticket commitments.
+	params := p.chainParams
+	for i, h160 := range ticketHash160s {
+		var addr stdaddr.StakeAddress
 		if ticketPayKinds[i] { // P2SH
-			scriptFn = txscript.PayToSSGenSHDirect
+			addr, _ = stdaddr.NewAddressScriptHashV0FromHash(h160, params)
+		} else {
+			addr, _ = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(h160, params)
 		}
-		// Error is checking for a nil hash160, just ignore it.
-		script, _ := scriptFn(hash160)
-		vote.AddTxOut(wire.NewTxOut(voteRewardValues[i], script))
+
+		scriptVer, script := addr.PayVoteCommitmentScript()
+		vote.AddTxOut(newTxOut(voteRewardValues[i], scriptVer, script))
 	}
 
 	// Perform any transaction munging just before signing.
@@ -679,13 +688,16 @@ func (p *poolHarness) CreateRevocation(ticket *dcrutil.Tx) (*dcrutil.Tx, error)
 
 	// All remaining outputs pay to the output destinations and amounts tagged
 	// by the ticket purchase.
-	for i, hash160 := range ticketHash160s {
-		scriptFn := txscript.PayToSSRtxPKHDirect
+	params := p.chainParams
+	for i, h160 := range ticketHash160s {
+		var addr stdaddr.StakeAddress
 		if ticketPayKinds[i] { // P2SH
-			scriptFn = txscript.PayToSSRtxSHDirect
+			addr, _ = stdaddr.NewAddressScriptHashV0FromHash(h160, params)
+		} else {
+			addr, _ = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(h160, params)
 		}
-		// Error is checking for a nil hash160, just ignore it.
-		script, _ := scriptFn(hash160)
+
+		_, script := addr.PayRevokeCommitmentScript()
 		revocation.AddTxOut(wire.NewTxOut(revocationValues[i], script))
 	}
 
@@ -739,16 +751,13 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp
 	// Generate associated pay-to-script-hash address and resulting payment
 	// script.
 	pubKeyBytes := signPub.SerializeCompressed()
-	payPubKeyAddr, err := dcrutil.NewAddressSecpPubKey(pubKeyBytes,
+	h160 := stdaddr.Hash160(pubKeyBytes)
+	payAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(h160,
 		chainParams)
 	if err != nil {
 		return nil, nil, err
 	}
-	payAddr := payPubKeyAddr.AddressPubKeyHash()
-	pkScript, err := txscript.PayToAddrScript(payAddr)
-	if err != nil {
-		return nil, nil, err
-	}
+	payScriptVer, payScript := payAddr.PaymentScript()
 
 	// Create a new fake chain and harness bound to it.
 	subsidyCache := standalone.NewSubsidyCache(chainParams)
@@ -761,13 +770,13 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp
 	}
 	var harness *poolHarness
 	harness = &poolHarness{
-		signKey:     keyBytes,
-		sigType:     dcrec.STEcdsaSecp256k1,
-		payAddr:     payAddr,
-		payScript:   pkScript,
-		chainParams: chainParams,
-
-		chain: chain,
+		signKey:      keyBytes,
+		sigType:      dcrec.STEcdsaSecp256k1,
+		payAddr:      payAddr,
+		payScriptVer: payScriptVer,
+		payScript:    payScript,
+		chainParams:  chainParams,
+		chain:        chain,
 		txPool: New(&Config{
 			Policy: Policy{
 				EnableAncestorTracking: true,
@@ -2541,23 +2550,18 @@ func TestHandlesTSpends(t *testing.T) {
 // createTAdd creates a treasury add transaction spending from the given
 // harness output and sending back any outstanding change to the given address.
 func createTAdd(t *testing.T, spend *spendableOutput, payScript, signKey []byte,
-	amount, fee dcrutil.Amount, changeAddr dcrutil.Address) *wire.MsgTx {
+	amount, fee dcrutil.Amount, changeAddr stdaddr.StakeAddress) *wire.MsgTx {
 	t.Helper()
 
 	// Calculate change and generate script to deliver it.
-	var (
-		changeScript []byte
-		err          error
-	)
+	var changeScriptVer uint16
+	var changeScript []byte
 	change := spend.amount - amount - fee
 	if change < 0 {
 		t.Fatalf("negative change %v", change)
 	}
 	if change > 0 {
-		changeScript, err = txscript.PayToSStxChange(changeAddr)
-		if err != nil {
-			t.Fatalf("unable to generate Pay2SStxChange: %v", err)
-		}
+		changeScriptVer, changeScript = changeAddr.StakeChangeScript()
 	}
 
 	// Generate and return the transaction spending from the provided
@@ -2569,12 +2573,12 @@ func createTAdd(t *testing.T, spend *spendableOutput, payScript, signKey []byte,
 		Sequence:         wire.MaxTxInSequenceNum,
 		ValueIn:          int64(spend.amount),
 	})
-	tx.AddTxOut(wire.NewTxOut(int64(amount),
-		[]byte{txscript.OP_TADD}))
+	tx.AddTxOut(wire.NewTxOut(int64(amount), []byte{txscript.OP_TADD}))
 	if len(changeScript) > 0 {
-		tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
+		tx.AddTxOut(newTxOut(int64(change), changeScriptVer, changeScript))
 	}
 
+	var err error
 	tx.TxIn[0].SignatureScript, err = txscript.SignatureScript(tx,
 		0, payScript, txscript.SigHashAll, signKey,
 		dcrec.STEcdsaSecp256k1, true)
@@ -2603,14 +2607,14 @@ func TestHandlesTAdds(t *testing.T) {
 	// Addresses to use in the following TAdds.
 	addPrivKey := secp256k1.NewPrivateKey(new(secp256k1.ModNScalar).SetInt(2))
 	pubKey := addPrivKey.PubKey().SerializeCompressed()
-	pubKeyHash := dcrutil.Hash160(pubKey)
-	addP2pkhAddr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, net,
-		dcrec.STEcdsaSecp256k1)
+	pubKeyHash := stdaddr.Hash160(pubKey)
+	addP2pkhAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pubKeyHash,
+		net)
 	if err != nil {
 		t.Fatal(err)
 	}
 	addP2shScript := []byte{txscript.OP_NOP, txscript.OP_NOP, txscript.OP_TRUE}
-	addP2shAddr, err := dcrutil.NewAddressScriptHash(addP2shScript, net)
+	addP2shAddr, err := stdaddr.NewAddressScriptHashV0(addP2shScript, net)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -2618,7 +2622,7 @@ func TestHandlesTAdds(t *testing.T) {
 	// Helper to create tadds for this test without having to keep passing
 	// repeated parameters.
 	createTAdd := func(spend *spendableOutput, amount, fee dcrutil.Amount,
-		changeAddr dcrutil.Address) *dcrutil.Tx {
+		changeAddr stdaddr.StakeAddress) *dcrutil.Tx {
 		t.Helper()
 		return dcrutil.NewTx(createTAdd(t, spend, harness.payScript,
 			harness.signKey, amount, fee, changeAddr))
diff --git a/internal/mempool/policy_test.go b/internal/mempool/policy_test.go
index cf5285bd..25393355 100644
--- a/internal/mempool/policy_test.go
+++ b/internal/mempool/policy_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2016 The btcsuite developers
-// Copyright (c) 2016-2020 The Decred developers
+// Copyright (c) 2016-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -14,10 +14,10 @@ import (
 	"github.com/decred/dcrd/blockchain/stake/v4"
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
-	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -326,18 +326,15 @@ func TestCheckTransactionStandard(t *testing.T) {
 		SignatureScript:  dummySigScript,
 	}
 	addrHash := [20]byte{0x01}
-	addr, err := dcrutil.NewAddressPubKeyHash(addrHash[:],
-		chaincfg.RegNetParams(), dcrec.STEcdsaSecp256k1)
+	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(addrHash[:],
+		chaincfg.RegNetParams())
 	if err != nil {
 		t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err)
 	}
-	dummyPkScript, err := txscript.PayToAddrScript(addr)
-	if err != nil {
-		t.Fatalf("PayToAddrScript: unexpected error: %v", err)
-	}
+	dummyPkScriptVer, dummyPkScript := addr.PaymentScript()
 	dummyTxOut := wire.TxOut{
 		Value:    100000000, // 1 BTC
-		Version:  0,
+		Version:  dummyPkScriptVer,
 		PkScript: dummyPkScript,
 	}
 
diff --git a/internal/mining/mining_harness_test.go b/internal/mining/mining_harness_test.go
index c92697ca..9167bc46 100644
--- a/internal/mining/mining_harness_test.go
+++ b/internal/mining/mining_harness_test.go
@@ -14,13 +14,13 @@ import (
 	"github.com/decred/dcrd/blockchain/stake/v4"
 	"github.com/decred/dcrd/blockchain/standalone/v2"
 	"github.com/decred/dcrd/blockchain/v4"
-	"github.com/decred/dcrd/blockchain/v4/chaingen"
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -918,10 +918,14 @@ type miningHarness struct {
 	//
 	// payAddr is the p2sh address for the signing key and is used for the
 	// payment address throughout the tests.
-	signKey   []byte
-	sigType   dcrec.SignatureType
-	payAddr   dcrutil.Address
-	payScript []byte
+	//
+	// payScriptVer and payScript are the script version and script to pay the
+	// aforementioned payAddr.
+	signKey      []byte
+	sigType      dcrec.SignatureType
+	payAddr      stdaddr.StakeAddress
+	payScriptVer uint16
+	payScript    []byte
 
 	generator *BlkTmplGenerator
 }
@@ -929,13 +933,13 @@ type miningHarness struct {
 // GetScript is the mining harness' implementation of the ScriptDB interface.
 // It returns the mining harness' payment redeem script for any address passed
 // in.
-func (m *miningHarness) GetScript(addr dcrutil.Address) ([]byte, error) {
+func (m *miningHarness) GetScript(addr stdaddr.Address) ([]byte, error) {
 	return m.payScript, nil
 }
 
 // GetKey is the mining harness' implementation of the KeyDB interface. It
 // returns the mining harness' signature key for any address passed in.
-func (m *miningHarness) GetKey(addr dcrutil.Address) ([]byte, dcrec.SignatureType, bool, error) {
+func (m *miningHarness) GetKey(addr stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
 	return m.signKey, m.sigType, true, nil
 }
 
@@ -971,10 +975,7 @@ func (m *miningHarness) CreateCoinbaseTx(blockHeight int64, numOutputs uint32) (
 		if i == numOutputs-1 {
 			amount = amountPerOutput + remainder
 		}
-		tx.AddTxOut(&wire.TxOut{
-			PkScript: m.payScript,
-			Value:    amount,
-		})
+		tx.AddTxOut(newTxOut(amount, m.payScriptVer, m.payScript))
 	}
 
 	return dcrutil.NewTx(tx), nil
@@ -998,10 +999,8 @@ func (m *miningHarness) CreateTxChain(firstOutput spendableOutput, numTxns uint3
 			Sequence:         wire.MaxTxInSequenceNum,
 			ValueIn:          int64(spendableAmount),
 		})
-		tx.AddTxOut(&wire.TxOut{
-			PkScript: m.payScript,
-			Value:    int64(spendableAmount),
-		})
+		tx.AddTxOut(newTxOut(int64(spendableAmount), m.payScriptVer,
+			m.payScript))
 
 		// Sign the new transaction.
 		sigScript, err := txscript.SignatureScript(tx, 0,
@@ -1067,10 +1066,7 @@ func (m *miningHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint
 		if i == numOutputs-1 {
 			amount += remainder
 		}
-		tx.AddTxOut(&wire.TxOut{
-			PkScript: m.payScript,
-			Value:    amount,
-		})
+		tx.AddTxOut(newTxOut(amount, m.payScriptVer, m.payScript))
 	}
 
 	// Perform any transaction munging just before signing.
@@ -1092,25 +1088,27 @@ func (m *miningHarness) CreateSignedTx(inputs []spendableOutput, numOutputs uint
 	return dcrutil.NewTx(tx), nil
 }
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 // CreateTicketPurchase creates a ticket purchase spending the first output of
 // the provided transaction.
 func (m *miningHarness) CreateTicketPurchase(sourceTx *dcrutil.Tx, cost int64) (*dcrutil.Tx, error) {
-	ticketFee := dcrutil.Amount(singleInputTicketSize)
-	ticketPrice := dcrutil.Amount(cost)
+	ticketFee := singleInputTicketSize
+	ticketPrice := cost
 
-	// Generate the p2sh, commitment and change scripts of the ticket.
-	pkScript, err := txscript.PayToSStx(m.payAddr)
-	if err != nil {
-		return nil, err
-	}
-	commitScript := chaingen.PurchaseCommitmentScript(m.payAddr,
+	// Generate the voting rights, commitment, and change scripts of the ticket.
+	voteScriptVer, voteScript := m.payAddr.VotingRightsScript()
+	commitScriptVer, commitScript := m.payAddr.RewardCommitmentScript(
 		ticketPrice+ticketFee, 0, ticketPrice)
-	change := dcrutil.Amount(sourceTx.MsgTx().TxOut[0].Value) -
-		ticketPrice - ticketFee
-	changeScript, err := txscript.PayToSStxChange(m.payAddr)
-	if err != nil {
-		return nil, err
-	}
+	change := sourceTx.MsgTx().TxOut[0].Value - ticketPrice - ticketFee
+	changeScriptVer, changeScript := m.payAddr.StakeChangeScript()
 
 	// Generate the ticket purchase.
 	tx := wire.NewMsgTx()
@@ -1125,9 +1123,9 @@ func (m *miningHarness) CreateTicketPurchase(sourceTx *dcrutil.Tx, cost int64) (
 		BlockHeight: uint32(m.generator.cfg.BestSnapshot().Height),
 	})
 
-	tx.AddTxOut(wire.NewTxOut(int64(ticketPrice), pkScript))
-	tx.AddTxOut(wire.NewTxOut(0, commitScript))
-	tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
+	tx.AddTxOut(newTxOut(ticketPrice, voteScriptVer, voteScript))
+	tx.AddTxOut(newTxOut(0, commitScriptVer, commitScript))
+	tx.AddTxOut(newTxOut(change, changeScriptVer, changeScript))
 
 	// Sign the ticket purchase.
 	sigScript, err := txscript.SignatureScript(tx, 0,
@@ -1195,14 +1193,17 @@ func (m *miningHarness) CreateVote(ticket *dcrutil.Tx, mungers ...func(*wire.Msg
 	}
 	vote.AddTxOut(wire.NewTxOut(0, voteScript))
 
-	// Create P2SH scripts for the ticket outputs.
-	for i, hash160 := range ticketHash160s {
-		scriptFn := txscript.PayToSSGenPKHDirect
+	// Create payment scripts for the ticket commitments.
+	params := m.chainParams
+	for i, h160 := range ticketHash160s {
+		var addr stdaddr.StakeAddress
 		if ticketPayKinds[i] { // P2SH
-			scriptFn = txscript.PayToSSGenSHDirect
+			addr, _ = stdaddr.NewAddressScriptHashV0FromHash(h160, params)
+		} else {
+			addr, _ = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(h160, params)
 		}
-		// Error is checking for a nil hash160, just ignore it.
-		script, _ := scriptFn(hash160)
+
+		_, script := addr.PayVoteCommitmentScript()
 		vote.AddTxOut(wire.NewTxOut(voteRewardValues[i], script))
 	}
 
@@ -1214,14 +1215,14 @@ func (m *miningHarness) CreateVote(ticket *dcrutil.Tx, mungers ...func(*wire.Msg
 	// Sign the input.
 	inputToSign := 1
 	redeemTicketScript := ticket.MsgTx().TxOut[0].PkScript
-	signedScript, err := txscript.SignTxOutput(m.chainParams, vote,
-		inputToSign, redeemTicketScript, txscript.SigHashAll, m, m,
+	signedScript, err := txscript.SignTxOutput(params, vote, inputToSign,
+		redeemTicketScript, txscript.SigHashAll, m, m,
 		vote.TxIn[inputToSign].SignatureScript, m.chain.isTreasuryAgendaActive)
 	if err != nil {
 		return nil, err
 	}
 
-	vote.TxIn[0].SignatureScript = m.chainParams.StakeBaseSigScript
+	vote.TxIn[0].SignatureScript = params.StakeBaseSigScript
 	vote.TxIn[1].SignatureScript = signedScript
 
 	return dcrutil.NewTx(vote), nil
@@ -1272,15 +1273,13 @@ func newMiningHarness(chainParams *chaincfg.Params) (*miningHarness, []spendable
 	// Generate associated pay-to-script-hash address and resulting payment
 	// script.
 	pubKeyBytes := signPub.SerializeCompressed()
-	payPubKeyAddr, err := dcrutil.NewAddressSecpPubKey(pubKeyBytes, chainParams)
-	if err != nil {
-		return nil, nil, err
-	}
-	payAddr := payPubKeyAddr.AddressPubKeyHash()
-	pkScript, err := txscript.PayToAddrScript(payAddr)
+	h160 := stdaddr.Hash160(pubKeyBytes)
+	payAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(h160,
+		chainParams)
 	if err != nil {
 		return nil, nil, err
 	}
+	payScriptVer, payScript := payAddr.PaymentScript()
 
 	// Create a SigCache instance.
 	sigCache, err := txscript.NewSigCache(1000)
@@ -1367,7 +1366,8 @@ func newMiningHarness(chainParams *chaincfg.Params) (*miningHarness, []spendable
 		signKey:      keyBytes,
 		sigType:      dcrec.STEcdsaSecp256k1,
 		payAddr:      payAddr,
-		payScript:    pkScript,
+		payScriptVer: payScriptVer,
+		payScript:    payScript,
 		generator: NewBlkTmplGenerator(&Config{
 			Policy:                     policy,
 			TxSource:                   txSource,
diff --git a/internal/mining/mining_test.go b/internal/mining/mining_test.go
index 741e6e08..4b04e67a 100644
--- a/internal/mining/mining_test.go
+++ b/internal/mining/mining_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Decred developers
+// Copyright (c) 2020-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -13,6 +13,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -28,7 +29,7 @@ func TestNewBlockTemplateBasicErrorScenarios(t *testing.T) {
 	}
 
 	// Create a test address for use in template generation.
-	address, err := dcrutil.DecodeAddress("Dsi8CRt85xYyempXs7ZPL1rBxvDdAGZmgsg",
+	address, err := stdaddr.DecodeAddress("Dsi8CRt85xYyempXs7ZPL1rBxvDdAGZmgsg",
 		harness.chainParams)
 	if err != nil {
 		t.Fatalf("error decoding address: %v", err)
@@ -70,7 +71,7 @@ func TestNewBlockTemplate(t *testing.T) {
 	}
 
 	// Create a test address for use in template generation.
-	address, err := dcrutil.DecodeAddress("Dsi8CRt85xYyempXs7ZPL1rBxvDdAGZmgsg",
+	address, err := stdaddr.DecodeAddress("Dsi8CRt85xYyempXs7ZPL1rBxvDdAGZmgsg",
 		harness.chainParams)
 	if err != nil {
 		t.Fatalf("error decoding address: %v", err)
diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go
index ec92c9e7..8e58b7b7 100644
--- a/internal/rpcserver/rpcserverhandlers_test.go
+++ b/internal/rpcserver/rpcserverhandlers_test.go
@@ -44,6 +44,7 @@ import (
 	"github.com/decred/dcrd/peer/v3"
 	"github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -392,7 +393,7 @@ func (c *testRPCChain) TicketPoolValue() (dcrutil.Amount, error) {
 
 // TicketsWithAddress returns a mocked slice of ticket hashes that are currently
 // live corresponding to the given address.
-func (c *testRPCChain) TicketsWithAddress(address dcrutil.Address, isTreasuryEnabled bool) ([]chainhash.Hash, error) {
+func (c *testRPCChain) TicketsWithAddress(address stdaddr.Address, isTreasuryEnabled bool) ([]chainhash.Hash, error) {
 	return c.ticketsWithAddress, c.ticketsWithAddressErr
 }
 
@@ -547,13 +548,13 @@ type testExistsAddresser struct {
 
 // ExistsAddress returns a mocked bool representing whether or not an address
 // has been seen before.
-func (e *testExistsAddresser) ExistsAddress(addr dcrutil.Address) (bool, error) {
+func (e *testExistsAddresser) ExistsAddress(addr stdaddr.Address) (bool, error) {
 	return e.existsAddress, e.existsAddressErr
 }
 
 // ExistsAddresses returns a mocked bool slice representing whether or not each
 // address in a slice of addresses has been seen before.
-func (e *testExistsAddresser) ExistsAddresses(addrs []dcrutil.Address) ([]bool, error) {
+func (e *testExistsAddresser) ExistsAddresses(addrs []stdaddr.Address) ([]bool, error) {
 	return e.existsAddresses, e.existsAddressesErr
 }
 
@@ -569,14 +570,14 @@ type testAddrIndexer struct {
 // EntriesForAddress returns a mocked slice of indexers.TxIndexEntry that
 // involve the given address.
 func (a *testAddrIndexer) EntriesForAddress(dbTx database.Tx,
-	addr dcrutil.Address, numToSkip, numRequested uint32, reverse bool) (
+	addr stdaddr.Address, numToSkip, numRequested uint32, reverse bool) (
 	[]indexers.TxIndexEntry, uint32, error) {
 	return a.entriesForAddress, a.entriesForAddressSkipped, a.entriesForAddressErr
 }
 
 // UnconfirmedTxnsForAddress returns a mocked slice of transactions that are
 // currently in the unconfirmed (memory-only) address index.
-func (a *testAddrIndexer) UnconfirmedTxnsForAddress(addr dcrutil.Address) []*dcrutil.Tx {
+func (a *testAddrIndexer) UnconfirmedTxnsForAddress(addr stdaddr.Address) []*dcrutil.Tx {
 	return a.unconfirmedTxnsForAddress
 }
 
@@ -940,7 +941,7 @@ func (f *testFiltererV2) FilterByBlockHash(hash *chainhash.Hash) (*gcs.FilterV2,
 // testMiningState provides a mock mining state.
 type testMiningState struct {
 	allowUnsyncedMining bool
-	miningAddrs         []dcrutil.Address
+	miningAddrs         []stdaddr.Address
 	workState           *workState
 }
 
@@ -1317,7 +1318,7 @@ type rpcTest struct {
 	mockLogManager        *testLogManager
 	mockFiltererV2        *testFiltererV2
 	mockTxMempooler       *testTxMempooler
-	mockMiningAddrs       []dcrutil.Address
+	mockMiningAddrs       []stdaddr.Address
 	mockHelpCacher        *testHelpCacher
 	result                interface{}
 	wantErr               bool
@@ -1968,7 +1969,7 @@ func TestHandleCreateRawSStx(t *testing.T) {
 			COuts:  defaultCmdCOuts,
 		},
 		wantErr: true,
-		errCode: dcrjson.ErrRPCInternal.Code,
+		errCode: dcrjson.ErrRPCInvalidAddressOrKey,
 	}, {
 		name:    "handleCreateRawSStx: change amount greater than input amount",
 		handler: handleCreateRawSStx,
@@ -2028,7 +2029,7 @@ func TestHandleCreateRawSStx(t *testing.T) {
 			}},
 		},
 		wantErr: true,
-		errCode: dcrjson.ErrRPCInternal.Code,
+		errCode: dcrjson.ErrRPCInvalidAddressOrKey,
 	}, {
 		name:    "handleCreateRawSStx: invalid change amount > dcrutil.MaxAmount",
 		handler: handleCreateRawSStx,
@@ -2108,7 +2109,7 @@ func TestHandleCreateRawSStx(t *testing.T) {
 			}},
 		},
 		wantErr: true,
-		errCode: dcrjson.ErrRPCInternal.Code,
+		errCode: dcrjson.ErrRPCInvalidAddressOrKey,
 	}})
 }
 
@@ -3312,11 +3313,11 @@ func TestHandleGenerate(t *testing.T) {
 	hashStrTwo := "00000000000000001a1ec2becd0dd90bfbd0c65f42fdaf608dd9ceac2a3aee1d"
 	generatedBlocks := []*chainhash.Hash{mustParseHash(hashStrOne), mustParseHash(hashStrTwo)}
 	res := []string{hashStrOne, hashStrTwo}
-	miningAddr, err := dcrutil.DecodeAddress("DcurAwesomeAddressmqDctW5wJCW1Cn2MF", defaultChainParams)
+	miningAddr, err := stdaddr.DecodeAddress("DcurAwesomeAddressmqDctW5wJCW1Cn2MF", defaultChainParams)
 	if err != nil {
 		t.Fatalf("[DecodeAddress] unexpected error: %v", err)
 	}
-	miningAddrs := []dcrutil.Address{miningAddr}
+	miningAddrs := []stdaddr.Address{miningAddr}
 	chainParams := cloneParams(defaultChainParams)
 	chainParams.GenerateSupported = true
 	cpu := defaultMockCPUMiner()
@@ -5994,14 +5995,14 @@ func TestHandleGetWork(t *testing.T) {
 	buf.Write(submissionB[240:])
 	invalidPOWSub := buf.String()
 
-	miningaddr, err := dcrutil.DecodeAddress("DsRM6qwzT3r85evKvDBJBviTgYcaLKL4ipD", defaultChainParams)
+	miningaddr, err := stdaddr.DecodeAddress("DsRM6qwzT3r85evKvDBJBviTgYcaLKL4ipD", defaultChainParams)
 	if err != nil {
 		t.Fatalf("[DecodeAddress] unexpected error: %v", err)
 	}
 
 	mine := func() *testMiningState {
 		ms := defaultMockMiningState()
-		ms.miningAddrs = []dcrutil.Address{miningaddr}
+		ms.miningAddrs = []stdaddr.Address{miningaddr}
 		return ms
 	}
 
@@ -6127,7 +6128,7 @@ func TestHandleGetWork(t *testing.T) {
 		},
 		mockMiningState: func() *testMiningState {
 			ms := defaultMockMiningState()
-			ms.miningAddrs = []dcrutil.Address{miningaddr}
+			ms.miningAddrs = []stdaddr.Address{miningaddr}
 			ms.workState = newWorkState()
 			return ms
 		}(),
@@ -6209,7 +6210,7 @@ func TestHandleSetGenerate(t *testing.T) {
 
 	procLimit := 2
 	zeroProcLimit := 0
-	miningaddr, err := dcrutil.DecodeAddress("DsRM6qwzT3r85evKvDBJBviTgYcaLKL4ipD", defaultChainParams)
+	miningaddr, err := stdaddr.DecodeAddress("DsRM6qwzT3r85evKvDBJBviTgYcaLKL4ipD", defaultChainParams)
 	if err != nil {
 		t.Fatalf("[DecodeAddress] unexpected error: %v", err)
 	}
@@ -6232,7 +6233,7 @@ func TestHandleSetGenerate(t *testing.T) {
 		},
 		mockMiningState: func() *testMiningState {
 			ms := defaultMockMiningState()
-			ms.miningAddrs = []dcrutil.Address{miningaddr}
+			ms.miningAddrs = []stdaddr.Address{miningaddr}
 			return ms
 		}(),
 	}, {
diff --git a/internal/rpcserver/treasury_test.go b/internal/rpcserver/treasury_test.go
index 3486cb08..95ea68b6 100644
--- a/internal/rpcserver/treasury_test.go
+++ b/internal/rpcserver/treasury_test.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 The Decred developers
+// Copyright (c) 2020-2021 The Decred developers
 
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
@@ -24,6 +24,7 @@ import (
 	"github.com/decred/dcrd/rpcclient/v7"
 	"github.com/decred/dcrd/rpctest"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -39,7 +40,7 @@ func timeoutCtx(t testing.TB, timeout time.Duration) context.Context {
 }
 
 type tspendPayout struct {
-	address dcrutil.Address
+	address stdaddr.Address
 	amount  dcrutil.Amount
 }
 
@@ -72,14 +73,9 @@ func createTSpend(privKey []byte, payouts []tspendPayout, fee dcrutil.Amount, ex
 
 	// OP_TGEN
 	for _, v := range payouts {
-		script, err := txscript.PayToAddrScript(v.address)
-		if err != nil {
-			panic(err)
-		}
-		tgenScript := make([]byte, len(script)+1)
-		tgenScript[0] = txscript.OP_TGEN
-		copy(tgenScript[1:], script)
-		msgTx.AddTxOut(wire.NewTxOut(int64(v.amount), tgenScript))
+		addr := v.address.(stdaddr.StakeAddress)
+		genScriptVer, genScript := addr.PayFromTreasuryScript()
+		msgTx.AddTxOut(newTxOut(int64(v.amount), genScriptVer, genScript))
 	}
 
 	// Treasury spend transactions have no inputs since the funds are
@@ -107,7 +103,7 @@ func createTSpend(privKey []byte, payouts []tspendPayout, fee dcrutil.Amount, ex
 
 func createTAdd(t testing.TB, privKey []byte, prevOut *wire.OutPoint, pkScript []byte,
 	amountIn, amountOut, fee dcrutil.Amount,
-	changeAddr dcrutil.Address) *wire.MsgTx {
+	changeAddr stdaddr.StakeAddress) *wire.MsgTx {
 
 	tx := wire.NewMsgTx()
 	tx.AddTxIn(&wire.TxIn{
@@ -116,14 +112,11 @@ func createTAdd(t testing.TB, privKey []byte, prevOut *wire.OutPoint, pkScript [
 		ValueIn:          int64(amountIn),
 	})
 
-	changeScript, err := txscript.PayToSStxChange(changeAddr)
-	if err != nil {
-		t.Fatal(err)
-	}
+	changeScriptVer, changeScript := changeAddr.StakeChangeScript()
 	changeAmount := amountIn - amountOut - fee
 	tx.AddTxOut(wire.NewTxOut(int64(amountOut), []byte{txscript.OP_TADD}))
 	if changeAmount > 0 {
-		tx.AddTxOut(wire.NewTxOut(int64(changeAmount), changeScript))
+		tx.AddTxOut(newTxOut(int64(changeAmount), changeScriptVer, changeScript))
 	}
 	tx.Version = wire.TxVersionTreasury
 
@@ -270,21 +263,18 @@ func TestTreasury(t *testing.T) {
 	// Create a privkey and p2pkh addr we control for use in the tests.
 	privKey := secp256k1.NewPrivateKey(new(secp256k1.ModNScalar).SetInt(1))
 	pubKey := privKey.PubKey().SerializeCompressed()
-	pubKeyHash := dcrutil.Hash160(pubKey)
-	p2pkhAddr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, net,
-		dcrec.STEcdsaSecp256k1)
-	if err != nil {
-		t.Fatal(err)
-	}
-	p2pkhScript, err := txscript.PayToAddrScript(p2pkhAddr)
+	pubKeyHash := stdaddr.Hash160(pubKey)
+	p2pkhAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pubKeyHash,
+		net)
 	if err != nil {
 		t.Fatal(err)
 	}
+	p2pkhScriptVer, p2pkhScript := p2pkhAddr.PaymentScript()
 
 	// Generate a p2sh script and addr we control for use in the tests.
-	p2shScript := []byte{txscript.OP_TRUE}
+	redeemScript := []byte{txscript.OP_TRUE}
 	p2shSigScript := []byte{txscript.OP_DATA_1, txscript.OP_TRUE}
-	p2shAddr, err := dcrutil.NewAddressScriptHash(p2shScript, net)
+	p2shAddr, err := stdaddr.NewAddressScriptHashV0(redeemScript, net)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -294,7 +284,7 @@ func TestTreasury(t *testing.T) {
 	taddInAmt := dcrutil.Amount(1e8) // 1 DCR
 	taddPrevOuts := make([]*wire.OutPoint, nbTAddPrevOuts)
 	for i := 0; i < nbTAddPrevOuts; i++ {
-		txOut := &wire.TxOut{PkScript: p2pkhScript, Value: int64(taddInAmt)}
+		txOut := newTxOut(int64(taddInAmt), p2pkhScriptVer, p2pkhScript)
 		txHash, err := hn.SendOutputs([]*wire.TxOut{txOut}, defaultFeeRate)
 		if err != nil {
 			t.Fatal(err)
diff --git a/rpctest/memwallet.go b/rpctest/memwallet.go
index e31d5927..594ead34 100644
--- a/rpctest/memwallet.go
+++ b/rpctest/memwallet.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2016-2017 The btcsuite developers
-// Copyright (c) 2017-2020 The Decred developers
+// Copyright (c) 2017-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -22,6 +22,7 @@ import (
 	"github.com/decred/dcrd/hdkeychain/v3"
 	"github.com/decred/dcrd/rpcclient/v7"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -82,7 +83,7 @@ type undoEntry struct {
 // hierarchy which promotes reproducibility between harness test runs.
 type memWallet struct {
 	coinbaseKey  *secp256k1.PrivateKey
-	coinbaseAddr dcrutil.Address
+	coinbaseAddr stdaddr.Address
 
 	// hdRoot is the root master private key for the wallet.
 	hdRoot *hdkeychain.ExtendedKey
@@ -96,7 +97,7 @@ type memWallet struct {
 
 	// addrs tracks all addresses belonging to the wallet. The addresses
 	// are indexed by their keypath from the hdRoot.
-	addrs map[uint32]dcrutil.Address
+	addrs map[uint32]stdaddr.Address
 
 	// utxos is the set of utxos spendable by the wallet.
 	utxos map[wire.OutPoint]*utxo
@@ -152,7 +153,7 @@ func newMemWallet(t *testing.T, net *chaincfg.Params, harnessID uint32) (*memWal
 
 	// Track the coinbase generation address to ensure we properly track
 	// newly generated coins we can spend.
-	addrs := make(map[uint32]dcrutil.Address)
+	addrs := make(map[uint32]stdaddr.Address)
 	addrs[0] = coinbaseAddr
 
 	return &memWallet{
@@ -280,8 +281,8 @@ func (m *memWallet) evalOutputs(outputs []*wire.TxOut, txHash *chainhash.Hash, i
 		// Scan all the addresses we currently control to see if the
 		// output is paying to us.
 		for keyIndex, addr := range m.addrs {
-			pkHash := addr.ScriptAddress()
-			if !bytes.Contains(pkScript, pkHash) {
+			pkHash := addr.(stdaddr.Hash160er).Hash160()
+			if !bytes.Contains(pkScript, pkHash[:]) {
 				continue
 			}
 
@@ -355,7 +356,7 @@ func (m *memWallet) UnwindBlock(header []byte) {
 // newAddress returns a new address from the wallet's hd key chain.  It also
 // loads the address into the RPC client's transaction filter to ensure any
 // transactions that involve it are delivered via the notifications.
-func (m *memWallet) newAddress() (dcrutil.Address, error) {
+func (m *memWallet) newAddress() (stdaddr.Address, error) {
 	tracef(m.t, "memwallet.newAddress")
 	defer tracef(m.t, "memwallet.newAddress exit")
 
@@ -376,7 +377,7 @@ func (m *memWallet) newAddress() (dcrutil.Address, error) {
 	}
 
 	err = m.rpc.LoadTxFilter(context.Background(), false,
-		[]dcrutil.Address{addr}, nil)
+		[]stdaddr.Address{addr}, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -391,7 +392,7 @@ func (m *memWallet) newAddress() (dcrutil.Address, error) {
 // NewAddress returns a fresh address spendable by the wallet.
 //
 // This function is safe for concurrent access.
-func (m *memWallet) NewAddress() (dcrutil.Address, error) {
+func (m *memWallet) NewAddress() (stdaddr.Address, error) {
 	m.Lock()
 	defer m.Unlock()
 
@@ -451,12 +452,10 @@ func (m *memWallet) fundTx(tx *wire.MsgTx, amt dcrutil.Amount, feeRate dcrutil.A
 			if err != nil {
 				return err
 			}
-			pkScript, err := txscript.PayToAddrScript(addr)
-			if err != nil {
-				return err
-			}
+			pkScriptVer, pkScript := addr.PaymentScript()
 			changeOutput := &wire.TxOut{
 				Value:    int64(changeVal),
+				Version:  pkScriptVer,
 				PkScript: pkScript,
 			}
 			tx.AddTxOut(changeOutput)
@@ -598,10 +597,11 @@ func (m *memWallet) ConfirmedBalance() dcrutil.Amount {
 }
 
 // keyToAddr maps the passed private to corresponding p2pkh address.
-func keyToAddr(serializedPrivKey []byte, net *chaincfg.Params) (dcrutil.Address, error) {
+func keyToAddr(serializedPrivKey []byte, net *chaincfg.Params) (stdaddr.Address, error) {
 	key := secp256k1.PrivKeyFromBytes(serializedPrivKey)
 	serializedKey := key.PubKey().SerializeCompressed()
-	pubKeyAddr, err := dcrutil.NewAddressSecpPubKey(serializedKey, net)
+	pubKeyAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(
+		serializedKey, net)
 	if err != nil {
 		return nil, err
 	}
diff --git a/rpctest/rpc_harness.go b/rpctest/rpc_harness.go
index 4e356db7..51017e5a 100644
--- a/rpctest/rpc_harness.go
+++ b/rpctest/rpc_harness.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2016 The btcsuite developers
-// Copyright (c) 2017-2020 The Decred developers
+// Copyright (c) 2017-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -21,6 +21,7 @@ import (
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/rpcclient/v7"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -259,7 +260,7 @@ func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error {
 
 	// Filter transactions that pay to the coinbase associated with the
 	// wallet.
-	filterAddrs := []dcrutil.Address{h.wallet.coinbaseAddr}
+	filterAddrs := []stdaddr.Address{h.wallet.coinbaseAddr}
 	if err := h.Node.LoadTxFilter(ctx, true, filterAddrs, nil); err != nil {
 		return err
 	}
@@ -367,7 +368,7 @@ func (h *Harness) connectRPCClient() error {
 // wallet.
 //
 // This function is safe for concurrent access.
-func (h *Harness) NewAddress() (dcrutil.Address, error) {
+func (h *Harness) NewAddress() (stdaddr.Address, error) {
 	return h.wallet.NewAddress()
 }
 
diff --git a/rpctest/rpc_harness_test.go b/rpctest/rpc_harness_test.go
index 0a923636..6b642672 100644
--- a/rpctest/rpc_harness_test.go
+++ b/rpctest/rpc_harness_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2016 The btcsuite developers
-// Copyright (c) 2017-2020 The Decred developers
+// Copyright (c) 2017-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -10,6 +10,7 @@ package rpctest
 
 import (
 	"context"
+	"fmt"
 	"os"
 	"testing"
 	"time"
@@ -18,7 +19,6 @@ import (
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
-	"github.com/decred/dcrd/txscript/v4"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -39,11 +39,8 @@ func testSendOutputs(ctx context.Context, r *Harness, t *testing.T) {
 
 		// Next, send amt to this address, spending from one of our
 		// mature coinbase outputs.
-		addrScript, err := txscript.PayToAddrScript(addr)
-		if err != nil {
-			t.Fatalf("unable to generate pkscript to addr: %v", err)
-		}
-		output := wire.NewTxOut(int64(amt), addrScript)
+		addrScriptVer, addrScript := addr.PaymentScript()
+		output := newTxOut(int64(amt), addrScriptVer, addrScript)
 		txid, err := r.SendOutputs([]*wire.TxOut{output}, 10)
 		if err != nil {
 			t.Fatalf("coinbase spend failed: %v", err)
@@ -393,11 +390,11 @@ func testJoinMempools(ctx context.Context, r *Harness, t *testing.T) {
 	if err != nil {
 		t.Fatalf("unable to get new address: %v", err)
 	}
-	addrScript, err := txscript.PayToAddrScript(addr)
+	addrScriptVer, addrScript := addr.PaymentScript()
 	if err != nil {
 		t.Fatalf("unable to generate pkscript to addr: %v", err)
 	}
-	output := wire.NewTxOut(5e8, addrScript)
+	output := newTxOut(5e8, addrScriptVer, addrScript)
 	testTx, err := r.CreateTransaction([]*wire.TxOut{output}, 10)
 	if err != nil {
 		t.Fatalf("coinbase spend failed: %v", err)
@@ -408,39 +405,49 @@ func testJoinMempools(ctx context.Context, r *Harness, t *testing.T) {
 
 	// Wait until the transaction shows up to ensure the two mempools are
 	// not the same.
-	harnessSynced := make(chan struct{})
+	harnessSynced := make(chan error)
 	go func() {
 		for {
 			poolHashes, err := r.Node.GetRawMempool(ctx, dcrdtypes.GRMAll)
 			if err != nil {
-				t.Fatalf("failed to retrieve harness mempool: %v", err)
+				err = fmt.Errorf("failed to retrieve harness mempool: %w", err)
+				harnessSynced <- err
+				return
 			}
 			if len(poolHashes) > 0 {
 				break
 			}
 			time.Sleep(time.Millisecond * 100)
 		}
-		harnessSynced <- struct{}{}
+		harnessSynced <- nil
 	}()
 
 	select {
-	case <-harnessSynced:
+	case err := <-harnessSynced:
+		if err != nil {
+			t.Fatal(err)
+		}
 	case <-time.After(time.Minute):
-		t.Fatalf("harness node never received transaction")
+		t.Fatal("harness node never received transaction")
 	}
 
 	// This select case should fall through to the default as the goroutine
 	// should be blocked on the JoinNodes call.
-	poolsSynced := make(chan struct{})
+	poolsSynced := make(chan error)
 	go func() {
 		if err := JoinNodes(nodeSlice, Mempools); err != nil {
-			t.Fatalf("unable to join node on mempools: %v", err)
+			err = fmt.Errorf("unable to join node on mempools: %w", err)
+			poolsSynced <- err
+			return
 		}
-		poolsSynced <- struct{}{}
+		poolsSynced <- nil
 	}()
 	select {
-	case <-poolsSynced:
-		t.Fatalf("mempools detected as synced yet harness has a new tx")
+	case err := <-poolsSynced:
+		if err != nil {
+			t.Fatal(err)
+		}
+		t.Fatal("mempools detected as synced yet harness has a new tx")
 	default:
 	}
 
@@ -464,10 +471,12 @@ func testJoinMempools(ctx context.Context, r *Harness, t *testing.T) {
 	// channel. The send should immediately succeed. In order to avoid the
 	// test hanging indefinitely, a 1 minute timeout is in place.
 	select {
-	case <-poolsSynced:
-		// fall through
+	case err := <-poolsSynced:
+		if err != nil {
+			t.Fatal(err)
+		}
 	case <-time.After(time.Minute):
-		t.Fatalf("mempools never detected as synced")
+		t.Fatal("mempools never detected as synced")
 	}
 }
 
@@ -487,18 +496,22 @@ func testJoinBlocks(_ context.Context, r *Harness, t *testing.T) {
 	defer harness.TearDown()
 
 	nodeSlice := []*Harness{r, harness}
-	blocksSynced := make(chan struct{})
+	blocksSynced := make(chan error)
 	go func() {
 		if err := JoinNodes(nodeSlice, Blocks); err != nil {
-			t.Fatalf("unable to join node on blocks: %v", err)
+			blocksSynced <- fmt.Errorf("unable to join node on blocks: %w", err)
+			return
 		}
-		blocksSynced <- struct{}{}
+		blocksSynced <- nil
 	}()
 
 	// This select case should fall through to the default as the goroutine
 	// should be blocked on the JoinNodes calls.
 	select {
-	case <-blocksSynced:
+	case err := <-blocksSynced:
+		if err != nil {
+			t.Fatal(err)
+		}
 		t.Fatalf("blocks detected as synced yet local harness is behind")
 	default:
 	}
@@ -514,7 +527,10 @@ func testJoinBlocks(_ context.Context, r *Harness, t *testing.T) {
 	// channel. The send should immediately succeed. In order to avoid the
 	// test hanging indefinitely, a 1 minute timeout is in place.
 	select {
-	case <-blocksSynced:
+	case err := <-blocksSynced:
+		if err != nil {
+			t.Fatal(err)
+		}
 	case <-time.After(time.Minute):
 		t.Fatalf("blocks never detected as synced")
 	}
@@ -576,12 +592,9 @@ func testMemWalletLockedOutputs(_ context.Context, r *Harness, t *testing.T) {
 	if err != nil {
 		t.Fatalf("unable to generate new address: %v", err)
 	}
-	pkScript, err := txscript.PayToAddrScript(addr)
-	if err != nil {
-		t.Fatalf("unable to create script: %v", err)
-	}
+	pkScriptVer, pkScript := addr.PaymentScript()
 	outputAmt := dcrutil.Amount(50 * dcrutil.AtomsPerCoin)
-	output := wire.NewTxOut(int64(outputAmt), pkScript)
+	output := newTxOut(int64(outputAmt), pkScriptVer, pkScript)
 	tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10)
 	if err != nil {
 		t.Fatalf("unable to create transaction: %v", err)
@@ -647,7 +660,7 @@ func TestHarness(t *testing.T) {
 	// Current tip should be at a height of numMatureOutputs plus the
 	// required number of blocks for coinbase maturity plus an additional
 	// block for the premine block.
-	ctx := context.TODO()
+	ctx := context.Background()
 	nodeInfo, err := mainHarness.Node.GetInfo(ctx)
 	if err != nil {
 		t.Fatalf("unable to execute getinfo on node: %v", err)
diff --git a/rpctest/votingwallet.go b/rpctest/votingwallet.go
index 6fb0c3bd..46d997f6 100644
--- a/rpctest/votingwallet.go
+++ b/rpctest/votingwallet.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2020 The Decred developers
+// Copyright (c) 2019-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -21,6 +21,7 @@ import (
 	dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/rpcclient/v7"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -92,18 +93,23 @@ type utxoInfo struct {
 type VotingWallet struct {
 	hn         *Harness
 	privateKey []byte
-	address    dcrutil.Address
+	address    stdaddr.Address
 	c          *rpcclient.Client
 
 	blockConnectedNtfnChan chan blockConnectedNtfn
 	winningTicketsNtfnChan chan winningTicketsNtfn
 	quitChan               chan struct{}
 
+	p2sstxVer        uint16
 	p2sstx           []byte
-	commitmentScript []byte
+	commitScriptVer  uint16
+	commitScript     []byte
 	p2pkh            []byte
+	p2pkhVer         uint16
+	voteScriptVer    uint16
 	voteScript       []byte
-	voteReturnScript []byte
+	voteRetScriptVer uint16
+	voteRetScript    []byte
 
 	errorReporter func(error)
 
@@ -136,42 +142,29 @@ type VotingWallet struct {
 // of the harness working after it has passed SVH (Stake Validation Height) by
 // continuously buying tickets and voting on them.
 func NewVotingWallet(ctx context.Context, hn *Harness) (*VotingWallet, error) {
-
 	privKey := secp256k1.PrivKeyFromBytes(hardcodedPrivateKey)
 	serPub := privKey.PubKey().SerializeCompressed()
-	hashPub := dcrutil.Hash160(serPub)
-	addr, err := dcrutil.NewAddressPubKeyHash(hashPub, hn.ActiveNet,
-		dcrec.STEcdsaSecp256k1)
+	h160 := stdaddr.Hash160(serPub)
+	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(h160, hn.ActiveNet)
 	if err != nil {
 		return nil, fmt.Errorf("unable to generate address for pubkey: %v", err)
 	}
 
-	p2sstx, err := txscript.PayToSStx(addr)
-	if err != nil {
-		return nil, fmt.Errorf("unable to prepare p2sstx script: %v", err)
-	}
-
-	p2pkh, err := txscript.PayToAddrScript(addr)
-	if err != nil {
-		return nil, fmt.Errorf("unable to prepare p2pkh script: %v", err)
-	}
+	p2sstxVer, p2sstx := addr.VotingRightsScript()
+	p2pkhVer, p2pkh := addr.PaymentScript()
 
-	commitAmount := dcrutil.Amount(hn.ActiveNet.MinimumStakeDiff * commitAmountMultiplier)
-	limit := uint16(0x0058)
-	commitmentScript, err := txscript.GenerateSStxAddrPush(addr, commitAmount, limit)
-	if err != nil {
-		return nil, fmt.Errorf("unable to prepare commitment script: %v", err)
-	}
+	commitAmount := hn.ActiveNet.MinimumStakeDiff * commitAmountMultiplier
+	const voteFeeLimit = 0
+	const revokeFeeLimit = 16777216
+	commitScriptVer, commitScript := addr.RewardCommitmentScript(commitAmount,
+		voteFeeLimit, revokeFeeLimit)
 
+	voteScriptVer := uint16(0)
 	voteScript, err := txscript.GenerateSSGenVotes(0x0001)
 	if err != nil {
 		return nil, fmt.Errorf("unable to prepare vote script: %v", err)
 	}
-
-	voteReturnScript, err := txscript.PayToSSGen(addr)
-	if err != nil {
-		return nil, fmt.Errorf("unable to generate vote return script: %v", err)
-	}
+	voteReturnScriptVer, voteReturnScript := addr.PayVoteCommitmentScript()
 
 	// Hints for the initial sizing of the tickets and maturing votes maps.
 	// Given we have a deterministic purchase process, this should allow us to
@@ -187,11 +180,16 @@ func NewVotingWallet(ctx context.Context, hn *Harness) (*VotingWallet, error) {
 		hn:                     hn,
 		privateKey:             hardcodedPrivateKey,
 		address:                addr,
+		p2sstxVer:              p2sstxVer,
 		p2sstx:                 p2sstx,
+		p2pkhVer:               p2pkhVer,
 		p2pkh:                  p2pkh,
-		commitmentScript:       commitmentScript,
+		commitScriptVer:        commitScriptVer,
+		commitScript:           commitScript,
+		voteScriptVer:          voteScriptVer,
 		voteScript:             voteScript,
-		voteReturnScript:       voteReturnScript,
+		voteRetScriptVer:       voteReturnScriptVer,
+		voteRetScript:          voteReturnScript,
 		subsidyCache:           standalone.NewSubsidyCache(hn.ActiveNet),
 		limitNbVotes:           int(hn.ActiveNet.TicketsPerBlock),
 		tickets:                make(map[chainhash.Hash]ticketInfo, hintTicketsCap),
@@ -399,6 +397,15 @@ func (w *VotingWallet) onBlockConnected(blockHeader []byte, transactions [][]byt
 	}
 }
 
+// newTxOut returns a new transaction output with the given parameters.
+func newTxOut(amount int64, pkScriptVer uint16, pkScript []byte) *wire.TxOut {
+	return &wire.TxOut{
+		Value:    amount,
+		Version:  pkScriptVer,
+		PkScript: pkScript,
+	}
+}
+
 func (w *VotingWallet) handleBlockConnectedNtfn(ntfn *blockConnectedNtfn) {
 	var header wire.BlockHeader
 	err := header.FromBytes(ntfn.blockHeader)
@@ -440,13 +447,13 @@ func (w *VotingWallet) handleBlockConnectedNtfn(ntfn *blockConnectedNtfn) {
 		t := &tickets[i]
 		t.Version = wire.TxVersion
 		t.AddTxIn(wire.NewTxIn(&utxos[i].outpoint, wire.NullValueIn, nil))
-		t.AddTxOut(wire.NewTxOut(ticketPrice, w.p2sstx))
-		t.AddTxOut(wire.NewTxOut(0, w.commitmentScript))
+		t.AddTxOut(newTxOut(ticketPrice, w.p2sstxVer, w.p2sstx))
+		t.AddTxOut(newTxOut(0, w.commitScriptVer, w.commitScript))
 		t.AddTxOut(wire.NewTxOut(changeAmount, nullPay2SSTXChange))
 
 		prevScript := w.p2pkh
 		if utxos[i].outpoint.Tree == wire.TxTreeStake {
-			prevScript = w.voteReturnScript
+			prevScript = w.voteRetScript
 		}
 
 		sig, err := txscript.SignatureScript(t, 0, prevScript, txscript.SigHashAll,
@@ -494,7 +501,6 @@ func (w *VotingWallet) onWinningTickets(blockHash *chainhash.Hash, blockHeight i
 }
 
 func (w *VotingWallet) handleWinningTicketsNtfn(ntfn *winningTicketsNtfn) {
-
 	blockRefScript, err := txscript.GenerateSSGenBlockRef(*ntfn.blockHash,
 		uint32(ntfn.blockHeight))
 	if err != nil {
@@ -502,8 +508,6 @@ func (w *VotingWallet) handleWinningTicketsNtfn(ntfn *winningTicketsNtfn) {
 		return
 	}
 
-	voteScript := w.voteScript
-	voteReturnScript := w.voteReturnScript
 	stakebaseValue := w.subsidyCache.CalcStakeVoteSubsidy(ntfn.blockHeight)
 
 	// Create the votes. nbVotes is the number of tickets from the wallet that
@@ -521,7 +525,7 @@ func (w *VotingWallet) handleWinningTicketsNtfn(ntfn *winningTicketsNtfn) {
 			continue
 		}
 
-		voteReturnValue := ticket.ticketPrice + stakebaseValue
+		voteRetValue := ticket.ticketPrice + stakebaseValue
 
 		// Create a corresponding vote transaction.
 		vote := &votes[nbVotes]
@@ -535,8 +539,8 @@ func (w *VotingWallet) handleWinningTicketsNtfn(ntfn *winningTicketsNtfn) {
 			wire.NullValueIn, nil,
 		))
 		vote.AddTxOut(wire.NewTxOut(0, blockRefScript))
-		vote.AddTxOut(wire.NewTxOut(0, voteScript))
-		vote.AddTxOut(wire.NewTxOut(voteReturnValue, voteReturnScript))
+		vote.AddTxOut(newTxOut(0, w.voteScriptVer, w.voteScript))
+		vote.AddTxOut(newTxOut(voteRetValue, w.voteRetScriptVer, w.voteRetScript))
 
 		// If there are tspends to vote for, create an additional
 		// output.
diff --git a/txscript/error_test.go b/txscript/error_test.go
index eeb901eb..9a22a404 100644
--- a/txscript/error_test.go
+++ b/txscript/error_test.go
@@ -19,7 +19,6 @@ func TestErrorKindStringer(t *testing.T) {
 	}{
 		{ErrInvalidIndex, "ErrInvalidIndex"},
 		{ErrInvalidSigHashSingleIndex, "ErrInvalidSigHashSingleIndex"},
-		{ErrUnsupportedAddress, "ErrUnsupportedAddress"},
 		{ErrTooManyRequiredSigs, "ErrTooManyRequiredSigs"},
 		{ErrTooMuchNullData, "ErrTooMuchNullData"},
 		{ErrUnsupportedScriptVersion, "ErrUnsupportedScriptVersion"},
diff --git a/txscript/example_test.go b/txscript/example_test.go
index 9c863ee3..204d375c 100644
--- a/txscript/example_test.go
+++ b/txscript/example_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2014-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -13,8 +13,8 @@ import (
 	"github.com/decred/dcrd/chaincfg/v3"
 	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
-	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/txscript/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -25,42 +25,6 @@ const (
 	noTreasury = false
 )
 
-// This example demonstrates creating a script which pays to a Decred address.
-// It also prints the created script hex and uses the DisasmString function to
-// display the disassembled script.
-func ExamplePayToAddrScript() {
-	// Parse the address to send the coins to into a dcrutil.Address
-	// which is useful to ensure the accuracy of the address and determine
-	// the address type.  It is also required for the upcoming call to
-	// PayToAddrScript.
-	mainNetParams := chaincfg.MainNetParams()
-	addressStr := "DsSej1qR3Fyc8kV176DCh9n9cY9nqf9Quxk"
-	address, err := dcrutil.DecodeAddress(addressStr, mainNetParams)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-
-	// Create a public key script that pays to the address.
-	script, err := txscript.PayToAddrScript(address)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-	fmt.Printf("Script Hex: %x\n", script)
-
-	disasm, err := txscript.DisasmString(script)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
-	fmt.Println("Script Disassembly:", disasm)
-
-	// Output:
-	// Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac
-	// Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG
-}
-
 // This example demonstrates extracting information from a standard public key
 // script.
 func ExampleExtractPkScriptAddrs() {
@@ -102,11 +66,10 @@ func ExampleSignTxOutput() {
 		return
 	}
 	pubKey := secp256k1.PrivKeyFromBytes(privKeyBytes).PubKey()
-	pubKeyHash := dcrutil.Hash160(pubKey.SerializeCompressed())
+	pubKeyHash := stdaddr.Hash160(pubKey.SerializeCompressed())
 	mainNetParams := chaincfg.MainNetParams()
-	sigType := dcrec.STEcdsaSecp256k1
-	addr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, mainNetParams,
-		sigType)
+	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pubKeyHash,
+		mainNetParams)
 	if err != nil {
 		fmt.Println(err)
 		return
@@ -119,12 +82,9 @@ func ExampleSignTxOutput() {
 	prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), wire.TxTreeRegular)
 	txIn := wire.NewTxIn(prevOut, 100000000, []byte{txscript.OP_0, txscript.OP_0})
 	originTx.AddTxIn(txIn)
-	pkScript, err := txscript.PayToAddrScript(addr)
-	if err != nil {
-		fmt.Println(err)
-		return
-	}
+	pkScriptVer, pkScript := addr.PaymentScript()
 	txOut := wire.NewTxOut(100000000, pkScript)
+	txOut.Version = pkScriptVer
 	originTx.AddTxOut(txOut)
 	originTxHash := originTx.TxHash()
 
@@ -144,7 +104,8 @@ func ExampleSignTxOutput() {
 	redeemTx.AddTxOut(txOut)
 
 	// Sign the redeeming transaction.
-	lookupKey := func(a dcrutil.Address) ([]byte, dcrec.SignatureType, bool, error) {
+	sigType := dcrec.STEcdsaSecp256k1
+	lookupKey := func(a stdaddr.Address) ([]byte, dcrec.SignatureType, bool, error) {
 		// Ordinarily this function would involve looking up the private
 		// key for the provided address, but since the only thing being
 		// signed in this example uses the address associated with the
@@ -199,7 +160,7 @@ func ExampleSignTxOutput() {
 func ExampleScriptTokenizer() {
 	// Create a script to use in the example.  Ordinarily this would come from
 	// some other source.
-	hash160 := dcrutil.Hash160([]byte("example"))
+	hash160 := stdaddr.Hash160([]byte("example"))
 	script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).
 		AddOp(txscript.OP_HASH160).AddData(hash160).
 		AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script()
diff --git a/txscript/sign_test.go b/txscript/sign_test.go
index d733c06b..bff58a09 100644
--- a/txscript/sign_test.go
+++ b/txscript/sign_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2016 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -17,7 +17,7 @@ import (
 	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/edwards/v2"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
-	"github.com/decred/dcrd/dcrutil/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 	"github.com/decred/dcrd/wire"
 )
 
@@ -47,12 +47,12 @@ type addressToKey struct {
 
 func mkGetKey(keys map[string]addressToKey) KeyDB {
 	if keys == nil {
-		return KeyClosure(func(addr dcrutil.Address) ([]byte,
+		return KeyClosure(func(addr stdaddr.Address) ([]byte,
 			dcrec.SignatureType, bool, error) {
 			return nil, 0, false, errors.New("nope 1")
 		})
 	}
-	return KeyClosure(func(addr dcrutil.Address) ([]byte,
+	return KeyClosure(func(addr stdaddr.Address) ([]byte,
 		dcrec.SignatureType, bool, error) {
 		a2k, ok := keys[addr.Address()]
 		if !ok {
@@ -64,14 +64,14 @@ func mkGetKey(keys map[string]addressToKey) KeyDB {
 
 func mkGetKeyPub(keys map[string]addressToKey) KeyDB {
 	if keys == nil {
-		return KeyClosure(func(addr dcrutil.Address) ([]byte,
+		return KeyClosure(func(addr stdaddr.Address) ([]byte,
 			dcrec.SignatureType, bool, error) {
 			return nil, 0, false, errors.New("nope 1")
 		})
 	}
-	return KeyClosure(func(addr dcrutil.Address) ([]byte,
+	return KeyClosure(func(addr stdaddr.Address) ([]byte,
 		dcrec.SignatureType, bool, error) {
-		a2k, ok := keys[addr.String()]
+		a2k, ok := keys[addr.Address()]
 		if !ok {
 			return nil, 0, false, errors.New("nope 2")
 		}
@@ -81,12 +81,12 @@ func mkGetKeyPub(keys map[string]addressToKey) KeyDB {
 
 func mkGetScript(scripts map[string][]byte) ScriptDB {
 	if scripts == nil {
-		return ScriptClosure(func(addr dcrutil.Address) (
+		return ScriptClosure(func(addr stdaddr.Address) (
 			[]byte, error) {
 			return nil, errors.New("nope 3")
 		})
 	}
-	return ScriptClosure(func(addr dcrutil.Address) ([]byte,
+	return ScriptClosure(func(addr stdaddr.Address) ([]byte,
 		error) {
 		script, ok := scripts[addr.Address()]
 		if !ok {
@@ -227,40 +227,42 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeUncompressed()
+					pkBytes := privKey.PubKey().SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
+					pkBytes := pk.SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams,
-					suite)
-
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, pkScript, hashType,
@@ -303,39 +305,42 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeUncompressed()
+					pkBytes := privKey.PubKey().SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
+					pkBytes := pk.SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams,
-					suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				sigScript, err := SignTxOutput(
@@ -408,39 +413,42 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
+					pkBytes := pk.SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams,
-					suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, pkScript, hashType,
@@ -483,39 +491,42 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
+					pkBytes := pk.SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams,
-					suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				sigScript, err := SignTxOutput(testingParams,
@@ -597,23 +608,16 @@ func TestSignTxOutput(t *testing.T) {
 			keyDB := privKey.Serialize()
 			pkBytes := privKey.PubKey().SerializeCompressed()
 
-			suite := dcrec.STEcdsaSecp256k1
-			address, err := dcrutil.NewAddressPubKeyHash(
-				dcrutil.Hash160(pkBytes), testingParams,
-				suite)
+			address, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+				stdaddr.Hash160(pkBytes), testingParams)
 			if err != nil {
-				t.Errorf("failed to make address for %s: %v",
-					msg, err)
-				break
+				t.Errorf("failed to make address for %s: %v", msg, err)
 			}
 
-			pkScript, err := PayToSStx(address)
-			if err != nil {
-				t.Errorf("failed to make pkscript "+
-					"for %s: %v", msg, err)
-			}
+			_, pkScript := address.VotingRightsScript()
 
 			// Without treasury agenda.
+			suite := dcrec.STEcdsaSecp256k1
 			if err := signAndCheck(msg, tx, i, pkScript, hashType,
 				mkGetKey(map[string]addressToKey{
 					address.Address(): {keyDB, suite, true},
@@ -662,23 +666,17 @@ func TestSignTxOutput(t *testing.T) {
 			keyDB := privKey.Serialize()
 			pkBytes := privKey.PubKey().SerializeCompressed()
 
-			suite := dcrec.STEcdsaSecp256k1
-			address, err := dcrutil.NewAddressPubKeyHash(
-				dcrutil.Hash160(pkBytes), testingParams,
-				suite)
+			address, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+				stdaddr.Hash160(pkBytes), testingParams)
 			if err != nil {
-				t.Errorf("failed to make address for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address for %s: %v", msg, err)
 				break
 			}
 
-			pkScript, err := PayToSStxChange(address)
-			if err != nil {
-				t.Errorf("failed to make pkscript "+
-					"for %s: %v", msg, err)
-			}
+			_, pkScript := address.StakeChangeScript()
 
 			// Without treasury agenda.
+			suite := dcrec.STEcdsaSecp256k1
 			if err := signAndCheck(msg, tx, i, pkScript, hashType,
 				mkGetKey(map[string]addressToKey{
 					address.Address(): {keyDB, suite, true},
@@ -727,23 +725,17 @@ func TestSignTxOutput(t *testing.T) {
 			keyDB := privKey.Serialize()
 			pkBytes := privKey.PubKey().SerializeCompressed()
 
-			suite := dcrec.STEcdsaSecp256k1
-			address, err := dcrutil.NewAddressPubKeyHash(
-				dcrutil.Hash160(pkBytes), testingParams,
-				suite)
+			address, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+				stdaddr.Hash160(pkBytes), testingParams)
 			if err != nil {
-				t.Errorf("failed to make address for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address for %s: %v", msg, err)
 				break
 			}
 
-			pkScript, err := PayToSSGen(address)
-			if err != nil {
-				t.Errorf("failed to make pkscript "+
-					"for %s: %v", msg, err)
-			}
+			_, pkScript := address.PayVoteCommitmentScript()
 
 			// Without treasury agenda.
+			suite := dcrec.STEcdsaSecp256k1
 			if err := signAndCheck(msg, tx, i, pkScript, hashType,
 				mkGetKey(map[string]addressToKey{
 					address.Address(): {keyDB, suite, true},
@@ -792,23 +784,18 @@ func TestSignTxOutput(t *testing.T) {
 			keyDB := privKey.Serialize()
 			pkBytes := privKey.PubKey().SerializeCompressed()
 
-			suite := dcrec.STEcdsaSecp256k1
-			address, err := dcrutil.NewAddressPubKeyHash(
-				dcrutil.Hash160(pkBytes), testingParams,
-				suite)
+			address, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+				stdaddr.Hash160(pkBytes), testingParams)
 			if err != nil {
 				t.Errorf("failed to make address for %s: %v",
 					msg, err)
 				break
 			}
 
-			pkScript, err := PayToSSRtx(address)
-			if err != nil {
-				t.Errorf("failed to make pkscript "+
-					"for %s: %v", msg, err)
-			}
+			_, pkScript := address.PayRevokeCommitmentScript()
 
 			// Without treasury agenda.
+			suite := dcrec.STEcdsaSecp256k1
 			if err := signAndCheck(msg, tx, i, pkScript, hashType,
 				mkGetKey(map[string]addressToKey{
 					address.Address(): {keyDB, suite, true},
@@ -846,68 +833,58 @@ func TestSignTxOutput(t *testing.T) {
 
 	// Pay to PubKey (uncompressed)
 	for _, hashType := range hashTypes {
-		for _, suite := range signatureSuites {
-			for i := range tx.TxIn {
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+		for i := range tx.TxIn {
+			msg := fmt.Sprintf("%d:%d", hashType, i)
 
-				privKey, err := secp256k1.GeneratePrivateKey()
-				if err != nil {
-					t.Errorf("failed to generate key: %v", err)
-					break
-				}
-				keyDB := privKey.Serialize()
-				pk := privKey.PubKey()
-				suite := dcrec.STEcdsaSecp256k1
-				// For address generation, consensus rules require using
-				// a compressed public key. Look up ExtractPkScriptAddrs
-				// for more details
-				address, err := dcrutil.NewAddressSecpPubKeyCompressed(pk,
-					testingParams)
-				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
-					break
-				}
+			privKey, err := secp256k1.GeneratePrivateKey()
+			if err != nil {
+				t.Errorf("failed to generate key: %v", err)
+				break
+			}
+			keyDB := privKey.Serialize()
+			pk := privKey.PubKey()
+			address, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
+				testingParams)
+			if err != nil {
+				t.Errorf("failed to make address for %s: %v", msg, err)
+				break
+			}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+			_, pkScript := address.PaymentScript()
 
-				// Without treasury agenda.
-				if err := signAndCheck(msg, tx, i, pkScript, hashType,
-					mkGetKey(map[string]addressToKey{
-						address.Address(): {keyDB, suite, false},
-					}), mkGetScript(nil), noTreasury); err != nil {
-					t.Error(err)
-					break
-				}
+			// Without treasury agenda.
+			suite := dcrec.STEcdsaSecp256k1
+			if err := signAndCheck(msg, tx, i, pkScript, hashType,
+				mkGetKey(map[string]addressToKey{
+					address.Address(): {keyDB, suite, false},
+				}), mkGetScript(nil), noTreasury); err != nil {
+				t.Error(err)
+				break
+			}
 
-				if err := signBadAndCheck(msg, tx, i, pkScript, hashType,
-					mkGetKey(map[string]addressToKey{
-						address.Address(): {keyDB, dcrec.STEcdsaSecp256k1, false},
-					}), mkGetScript(nil), noTreasury); err == nil {
-					t.Errorf("corrupted signature validated: %s", msg)
-					break
-				}
+			if err := signBadAndCheck(msg, tx, i, pkScript, hashType,
+				mkGetKey(map[string]addressToKey{
+					address.Address(): {keyDB, dcrec.STEcdsaSecp256k1, false},
+				}), mkGetScript(nil), noTreasury); err == nil {
+				t.Errorf("corrupted signature validated: %s", msg)
+				break
+			}
 
-				// With treasury agenda.
-				if err := signAndCheck(msg, tx, i, pkScript, hashType,
-					mkGetKey(map[string]addressToKey{
-						address.Address(): {keyDB, suite, false},
-					}), mkGetScript(nil), withTreasury); err != nil {
-					t.Error(err)
-					break
-				}
+			// With treasury agenda.
+			if err := signAndCheck(msg, tx, i, pkScript, hashType,
+				mkGetKey(map[string]addressToKey{
+					address.Address(): {keyDB, suite, false},
+				}), mkGetScript(nil), withTreasury); err != nil {
+				t.Error(err)
+				break
+			}
 
-				if err := signBadAndCheck(msg, tx, i, pkScript, hashType,
-					mkGetKey(map[string]addressToKey{
-						address.Address(): {keyDB, dcrec.STEcdsaSecp256k1, false},
-					}), mkGetScript(nil), withTreasury); err == nil {
-					t.Errorf("corrupted signature validated: %s", msg)
-					break
-				}
+			if err := signBadAndCheck(msg, tx, i, pkScript, hashType,
+				mkGetKey(map[string]addressToKey{
+					address.Address(): {keyDB, dcrec.STEcdsaSecp256k1, false},
+				}), mkGetScript(nil), withTreasury); err == nil {
+				t.Errorf("corrupted signature validated: %s", msg)
+				break
 			}
 		}
 	}
@@ -916,61 +893,50 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					// For address generation, consensus rules require using
-					// a compressed public key. Look up ExtractPkScriptAddrs
-					// for more details
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeUncompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				sigScript, err := SignTxOutput(testingParams,
 					tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, false},
+						address.Address(): {keyDB, suite, false},
 					}), mkGetScript(nil), nil, noTreasury)
 				if err != nil {
 					t.Errorf("failed to sign output %s: %v", msg,
@@ -982,7 +948,7 @@ func TestSignTxOutput(t *testing.T) {
 				sigScript, err = SignTxOutput(testingParams,
 					tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, false},
+						address.Address(): {keyDB, suite, false},
 					}), mkGetScript(nil), sigScript, noTreasury)
 				if err != nil {
 					t.Errorf("failed to sign output %s a "+
@@ -999,7 +965,7 @@ func TestSignTxOutput(t *testing.T) {
 				sigScript, err = SignTxOutput(testingParams,
 					tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, false},
+						address.Address(): {keyDB, suite, false},
 					}), mkGetScript(nil), nil, withTreasury)
 				if err != nil {
 					t.Errorf("failed to sign output %s: %v", msg,
@@ -1011,7 +977,7 @@ func TestSignTxOutput(t *testing.T) {
 				sigScript, err = SignTxOutput(testingParams,
 					tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, false},
+						address.Address(): {keyDB, suite, false},
 					}), mkGetScript(nil), sigScript, withTreasury)
 				if err != nil {
 					t.Errorf("failed to sign output %s a "+
@@ -1031,60 +997,49 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					// For address generation, consensus rules require using
-					// a compressed public key. Look up ExtractPkScriptAddrs
-					// for more details
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeCompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, true},
+						address.Address(): {keyDB, suite, true},
 					}), mkGetScript(nil), noTreasury); err != nil {
 					t.Error(err)
 					break
@@ -1092,7 +1047,7 @@ func TestSignTxOutput(t *testing.T) {
 
 				if err := signBadAndCheck(msg, tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, true},
+						address.Address(): {keyDB, suite, true},
 					}), mkGetScript(nil), noTreasury); err == nil {
 					t.Errorf("corrupted signature validated: %s", msg)
 					break
@@ -1101,7 +1056,7 @@ func TestSignTxOutput(t *testing.T) {
 				// With treasury agenda.
 				if err := signAndCheck(msg, tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, true},
+						address.Address(): {keyDB, suite, true},
 					}), mkGetScript(nil), withTreasury); err != nil {
 					t.Error(err)
 					break
@@ -1109,7 +1064,7 @@ func TestSignTxOutput(t *testing.T) {
 
 				if err := signBadAndCheck(msg, tx, i, pkScript, hashType,
 					mkGetKeyPub(map[string]addressToKey{
-						address.String(): {keyDB, suite, true},
+						address.Address(): {keyDB, suite, true},
 					}), mkGetScript(nil), withTreasury); err == nil {
 					t.Errorf("corrupted signature validated: %s", msg)
 					break
@@ -1122,52 +1077,44 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeCompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
 					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
+						t.Errorf("failed to make address for %s: %v", msg, err)
 					}
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
 				// Without treasury agenda.
 				sigScript, err := SignTxOutput(testingParams,
@@ -1241,55 +1188,51 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeUncompressed()
+					pkBytes := privKey.PubKey().SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
+					pkBytes := pk.SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams, suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-					break
-				}
+				_, pkScript := address.PaymentScript()
 
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 					break
 				}
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
-				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
-					break
-				}
+				_, scriptPkScript := scriptAddr.PaymentScript()
 
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, scriptPkScript,
@@ -1338,55 +1281,51 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeUncompressed()
+					pkBytes := privKey.PubKey().SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
+					pkBytes := pk.SerializeUncompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams, suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-					break
-				}
+				_, pkScript := address.PaymentScript()
 
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 					break
 				}
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
-				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
-					break
-				}
+				_, scriptPkScript := scriptAddr.PaymentScript()
 
 				// Without treasury agenda.
 				_, err = SignTxOutput(testingParams, tx, i,
@@ -1465,54 +1404,51 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
+					pkBytes := pk.SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams, suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 					break
 				}
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
-				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
-					break
-				}
+				_, scriptPkScript := scriptAddr.PaymentScript()
 
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, scriptPkScript,
@@ -1561,54 +1497,51 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
+				var keyDB []byte
 
+				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+						h160, testingParams)
+
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
+					pkBytes := pk.SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashEd25519V0(h160,
+						testingParams)
+
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
+					pkBytes := privKey.PubKey().SerializeCompressed()
+					h160 := stdaddr.Hash160(pkBytes)
+					address, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(
+						h160, testingParams)
 				}
-
-				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
-				address, err := dcrutil.NewAddressPubKeyHash(
-					dcrutil.Hash160(pkBytes), testingParams, suite)
 				if err != nil {
-					t.Errorf("failed to make address for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 					break
 				}
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
-				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
-					break
-				}
+				_, scriptPkScript := scriptAddr.PaymentScript()
 
 				// Without treasury agenda.
 				_, err = SignTxOutput(testingParams,
@@ -1689,70 +1622,48 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					// For address generation, consensus rules require using
-					// a compressed public key. Look up ExtractPkScriptAddrs
-					// for more details
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeUncompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 				}
-
-				pkScript, err := PayToAddrScript(address)
 				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
+					break
 				}
 
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
-				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 				}
 
+				_, scriptPkScript := scriptAddr.PaymentScript()
+
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, scriptPkScript,
 					hashType,
@@ -1798,70 +1709,48 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					// For address generation, consensus rules require using
-					// a compressed public key. Look up ExtractPkScriptAddrs
-					// for more details
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeUncompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeUncompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 				}
-
-				pkScript, err := PayToAddrScript(address)
 				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
+					break
 				}
 
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
-				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
-				}
+				_, pkScript := address.PaymentScript()
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 				}
 
+				_, scriptPkScript := scriptAddr.PaymentScript()
+
 				// Without treasury agenda.
 				_, err = SignTxOutput(testingParams,
 					tx, i, scriptPkScript, hashType,
@@ -1941,69 +1830,49 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeCompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
-				}
-
-				pkScript, err := PayToAddrScript(address)
-				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
 				}
-
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
 				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
+				_, pkScript := address.PaymentScript()
+
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 					break
 				}
 
+				_, scriptPkScript := scriptAddr.PaymentScript()
+
 				// Without treasury agenda.
 				if err := signAndCheck(msg, tx, i, scriptPkScript,
 					hashType,
@@ -2051,69 +1920,49 @@ func TestSignTxOutput(t *testing.T) {
 	for _, hashType := range hashTypes {
 		for _, suite := range signatureSuites {
 			for i := range tx.TxIn {
-				var keyDB, pkBytes []byte
-				var address dcrutil.Address
-				var err error
+				var keyDB []byte
 
 				msg := fmt.Sprintf("%d:%d:%d", hashType, i, suite)
-
+				var address stdaddr.Address
+				var err error
 				switch suite {
 				case dcrec.STEcdsaSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
 					pk := privKey.PubKey()
-					address, err = dcrutil.NewAddressSecpPubKeyCompressed(pk,
+					address, err = stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STEd25519:
 					keyDB, _, _, _ = edwards.GenerateKey(rand.Reader)
 					_, pk := edwards.PrivKeyFromBytes(keyDB)
-					pkBytes = pk.SerializeCompressed()
-					address, err = dcrutil.NewAddressEdwardsPubKey(pkBytes,
+					pkBytes := pk.SerializeCompressed()
+					address, err = stdaddr.NewAddressPubKeyEd25519V0Raw(pkBytes,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 
 				case dcrec.STSchnorrSecp256k1:
 					privKey, _ := secp256k1.GeneratePrivateKey()
 					keyDB = privKey.Serialize()
-					pkBytes = privKey.PubKey().SerializeCompressed()
-					address, err = dcrutil.NewAddressSecSchnorrPubKey(pkBytes,
+					pk := privKey.PubKey()
+					address, err = stdaddr.NewAddressPubKeySchnorrSecp256k1V0(pk,
 						testingParams)
-					if err != nil {
-						t.Errorf("failed to make address for %s: %v",
-							msg, err)
-					}
 				}
-
-				pkScript, err := PayToAddrScript(address)
 				if err != nil {
-					t.Errorf("failed to make pkscript "+
-						"for %s: %v", msg, err)
-				}
-
-				scriptAddr, err := dcrutil.NewAddressScriptHash(
-					pkScript, testingParams)
-				if err != nil {
-					t.Errorf("failed to make p2sh addr for %s: %v",
-						msg, err)
+					t.Errorf("failed to make address for %s: %v", msg, err)
 					break
 				}
 
-				scriptPkScript, err := PayToAddrScript(
-					scriptAddr)
+				_, pkScript := address.PaymentScript()
+
+				scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+					testingParams)
 				if err != nil {
-					t.Errorf("failed to make script pkscript for "+
-						"%s: %v", msg, err)
+					t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 					break
 				}
 
+				_, scriptPkScript := scriptAddr.PaymentScript()
+
 				// Without treasury agenda.
 				_, err = SignTxOutput(testingParams,
 					tx, i, scriptPkScript, hashType,
@@ -2201,13 +2050,11 @@ func TestSignTxOutput(t *testing.T) {
 			}
 			keyDB1 := privKey1.Serialize()
 			pk1 := privKey1.PubKey()
-			suite1 := dcrec.STEcdsaSecp256k1
 
-			address1, err := dcrutil.NewAddressSecpPubKeyCompressed(pk1,
+			address1, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk1,
 				testingParams)
 			if err != nil {
-				t.Errorf("failed to make address for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address for %s: %v", msg, err)
 				break
 			}
 
@@ -2218,39 +2065,32 @@ func TestSignTxOutput(t *testing.T) {
 			}
 			keyDB2 := privKey2.Serialize()
 			pk2 := privKey2.PubKey()
-			suite2 := dcrec.STEcdsaSecp256k1
 
-			address2, err := dcrutil.NewAddressSecpPubKeyCompressed(pk2,
+			address2, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk2,
 				testingParams)
 			if err != nil {
-				t.Errorf("failed to make address 2 for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address 2 for %s: %v", msg, err)
 				break
 			}
 
 			pkScript, err := MultiSigScript(2, pk1.SerializeCompressed(),
 				pk2.SerializeCompressed())
 			if err != nil {
-				t.Errorf("failed to make pkscript "+
-					"for %s: %v", msg, err)
+				t.Errorf("failed to make pkscript for %s: %v", msg, err)
 			}
 
-			scriptAddr, err := dcrutil.NewAddressScriptHash(
-				pkScript, testingParams)
+			scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+				testingParams)
 			if err != nil {
-				t.Errorf("failed to make p2sh addr for %s: %v",
-					msg, err)
+				t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 				break
 			}
 
-			scriptPkScript, err := PayToAddrScript(scriptAddr)
-			if err != nil {
-				t.Errorf("failed to make script pkscript for "+
-					"%s: %v", msg, err)
-				break
-			}
+			_, scriptPkScript := scriptAddr.PaymentScript()
 
 			// Without treasury agenda.
+			suite1 := dcrec.STEcdsaSecp256k1
+			suite2 := dcrec.STEcdsaSecp256k1
 			if err := signAndCheck(msg, tx, i, scriptPkScript,
 				hashType,
 				mkGetKey(map[string]addressToKey{
@@ -2308,13 +2148,11 @@ func TestSignTxOutput(t *testing.T) {
 			}
 			keyDB1 := privKey1.Serialize()
 			pk1 := privKey1.PubKey()
-			suite1 := dcrec.STEcdsaSecp256k1
 
-			address1, err := dcrutil.NewAddressSecpPubKeyCompressed(pk1,
+			address1, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk1,
 				testingParams)
 			if err != nil {
-				t.Errorf("failed to make address for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address for %s: %v", msg, err)
 				break
 			}
 
@@ -2325,13 +2163,11 @@ func TestSignTxOutput(t *testing.T) {
 			}
 			keyDB2 := privKey2.Serialize()
 			pk2 := privKey2.PubKey()
-			suite2 := dcrec.STEcdsaSecp256k1
 
-			address2, err := dcrutil.NewAddressSecpPubKeyCompressed(pk2,
+			address2, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk2,
 				testingParams)
 			if err != nil {
-				t.Errorf("failed to make address 2 for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address 2 for %s: %v", msg, err)
 				break
 			}
 
@@ -2342,22 +2178,18 @@ func TestSignTxOutput(t *testing.T) {
 					"for %s: %v", msg, err)
 			}
 
-			scriptAddr, err := dcrutil.NewAddressScriptHash(
-				pkScript, testingParams)
+			scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+				testingParams)
 			if err != nil {
-				t.Errorf("failed to make p2sh addr for %s: %v",
-					msg, err)
+				t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 				break
 			}
 
-			scriptPkScript, err := PayToAddrScript(scriptAddr)
-			if err != nil {
-				t.Errorf("failed to make script pkscript for "+
-					"%s: %v", msg, err)
-				break
-			}
+			_, scriptPkScript := scriptAddr.PaymentScript()
 
 			// Without treasury agenda.
+			suite1 := dcrec.STEcdsaSecp256k1
+			suite2 := dcrec.STEcdsaSecp256k1
 			sigScript, err := SignTxOutput(testingParams, tx, i,
 				scriptPkScript, hashType,
 				mkGetKey(map[string]addressToKey{
@@ -2456,13 +2288,11 @@ func TestSignTxOutput(t *testing.T) {
 			}
 			keyDB1 := privKey1.Serialize()
 			pk1 := privKey1.PubKey()
-			suite1 := dcrec.STEcdsaSecp256k1
 
-			address1, err := dcrutil.NewAddressSecpPubKeyCompressed(pk1,
+			address1, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk1,
 				testingParams)
 			if err != nil {
-				t.Errorf("failed to make address for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address for %s: %v", msg, err)
 				break
 			}
 
@@ -2473,38 +2303,35 @@ func TestSignTxOutput(t *testing.T) {
 			}
 			keyDB2 := privKey2.Serialize()
 			pk2 := privKey2.PubKey()
-			suite2 := dcrec.STEcdsaSecp256k1
-			address2, err := dcrutil.NewAddressSecpPubKeyCompressed(pk2,
+			address2, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pk2,
 				testingParams)
 			if err != nil {
-				t.Errorf("failed to make address 2 for %s: %v",
-					msg, err)
+				t.Errorf("failed to make address 2 for %s: %v", msg, err)
 				break
 			}
 
 			pkScript, err := MultiSigScript(2, pk1.SerializeCompressed(),
 				pk2.SerializeCompressed())
 			if err != nil {
-				t.Errorf("failed to make pkscript "+
-					"for %s: %v", msg, err)
+				t.Errorf("failed to make pkscript for %s: %v", msg, err)
 			}
 
-			scriptAddr, err := dcrutil.NewAddressScriptHash(
-				pkScript, testingParams)
+			scriptAddr, err := stdaddr.NewAddressScriptHashV0(pkScript,
+				testingParams)
 			if err != nil {
-				t.Errorf("failed to make p2sh addr for %s: %v",
-					msg, err)
+				t.Errorf("failed to make p2sh addr for %s: %v", msg, err)
 				break
 			}
 
-			scriptPkScript, err := PayToAddrScript(scriptAddr)
+			_, scriptPkScript := scriptAddr.PaymentScript()
 			if err != nil {
-				t.Errorf("failed to make script pkscript for "+
-					"%s: %v", msg, err)
+				t.Errorf("failed to make script pkscript for %s: %v", msg, err)
 				break
 			}
 
 			// Without treasury agenda.
+			suite1 := dcrec.STEcdsaSecp256k1
+			suite2 := dcrec.STEcdsaSecp256k1
 			sigScript, err := SignTxOutput(testingParams, tx, i,
 				scriptPkScript, hashType,
 				mkGetKey(map[string]addressToKey{
@@ -2622,14 +2449,14 @@ var (
 		0xac, 0x70, 0x7f, 0x3d, 0xa4, 0x39, 0x5e, 0xcb, 0x3b, 0xb0,
 		0xd6, 0x0e, 0x06, 0x92}
 	thisPubKey        = secp256k1.PrivKeyFromBytes(privKeyD).PubKey()
-	thisAddressUnc, _ = dcrutil.NewAddressPubKeyHash(
-		dcrutil.Hash160(thisPubKey.SerializeUncompressed()),
-		testingParams, dcrec.STEcdsaSecp256k1)
-	uncompressedPkScript, _ = PayToAddrScript(thisAddressUnc)
-	thisAddressCom, _       = dcrutil.NewAddressPubKeyHash(
-		dcrutil.Hash160(thisPubKey.SerializeCompressed()),
-		testingParams, dcrec.STEcdsaSecp256k1)
-	compressedPkScript, _ = PayToAddrScript(thisAddressCom)
+	thisAddressUnc, _ = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+		stdaddr.Hash160(thisPubKey.SerializeUncompressed()),
+		testingParams)
+	_, uncompressedPkScript = thisAddressUnc.PaymentScript()
+	thisAddressCom, _       = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(
+		stdaddr.Hash160(thisPubKey.SerializeCompressed()),
+		testingParams)
+	_, compressedPkScript = thisAddressCom.PaymentScript()
 	shortPkScript         = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5,
 		0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32,
 		0x53, 0x90, 0x0e, 0x0a, 0x88, 0xac}
diff --git a/txscript/standard_test.go b/txscript/standard_test.go
index 6a801a67..f7cf5a4e 100644
--- a/txscript/standard_test.go
+++ b/txscript/standard_test.go
@@ -1,5 +1,5 @@
 // Copyright (c) 2013-2017 The btcsuite developers
-// Copyright (c) 2015-2020 The Decred developers
+// Copyright (c) 2015-2021 The Decred developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
@@ -14,9 +14,8 @@ import (
 
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/chaincfg/v3"
-	"github.com/decred/dcrd/dcrec"
 	"github.com/decred/dcrd/dcrec/secp256k1/v4"
-	"github.com/decred/dcrd/dcrutil/v4"
+	"github.com/decred/dcrd/txscript/v4/stdaddr"
 )
 
 // mainNetParams is an instance of the main network parameters and is shared
@@ -37,16 +36,16 @@ func mustParseShortForm(script string) []byte {
 	return s
 }
 
-// newAddressPubKey returns a new dcrutil.AddressPubKey from the provided
-// serialized public key.  It panics if an error occurs.  This is only used in
-// the tests as a helper since the only way it can fail is if there is an error
-// in the test source code.
-func newAddressPubKey(serializedPubKey []byte) dcrutil.Address {
+// newAddressPubKey returns a new pubkey address from the provided serialized
+// public key.  It panics if an error occurs.  This is only used in the tests as
+// a helper since the only way it can fail is if there is an error in the test
+// source code.
+func newAddressPubKey(serializedPubKey []byte) stdaddr.Address {
 	pubkey, err := secp256k1.ParsePubKey(serializedPubKey)
 	if err != nil {
 		panic("invalid public key in test source")
 	}
-	addr, err := dcrutil.NewAddressSecpPubKeyCompressed(pubkey, mainNetParams)
+	addr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(pubkey, mainNetParams)
 	if err != nil {
 		panic("invalid public key in test source")
 	}
@@ -54,13 +53,12 @@ func newAddressPubKey(serializedPubKey []byte) dcrutil.Address {
 	return addr
 }
 
-// newAddressPubKeyHash returns a new dcrutil.AddressPubKeyHash from the
-// provided hash.  It panics if an error occurs.  This is only used in the tests
-// as a helper since the only way it can fail is if there is an error in the
-// test source code.
-func newAddressPubKeyHash(pkHash []byte) dcrutil.Address {
-	addr, err := dcrutil.NewAddressPubKeyHash(pkHash, mainNetParams,
-		dcrec.STEcdsaSecp256k1)
+// newAddressPubKeyHash returns a new pubkey hash from the provided hash.  It
+// panics if an error occurs.  This is only used in the tests as a helper since
+// the only way it can fail is if there is an error in the test source code.
+func newAddressPubKeyHash(pkHash []byte) stdaddr.Address {
+	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash,
+		mainNetParams)
 	if err != nil {
 		panic("invalid public key hash in test source")
 	}
@@ -68,12 +66,13 @@ func newAddressPubKeyHash(pkHash []byte) dcrutil.Address {
 	return addr
 }
 
-// newAddressScriptHash returns a new dcrutil.AddressScriptHash from the
-// provided hash.  It panics if an error occurs.  This is only used in the tests
-// as a helper since the only way it can fail is if there is an error in the
-// test source code.
-func newAddressScriptHash(scriptHash []byte) dcrutil.Address {
-	addr, err := dcrutil.NewAddressScriptHashFromHash(scriptHash, mainNetParams)
+// newAddressScriptHash returns a new script hash address from the provided
+// hash.  It panics if an error occurs.  This is only used in the tests as a
+// helper since the only way it can fail is if there is an error in the test
+// source code.
+func newAddressScriptHash(scriptHash []byte) stdaddr.Address {
+	addr, err := stdaddr.NewAddressScriptHashV0FromHash(scriptHash,
+		mainNetParams)
 	if err != nil {
 		panic("invalid script hash in test source")
 	}
@@ -90,7 +89,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 	tests := []struct {
 		name    string
 		script  []byte
-		addrs   []dcrutil.Address
+		addrs   []stdaddr.Address
 		reqSigs int
 		class   ScriptClass
 		noparse bool
@@ -99,7 +98,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 			name: "standard p2pk with compressed pubkey (0x02)",
 			script: hexToBytes("2102192d74d0cb94344c9569c2e779015" +
 				"73d8d7903c3ebec3a957724895dca52c6b4ac"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKey(hexToBytes("02192d74d0cb9434" +
 					"4c9569c2e77901573d8d7903c3ebec3a9577" +
 					"24895dca52c6b4")),
@@ -113,7 +112,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 				"c1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddf" +
 				"b84ccf9744464f82e160bfa9b8b64f9d4c03f999b864" +
 				"3f656b412a3ac"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKey(hexToBytes("0411db93e1dcdb8a" +
 					"016b49840f8c53bc1eb68a382e97b1482eca" +
 					"d7b148a6909a5cb2e0eaddfb84ccf9744464" +
@@ -127,7 +126,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 			name: "standard p2pk with compressed pubkey (0x03)",
 			script: hexToBytes("2103b0bd634234abbb1ba1e986e884185" +
 				"c61cf43e001f9137f23c2c409273eb16e65ac"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKey(hexToBytes("03b0bd634234abbb" +
 					"1ba1e986e884185c61cf43e001f9137f23c2" +
 					"c409273eb16e65")),
@@ -141,7 +140,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 				"c61cf43e001f9137f23c2c409273eb16e6537a576782" +
 				"eba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3" +
 				"c1e0908ef7bac"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKey(hexToBytes("04b0bd634234abbb" +
 					"1ba1e986e884185c61cf43e001f9137f23c2" +
 					"c409273eb16e6537a576782eba668a7ef8bd" +
@@ -155,7 +154,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 			name: "standard p2pkh",
 			script: hexToBytes("76a914ad06dd6ddee55cbca9a9e3713bd" +
 				"7587509a3056488ac"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKeyHash(hexToBytes("ad06dd6ddee5" +
 					"5cbca9a9e3713bd7587509a30564")),
 			},
@@ -166,7 +165,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 			name: "standard p2sh",
 			script: hexToBytes("a91463bcc565f9e68ee0189dd5cc67f1b" +
 				"0e5f02f45cb87"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressScriptHash(hexToBytes("63bcc565f9e6" +
 					"8ee0189dd5cc67f1b0e5f02f45cb")),
 			},
@@ -183,7 +182,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 				"1354d80e550078cb532a34bfa2fcfdeb7d76519aecc6" +
 				"2770f5b0e4ef8551946d8a540911abe3e7854a26f39f" +
 				"58b25c15342af52ae"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKey(hexToBytes("04cc71eb30d653c0" +
 					"c3163990c47b976f3fb3f37cccdcbedb169a" +
 					"1dfef58bbfbfaff7d8a473e7e2e6d317b87b" +
@@ -211,7 +210,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 				"2bbf781c5410d3f22a7a3a56ffefb2238af8627363bd" +
 				"f2ed97c1f89784a1aecdb43384f11d2acc64443c7fc2" +
 				"99cef0400421a53ae"),
-			addrs: []dcrutil.Address{
+			addrs: []stdaddr.Address{
 				newAddressPubKey(hexToBytes("04cb9c3c222c5f7a" +
 					"7d3b9bd152f363a0b6d54c9eb312c4d4f9af" +
 					"1e8551b6c421a6a4ab0e29105f24de20ff46" +
@@ -288,7 +287,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 				"16e20626520666f756e6420696e207472616e7361637" +
 				"4696f6e2036633533636439383731313965663739376" +
 				"435616463636453ae"),
-			addrs:   []dcrutil.Address{},
+			addrs:   []stdaddr.Address{},
 			reqSigs: 1,
 			class:   MultiSigTy,
 		},
@@ -305,7 +304,7 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 				"13963663463303363363039633539336333653931666" +
 				"56465373032392102323364643432643235363339643" +
 				"338613663663530616234636434340a00000053ae"),
-			addrs:   []dcrutil.Address{},
+			addrs:   []stdaddr.Address{},
 			reqSigs: 1,
 			class:   MultiSigTy,
 		},
@@ -391,146 +390,6 @@ func TestExtractPkScriptAddrs(t *testing.T) {
 	}
 }
 
-// bogusAddress implements the dcrutil.Address interface so the tests can ensure
-// unsupported address types are handled properly.
-type bogusAddress struct{}
-
-// Address simply returns an empty string.  It exists to satisfy the
-// dcrutil.Address interface.
-func (b *bogusAddress) Address() string {
-	return ""
-}
-
-// ScriptAddress simply returns an empty byte slice.  It exists to satisfy the
-// dcrutil.Address interface.
-func (b *bogusAddress) ScriptAddress() []byte {
-	return nil
-}
-
-// Hash160 simply returns an empty byte slice.  It exists to satisfy the
-// dcrutil.Address interface.
-func (b *bogusAddress) Hash160() *[20]byte {
-	return nil
-}
-
-// String simply returns an empty string.  It exists to satisfy the
-// dcrutil.Address interface.
-func (b *bogusAddress) String() string {
-	return ""
-}
-
-// TestPayToAddrScript ensures the PayToAddrScript function generates the
-// correct scripts for the various types of addresses.
-func TestPayToAddrScript(t *testing.T) {
-	t.Parallel()
-
-	// 1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX
-	p2pkhMain, err := dcrutil.NewAddressPubKeyHash(hexToBytes("e34cce70c86"+
-		"373273efcc54ce7d2a491bb4a0e84"), mainNetParams, dcrec.STEcdsaSecp256k1)
-	if err != nil {
-		t.Fatalf("Unable to create public key hash address: %v", err)
-	}
-
-	// Taken from transaction:
-	// b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d
-	p2shMain, _ := dcrutil.NewAddressScriptHashFromHash(hexToBytes("e8c30"+
-		"0c87986efa84c37c0519929019ef86eb5b4"), mainNetParams)
-	if err != nil {
-		t.Fatalf("Unable to create script hash address: %v", err)
-	}
-
-	//  mainnet p2pk 13CG6SJ3yHUXo4Cr2RY4THLLJrNFuG3gUg
-	p2pkCompressedMain, err := dcrutil.NewAddressSecpPubKey(hexToBytes("02192d7"+
-		"4d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
-		mainNetParams)
-	if err != nil {
-		t.Fatalf("Unable to create pubkey address (compressed): %v",
-			err)
-	}
-	p2pkCompressed2Main, err := dcrutil.NewAddressSecpPubKey(hexToBytes("03b0b"+
-		"d634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"),
-		mainNetParams)
-	if err != nil {
-		t.Fatalf("Unable to create pubkey address (compressed 2): %v",
-			err)
-	}
-
-	p2pkUncompressedMain := newAddressPubKey(hexToBytes("0411db" +
-		"93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2" +
-		"e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"))
-
-	tests := []struct {
-		in       dcrutil.Address
-		expected string
-		err      error
-	}{
-		// pay-to-pubkey-hash address on mainnet 0
-		{
-			p2pkhMain,
-			"DUP HASH160 DATA_20 0xe34cce70c86373273efcc54ce7d2a4" +
-				"91bb4a0e8488 CHECKSIG",
-			nil,
-		},
-		// pay-to-script-hash address on mainnet 1
-		{
-			p2shMain,
-			"HASH160 DATA_20 0xe8c300c87986efa84c37c0519929019ef8" +
-				"6eb5b4 EQUAL",
-			nil,
-		},
-		// pay-to-pubkey address on mainnet. compressed key. 2
-		{
-			p2pkCompressedMain,
-			"DATA_33 0x02192d74d0cb94344c9569c2e77901573d8d7903c3" +
-				"ebec3a957724895dca52c6b4 CHECKSIG",
-			nil,
-		},
-		// pay-to-pubkey address on mainnet. compressed key (other way). 3
-		{
-			p2pkCompressed2Main,
-			"DATA_33 0x03b0bd634234abbb1ba1e986e884185c61cf43e001" +
-				"f9137f23c2c409273eb16e65 CHECKSIG",
-			nil,
-		},
-		// pay-to-pubkey address on mainnet. for Decred this would
-		// be uncompressed, but standard for Decred is 33 byte
-		// compressed public keys.
-		{
-			p2pkUncompressedMain,
-			"DATA_33 0x0311db93e1dcdb8a016b49840f8c53bc1eb68a382e97b" +
-				"1482ecad7b148a6909a5cac",
-			nil,
-		},
-
-		// Supported address types with nil pointers.
-		{(*dcrutil.AddressPubKeyHash)(nil), "", ErrUnsupportedAddress},
-		{(*dcrutil.AddressScriptHash)(nil), "", ErrUnsupportedAddress},
-		{(*dcrutil.AddressSecpPubKey)(nil), "", ErrUnsupportedAddress},
-		{(*dcrutil.AddressEdwardsPubKey)(nil), "", ErrUnsupportedAddress},
-		{(*dcrutil.AddressSecSchnorrPubKey)(nil), "", ErrUnsupportedAddress},
-
-		// Unsupported address type.
-		{&bogusAddress{}, "", ErrUnsupportedAddress},
-	}
-
-	t.Logf("Running %d tests", len(tests))
-	for i, test := range tests {
-		pkScript, err := PayToAddrScript(test.in)
-		if !errors.Is(err, test.err) {
-			t.Errorf("PayToAddrScript #%d unexpected error - got %v, want %v",
-				i, err, test.err)
-			continue
-		}
-
-		expected := mustParseShortForm(test.expected)
-		if !bytes.Equal(pkScript, expected) {
-			t.Errorf("PayToAddrScript #%d got: %x\nwant: %x",
-				i, pkScript, expected)
-			continue
-		}
-	}
-}
-
 // TestMultiSigScript ensures the MultiSigScript function returns the expected
 // scripts and errors.
 func TestMultiSigScript(t *testing.T) {
@@ -1055,51 +914,6 @@ func TestGenerateProvablyPruneableOut(t *testing.T) {
 	}
 }
 
-// TestGenerateSStxAddrPush ensures an expected OP_RETURN push is generated.
-func TestGenerateSStxAddrPush(t *testing.T) {
-	testNetParams := chaincfg.TestNet3Params()
-	var tests = []struct {
-		addrStr  string
-		net      dcrutil.AddressParams
-		amount   dcrutil.Amount
-		limits   uint16
-		expected []byte
-	}{
-		{
-			"Dcur2mcGjmENx4DhNqDctW5wJCVyT3Qeqkx",
-			mainNetParams,
-			1000,
-			10,
-			hexToBytes("6a1ef5916158e3e2c4551c1796708db8367207ed1" +
-				"3bbe8030000000000800a00"),
-		},
-		{
-			"TscB7V5RuR1oXpA364DFEsNDuAs8Rk6BHJE",
-			testNetParams,
-			543543,
-			256,
-			hexToBytes("6a1e7a5c4cca76f2e0b36db4763daacbd6cbb6ee6" +
-				"e7b374b0800000000000001"),
-		},
-	}
-	for _, test := range tests {
-		addr, err := dcrutil.DecodeAddress(test.addrStr, test.net)
-		if err != nil {
-			t.Errorf("DecodeAddress failed: %v", err)
-			continue
-		}
-		s, err := GenerateSStxAddrPush(addr, test.amount, test.limits)
-		if err != nil {
-			t.Errorf("GenerateSStxAddrPush failed: %v", err)
-			continue
-		}
-		if !bytes.Equal(s, test.expected) {
-			t.Errorf("GenerateSStxAddrPush: unexpected script:\n "+
-				"got %x\nwant %x", s, test.expected)
-		}
-	}
-}
-
 // TestGenerateSSGenBlockRef ensures an expected OP_RETURN push is generated.
 func TestGenerateSSGenBlockRef(t *testing.T) {
 	var tests = []struct {