diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py
index 4777084be9e9543dbc92e5f34897dca6f4631032..95a45a4388485c136e00087d29db949046e84271 100644
--- a/src/_zkapauthorizer/_plugin.py
+++ b/src/_zkapauthorizer/_plugin.py
@@ -25,7 +25,11 @@ from weakref import WeakValueDictionary
 
 import attr
 from allmydata.client import _Client
-from allmydata.interfaces import IAnnounceableStorageServer, IFoolscapStoragePlugin, IFilesystemNode
+from allmydata.interfaces import (
+    IAnnounceableStorageServer,
+    IFilesystemNode,
+    IFoolscapStoragePlugin,
+)
 from allmydata.node import MissingConfigEntry
 from challenge_bypass_ristretto import PublicKey, SigningKey
 from eliot import start_action
diff --git a/src/_zkapauthorizer/controller.py b/src/_zkapauthorizer/controller.py
index 707811298e8f9223c15ee33c9900ee71b3a8070a..de456a9ec3786a1b8a1c0cc396419407635fe35e 100644
--- a/src/_zkapauthorizer/controller.py
+++ b/src/_zkapauthorizer/controller.py
@@ -99,10 +99,10 @@ class RedemptionResult(object):
         the redemption process.
     """
 
-    unblinded_tokens = attr.ib(  # type: List[UnblindedToken]
+    unblinded_tokens: List[UnblindedToken] = attr.ib(
         validator=attr.validators.instance_of(list),
     )
-    public_key = attr.ib(  # type: str
+    public_key: str = attr.ib(
         validator=attr.validators.instance_of(str),
     )
 
diff --git a/src/_zkapauthorizer/spending.py b/src/_zkapauthorizer/spending.py
index 89625b564b0afad54f60376a93f8e1986f738b07..e3af5af91919bd9c075343b6f418a67bbb6970c5 100644
--- a/src/_zkapauthorizer/spending.py
+++ b/src/_zkapauthorizer/spending.py
@@ -16,7 +16,7 @@
 A module for logic controlling the manner in which ZKAPs are spent.
 """
 
-from typing import List, Tuple
+from typing import Callable, List, Tuple
 
 import attr
 from zope.interface import Attribute, Interface, implementer
@@ -112,26 +112,21 @@ class PassGroup(object):
     :ivar list[Pass] passes: The passes of which this group consists.
     """
 
-    _message = attr.ib(validator=attr.validators.instance_of(bytes))  # type: bytes
-    _factory = attr.ib(
-        validator=attr.validators.provides(IPassFactory)
-    )  # type: IPassFactory
-    _tokens = attr.ib(
+    _message: bytes = attr.ib(validator=attr.validators.instance_of(bytes))
+    _factory: IPassFactory = attr.ib(validator=attr.validators.provides(IPassFactory))
+    _tokens: List[Tuple[UnblindedToken, Pass]] = attr.ib(
         validator=attr.validators.instance_of(list)
-    )  # type: List[Tuple[UnblindedToken, Pass]]
+    )
 
     @property
-    def passes(self):
-        # type: () -> List[Pass]
+    def passes(self) -> List[Pass]:
         return list(pass_ for (unblinded_token, pass_) in self._tokens)
 
     @property
-    def unblinded_tokens(self):
-        # type: () -> List[UnblindedToken]
+    def unblinded_tokens(self) -> List[UnblindedToken]:
         return list(unblinded_token for (unblinded_token, pass_) in self._tokens)
 
-    def split(self, select_indices):
-        # type: (List[int]) -> (PassGroup, PassGroup)
+    def split(self, select_indices: List[int]) -> ("PassGroup", "PassGroup"):
         selected = []
         unselected = []
         for idx, t in enumerate(self._tokens):
@@ -144,23 +139,19 @@ class PassGroup(object):
             attr.evolve(self, tokens=unselected),
         )
 
-    def expand(self, by_amount):
-        # type: (int) -> PassGroup
+    def expand(self, by_amount: int) -> "PassGroup":
         return attr.evolve(
             self,
             tokens=self._tokens + self._factory.get(self._message, by_amount)._tokens,
         )
 
-    def mark_spent(self):
-        # type: () -> None
+    def mark_spent(self) -> None:
         self._factory._mark_spent(self.unblinded_tokens)
 
-    def mark_invalid(self, reason):
-        # type: () -> None
+    def mark_invalid(self, reason) -> None:
         self._factory._mark_invalid(reason, self.unblinded_tokens)
 
-    def reset(self):
-        # tye: () -> None
+    def reset(self) -> None:
         self._factory._reset(self.unblinded_tokens)
 
 
@@ -172,12 +163,12 @@ class SpendingController(object):
     attempts when necessary.
     """
 
