diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryConfirmFact.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryConfirmFact.swift
new file mode 100644
index 0000000000000000000000000000000000000000..cad19c795866f6ee0086b3901692fa9638f9ef94
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryConfirmFact.swift
@@ -0,0 +1,25 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoveryConfirmFact {
+  public var run: (String, String) throws -> Void
+
+  public func callAsFunction(
+    confirmationId: String,
+    code: String
+  ) throws {
+    try run(confirmationId, code)
+  }
+}
+
+extension UserDiscoveryConfirmFact {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoveryConfirmFact {
+    UserDiscoveryConfirmFact(run: bindingsUD.confirmFact(_:code:))
+  }
+}
+
+extension UserDiscoveryConfirmFact {
+  public static let unimplemented = UserDiscoveryConfirmFact(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetContact.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetContact.swift
new file mode 100644
index 0000000000000000000000000000000000000000..19d7fb76c0a17fdcf6374f00eedad8be2d68ceba
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetContact.swift
@@ -0,0 +1,23 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoveryGetContact {
+  public var run: () throws -> Data
+
+  public func callAsFunction() throws -> Data {
+    try run()
+  }
+}
+
+extension UserDiscoveryGetContact {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoveryGetContact {
+    UserDiscoveryGetContact(run: bindingsUD.getContact)
+  }
+}
+
+extension UserDiscoveryGetContact {
+  public static let unimplemented = UserDiscoveryGetContact(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
+
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetFacts.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetFacts.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a6029083f85603c8c7c6590562f75aa5f36b061a
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetFacts.swift
@@ -0,0 +1,27 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoveryGetFacts {
+  public var run: () throws -> [Fact]
+
+  public func callAsFunction() throws -> [Fact] {
+    try run()
+  }
+}
+
+extension UserDiscoveryGetFacts {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoveryGetFacts {
+    UserDiscoveryGetFacts {
+      guard let data = bindingsUD.getFacts() else {
+        fatalError("BindingsUserDiscovery.getFacts returned `nil`")
+      }
+      return try [Fact].decode(data)
+    }
+  }
+}
+
+extension UserDiscoveryGetFacts {
+  public static let unimplemented = UserDiscoveryGetFacts(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetId.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetId.swift
new file mode 100644
index 0000000000000000000000000000000000000000..77de9ad9d6dcc6d4db4fe936a257b9fd8352e190
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryGetId.swift
@@ -0,0 +1,23 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoveryGetId {
+  public var run: () -> Int
+
+  public func callAsFunction() -> Int {
+    run()
+  }
+}
+
+extension UserDiscoveryGetId {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoveryGetId {
+    UserDiscoveryGetId(run: bindingsUD.getID)
+  }
+}
+
+extension UserDiscoveryGetId {
+  public static let unimplemented = UserDiscoveryGetId(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
+
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryPermanentDeleteAccount.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryPermanentDeleteAccount.swift
new file mode 100644
index 0000000000000000000000000000000000000000..3f570e1e1919c2fe6887b232c65a7eb28c9d77ad
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryPermanentDeleteAccount.swift
@@ -0,0 +1,24 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoveryPermanentDeleteAccount {
+  public var run: (Fact) throws -> Void
+
+  public func callAsFunction(_ fact: Fact) throws {
+    try run(fact)
+  }
+}
+
+extension UserDiscoveryPermanentDeleteAccount {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoveryPermanentDeleteAccount {
+    UserDiscoveryPermanentDeleteAccount { fact in
+      try bindingsUD.permanentDeleteAccount(fact.encode())
+    }
+  }
+}
+
+extension UserDiscoveryPermanentDeleteAccount {
+  public static let unimplemented = UserDiscoveryPermanentDeleteAccount(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryRemoveFact.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryRemoveFact.swift
new file mode 100644
index 0000000000000000000000000000000000000000..3996ba446981172e0e24477c92fd032b377eb53a
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoveryRemoveFact.swift
@@ -0,0 +1,24 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoveryRemoveFact {
+  public var run: (Fact) throws -> Void
+
+  public func callAsFunction(_ fact: Fact) throws {
+    try run(fact)
+  }
+}
+
+extension UserDiscoveryRemoveFact {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoveryRemoveFact {
+    UserDiscoveryRemoveFact { fact in
+      try bindingsUD.removeFact(fact.encode())
+    }
+  }
+}
+
+extension UserDiscoveryRemoveFact {
+  public static let unimplemented = UserDiscoveryRemoveFact(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoverySendRegisterFact.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoverySendRegisterFact.swift
new file mode 100644
index 0000000000000000000000000000000000000000..78c9a77fd99e1d292bbc1ae2263668ab3bc11f53
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoverySendRegisterFact.swift
@@ -0,0 +1,29 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoverySendRegisterFact {
+  public var run: (Fact) throws -> String
+
+  public func callAsFunction(_ fact: Fact) throws -> String {
+    try run(fact)
+  }
+}
+
+extension UserDiscoverySendRegisterFact {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoverySendRegisterFact {
+    UserDiscoverySendRegisterFact { fact in
+      var error: NSError?
+      let confirmationId = bindingsUD.sendRegisterFact(try fact.encode(), error: &error)
+      if let error = error {
+        throw error
+      }
+      return confirmationId
+    }
+  }
+}
+
+extension UserDiscoverySendRegisterFact {
+  public static let unimplemented = UserDiscoverySendRegisterFact(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoverySetAlternative.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoverySetAlternative.swift
new file mode 100644
index 0000000000000000000000000000000000000000..731518eb8868f40d77639e55306e345d8a79be36
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/Functors/UserDiscoverySetAlternative.swift
@@ -0,0 +1,33 @@
+import Bindings
+import XCTestDynamicOverlay
+
+public struct UserDiscoverySetAlternative {
+  public var run: (UserDiscoveryConfig?) throws -> Void
+
+  public func callAsFunction(_ config: UserDiscoveryConfig?) throws {
+    try run(config)
+  }
+}
+
+extension UserDiscoverySetAlternative {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscoverySetAlternative {
+    UserDiscoverySetAlternative { config in
+      if let config = config {
+        try bindingsUD.setAlternative(
+          config.cert,
+          altAddress: config.address,
+          contactFile: config.contact
+        )
+      } else {
+        try bindingsUD.unsetAlternativeUserDiscovery()
+      }
+    }
+  }
+}
+
+extension UserDiscoverySetAlternative {
+  public static let unimplemented = UserDiscoverySetAlternative(
+    run: XCTUnimplemented("\(Self.self)")
+  )
+}
+
diff --git a/Sources/ElixxirDAppsSDK/UserDiscovery/UserDiscovery.swift b/Sources/ElixxirDAppsSDK/UserDiscovery/UserDiscovery.swift
new file mode 100644
index 0000000000000000000000000000000000000000..a0935d810b19e6145274edd6d301901ceb36e31c
--- /dev/null
+++ b/Sources/ElixxirDAppsSDK/UserDiscovery/UserDiscovery.swift
@@ -0,0 +1,40 @@
+import Bindings
+
+public struct UserDiscovery {
+  public var setAlternative: UserDiscoverySetAlternative
+  public var getId: UserDiscoveryGetId
+  public var getContact: UserDiscoveryGetContact
+  public var getFacts: UserDiscoveryGetFacts
+  public var sendRegisterFact: UserDiscoverySendRegisterFact
+  public var confirmFact: UserDiscoveryConfirmFact
+  public var removeFact: UserDiscoveryRemoveFact
+  public var permanentDeleteAccount: UserDiscoveryPermanentDeleteAccount
+}
+
+extension UserDiscovery {
+  public static func live(_ bindingsUD: BindingsUserDiscovery) -> UserDiscovery {
+    UserDiscovery(
+      setAlternative: .live(bindingsUD),
+      getId: .live(bindingsUD),
+      getContact: .live(bindingsUD),
+      getFacts: .live(bindingsUD),
+      sendRegisterFact: .live(bindingsUD),
+      confirmFact: .live(bindingsUD),
+      removeFact: .live(bindingsUD),
+      permanentDeleteAccount: .live(bindingsUD)
+    )
+  }
+}
+
+extension UserDiscovery {
+  public static let unimplemented = UserDiscovery(
+    setAlternative: .unimplemented,
+    getId: .unimplemented,
+    getContact: .unimplemented,
+    getFacts: .unimplemented,
+    sendRegisterFact: .unimplemented,
+    confirmFact: .unimplemented,
+    removeFact: .unimplemented,
+    permanentDeleteAccount: .unimplemented
+  )
+}