diff --git a/README.md b/README.md
index 38dd95093b98c83220d1af039768d0da110cb0ef..a96835a5c0a7a4c83a3939c16d6f94f0569cb788 100644
--- a/README.md
+++ b/README.md
@@ -23,9 +23,9 @@ be run using the following command.
 $ GOOS=js GOARCH=wasm go test ./...
 ```
 
-Note, this will fail because `wasm/wasm_js.s` contains commands only recognized
+Note, this will fail because `utils/utils_js.s` contains commands only recognized
 by the Go WebAssembly compiler and for some reason not recognized by the test
-runner. To get tests to run, temporarily delete the body of `wasm/wasm_js.s`
+runner. To get tests to run, temporarily delete the body of `utils/utils_js.s`
 during testing.
 
 ## Testing
@@ -65,7 +65,7 @@ global.Go = class {
             go: {
                 // ...
                 // func Throw(exception string, message string)
-                'gitlab.com/elixxir/xxdk-wasm/wasm.throw': (sp) => {
+                'gitlab.com/elixxir/xxdk-wasm/utils.throw': (sp) => {
                     const exception = loadString(sp + 8)
                     const message = loadString(sp + 24)
                     throw globalThis[exception](message)
diff --git a/examples/ndf.json b/examples/ndf.json
new file mode 100644
index 0000000000000000000000000000000000000000..0ca92937841d565ce0ed0243f5f032e86cfbbe55
--- /dev/null
+++ b/examples/ndf.json
@@ -0,0 +1 @@
+{"Timestamp":"2022-09-02T10:27:19.6661079-07:00","Gateways":[{"Id":"uKOO90b6GDwXcLr/hGU3sjX6O0AmDA4nvC4bDEO7wFMB","Address":"0.0.0.0:8440","Tls_certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","Bin":"SouthAndCentralAmerica"},{"Id":"KDzk21/pQ01RtoOHdh7207dkUqx5Mq7MIJsRPsOKXcMB","Address":"0.0.0.0:8442","Tls_certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","Bin":"CentralEurope"},{"Id":"jgZt6uZkK6tTGRnk0tAmVO9Akv11y0yBPolNkw/e+bAB","Address":"0.0.0.0:8443","Tls_certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","Bin":"EasternEurope"},{"Id":"AFARX39tIj246msuyhutEWlFQv7LSaAe8cR0i+aB59gB","Address":"0.0.0.0:8444","Tls_certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","Bin":"MiddleEast"},{"Id":"EyVHEz37phB8XZVl2N6Lu3p/bxnVIBvoAPhbPXIcpZsB","Address":"0.0.0.0:8441","Tls_certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","Bin":"WesternEurope"}],"Nodes":[{"Id":"uKOO90b6GDwXcLr/hGU3sjX6O0AmDA4nvC4bDEO7wFMC","Address":"","Tls_certificate":"","Status":0},{"Id":"KDzk21/pQ01RtoOHdh7207dkUqx5Mq7MIJsRPsOKXcMC","Address":"","Tls_certificate":"","Status":0},{"Id":"jgZt6uZkK6tTGRnk0tAmVO9Akv11y0yBPolNkw/e+bAC","Address":"","Tls_certificate":"","Status":0},{"Id":"AFARX39tIj246msuyhutEWlFQv7LSaAe8cR0i+aB59gC","Address":"","Tls_certificate":"","Status":0},{"Id":"EyVHEz37phB8XZVl2N6Lu3p/bxnVIBvoAPhbPXIcpZsC","Address":"","Tls_certificate":"","Status":0}],"Registration":{"Address":":18000","ClientRegistrationAddress":"0.0.0.0:11421","Tls_certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","EllipticPubKey":"AWjTOI++PCwzIVLMiXcBHzKtxWShdbBLluGOtbnqd10="},"Notification":{"Address":"","Tls_certificate":""},"Udb":{"Id":"uu49Kr9myQUG0gG8VKwalxmem65pfPu4D2Lk1u8pi/UD","Cert":"-----BEGIN CERTIFICATE-----\r\nMIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\r\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp\r\ncDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT\r\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV\r\nBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh\r\nDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs\r\nWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE\r\ntJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA\r\nm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9\r\nbJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA\r\nAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA\r\nneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf\r\nU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2\r\nqvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4\r\ncyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R\r\ntgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5\r\n6m52PyzMNV+2N21IPppKwA==\r\n-----END CERTIFICATE-----\r\n","Address":"127.0.0.1:18001","DhPubKey":"eyJWYWx1ZSI6MTQ3MTM5NzkxNDA3Njk5MjM0OTk3MTkyOTEwNTk1MDc1NTI3NDM1NDYwMDYxNTI0NzcyOTQ0MzQzNzQ4NjgxMjA2MjY1ODY5ODQ2NjQyMzMyMzQ1NzA3OTkwNjc3NDA2MDQwMTQ1MTc3NTM0MzIxNTM4MTgyNDQwNzE3ODkwMzU5OTkzNDI4Njg2MTA5NTI1MjAyMTE4MjA1MjIzODkxNDMxODk5MTY4MjYxOTU5MTIzOTc3MDcxMzYzMDIzMTYxOTgzNTIwNjIwNDA1MjM1NzQ3NTkwMDg0NjkzNjYxODA0MTE1NjkxNjc3NDg4MDQ5MzY3NTIxMTk1Nzk1NjAyOTcxNjI1MzYzODY0NDkyOTk5NDk2NTI3MTkwNjA2MzgzNTkzMjg5NjA3MDk3MTg1MjEwNzkyOTk5OTgyNjY3Mjc5MDk1Njc4ODI2NTE1OTA5NTA2MDQyNTA4MDgxNDU2NDkzNDU2OTk3MzU0MDQwNTA0NDM2NTMwMTI2OTQwNDM0MzkxNjEyMTMyNTcyMjEwNDY5OTExMDY4NjAyMzQ2MTExNjAyOTUwNzQyNzk2NzUyNDQ2MDQ0MTk3OTM4OTQ2ODg1ODQ1MTE5NzMzNjU0NzQzOTc1OTY4NTIwNzYyNDgyODI3MzY1NDM0MDMxNDc5NjEzOTE3MTQ5ODgzNjQwMzQ3ODU1NTEyODY0ODc5NTU2NjY5ODcxNjI1NzAyMzczNTU1MTkxMTE5NjM2NDE0MDg4NDc2NjUyODc4NTYzNzAwNzE2NTAxNzk5NDA5NjA3NDI3Njg5OTI5MTcxMjY2MTQ3MDQ3NzU2MjA0Njg5NDM5NTE5MDkyNjgwNDk0ODg2MTMxNDc0NjY1MzMxNzE4MDY0MjQ3NzQ0MTE5MTQ2Mjg4MDY0NjIwOTMzMjQ4OTg3MjAzNzc4MjQxNTEzNTQ2MTc1NDQ1NjM4ODU4MjU0MTk0NTQ4MjY3NjIwOTYwNTEwMjc0Nzc2NjU4Mzc3MDM4OTcxNzkzMDUxOTAxNTk1NDkyNDQ1OTQzNjk3NTE5NTExMjAzOTkzMzgxOTAxNjI3NzY2Mjk1MDc1NDQ0NTM3NDIyMTc5ODgxMDI3NjcwMTE4MzM2NzgzODA5MjcxODAxNTMwNjk4MzE5ODIxNjg2Mzc1ODc5NTY1NzgwMDcxMjE0MzMwOTA5ODk5MTU1MTExNjQ3Mjc5NTg5NDc0ODIyMzkzMDQ3OTU1NzMyMTY3NzI4ODI1NTMyNDQ1ODg4MDk2MTY1NzIxMzExNDcxNTIyODUxLCJGaW5nZXJwcmludCI6MTY4MDE1NDE1MTEyMzMwOTgzNjN9"},"E2e":{"Prime":"E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873847AEF49F66E43873","Small_prime":"","Generator":"2"},"Cmix":{"Prime":"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF","Small_prime":"","Generator":"2"},"AddressSpace":[{"Size":32,"Timestamp":"2022-09-02T10:27:19.6392244-07:00"}],"ClientVersion":"","WhitelistedIds":null,"WhitelistedIpAddresses":null,"RateLimits":{"Capacity":0,"LeakedTokens":0,"LeakDuration":0}}
\ No newline at end of file
diff --git a/examples/sendE2E/index.html b/examples/sendE2E/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..27067671ba8e358138343310445fb490bd21dcab
--- /dev/null
+++ b/examples/sendE2E/index.html
@@ -0,0 +1,70 @@
+<!--
+  ~ Copyright © 2020 xx network SEZC                                           ///
+  ~                                                                            ///
+  ~ Use of this source code is governed by a license that can be found in the  ///
+  ~ LICENSE file                                                               ///
+  -->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="UTF-8">
+	<title>Receiver</title>
+
+	<link rel="stylesheet" type="text/css" href="../styles.css">
+	<link rel="shortcut icon" type="image/x-icon" href="receiver-favicon.ico" />
+
+	<script type="text/javascript" src="xxdk.js"></script>
+	<script type="text/javascript" src="../wasm_exec.js"></script>
+	<script>
+        // Use the encoder to convert a string to Uint8Array using enc.encode()
+
+        const go = new Go();
+        WebAssembly.instantiateStreaming(fetch("../xxdk.wasm"), go.importObject).then((result) => {
+            go.run(result.instance);
+
+            LogLevel(0);
+
+            const recipientContactFile = '';
+            const myContactFileName = 'myE2eContact.xxc';
+            const statePath = 'statePathRecipient';
+            const statePass = 'password';
+
+            document.getElementById('ndf-input')
+                .addEventListener('change', function (e) {
+                    let reader = new FileReader();
+                    reader.onload = function (e) {
+                        SendE2e(e.target.result, recipientContactFile, myContactFileName, statePath, statePass);
+                    };
+                    reader.readAsText(e.target.files[0]);
+                }, false);
+        });
+	</script>
+</head>
+<body>
+<h1>Receiver</h1>
+<form id="start-cmix">
+	<div>
+		<label for="ndf-input">Select NDF file to use <code>ndf</code> variable in JS (required):</label><br/>
+		<input type="file" id="ndf-input" required/>
+	</div>
+	<!--	<div>
+			<label for="recipient-contact-input">Recipient contact path (optional):</label><br/>
+			<input type="file" id="recipient-contact-input"/>
+		</div>
+		<div>
+			<label for="session-path">Session path (required):</label><br/>
+			<input type="text" id="session-path" required/>
+		</div>
+		<div>
+			<label for="session-password">Storage password (required):</label><br/>
+			<input type="text" id="session-password" required/>
+		</div>
+		<input type="submit" value="Start">-->
+</form>
+<div id="output" style="padding:10px;border:1px solid red"></div>
+</body>
+
+<script>
+</script>
+</html>
\ No newline at end of file
diff --git a/examples/sendE2E/index2.html b/examples/sendE2E/index2.html
new file mode 100644
index 0000000000000000000000000000000000000000..6665b33c9a44dab1e5974de6d742e075d92c2270
--- /dev/null
+++ b/examples/sendE2E/index2.html
@@ -0,0 +1,61 @@
+<!--
+  ~ Copyright © 2020 xx network SEZC                                           ///
+  ~                                                                            ///
+  ~ Use of this source code is governed by a license that can be found in the  ///
+  ~ LICENSE file                                                               ///
+  -->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="UTF-8">
+	<title>Sender</title>
+
+	<link rel="stylesheet" type="text/css" href="../styles.css">
+	<link rel="shortcut icon" type="image/x-icon" href="sender-favicon.ico" />
+	<script type="text/javascript" src="xxdk.js"></script>
+	<script type="text/javascript" src="../wasm_exec.js"></script>
+	<script>
+        // Use the encoder to convert a string to Uint8Array using enc.encode()
+
+        const go = new Go();
+        WebAssembly.instantiateStreaming(fetch("../xxdk.wasm"), go.importObject).then((result) => {
+            go.run(result.instance);
+
+            LogLevel(0);
+
+            const recipientContactFile = '<xxc(2)Q2uq69ry88AeNFKcnpOkCjuCtkYIdv/fgC3F4z6eNUMDrgZ7Ugdw/BAr6bG0KyMpEOL6fulH331JBFOD69e7iZLxkBr3BvZyfyTXL9QtQwpzaDnmPXjyRR5cjAQ6gfk5XQzsrw45Q8qsrKewwZsqGhac0NKJYPiXGa9DTQR6f1VMOY7cco4uquNN5BRo4ucz1gu9w2Ff3N+1Hdz9V1r7xulkkd/78IyoJqHQDgp1eO3q6NZJT55DNsSuS2ZuYQc3yQUyTHH/G7gIF7EwRTU6dhRTGMYpfOQhRT67EVidHOT4e7GSpkWoGzi5XUBA+9N1llRCAwzRJ65lQqJZIC/vNgxJhHR5Cd4AOfHc+KuGlXMpCQkeYjd7a+ggh4RUuPsMW59QlKaBbZ1mbGHPzphFUrJIS63TMcJRrWbGwXHJrvZnD1NYNOM/x/qSR4pTkv8soIHFzgvFbRME5VTfxWfWKSCG/qu3TaWfetnYXmsqhdmnURekF4iSMtyDpVch+86zK5YmuO9Ap18MvdwmnfX5r9xhyPRga/QqmkdEI6ZebuXVYJQGr6Lzsz4z+4AGI0v87j//APsGxvi8fwAAAgA7w6N/pgZWFlB+p/Muirk+VQ==xxc>';
+            const myContactFileName = 'theirE2eContact.xxc';
+            const statePath = 'statePathSender';
+            const statePass = 'password';
+
+            document.getElementById('ndf-input')
+                .addEventListener('change', function (e) {
+                    let reader = new FileReader();
+                    reader.onload = function (e) {
+                        SendE2e(e.target.result, recipientContactFile, myContactFileName, statePath, statePass);
+                    };
+                    reader.readAsText(e.target.files[0]);
+                }, false);
+        });
+	</script>
+</head>
+<body>
+<h1>Sender</h1>
+<form id="start-cmix">
+	<div>
+		<label for="ndf-input">Select NDF file to use <code>ndf</code> variable in JS:</label><br/>
+		<input type="file" id="ndf-input" required/>
+	</div>
+<!--	<div>
+		<label for="ndf-input">Recipient contact path:</label><br/>
+		<input type="file" id="recipient-contact-input" required/>
+	</div>
+	<input type="submit" value="Start">-->
+</form>
+<div id="output" style="padding:10px;border:1px solid red"></div>
+</body>
+
+<script>
+</script>
+</html>
\ No newline at end of file
diff --git a/examples/sendE2E/receiver-favicon.ico b/examples/sendE2E/receiver-favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..3a7b5826dfdad175084ea63406202334dfe29bf4
Binary files /dev/null and b/examples/sendE2E/receiver-favicon.ico differ
diff --git a/examples/sendE2E/sender-favicon.ico b/examples/sendE2E/sender-favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..149d6cb2fdb5e20b4a4f80a5797878f73e14d25e
Binary files /dev/null and b/examples/sendE2E/sender-favicon.ico differ
diff --git a/examples/sendE2E/xxdk.js b/examples/sendE2E/xxdk.js
new file mode 100644
index 0000000000000000000000000000000000000000..533eead2b67038e3fbc4d16c13cdca758a3da86f
--- /dev/null
+++ b/examples/sendE2E/xxdk.js
@@ -0,0 +1,248 @@
+/*
+ * Copyright © 2020 xx network SEZC                                           ///
+ *                                                                            ///
+ * Use of this source code is governed by a license that can be found in the  ///
+ * LICENSE file                                                               ///
+ */
+
+async function SendE2e(ndf, recipientContactFile, myContactFileName, statePath, statePassString) {
+    let enc = new TextEncoder();
+    let dec = new TextDecoder();
+
+    const output = document.getElementById("output")
+    const statePass = enc.encode(statePassString);
+
+    // Check if state exists
+    if (localStorage.getItem(statePath) === null) {
+        console.log('getting key ' + statePath + ' returned null; making new cmix');
+
+        output.innerHTML += "Loading new storage\n"
+
+        // Initialize the state
+        NewCmix(ndf, statePath, statePass, '');
+    }
+
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Login to your client session                                           //
+    ////////////////////////////////////////////////////////////////////////////
+
+    // Login with the same statePath and statePass used to call NewCmix
+    let netID = LoadCmix(statePath, statePass, GetDefaultCMixParams());
+    console.log("LoadCmix() " + netID)
+
+    console.log("sleep start")
+    await sleep(3000)
+    console.log("sleep end")
+
+    let net = GetLoadCmix(netID)
+    console.log("loaded cmix: " + net)
+
+    // Get reception identity (automatically created if one does not exist)
+    const identityStorageKey = "identityStorageKey";
+    let identity;
+    try {
+        identity = LoadReceptionIdentity(identityStorageKey, net.GetID());
+    } catch {
+        // If no extant xxdk.ReceptionIdentity, generate and store a new one
+        identity = net.MakeReceptionIdentity();
+
+        StoreReceptionIdentity(identityStorageKey, identity, net.GetID());
+    }
+
+    // Print contact to console. This should probably save a file.
+    const myContactFile = dec.decode(GetContactFromReceptionIdentity(identity))
+    console.log("my contact file content: " + myContactFile);
+
+    // Start file download.
+    download(myContactFileName, myContactFile);
+
+    let confirm = false;
+    let confirmContact;
+    let e2eClient;
+    let authCallbacks = {
+        Confirm: function (contact, receptionId, ephemeralId, roundId) {
+            confirm = true;
+            confirmContact = contact
+            console.log("Confirm:");
+            console.log("contact: " + dec.decode(contact));
+            console.log("receptionId: " + ephemeralId.toString());
+            console.log("ephemeralId: " + roundId.toString());
+
+            output.innerHTML += "Received confirmation from " + ephemeralId.toString() + "<br />"
+        },
+        Request: function (contact, receptionId, ephemeralId, roundId) {
+            console.log("Request:");
+            console.log("contact: " + dec.decode(contact));
+            console.log("receptionId: " + ephemeralId.toString());
+            console.log("ephemeralId: " + roundId.toString());
+
+            e2eClient.Confirm(contact)
+            output.innerHTML += "Received Request from " + ephemeralId.toString() + "<br />"
+        }
+    }
+
+    // Create an E2E client
+    // Pass in auth object which controls auth callbacks for this client
+    const params = GetDefaultE2EParams();
+    console.log("Using E2E parameters: " + dec.decode(params));
+    e2eClient = Login(net.GetID(), authCallbacks, identity, params);
+
+    e2eClient.DeleteAllRequests()
+
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Start network threads                                                  //
+    ////////////////////////////////////////////////////////////////////////////
+
+    // Set networkFollowerTimeout to a value of your choice (seconds)
+    net.StartNetworkFollower(5000);
+
+    output.innerHTML += "Starting network follower<br />"
+
+    // Provide a callback that will be signalled when network health status changes
+    let health = false
+    net.AddHealthCallback({
+        Callback: function (healthy) {
+            health = healthy;
+        }
+    });
+    await sleep(3000)
+
+
+    const n = 100
+    for (let i = 0; (health === false) && (i < n); i++) {
+        await sleep(100)
+    }
+
+    if (health === false) {
+        console.error("Continuing with unhealthy network")
+        output.innerHTML += "Network NOT healthy<br />"
+    } else {
+        output.innerHTML += "Network healthy<br />"
+    }
+
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Register a listener for messages                                       //
+    ////////////////////////////////////////////////////////////////////////////
+
+    let listener = {
+        Hear: function (item) {
+            console.log("Listener heard: " + dec.decode(item));
+            output.innerHTML += "Listener heard: " + dec.decode(item) + "<br />"
+        },
+        Name: function () {
+            return "Listener";
+        }
+    }
+
+    // Listen for all types of messages using catalog.NoType
+    // Listen for messages from all users using id.ZeroUser
+    let zerUser = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]);
+    e2eClient.RegisterListener(zerUser, 0, listener);
+
+    output.innerHTML += "Registered listener<br />"
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Connect with the recipient                                             //
+    ////////////////////////////////////////////////////////////////////////////
+
+    // Check that the partner exists
+    if (recipientContactFile !== '') {
+        let exists = false;
+        output.innerHTML += "getting ID from contact<br />"
+        const recipientContactID = GetIDFromContact(recipientContactFile);
+        console.log(typeof recipientContactID)
+        console.log("recipientContactID: " + recipientContactID)
+
+        output.innerHTML += "Checking for " + recipientContactID + "<br />"
+
+
+        const partnerIDS = dec.decode(e2eClient.GetAllPartnerIDs())
+        console.log("partnerIDS: " + partnerIDS)
+        let partners = JSON.parse(partnerIDS);
+
+        for (let i = 0; i < partners.length; i++) {
+            const partnerBytes = base64ToArrayBuffer(partners[i])
+            console.log("partner " + recipientContactID + " == " + i + " " + partnerBytes)
+
+            if (partnerBytes.toString() === recipientContactID.toString()) {
+                console.log("MATCH! partner " + recipientContactID + " matches partner " + i + " " + partnerBytes)
+                exists = true;
+                break
+            }
+        }
+
+        // If the partner does not exist, send a request
+        if (exists === false) {
+            output.innerHTML += "Partner does not exist, Request sent to " + recipientContactID + "<br />"
+            const factList = enc.encode('[]')
+            e2eClient.Request(enc.encode(recipientContactFile), factList)
+
+            for (let i = 0; (i < 600) && (confirm === false); i++) {
+                await sleep(50)
+            }
+            if (confirm === false) {
+                output.innerHTML += "Checking for " + recipientContactIDBase64 + "<br />"
+                console.error(new Error("timed out waiting for confirmation"))
+            }
+
+            const confirmContactID = GetIDFromContact(confirmContact)
+            if (recipientContactID.toString() !== confirmContactID.toString()) {
+                throw new Error("contact ID from confirmation " +
+                    btoa(dec.decode(confirmContactID)) +
+                    " does not match recipient ID " +
+                    recipientContactIDBase64)
+            }
+        } else {
+            output.innerHTML += "Partner exists<br />"
+        }
+
+        ////////////////////////////////////////////////////////////////////////////
+        // Send a message to the recipient                                        //
+        ////////////////////////////////////////////////////////////////////////////
+
+        // Test message
+        const msgBody = "If this message is sent successfully, we'll have established contact with the recipient."
+
+        output.innerHTML += "Sending E2E message<br />"
+        // const paramsObj = JSON.parse(dec.decode(params))
+        // const e2eParams = JSON.stringify(paramsObj.Base)
+        // console.log("e2eParams: " + e2eParams)
+        const e2eSendReport = e2eClient.SendE2E(2, recipientContactID, enc.encode(msgBody), params)
+
+        console.log("e2e send report: " + dec.decode(e2eSendReport))
+        output.innerHTML += "Send e2e: " + dec.decode(e2eSendReport) + "<br />"
+    } else {
+        output.innerHTML += "Partner does not exist<br />"
+    }
+}
+
+function sleep(ms) {
+    return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+function download(filename, text) {
+    let element = document.createElement('a');
+    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
+    element.setAttribute('download', filename);
+
+    element.style.display = 'none';
+    document.body.appendChild(element);
+
+    element.click();
+
+    document.body.removeChild(element);
+}
+
+function base64ToArrayBuffer(base64) {
+    const binary_string = window.atob(base64);
+    const len = binary_string.length;
+    const bytes = new Uint8Array(len);
+    for (let i = 0; i < len; i++) {
+        bytes[i] = binary_string.charCodeAt(i);
+    }
+    return bytes;
+}
\ No newline at end of file
diff --git a/examples/styles.css b/examples/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..802a6a38cc0fcaab7f49dc0312a05fe63f8c11c5
--- /dev/null
+++ b/examples/styles.css
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2020 xx network SEZC                                           ///
+ *                                                                            ///
+ * Use of this source code is governed by a license that can be found in the  ///
+ * LICENSE file                                                               ///
+ */
+
+/******************************************************************************
+ * Copyright © 2022 xx foundation                                             *
+ *                                                                            *
+ * Use of this source code is governed by a license that can be found in the  *
+ * LICENSE file.                                                              *
+ ******************************************************************************/
+
+/******************************************************************************
+ * CSS Reset (meyerweb reset, v2.0 | 20110126)                                *
+ ******************************************************************************/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+    margin: 0;
+    padding: 0;
+    border: 0;
+    font-size: 100%;
+    font: inherit;
+    vertical-align: baseline;
+}
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+    display: block;
+}
+
+body {
+    line-height: 1;
+}
+
+ol, ul {
+    list-style: none;
+}
+
+blockquote, q {
+    quotes: none;
+}
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+    content: '';
+    content: none;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+
+/******************************************************************************
+ * Global Styles                                                              *
+ ******************************************************************************/
+body {
+    font-family: "Roboto", "Franklin Gothic Medium", Tahoma, sans-serif;
+}
+
+form {
+    margin:1em;
+}
+
+form div {
+    margin:1em 0;
+}
\ No newline at end of file
diff --git a/examples/wasm_exec.js b/examples/wasm_exec.js
new file mode 100644
index 0000000000000000000000000000000000000000..bba4a84051b5fb6bf284df03d21556eb56500062
--- /dev/null
+++ b/examples/wasm_exec.js
@@ -0,0 +1,650 @@
+/*
+ * Copyright © 2020 xx network SEZC                                           ///
+ *                                                                            ///
+ * Use of this source code is governed by a license that can be found in the  ///
+ * LICENSE file                                                               ///
+ */
+
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+(() => {
+	// Map multiple JavaScript environments to a single common API,
+	// preferring web standards over Node.js API.
+	//
+	// Environments considered:
+	// - Browsers
+	// - Node.js
+	// - Electron
+	// - Parcel
+	// - Webpack
+
+	if (typeof global !== "undefined") {
+		// global already exists
+	} else if (typeof window !== "undefined") {
+		window.global = window;
+	} else if (typeof self !== "undefined") {
+		self.global = self;
+	} else {
+		throw new Error("cannot export Go (neither global, window nor self is defined)");
+	}
+
+	if (!global.require && typeof require !== "undefined") {
+		global.require = require;
+	}
+
+	if (!global.fs && global.require) {
+		const fs = require("fs");
+		if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
+			global.fs = fs;
+		}
+	}
+
+	const enosys = () => {
+		const err = new Error("not implemented");
+		err.code = "ENOSYS";
+		return err;
+	};
+
+	if (!global.fs) {
+		let outputBuf = "";
+		global.fs = {
+			constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
+			writeSync(fd, buf) {
+				outputBuf += decoder.decode(buf);
+				const nl = outputBuf.lastIndexOf("\n");
+				if (nl != -1) {
+					console.log(outputBuf.substr(0, nl));
+					outputBuf = outputBuf.substr(nl + 1);
+				}
+				return buf.length;
+			},
+			write(fd, buf, offset, length, position, callback) {
+				if (offset !== 0 || length !== buf.length || position !== null) {
+					callback(enosys());
+					return;
+				}
+				const n = this.writeSync(fd, buf);
+				callback(null, n);
+			},
+			chmod(path, mode, callback) { callback(enosys()); },
+			chown(path, uid, gid, callback) { callback(enosys()); },
+			close(fd, callback) { callback(enosys()); },
+			fchmod(fd, mode, callback) { callback(enosys()); },
+			fchown(fd, uid, gid, callback) { callback(enosys()); },
+			fstat(fd, callback) { callback(enosys()); },
+			fsync(fd, callback) { callback(null); },
+			ftruncate(fd, length, callback) { callback(enosys()); },
+			lchown(path, uid, gid, callback) { callback(enosys()); },
+			link(path, link, callback) { callback(enosys()); },
+			lstat(path, callback) { callback(enosys()); },
+			mkdir(path, perm, callback) { callback(enosys()); },
+			open(path, flags, mode, callback) { callback(enosys()); },
+			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
+			readdir(path, callback) { callback(enosys()); },
+			readlink(path, callback) { callback(enosys()); },
+			rename(from, to, callback) { callback(enosys()); },
+			rmdir(path, callback) { callback(enosys()); },
+			stat(path, callback) { callback(enosys()); },
+			symlink(path, link, callback) { callback(enosys()); },
+			truncate(path, length, callback) { callback(enosys()); },
+			unlink(path, callback) { callback(enosys()); },
+			utimes(path, atime, mtime, callback) { callback(enosys()); },
+		};
+	}
+
+	if (!global.process) {
+		global.process = {
+			getuid() { return -1; },
+			getgid() { return -1; },
+			geteuid() { return -1; },
+			getegid() { return -1; },
+			getgroups() { throw enosys(); },
+			pid: -1,
+			ppid: -1,
+			umask() { throw enosys(); },
+			cwd() { throw enosys(); },
+			chdir() { throw enosys(); },
+		}
+	}
+
+	if (!global.crypto && global.require) {
+		const nodeCrypto = require("crypto");
+		global.crypto = {
+			getRandomValues(b) {
+				nodeCrypto.randomFillSync(b);
+			},
+		};
+	}
+	if (!global.crypto) {
+		throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
+	}
+
+	if (!global.performance) {
+		global.performance = {
+			now() {
+				const [sec, nsec] = process.hrtime();
+				return sec * 1000 + nsec / 1000000;
+			},
+		};
+	}
+
+	if (!global.TextEncoder && global.require) {
+		global.TextEncoder = require("util").TextEncoder;
+	}
+	if (!global.TextEncoder) {
+		throw new Error("global.TextEncoder is not available, polyfill required");
+	}
+
+	if (!global.TextDecoder && global.require) {
+		global.TextDecoder = require("util").TextDecoder;
+	}
+	if (!global.TextDecoder) {
+		throw new Error("global.TextDecoder is not available, polyfill required");
+	}
+
+	// End of polyfills for common API.
+
+	const encoder = new TextEncoder("utf-8");
+	const decoder = new TextDecoder("utf-8");
+
+	global.Go = class {
+		constructor() {
+			this.argv = ["js"];
+			this.env = {};
+			this.exit = (code) => {
+				if (code !== 0) {
+					console.warn("exit code:", code);
+				}
+			};
+			this._exitPromise = new Promise((resolve) => {
+				this._resolveExitPromise = resolve;
+			});
+			this._pendingEvent = null;
+			this._scheduledTimeouts = new Map();
+			this._nextCallbackTimeoutID = 1;
+
+			const setInt64 = (addr, v) => {
+				this.mem.setUint32(addr + 0, v, true);
+				this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
+			}
+
+			const getInt64 = (addr) => {
+				const low = this.mem.getUint32(addr + 0, true);
+				const high = this.mem.getInt32(addr + 4, true);
+				return low + high * 4294967296;
+			}
+
+			const loadValue = (addr) => {
+				const f = this.mem.getFloat64(addr, true);
+				if (f === 0) {
+					return undefined;
+				}
+				if (!isNaN(f)) {
+					return f;
+				}
+
+				const id = this.mem.getUint32(addr, true);
+				return this._values[id];
+			}
+
+			const storeValue = (addr, v) => {
+				const nanHead = 0x7FF80000;
+
+				if (typeof v === "number" && v !== 0) {
+					if (isNaN(v)) {
+						this.mem.setUint32(addr + 4, nanHead, true);
+						this.mem.setUint32(addr, 0, true);
+						return;
+					}
+					this.mem.setFloat64(addr, v, true);
+					return;
+				}
+
+				if (v === undefined) {
+					this.mem.setFloat64(addr, 0, true);
+					return;
+				}
+
+				let id = this._ids.get(v);
+				if (id === undefined) {
+					id = this._idPool.pop();
+					if (id === undefined) {
+						id = this._values.length;
+					}
+					this._values[id] = v;
+					this._goRefCounts[id] = 0;
+					this._ids.set(v, id);
+				}
+				this._goRefCounts[id]++;
+				let typeFlag = 0;
+				switch (typeof v) {
+					case "object":
+						if (v !== null) {
+							typeFlag = 1;
+						}
+						break;
+					case "string":
+						typeFlag = 2;
+						break;
+					case "symbol":
+						typeFlag = 3;
+						break;
+					case "function":
+						typeFlag = 4;
+						break;
+				}
+				this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
+				this.mem.setUint32(addr, id, true);
+			}
+
+			const loadSlice = (addr) => {
+				const array = getInt64(addr + 0);
+				const len = getInt64(addr + 8);
+				return new Uint8Array(this._inst.exports.mem.buffer, array, len);
+			}
+
+			const loadSliceOfValues = (addr) => {
+				const array = getInt64(addr + 0);
+				const len = getInt64(addr + 8);
+				const a = new Array(len);
+				for (let i = 0; i < len; i++) {
+					a[i] = loadValue(array + i * 8);
+				}
+				return a;
+			}
+
+			const loadString = (addr) => {
+				const saddr = getInt64(addr + 0);
+				const len = getInt64(addr + 8);
+				return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
+			}
+
+			const timeOrigin = Date.now() - performance.now();
+			this.importObject = {
+				go: {
+					// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
+					// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
+					// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
+					// This changes the SP, thus we have to update the SP used by the imported function.
+
+					// func wasmExit(code int32)
+					"runtime.wasmExit": (sp) => {
+						sp >>>= 0;
+						const code = this.mem.getInt32(sp + 8, true);
+						this.exited = true;
+						delete this._inst;
+						delete this._values;
+						delete this._goRefCounts;
+						delete this._ids;
+						delete this._idPool;
+						this.exit(code);
+					},
+
+					// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
+					"runtime.wasmWrite": (sp) => {
+						sp >>>= 0;
+						const fd = getInt64(sp + 8);
+						const p = getInt64(sp + 16);
+						const n = this.mem.getInt32(sp + 24, true);
+						fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
+					},
+
+					// func resetMemoryDataView()
+					"runtime.resetMemoryDataView": (sp) => {
+						sp >>>= 0;
+						this.mem = new DataView(this._inst.exports.mem.buffer);
+					},
+
+					// func nanotime1() int64
+					"runtime.nanotime1": (sp) => {
+						sp >>>= 0;
+						setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
+					},
+
+					// func walltime() (sec int64, nsec int32)
+					"runtime.walltime": (sp) => {
+						sp >>>= 0;
+						const msec = (new Date).getTime();
+						setInt64(sp + 8, msec / 1000);
+						this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
+					},
+
+					// func scheduleTimeoutEvent(delay int64) int32
+					"runtime.scheduleTimeoutEvent": (sp) => {
+						sp >>>= 0;
+						const id = this._nextCallbackTimeoutID;
+						this._nextCallbackTimeoutID++;
+						this._scheduledTimeouts.set(id, setTimeout(
+							() => {
+								this._resume();
+								while (this._scheduledTimeouts.has(id)) {
+									// for some reason Go failed to register the timeout event, log and try again
+									// (temporary workaround for https://github.com/golang/go/issues/28975)
+									console.warn("scheduleTimeoutEvent: missed timeout event");
+									this._resume();
+								}
+							},
+							getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
+						));
+						this.mem.setInt32(sp + 16, id, true);
+					},
+
+					// func clearTimeoutEvent(id int32)
+					"runtime.clearTimeoutEvent": (sp) => {
+						sp >>>= 0;
+						const id = this.mem.getInt32(sp + 8, true);
+						clearTimeout(this._scheduledTimeouts.get(id));
+						this._scheduledTimeouts.delete(id);
+					},
+
+					// func getRandomData(r []byte)
+					"runtime.getRandomData": (sp) => {
+						sp >>>= 0;
+						crypto.getRandomValues(loadSlice(sp + 8));
+					},
+
+					// func finalizeRef(v ref)
+					"syscall/js.finalizeRef": (sp) => {
+						sp >>>= 0;
+						const id = this.mem.getUint32(sp + 8, true);
+						this._goRefCounts[id]--;
+						if (this._goRefCounts[id] === 0) {
+							const v = this._values[id];
+							this._values[id] = null;
+							this._ids.delete(v);
+							this._idPool.push(id);
+						}
+					},
+
+					// func stringVal(value string) ref
+					"syscall/js.stringVal": (sp) => {
+						sp >>>= 0;
+						storeValue(sp + 24, loadString(sp + 8));
+					},
+
+					// func valueGet(v ref, p string) ref
+					"syscall/js.valueGet": (sp) => {
+						sp >>>= 0;
+						const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
+						sp = this._inst.exports.getsp() >>> 0; // see comment above
+						storeValue(sp + 32, result);
+					},
+
+					// func valueSet(v ref, p string, x ref)
+					"syscall/js.valueSet": (sp) => {
+						sp >>>= 0;
+						Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
+					},
+
+					// func valueDelete(v ref, p string)
+					"syscall/js.valueDelete": (sp) => {
+						sp >>>= 0;
+						Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
+					},
+
+					// func valueIndex(v ref, i int) ref
+					"syscall/js.valueIndex": (sp) => {
+						sp >>>= 0;
+						storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
+					},
+
+					// valueSetIndex(v ref, i int, x ref)
+					"syscall/js.valueSetIndex": (sp) => {
+						sp >>>= 0;
+						Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
+					},
+
+					// func valueCall(v ref, m string, args []ref) (ref, bool)
+					"syscall/js.valueCall": (sp) => {
+						sp >>>= 0;
+						try {
+							const v = loadValue(sp + 8);
+							const m = Reflect.get(v, loadString(sp + 16));
+							const args = loadSliceOfValues(sp + 32);
+							const result = Reflect.apply(m, v, args);
+							sp = this._inst.exports.getsp() >>> 0; // see comment above
+							storeValue(sp + 56, result);
+							this.mem.setUint8(sp + 64, 1);
+						} catch (err) {
+							sp = this._inst.exports.getsp() >>> 0; // see comment above
+							storeValue(sp + 56, err);
+							this.mem.setUint8(sp + 64, 0);
+						}
+					},
+
+					// func valueInvoke(v ref, args []ref) (ref, bool)
+					"syscall/js.valueInvoke": (sp) => {
+						sp >>>= 0;
+						try {
+							const v = loadValue(sp + 8);
+							const args = loadSliceOfValues(sp + 16);
+							const result = Reflect.apply(v, undefined, args);
+							sp = this._inst.exports.getsp() >>> 0; // see comment above
+							storeValue(sp + 40, result);
+							this.mem.setUint8(sp + 48, 1);
+						} catch (err) {
+							sp = this._inst.exports.getsp() >>> 0; // see comment above
+							storeValue(sp + 40, err);
+							this.mem.setUint8(sp + 48, 0);
+						}
+					},
+
+					// func valueNew(v ref, args []ref) (ref, bool)
+					"syscall/js.valueNew": (sp) => {
+						sp >>>= 0;
+						try {
+							const v = loadValue(sp + 8);
+							const args = loadSliceOfValues(sp + 16);
+							const result = Reflect.construct(v, args);
+							sp = this._inst.exports.getsp() >>> 0; // see comment above
+							storeValue(sp + 40, result);
+							this.mem.setUint8(sp + 48, 1);
+						} catch (err) {
+							sp = this._inst.exports.getsp() >>> 0; // see comment above
+							storeValue(sp + 40, err);
+							this.mem.setUint8(sp + 48, 0);
+						}
+					},
+
+					// func valueLength(v ref) int
+					"syscall/js.valueLength": (sp) => {
+						sp >>>= 0;
+						setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
+					},
+
+					// valuePrepareString(v ref) (ref, int)
+					"syscall/js.valuePrepareString": (sp) => {
+						sp >>>= 0;
+						const str = encoder.encode(String(loadValue(sp + 8)));
+						storeValue(sp + 16, str);
+						setInt64(sp + 24, str.length);
+					},
+
+					// valueLoadString(v ref, b []byte)
+					"syscall/js.valueLoadString": (sp) => {
+						sp >>>= 0;
+						const str = loadValue(sp + 8);
+						loadSlice(sp + 16).set(str);
+					},
+
+					// func valueInstanceOf(v ref, t ref) bool
+					"syscall/js.valueInstanceOf": (sp) => {
+						sp >>>= 0;
+						this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
+					},
+
+					// func copyBytesToGo(dst []byte, src ref) (int, bool)
+					"syscall/js.copyBytesToGo": (sp) => {
+						sp >>>= 0;
+						const dst = loadSlice(sp + 8);
+						const src = loadValue(sp + 32);
+						if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
+							this.mem.setUint8(sp + 48, 0);
+							return;
+						}
+						const toCopy = src.subarray(0, dst.length);
+						dst.set(toCopy);
+						setInt64(sp + 40, toCopy.length);
+						this.mem.setUint8(sp + 48, 1);
+					},
+
+					// func copyBytesToJS(dst ref, src []byte) (int, bool)
+					"syscall/js.copyBytesToJS": (sp) => {
+						sp >>>= 0;
+						const dst = loadValue(sp + 8);
+						const src = loadSlice(sp + 16);
+						if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
+							this.mem.setUint8(sp + 48, 0);
+							return;
+						}
+						const toCopy = src.subarray(0, dst.length);
+						dst.set(toCopy);
+						setInt64(sp + 40, toCopy.length);
+						this.mem.setUint8(sp + 48, 1);
+					},
+
+					"debug": (value) => {
+						console.log(value);
+					},
+
+					// func throw(exception string, message string)
+					'gitlab.com/elixxir/xxdk-wasm/utils.throw': (sp) => {
+						const exception = loadString(sp + 8)
+						const message = loadString(sp + 24)
+						throw globalThis[exception](message)
+					},
+				}
+			};
+		}
+
+		async run(instance) {
+			if (!(instance instanceof WebAssembly.Instance)) {
+				throw new Error("Go.run: WebAssembly.Instance expected");
+			}
+			this._inst = instance;
+			this.mem = new DataView(this._inst.exports.mem.buffer);
+			this._values = [ // JS values that Go currently has references to, indexed by reference id
+				NaN,
+				0,
+				null,
+				true,
+				false,
+				global,
+				this,
+			];
+			this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
+			this._ids = new Map([ // mapping from JS values to reference ids
+				[0, 1],
+				[null, 2],
+				[true, 3],
+				[false, 4],
+				[global, 5],
+				[this, 6],
+			]);
+			this._idPool = [];   // unused ids that have been garbage collected
+			this.exited = false; // whether the Go program has exited
+
+			// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
+			let offset = 4096;
+
+			const strPtr = (str) => {
+				const ptr = offset;
+				const bytes = encoder.encode(str + "\0");
+				new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
+				offset += bytes.length;
+				if (offset % 8 !== 0) {
+					offset += 8 - (offset % 8);
+				}
+				return ptr;
+			};
+
+			const argc = this.argv.length;
+
+			const argvPtrs = [];
+			this.argv.forEach((arg) => {
+				argvPtrs.push(strPtr(arg));
+			});
+			argvPtrs.push(0);
+
+			const keys = Object.keys(this.env).sort();
+			keys.forEach((key) => {
+				argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
+			});
+			argvPtrs.push(0);
+
+			const argv = offset;
+			argvPtrs.forEach((ptr) => {
+				this.mem.setUint32(offset, ptr, true);
+				this.mem.setUint32(offset + 4, 0, true);
+				offset += 8;
+			});
+
+			// The linker guarantees global data starts from at least wasmMinDataAddr.
+			// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
+			const wasmMinDataAddr = 4096 + 8192;
+			if (offset >= wasmMinDataAddr) {
+				throw new Error("total length of command line and environment variables exceeds limit");
+			}
+
+			this._inst.exports.run(argc, argv);
+			if (this.exited) {
+				this._resolveExitPromise();
+			}
+			await this._exitPromise;
+		}
+
+		_resume() {
+			if (this.exited) {
+				throw new Error("Go program has already exited");
+			}
+			this._inst.exports.resume();
+			if (this.exited) {
+				this._resolveExitPromise();
+			}
+		}
+
+		_makeFuncWrapper(id) {
+			const go = this;
+			return function () {
+				const event = { id: id, this: this, args: arguments };
+				go._pendingEvent = event;
+				go._resume();
+				return event.result;
+			};
+		}
+	}
+
+	if (
+		typeof module !== "undefined" &&
+		global.require &&
+		global.require.main === module &&
+		global.process &&
+		global.process.versions &&
+		!global.process.versions.electron
+	) {
+		if (process.argv.length < 3) {
+			console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
+			process.exit(1);
+		}
+
+		const go = new Go();
+		go.argv = process.argv.slice(2);
+		go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
+		go.exit = process.exit;
+		WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
+			process.on("exit", (code) => { // Node.js exits if no event handler is pending
+				if (code === 0 && !go.exited) {
+					// deadlock, make Go print error and stack traces
+					go._pendingEvent = { id: 0 };
+					go._resume();
+				}
+			});
+			return go.run(result.instance);
+		}).catch((err) => {
+			console.error(err);
+			process.exit(1);
+		});
+	}
+})();
diff --git a/main.go b/main.go
index 3f38c3ed65bfc2d89201e5e4c564dcb5ea617cfb..a3c8678bd5054ffec7fcb10c725b710b5f8121ce 100644
--- a/main.go
+++ b/main.go
@@ -12,6 +12,7 @@ package main
 import (
 	"fmt"
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"gitlab.com/elixxir/xxdk-wasm/wasm"
 	"os"
 	"syscall/js"
@@ -21,6 +22,11 @@ func main() {
 	fmt.Println("Starting xxDK WebAssembly bindings.")
 	fmt.Printf("Client version %s\n", bindings.GetVersion())
 
+	// utils/array.go
+	js.Global().Set("Uint8ArrayToBase64", js.FuncOf(utils.Uint8ArrayToBase64))
+	js.Global().Set("Base64ToUint8Array", js.FuncOf(utils.Base64ToUint8Array))
+	js.Global().Set("Uint8ArrayEquals", js.FuncOf(utils.Uint8ArrayEquals))
+
 	// wasm/backup.go
 	js.Global().Set("NewCmixFromBackup", js.FuncOf(wasm.NewCmixFromBackup))
 	js.Global().Set("InitializeBackup", js.FuncOf(wasm.InitializeBackup))
diff --git a/test/assets/wasm_exec.js b/test/assets/wasm_exec.js
index 528d7465484087fe3bfdc3f1cdbe93cb1be859a9..c613dfc656f2799b6b3c922f59003cd3347454a7 100644
--- a/test/assets/wasm_exec.js
+++ b/test/assets/wasm_exec.js
@@ -503,7 +503,7 @@
 					},
 
 					// func throw(exception string, message string)
-					'gitlab.com/elixxir/xxdk-wasm/wasm.throw': (sp) => {
+					'gitlab.com/elixxir/xxdk-wasm/utils.throw': (sp) => {
 						const exception = loadString(sp + 8)
 						const message = loadString(sp + 24)
 						throw globalThis[exception](message)
diff --git a/utils/array.go b/utils/array.go
new file mode 100644
index 0000000000000000000000000000000000000000..9ec8df39d596afd6e9d0297b2073e4f96364ede9
--- /dev/null
+++ b/utils/array.go
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package utils
+
+import (
+	"bytes"
+	"encoding/base64"
+	"syscall/js"
+)
+
+// Uint8ArrayToBase64 encodes an uint8 array to a base 64 string.
+//
+// Parameters:
+//  - args[0] - uint8 array (Uint8Array)
+//
+// Returns:
+//  - Base 64 encoded string (string).
+func Uint8ArrayToBase64(_ js.Value, args []js.Value) interface{} {
+	return base64.StdEncoding.EncodeToString(CopyBytesToGo(args[0]))
+}
+
+// Base64ToUint8Array decodes a base 64 encoded string to a Uint8Array.
+//
+// Parameters:
+//  - args[0] - base 64 encoded string (string)
+//
+// Returns:
+//  - Decoded uint8 array (Uint8Array).
+//  - Throws TypeError if decoding the string fails.
+func Base64ToUint8Array(_ js.Value, args []js.Value) interface{} {
+	b, err := base64.StdEncoding.DecodeString(args[0].String())
+	if err != nil {
+		Throw(TypeError, err)
+	}
+
+	return CopyBytesToJS(b)
+}
+
+// Uint8ArrayEquals returns true if the two Uint8Array are equal and false
+// otherwise.
+//
+// Parameters:
+//  - args[0] - array A (Uint8Array)
+//  - args[1] - array B (Uint8Array)
+//
+// Returns:
+//  - If the two arrays are equal (boolean).
+func Uint8ArrayEquals(_ js.Value, args []js.Value) interface{} {
+	a := CopyBytesToGo(args[0])
+	b := CopyBytesToGo(args[1])
+
+	return bytes.Equal(a, b)
+}
diff --git a/wasm/utils.go b/utils/errors.go
similarity index 55%
rename from wasm/utils.go
rename to utils/errors.go
index 7959879b487f59a53187cde114bd01eeaa6a708a..b599a4480f0fa57505f4a9182da09ff4cc75670c 100644
--- a/wasm/utils.go
+++ b/utils/errors.go
@@ -1,64 +1,26 @@
 ////////////////////////////////////////////////////////////////////////////////
-// Copyright © 2022 xx foundation                                             //
+// Copyright © 2022 xx network SEZC                                           //
 //                                                                            //
 // Use of this source code is governed by a license that can be found in the  //
-// LICENSE file.                                                              //
+// LICENSE file                                                               //
 ////////////////////////////////////////////////////////////////////////////////
 
-//go:build js && wasm
-
-package wasm
+package utils
 
 import (
-	"encoding/json"
 	"fmt"
-	"github.com/pkg/errors"
-	jww "github.com/spf13/jwalterweatherman"
 	"syscall/js"
 )
 
-// CopyBytesToGo copies the Uint8Array stored in the js.Value to []byte. This is
-// a wrapper for js.CopyBytesToGo to make it more convenient.
-func CopyBytesToGo(src js.Value) []byte {
-	b := make([]byte, src.Length())
-	js.CopyBytesToGo(b, src)
-	return b
-}
-
-// CopyBytesToJS copies the []byte to a Uint8Array stored in a js.Value. This is
-// a wrapper for js.CopyBytesToJS to make it more convenient.
-func CopyBytesToJS(src []byte) js.Value {
-	dst := js.Global().Get("Uint8Array").New(len(src))
-	js.CopyBytesToJS(dst, src)
-	return dst
+// JsError converts the error to a Javascript Error.
+func JsError(err error) js.Value {
+	return Error.New(err.Error())
 }
 
-// WrapCB wraps a Javascript function in an object so that it can be called
-// later with only the arguments and without specifying the function name.
-//
-// Panics if m is not a function.
-func WrapCB(parent js.Value, m string) func(args ...interface{}) js.Value {
-	if parent.Get(m).Type() != js.TypeFunction {
-		// Create the error separate from the print so stack trace is printed
-		err := errors.Errorf("Function %q is not of type %s", m, js.TypeFunction)
-		jww.FATAL.Panicf("%+v", err)
-	}
-
-	return func(args ...interface{}) js.Value {
-		return parent.Call(m, args)
-	}
-}
-
-// JsonToJS converts a marshalled JSON bytes to a Javascript object.
-func JsonToJS(src []byte) js.Value {
-	var inInterface map[string]interface{}
-	err := json.Unmarshal(src, &inInterface)
-	if err != nil {
-		Throw(TypeError, err)
-		return js.ValueOf(nil)
-	}
-
-	return js.ValueOf(inInterface)
+// JsTrace converts the error to a Javascript Error that includes the error's
+// stack trace.
+func JsTrace(err error) js.Value {
+	return Error.New(fmt.Sprintf("%+v", err))
 }
 
 // Throw function stub to throws Javascript exceptions. The exception must be
diff --git a/utils/errors_test.go b/utils/errors_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6f48e4609a3b33b10941336b332eb0f956ac2611
--- /dev/null
+++ b/utils/errors_test.go
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+package utils
+
+import (
+	"github.com/pkg/errors"
+	"syscall/js"
+	"testing"
+)
+
+func TestJsError(t *testing.T) {
+	err := errors.Errorf("test error")
+
+	jsError := JsError(err)
+
+	t.Logf("%+v", jsError)
+	t.Logf("%+v", jsError.String())
+	t.Logf("%+v", js.Error{Value: jsError})
+}
+
+func TestJsTrace(t *testing.T) {
+}
+
+func TestThrow(t *testing.T) {
+}
+
+func Test_throw(t *testing.T) {
+}
diff --git a/utils/utils.go b/utils/utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..816f4f67c1d795afbab16dc6b6fc4593cd6d5132
--- /dev/null
+++ b/utils/utils.go
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// 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 utils
+
+import (
+	"encoding/json"
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+	"syscall/js"
+)
+
+var (
+	Error      = js.Global().Get("Error")
+	JSON       = js.Global().Get("JSON")
+	Promise    = js.Global().Get("Promise")
+	Uint8Array = js.Global().Get("Uint8Array")
+)
+
+// CopyBytesToGo copies the Uint8Array stored in the js.Value to []byte. This is
+// a wrapper for js.CopyBytesToGo to make it more convenient.
+func CopyBytesToGo(src js.Value) []byte {
+	b := make([]byte, src.Length())
+	js.CopyBytesToGo(b, src)
+	return b
+}
+
+// CopyBytesToJS copies the []byte to a Uint8Array stored in a js.Value. This is
+// a wrapper for js.CopyBytesToJS to make it more convenient.
+func CopyBytesToJS(src []byte) js.Value {
+	dst := Uint8Array.New(len(src))
+	js.CopyBytesToJS(dst, src)
+	return dst
+}
+
+// WrapCB wraps a Javascript function in an object so that it can be called
+// later with only the arguments and without specifying the function name.
+//
+// Panics if m is not a function.
+func WrapCB(parent js.Value, m string) func(args ...interface{}) js.Value {
+	if parent.Get(m).Type() != js.TypeFunction {
+		// Create the error separate from the print so stack trace is printed
+		err := errors.Errorf("Function %q is not of type %s", m, js.TypeFunction)
+		jww.FATAL.Panicf("%+v", err)
+	}
+
+	return func(args ...interface{}) js.Value {
+		return parent.Call(m, args...)
+	}
+}
+
+// JsonToJS converts a marshalled JSON bytes to a Javascript object.
+func JsonToJS(src []byte) (js.Value, error) {
+	var inInterface map[string]interface{}
+	err := json.Unmarshal(src, &inInterface)
+	if err != nil {
+		Throw(TypeError, err)
+		return js.ValueOf(nil), err
+	}
+
+	return js.ValueOf(inInterface), nil
+}
+
+// JsToJson converts the Javascript value to JSON.
+func JsToJson(value js.Value) string {
+	return JSON.Call("stringify", value).String()
+}
+
+type PromiseFn func(resolve, reject func(args ...interface{}) js.Value)
+
+// CreatePromise creates a Javascript promise to return the value of a blocking
+// Go function to Javascript.
+func CreatePromise(f PromiseFn) interface{} {
+	// Create handler for promise (this will be a Javascript function)
+	handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+		// Spawn a new go routine to perform the blocking function
+		go func(resolve, reject js.Value) {
+			f(resolve.Invoke, reject.Invoke)
+		}(args[0], args[1])
+
+		return nil
+	})
+
+	// Create and return the Promise object
+	return Promise.New(handler)
+}
diff --git a/wasm/wasm_js.s b/utils/utils_js.s
similarity index 100%
rename from wasm/wasm_js.s
rename to utils/utils_js.s
diff --git a/wasm/authenticatedConnection.go b/wasm/authenticatedConnection.go
index ca170f49f9e875561248fc046c70f0aaf48a4d7b..d462797ac75f329c6efdcc4b0964215aa9139d7e 100644
--- a/wasm/authenticatedConnection.go
+++ b/wasm/authenticatedConnection.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -49,13 +50,13 @@ func (ac *AuthenticatedConnection) IsAuthenticated(js.Value, []js.Value) interfa
 //  - Javascript representation of the Connection object
 //  - throws a TypeError if creating loading the parameters or connecting fails
 func (c *Cmix) ConnectWithAuthentication(_ js.Value, args []js.Value) interface{} {
-	recipientContact := CopyBytesToGo(args[1])
-	e2eParamsJSON := CopyBytesToGo(args[2])
+	recipientContact := utils.CopyBytesToGo(args[1])
+	e2eParamsJSON := utils.CopyBytesToGo(args[2])
 
 	ac, err := c.api.ConnectWithAuthentication(
 		args[0].Int(), recipientContact, e2eParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/backup.go b/wasm/backup.go
index c3bfba7e0fa4d762a2dfcd30cc312e9c9a5178f3..ca3072c0105ffc87b170fe3d71aaeb21ea27bde9 100644
--- a/wasm/backup.go
+++ b/wasm/backup.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -44,7 +45,7 @@ type updateBackupFunc struct {
 }
 
 func (ubf *updateBackupFunc) UpdateBackup(encryptedBackup []byte) {
-	ubf.updateBackup(CopyBytesToJS(encryptedBackup))
+	ubf.updateBackup(utils.CopyBytesToJS(encryptedBackup))
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -69,17 +70,17 @@ func NewCmixFromBackup(_ js.Value, args []js.Value) interface{} {
 	ndfJSON := args[0].String()
 	storageDir := args[1].String()
 	backupPassphrase := args[2].String()
-	sessionPassword := CopyBytesToGo(args[3])
-	backupFileContents := CopyBytesToGo(args[4])
+	sessionPassword := utils.CopyBytesToGo(args[3])
+	backupFileContents := utils.CopyBytesToGo(args[4])
 
 	report, err := bindings.NewCmixFromBackup(ndfJSON, storageDir,
 		backupPassphrase, sessionPassword, backupFileContents)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -101,11 +102,11 @@ func NewCmixFromBackup(_ js.Value, args []js.Value) interface{} {
 //  - Javascript representation of the Backup object
 //  - Throws a TypeError if initializing the Backup fails.
 func InitializeBackup(_ js.Value, args []js.Value) interface{} {
-	cb := &updateBackupFunc{WrapCB(args[3], "UpdateBackup")}
+	cb := &updateBackupFunc{utils.WrapCB(args[3], "UpdateBackup")}
 	api, err := bindings.InitializeBackup(
 		args[0].Int(), args[1].Int(), args[2].String(), cb)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -130,10 +131,10 @@ func InitializeBackup(_ js.Value, args []js.Value) interface{} {
 //  - Javascript representation of the Backup object
 //  - Throws a TypeError if initializing the Backup fails.
 func ResumeBackup(_ js.Value, args []js.Value) interface{} {
-	cb := &updateBackupFunc{WrapCB(args[2], "UpdateBackup")}
+	cb := &updateBackupFunc{utils.WrapCB(args[2], "UpdateBackup")}
 	api, err := bindings.ResumeBackup(args[0].Int(), args[1].Int(), cb)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -148,7 +149,7 @@ func ResumeBackup(_ js.Value, args []js.Value) interface{} {
 func (b *Backup) StopBackup(js.Value, []js.Value) interface{} {
 	err := b.api.StopBackup()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/broadcast.go b/wasm/broadcast.go
index df8bd0851594ee39abca5562f82b2f7e6331cfa3..456027b4c63828ac80578ebecc5eea62dea9b7cf 100644
--- a/wasm/broadcast.go
+++ b/wasm/broadcast.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -48,11 +49,11 @@ func newChannelJS(api *bindings.Channel) map[string]interface{} {
 //  - Javascript representation of the Channel object.
 //  - Throws a TypeError if creation fails.
 func NewBroadcastChannel(_ js.Value, args []js.Value) interface{} {
-	channelDefinition := CopyBytesToGo(args[1])
+	channelDefinition := utils.CopyBytesToGo(args[1])
 
 	api, err := bindings.NewBroadcastChannel(args[0].Int(), channelDefinition)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -66,7 +67,7 @@ type broadcastListener struct {
 }
 
 func (bl *broadcastListener) Callback(payload []byte, err error) {
-	bl.callback(CopyBytesToJS(payload), err.Error())
+	bl.callback(utils.CopyBytesToJS(payload), err.Error())
 }
 
 // Listen registers a BroadcastListener for a given method. This allows users to
@@ -82,9 +83,9 @@ func (bl *broadcastListener) Callback(payload []byte, err error) {
 //  - Throws a TypeError if registering the listener fails.
 func (c *Channel) Listen(_ js.Value, args []js.Value) interface{} {
 	err := c.api.Listen(
-		&broadcastListener{WrapCB(args[0], "Callback")}, args[1].Int())
+		&broadcastListener{utils.WrapCB(args[0], "Callback")}, args[1].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -102,13 +103,13 @@ func (c *Channel) Listen(_ js.Value, args []js.Value) interface{} {
 //    Cmix.WaitForRoundResult to see if the broadcast succeeded (Uint8Array).
 //  - Throws a TypeError if broadcasting fails.
 func (c *Channel) Broadcast(_ js.Value, args []js.Value) interface{} {
-	report, err := c.api.Broadcast(CopyBytesToGo(args[0]))
+	report, err := c.api.Broadcast(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 // BroadcastAsymmetric sends a given payload over the broadcast channel using
@@ -124,13 +125,13 @@ func (c *Channel) Broadcast(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if broadcasting fails.
 func (c *Channel) BroadcastAsymmetric(_ js.Value, args []js.Value) interface{} {
 	report, err := c.api.BroadcastAsymmetric(
-		CopyBytesToGo(args[0]), CopyBytesToGo(args[1]))
+		utils.CopyBytesToGo(args[0]), utils.CopyBytesToGo(args[1]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 // MaxPayloadSize returns the maximum possible payload size which can be
@@ -159,11 +160,11 @@ func (c *Channel) MaxAsymmetricPayloadSize(js.Value, []js.Value) interface{} {
 func (c *Channel) Get(js.Value, []js.Value) interface{} {
 	def, err := c.api.Get()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(def)
+	return utils.CopyBytesToJS(def)
 }
 
 // Stop stops the channel from listening for more messages.
diff --git a/wasm/cmix.go b/wasm/cmix.go
index 1908b7f6a4ae5a470813e042002dde87bccb09c1..70c2bed182e13601e14a4fc4e9a224bfe7cfcc2f 100644
--- a/wasm/cmix.go
+++ b/wasm/cmix.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -74,12 +75,12 @@ func newCmixJS(api *bindings.Cmix) map[string]interface{} {
 // Returns:
 //  - throws a TypeError if creating new Cmix fails.
 func NewCmix(_ js.Value, args []js.Value) interface{} {
-	password := CopyBytesToGo(args[2])
+	password := utils.CopyBytesToGo(args[2])
 
 	err := bindings.NewCmix(
 		args[0].String(), args[1].String(), password, args[3].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -102,19 +103,23 @@ func NewCmix(_ js.Value, args []js.Value) interface{} {
 //  - args[2] - JSON of [xxdk.CMIXParams] (Uint8Array)
 //
 // Returns:
-//  - Javascript representation of the Cmix object
-//  - throws a TypeError if creating loading Cmix fails
+//  - A promise that returns a Javascript representation of the Cmix object.
+//  - Throws a error if loading Cmix fails.
 func LoadCmix(_ js.Value, args []js.Value) interface{} {
-	password := CopyBytesToGo(args[1])
-	cmixParamsJSON := CopyBytesToGo(args[2])
-
-	net, err := bindings.LoadCmix(args[0].String(), password, cmixParamsJSON)
-	if err != nil {
-		Throw(TypeError, err)
-		return nil
+	storageDir := args[0].String()
+	password := utils.CopyBytesToGo(args[1])
+	cmixParamsJSON := utils.CopyBytesToGo(args[2])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		net, err := bindings.LoadCmix(storageDir, password, cmixParamsJSON)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(newCmixJS(net))
+		}
 	}
 
-	return newCmixJS(net)
+	return utils.CreatePromise(promiseFn)
 }
 
 // GetID returns the ID for this [bindings.Cmix] in the cmixTracker.
diff --git a/wasm/connect.go b/wasm/connect.go
index a3ceda0de48d375299c1aa94fd6ecbaa783f6692..779ff4c9ae37f5f5124af5fe0db4ea12f7aa756b 100644
--- a/wasm/connect.go
+++ b/wasm/connect.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -59,11 +60,11 @@ func (c *Connection) GetID(js.Value, []js.Value) interface{} {
 //  - Javascript representation of the Connection object
 //  - throws a TypeError if creating loading the parameters or connecting fails
 func (c *Cmix) Connect(_ js.Value, args []js.Value) interface{} {
-	recipientContact := CopyBytesToGo(args[1])
-	e2eParamsJSON := CopyBytesToGo(args[2])
+	recipientContact := utils.CopyBytesToGo(args[1])
+	e2eParamsJSON := utils.CopyBytesToGo(args[2])
 	api, err := c.api.Connect(args[0].Int(), recipientContact, e2eParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -86,12 +87,12 @@ func (c *Cmix) Connect(_ js.Value, args []js.Value) interface{} {
 //    cmix.WaitForRoundResult to see if the send succeeded (Uint8Array)
 //  - throws a TypeError if sending fails
 func (c *Connection) SendE2E(_ js.Value, args []js.Value) interface{} {
-	sendReport, err := c.api.SendE2E(args[0].Int(), CopyBytesToGo(args[1]))
+	sendReport, err := c.api.SendE2E(args[0].Int(), utils.CopyBytesToGo(args[1]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
-	return CopyBytesToJS(sendReport)
+	return utils.CopyBytesToJS(sendReport)
 }
 
 // Close deletes this Connection's partner.Manager and releases resources.
@@ -101,7 +102,7 @@ func (c *Connection) SendE2E(_ js.Value, args []js.Value) interface{} {
 func (c *Connection) Close(js.Value, []js.Value) interface{} {
 	err := c.api.Close()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -113,7 +114,7 @@ func (c *Connection) Close(js.Value, []js.Value) interface{} {
 // Returns:
 //  - bytes of the partner's [id.ID] (Uint8Array)
 func (c *Connection) GetPartner(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(c.api.GetPartner())
+	return utils.CopyBytesToJS(c.api.GetPartner())
 }
 
 // listener adheres to the [bindings.Listener] interface.
@@ -122,7 +123,7 @@ type listener struct {
 	name func(args ...interface{}) js.Value
 }
 
-func (l *listener) Hear(item []byte) { l.hear(CopyBytesToJS(item)) }
+func (l *listener) Hear(item []byte) { l.hear(utils.CopyBytesToJS(item)) }
 func (l *listener) Name() string     { return l.name().String() }
 
 // RegisterListener is used for E2E reception and allows for reading data sent
@@ -137,9 +138,9 @@ func (l *listener) Name() string     { return l.name().String() }
 //  - throws a TypeError is registering the listener fails
 func (c *Connection) RegisterListener(_ js.Value, args []js.Value) interface{} {
 	err := c.api.RegisterListener(args[0].Int(),
-		&listener{WrapCB(args[1], "Hear"), WrapCB(args[1], "Name")})
+		&listener{utils.WrapCB(args[1], "Hear"), utils.WrapCB(args[1], "Name")})
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/delivery.go b/wasm/delivery.go
index d2b8d31efa923ada83881fb4e57c48392018ff14..b0f340978e6a92ccb510bc36c60a8875b309c77f 100644
--- a/wasm/delivery.go
+++ b/wasm/delivery.go
@@ -10,6 +10,7 @@
 package wasm
 
 import (
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -21,7 +22,7 @@ type messageDeliveryCallback struct {
 
 func (mdc *messageDeliveryCallback) EventCallback(
 	delivered, timedOut bool, roundResults []byte) {
-	mdc.eventCallback(delivered, timedOut, CopyBytesToJS(roundResults))
+	mdc.eventCallback(delivered, timedOut, utils.CopyBytesToJS(roundResults))
 }
 
 // WaitForRoundResult allows the caller to get notified if the rounds a message
@@ -48,12 +49,12 @@ func (mdc *messageDeliveryCallback) EventCallback(
 //  - throws a TypeError if the parameters are invalid or getting round results
 //    fails
 func (c *Cmix) WaitForRoundResult(_ js.Value, args []js.Value) interface{} {
-	roundList := CopyBytesToGo(args[0])
-	mdc := &messageDeliveryCallback{WrapCB(args[1], "EventCallback")}
+	roundList := utils.CopyBytesToGo(args[0])
+	mdc := &messageDeliveryCallback{utils.WrapCB(args[1], "EventCallback")}
 
 	err := c.api.WaitForRoundResult(roundList, mdc, args[2].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/dummy.go b/wasm/dummy.go
index 822acd644a2e33632aad75fd369c6cf6a894e8f7..a8400f356be09045da9dcfe3da2234bbd5838fe9 100644
--- a/wasm/dummy.go
+++ b/wasm/dummy.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -56,7 +57,7 @@ func NewDummyTrafficManager(_ js.Value, args []js.Value) interface{} {
 	dt, err := bindings.NewDummyTrafficManager(
 		args[0].Int(), args[1].Int(), args[2].Int(), args[3].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -80,7 +81,7 @@ func NewDummyTrafficManager(_ js.Value, args []js.Value) interface{} {
 func (dt *DummyTraffic) SetStatus(_ js.Value, args []js.Value) interface{} {
 	err := dt.api.SetStatus(args[0].Bool())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/e2e.go b/wasm/e2e.go
index 9ae1551b29e59d70cd968cb41540a7898429e904..70a66436a5b0bad3a5e5544b163e3b0d14c57f38 100644
--- a/wasm/e2e.go
+++ b/wasm/e2e.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -91,13 +92,13 @@ func (e *E2e) GetID(js.Value, []js.Value) interface{} {
 //  - Throws a TypeError if logging in fails.
 func Login(_ js.Value, args []js.Value) interface{} {
 	callbacks := newAuthCallbacks(args[1])
-	identity := CopyBytesToGo(args[2])
-	e2eParamsJSON := CopyBytesToGo(args[3])
+	identity := utils.CopyBytesToGo(args[2])
+	e2eParamsJSON := utils.CopyBytesToGo(args[3])
 
 	newE2E, err := bindings.Login(
 		args[0].Int(), callbacks, identity, e2eParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -121,13 +122,13 @@ func Login(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if logging in fails.
 func LoginEphemeral(_ js.Value, args []js.Value) interface{} {
 	callbacks := newAuthCallbacks(args[1])
-	identity := CopyBytesToGo(args[2])
-	e2eParamsJSON := CopyBytesToGo(args[3])
+	identity := utils.CopyBytesToGo(args[2])
+	e2eParamsJSON := utils.CopyBytesToGo(args[3])
 
 	newE2E, err := bindings.LoginEphemeral(
 		args[0].Int(), callbacks, identity, e2eParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -139,7 +140,7 @@ func LoginEphemeral(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - Marshalled [contact.Contact] (Uint8Array)
 func (e *E2e) GetContact(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(e.api.GetContact())
+	return utils.CopyBytesToJS(e.api.GetContact())
 }
 
 // GetUdAddressFromNdf retrieve the User Discovery's network address fom the
@@ -156,7 +157,7 @@ func (e *E2e) GetUdAddressFromNdf(js.Value, []js.Value) interface{} {
 // Returns:
 //  - Public certificate in PEM format (Uint8Array)
 func (e *E2e) GetUdCertFromNdf(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(e.api.GetUdCertFromNdf())
+	return utils.CopyBytesToJS(e.api.GetUdCertFromNdf())
 }
 
 // GetUdContactFromNdf assembles the User Discovery's contact file from the data
@@ -168,11 +169,11 @@ func (e *E2e) GetUdCertFromNdf(js.Value, []js.Value) interface{} {
 func (e *E2e) GetUdContactFromNdf(js.Value, []js.Value) interface{} {
 	b, err := e.api.GetUdContactFromNdf()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(b)
+	return utils.CopyBytesToJS(b)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -193,15 +194,15 @@ func newAuthCallbacks(value js.Value) *authCallbacks {
 	a := &authCallbacks{}
 
 	if value.Get("Request").Type() == js.TypeFunction {
-		a.request = WrapCB(value, "Request")
+		a.request = utils.WrapCB(value, "Request")
 	}
 
 	if value.Get("Confirm").Type() == js.TypeFunction {
-		a.confirm = WrapCB(value, "Confirm")
+		a.confirm = utils.WrapCB(value, "Confirm")
 	}
 
 	if value.Get("Reset").Type() == js.TypeFunction {
-		a.reset = WrapCB(value, "Reset")
+		a.reset = utils.WrapCB(value, "Reset")
 	}
 
 	return a
@@ -210,20 +211,23 @@ func newAuthCallbacks(value js.Value) *authCallbacks {
 func (a *authCallbacks) Request(
 	contact, receptionId []byte, ephemeralId, roundId int64) {
 	if a.request != nil {
-		a.request(contact, receptionId, ephemeralId, roundId)
+		a.request(utils.CopyBytesToJS(contact), utils.CopyBytesToJS(receptionId),
+			ephemeralId, roundId)
 	}
 }
 
 func (a *authCallbacks) Confirm(
 	contact, receptionId []byte, ephemeralId, roundId int64) {
 	if a.confirm != nil {
-		a.confirm(contact, receptionId, ephemeralId, roundId)
+		a.confirm(utils.CopyBytesToJS(contact), utils.CopyBytesToJS(receptionId),
+			ephemeralId, roundId)
 	}
-
 }
+
 func (a *authCallbacks) Reset(
 	contact, receptionId []byte, ephemeralId, roundId int64) {
 	if a.reset != nil {
-		a.reset(contact, receptionId, ephemeralId, roundId)
+		a.reset(utils.CopyBytesToJS(contact), utils.CopyBytesToJS(receptionId),
+			ephemeralId, roundId)
 	}
 }
diff --git a/wasm/e2eAuth.go b/wasm/e2eAuth.go
index 39158b8d387142f4a4e4b99b7e272b74724b415f..3d6efa4343e7441a51c328e99defa1fb75873625 100644
--- a/wasm/e2eAuth.go
+++ b/wasm/e2eAuth.go
@@ -10,6 +10,7 @@
 package wasm
 
 import (
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -36,18 +37,22 @@ import (
 //  - args[1] - JSON of [fact.FactList] (Uint8Array).
 //
 // Returns:
-//  - ID of the round (int).
-//  - Throws TypeError if the request fails.
+//  - A promise that returns the ID of the round (int).
+//  - Throws an error if the request fails.
 func (e *E2e) Request(_ js.Value, args []js.Value) interface{} {
-	partnerContact := CopyBytesToGo(args[0])
-	factsListJson := CopyBytesToGo(args[1])
-	rid, err := e.api.Request(partnerContact, factsListJson)
-	if err != nil {
-		Throw(TypeError, err)
-		return nil
+	partnerContact := utils.CopyBytesToGo(args[0])
+	factsListJson := utils.CopyBytesToGo(args[1])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		rid, err := e.api.Request(partnerContact, factsListJson)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(rid)
+		}
 	}
 
-	return rid
+	return utils.CreatePromise(promiseFn)
 }
 
 // Confirm sends a confirmation for a received request. It can only be called
@@ -70,17 +75,21 @@ func (e *E2e) Request(_ js.Value, args []js.Value) interface{} {
 //  - args[0] - marshalled bytes of the partner [contact.Contact] (Uint8Array).
 //
 // Returns:
-//  - ID of the round (int).
-//  - Throws TypeError if the confirmation fails.
+//  - A promise that returns the ID of the round (int).
+//  - Throws an error if the confirmation fails.
 func (e *E2e) Confirm(_ js.Value, args []js.Value) interface{} {
-	partnerContact := CopyBytesToGo(args[0])
-	rid, err := e.api.Confirm(partnerContact)
-	if err != nil {
-		Throw(TypeError, err)
-		return nil
+	partnerContact := utils.CopyBytesToGo(args[0])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		rid, err := e.api.Confirm(partnerContact)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(rid)
+		}
 	}
 
-	return rid
+	return utils.CreatePromise(promiseFn)
 }
 
 // Reset sends a contact reset request from the user identity in the imported
@@ -101,17 +110,21 @@ func (e *E2e) Confirm(_ js.Value, args []js.Value) interface{} {
 //  - args[0] - marshalled bytes of the partner [contact.Contact] (Uint8Array).
 //
 // Returns:
-//  - ID of the round (int).
-//  - Throws TypeError if the reset fails.
+//  - A promise that returns the ID of the round (int).
+//  - Throws an error if the reset fails.
 func (e *E2e) Reset(_ js.Value, args []js.Value) interface{} {
-	partnerContact := CopyBytesToGo(args[0])
-	rid, err := e.api.Reset(partnerContact)
-	if err != nil {
-		Throw(TypeError, err)
-		return nil
+	partnerContact := utils.CopyBytesToGo(args[0])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		rid, err := e.api.Reset(partnerContact)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(rid)
+		}
 	}
 
-	return rid
+	return utils.CreatePromise(promiseFn)
 }
 
 // ReplayConfirm resends a confirmation to the partner. It will fail to send if
@@ -126,17 +139,21 @@ func (e *E2e) Reset(_ js.Value, args []js.Value) interface{} {
 //  - args[0] - marshalled bytes of the partner [contact.Contact] (Uint8Array).
 //
 // Returns:
-//  - ID of the round (int).
-//  - Throws TypeError if the confirmation fails.
+//  - A promise that returns the ID of the round (int).
+//  - Throws an error if the confirmation fails.
 func (e *E2e) ReplayConfirm(_ js.Value, args []js.Value) interface{} {
-	partnerContact := CopyBytesToGo(args[0])
-	rid, err := e.api.ReplayConfirm(partnerContact)
-	if err != nil {
-		Throw(TypeError, err)
-		return nil
+	partnerContact := utils.CopyBytesToGo(args[0])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		rid, err := e.api.ReplayConfirm(partnerContact)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(rid)
+		}
 	}
 
-	return rid
+	return utils.CreatePromise(promiseFn)
 }
 
 // CallAllReceivedRequests will iterate through all pending contact requests and
@@ -154,10 +171,10 @@ func (e *E2e) CallAllReceivedRequests(js.Value, []js.Value) interface{} {
 // Returns:
 //  - Throws TypeError if the deletion fails.
 func (e *E2e) DeleteRequest(_ js.Value, args []js.Value) interface{} {
-	partnerContact := CopyBytesToGo(args[0])
+	partnerContact := utils.CopyBytesToGo(args[0])
 	err := e.api.DeleteRequest(partnerContact)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -171,7 +188,7 @@ func (e *E2e) DeleteRequest(_ js.Value, args []js.Value) interface{} {
 func (e *E2e) DeleteAllRequests(js.Value, []js.Value) interface{} {
 	err := e.api.DeleteAllRequests()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -185,7 +202,7 @@ func (e *E2e) DeleteAllRequests(js.Value, []js.Value) interface{} {
 func (e *E2e) DeleteSentRequests(js.Value, []js.Value) interface{} {
 	err := e.api.DeleteSentRequests()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -199,7 +216,7 @@ func (e *E2e) DeleteSentRequests(js.Value, []js.Value) interface{} {
 func (e *E2e) DeleteReceiveRequests(js.Value, []js.Value) interface{} {
 	err := e.api.DeleteReceiveRequests()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -215,14 +232,14 @@ func (e *E2e) DeleteReceiveRequests(js.Value, []js.Value) interface{} {
 //  - The marshalled bytes of [contact.Contact] (Uint8Array).
 //  - Throws TypeError if getting the received request fails.
 func (e *E2e) GetReceivedRequest(_ js.Value, args []js.Value) interface{} {
-	partnerContact := CopyBytesToGo(args[0])
+	partnerContact := utils.CopyBytesToGo(args[0])
 	c, err := e.api.GetReceivedRequest(partnerContact)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(c)
+	return utils.CopyBytesToJS(c)
 }
 
 // VerifyOwnership checks if the received ownership proof is valid.
@@ -236,12 +253,12 @@ func (e *E2e) GetReceivedRequest(_ js.Value, args []js.Value) interface{} {
 //  - Returns true if the ownership is valid (boolean)
 //  - Throws TypeError if loading the parameters fails.
 func (e *E2e) VerifyOwnership(_ js.Value, args []js.Value) interface{} {
-	receivedContact := CopyBytesToGo(args[0])
-	verifiedContact := CopyBytesToGo(args[1])
+	receivedContact := utils.CopyBytesToGo(args[0])
+	verifiedContact := utils.CopyBytesToGo(args[1])
 	isValid, err := e.api.VerifyOwnership(
 		receivedContact, verifiedContact, args[2].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -259,11 +276,11 @@ func (e *E2e) VerifyOwnership(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - Throws TypeError if the [id.ID] cannot be unmarshalled.
 func (e *E2e) AddPartnerCallback(_ js.Value, args []js.Value) interface{} {
-	partnerID := CopyBytesToGo(args[0])
+	partnerID := utils.CopyBytesToGo(args[0])
 	callbacks := newAuthCallbacks(args[1])
 	err := e.api.AddPartnerCallback(partnerID, callbacks)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -279,10 +296,10 @@ func (e *E2e) AddPartnerCallback(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - Throws TypeError if the [id.ID] cannot be unmarshalled.
 func (e *E2e) DeletePartnerCallback(_ js.Value, args []js.Value) interface{} {
-	partnerID := CopyBytesToGo(args[0])
+	partnerID := utils.CopyBytesToGo(args[0])
 	err := e.api.DeletePartnerCallback(partnerID)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/e2eHandler.go b/wasm/e2eHandler.go
index 57d16f894d1290d3cb9cf78e0e34847f6155c8ab..8d6efc63c5e015936230de093d71cf78c0b9b336 100644
--- a/wasm/e2eHandler.go
+++ b/wasm/e2eHandler.go
@@ -10,6 +10,7 @@
 package wasm
 
 import (
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -18,7 +19,7 @@ import (
 // Returns:
 //  - The marshalled bytes of the [id.ID] object (Uint8Array)
 func (e *E2e) GetReceptionID(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(e.api.GetReceptionID())
+	return utils.CopyBytesToJS(e.api.GetReceptionID())
 }
 
 // GetAllPartnerIDs returns a list of all partner IDs that the user has an E2E
@@ -30,10 +31,10 @@ func (e *E2e) GetReceptionID(js.Value, []js.Value) interface{} {
 func (e *E2e) GetAllPartnerIDs(js.Value, []js.Value) interface{} {
 	partnerIDs, err := e.api.GetAllPartnerIDs()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
-	return CopyBytesToJS(partnerIDs)
+	return utils.CopyBytesToJS(partnerIDs)
 }
 
 // PayloadSize returns the max payload size for a partitionable E2E message.
@@ -83,10 +84,10 @@ func (e *E2e) FirstPartitionSize(js.Value, []js.Value) interface{} {
 func (e *E2e) GetHistoricalDHPrivkey(js.Value, []js.Value) interface{} {
 	privKey, err := e.api.GetHistoricalDHPrivkey()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
-	return CopyBytesToJS(privKey)
+	return utils.CopyBytesToJS(privKey)
 }
 
 // GetHistoricalDHPubkey returns the user's marshalled historical DH public key.
@@ -98,10 +99,10 @@ func (e *E2e) GetHistoricalDHPrivkey(js.Value, []js.Value) interface{} {
 func (e *E2e) GetHistoricalDHPubkey(js.Value, []js.Value) interface{} {
 	pubKey, err := e.api.GetHistoricalDHPubkey()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
-	return CopyBytesToJS(pubKey)
+	return utils.CopyBytesToJS(pubKey)
 }
 
 // HasAuthenticatedChannel returns true if an authenticated channel with the
@@ -114,9 +115,9 @@ func (e *E2e) GetHistoricalDHPubkey(js.Value, []js.Value) interface{} {
 //  - Existence of authenticated channel (boolean)
 //  - Throws TypeError if unmarshalling the ID or getting the channel fails
 func (e *E2e) HasAuthenticatedChannel(_ js.Value, args []js.Value) interface{} {
-	exists, err := e.api.HasAuthenticatedChannel(CopyBytesToGo(args[0]))
+	exists, err := e.api.HasAuthenticatedChannel(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 	return exists
@@ -132,7 +133,7 @@ func (e *E2e) HasAuthenticatedChannel(_ js.Value, args []js.Value) interface{} {
 func (e *E2e) RemoveService(_ js.Value, args []js.Value) interface{} {
 	err := e.api.RemoveService(args[0].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -149,22 +150,26 @@ func (e *E2e) RemoveService(_ js.Value, args []js.Value) interface{} {
 //  - args[3] - JSON [e2e.Params] (Uint8Array)
 //
 // Returns:
-//  - JSON of the [bindings.E2ESendReport], which can be passed into
-//    Cmix.WaitForRoundResult to see if the send succeeded (Uint8Array)
-//  - Throws TypeError if sending fails
+//  - A promise that returns the JSON of the [bindings.E2ESendReport], which can
+//    be passed into Cmix.WaitForRoundResult to see if the send succeeded
+//    (Uint8Array).
+//  - Throws error if sending fails.
 func (e *E2e) SendE2E(_ js.Value, args []js.Value) interface{} {
-	recipientId := CopyBytesToGo(args[1])
-	payload := CopyBytesToGo(args[2])
-	e2eParams := CopyBytesToGo(args[3])
-
-	sendReport, err := e.api.SendE2E(
-		args[0].Int(), recipientId, payload, e2eParams)
-	if err != nil {
-		Throw(TypeError, err)
-		return nil
+	recipientId := utils.CopyBytesToGo(args[1])
+	payload := utils.CopyBytesToGo(args[2])
+	e2eParams := utils.CopyBytesToGo(args[3])
+
+	promiseFn := func(resolve, reject func(args ...interface{}) js.Value) {
+		sendReport, err := e.api.SendE2E(
+			args[0].Int(), recipientId, payload, e2eParams)
+		if err != nil {
+			reject(utils.JsTrace(err))
+		} else {
+			resolve(utils.CopyBytesToJS(sendReport))
+		}
 	}
 
-	return CopyBytesToJS(sendReport)
+	return utils.CreatePromise(promiseFn)
 }
 
 // processor wraps Javascript callbacks to adhere to the [bindings.Processor]
@@ -176,7 +181,7 @@ type processor struct {
 
 func (p *processor) Process(
 	message, receptionId []byte, ephemeralId, roundId int64) {
-	p.process(CopyBytesToJS(message), CopyBytesToJS(receptionId), ephemeralId,
+	p.process(utils.CopyBytesToJS(message), utils.CopyBytesToJS(receptionId), ephemeralId,
 		roundId)
 }
 
@@ -201,11 +206,11 @@ func (p *processor) String() string {
 // Returns:
 //  - Throws TypeError if registering the service fails
 func (e *E2e) AddService(_ js.Value, args []js.Value) interface{} {
-	p := &processor{WrapCB(args[1], "Process"), WrapCB(args[1], "String")}
+	p := &processor{utils.WrapCB(args[1], "Process"), utils.WrapCB(args[1], "String")}
 
 	err := e.api.AddService(args[0].String(), p)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -225,12 +230,12 @@ func (e *E2e) AddService(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - Throws TypeError if registering the service fails
 func (e *E2e) RegisterListener(_ js.Value, args []js.Value) interface{} {
-	recipientId := CopyBytesToGo(args[0])
-	l := &listener{WrapCB(args[2], "Hear"), WrapCB(args[2], "Name")}
+	recipientId := utils.CopyBytesToGo(args[0])
+	l := &listener{utils.WrapCB(args[2], "Hear"), utils.WrapCB(args[2], "Name")}
 
 	err := e.api.RegisterListener(recipientId, args[1].Int(), l)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/errors.go b/wasm/errors.go
index 4aae7fe7ec5709b0dbd7d06f9d93677ebb1186b1..a22cdb4cd6422f309ccc45732df86174667e7ea2 100644
--- a/wasm/errors.go
+++ b/wasm/errors.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -51,7 +52,7 @@ func CreateUserFriendlyErrorMessage(_ js.Value, args []js.Value) interface{} {
 func UpdateCommonErrors(_ js.Value, args []js.Value) interface{} {
 	err := bindings.UpdateCommonErrors(args[0].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/fileTransfer.go b/wasm/fileTransfer.go
index 52d4a82c41b8d4ef072800bb601400b1e2108364..1a7d7953f29f42d54f569f75caf6547bce3f4a0c 100644
--- a/wasm/fileTransfer.go
+++ b/wasm/fileTransfer.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -55,7 +56,7 @@ type receiveFileCallback struct {
 }
 
 func (rfc *receiveFileCallback) Callback(payload []byte, err error) {
-	rfc.callback(CopyBytesToJS(payload), err.Error())
+	rfc.callback(utils.CopyBytesToJS(payload), err.Error())
 }
 
 // fileTransferSentProgressCallback wraps Javascript callbacks to adhere to the
@@ -66,7 +67,7 @@ type fileTransferSentProgressCallback struct {
 
 func (spc *fileTransferSentProgressCallback) Callback(
 	payload []byte, t *bindings.FilePartTracker, err error) {
-	spc.callback(CopyBytesToJS(payload), newFilePartTrackerJS(t), err.Error())
+	spc.callback(utils.CopyBytesToJS(payload), newFilePartTrackerJS(t), err.Error())
 }
 
 // fileTransferReceiveProgressCallback wraps Javascript callbacks to adhere to
@@ -77,7 +78,7 @@ type fileTransferReceiveProgressCallback struct {
 
 func (rpc *fileTransferReceiveProgressCallback) Callback(
 	payload []byte, t *bindings.FilePartTracker, err error) {
-	rpc.callback(CopyBytesToJS(payload), newFilePartTrackerJS(t), err.Error())
+	rpc.callback(utils.CopyBytesToJS(payload), newFilePartTrackerJS(t), err.Error())
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -97,14 +98,14 @@ func (rpc *fileTransferReceiveProgressCallback) Callback(
 //  - Javascript representation of the FileTransfer object.
 //  - Throws a TypeError initialising the file transfer manager fails.
 func InitFileTransfer(_ js.Value, args []js.Value) interface{} {
-	rfc := &receiveFileCallback{WrapCB(args[1], "Callback")}
-	e2eFileTransferParamsJson := CopyBytesToGo(args[2])
-	fileTransferParamsJson := CopyBytesToGo(args[3])
+	rfc := &receiveFileCallback{utils.WrapCB(args[1], "Callback")}
+	e2eFileTransferParamsJson := utils.CopyBytesToGo(args[2])
+	fileTransferParamsJson := utils.CopyBytesToGo(args[3])
 
 	api, err := bindings.InitFileTransfer(
 		args[0].Int(), rfc, e2eFileTransferParamsJson, fileTransferParamsJson)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -126,18 +127,18 @@ func InitFileTransfer(_ js.Value, args []js.Value) interface{} {
 //  - A unique ID for this file transfer (Uint8Array).
 //  - Throws a TypeError if sending fails.
 func (f *FileTransfer) Send(_ js.Value, args []js.Value) interface{} {
-	payload := CopyBytesToGo(args[0])
-	recipientID := CopyBytesToGo(args[1])
+	payload := utils.CopyBytesToGo(args[0])
+	recipientID := utils.CopyBytesToGo(args[1])
 	retry := float32(args[2].Float())
-	spc := &fileTransferSentProgressCallback{WrapCB(args[3], "Callback")}
+	spc := &fileTransferSentProgressCallback{utils.WrapCB(args[3], "Callback")}
 
 	ftID, err := f.api.Send(payload, recipientID, retry, spc, args[4].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(ftID)
+	return utils.CopyBytesToJS(ftID)
 }
 
 // Receive returns the full file on the completion of the transfer. It deletes
@@ -156,13 +157,13 @@ func (f *FileTransfer) Send(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError the file transfer is incomplete or Receive has already
 //    been called.
 func (f *FileTransfer) Receive(_ js.Value, args []js.Value) interface{} {
-	file, err := f.api.Receive(CopyBytesToGo(args[0]))
+	file, err := f.api.Receive(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(file)
+	return utils.CopyBytesToJS(file)
 }
 
 // CloseSend deletes a file from the internal storage once a transfer has
@@ -178,9 +179,9 @@ func (f *FileTransfer) Receive(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - Throws a TypeError if the file transfer is incomplete.
 func (f *FileTransfer) CloseSend(_ js.Value, args []js.Value) interface{} {
-	err := f.api.CloseSend(CopyBytesToGo(args[0]))
+	err := f.api.CloseSend(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -208,12 +209,12 @@ func (f *FileTransfer) CloseSend(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if registering the callback fails.
 func (f *FileTransfer) RegisterSentProgressCallback(
 	_ js.Value, args []js.Value) interface{} {
-	tidBytes := CopyBytesToGo(args[0])
-	spc := &fileTransferSentProgressCallback{WrapCB(args[1], "Callback")}
+	tidBytes := utils.CopyBytesToGo(args[0])
+	spc := &fileTransferSentProgressCallback{utils.WrapCB(args[1], "Callback")}
 
 	err := f.api.RegisterSentProgressCallback(tidBytes, spc, args[2].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -236,13 +237,13 @@ func (f *FileTransfer) RegisterSentProgressCallback(
 //  - Throws a TypeError if registering the callback fails.
 func (f *FileTransfer) RegisterReceivedProgressCallback(
 	_ js.Value, args []js.Value) interface{} {
-	tidBytes := CopyBytesToGo(args[0])
-	rpc := &fileTransferReceiveProgressCallback{WrapCB(args[1], "Callback")}
+	tidBytes := utils.CopyBytesToGo(args[0])
+	rpc := &fileTransferReceiveProgressCallback{utils.WrapCB(args[1], "Callback")}
 
 	err := f.api.RegisterReceivedProgressCallback(
 		tidBytes, rpc, args[2].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/follow.go b/wasm/follow.go
index 634ea5ceff23e4043540896fe5392fb0fac06c76..8da6a4d72b192590b0d8e84b861d1804b785940a 100644
--- a/wasm/follow.go
+++ b/wasm/follow.go
@@ -10,6 +10,7 @@
 package wasm
 
 import (
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -55,7 +56,7 @@ import (
 func (c *Cmix) StartNetworkFollower(_ js.Value, args []js.Value) interface{} {
 	err := c.api.StartNetworkFollower(args[0].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -73,7 +74,7 @@ func (c *Cmix) StartNetworkFollower(_ js.Value, args []js.Value) interface{} {
 func (c *Cmix) StopNetworkFollower(js.Value, []js.Value) interface{} {
 	err := c.api.StopNetworkFollower()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -115,11 +116,11 @@ func (c *Cmix) NetworkFollowerStatus(js.Value, []js.Value) interface{} {
 func (c *Cmix) GetNodeRegistrationStatus(js.Value, []js.Value) interface{} {
 	b, err := c.api.GetNodeRegistrationStatus()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(b)
+	return utils.CopyBytesToJS(b)
 }
 
 // HasRunningProcessies checks if any background threads are running and returns
@@ -163,7 +164,7 @@ func (nhc *networkHealthCallback) Callback(health bool) { nhc.callback(health) }
 //  - Returns a registration ID that can be used to unregister (int)
 func (c *Cmix) AddHealthCallback(_ js.Value, args []js.Value) interface{} {
 	return c.api.AddHealthCallback(
-		&networkHealthCallback{WrapCB(args[0], "Callback")})
+		&networkHealthCallback{utils.WrapCB(args[0], "Callback")})
 }
 
 // RemoveHealthCallback removes a health callback using its registration ID.
@@ -192,6 +193,6 @@ func (ce *clientError) Report(source, message, trace string) {
 //  - args[0] - Javascript object that has functions that implement the
 //    [bindings.ClientError] interface
 func (c *Cmix) RegisterClientErrorCallback(_ js.Value, args []js.Value) interface{} {
-	c.api.RegisterClientErrorCallback(&clientError{WrapCB(args[0], "Report")})
+	c.api.RegisterClientErrorCallback(&clientError{utils.WrapCB(args[0], "Report")})
 	return nil
 }
diff --git a/wasm/group.go b/wasm/group.go
index 5158e4658e021289bd4155976d0fd638be71dd3f..d19da538da36abca4fde43c428529976f6b97eba 100644
--- a/wasm/group.go
+++ b/wasm/group.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -55,13 +56,13 @@ func newGroupChatJS(api *bindings.GroupChat) map[string]interface{} {
 //  - Javascript representation of the GroupChat object.
 //  - Throws a TypeError if creating the GroupChat fails.
 func NewGroupChat(_ js.Value, args []js.Value) interface{} {
-	requestFunc := &groupRequest{WrapCB(args[1], "Callback")}
+	requestFunc := &groupRequest{utils.WrapCB(args[1], "Callback")}
 	p := &groupChatProcessor{
-		WrapCB(args[2], "Process"), WrapCB(args[2], "String")}
+		utils.WrapCB(args[2], "Process"), utils.WrapCB(args[2], "String")}
 
 	api, err := bindings.NewGroupChat(args[0].Int(), requestFunc, p)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -87,17 +88,17 @@ func NewGroupChat(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if making the group fails.
 func (g *GroupChat) MakeGroup(_ js.Value, args []js.Value) interface{} {
 	// (membershipBytes, message, name []byte) ([]byte, error)
-	membershipBytes := CopyBytesToGo(args[0])
-	message := CopyBytesToGo(args[1])
-	name := CopyBytesToGo(args[2])
+	membershipBytes := utils.CopyBytesToGo(args[0])
+	message := utils.CopyBytesToGo(args[1])
+	name := utils.CopyBytesToGo(args[2])
 
 	report, err := g.api.MakeGroup(membershipBytes, message, name)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 // ResendRequest resends a group request to all members in the group.
@@ -112,13 +113,13 @@ func (g *GroupChat) MakeGroup(_ js.Value, args []js.Value) interface{} {
 //    succeeded.
 //  - Throws a TypeError if resending the request fails.
 func (g *GroupChat) ResendRequest(_ js.Value, args []js.Value) interface{} {
-	report, err := g.api.ResendRequest(CopyBytesToGo(args[0]))
+	report, err := g.api.ResendRequest(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 // JoinGroup allows a user to join a group when a request is received.
@@ -134,7 +135,7 @@ func (g *GroupChat) ResendRequest(_ js.Value, args []js.Value) interface{} {
 func (g *GroupChat) JoinGroup(_ js.Value, args []js.Value) interface{} {
 	err := g.api.JoinGroup(args[0].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -150,9 +151,9 @@ func (g *GroupChat) JoinGroup(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - Throws a TypeError if leaving the group fails.
 func (g *GroupChat) LeaveGroup(_ js.Value, args []js.Value) interface{} {
-	err := g.api.LeaveGroup(CopyBytesToGo(args[0]))
+	err := g.api.LeaveGroup(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -173,16 +174,16 @@ func (g *GroupChat) LeaveGroup(_ js.Value, args []js.Value) interface{} {
 //  - JSON of [bindings.GroupSendReport] (Uint8Array), which can be passed into
 //    Cmix.WaitForRoundResult to see if the group message send succeeded.
 func (g *GroupChat) Send(_ js.Value, args []js.Value) interface{} {
-	groupId := CopyBytesToGo(args[0])
-	message := CopyBytesToGo(args[1])
+	groupId := utils.CopyBytesToGo(args[0])
+	message := utils.CopyBytesToGo(args[1])
 
 	report, err := g.api.Send(groupId, message, args[2].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 // GetGroups returns a list of group IDs that the user is a member of.
@@ -194,11 +195,11 @@ func (g *GroupChat) GetGroups(js.Value, []js.Value) interface{} {
 	// () ([]byte, error)
 	groups, err := g.api.GetGroups()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(groups)
+	return utils.CopyBytesToJS(groups)
 }
 
 // GetGroup returns the group with the group ID. If no group exists, then the
@@ -212,9 +213,9 @@ func (g *GroupChat) GetGroups(js.Value, []js.Value) interface{} {
 //  - Javascript representation of the Group object.
 //  - Throws a TypeError if getting the group fails
 func (g *GroupChat) GetGroup(_ js.Value, args []js.Value) interface{} {
-	grp, err := g.api.GetGroup(CopyBytesToGo(args[0]))
+	grp, err := g.api.GetGroup(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -262,7 +263,7 @@ func newGroupJS(api *bindings.Group) map[string]interface{} {
 // Returns:
 //  - Uint8Array
 func (g *Group) GetName(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(g.api.GetName())
+	return utils.CopyBytesToJS(g.api.GetName())
 }
 
 // GetID return the 33-byte unique group ID. This represents the id.ID object.
@@ -270,7 +271,7 @@ func (g *Group) GetName(js.Value, []js.Value) interface{} {
 // Returns:
 //  - Uint8Array
 func (g *Group) GetID(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(g.api.GetID())
+	return utils.CopyBytesToJS(g.api.GetID())
 }
 
 // GetTrackedID returns the tracked ID of the Group object. This is used by the
@@ -287,7 +288,7 @@ func (g *Group) GetTrackedID(js.Value, []js.Value) interface{} {
 // Returns:
 //  - Uint8Array
 func (g *Group) GetInitMessage(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(g.api.GetInitMessage())
+	return utils.CopyBytesToJS(g.api.GetInitMessage())
 }
 
 // GetCreatedNano returns the time the group was created in nanoseconds. This is
@@ -318,11 +319,11 @@ func (g *Group) GetCreatedMS(js.Value, []js.Value) interface{} {
 func (g *Group) GetMembership(js.Value, []js.Value) interface{} {
 	membership, err := g.api.GetMembership()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(membership)
+	return utils.CopyBytesToJS(membership)
 }
 
 // Serialize serializes the Group.
@@ -330,7 +331,7 @@ func (g *Group) GetMembership(js.Value, []js.Value) interface{} {
 // Returns:
 //  - Byte representation of the Group (Uint8Array).
 func (g *Group) Serialize(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(g.api.Serialize())
+	return utils.CopyBytesToJS(g.api.Serialize())
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -356,8 +357,8 @@ type groupChatProcessor struct {
 
 func (gcp *groupChatProcessor) Process(decryptedMessage, msg,
 	receptionId []byte, ephemeralId, roundId int64, err error) {
-	gcp.callback(CopyBytesToJS(decryptedMessage), CopyBytesToJS(msg),
-		CopyBytesToJS(receptionId), ephemeralId, roundId, err.Error())
+	gcp.callback(utils.CopyBytesToJS(decryptedMessage), utils.CopyBytesToJS(msg),
+		utils.CopyBytesToJS(receptionId), ephemeralId, roundId, err.Error())
 }
 
 func (gcp *groupChatProcessor) String() string {
diff --git a/wasm/identity.go b/wasm/identity.go
index eb8fadf47b0a8d9fe14e847a1a922ab7f2429c7e..c94de98d55f6352a9846871f74afb9e3b870944e 100644
--- a/wasm/identity.go
+++ b/wasm/identity.go
@@ -12,6 +12,7 @@ package wasm
 import (
 	"gitlab.com/elixxir/client/bindings"
 	"gitlab.com/elixxir/client/xxdk"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -32,12 +33,12 @@ import (
 // Returns:
 //  - throws a TypeError if the identity cannot be stored in storage
 func StoreReceptionIdentity(_ js.Value, args []js.Value) interface{} {
-	identity := CopyBytesToGo(args[1])
+	identity := utils.CopyBytesToGo(args[1])
 	err := bindings.StoreReceptionIdentity(
 		args[0].String(), identity, args[2].Int())
 
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -57,11 +58,11 @@ func StoreReceptionIdentity(_ js.Value, args []js.Value) interface{} {
 func LoadReceptionIdentity(_ js.Value, args []js.Value) interface{} {
 	ri, err := bindings.LoadReceptionIdentity(args[0].String(), args[1].Int())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(ri)
+	return utils.CopyBytesToJS(ri)
 }
 
 // MakeReceptionIdentity generates a new cryptographic identity for receiving
@@ -73,11 +74,11 @@ func LoadReceptionIdentity(_ js.Value, args []js.Value) interface{} {
 func (c *Cmix) MakeReceptionIdentity(js.Value, []js.Value) interface{} {
 	ri, err := c.api.MakeReceptionIdentity()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(ri)
+	return utils.CopyBytesToJS(ri)
 }
 
 // MakeLegacyReceptionIdentity generates the legacy identity for receiving
@@ -89,11 +90,11 @@ func (c *Cmix) MakeReceptionIdentity(js.Value, []js.Value) interface{} {
 func (c *Cmix) MakeLegacyReceptionIdentity(js.Value, []js.Value) interface{} {
 	ri, err := c.api.MakeLegacyReceptionIdentity()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(ri)
+	return utils.CopyBytesToJS(ri)
 }
 
 // GetReceptionRegistrationValidationSignature returns the signature provided by
@@ -103,7 +104,7 @@ func (c *Cmix) MakeLegacyReceptionIdentity(js.Value, []js.Value) interface{} {
 //  - signature (Uint8Array)
 func (c *Cmix) GetReceptionRegistrationValidationSignature(
 	js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(c.api.GetReceptionRegistrationValidationSignature())
+	return utils.CopyBytesToJS(c.api.GetReceptionRegistrationValidationSignature())
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -121,14 +122,14 @@ func (c *Cmix) GetReceptionRegistrationValidationSignature(
 //  - Throws a TypeError if unmarshalling the identity fails
 func GetContactFromReceptionIdentity(_ js.Value, args []js.Value) interface{} {
 	// Note that this function does not appear in normal bindings
-	identityJSON := CopyBytesToGo(args[0])
+	identityJSON := utils.CopyBytesToGo(args[0])
 	identity, err := xxdk.UnmarshalReceptionIdentity(identityJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(identity.GetContact().Marshal())
+	return utils.CopyBytesToJS(identity.GetContact().Marshal())
 }
 
 // GetIDFromContact returns the ID in the [contact.Contact] object.
@@ -142,11 +143,11 @@ func GetContactFromReceptionIdentity(_ js.Value, args []js.Value) interface{} {
 func GetIDFromContact(_ js.Value, args []js.Value) interface{} {
 	cID, err := bindings.GetIDFromContact([]byte(args[0].String()))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(cID)
+	return utils.CopyBytesToJS(cID)
 }
 
 // GetPubkeyFromContact returns the DH public key in the [contact.Contact]
@@ -161,11 +162,11 @@ func GetIDFromContact(_ js.Value, args []js.Value) interface{} {
 func GetPubkeyFromContact(_ js.Value, args []js.Value) interface{} {
 	key, err := bindings.GetPubkeyFromContact([]byte(args[0].String()))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(key)
+	return utils.CopyBytesToJS(key)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -183,11 +184,11 @@ func GetPubkeyFromContact(_ js.Value, args []js.Value) interface{} {
 //  - marshalled bytes of the modified [contact.Contact] (string)
 //  - throws a TypeError if loading or modifying the contact fails
 func SetFactsOnContact(_ js.Value, args []js.Value) interface{} {
-	marshaledContact := CopyBytesToGo(args[0])
-	factListJSON := CopyBytesToGo(args[1])
+	marshaledContact := utils.CopyBytesToGo(args[0])
+	factListJSON := utils.CopyBytesToGo(args[1])
 	c, err := bindings.SetFactsOnContact(marshaledContact, factListJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -203,11 +204,11 @@ func SetFactsOnContact(_ js.Value, args []js.Value) interface{} {
 //  - JSON of [fact.FactList] (Uint8Array)
 //  - throws a TypeError if loading the contact fails
 func GetFactsFromContact(_ js.Value, args []js.Value) interface{} {
-	fl, err := bindings.GetFactsFromContact(CopyBytesToGo(args[0]))
+	fl, err := bindings.GetFactsFromContact(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(fl)
+	return utils.CopyBytesToJS(fl)
 }
diff --git a/wasm/logging.go b/wasm/logging.go
index 24901db500f2c44bdf63019be0d17e8d65a2d502..e309addce9185edb6ec255abb0ffaf1d34ad12f6 100644
--- a/wasm/logging.go
+++ b/wasm/logging.go
@@ -13,6 +13,7 @@ import (
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"io"
 	"log"
 	"syscall/js"
@@ -42,7 +43,7 @@ func LogLevel(_ js.Value, args []js.Value) interface{} {
 	level := args[0].Int()
 	if level < 0 || level > 6 {
 		err := errors.Errorf("log level is not valid: log level: %d", level)
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/ndf.go b/wasm/ndf.go
index 9f1b3e9f1c56fe0e2ccf72efb0cb47167545cb90..7e00d3d988973c444afef910876cbecc67606fe5 100644
--- a/wasm/ndf.go
+++ b/wasm/ndf.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -28,9 +29,9 @@ func DownloadAndVerifySignedNdfWithUrl(_ js.Value, args []js.Value) interface{}
 	ndf, err := bindings.DownloadAndVerifySignedNdfWithUrl(
 		args[0].String(), args[1].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(ndf)
+	return utils.CopyBytesToJS(ndf)
 }
diff --git a/wasm/params.go b/wasm/params.go
index c63d0c7bacc501e9c77b5209bd141b31e83320c8..accafbc3166e80f6d489510e7cc5f9fc0beabae2 100644
--- a/wasm/params.go
+++ b/wasm/params.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -21,7 +22,7 @@ import (
 // Returns:
 //  - JSON of [xxdk.CMIXParams] (Uint8Array)
 func GetDefaultCMixParams(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(bindings.GetDefaultCMixParams())
+	return utils.CopyBytesToJS(bindings.GetDefaultCMixParams())
 }
 
 // GetDefaultE2EParams returns a JSON serialized object with all of the E2E
@@ -31,7 +32,7 @@ func GetDefaultCMixParams(js.Value, []js.Value) interface{} {
 // Returns:
 //  - JSON of [xxdk.E2EParams] (Uint8Array)
 func GetDefaultE2EParams(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(bindings.GetDefaultE2EParams())
+	return utils.CopyBytesToJS(bindings.GetDefaultE2EParams())
 }
 
 // GetDefaultFileTransferParams returns a JSON serialized object with all the
@@ -41,7 +42,7 @@ func GetDefaultE2EParams(js.Value, []js.Value) interface{} {
 // Returns:
 //  - JSON of [fileTransfer.Params] (Uint8Array)
 func GetDefaultFileTransferParams(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(bindings.GetDefaultFileTransferParams())
+	return utils.CopyBytesToJS(bindings.GetDefaultFileTransferParams())
 }
 
 // GetDefaultSingleUseParams returns a JSON serialized object with all the
@@ -51,7 +52,7 @@ func GetDefaultFileTransferParams(js.Value, []js.Value) interface{} {
 // Returns:
 //  - JSON of [single.RequestParams] (Uint8Array)
 func GetDefaultSingleUseParams(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(bindings.GetDefaultSingleUseParams())
+	return utils.CopyBytesToJS(bindings.GetDefaultSingleUseParams())
 }
 
 // GetDefaultE2eFileTransferParams returns a JSON serialized object with all the
@@ -61,5 +62,5 @@ func GetDefaultSingleUseParams(js.Value, []js.Value) interface{} {
 // Returns:
 //  - JSON of [fileTransfer.e2e.Params] (Uint8Array)
 func GetDefaultE2eFileTransferParams(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(bindings.GetDefaultSingleUseParams())
+	return utils.CopyBytesToJS(bindings.GetDefaultSingleUseParams())
 }
diff --git a/wasm/restlike.go b/wasm/restlike.go
index 1833abd1721d580bed12c77ba343c2199d035dab..a12d8ee19b5e6e5501174a88f94ae40ce345f955 100644
--- a/wasm/restlike.go
+++ b/wasm/restlike.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -28,17 +29,17 @@ import (
 func RestlikeRequest(_ js.Value, args []js.Value) interface{} {
 	cmixId := args[0].Int()
 	connectionID := args[1].Int()
-	request := CopyBytesToGo(args[2])
-	e2eParamsJSON := CopyBytesToGo(args[3])
+	request := utils.CopyBytesToGo(args[2])
+	e2eParamsJSON := utils.CopyBytesToGo(args[3])
 
 	msg, err := bindings.RestlikeRequest(
 		cmixId, connectionID, request, e2eParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(msg)
+	return utils.CopyBytesToJS(msg)
 }
 
 // RestlikeRequestAuth performs an authenticated restlike request.
@@ -55,15 +56,15 @@ func RestlikeRequest(_ js.Value, args []js.Value) interface{} {
 func RestlikeRequestAuth(_ js.Value, args []js.Value) interface{} {
 	cmixId := args[0].Int()
 	authConnectionID := args[1].Int()
-	request := CopyBytesToGo(args[2])
-	e2eParamsJSON := CopyBytesToGo(args[3])
+	request := utils.CopyBytesToGo(args[2])
+	e2eParamsJSON := utils.CopyBytesToGo(args[3])
 
 	msg, err := bindings.RestlikeRequestAuth(
 		cmixId, authConnectionID, request, e2eParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(msg)
+	return utils.CopyBytesToJS(msg)
 }
diff --git a/wasm/restlikeSingle.go b/wasm/restlikeSingle.go
index a3c546fd1ccf361d6534ed66e885d8cbdbccb476..bcc5e93a74d67028b0489a2e5148d1c0323b307f 100644
--- a/wasm/restlikeSingle.go
+++ b/wasm/restlikeSingle.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -21,7 +22,7 @@ type restlikeCallback struct {
 }
 
 func (rlc *restlikeCallback) Callback(payload []byte, err error) {
-	rlc.callback(CopyBytesToJS(payload), err.Error())
+	rlc.callback(utils.CopyBytesToJS(payload), err.Error())
 }
 
 // RequestRestLike sends a restlike request to a given contact.
@@ -37,17 +38,17 @@ func (rlc *restlikeCallback) Callback(payload []byte, err error) {
 //  - Throws a TypeError if parsing the parameters or making the request fails.
 func RequestRestLike(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	recipient := CopyBytesToGo(args[1])
-	request := CopyBytesToGo(args[2])
-	paramsJSON := CopyBytesToGo(args[3])
+	recipient := utils.CopyBytesToGo(args[1])
+	request := utils.CopyBytesToGo(args[2])
+	paramsJSON := utils.CopyBytesToGo(args[3])
 
 	msg, err := bindings.RequestRestLike(e2eID, recipient, request, paramsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(msg)
+	return utils.CopyBytesToJS(msg)
 }
 
 // AsyncRequestRestLike sends an asynchronous restlike request to a given
@@ -68,15 +69,15 @@ func RequestRestLike(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if parsing the parameters or making the request fails.
 func AsyncRequestRestLike(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	recipient := CopyBytesToGo(args[1])
-	request := CopyBytesToGo(args[2])
-	paramsJSON := CopyBytesToGo(args[3])
-	cb := &restlikeCallback{WrapCB(args[4], "Callback")}
+	recipient := utils.CopyBytesToGo(args[1])
+	request := utils.CopyBytesToGo(args[2])
+	paramsJSON := utils.CopyBytesToGo(args[3])
+	cb := &restlikeCallback{utils.WrapCB(args[4], "Callback")}
 
 	err := bindings.AsyncRequestRestLike(
 		e2eID, recipient, request, paramsJSON, cb)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
diff --git a/wasm/secrets.go b/wasm/secrets.go
index f604141e1b9fbdb8052b600ac2b034a51f4039e8..5a7b7127af4cfa5d5292f941553580fc672c086c 100644
--- a/wasm/secrets.go
+++ b/wasm/secrets.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -25,5 +26,5 @@ import (
 //  - secret password (Uint8Array).
 func GenerateSecret(_ js.Value, args []js.Value) interface{} {
 	secret := bindings.GenerateSecret(args[0].Int())
-	return CopyBytesToJS(secret)
+	return utils.CopyBytesToJS(secret)
 }
diff --git a/wasm/single.go b/wasm/single.go
index 3f40bf613bf15ba4fc63d328136a64897a0c66be..7a55ffa7a876b5ab60b42d683ce7e852357f5cc3 100644
--- a/wasm/single.go
+++ b/wasm/single.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -36,20 +37,20 @@ import (
 //  - Throws a TypeError if transmission fails.
 func TransmitSingleUse(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	recipient := CopyBytesToGo(args[1])
+	recipient := utils.CopyBytesToGo(args[1])
 	tag := args[2].String()
-	payload := CopyBytesToGo(args[3])
-	paramsJSON := CopyBytesToGo(args[4])
-	responseCB := &singleUseResponse{WrapCB(args[5], "Callback")}
+	payload := utils.CopyBytesToGo(args[3])
+	paramsJSON := utils.CopyBytesToGo(args[4])
+	responseCB := &singleUseResponse{utils.WrapCB(args[5], "Callback")}
 
 	report, err := bindings.TransmitSingleUse(
 		e2eID, recipient, tag, payload, paramsJSON, responseCB)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 // Listen starts a single-use listener on a given tag using the passed in E2e
@@ -67,10 +68,10 @@ func TransmitSingleUse(_ js.Value, args []js.Value) interface{} {
 //    function used to stop the listener.
 //  - Throws a TypeError if listening fails.
 func Listen(_ js.Value, args []js.Value) interface{} {
-	cb := &singleUseCallback{WrapCB(args[2], "Callback")}
+	cb := &singleUseCallback{utils.WrapCB(args[2], "Callback")}
 	api, err := bindings.Listen(args[0].Int(), args[1].String(), cb)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -114,7 +115,7 @@ type singleUseCallback struct {
 }
 
 func (suc *singleUseCallback) Callback(callbackReport []byte, err error) {
-	suc.callback(CopyBytesToJS(callbackReport), err.Error())
+	suc.callback(utils.CopyBytesToJS(callbackReport), err.Error())
 }
 
 // singleUseResponse wraps Javascript callbacks to adhere to the
@@ -124,5 +125,5 @@ type singleUseResponse struct {
 }
 
 func (sur *singleUseResponse) Callback(responseReport []byte, err error) {
-	sur.callback(CopyBytesToJS(responseReport), err.Error())
+	sur.callback(utils.CopyBytesToJS(responseReport), err.Error())
 }
diff --git a/wasm/ud.go b/wasm/ud.go
index 17627eeb6a4bcf5af32fea34d1d51dd973caf648..1c0198a753bfe5c0c2f11ccdd0ef3f06f6cd9618 100644
--- a/wasm/ud.go
+++ b/wasm/ud.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"gitlab.com/elixxir/client/bindings"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
 	"syscall/js"
 )
 
@@ -97,17 +98,17 @@ func (uns *udNetworkStatus) UdNetworkStatus() int {
 //  - Throws a TypeError if creating or loading fails.
 func NewOrLoadUd(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	follower := &udNetworkStatus{WrapCB(args[1], "UdNetworkStatus")}
+	follower := &udNetworkStatus{utils.WrapCB(args[1], "UdNetworkStatus")}
 	username := args[2].String()
-	registrationValidationSignature := CopyBytesToGo(args[3])
-	cert := CopyBytesToGo(args[4])
-	contactFile := CopyBytesToGo(args[5])
+	registrationValidationSignature := utils.CopyBytesToGo(args[3])
+	cert := utils.CopyBytesToGo(args[4])
+	contactFile := utils.CopyBytesToGo(args[5])
 	address := args[6].String()
 
 	api, err := bindings.NewOrLoadUd(e2eID, follower, username,
 		registrationValidationSignature, cert, contactFile, address)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -149,18 +150,18 @@ func NewOrLoadUd(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if getting UD from backup fails.
 func NewUdManagerFromBackup(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	follower := &udNetworkStatus{WrapCB(args[1], "UdNetworkStatus")}
-	usernameFactJson := CopyBytesToGo(args[2])
-	emailFactJson := CopyBytesToGo(args[3])
-	phoneFactJson := CopyBytesToGo(args[4])
-	cert := CopyBytesToGo(args[5])
-	contactFile := CopyBytesToGo(args[6])
+	follower := &udNetworkStatus{utils.WrapCB(args[1], "UdNetworkStatus")}
+	usernameFactJson := utils.CopyBytesToGo(args[2])
+	emailFactJson := utils.CopyBytesToGo(args[3])
+	phoneFactJson := utils.CopyBytesToGo(args[4])
+	cert := utils.CopyBytesToGo(args[5])
+	contactFile := utils.CopyBytesToGo(args[6])
 	address := args[7].String()
 
 	api, err := bindings.NewUdManagerFromBackup(e2eID, follower, usernameFactJson, emailFactJson,
 		phoneFactJson, cert, contactFile, address)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -173,7 +174,7 @@ func NewUdManagerFromBackup(_ js.Value, args []js.Value) interface{} {
 // Returns:
 //  - JSON of [fact.FactList] (Uint8Array).
 func (ud *UserDiscovery) GetFacts(js.Value, []js.Value) interface{} {
-	return CopyBytesToJS(ud.api.GetFacts())
+	return utils.CopyBytesToJS(ud.api.GetFacts())
 }
 
 // GetContact returns the marshalled bytes of the [contact.Contact] for UD as
@@ -185,11 +186,11 @@ func (ud *UserDiscovery) GetFacts(js.Value, []js.Value) interface{} {
 func (ud *UserDiscovery) GetContact(js.Value, []js.Value) interface{} {
 	c, err := ud.api.GetContact()
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(c)
+	return utils.CopyBytesToJS(c)
 }
 
 // ConfirmFact confirms a fact first registered via SendRegisterFact. The
@@ -205,7 +206,7 @@ func (ud *UserDiscovery) GetContact(js.Value, []js.Value) interface{} {
 func (ud *UserDiscovery) ConfirmFact(_ js.Value, args []js.Value) interface{} {
 	err := ud.api.ConfirmFact(args[0].String(), args[1].String())
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -228,9 +229,9 @@ func (ud *UserDiscovery) ConfirmFact(_ js.Value, args []js.Value) interface{} {
 //  - The confirmation ID (string).
 //  - Throws TypeError if sending the fact fails.
 func (ud *UserDiscovery) SendRegisterFact(_ js.Value, args []js.Value) interface{} {
-	confirmationID, err := ud.api.SendRegisterFact(CopyBytesToGo(args[0]))
+	confirmationID, err := ud.api.SendRegisterFact(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -247,9 +248,9 @@ func (ud *UserDiscovery) SendRegisterFact(_ js.Value, args []js.Value) interface
 // Returns:
 //  - Throws TypeError if deletion fails.
 func (ud *UserDiscovery) PermanentDeleteAccount(_ js.Value, args []js.Value) interface{} {
-	err := ud.api.PermanentDeleteAccount(CopyBytesToGo(args[0]))
+	err := ud.api.PermanentDeleteAccount(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -265,9 +266,9 @@ func (ud *UserDiscovery) PermanentDeleteAccount(_ js.Value, args []js.Value) int
 // Returns:
 //  - Throws TypeError if removing the fact fails.
 func (ud *UserDiscovery) RemoveFact(_ js.Value, args []js.Value) interface{} {
-	err := ud.api.RemoveFact(CopyBytesToGo(args[0]))
+	err := ud.api.RemoveFact(utils.CopyBytesToGo(args[0]))
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
@@ -285,7 +286,7 @@ type udLookupCallback struct {
 }
 
 func (ulc *udLookupCallback) Callback(contactBytes []byte, err error) {
-	ulc.callback(CopyBytesToJS(contactBytes), err.Error())
+	ulc.callback(utils.CopyBytesToJS(contactBytes), err.Error())
 }
 
 // LookupUD returns the public key of the passed ID as known by the user
@@ -305,19 +306,19 @@ func (ulc *udLookupCallback) Callback(contactBytes []byte, err error) {
 //  - Throws a TypeError if the lookup fails.
 func LookupUD(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	udContact := CopyBytesToGo(args[1])
-	cb := &udLookupCallback{WrapCB(args[2], "Callback")}
-	lookupId := CopyBytesToGo(args[3])
-	singleRequestParamsJSON := CopyBytesToGo(args[4])
+	udContact := utils.CopyBytesToGo(args[1])
+	cb := &udLookupCallback{utils.WrapCB(args[2], "Callback")}
+	lookupId := utils.CopyBytesToGo(args[3])
+	singleRequestParamsJSON := utils.CopyBytesToGo(args[4])
 
 	report, err := bindings.LookupUD(
 		e2eID, udContact, cb, lookupId, singleRequestParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -331,7 +332,7 @@ type udSearchCallback struct {
 }
 
 func (usc *udSearchCallback) Callback(contactListJSON []byte, err error) {
-	usc.callback(CopyBytesToJS(contactListJSON), err.Error())
+	usc.callback(utils.CopyBytesToJS(contactListJSON), err.Error())
 }
 
 // SearchUD searches user discovery for the passed Facts. The searchCallback
@@ -352,17 +353,17 @@ func (usc *udSearchCallback) Callback(contactListJSON []byte, err error) {
 //  - Throws a TypeError if the search fails.
 func SearchUD(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	udContact := CopyBytesToGo(args[1])
-	cb := &udSearchCallback{WrapCB(args[2], "Callback")}
-	factListJSON := CopyBytesToGo(args[3])
-	singleRequestParamsJSON := CopyBytesToGo(args[4])
+	udContact := utils.CopyBytesToGo(args[1])
+	cb := &udSearchCallback{utils.WrapCB(args[2], "Callback")}
+	factListJSON := utils.CopyBytesToGo(args[3])
+	singleRequestParamsJSON := utils.CopyBytesToGo(args[4])
 
 	report, err := bindings.SearchUD(
 		e2eID, udContact, cb, factListJSON, singleRequestParamsJSON)
 	if err != nil {
-		Throw(TypeError, err)
+		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	return CopyBytesToJS(report)
+	return utils.CopyBytesToJS(report)
 }