-    get_unblinded_tokens = attr.ib()  # type: (int) -> [UnblindedToken]
-    discard_unblinded_tokens = attr.ib()  # type: ([UnblindedToken]) -> None
-    invalidate_unblinded_tokens = attr.ib()  # type: ([UnblindedToken]) -> None
-    reset_unblinded_tokens = attr.ib()  # type: ([UnblindedToken]) -> None
+    get_unblinded_tokens: Callable[[int], List[UnblindedToken]] = attr.ib()
+    discard_unblinded_tokens: Callable[[List[UnblindedToken]], None] = attr.ib()
+    invalidate_unblinded_tokens: Callable[[List[UnblindedToken]], None] = attr.ib()
+    reset_unblinded_tokens: Callable[[List[UnblindedToken]], None] = attr.ib()
 
-    tokens_to_passes = attr.ib()  # type: (bytes, [UnblindedToken]) -> [Pass]
+    tokens_to_passes: Callable[[bytes, List[UnblindedToken]], List[Pass]] = attr.ib()
 
     @classmethod
     def for_store(cls, tokens_to_passes, store):
diff --git a/src/_zkapauthorizer/tests/storage_common.py b/src/_zkapauthorizer/tests/storage_common.py
index 62bfae30a6bc885183a69528b51a2cce903baa24..a168395ca073ba197ece412a39dbcbf346c2a3f6 100644
--- a/src/_zkapauthorizer/tests/storage_common.py
+++ b/src/_zkapauthorizer/tests/storage_common.py
@@ -20,10 +20,10 @@ from functools import partial
 from itertools import islice
 from os import SEEK_CUR
 from struct import pack
-from typing import Dict, List, Set
+from typing import Callable, Dict, List, Set
 
 import attr
-from challenge_bypass_ristretto import RandomToken
+from challenge_bypass_ristretto import RandomToken, SigningKey
 from twisted.python.filepath import FilePath
 from zope.interface import implementer
 
@@ -135,7 +135,7 @@ def whitebox_write_sparse_share(sharepath, version, size, leases, now):
         )
 
 
-def integer_passes(limit):
+def integer_passes(limit: int) -> Callable[[bytes, int], List[int]]:
     """
     :return: A function which can be used to get a number of passes.  The
         function accepts a unicode request-binding message and an integer
@@ -153,7 +153,9 @@ def integer_passes(limit):
     return get_passes
 
 
-def get_passes(message, count, signing_key):
+def get_passes(
+    message: bytes, count: int, signing_key: SigningKey
+) -> List[RandomToken]:
     """
     :param bytes message: Request-binding message for PrivacyPass.
 
@@ -219,16 +221,15 @@ class _PassFactory(object):
         via ``IPassGroup.reset``.
     """
 
-    _get_passes = attr.ib()  # type: (bytes, int) -> List[bytes]
+    _get_passes: Callable[[bytes, int], List[bytes]] = attr.ib()
 
-    returned = attr.ib(default=attr.Factory(list), init=False)  # type: List[int]
-    in_use = attr.ib(default=attr.Factory(set), init=False)  # type: Set[int]
-    invalid = attr.ib(default=attr.Factory(dict), init=False)  # type: Dict[int, str]
-    spent = attr.ib(default=attr.Factory(set), init=False)  # type: Set[int]
-    issued = attr.ib(default=attr.Factory(set), init=False)  # type: Set[int]
+    returned: List[int] = attr.ib(default=attr.Factory(list), init=False)
+    in_use: Set[int] = attr.ib(default=attr.Factory(set), init=False)
+    invalid: Dict[int, str] = attr.ib(default=attr.Factory(dict), init=False)
+    spent: Set[int] = attr.ib(default=attr.Factory(set), init=False)
+    issued: Set[int] = attr.ib(default=attr.Factory(set), init=False)
 
-    def get(self, message, num_passes):
-        # type: (bytes, int) -> PassGroup
+    def get(self, message: bytes, num_passes: int) -> PassGroup:
         passes = []
         if self.returned:
             passes.extend(self.returned[:num_passes])
diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py
index 0136da1173aa7cc29128f6754ba4f6e1b53d31a8..01b56e4b236be033d5685281dcaafc11fac878d3 100644
--- a/src/_zkapauthorizer/tests/test_plugin.py
+++ b/src/_zkapauthorizer/tests/test_plugin.py
@@ -25,10 +25,10 @@ from os import makedirs
 from allmydata.client import config_from_string, create_client_from_config
 from allmydata.interfaces import (
     IAnnounceableStorageServer,
+    IFilesystemNode,
     IFoolscapStoragePlugin,
     IStorageServer,
     RIStorageServer,
-    IFilesystemNode,
 )
 from challenge_bypass_ristretto import SigningKey
 from eliot.testing import LoggedMessage, capture_logging
@@ -68,7 +68,7 @@ from twisted.web.resource import IResource
 
 from twisted.plugins.zkapauthorizer import storage_server
 
-from .._plugin import load_signing_key, get_root_nodes
+from .._plugin import get_root_nodes, load_signing_key
 from .._storage_client import IncorrectStorageServerReference
 from ..controller import DummyRedeemer, IssuerConfigurationMismatch, PaymentController
 from ..foolscap import RIPrivacyPassAuthorizedStorageServer