diff --git a/go.mod b/go.mod
index 43305904a3acf6edaa1be95ae7cb30ca9e523316..680aeae38ca4edd1294355cfe02f54d947f3a74f 100644
--- a/go.mod
+++ b/go.mod
@@ -7,9 +7,9 @@ require (
 	github.com/hack-pad/go-indexeddb v0.2.0
 	github.com/pkg/errors v0.9.1
 	github.com/spf13/jwalterweatherman v1.1.0
-	gitlab.com/elixxir/client/v4 v4.3.9-0.20221210003613-b73478d56e0d
-	gitlab.com/elixxir/crypto v0.0.7-0.20221209195912-492ced8f7c92
-	gitlab.com/elixxir/primitives v0.0.3-0.20221114231218-cc461261a6af
+	gitlab.com/elixxir/client/v4 v4.3.12-0.20221219215743-62264017437e
+	gitlab.com/elixxir/crypto v0.0.7-0.20221219161351-7c3751afd8f2
+	gitlab.com/elixxir/primitives v0.0.3-0.20221214192222-988b44a6958a
 	gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46
 	gitlab.com/xx_network/primitives v0.0.4-0.20221209210320-376735467d58
 	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
@@ -17,7 +17,7 @@ require (
 
 require (
 	filippo.io/edwards25519 v1.0.0 // indirect
-	git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221102223039-dc1f37d94e70 // indirect
+	git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221215201903-f66fe2cea35f // 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
@@ -59,9 +59,9 @@ require (
 	github.com/tyler-smith/go-bip39 v1.1.0 // indirect
 	github.com/zeebo/blake3 v0.2.3 // indirect
 	gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f // indirect
-	gitlab.com/elixxir/comms v0.0.4-0.20221110181420-84bca6216fe4 // indirect
+	gitlab.com/elixxir/comms v0.0.4-0.20221215214627-7807bfdde33a // indirect
 	gitlab.com/elixxir/ekv v0.2.1 // indirect
-	gitlab.com/xx_network/comms v0.0.4-0.20221207203143-462f82d6ec01 // indirect
+	gitlab.com/xx_network/comms v0.0.4-0.20221215214252-1275cef8760e // indirect
 	gitlab.com/xx_network/ring v0.0.3-0.20220902183151-a7d3b15bc981 // indirect
 	gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
 	gitlab.com/yawning/nyquist.git v0.0.0-20221003103146-de5645224a22 // indirect
@@ -78,4 +78,5 @@ require (
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	nhooyr.io/websocket v1.8.7 // indirect
+	src.agwa.name/tlshacks v0.0.0-20220518131152-d2c6f4e2b780 // indirect
 )
diff --git a/go.sum b/go.sum
index f4fa8682d9207c9ee0037e02dacf10c361217d50..003ae101f705ae6473a5c2d1aa11d4987a3d3da2 100644
--- a/go.sum
+++ b/go.sum
@@ -40,6 +40,10 @@ filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221102223039-dc1f37d94e70 h1:p24wUpzdil0wgyFerGJM69fD5Xz9hsBDBK8f9m01pq8=
 git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221102223039-dc1f37d94e70/go.mod h1:uFKw2wmgtlYMdiIm08dM0Vj4XvX9ZKVCj71c8O7SAPo=
+git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221215181401-0b8a26d47532 h1:EH4TFLgXGgofV2MsUOgNDmn3X+qfhbQ2RV6zOYRaSdU=
+git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221215181401-0b8a26d47532/go.mod h1:uFKw2wmgtlYMdiIm08dM0Vj4XvX9ZKVCj71c8O7SAPo=
+git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221215201903-f66fe2cea35f h1:0/09N8iy4t3a//5z5VB1tNjmx6XnPxtcjpOGGPoVs5c=
+git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221215201903-f66fe2cea35f/go.mod h1:uFKw2wmgtlYMdiIm08dM0Vj4XvX9ZKVCj71c8O7SAPo=
 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=
@@ -496,18 +500,50 @@ gitlab.com/elixxir/client/v4 v4.3.9-0.20221208215024-325fafebf519 h1:nxGzvotSXP/
 gitlab.com/elixxir/client/v4 v4.3.9-0.20221208215024-325fafebf519/go.mod h1:ID5txokZGTr7l+xTAoEtQMSSdvNZUBntJAWTR81upds=
 gitlab.com/elixxir/client/v4 v4.3.9-0.20221210003613-b73478d56e0d h1:Ydy9DnxHrrCfHY2UI6//88wT9L2kKtXUuA6di7ER3Ew=
 gitlab.com/elixxir/client/v4 v4.3.9-0.20221210003613-b73478d56e0d/go.mod h1:76yQ2oAQgAIFsb71+sZXeb361RBEfOU7jjY8gtyXTgU=
+gitlab.com/elixxir/client/v4 v4.3.11 h1:LGXdAjnGdWbK1eVtrAn5Fwage7vrlrgPXsoEtUJ4Lyg=
+gitlab.com/elixxir/client/v4 v4.3.11/go.mod h1:gxRW2YXDxCzESBnYqMDKH2I25TGv226TDujdQno3JQw=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219162223-d8e2974631fd h1:/GmQucH2O1LLLQ0FrexmUMivW2BuMfLTXQ8n3nYPKj4=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219162223-d8e2974631fd/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219191501-3789240dd3ed h1:5Gahh0ZkYoJEFgqDm6IYhPQYsUBVmfexVLcxB5ewauQ=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219191501-3789240dd3ed/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219191912-e8cd5d26d6df h1:E+LeA/BoZVkAiqjJ09QkIHXeAld5guOoaTuFkR1kwtY=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219191912-e8cd5d26d6df/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219194340-7f6106b6ec9c h1:LP07xMoRjwngjY2CIr8+ArZ3r8sCXnDUNe6CWLLg9xU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219194340-7f6106b6ec9c/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219194906-a8842179cf63 h1:4Adk73psccchWDHeI9pm/uI/ztIwErU/3bnxCCUe9kA=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219194906-a8842179cf63/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219202220-4a509612689a h1:UR69fckejY5S1hjg/qkDLu+hi6rT0gYUPgq6fXYGC8k=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219202220-4a509612689a/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219212339-818b3e804db2 h1:G+yJqmvXOmCmskwRndMg5MS9hFXgeBhWCcPyDv+L99w=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219212339-818b3e804db2/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219214237-b1d228b3a2ee h1:STHVMACRZRSiC2+d5opRbbqwNp7jmOVemxbrPju6/mo=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219214237-b1d228b3a2ee/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219215313-24f256274d49 h1:CHKcCZ0RIsfF0/KnEmeq9ca4gDLLTDzkhCw29u/a+OY=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219215313-24f256274d49/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219215743-62264017437e h1:LoQu539ZiyEagrfrs8gaZ0w4IyjQomfZwSK/QVJ4ejA=
+gitlab.com/elixxir/client/v4 v4.3.12-0.20221219215743-62264017437e/go.mod h1:lwm2uYZQHy12rDtANimw02Hfws8osuVkCKN6snm/jCU=
 gitlab.com/elixxir/comms v0.0.4-0.20221110181420-84bca6216fe4 h1:bLRjVCyMVde4n2hTVgoyyIAWrKI4CevpChchkPeb6A0=
 gitlab.com/elixxir/comms v0.0.4-0.20221110181420-84bca6216fe4/go.mod h1:XhI2/CMng+xcH3mAs+1aPz29PSNu1079XMJ8V+xxihw=
+gitlab.com/elixxir/comms v0.0.4-0.20221215214627-7807bfdde33a h1:DuqDqWc5cWjZ3qk98K1Bf9y1dYlyCeIigFmkHWDKc1Q=
+gitlab.com/elixxir/comms v0.0.4-0.20221215214627-7807bfdde33a/go.mod h1:B2Yek4mCbtN2aXZkyZcUffd3sTEZ5WgKD0mRBSVYtF8=
 gitlab.com/elixxir/crypto v0.0.7-0.20221208214832-13e2a751db1a h1:d514iJOaPmH2qjqUyI1N93UyEPTWvZ40LJiRPvQ89jw=
 gitlab.com/elixxir/crypto v0.0.7-0.20221208214832-13e2a751db1a/go.mod h1:fb6UMdmr0hVnzOU67hOZzTeS+wcQZ4pUtTO82039wGg=
 gitlab.com/elixxir/crypto v0.0.7-0.20221209195912-492ced8f7c92 h1:76JsxCk9Qwe1SFzYkrP42YVffhDcHXjwDBH/B3YS40U=
 gitlab.com/elixxir/crypto v0.0.7-0.20221209195912-492ced8f7c92/go.mod h1:fb6UMdmr0hVnzOU67hOZzTeS+wcQZ4pUtTO82039wGg=
+gitlab.com/elixxir/crypto v0.0.7-0.20221214192244-6783272c04a0 h1:dwCf7wKv2DCuYZZ394bSQWdUOXiABLsEyDvXZUOo83o=
+gitlab.com/elixxir/crypto v0.0.7-0.20221214192244-6783272c04a0/go.mod h1:oRh3AwveOEvpk9E3kRcMGK8fImcEnN0PY4jr9HDgQE8=
+gitlab.com/elixxir/crypto v0.0.7-0.20221219161351-7c3751afd8f2 h1:QPTL5+jHqfauN8WrmHMwvJ/d0Rbm/EY20Iggeu+NPvs=
+gitlab.com/elixxir/crypto v0.0.7-0.20221219161351-7c3751afd8f2/go.mod h1:7whUm4bnEdEoiVfMnu3TbHgvlrz0Ywp/Tekqg2Wl7vw=
 gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is=
 gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU=
 gitlab.com/elixxir/primitives v0.0.3-0.20221114231218-cc461261a6af h1:xcPqknK1ehNb9xwcutTdoR0YgD7DC/ySh9z49tIpSxQ=
 gitlab.com/elixxir/primitives v0.0.3-0.20221114231218-cc461261a6af/go.mod h1:DUnCTXYKgjpro5+6ITySKIf+qzW2vhW40IVHMimdsqw=
+gitlab.com/elixxir/primitives v0.0.3-0.20221214192222-988b44a6958a h1:F17FfEjS+/uDI/TTYQD21S5JvNZ9+p9bieau2nyLCzo=
+gitlab.com/elixxir/primitives v0.0.3-0.20221214192222-988b44a6958a/go.mod h1:DUnCTXYKgjpro5+6ITySKIf+qzW2vhW40IVHMimdsqw=
 gitlab.com/xx_network/comms v0.0.4-0.20221207203143-462f82d6ec01 h1:0jkud7OWqneH9xEjDARnLspspxA3SDsNrbg8heY+wMQ=
 gitlab.com/xx_network/comms v0.0.4-0.20221207203143-462f82d6ec01/go.mod h1:+RfHgk75ywMvmucOpPS7rSUlsnbPyBuLsr13tsthUTE=
+gitlab.com/xx_network/comms v0.0.4-0.20221215214252-1275cef8760e h1:l+FiCBP2Lc1+cR6xwWDVDvSHnuzOaZFIRUEYGUwKGBA=
+gitlab.com/xx_network/comms v0.0.4-0.20221215214252-1275cef8760e/go.mod h1:FR/OyruSuob6+xzSZtk+rXlncbRr6nDKFypX3vwtkFc=
 gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46 h1:6AHgUpWdJ72RVTTdJSvfThZiYTQNUnrPaTCl/EkRLpg=
 gitlab.com/xx_network/crypto v0.0.5-0.20221121220724-8eefdbb0eb46/go.mod h1:acWUBKCpae/XVaQF7J9RnLAlBT13i5r7gnON+mrIxBk=
 gitlab.com/xx_network/primitives v0.0.4-0.20221110180011-fd6ea3058225 h1:TAn87e6Zt9KwcSnWKyIul5eu8T0RHY9FDubCGs3G0dw=
@@ -939,3 +975,5 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+src.agwa.name/tlshacks v0.0.0-20220518131152-d2c6f4e2b780 h1:iMW3HbLV3/OuK02FDW8qNC13i5o1uK079MGLH404rnQ=
+src.agwa.name/tlshacks v0.0.0-20220518131152-d2c6f4e2b780/go.mod h1:NT4HI59yJusF5Il4/DlC8F5+mfylE4CbRVwdoEi6MF8=
diff --git a/indexedDb/channels/implementation.go b/indexedDb/channels/implementation.go
index bcd957f13863d47e2ce493f6c00b5e5bfd4775b4..cf64d77f6dc3a492990110061e5d76b0ac8e4cf4 100644
--- a/indexedDb/channels/implementation.go
+++ b/indexedDb/channels/implementation.go
@@ -13,11 +13,12 @@ import (
 	"crypto/ed25519"
 	"encoding/base64"
 	"encoding/json"
-	"gitlab.com/elixxir/xxdk-wasm/indexedDb"
 	"sync"
 	"syscall/js"
 	"time"
 
+	"gitlab.com/elixxir/xxdk-wasm/indexedDb"
+
 	"github.com/hack-pad/go-indexeddb/idb"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
@@ -26,6 +27,7 @@ import (
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
 	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"gitlab.com/xx_network/primitives/id"
 )
@@ -166,7 +168,7 @@ func (w *wasmModel) deleteMsgByChannel(channelID *id.ID) error {
 // It may be called multiple times on the same message; it is incumbent on the
 // user of the API to filter such called by message ID.
 func (w *wasmModel) ReceiveMessage(channelID *id.ID,
-	messageID cryptoChannel.MessageID, nickname, text string,
+	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) uint64 {
@@ -202,7 +204,7 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID,
 // Messages may arrive our of order, so a reply, in theory, can arrive before
 // the initial message. As a result, it may be important to buffer replies.
 func (w *wasmModel) ReceiveReply(channelID *id.ID,
-	messageID cryptoChannel.MessageID, replyTo cryptoChannel.MessageID,
+	messageID message.ID, replyTo 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) uint64 {
@@ -238,7 +240,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID,
 // Messages may arrive our of order, so a reply, in theory, can arrive before
 // the initial message. As a result, it may be important to buffer reactions.
 func (w *wasmModel) ReceiveReaction(channelID *id.ID,
-	messageID cryptoChannel.MessageID, reactionTo cryptoChannel.MessageID,
+	messageID message.ID, reactionTo message.ID,
 	nickname, reaction string, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
 	timestamp time.Time, lease time.Duration, round rounds.Round,
 	mType channels.MessageType, status channels.SentStatus) uint64 {
@@ -272,7 +274,7 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID,
 //
 // TODO: Potential race condition due to separate get/update operations.
 func (w *wasmModel) UpdateSentStatus(uuid uint64,
-	messageID cryptoChannel.MessageID, timestamp time.Time, round rounds.Round,
+	messageID message.ID, timestamp time.Time, round rounds.Round,
 	status channels.SentStatus) {
 	parentErr := errors.New("failed to UpdateSentStatus")
 
@@ -303,7 +305,7 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64,
 	}
 
 	newMessage.Status = uint8(status)
-	if !messageID.Equals(cryptoChannel.MessageID{}) {
+	if !messageID.Equals(message.ID{}) {
 		newMessage.MessageID = messageID.Bytes()
 	}
 
@@ -387,7 +389,7 @@ func (w *wasmModel) receiveHelper(newMessage *Message, isUpdate bool) (uint64,
 	// NOTE: Sometimes the insert fails to return an error but hits a duplicate
 	//  insert, so this fallthrough returns the UUID entry in that case.
 	if res.IsUndefined() {
-		msgID := cryptoChannel.MessageID{}
+		msgID := message.ID{}
 		copy(msgID[:], newMessage.MessageID)
 		uuid, errLookup := w.msgIDLookup(msgID)
 		if uuid != 0 && errLookup == nil {
@@ -402,7 +404,7 @@ func (w *wasmModel) receiveHelper(newMessage *Message, isUpdate bool) (uint64,
 }
 
 // msgIDLookup gets the UUID of the Message with the given messageID.
-func (w *wasmModel) msgIDLookup(messageID cryptoChannel.MessageID) (uint64,
+func (w *wasmModel) msgIDLookup(messageID message.ID) (uint64,
 	error) {
 	msgIDStr := js.ValueOf(base64.StdEncoding.EncodeToString(messageID.Bytes()))
 	resultObj, err := indexedDb.GetIndex(w.db, messageStoreName,
diff --git a/indexedDb/channels/implementation_test.go b/indexedDb/channels/implementation_test.go
index 9f17457013d26125f05d411d29b09afff4cd16c3..8f240d9f5f5f949f9501a7c8c05e3249f78f5b5f 100644
--- a/indexedDb/channels/implementation_test.go
+++ b/indexedDb/channels/implementation_test.go
@@ -12,22 +12,23 @@ package channels
 import (
 	"encoding/json"
 	"fmt"
+	"os"
+	"strconv"
+	"testing"
+	"time"
+
 	"github.com/hack-pad/go-indexeddb/idb"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/primitives/netTime"
-	"os"
-	"strconv"
-	"testing"
-	"time"
 
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/v4/channels"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	"gitlab.com/elixxir/crypto/channel"
 	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/xx_network/primitives/id"
 )
 
@@ -53,7 +54,8 @@ func TestWasmModel_msgIDLookup(t *testing.T) {
 
 			storage.GetLocalStorage().Clear()
 			testString := "test"
-			testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
+			testMsgId := message.DeriveChannelMessageID(&id.ID{1},
+				0, []byte(testString))
 			eventModel, err := newWASMModel(testString, c, dummyCallback)
 			if err != nil {
 				t.Fatalf("%+v", err)
@@ -92,7 +94,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 		t.Run(fmt.Sprintf("Test_wasmModel_UpdateSentStatus%s", cs), func(t *testing.T) {
 			storage.GetLocalStorage().Clear()
 			testString := "test"
-			testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
+			testMsgId := message.DeriveChannelMessageID(&id.ID{1},
+				0, []byte(testString))
 			eventModel, err := newWASMModel(testString, c, dummyCallback)
 			if err != nil {
 				t.Fatalf("%+v", err)
@@ -221,7 +224,7 @@ func Test_wasmModel_UUIDTest(t *testing.T) {
 			for i := 0; i < 10; i++ {
 				// Store a test message
 				channelID := id.NewIdFromBytes([]byte(testString), t)
-				msgID := channel.MessageID{}
+				msgID := message.ID{}
 				copy(msgID[:], testString+fmt.Sprintf("%d", i))
 				rnd := rounds.Round{ID: id.Round(42)}
 				uuid := eventModel.ReceiveMessage(channelID, msgID, "test",
@@ -263,7 +266,7 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) {
 
 			uuids := make([]uint64, 10)
 
-			msgID := channel.MessageID{}
+			msgID := message.ID{}
 			copy(msgID[:], testString)
 			for i := 0; i < 10; i++ {
 				// Store a test message
@@ -324,7 +327,8 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
 					thisChannel = keepChannel
 				}
 
-				testMsgId := channel.MakeMessageID([]byte(testStr), &id.ID{1})
+				testMsgId := message.DeriveChannelMessageID(
+					&id.ID{1}, 0, []byte(testStr))
 				eventModel.ReceiveMessage(thisChannel, testMsgId, testStr, testStr,
 					[]byte{8, 6, 7, 5}, 0, 0, netTime.Now(), time.Second,
 					rounds.Round{ID: id.Round(0)}, 0, channels.Sent)
@@ -398,7 +402,8 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
 			}
 
 			// First message insert should succeed
-			testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1})
+			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, netTime.Now(),
 				time.Second, 0, 0, channels.Sent)
@@ -421,7 +426,8 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
 			}
 
 			// Now insert a message with a different message ID from the first
-			testMsgId2 := channel.MakeMessageID([]byte(testString), &id.ID{2})
+			testMsgId2 := message.DeriveChannelMessageID(&id.ID{1},
+				0, []byte(testString))
 			testMsg = buildMessage([]byte(testString), testMsgId2.Bytes(), nil,
 				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
 				time.Second, 0, 0, channels.Sent)
diff --git a/indexedDb/dm/implementation.go b/indexedDb/dm/implementation.go
index 5f2361ac1363714d3f8d899231ab7f746a8ad1b9..6324aecfa8d38e463496e95213b5dba89b9e18ab 100644
--- a/indexedDb/dm/implementation.go
+++ b/indexedDb/dm/implementation.go
@@ -12,6 +12,11 @@ package channelEventModel
 import (
 	"crypto/ed25519"
 	"encoding/json"
+	"strings"
+	"sync"
+	"syscall/js"
+	"time"
+
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
@@ -19,13 +24,10 @@ import (
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"gitlab.com/xx_network/primitives/id"
-	"strings"
-	"sync"
-	"syscall/js"
-	"time"
 
 	"github.com/hack-pad/go-indexeddb/idb"
 	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/elixxir/crypto/message"
 )
 
 // wasmModel implements [dm.Receiver] interface, which uses the channels
@@ -93,7 +95,7 @@ func buildMessage(messageID, parentID []byte, text []byte,
 	}
 }
 
-func (w *wasmModel) Receive(messageID dm.MessageID, nickname string, text []byte,
+func (w *wasmModel) Receive(messageID message.ID, nickname string, text []byte,
 	pubKey ed25519.PublicKey, dmToken uint32, codeset uint8, timestamp time.Time,
 	round rounds.Round, mType dm.MessageType, status dm.Status) uint64 {
 	parentErr := errors.New("failed to Receive")
@@ -135,7 +137,7 @@ func (w *wasmModel) Receive(messageID dm.MessageID, nickname string, text []byte
 	return uuid
 }
 
-func (w *wasmModel) ReceiveText(messageID dm.MessageID, nickname, text string,
+func (w *wasmModel) ReceiveText(messageID message.ID, nickname, text string,
 	pubKey ed25519.PublicKey, dmToken uint32, codeset uint8, timestamp time.Time,
 	round rounds.Round, status dm.Status) uint64 {
 	parentErr := errors.New("failed to ReceiveText")
@@ -179,7 +181,7 @@ func (w *wasmModel) ReceiveText(messageID dm.MessageID, nickname, text string,
 	return uuid
 }
 
-func (w *wasmModel) ReceiveReply(messageID dm.MessageID, reactionTo dm.MessageID,
+func (w *wasmModel) ReceiveReply(messageID message.ID, reactionTo message.ID,
 	nickname, text string, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
 	timestamp time.Time, round rounds.Round, status dm.Status) uint64 {
 	parentErr := errors.New("failed to ReceiveReply")
@@ -223,7 +225,7 @@ func (w *wasmModel) ReceiveReply(messageID dm.MessageID, reactionTo dm.MessageID
 	return uuid
 }
 
-func (w *wasmModel) ReceiveReaction(messageID dm.MessageID, reactionTo dm.MessageID,
+func (w *wasmModel) ReceiveReaction(messageID message.ID, reactionTo message.ID,
 	nickname, reaction string, pubKey ed25519.PublicKey, dmToken uint32,
 	codeset uint8, timestamp time.Time, round rounds.Round, status dm.Status) uint64 {
 	parentErr := errors.New("failed to ReceiveText")
@@ -268,7 +270,7 @@ func (w *wasmModel) ReceiveReaction(messageID dm.MessageID, reactionTo dm.Messag
 }
 
 func (w *wasmModel) UpdateSentStatus(uuid uint64,
-	messageID dm.MessageID, timestamp time.Time, round rounds.Round,
+	messageID message.ID, timestamp time.Time, round rounds.Round,
 	status dm.Status) {
 	parentErr := errors.New("failed to UpdateSentStatus")
 
@@ -299,7 +301,7 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64,
 	}
 
 	newMessage.Status = uint8(status)
-	if !messageID.Equals(dm.MessageID{}) {
+	if !messageID.Equals(message.ID{}) {
 		newMessage.MessageID = messageID.Bytes()
 	}
 
@@ -351,7 +353,7 @@ func (w *wasmModel) receiveHelper(newMessage *Message, isUpdate bool) (uint64,
 	// NOTE: Sometimes the insert fails to return an error but hits a duplicate
 	//  insert, so this fallthrough returns the UUID entry in that case.
 	if res.IsUndefined() {
-		msgID := cryptoChannel.MessageID{}
+		msgID := message.ID{}
 		copy(msgID[:], newMessage.MessageID)
 		uuid, errLookup := w.msgIDLookup(msgID)
 		if uuid != 0 && errLookup == nil {
@@ -366,7 +368,7 @@ func (w *wasmModel) receiveHelper(newMessage *Message, isUpdate bool) (uint64,
 }
 
 // msgIDLookup gets the UUID of the Message with the given messageID.
-func (w *wasmModel) msgIDLookup(messageID cryptoChannel.MessageID) (uint64,
+func (w *wasmModel) msgIDLookup(messageID message.ID) (uint64,
 	error) {
 	resultObj, err := indexedDb.GetIndex(w.db, messageStoreName,
 		messageStoreMessageIndex, utils.CopyBytesToJS(messageID.Marshal()))
diff --git a/indexedDb/dm/init.go b/indexedDb/dm/init.go
index d2c3ba1dbb2d6306a39ffc8f5f0b4e6f0159dc73..380df647c810d7fb6239cee9c4e7c1fc4181e4ca 100644
--- a/indexedDb/dm/init.go
+++ b/indexedDb/dm/init.go
@@ -11,6 +11,8 @@ package channelEventModel
 
 import (
 	"crypto/ed25519"
+	"syscall/js"
+
 	"github.com/hack-pad/go-indexeddb/idb"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
@@ -18,7 +20,6 @@ import (
 	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
-	"syscall/js"
 )
 
 const (
@@ -39,7 +40,7 @@ type MessageReceivedCallback func(uuid uint64, pubKey ed25519.PublicKey, update
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key.
 func NewWASMEventModel(path string, encryption cryptoChannel.Cipher,
-	cb MessageReceivedCallback) (dm.Receiver, error) {
+	cb MessageReceivedCallback) (dm.EventModel, error) {
 	databaseName := path + databaseSuffix
 	return newWASMModel(databaseName, encryption, cb)
 }
diff --git a/main.go b/main.go
index d06333fb8dd96ea545ad3d00fb182e8849ae1dad..d18364cc74455d2b2c68b1b97e423d8c68d4025e 100644
--- a/main.go
+++ b/main.go
@@ -11,13 +11,14 @@ package main
 
 import (
 	"fmt"
+	"os"
+	"syscall/js"
+
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/v4/bindings"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"gitlab.com/elixxir/xxdk-wasm/wasm"
-	"os"
-	"syscall/js"
 )
 
 func init() {
@@ -89,6 +90,15 @@ func main() {
 	js.Global().Set("NewChannelsDatabaseCipher",
 		js.FuncOf(wasm.NewChannelsDatabaseCipher))
 
+	// wasm/dm.go
+	js.Global().Set("NewDMClient", js.FuncOf(wasm.NewDMClient))
+	js.Global().Set("NewDMClientWithIndexedDb",
+		js.FuncOf(wasm.NewDMClientWithIndexedDb))
+	js.Global().Set("NewDMClientWithIndexedDbUnsafe",
+		js.FuncOf(wasm.NewDMClientWithIndexedDbUnsafe))
+	js.Global().Set("NewDMsDatabaseCipher",
+		js.FuncOf(wasm.NewDMsDatabaseCipher))
+
 	// wasm/cmix.go
 	js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix))
 	js.Global().Set("LoadCmix", js.FuncOf(wasm.LoadCmix))
diff --git a/wasm/dm.go b/wasm/dm.go
new file mode 100644
index 0000000000000000000000000000000000000000..678ca74f529f1ccce1a0a8cac63242b5073ed835
--- /dev/null
+++ b/wasm/dm.go
@@ -0,0 +1,872 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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 (
+	"crypto/ed25519"
+	"syscall/js"
+
+	indexDB "gitlab.com/elixxir/xxdk-wasm/indexedDb/dm"
+
+	"encoding/base64"
+
+	"gitlab.com/elixxir/client/v4/bindings"
+	"gitlab.com/elixxir/crypto/codename"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Basic Channel API                                                          //
+////////////////////////////////////////////////////////////////////////////////
+
+// DMClient wraps the [bindings.DMClient] object so its methods
+// can be wrapped to be Javascript compatible.
+type DMClient struct {
+	api *bindings.DMClient
+}
+
+// newDMClientJS creates a new Javascript compatible object
+// (map[string]any) that matches the [DMClient] structure.
+func newDMClientJS(api *bindings.DMClient) map[string]any {
+	cm := DMClient{api}
+	dmClientMap := map[string]any{
+		// Basic Channel API
+		"GetID": js.FuncOf(cm.GetID),
+
+		// Identity and Nickname Controls
+		"GetPublicKey":          js.FuncOf(cm.GetPublicKey),
+		"GetToken":              js.FuncOf(cm.GetToken),
+		"GetIdentity":           js.FuncOf(cm.GetIdentity),
+		"ExportPrivateIdentity": js.FuncOf(cm.ExportPrivateIdentity),
+		"SetNickname":           js.FuncOf(cm.SetNickname),
+		"GetNickname":           js.FuncOf(cm.GetNickname),
+
+		// DM Sending Methods and Reports
+		"SendText":     js.FuncOf(cm.SendText),
+		"SendReply":    js.FuncOf(cm.SendReply),
+		"SendReaction": js.FuncOf(cm.SendReaction),
+		"Send":         js.FuncOf(cm.Send),
+	}
+
+	return dmClientMap
+}
+
+// GetPublicKey returns the ecdh Public Key for this [DMClient] in the
+// [DMClient] tracker.
+//
+// Returns:
+//   - Tracker ID (int).
+func (ch *DMClient) GetID(js.Value, []js.Value) any {
+	return ch.api.GetID()
+}
+
+func (ch *DMClient) GetPublicKey(js.Value, []js.Value) any {
+	return ch.api.GetPublicKey()
+}
+
+func (ch *DMClient) GetToken(js.Value, []js.Value) any {
+	return ch.api.GetToken()
+}
+
+// dmReceiverBuilder adheres to the [bindings.DMReceiverBuilder] interface.
+type dmReceiverBuilder struct {
+	build func(args ...any) js.Value
+}
+
+// Build initializes and returns the event model.  It wraps a Javascript object
+// that has all the methods in [bindings.EventModel] to make it adhere to the Go
+// interface [bindings.EventModel].
+func (emb *dmReceiverBuilder) Build(path string) bindings.DMReceiver {
+	emJs := emb.build(path)
+	return &dmReceiver{
+		receive:          utils.WrapCB(emJs, "ReceiveText"),
+		receiveText:      utils.WrapCB(emJs, "ReceiveText"),
+		receiveReply:     utils.WrapCB(emJs, "ReceiveReply"),
+		receiveReaction:  utils.WrapCB(emJs, "ReceiveReaction"),
+		updateSentStatus: utils.WrapCB(emJs, "UpdateSentStatus"),
+	}
+}
+
+// NewDMClient creates a new [DMClient] from a new private
+// identity ([channel.PrivateIdentity]).
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadDMClient], passing in the
+// storage tag retrieved by [DMClient.GetStorageTag].
+//
+// Parameters:
+//   - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
+//     using [Cmix.GetID].
+//   - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is
+//     generated by [GenerateChannelIdentity] (Uint8Array).
+//   - args[2] - A function that initialises and returns a Javascript object
+//     that matches the [bindings.EventModel] interface. The function must match
+//     the Build function in [bindings.EventModelBuilder].
+//
+// Returns:
+//   - Javascript representation of the [DMClient] object.
+//   - Throws a TypeError if creating the manager fails.
+func NewDMClient(_ js.Value, args []js.Value) any {
+	privateIdentity := utils.CopyBytesToGo(args[1])
+
+	em := &dmReceiverBuilder{args[2].Invoke}
+
+	cm, err := bindings.NewDMClient(args[0].Int(), privateIdentity, em)
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return newDMClientJS(cm)
+}
+
+// NewDMClientWithIndexedDb creates a new [DMClient] from a new
+// private identity ([channel.PrivateIdentity]) and using indexedDb as a backend
+// to manage the event model.
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadDMClientWithIndexedDb], passing
+// in the storage tag retrieved by [DMClient.GetStorageTag].
+//
+// This function initialises an indexedDb database.
+//
+// Parameters:
+//   - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
+//     using [Cmix.GetID].
+//   - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is
+//     generated by [GenerateChannelIdentity] (Uint8Array).
+//   - args[2] - Function that takes in the same parameters as
+//     [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is
+//     returned as an int and the channelID as a Uint8Array. The row in the
+//     database that was updated can be found using the UUID. The channel ID is
+//     provided so that the recipient can filter if they want to the processes
+//     the update now or not. An "update" bool is present which tells you if the
+//     row is new or if it is an edited old row.
+//   - args[3] - ID of [ChannelDbCipher] object in tracker (int). Create this
+//     object with [NewChannelsDatabaseCipher] and get its id with
+//     [ChannelDbCipher.GetID].
+//
+// Returns a promise:
+//   - Resolves to a Javascript representation of the [DMClient] object.
+//   - Rejected with an error if loading indexedDb or the manager fails.
+//   - Throws a TypeError if the cipher ID does not correspond to a cipher.
+func NewDMClientWithIndexedDb(_ js.Value, args []js.Value) any {
+	cmixID := args[0].Int()
+	privateIdentity := utils.CopyBytesToGo(args[1])
+	messageReceivedCB := args[2]
+	cipherID := args[3].Int()
+
+	cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+	}
+
+	return newDMClientWithIndexedDb(
+		cmixID, privateIdentity, messageReceivedCB, cipher)
+}
+
+// NewDMClientWithIndexedDbUnsafe creates a new [DMClient] from a
+// new private identity ([channel.PrivateIdentity]) and using indexedDb as a
+// backend to manage the event model. However, the data is written in plain text
+// and not encrypted. It is recommended that you do not use this in production.
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadDMClientWithIndexedDbUnsafe],
+// passing in the storage tag retrieved by [DMClient.GetStorageTag].
+//
+// This function initialises an indexedDb database.
+//
+// Parameters:
+//   - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
+//     using [Cmix.GetID].
+//   - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is
+//     generated by [GenerateChannelIdentity] (Uint8Array).
+//   - args[2] - Function that takes in the same parameters as
+//     [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is
+//     returned as an int and the channelID as a Uint8Array. The row in the
+//     database that was updated can be found using the UUID. The channel ID is
+//     provided so that the recipient can filter if they want to the processes
+//     the update now or not. An "update" bool is present which tells you if
+//     the row is new or if it is an edited old row
+//
+// Returns a promise:
+//   - Resolves to a Javascript representation of the [DMClient] object.
+//   - Rejected with an error if loading indexedDb or the manager fails.
+func NewDMClientWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
+	cmixID := args[0].Int()
+	privateIdentity := utils.CopyBytesToGo(args[1])
+	messageReceivedCB := args[2]
+
+	return newDMClientWithIndexedDb(
+		cmixID, privateIdentity, messageReceivedCB, nil)
+}
+
+func newDMClientWithIndexedDb(cmixID int, privateIdentity []byte,
+	cb js.Value, cipher *bindings.ChannelDbCipher) any {
+
+	messageReceivedCB := func(uuid uint64, pubKey ed25519.PublicKey,
+		update bool) {
+		cb.Invoke(uuid, utils.CopyBytesToJS(pubKey[:]), update)
+	}
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+
+		pi, err := codename.UnmarshalPrivateIdentity(privateIdentity)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		}
+		dmPath := base64.RawStdEncoding.EncodeToString(pi.PubKey[:])
+		model, err := indexDB.NewWASMEventModel(dmPath, cipher,
+			messageReceivedCB)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		}
+
+		cm, err := bindings.NewDMClientWithGoEventModel(
+			cmixID, privateIdentity, model)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(newDMClientJS(cm))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Channel Sending Methods and Reports                                        //
+////////////////////////////////////////////////////////////////////////////////
+
+// SendGeneric is used to send a raw message over a channel. In general, it
+// should be wrapped in a function which defines the wire protocol. If the final
+// message, before being sent over the wire, is too long, this will return an
+// error. Due to the underlying encoding using compression, it isn't possible to
+// define the largest payload that can be sent, but it will always be possible
+// to send a payload of 802 bytes at minimum. The meaning of validUntil depends
+// on the use case.
+//
+// Parameters:
+//   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - args[1] - The message type of the message. This will be a valid
+//     [channels.MessageType] (int).
+//   - args[2] - The contents of the message (Uint8Array).
+//   - args[3] - The lease of the message. This will be how long the message is
+//     valid until, in milliseconds. As per the [channels.Manager]
+//     documentation, this has different meanings depending on the use case.
+//     These use cases may be generic enough that they will not be enumerated
+//     here (int).
+//   - args[4] - JSON of [xxdk.CMIXParams]. If left empty
+//     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns a promise:
+//   - Resolves to the JSON of [bindings.ChannelSendReport] (Uint8Array).
+//   - Rejected with an error if sending fails.
+func (ch *DMClient) Send(_ js.Value, args []js.Value) any {
+	messageType := args[0].Int()
+	partnerPubKeyBytes := utils.CopyBytesToGo(args[1])
+	partnerToken := args[2].Int()
+	message := utils.CopyBytesToGo(args[3])
+	leaseTimeMS := int64(args[4].Int())
+	cmixParamsJSON := utils.CopyBytesToGo(args[5])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		sendReport, err := ch.api.Send(messageType, partnerPubKeyBytes,
+			uint32(partnerToken), message, leaseTimeMS,
+			cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// SendMessage is used to send a formatted message over a channel.
+// Due to the underlying encoding using compression, it isn't possible to define
+// the largest payload that can be sent, but it will always be possible to send
+// a payload of 798 bytes at minimum.
+//
+// The message will auto delete validUntil after the round it is sent in,
+// lasting forever if [channels.ValidForever] is used.
+//
+// Parameters:
+//   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - args[1] - The contents of the message (string).
+//   - args[2] - The lease of the message. This will be how long the message is
+//     valid until, in milliseconds. As per the [channels.Manager]
+//     documentation, this has different meanings depending on the use case.
+//     These use cases may be generic enough that they will not be enumerated
+//     here (int).
+//   - args[3] - JSON of [xxdk.CMIXParams]. If left empty
+//     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns a promise:
+//   - Resolves to the JSON of [bindings.ChannelSendReport] (Uint8Array).
+//   - Rejected with an error if sending fails.
+func (ch *DMClient) SendText(_ js.Value, args []js.Value) any {
+	partnerPubKeyBytes := utils.CopyBytesToGo(args[0])
+	partnerToken := args[1].Int()
+	message := args[2].String()
+	leaseTimeMS := int64(args[3].Int())
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		sendReport, err := ch.api.SendText(partnerPubKeyBytes,
+			uint32(partnerToken), message, leaseTimeMS,
+			cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// SendReply is used to send a formatted message over a channel. Due to the
+// underlying encoding using compression, it isn't possible to define the
+// largest payload that can be sent, but it will always be possible to send a
+// payload of 766 bytes at minimum.
+//
+// If the message ID the reply is sent to is nonexistent, the other side will
+// post the message as a normal message and not a reply. The message will auto
+// delete validUntil after the round it is sent in, lasting forever if
+// [channels.ValidForever] is used.
+//
+// Parameters:
+//   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - args[1] - The contents of the message. The message should be at most 510
+//     bytes. This is expected to be Unicode, and thus a string data type is
+//     expected (string).
+//   - args[2] - JSON of [channel.MessageID] of the message you wish to reply
+//     to. This may be found in the [bindings.ChannelSendReport] if replying to
+//     your own. Alternatively, if reacting to another user's message, you may
+//     retrieve it via the [bindings.ChannelMessageReceptionCallback] registered
+//     using  RegisterReceiveHandler (Uint8Array).
+//   - args[3] - The lease of the message. This will be how long the message is
+//     valid until, in milliseconds. As per the [channels.Manager]
+//     documentation, this has different meanings depending on the use case.
+//     These use cases may be generic enough that they will not be enumerated
+//     here (int).
+//   - args[4] - JSON of [xxdk.CMIXParams]. If left empty
+//     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns a promise:
+//   - Resolves to the JSON of [bindings.ChannelSendReport] (Uint8Array).
+//   - Rejected with an error if sending fails.
+func (ch *DMClient) SendReply(_ js.Value, args []js.Value) any {
+	partnerPubKeyBytes := utils.CopyBytesToGo(args[0])
+	partnerToken := args[1].Int()
+	replyID := utils.CopyBytesToGo(args[2])
+	message := args[3].String()
+	leaseTimeMS := int64(args[4].Int())
+	cmixParamsJSON := utils.CopyBytesToGo(args[5])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		sendReport, err := ch.api.SendReply(partnerPubKeyBytes,
+			uint32(partnerToken), message, replyID, leaseTimeMS,
+			cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// SendReaction is used to send a reaction to a message over a channel.
+// The reaction must be a single emoji with no other characters, and will
+// be rejected otherwise.
+// Users will drop the reaction if they do not recognize the reactTo message.
+//
+// Parameters:
+//   - args[0] - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - args[1] - The user's reaction. This should be a single emoji with no
+//     other characters. As such, a Unicode string is expected (string).
+//   - args[2] - JSON of [channel.MessageID] of the message you wish to reply
+//     to. This may be found in the [bindings.ChannelSendReport] if replying to
+//     your own. Alternatively, if reacting to another user's message, you may
+//     retrieve it via the ChannelMessageReceptionCallback registered using
+//     RegisterReceiveHandler (Uint8Array).
+//   - args[3] - JSON of [xxdk.CMIXParams]. If left empty
+//     [bindings.GetDefaultCMixParams] will be used internally (Uint8Array).
+//
+// Returns a promise:
+//   - Resolves to the JSON of [bindings.ChannelSendReport] (Uint8Array).
+//   - Rejected with an error if sending fails.
+func (ch *DMClient) SendReaction(_ js.Value, args []js.Value) any {
+	partnerPubKeyBytes := utils.CopyBytesToGo(args[0])
+	partnerToken := args[1].Int()
+	replyID := utils.CopyBytesToGo(args[2])
+	message := args[3].String()
+	cmixParamsJSON := utils.CopyBytesToGo(args[4])
+
+	promiseFn := func(resolve, reject func(args ...any) js.Value) {
+		sendReport, err := ch.api.SendReaction(partnerPubKeyBytes,
+			uint32(partnerToken), message, replyID,
+			cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
+	}
+
+	return utils.CreatePromise(promiseFn)
+}
+
+// GetIdentity returns the marshaled public identity ([codename.Identity]) that
+// the client is using.
+//
+// Returns:
+//   - JSON of the [channel.Identity] (Uint8Array).
+//   - Throws TypeError if marshalling the identity fails.
+func (ch *DMClient) GetIdentity(js.Value, []js.Value) any {
+	i := ch.api.GetIdentity()
+
+	return utils.CopyBytesToJS(i)
+}
+
+// ExportPrivateIdentity encrypts and exports the private identity to a portable
+// string.
+//
+// Parameters:
+//   - args[0] - Password to encrypt the identity with (string).
+//
+// Returns:
+//   - JSON of the encrypted private identity (Uint8Array).
+//   - Throws TypeError if exporting the identity fails.
+func (ch *DMClient) ExportPrivateIdentity(_ js.Value, args []js.Value) any {
+	i, err := ch.api.ExportPrivateIdentity(args[0].String())
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(i)
+}
+
+// SetNickname sets the nickname for a given channel. The nickname must be valid
+// according to [IsNicknameValid].
+//
+// Parameters:
+//   - args[0] - The nickname to set (string).
+//   - args[1] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
+//
+// Returns:
+//   - Throws TypeError if unmarshalling the ID fails or the nickname is
+//     invalid.
+func (ch *DMClient) SetNickname(_ js.Value, args []js.Value) any {
+	ch.api.SetNickname(args[0].String())
+	return nil
+}
+
+// GetNickname returns the nickname set for a given channel. Returns an error if
+// there is no nickname set.
+//
+// Parameters:
+//   - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
+//
+// Returns:
+//   - The nickname (string).
+//   - Throws TypeError if the channel has no nickname set.
+func (ch *DMClient) GetNickname(_ js.Value, args []js.Value) any {
+	nickname, err := ch.api.GetNickname(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return nickname
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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),
+		utils.JsTrace(err))
+
+	return uuid.Int()
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Event Model Logic                                                          //
+////////////////////////////////////////////////////////////////////////////////
+
+// dmReceiver wraps Javascript callbacks to adhere to the [bindings.EventModel]
+// interface.
+type dmReceiver struct {
+	receive          func(args ...any) js.Value
+	receiveText      func(args ...any) js.Value
+	receiveReply     func(args ...any) js.Value
+	receiveReaction  func(args ...any) js.Value
+	updateSentStatus func(args ...any) js.Value
+}
+
+// ReceiveMessage is called whenever a message is received on a given channel.
+// It may be called multiple times on the same message. It is incumbent on the
+// user of the API to filter such called by message ID.
+//
+// Parameters:
+//   - channelID - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - messageID - The bytes of the [channel.MessageID] of the received message
+//     (Uint8Array).
+//   - nickname - The nickname of the sender of the message (string).
+//   - text - The content of the message (string).
+//   - pubKey - The sender's Ed25519 public key (Uint8Array).
+//   - dmToken - The dmToken (int32).
+//   - codeset - The codeset version (int).
+//   - timestamp - Time the message was received; represented as nanoseconds
+//     since unix epoch (int).
+//   - lease - The number of nanoseconds that the message is valid for (int).
+//   - roundId - The ID of the round that the message was received on (int).
+//   - msgType - The type of message ([channels.MessageType]) to send (int).
+//   - status - The [channels.SentStatus] of the message (int).
+//
+// Statuses will be enumerated as such:
+//
+//	Sent      =  0
+//	Delivered =  1
+//	Failed    =  2
+//
+// Returns:
+//   - A non-negative unique UUID for the message that it can be referenced by
+//     later with [dmReceiver.UpdateSentStatus].
+func (em *dmReceiver) Receive(messageID []byte, nickname string,
+	text []byte, pubKey []byte, dmToken int32, codeset int, timestamp,
+	roundId, mType, status int64) int64 {
+	uuid := em.receive(messageID, nickname, text, pubKey, dmToken,
+		codeset, timestamp, roundId, mType, status)
+
+	return int64(uuid.Int())
+}
+
+// ReceiveText is called whenever a message is received that is a reply on a
+// given channel. It may be called multiple times on the same message. It is
+// incumbent on the user of the API to filter such called by message ID.
+//
+// Messages may arrive our of order, so a reply in theory can arrive before the
+// initial message. As a result, it may be important to buffer replies.
+//
+// Parameters:
+//   - channelID - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - messageID - The bytes of the [channel.MessageID] of the received message
+//     (Uint8Array).
+//   - reactionTo - The [channel.MessageID] for the message that received a
+//     reply (Uint8Array).
+//   - senderUsername - The username of the sender of the message (string).
+//   - text - The content of the message (string).
+//   - pubKey - The sender's Ed25519 public key (Uint8Array).
+//   - dmToken - The dmToken (int32).
+//   - codeset - The codeset version (int).
+//   - timestamp - Time the message was received; represented as nanoseconds
+//     since unix epoch (int).
+//   - lease - The number of nanoseconds that the message is valid for (int).
+//   - roundId - The ID of the round that the message was received on (int).
+//   - msgType - The type of message ([channels.MessageType]) to send (int).
+//   - status - The [channels.SentStatus] of the message (int).
+//
+// Statuses will be enumerated as such:
+//
+//	Sent      =  0
+//	Delivered =  1
+//	Failed    =  2
+//
+// Returns:
+//   - A non-negative unique UUID for the message that it can be referenced by
+//     later with [dmReceiver.UpdateSentStatus].
+func (em *dmReceiver) ReceiveText(messageID []byte, nickname, text string,
+	pubKey []byte, dmToken int32, codeset int, timestamp,
+	roundId, status int64) int64 {
+
+	uuid := em.receiveText(messageID, nickname, text, pubKey, dmToken,
+		codeset, timestamp, roundId, status)
+
+	return int64(uuid.Int())
+}
+
+// ReceiveReply is called whenever a message is received that is a reply on a
+// given channel. It may be called multiple times on the same message. It is
+// incumbent on the user of the API to filter such called by message ID.
+//
+// Messages may arrive our of order, so a reply in theory can arrive before the
+// initial message. As a result, it may be important to buffer replies.
+//
+// Parameters:
+//   - channelID - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - messageID - The bytes of the [channel.MessageID] of the received message
+//     (Uint8Array).
+//   - reactionTo - The [channel.MessageID] for the message that received a
+//     reply (Uint8Array).
+//   - senderUsername - The username of the sender of the message (string).
+//   - text - The content of the message (string).
+//   - pubKey - The sender's Ed25519 public key (Uint8Array).
+//   - dmToken - The dmToken (int32).
+//   - codeset - The codeset version (int).
+//   - timestamp - Time the message was received; represented as nanoseconds
+//     since unix epoch (int).
+//   - lease - The number of nanoseconds that the message is valid for (int).
+//   - roundId - The ID of the round that the message was received on (int).
+//   - msgType - The type of message ([channels.MessageType]) to send (int).
+//   - status - The [channels.SentStatus] of the message (int).
+//
+// Statuses will be enumerated as such:
+//
+//	Sent      =  0
+//	Delivered =  1
+//	Failed    =  2
+//
+// Returns:
+//   - A non-negative unique UUID for the message that it can be referenced by
+//     later with [dmReceiver.UpdateSentStatus].
+func (em *dmReceiver) ReceiveReply(messageID, replyTo []byte, nickname,
+	text string, pubKey []byte, dmToken int32, codeset int,
+	timestamp, roundId, status int64) int64 {
+	uuid := em.receiveReply(messageID, replyTo, nickname, text, pubKey,
+		dmToken, codeset, timestamp, roundId, status)
+
+	return int64(uuid.Int())
+}
+
+// ReceiveReaction is called whenever a reaction to a message is received on a
+// given channel. It may be called multiple times on the same reaction. It is
+// incumbent on the user of the API to filter such called by message ID.
+//
+// Messages may arrive our of order, so a reply in theory can arrive before the
+// initial message. As a result, it may be important to buffer reactions.
+//
+// Parameters:
+//   - channelID - Marshalled bytes of the channel [id.ID] (Uint8Array).
+//   - messageID - The bytes of the [channel.MessageID] of the received message
+//     (Uint8Array).
+//   - reactionTo - The [channel.MessageID] for the message that received a
+//     reply (Uint8Array).
+//   - senderUsername - The username of the sender of the message (string).
+//   - reaction - The contents of the reaction message (string).
+//   - pubKey - The sender's Ed25519 public key (Uint8Array).
+//   - dmToken - The dmToken (int32).
+//   - codeset - The codeset version (int).
+//   - timestamp - Time the message was received; represented as nanoseconds
+//     since unix epoch (int).
+//   - lease - The number of nanoseconds that the message is valid for (int).
+//   - roundId - The ID of the round that the message was received on (int).
+//   - msgType - The type of message ([channels.MessageType]) to send (int).
+//   - status - The [channels.SentStatus] of the message (int).
+//
+// Statuses will be enumerated as such:
+//
+//	Sent      =  0
+//	Delivered =  1
+//	Failed    =  2
+//
+// Returns:
+//   - A non-negative unique UUID for the message that it can be referenced by
+//     later with [dmReceiver.UpdateSentStatus].
+func (em *dmReceiver) ReceiveReaction(messageID, reactionTo []byte,
+	nickname, reaction string, pubKey []byte, dmToken int32,
+	codeset int, timestamp, roundId,
+	status int64) int64 {
+	uuid := em.receiveReaction(messageID, reactionTo, nickname, reaction,
+		pubKey, dmToken, codeset, timestamp, roundId, status)
+
+	return int64(uuid.Int())
+}
+
+// UpdateSentStatus is called whenever the sent status of a message has
+// changed.
+//
+// Parameters:
+//   - uuid - The unique identifier for the message (int).
+//   - messageID - The bytes of the [channel.MessageID] of the received message
+//     (Uint8Array).
+//   - timestamp - Time the message was received; represented as nanoseconds
+//     since unix epoch (int).
+//   - roundId - The ID of the round that the message was received on (int).
+//   - status - The [channels.SentStatus] of the message (int).
+//
+// Statuses will be enumerated as such:
+//
+//	Sent      =  0
+//	Delivered =  1
+//	Failed    =  2
+func (em *dmReceiver) UpdateSentStatus(uuid int64, messageID []byte,
+	timestamp, roundID, status int64) {
+	em.updateSentStatus(uuid, utils.CopyBytesToJS(messageID),
+		timestamp, roundID, status)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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 a TypeError 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 {
+		utils.Throw(utils.TypeError, 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 a TypeError 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 {
+		utils.Throw(utils.TypeError, 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 a TypeError 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 {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(plaintext)
+}
+
+// MarshalJSON marshals the cipher into valid JSON.
+//
+// Returns:
+//   - JSON of the cipher (Uint8Array).
+//   - Throws a TypeError if marshalling fails.
+func (c *DMDbCipher) MarshalJSON(js.Value, []js.Value) any {
+	data, err := c.api.MarshalJSON()
+	if err != nil {
+		utils.Throw(utils.TypeError, 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 a TypeError if marshalling fails.
+func (c *DMDbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
+	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+	return nil
+}
diff --git a/wasm/docs.go b/wasm/docs.go
index 33b0d56ed2951fa2e98c6217f09e77ebe4024fb4..2f783b6374afb19e8e7aede6e732274c7e17b854 100644
--- a/wasm/docs.go
+++ b/wasm/docs.go
@@ -23,11 +23,11 @@ import (
 	"gitlab.com/elixxir/client/v4/restlike"
 	"gitlab.com/elixxir/client/v4/single"
 	"gitlab.com/elixxir/crypto/broadcast"
-	"gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/elixxir/crypto/group"
+	cryptoMessage "gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/primitives/fact"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/id"
@@ -50,7 +50,7 @@ var (
 	_ = connect.Callback(nil)
 	_ = partner.Manager(nil)
 	_ = ndf.NetworkDefinition{}
-	_ = channel.MessageID{}
+	_ = cryptoMessage.ID{}
 	_ = channels.SentStatus(0)
 	_ = ftE2e.Params{}
 	_ = fileTransfer.TransferID{}
diff --git a/wasm_test.go b/wasm_test.go
index b7afa43099adac67ae4d2136676fe37477a05db5..68cc545ba79c45b67cf1eefdb596e28c69b5eba3 100644
--- a/wasm_test.go
+++ b/wasm_test.go
@@ -49,10 +49,11 @@ func TestPublicFunctions(t *testing.T) {
 		"GetGitVersion":   {},
 		"GetDependencies": {},
 
-		// DM Functions -- temporarily not implemented
+		// DM Functions these are used but not exported by
+		// WASM bindins, so are not exposed.
 		"NewDMReceiver":               {},
-		"NewDMClient":                 {},
 		"NewDMClientWithGoEventModel": {},
+		"GetDMDbCipherTrackerFromID":  {},
 	}
 	wasmFuncs := getPublicFunctions("wasm", t)
 	bindingsFuncs := getPublicFunctions(