diff --git a/Makefile b/Makefile
index cd183023d09da89d457b2f7a4ec08d33ab75db15..693d4c626a8ceecb85204819f36ccca46e203d3c 100644
--- a/Makefile
+++ b/Makefile
@@ -11,20 +11,20 @@ build:
 	GOOS=js GOARCH=wasm go build ./...
 
 update_release:
-	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@release
 	GOFLAGS="" go get gitlab.com/elixxir/primitives@release
 	GOFLAGS="" go get gitlab.com/xx_network/crypto@release
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@release
-	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@project/HavenBeta
+	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@release
+	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
 
 update_master:
-	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@master
 	GOFLAGS="" go get gitlab.com/elixxir/primitives@master
 	GOFLAGS="" go get gitlab.com/xx_network/crypto@master
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@master
 	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@master
+	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
 
 binary:
 	GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk.wasm main.go
diff --git a/go.mod b/go.mod
index cfd6919d47ca27219deda0a5f53c7ee8f7e302fe..36c45b73c8a414b387ac213eed919af54b8822d5 100644
--- a/go.mod
+++ b/go.mod
@@ -9,10 +9,10 @@ require (
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/stretchr/testify v1.8.2
-	gitlab.com/elixxir/client/v4 v4.6.4-0.20230608163043-96d0be74ffb0
-	gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235
+	gitlab.com/elixxir/client/v4 v4.6.4-0.20230608170359-6f802c11e487
+	gitlab.com/elixxir/crypto v0.0.7-0.20230607170539-92d9508c78f9
 	gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c
-	gitlab.com/elixxir/wasm-utils v0.0.0-20230522231408-a43b2c1481b2
+	gitlab.com/elixxir/wasm-utils v0.0.0-20230607204433-22c46f9d680f
 	gitlab.com/xx_network/crypto v0.0.5-0.20230214003943-8a09396e95dd
 	gitlab.com/xx_network/primitives v0.0.4-0.20230522171102-940cdd68e516
 	golang.org/x/crypto v0.5.0
@@ -21,6 +21,7 @@ require (
 require (
 	filippo.io/edwards25519 v1.0.0 // indirect
 	git.xx.network/elixxir/grpc-web-go-client v0.0.0-20230214175953-5b5a8c33d28a // indirect
+	github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd // indirect
 	github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
 	github.com/badoux/checkmail v1.2.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.3 // indirect
diff --git a/go.sum b/go.sum
index 9a7a679c0ec3004200d135bd386af7c1e2dc3bea..e2d036e83fd8a7a6ac0ede5fbe27536029efd132 100644
--- a/go.sum
+++ b/go.sum
@@ -43,6 +43,8 @@ git.xx.network/elixxir/grpc-web-go-client v0.0.0-20230214175953-5b5a8c33d28a/go.
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9IlZinHa+HVffy+NmVRoKr+wHN8fpLE=
+github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@@ -513,76 +515,22 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
 github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20230322223210-fa84f6842de8 h1:uAFCyBkXprQoPkcDDfxXtaMyL5x+xSGrAWzR907xROQ=
 gitlab.com/elixxir/bloomfilter v0.0.0-20230322223210-fa84f6842de8/go.mod h1:1X8gRIAPDisS3W6Vtr/ymiUmZMJUIwDV1o5DEOo/pzw=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230525191042-3795029e4315 h1:rOgY9KPwq0wCQGM2VWzHQLidUwo/igqEbHvrfrX14NE=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230525191042-3795029e4315/go.mod h1:1+FU4spF6kwSA84AnFq0i6j4jsAICuvVfp6ACh00K0U=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230526074923-c0fb2a45c312 h1:yUyz1L/rzodZxUU45CeH7mfMfmKAs706+s2j2ZGUSwE=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230526074923-c0fb2a45c312/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530165750-f6ea41bc69e4 h1:dgq4fwLdVzsPGdk1E7vLZI1gWk/QB/K7Rntg+DrxAjY=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530165750-f6ea41bc69e4/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530170051-243baab3da48 h1:2HP8w4HOlqjg0FcvSnhMmpwUa1pcU0WbvWMOUNiy/hE=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530170051-243baab3da48/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530182422-d12ab437257f h1:8TJwqKjeD0eZSvWIlPwjgIwfgdzcHhTzGwaiUJCwwuI=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530182422-d12ab437257f/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530201605-ba087da90ba2 h1:sxAgLmG2RLarBuXtcH8g5ZcQbAF9PmS3wrnlwZVFrLA=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530201605-ba087da90ba2/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530211720-a1bc89986df8 h1:XTklpI9leJYgZ4bYZLQUOzzgoxlYUz7xDmcl2Fy7knc=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530211720-a1bc89986df8/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530220505-726d2194d278 h1:etC7c2JeKbykuSHIm8jvb3cpMUdJGlyMsBQbImXlIK8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530220505-726d2194d278/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530222715-a0c0ea0de57f h1:67k1pJg8w6bGfDLex3/e4rdm8Cb2m43qjrryKjVo/UE=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230530222715-a0c0ea0de57f/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531180314-040f2bf4bc9f h1:KJQjb0WfOtrJo1vKGeISwlmdQTwtV3CmCg4nbP6bzlM=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531180314-040f2bf4bc9f/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531193105-0f398d814565 h1:kBbMa8AdCD/0KaLY7jIMk8yWsrRYSMb4zrdGRT4xZ6w=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531193105-0f398d814565/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531195028-ad25aee27c19 h1:OLZTJAqRm1hqGeeHs+aYZ9v3ojgiv+6qykC8+9pFBVM=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531195028-ad25aee27c19/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531195851-a65797612a21 h1:u4H7ITaOwO4aQoSJCo8zpfRpenaqgHuvjqF3GAe8RrE=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531195851-a65797612a21/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531212217-e21b55fbf23c h1:6OTyW9U/ntZ41Xry+8qDkOH4LoxhlyWTwKpAxi2fvZY=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531212217-e21b55fbf23c/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531224642-b24cd3f5e4a4 h1:YacWU7IJUfixfZyckdXEWPwhix7sq+STK+8Vz8DzVO4=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230531224642-b24cd3f5e4a4/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230601191407-1b0289b33708 h1:wNIKci4XuDfzAJM8+4awP5CgY0jlOEaVbHgp+BkEtz4=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230601191407-1b0289b33708/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230602201618-11b41b04386f h1:jwXyq/17fo5VxdQUDQiIfUHqr8m6Df1bV8wRjV4ssyw=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230602201618-11b41b04386f/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230602205033-94b7a8f73adf h1:n9P9trdEGTBQBhraTI800+ZTrNeahmb01uFtfuyaga0=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230602205033-94b7a8f73adf/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230605163131-05f3396132b7 h1:10vkqej0JYCBYgJqjclTBnfVpc3//SY015O69HbTaVA=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230605163131-05f3396132b7/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230605225443-4ab5e5b32c16 h1:OEiENhrAHDgSG5ZI3GLCgYr+e7sLKmYr/6AC+HBHdsc=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230605225443-4ab5e5b32c16/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230605230916-3e5554db4b70 h1:8GTmUVLfPasdreToIgnoduhwVvUeAnUnUs2btzgBr+0=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230605230916-3e5554db4b70/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606161714-339cef753b8d h1:o7PqwDJ8fSoHc+AYX1InCD1uI2SmMkawwdJMvaxKjmY=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606161714-339cef753b8d/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606201910-fe9f57e3141f h1:JTPDrf4MsaYIuUOFTawEQLZ9rNbCA/SBENPUMFNFijY=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606201910-fe9f57e3141f/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606202433-a9136c29d24d h1:bh4tINZyGSZPS9TSOd9exPymYA9shMSvAqeslMGZFH0=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606202433-a9136c29d24d/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606205101-cc748980feed h1:UOrzm9I7ZwKSxl+3NKf3hyqHLfKlneE9ifx533bXMSg=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606205101-cc748980feed/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606205543-83a88c354a91 h1:7+XixtLZdt9jnDFKsOovfIFQ9Z3/hKNtxNOn3ixLhGQ=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230606205543-83a88c354a91/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230607192055-bb48aae366c4 h1:65ivQ2uhBNYPuxZAC9vMIH+4Sljzu2GD9tQpqcOkphk=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230607192055-bb48aae366c4/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230607195322-23fc4942a31f h1:bH4KKrsoMbIR0nga2F2kIVuTyAGfKvNtV8wd5JIbric=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230607195322-23fc4942a31f/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230608163043-96d0be74ffb0 h1:jkdITfmmBH8ou6ip43OvyplXrmZfnVrP+5Y6ZI0R2L0=
-gitlab.com/elixxir/client/v4 v4.6.4-0.20230608163043-96d0be74ffb0/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230607172502-b56996e764f5 h1:TbuQ5a0HJifvNaUey1AnqpX8v/OPdb2GvNmHPy2hm9g=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230607172502-b56996e764f5/go.mod h1:LmbxL4WIT2qiDN7wzpcJG+U13jn3pSqUHtT1+uxN4VI=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230607204847-4a74a5fe2066 h1:NxTd/+AjbVkg+uzf1dIRF23AoDBDDI83jQ2lMJzdEcw=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230607204847-4a74a5fe2066/go.mod h1:GyXpu03l8AybmrGSq+AiKTuB+oJI5of78MdvdqFo29U=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230608170359-6f802c11e487 h1:th4mIU968ZuTd0ZhX6RakJKwgQ3zUESENXdlsWe9jco=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230608170359-6f802c11e487/go.mod h1:GyXpu03l8AybmrGSq+AiKTuB+oJI5of78MdvdqFo29U=
 gitlab.com/elixxir/comms v0.0.4-0.20230519211512-4a998f4b0938 h1:f27+QUFiGWrprKm+fstOg3ABkYLpWcZi3+8Lf5eDnqY=
 gitlab.com/elixxir/comms v0.0.4-0.20230519211512-4a998f4b0938/go.mod h1:z+qW0D9VpY5QKTd7wRlb5SK4kBNqLYsa4DXBcUXue9Q=
-gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235 h1:0BySdXTzRWxzH8k5RiNNMmmn2lpuQWLVcDDA/7ehyqc=
-gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235/go.mod h1:IYInxKr5Q7EH3oNhg1QX1/sTTRNi7L0JkcyfdRegoio=
-gitlab.com/elixxir/ekv v0.3.1-0.20230525165450-f444c687504b h1:hf28yepO93tCacx1bUAh8vVFkBUEuBaJhOjifBxEQK4=
-gitlab.com/elixxir/ekv v0.3.1-0.20230525165450-f444c687504b/go.mod h1:EMaUQrsOxvEPQ0/8V/PSkGqFmEC2axBG/uqY0oW2uJM=
+gitlab.com/elixxir/crypto v0.0.7-0.20230607170539-92d9508c78f9 h1:+RZcoU7NOPXCenkKM/ImYyLcNDdG4vsWrJyKqoAAv8k=
+gitlab.com/elixxir/crypto v0.0.7-0.20230607170539-92d9508c78f9/go.mod h1:lAib0KO9TeTLWbwgFk2uszRxPkHeu843xqnYdkzdEB0=
 gitlab.com/elixxir/ekv v0.3.1-0.20230525213559-f9da13f4fce1 h1:8XBo6QQBXXGCTrgXHFuqPL21mROLKLAoO3X9xR5TwA0=
 gitlab.com/elixxir/ekv v0.3.1-0.20230525213559-f9da13f4fce1/go.mod h1:UStTZ9d1UVn9Ahyb49lrbPKyr/Wb8xFWqMXbDgIqQhE=
 gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c h1:muG8ff95woeVVwQoJHCEclxBFB22lc7EixPylEkYDRU=
 gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c/go.mod h1:phun4PLkHJA6wcL4JIhhxZztrmCyJHWPNppBP3DUD2Y=
-gitlab.com/elixxir/wasm-utils v0.0.0-20230522231408-a43b2c1481b2 h1:GQb350yPBkWRkPRgNSVFF0ZZDOAlXWIKQBI/1Ff6biU=
-gitlab.com/elixxir/wasm-utils v0.0.0-20230522231408-a43b2c1481b2/go.mod h1:wB7Vh/7LWUm8wYRBSd+6lxfpk4CnDaHTkLCIVKfL2TA=
+gitlab.com/elixxir/wasm-utils v0.0.0-20230607204433-22c46f9d680f h1:Nz87zyNu85uxCI+fiS7dJl6HHxPoGUzyec4FpZy7Vv8=
+gitlab.com/elixxir/wasm-utils v0.0.0-20230607204433-22c46f9d680f/go.mod h1:wB7Vh/7LWUm8wYRBSd+6lxfpk4CnDaHTkLCIVKfL2TA=
 gitlab.com/xx_network/comms v0.0.4-0.20230214180029-5387fb85736d h1:AZf2h0fxyO1KxhZPP9//jG3Swb2BcuKbxtNXJgooLss=
 gitlab.com/xx_network/comms v0.0.4-0.20230214180029-5387fb85736d/go.mod h1:8cwPyH6G8C4qf/U5KDghn1ksOh79MrNqthjKDrfvbXY=
 gitlab.com/xx_network/crypto v0.0.5-0.20230214003943-8a09396e95dd h1:IleH6U5D/c2zF6YL/z3cBKqBPnI5ApNMCtU7ia4t228=
diff --git a/indexedDb/impl/channels/callbacks.go b/indexedDb/impl/channels/callbacks.go
index 2a901597117fac367e360ee2f80a2567fcab036f..7fe0a8a13385c5984ccc005853231634a9d4a8d6 100644
--- a/indexedDb/impl/channels/callbacks.go
+++ b/indexedDb/impl/channels/callbacks.go
@@ -18,8 +18,8 @@ import (
 	"gitlab.com/elixxir/client/v4/channels"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	wChannels "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
@@ -64,7 +64,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
 
 	// Create new encryption cipher
 	rng := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
-	encryption, err := cryptoChannel.NewCipherFromJSON(
+	encryption, err := idbCrypto.NewCipherFromJSON(
 		[]byte(msg.EncryptionJSON), rng.GetStream())
 	if err != nil {
 		return []byte{}, errors.Errorf(
diff --git a/indexedDb/impl/channels/implementation.go b/indexedDb/impl/channels/implementation.go
index e755acbafb3f750e577305fb9e241c537f30febe..dfc000eba4476870a38b3e6dbd7c8181f55fb5f3 100644
--- a/indexedDb/impl/channels/implementation.go
+++ b/indexedDb/impl/channels/implementation.go
@@ -25,7 +25,7 @@ import (
 	"gitlab.com/elixxir/client/v4/channels"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/wasm-utils/utils"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
@@ -37,7 +37,7 @@ import (
 // caller to ensure that its methods are called sequentially.
 type wasmModel struct {
 	db          *idb.Database
-	cipher      cryptoChannel.Cipher
+	cipher      idbCrypto.Cipher
 	eventUpdate func(eventType int64, jsonMarshallable any)
 }
 
@@ -145,12 +145,11 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID,
 	nickname, text string, pubKey ed25519.PublicKey, dmToken uint32,
 	codeset uint8, timestamp time.Time, lease time.Duration, round rounds.Round,
 	mType channels.MessageType, status channels.SentStatus, hidden bool) uint64 {
-	textBytes := []byte(text)
 	var err error
 
 	// Handle encryption, if it is present
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt([]byte(text))
+		text, err = w.cipher.Encrypt([]byte(text))
 		if err != nil {
 			jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
 			return 0
@@ -161,7 +160,7 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID,
 
 	msgToInsert := buildMessage(
 		channelIDBytes, messageID.Bytes(), nil, nickname,
-		textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
+		text, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
 		false, hidden, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
@@ -189,12 +188,11 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID,
 	dmToken uint32, codeset uint8, timestamp time.Time, lease time.Duration,
 	round rounds.Round, mType channels.MessageType, status channels.SentStatus,
 	hidden bool) uint64 {
-	textBytes := []byte(text)
 	var err error
 
 	// Handle encryption, if it is present
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt([]byte(text))
+		text, err = w.cipher.Encrypt([]byte(text))
 		if err != nil {
 			jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
 			return 0
@@ -204,7 +202,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID,
 	channelIDBytes := channelID.Marshal()
 
 	msgToInsert := buildMessage(channelIDBytes, messageID.Bytes(),
-		replyTo.Bytes(), nickname, textBytes, pubKey, dmToken, codeset,
+		replyTo.Bytes(), nickname, text, pubKey, dmToken, codeset,
 		timestamp, lease, round.ID, mType, hidden, false, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
@@ -232,12 +230,11 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID,
 	dmToken uint32, codeset uint8, timestamp time.Time, lease time.Duration,
 	round rounds.Round, mType channels.MessageType, status channels.SentStatus,
 	hidden bool) uint64 {
-	textBytes := []byte(reaction)
 	var err error
 
 	// Handle encryption, if it is present
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt([]byte(reaction))
+		reaction, err = w.cipher.Encrypt([]byte(reaction))
 		if err != nil {
 			jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
 			return 0
@@ -247,7 +244,7 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID,
 	channelIDBytes := channelID.Marshal()
 	msgToInsert := buildMessage(
 		channelIDBytes, messageID.Bytes(), reactionTo.Bytes(), nickname,
-		textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
+		reaction, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
 		false, hidden, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
@@ -349,8 +346,8 @@ func (w *wasmModel) UpdateFromMessageID(messageID message.ID,
 // NOTE: ID is not set inside this function because we want to use the
 // autoincrement key by default. If you are trying to overwrite an existing
 // message, then you need to set it manually yourself.
-func buildMessage(channelID, messageID, parentID []byte, nickname string,
-	text []byte, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
+func buildMessage(channelID, messageID, parentID []byte, nickname,
+	text string, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
 	timestamp time.Time, lease time.Duration, round id.Round,
 	mType channels.MessageType, pinned, hidden bool,
 	status channels.SentStatus) *Message {
@@ -499,7 +496,7 @@ func (w *wasmModel) GetMessage(
 		Status:          channels.SentStatus(lookupResult.Status),
 		Hidden:          lookupResult.Hidden,
 		Pinned:          lookupResult.Pinned,
-		Content:         lookupResult.Text,
+		Content:         []byte(lookupResult.Text),
 		Type:            channels.MessageType(lookupResult.Type),
 		Round:           id.Round(lookupResult.Round),
 		PubKey:          lookupResult.Pubkey,
diff --git a/indexedDb/impl/channels/implementation_test.go b/indexedDb/impl/channels/implementation_test.go
index f596065dcc47aa39e3b5aeda7b97a1f389071977..58cd04a1c6d346a02d0073ef33e9e5e292c8607e 100644
--- a/indexedDb/impl/channels/implementation_test.go
+++ b/indexedDb/impl/channels/implementation_test.go
@@ -27,8 +27,8 @@ import (
 	cft "gitlab.com/elixxir/client/v4/channelsFileTransfer"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fileTransfer"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/wasm-utils/storage"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
@@ -120,12 +120,12 @@ func TestWasmModel_ReceiveFile(t *testing.T) {
 
 // Happy path, insert message and look it up
 func TestWasmModel_GetMessage(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -142,7 +142,7 @@ func TestWasmModel_GetMessage(t *testing.T) {
 			}
 
 			testMsg := buildMessage(id.NewIdFromBytes([]byte(testString), t).Marshal(),
-				testMsgId.Bytes(), nil, testString, []byte(testString),
+				testMsgId.Bytes(), nil, testString, testString,
 				[]byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
 				time.Second, 0, 0, false, false, channels.Sent)
 			_, err = eventModel.upsertMessage(testMsg)
@@ -173,7 +173,7 @@ func TestWasmModel_DeleteMessage(t *testing.T) {
 
 	// Insert a message
 	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
-		testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
+		testString, testString, []byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
 		time.Second, 0, 0, false, false, channels.Sent)
 	_, err = eventModel.upsertMessage(testMsg)
 	if err != nil {
@@ -207,12 +207,12 @@ func TestWasmModel_DeleteMessage(t *testing.T) {
 
 // Test wasmModel.UpdateSentStatus happy path and ensure fields don't change.
 func Test_wasmModel_UpdateSentStatus(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -234,7 +234,7 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 
 			// Store a test message
 			testMsg := buildMessage(cid.Bytes(), testMsgId.Bytes(), nil,
-				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
+				testString, testString, []byte{8, 6, 7, 5}, 0, 0,
 				netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
 			uuid, err2 := eventModel.upsertMessage(testMsg)
 			if err2 != nil {
@@ -282,12 +282,12 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 
 // Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths.
 func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -334,12 +334,12 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
 
 // Test UUID gets returned when different messages are added.
 func Test_wasmModel_UUIDTest(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -381,12 +381,12 @@ func Test_wasmModel_UUIDTest(t *testing.T) {
 
 // Tests if the same message ID being sent always returns the same UUID.
 func Test_wasmModel_DuplicateReceives(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -428,12 +428,12 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) {
 // Happy path: Inserts many messages, deletes some, and checks that the final
 // result is as expected.
 func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -501,12 +501,12 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
 // This test is designed to prove the behavior of unique indexes.
 // Inserts will not fail, they simply will not happen.
 func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for i, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for i, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -542,12 +542,12 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
 
 			testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString))
 			testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
-				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
+				testString, testString, []byte{8, 6, 7, 5}, 0, 0,
 				netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
 
 			testMsgId2 := message.DeriveChannelMessageID(&id.ID{2}, 0, []byte(testString))
 			testMsg2 := buildMessage([]byte(testString), testMsgId2.Bytes(), nil,
-				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
+				testString, testString, []byte{8, 6, 7, 5}, 0, 0,
 				netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
 
 			// First message insert should succeed
diff --git a/indexedDb/impl/channels/init.go b/indexedDb/impl/channels/init.go
index 48f5359f9b61c67b42533a42fa37cec4c3c917e7..e604b666636e0953a0a9e19947c53550972d1e76 100644
--- a/indexedDb/impl/channels/init.go
+++ b/indexedDb/impl/channels/init.go
@@ -18,7 +18,7 @@ import (
 
 	"gitlab.com/elixxir/client/v4/bindings"
 	"gitlab.com/elixxir/client/v4/channels"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 )
 
@@ -29,13 +29,13 @@ const currentVersion uint = 1
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key. Returns the
 // EventModel based on IndexedDb and the database name as reported by IndexedDb.
-func NewWASMEventModel(databaseName string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(databaseName string, encryption idbCrypto.Cipher,
 	uiCallbacks bindings.ChannelUICallbacks) (channels.EventModel, error) {
 	return newWASMModel(databaseName, encryption, uiCallbacks)
 }
 
 // newWASMModel creates the given [idb.Database] and returns a wasmModel.
-func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
+func newWASMModel(databaseName string, encryption idbCrypto.Cipher,
 	uiCallbacks bindings.ChannelUICallbacks) (*wasmModel, error) {
 	// Attempt to open database object
 	ctx, cancel := impl.NewContext()
diff --git a/indexedDb/impl/channels/model.go b/indexedDb/impl/channels/model.go
index e5d3e00aa5209985de60a77f5330a53208b1af88..702d489b04d4eb6bfac510a5b9e77f753f411cee 100644
--- a/indexedDb/impl/channels/model.go
+++ b/indexedDb/impl/channels/model.go
@@ -56,7 +56,7 @@ type Message struct {
 	Status          uint8     `json:"status"`
 	Hidden          bool      `json:"hidden"`
 	Pinned          bool      `json:"pinned"` // Index
-	Text            []byte    `json:"text"`
+	Text            string    `json:"text"`
 	Type            uint16    `json:"type"`
 	Round           uint64    `json:"round"`
 
diff --git a/indexedDb/impl/dm/callbacks.go b/indexedDb/impl/dm/callbacks.go
index 380f04d9953fdda65c4b0d80a66e7b40a7edc2b1..061c46a31b5667dd6c04303def1b3b1bab57a66a 100644
--- a/indexedDb/impl/dm/callbacks.go
+++ b/indexedDb/impl/dm/callbacks.go
@@ -17,8 +17,8 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	wDm "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/dm"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 	"gitlab.com/xx_network/crypto/csprng"
@@ -61,7 +61,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
 
 	// Create new encryption cipher
 	rng := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
-	encryption, err := cryptoChannel.NewCipherFromJSON(
+	encryption, err := idbCrypto.NewCipherFromJSON(
 		[]byte(msg.EncryptionJSON), rng.GetStream())
 	if err != nil {
 		return []byte{}, errors.Errorf("failed to JSON unmarshal channel "+
diff --git a/indexedDb/impl/dm/implementation.go b/indexedDb/impl/dm/implementation.go
index a92691878ebc4b6c21cd1d38297aca23e4109920..f40791ece46d05b397dadd6766ec455045168452 100644
--- a/indexedDb/impl/dm/implementation.go
+++ b/indexedDb/impl/dm/implementation.go
@@ -24,7 +24,7 @@ import (
 
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/wasm-utils/utils"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
@@ -36,7 +36,7 @@ import (
 // caller to ensure that its methods are called sequentially.
 type wasmModel struct {
 	db                *idb.Database
-	cipher            cryptoChannel.Cipher
+	cipher            idbCrypto.Cipher
 	receivedMessageCB MessageReceivedCallback
 }
 
@@ -81,7 +81,7 @@ func (w *wasmModel) upsertConversation(nickname string,
 // NOTE: ID is not set inside this function because we want to use the
 // autoincrement key by default. If you are trying to overwrite an existing
 // message, then you need to set it manually yourself.
-func buildMessage(messageID, parentID, text []byte, partnerKey,
+func buildMessage(messageID, parentID []byte, text string, partnerKey []byte,
 	senderKey ed25519.PublicKey, timestamp time.Time, round id.Round,
 	mType dm.MessageType, codeset uint8, status dm.Status) *Message {
 	return &Message{
@@ -276,9 +276,8 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n
 	}
 
 	// Handle encryption, if it is present
-	textBytes := []byte(data)
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt(textBytes)
+		data, err = w.cipher.Encrypt([]byte(data))
 		if err != nil {
 			return 0, err
 		}
@@ -289,7 +288,7 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n
 		parentIdBytes = parentID.Marshal()
 	}
 
-	msgToInsert := buildMessage(messageID.Bytes(), parentIdBytes, textBytes,
+	msgToInsert := buildMessage(messageID.Bytes(), parentIdBytes, data,
 		partnerKey, senderKey, timestamp, round.ID, mType, codeset, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
diff --git a/indexedDb/impl/dm/init.go b/indexedDb/impl/dm/init.go
index 8332866b95055e7d322308cdf8669d1be78a95ac..aabad5c593eaca751ada0e364893aa6382135f35 100644
--- a/indexedDb/impl/dm/init.go
+++ b/indexedDb/impl/dm/init.go
@@ -17,7 +17,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 )
 
@@ -35,13 +35,13 @@ type MessageReceivedCallback func(
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key. Returns the
 // EventModel based on IndexedDb and the database name as reported by IndexedDb.
-func NewWASMEventModel(databaseName string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(databaseName string, encryption idbCrypto.Cipher,
 	cb MessageReceivedCallback) (dm.EventModel, error) {
 	return newWASMModel(databaseName, encryption, cb)
 }
 
 // newWASMModel creates the given [idb.Database] and returns a wasmModel.
-func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
+func newWASMModel(databaseName string, encryption idbCrypto.Cipher,
 	cb MessageReceivedCallback) (*wasmModel, error) {
 	// Attempt to open database object
 	ctx, cancel := impl.NewContext()
diff --git a/indexedDb/impl/dm/model.go b/indexedDb/impl/dm/model.go
index 774d011fe4987078808febd74a1657839e06dc8a..6893fc3a6a7fffd95eb31e389e8352040e797b9f 100644
--- a/indexedDb/impl/dm/model.go
+++ b/indexedDb/impl/dm/model.go
@@ -46,7 +46,7 @@ type Message struct {
 	SenderPubKey       []byte    `json:"sender_pub_key"` // Index
 	CodesetVersion     uint8     `json:"codeset_version"`
 	Status             uint8     `json:"status"`
-	Text               []byte    `json:"text"`
+	Text               string    `json:"text"`
 	Type               uint16    `json:"type"`
 	Round              uint64    `json:"round"`
 }
diff --git a/indexedDb/impl/state/callbacks.go b/indexedDb/impl/state/callbacks.go
index fbeb674ad08e8ab9c6f0ef95fff05e96d178a7d5..73dbc2a50ec2e45237803259a50066e239acc0ef 100644
--- a/indexedDb/impl/state/callbacks.go
+++ b/indexedDb/impl/state/callbacks.go
@@ -12,8 +12,8 @@ package main
 import (
 	"encoding/json"
 	"github.com/pkg/errors"
+	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 
-	"gitlab.com/elixxir/client/v4/storage/utility"
 	stateWorker "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/state"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 )
@@ -22,7 +22,7 @@ import (
 // send information between the model and the main thread.
 type manager struct {
 	wtm   *worker.ThreadManager
-	model utility.WebState
+	model impl.WebState
 }
 
 // registerCallbacks registers all the reception callbacks to manage messages
diff --git a/indexedDb/impl/state/init.go b/indexedDb/impl/state/init.go
index e5ba9f9add4bd0f55c092a8ddd40ce97d936ebf0..14aab3a896ea8dc248f8d7f8b9f7c2bf6b42b87d 100644
--- a/indexedDb/impl/state/init.go
+++ b/indexedDb/impl/state/init.go
@@ -12,7 +12,6 @@ package main
 import (
 	"github.com/hack-pad/go-indexeddb/idb"
 	jww "github.com/spf13/jwalterweatherman"
-	"gitlab.com/elixxir/client/v4/storage/utility"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 	"syscall/js"
 )
@@ -23,7 +22,7 @@ const currentVersion uint = 1
 
 // NewState returns a [utility.WebState] backed by IndexedDb.
 // The name should be a base64 encoding of the users public key.
-func NewState(databaseName string) (utility.WebState, error) {
+func NewState(databaseName string) (impl.WebState, error) {
 	return newState(databaseName)
 }
 
diff --git a/indexedDb/impl/utils.go b/indexedDb/impl/utils.go
index 7dbf631c9deee97f3ef79b731832aab142820fbc..d9c2fa8ed1eeb32ee7a71a879e5ae2e405f01e69 100644
--- a/indexedDb/impl/utils.go
+++ b/indexedDb/impl/utils.go
@@ -32,6 +32,13 @@ const (
 	ErrDoesNotExist = "result is undefined"
 )
 
+// WebState defines an interface for setting persistent state in a KV format
+// specifically for web-based implementations.
+type WebState interface {
+	Get(key string) ([]byte, error)
+	Set(key string, value []byte) error
+}
+
 // NewContext builds a context for indexedDb operations.
 func NewContext() (context.Context, context.CancelFunc) {
 	return context.WithTimeout(context.Background(), dbTimeout)
diff --git a/indexedDb/worker/channels/init.go b/indexedDb/worker/channels/init.go
index d24120aa445d6c161bf148a242439f4a6bf05eb1..d14a80ebfc5d91dd3e100e58d7ec2cb45f7d281e 100644
--- a/indexedDb/worker/channels/init.go
+++ b/indexedDb/worker/channels/init.go
@@ -18,8 +18,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/v4/bindings"
 	"gitlab.com/elixxir/client/v4/channels"
-
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 )
@@ -36,7 +35,7 @@ type eventUpdateCallback func(eventType int64, jsonData []byte)
 // the channel manager to define the path but the callback is the same
 // across the board.
 func NewWASMEventModelBuilder(wasmJsPath string,
-	encryption cryptoChannel.Cipher,
+	encryption idbCrypto.Cipher,
 	channelCbs bindings.ChannelUICallbacks) channels.EventModelBuilder {
 	fn := func(path string) (channels.EventModel, error) {
 		return NewWASMEventModel(path, wasmJsPath, encryption,
@@ -54,7 +53,7 @@ type NewWASMEventModelMessage struct {
 
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key.
-func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(path, wasmJsPath string, encryption idbCrypto.Cipher,
 	channelCbs bindings.ChannelUICallbacks) (
 	channels.EventModel, error) {
 	databaseName := path + databaseSuffix
diff --git a/indexedDb/worker/dm/init.go b/indexedDb/worker/dm/init.go
index 3fd1cd13897bdc8684e2efc13750e1fed20ff000..b7afc2b2237c2258feb91084e644402e775c45f1 100644
--- a/indexedDb/worker/dm/init.go
+++ b/indexedDb/worker/dm/init.go
@@ -18,7 +18,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 )
@@ -42,7 +42,7 @@ type NewWASMEventModelMessage struct {
 
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key.
-func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(path, wasmJsPath string, encryption idbCrypto.Cipher,
 	cb MessageReceivedCallback) (dm.EventModel, error) {
 	databaseName := path + databaseSuffix
 
diff --git a/indexedDb/worker/state/init.go b/indexedDb/worker/state/init.go
index b4842ae5ca954e1bfead70a483690b88fbf1974d..3ed7ac513204b26912f17a298d320b089d1fbf4e 100644
--- a/indexedDb/worker/state/init.go
+++ b/indexedDb/worker/state/init.go
@@ -11,11 +11,11 @@ package dm
 
 import (
 	"encoding/json"
+	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 	"time"
 
 	"github.com/pkg/errors"
 
-	"gitlab.com/elixxir/client/v4/storage/utility"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 )
@@ -29,9 +29,16 @@ type NewStateMessage struct {
 	DatabaseName string `json:"databaseName"`
 }
 
+// WebState defines an interface for setting persistent state in a KV format
+// specifically for web-based implementations.
+type WebState interface {
+	Get(key string) ([]byte, error)
+	Set(key string, value []byte) error
+}
+
 // NewState returns a [utility.WebState] backed by indexeddb.
 // The name should be a base64 encoding of the users public key.
-func NewState(path, wasmJsPath string) (utility.WebState, error) {
+func NewState(path, wasmJsPath string) (impl.WebState, error) {
 	databaseName := path + databaseSuffix
 
 	wh, err := worker.NewManager(wasmJsPath, "stateIndexedDb", true)
diff --git a/main.go b/main.go
index 5315e3738a2c8825791ac725026aa5c828a5ea44..b2a964719718eb79e9a006ec3e8b225c0e075b17 100644
--- a/main.go
+++ b/main.go
@@ -136,8 +136,12 @@ func setGlobals() {
 		js.FuncOf(wasm.GetChannelNotificationReportsForMe))
 	js.Global().Set("GetNoMessageErr", js.FuncOf(wasm.GetNoMessageErr))
 	js.Global().Set("CheckNoMessageErr", js.FuncOf(wasm.CheckNoMessageErr))
-	js.Global().Set("NewChannelsDatabaseCipher",
-		js.FuncOf(wasm.NewChannelsDatabaseCipher))
+	js.Global().Set("GetNotificationReportsForMe",
+		js.FuncOf(wasm.GetChannelNotificationReportsForMe))
+
+	// wasm/cipher.go
+	js.Global().Set("NewDatabaseCipher",
+		js.FuncOf(wasm.NewDatabaseCipher))
 
 	// wasm/dm.go
 	js.Global().Set("InitChannelsFileTransfer",
@@ -150,7 +154,7 @@ func setGlobals() {
 	js.Global().Set("NewDMClientWithIndexedDbUnsafe",
 		js.FuncOf(wasm.NewDMClientWithIndexedDbUnsafe))
 	js.Global().Set("NewDMsDatabaseCipher",
-		js.FuncOf(wasm.NewDMsDatabaseCipher))
+		js.FuncOf(wasm.NewDatabaseCipher))
 
 	// wasm/cmix.go
 	js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix))
diff --git a/storage/password.go b/storage/password.go
index 5dec9055907b819c3290be05cd07454ddd312036..95abcb4fc940890f9682ec9413bfc9eddb63295b 100644
--- a/storage/password.go
+++ b/storage/password.go
@@ -195,7 +195,7 @@ func verifyPassword(externalPassword string) bool {
 // initInternalPassword generates a new internal password, stores an encrypted
 // version in local storage, and returns it.
 func initInternalPassword(externalPassword string,
-	localStorage *storage.LocalStorage, csprng io.Reader,
+	localStorage storage.LocalStorage, csprng io.Reader,
 	params argonParams) ([]byte, error) {
 	internalPassword := make([]byte, internalPasswordLen)
 
@@ -250,7 +250,7 @@ func initInternalPassword(externalPassword string,
 // getInternalPassword retrieves the internal password from local storage,
 // decrypts it, and returns it.
 func getInternalPassword(
-	externalPassword string, localStorage *storage.LocalStorage) ([]byte, error) {
+	externalPassword string, localStorage storage.LocalStorage) ([]byte, error) {
 	encryptedInternalPassword, err := localStorage.Get(passwordKey)
 	if err != nil {
 		return nil, errors.WithMessage(err, getPasswordStorageErr)
diff --git a/storage/purge.go b/storage/purge.go
index df75cebf9175251d53c722565e7c1111d6d9262f..a7b7f64f6dca64d8ef69b2b84cbf976db1569675 100644
--- a/storage/purge.go
+++ b/storage/purge.go
@@ -16,7 +16,6 @@ import (
 	"github.com/hack-pad/go-indexeddb/idb"
 	jww "github.com/spf13/jwalterweatherman"
 
-	"gitlab.com/elixxir/client/v4/storage/utility"
 	"gitlab.com/elixxir/wasm-utils/exception"
 	"gitlab.com/elixxir/wasm-utils/storage"
 )
@@ -45,17 +44,14 @@ func DecrementNumClientsRunning() {
 // password is required.
 //
 // Parameters:
-//   - args[0] - Storage directory path (string). This is the same directory
-//     path passed into [wasm.NewCmix].
-//   - args[1] - The user-supplied password (string). This is the same password
+//   - args[0] - The user-supplied password (string). This is the same password
 //     passed into [wasm.NewCmix].
 //
 // Returns:
-//   - Throws an error if the password is incorrect or if not all cMix
-//     followers have been stopped.
+//   - Throws an error if the password is incorrect or if not all cMix followers
+//     have been stopped.
 func Purge(_ js.Value, args []js.Value) any {
-	storageDirectory := args[0].String()
-	userPassword := args[1].String()
+	userPassword := args[0].String()
 
 	// Check the password
 	if !verifyPassword(userPassword) {
@@ -96,23 +92,5 @@ func Purge(_ js.Value, args []js.Value) any {
 	n := ls.Clear()
 	jww.DEBUG.Printf("[PURGE] Cleared %d WASM keys in local storage", n)
 
-	// Clear all EKV from local storage
-	keys := ls.LocalStorageUNSAFE().KeysPrefix(storageDirectory)
-	n = len(keys)
-	for _, keyName := range keys {
-		ls.LocalStorageUNSAFE().RemoveItem(keyName)
-	}
-	jww.DEBUG.Printf("[PURGE] Cleared %d keys with the prefix %q (for EKV)",
-		n, storageDirectory)
-
-	// Clear all NDFs saved to local storage
-	keys = ls.LocalStorageUNSAFE().KeysPrefix(utility.NdfStorageKeyNamePrefix)
-	n = len(keys)
-	for _, keyName := range keys {
-		ls.LocalStorageUNSAFE().RemoveItem(keyName)
-	}
-	jww.DEBUG.Printf("[PURGE] Cleared %d keys with the prefix %q (for NDF)",
-		n, utility.NdfStorageKeyNamePrefix)
-
 	return nil
 }
diff --git a/storage/version.go b/storage/version.go
index 5fa7e439af29a96c66b22c92b86db42c384c121f..3a35edfd31eda105afa5e1583dcf891b511b62f8 100644
--- a/storage/version.go
+++ b/storage/version.go
@@ -40,7 +40,7 @@ func CheckAndStoreVersions() error {
 }
 
 func checkAndStoreVersions(
-	currentWasmVer, currentClientVer string, ls *storage.LocalStorage) error {
+	currentWasmVer, currentClientVer string, ls storage.LocalStorage) error {
 	// Get the stored client version, if it exists
 	storedClientVer, err :=
 		initOrLoadStoredSemver(clientVerKey, currentClientVer, ls)
@@ -91,7 +91,7 @@ func checkAndStoreVersions(
 // local storage. If no version is stored, then the current version is stored
 // and returned.
 func initOrLoadStoredSemver(
-	key, currentVersion string, ls *storage.LocalStorage) (string, error) {
+	key, currentVersion string, ls storage.LocalStorage) (string, error) {
 	storedVersion, err := ls.Get(key)
 	if err != nil {
 		if errors.Is(err, os.ErrNotExist) {
diff --git a/wasm/channels.go b/wasm/channels.go
index 13562a200cef571cad1f3d881ee172e6c5f6d399..b3535c9c8be609ab9cf992c8de440797d3ee4723 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -369,9 +369,9 @@ func LoadChannelsManager(_ js.Value, args []js.Value) any {
 //     [bindings.ChannelUICallbacks]. It is a callback that informs the UI about
 //     various events. The entire interface can be nil, but if defined, each
 //     method must be implemented.
-//   - args[6] - ID of [ChannelDbCipher] object in tracker (int). Create this
-//     object with [NewChannelsDatabaseCipher] and get its id with
-//     [ChannelDbCipher.GetID].
+//   - args[6] - ID of [DbCipher] object in tracker (int). Create this
+//     object with [NewDatabaseCipher] and get its id with
+//     [DbCipher.GetID].
 //
 // Returns a promise:
 //   - Resolves to a Javascript representation of the [ChannelsManager] object.
@@ -386,7 +386,7 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any {
 	cUI := newChannelUI(args[5])
 	cipherID := args[6].Int()
 
-	cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
+	cipher, err := dbCipherTrackerSingleton.get(cipherID)
 	if err != nil {
 		exception.ThrowTrace(err)
 	}
@@ -444,10 +444,10 @@ func NewChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
 func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string,
 	privateIdentity, extensionBuilderIDsJSON []byte, notificationsID int,
 	channelsCbs bindings.ChannelUICallbacks,
-	cipher *bindings.ChannelDbCipher) any {
+	cipher *DbCipher) any {
 
 	model := channelsDb.NewWASMEventModelBuilder(
-		wasmJsPath, cipher, channelsCbs)
+		wasmJsPath, cipher.api, channelsCbs)
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
 		cm, err := bindings.NewChannelsManagerGoEventModel(cmixID,
@@ -487,9 +487,9 @@ func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string,
 //     [bindings.ChannelUICallbacks]. It is a callback that informs the UI about
 //     various events. The entire interface can be nil, but if defined, each
 //     method must be implemented.
-//   - args[6] - ID of [ChannelDbCipher] object in tracker (int). Create this
-//     object with [NewChannelsDatabaseCipher] and get its id with
-//     [ChannelDbCipher.GetID].
+//   - args[6] - ID of [DbCipher] object in tracker (int). Create this
+//     object with [NewDatabaseCipher] and get its id with
+//     [DbCipher.GetID].
 //
 // Returns a promise:
 //   - Resolves to a Javascript representation of the [ChannelsManager] object.
@@ -504,7 +504,7 @@ func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any {
 	channelsCbs := newChannelUI(args[5])
 	cipherID := args[6].Int()
 
-	cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
+	cipher, err := dbCipherTrackerSingleton.get(cipherID)
 	if err != nil {
 		exception.ThrowTrace(err)
 	}
@@ -557,10 +557,10 @@ func LoadChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
 
 func loadChannelsManagerWithIndexedDb(cmixID int, wasmJsPath, storageTag string,
 	extensionBuilderIDsJSON []byte, notificationsID int, channelsCbs bindings.ChannelUICallbacks,
-	cipher *bindings.ChannelDbCipher) any {
+	cipher *DbCipher) any {
 
 	model := channelsDb.NewWASMEventModelBuilder(
-		wasmJsPath, cipher, channelsCbs)
+		wasmJsPath, cipher.api, channelsCbs)
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
 		cm, err := bindings.LoadChannelsManagerGoEventModel(
@@ -2315,145 +2315,6 @@ type MessageAndError struct {
 	Error string
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Channel Cipher                                                             //
-////////////////////////////////////////////////////////////////////////////////
-
-// ChannelDbCipher wraps the [bindings.ChannelDbCipher] object so its methods
-// can be wrapped to be Javascript compatible.
-type ChannelDbCipher struct {
-	api *bindings.ChannelDbCipher
-}
-
-// newChannelDbCipherJS creates a new Javascript compatible object
-// (map[string]any) that matches the [ChannelDbCipher] structure.
-func newChannelDbCipherJS(api *bindings.ChannelDbCipher) map[string]any {
-	c := ChannelDbCipher{api}
-	channelDbCipherMap := map[string]any{
-		"GetID":         js.FuncOf(c.GetID),
-		"Encrypt":       js.FuncOf(c.Encrypt),
-		"Decrypt":       js.FuncOf(c.Decrypt),
-		"MarshalJSON":   js.FuncOf(c.MarshalJSON),
-		"UnmarshalJSON": js.FuncOf(c.UnmarshalJSON),
-	}
-
-	return channelDbCipherMap
-}
-
-// NewChannelsDatabaseCipher constructs a [ChannelDbCipher] object.
-//
-// Parameters:
-//   - args[0] - The tracked [Cmix] object ID (int).
-//   - args[1] - The password for storage. This should be the same password
-//     passed into [NewCmix] (Uint8Array).
-//   - args[2] - The maximum size of a payload to be encrypted. A payload passed
-//     into [ChannelDbCipher.Encrypt] that is larger than this value will result
-//     in an error (int).
-//
-// Returns:
-//   - JavaScript representation of the [ChannelDbCipher] object.
-//   - Throws an error if creating the cipher fails.
-func NewChannelsDatabaseCipher(_ js.Value, args []js.Value) any {
-	cmixId := args[0].Int()
-	password := utils.CopyBytesToGo(args[1])
-	plaintTextBlockSize := args[2].Int()
-
-	cipher, err := bindings.NewChannelsDatabaseCipher(
-		cmixId, password, plaintTextBlockSize)
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return newChannelDbCipherJS(cipher)
-}
-
-// GetID returns the ID for this [bindings.ChannelDbCipher] in the
-// channelDbCipherTracker.
-//
-// Returns:
-//   - Tracker ID (int).
-func (c *ChannelDbCipher) GetID(js.Value, []js.Value) any {
-	return c.api.GetID()
-}
-
-// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
-// done on the plaintext so all encrypted data looks uniform at rest.
-//
-// Parameters:
-//   - args[0] - The data to be encrypted (Uint8Array). This must be smaller
-//     than the block size passed into [NewChannelsDatabaseCipher]. If it is
-//     larger, this will return an error.
-//
-// Returns:
-//   - The ciphertext of the plaintext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *ChannelDbCipher) Encrypt(_ js.Value, args []js.Value) any {
-	ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(ciphertext)
-}
-
-// Decrypt will decrypt the passed in encrypted value. The plaintext will be
-// returned by this function. Any padding will be discarded within this
-// function.
-//
-// Parameters:
-//   - args[0] - the encrypted data returned by [ChannelDbCipher.Encrypt]
-//     (Uint8Array).
-//
-// Returns:
-//   - The plaintext of the ciphertext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *ChannelDbCipher) Decrypt(_ js.Value, args []js.Value) any {
-	plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(plaintext)
-}
-
-// MarshalJSON marshals the cipher into valid JSON.
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *ChannelDbCipher) MarshalJSON(js.Value, []js.Value) any {
-	data, err := c.api.MarshalJSON()
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(data)
-}
-
-// UnmarshalJSON unmarshalls JSON into the cipher.
-//
-// Note that this function does not transfer the internal RNG. Use
-// [channel.NewCipherFromJSON] to properly reconstruct a cipher from JSON.
-//
-// Parameters:
-//   - args[0] - JSON data to unmarshal (Uint8Array).
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *ChannelDbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
-	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-	return nil
-}
-
 // newChannelUI maps the methods on the Javascript object to the
 // channelUI callbacks implementation struct.
 func newChannelUI(cbImpl js.Value) *channelUI {
diff --git a/wasm/channels_test.go b/wasm/channels_test.go
index 1b4d9eb555177951ea66e9fb81ed7420418f2b22..9ebbc691debd6d330a9cadc3a4aa4c9c62d342ad 100644
--- a/wasm/channels_test.go
+++ b/wasm/channels_test.go
@@ -60,47 +60,6 @@ func Test_ChannelsManagerMethods(t *testing.T) {
 	}
 }
 
-// Tests that the map representing ChannelDbCipher returned by
-// newChannelDbCipherJS contains all of the methods on ChannelDbCipher.
-func Test_newChannelDbCipherJS(t *testing.T) {
-	cipherType := reflect.TypeOf(&ChannelDbCipher{})
-
-	cipher := newChannelDbCipherJS(&bindings.ChannelDbCipher{})
-	if len(cipher) != cipherType.NumMethod() {
-		t.Errorf("ChannelDbCipher JS object does not have all methods."+
-			"\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher))
-	}
-
-	for i := 0; i < cipherType.NumMethod(); i++ {
-		method := cipherType.Method(i)
-
-		if _, exists := cipher[method.Name]; !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
-// Tests that ChannelDbCipher has all the methods that
-// [bindings.ChannelDbCipher] has.
-func Test_ChannelDbCipherMethods(t *testing.T) {
-	cipherType := reflect.TypeOf(&ChannelDbCipher{})
-	binCipherType := reflect.TypeOf(&bindings.ChannelDbCipher{})
-
-	if binCipherType.NumMethod() != cipherType.NumMethod() {
-		t.Errorf("WASM ChannelDbCipher object does not have all methods from "+
-			"bindings.\nexpected: %d\nreceived: %d",
-			binCipherType.NumMethod(), cipherType.NumMethod())
-	}
-
-	for i := 0; i < binCipherType.NumMethod(); i++ {
-		method := binCipherType.Method(i)
-
-		if _, exists := cipherType.MethodByName(method.Name); !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
 type jsIdentity struct {
 	pubKey  js.Value
 	codeset js.Value
diff --git a/wasm/cipher.go b/wasm/cipher.go
new file mode 100644
index 0000000000000000000000000000000000000000..5504d2f3a426c78640ff1611e025103e91fb876e
--- /dev/null
+++ b/wasm/cipher.go
@@ -0,0 +1,232 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package wasm
+
+import (
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/v4/bindings"
+	"gitlab.com/elixxir/client/v4/storage/utility"
+	"gitlab.com/elixxir/crypto/indexedDb"
+	"gitlab.com/elixxir/wasm-utils/exception"
+	"gitlab.com/elixxir/wasm-utils/utils"
+	"sync"
+	"syscall/js"
+)
+
+// dbCipherTrackerSingleton is used to track DbCipher objects
+// so that they can be referenced by ID back over the bindings.
+var dbCipherTrackerSingleton = &DbCipherTracker{
+	tracked: make(map[int]*DbCipher),
+	count:   0,
+}
+
+// DbCipherTracker is a singleton used to keep track of extant
+// DbCipher objects, preventing race conditions created by passing it
+// over the bindings.
+type DbCipherTracker struct {
+	tracked map[int]*DbCipher
+	count   int
+	mux     sync.RWMutex
+}
+
+// create creates a DbCipher from a [indexedDb.Cipher], assigns it a unique
+// ID, and adds it to the DbCipherTracker.
+func (ct *DbCipherTracker) create(c indexedDb.Cipher) *DbCipher {
+	ct.mux.Lock()
+	defer ct.mux.Unlock()
+
+	chID := ct.count
+	ct.count++
+
+	ct.tracked[chID] = &DbCipher{
+		api: c,
+		id:  chID,
+	}
+
+	return ct.tracked[chID]
+}
+
+// get an DbCipher from the DbCipherTracker given its ID.
+func (ct *DbCipherTracker) get(id int) (*DbCipher, error) {
+	ct.mux.RLock()
+	defer ct.mux.RUnlock()
+
+	c, exist := ct.tracked[id]
+	if !exist {
+		return nil, errors.Errorf(
+			"Cannot get DbCipher for ID %d, does not exist", id)
+	}
+
+	return c, nil
+}
+
+// delete removes a DbCipherTracker from the DbCipherTracker.
+func (ct *DbCipherTracker) delete(id int) {
+	ct.mux.Lock()
+	defer ct.mux.Unlock()
+
+	delete(ct.tracked, id)
+}
+
+// DbCipher wraps the [indexedDb.Cipher] object so its methods
+// can be wrapped to be Javascript compatible.
+type DbCipher struct {
+	api  indexedDb.Cipher
+	salt []byte
+	id   int
+}
+
+// newDbCipherJS creates a new Javascript compatible object
+// (map[string]any) that matches the [DbCipher] structure.
+func newDbCipherJS(c *DbCipher) map[string]any {
+	DbCipherMap := map[string]any{
+		"GetID":         js.FuncOf(c.GetID),
+		"Encrypt":       js.FuncOf(c.Encrypt),
+		"Decrypt":       js.FuncOf(c.Decrypt),
+		"MarshalJSON":   js.FuncOf(c.MarshalJSON),
+		"UnmarshalJSON": js.FuncOf(c.UnmarshalJSON),
+	}
+
+	return DbCipherMap
+}
+
+// NewDatabaseCipher constructs a [DbCipher] object.
+//
+// Parameters:
+//   - args[0] - The tracked [Cmix] object ID (int).
+//   - args[1] - The password for storage. This should be the same password
+//     passed into [NewCmix] (Uint8Array).
+//   - args[2] - The maximum size of a payload to be encrypted. A payload passed
+//     into [DbCipher.Encrypt] that is larger than this value will result
+//     in an error (int).
+//
+// Returns:
+//   - JavaScript representation of the [DbCipher] object.
+//   - Throws an error if creating the cipher fails.
+func NewDatabaseCipher(_ js.Value, args []js.Value) any {
+	cmixId := args[0].Int()
+	password := utils.CopyBytesToGo(args[1])
+	plaintTextBlockSize := args[2].Int()
+
+	// Get user from singleton
+	user, err := bindings.GetCMixInstance(cmixId)
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	// Generate RNG
+	stream := user.GetRng().GetStream()
+
+	// Load or generate a salt
+	salt, err := utility.NewOrLoadSalt(
+		user.GetStorage().GetKV(), stream)
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	// Construct a cipher
+	c, err := indexedDb.NewCipher(
+		password, salt, plaintTextBlockSize, stream)
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	// Add to singleton and return
+	return newDbCipherJS(dbCipherTrackerSingleton.create(c))
+}
+
+// GetID returns the ID for this [DbCipher] in the
+// DbCipherTracker.
+//
+// Returns:
+//   - Tracker ID (int).
+func (c *DbCipher) GetID(js.Value, []js.Value) any {
+	return c.id
+}
+
+// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
+// done on the plaintext so all encrypted data looks uniform at rest.
+//
+// Parameters:
+//   - args[0] - The data to be encrypted (Uint8Array). This must be smaller
+//     than the block size passed into [NewDatabaseCipher]. If it is
+//     larger, this will return an error.
+//
+// Returns:
+//   - The ciphertext of the plaintext passed in (String).
+//   - Throws an error if it fails to encrypt the plaintext.
+func (c *DbCipher) Encrypt(_ js.Value, args []js.Value) any {
+	ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	return ciphertext
+}
+
+// Decrypt will decrypt the passed in encrypted value. The plaintext will be
+// returned by this function. Any padding will be discarded within this
+// function.
+//
+// Parameters:
+//   - args[0] - the encrypted data returned by [DbCipher.Encrypt]
+//     (String).
+//
+// Returns:
+//   - The plaintext of the ciphertext passed in (Uint8Array).
+//   - Throws an error if it fails to encrypt the plaintext.
+func (c *DbCipher) Decrypt(_ js.Value, args []js.Value) any {
+	plaintext, err := c.api.Decrypt(args[0].String())
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(plaintext)
+}
+
+// MarshalJSON marshals the cipher into valid JSON.
+//
+// Returns:
+//   - JSON of the cipher (Uint8Array).
+//   - Throws an error if marshalling fails.
+func (c *DbCipher) MarshalJSON(js.Value, []js.Value) any {
+	data, err := c.api.MarshalJSON()
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(data)
+}
+
+// UnmarshalJSON unmarshalls JSON into the cipher.
+//
+// Note that this function does not transfer the internal RNG. Use
+// [indexedDb.NewCipherFromJSON] to properly reconstruct a cipher from JSON.
+//
+// Parameters:
+//   - args[0] - JSON data to unmarshal (Uint8Array).
+//
+// Returns:
+//   - JSON of the cipher (Uint8Array).
+//   - Throws an error if marshalling fails.
+func (c *DbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
+	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+	return nil
+}
diff --git a/wasm/cipher_test.go b/wasm/cipher_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f83461509bc0f70ff5afd7104de9ab5fb3e6e11
--- /dev/null
+++ b/wasm/cipher_test.go
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package wasm
+
+import (
+	"reflect"
+	"testing"
+)
+
+// Tests that the map representing DbCipher returned by
+// newDbCipherJS contains all the methods on DbCipher.
+func Test_newChannelDbCipherJS(t *testing.T) {
+	cipherType := reflect.TypeOf(&DbCipher{})
+
+	cipher := newDbCipherJS(&DbCipher{})
+	if len(cipher) != cipherType.NumMethod() {
+		t.Errorf("DbCipher JS object does not have all methods."+
+			"\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher))
+	}
+
+	for i := 0; i < cipherType.NumMethod(); i++ {
+		method := cipherType.Method(i)
+
+		if _, exists := cipher[method.Name]; !exists {
+			t.Errorf("Method %s does not exist.", method.Name)
+		}
+	}
+}
diff --git a/wasm/dm.go b/wasm/dm.go
index 53d123a26c3f92743f9868e03017e7c1858ca9bb..3786b0df32a68b9b0b70c09a9d5664263df3792f 100644
--- a/wasm/dm.go
+++ b/wasm/dm.go
@@ -128,8 +128,8 @@ func NewDMClient(_ js.Value, args []js.Value) any {
 //     The row in the database that was updated can be found using the UUID.
 //     messageUpdate is true if the message already exists and was edited.
 //     conversationUpdate is true if the Conversation was created or modified.
-//   - args[4] - ID of [DMDbCipher] object in tracker (int). Create this object
-//     with [NewDMsDatabaseCipher] and get its id with [DMDbCipher.GetID].
+//   - args[4] - ID of [DbCipher] object in tracker (int). Create this object
+//     with [NewDatabaseCipher] and get its id with [DbCipher.GetID].
 //
 // Returns:
 //   - Resolves to a Javascript representation of the [DMClient] object.
@@ -142,7 +142,7 @@ func NewDMClientWithIndexedDb(_ js.Value, args []js.Value) any {
 	messageReceivedCB := args[3]
 	cipherID := args[4].Int()
 
-	cipher, err := bindings.GetDMDbCipherTrackerFromID(cipherID)
+	cipher, err := dbCipherTrackerSingleton.get(cipherID)
 	if err != nil {
 		exception.ThrowTrace(err)
 	}
@@ -190,7 +190,7 @@ func NewDMClientWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
 }
 
 func newDMClientWithIndexedDb(cmixID int, wasmJsPath string,
-	privateIdentity []byte, cb js.Value, cipher *bindings.DMDbCipher) any {
+	privateIdentity []byte, cb js.Value, cipher *DbCipher) any {
 
 	messageReceivedCB := func(uuid uint64, pubKey ed25519.PublicKey,
 		messageUpdate, conversationUpdate bool) {
@@ -206,7 +206,7 @@ func newDMClientWithIndexedDb(cmixID int, wasmJsPath string,
 		}
 		dmPath := base64.RawStdEncoding.EncodeToString(pi.PubKey[:])
 		model, err := indexDB.NewWASMEventModel(
-			dmPath, wasmJsPath, cipher, messageReceivedCB)
+			dmPath, wasmJsPath, cipher.api, messageReceivedCB)
 		if err != nil {
 			reject(exception.NewTrace(err))
 		}
@@ -671,35 +671,6 @@ func DecodeDMShareURL(_ js.Value, args []js.Value) any {
 	return utils.CopyBytesToJS(report)
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Channel Receiving Logic and Callback Registration                          //
-////////////////////////////////////////////////////////////////////////////////
-
-// channelMessageReceptionCallback wraps Javascript callbacks to adhere to the
-// [bindings.ChannelMessageReceptionCallback] interface.
-type dmReceptionCallback struct {
-	callback func(args ...any) js.Value
-}
-
-// Callback returns the context for a channel message.
-//
-// Parameters:
-//   - receivedChannelMessageReport - Returns the JSON of
-//     [bindings.ReceivedChannelMessageReport] (Uint8Array).
-//   - err - Returns an error on failure (Error).
-//
-// Returns:
-//   - It must return a unique UUID for the message that it can be referenced by
-//     later (int).
-func (cmrCB *dmReceptionCallback) Callback(
-	receivedChannelMessageReport []byte, err error) int {
-	uuid := cmrCB.callback(
-		utils.CopyBytesToJS(receivedChannelMessageReport),
-		exception.NewTrace(err))
-
-	return uuid.Int()
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Event Model Logic                                                          //
 ////////////////////////////////////////////////////////////////////////////////
@@ -990,146 +961,6 @@ func (em *dmReceiver) GetConversations() []byte {
 	return conversationsBytes
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// DM DB Cipher                                                               //
-////////////////////////////////////////////////////////////////////////////////
-
-// DMDbCipher wraps the [bindings.DMDbCipher] object so its methods
-// can be wrapped to be Javascript compatible.
-type DMDbCipher struct {
-	api *bindings.DMDbCipher
-}
-
-// newDMDbCipherJS creates a new Javascript compatible object
-// (map[string]any) that matches the [DMDbCipher] structure.
-func newDMDbCipherJS(api *bindings.DMDbCipher) map[string]any {
-	c := DMDbCipher{api}
-	channelDbCipherMap := map[string]any{
-		"GetID":         js.FuncOf(c.GetID),
-		"Encrypt":       js.FuncOf(c.Encrypt),
-		"Decrypt":       js.FuncOf(c.Decrypt),
-		"MarshalJSON":   js.FuncOf(c.MarshalJSON),
-		"UnmarshalJSON": js.FuncOf(c.UnmarshalJSON),
-	}
-
-	return channelDbCipherMap
-}
-
-// NewDMsDatabaseCipher constructs a [DMDbCipher] object.
-//
-// Parameters:
-//   - args[0] - The tracked [Cmix] object ID (int).
-//   - args[1] - The password for storage. This should be the same password
-//     passed into [NewCmix] (Uint8Array).
-//   - args[2] - The maximum size of a payload to be encrypted. A payload passed
-//     into [DMDbCipher.Encrypt] that is larger than this value will result
-//     in an error (int).
-//
-// Returns:
-//   - JavaScript representation of the [DMDbCipher] object.
-//   - Throws an error if creating the cipher fails.
-func NewDMsDatabaseCipher(_ js.Value, args []js.Value) any {
-	cmixId := args[0].Int()
-	password := utils.CopyBytesToGo(args[1])
-	plaintTextBlockSize := args[2].Int()
-
-	cipher, err := bindings.NewDMsDatabaseCipher(
-		cmixId, password, plaintTextBlockSize)
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return newDMDbCipherJS(cipher)
-}
-
-// GetID returns the ID for this [bindings.DMDbCipher] in the
-// channelDbCipherTracker.
-//
-// Returns:
-//   - Tracker ID (int).
-func (c *DMDbCipher) GetID(js.Value, []js.Value) any {
-	return c.api.GetID()
-}
-
-// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
-// done on the plaintext so all encrypted data looks uniform at rest.
-//
-// Parameters:
-//   - args[0] - The data to be encrypted (Uint8Array). This must be smaller
-//     than the block size passed into [NewDMsDatabaseCipher]. If it is
-//     larger, this will return an error.
-//
-// Returns:
-//   - The ciphertext of the plaintext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *DMDbCipher) Encrypt(_ js.Value, args []js.Value) any {
-	ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(ciphertext)
-}
-
-// Decrypt will decrypt the passed in encrypted value. The plaintext will be
-// returned by this function. Any padding will be discarded within this
-// function.
-//
-// Parameters:
-//   - args[0] - the encrypted data returned by [DMDbCipher.Encrypt]
-//     (Uint8Array).
-//
-// Returns:
-//   - The plaintext of the ciphertext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *DMDbCipher) Decrypt(_ js.Value, args []js.Value) any {
-	plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(plaintext)
-}
-
-// MarshalJSON marshals the cipher into valid JSON.
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *DMDbCipher) MarshalJSON(js.Value, []js.Value) any {
-	data, err := c.api.MarshalJSON()
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(data)
-}
-
-// UnmarshalJSON unmarshalls JSON into the cipher. This function adheres to the
-// json.Unmarshaler interface.
-//
-// Note that this function does not transfer the internal RNG. Use
-// [channel.NewCipherFromJSON] to properly reconstruct a cipher from JSON.
-//
-// Parameters:
-//   - args[0] - JSON data to unmarshal (Uint8Array).
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *DMDbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
-	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-	return nil
-}
-
 // truncate truncates the string to length n. If the string is trimmed, then
 // ellipses (...) are appended.
 func truncate(s string, n int) string {
diff --git a/wasm/dm_test.go b/wasm/dm_test.go
index 58f837ea0bb857deaede29fd9f5c37f4cd936e34..f959c9463fe793d7cac78927dcdc8eba5980d5f7 100644
--- a/wasm/dm_test.go
+++ b/wasm/dm_test.go
@@ -62,43 +62,3 @@ func Test_DMClientMethods(t *testing.T) {
 		}
 	}
 }
-
-// Tests that the map representing DMDbCipher returned by newDMDbCipherJS
-// contains all of the methods on DMDbCipher.
-func Test_newDMDbCipherJS(t *testing.T) {
-	cipherType := reflect.TypeOf(&DMDbCipher{})
-
-	cipher := newDMDbCipherJS(&bindings.DMDbCipher{})
-	if len(cipher) != cipherType.NumMethod() {
-		t.Errorf("DMDbCipher JS object does not have all methods."+
-			"\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher))
-	}
-
-	for i := 0; i < cipherType.NumMethod(); i++ {
-		method := cipherType.Method(i)
-
-		if _, exists := cipher[method.Name]; !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
-// Tests that DMDbCipher has all the methods that [bindings.DMDbCipher] has.
-func Test_DMDbCipherMethods(t *testing.T) {
-	cipherType := reflect.TypeOf(&DMDbCipher{})
-	binCipherType := reflect.TypeOf(&bindings.DMDbCipher{})
-
-	if binCipherType.NumMethod() != cipherType.NumMethod() {
-		t.Errorf("WASM DMDbCipher object does not have all methods from "+
-			"bindings.\nexpected: %d\nreceived: %d",
-			binCipherType.NumMethod(), cipherType.NumMethod())
-	}
-
-	for i := 0; i < binCipherType.NumMethod(); i++ {
-		method := binCipherType.Method(i)
-
-		if _, exists := cipherType.MethodByName(method.Name); !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
diff --git a/wasm_test.go b/wasm_test.go
index b415ece5623aa4e235511fb1f9cb24058e02e384..1e0494dbe4f6e08ec9276aa3b984b5c245cfa7e8 100644
--- a/wasm_test.go
+++ b/wasm_test.go
@@ -42,7 +42,7 @@ func TestPublicFunctions(t *testing.T) {
 		"NewEventModel":                   {},
 		"NewChannelsManagerGoEventModel":  {},
 		"LoadChannelsManagerGoEventModel": {},
-		"GetChannelDbCipherTrackerFromID": {},
+		"GetDbCipherTrackerFromID":        {},
 
 		// Version functions were renamed to differentiate between WASM and
 		// client versions