Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
44ce9eb
restructure repository to plugin as root
dirtycajunrice Jul 27, 2024
3563aa8
Cleanup plugin general definitions
dirtycajunrice Jul 27, 2024
760efcd
minimal conversion from actor to subsystem
dirtycajunrice Jul 27, 2024
79b00df
simplify library operation flow
dirtycajunrice Jul 27, 2024
ca725db
Progress Push
dirtycajunrice Jul 27, 2024
3916229
Finish initial refactor
dirtycajunrice Jul 28, 2024
21d7aa2
minor cleanup of metadata
dirtycajunrice Jul 28, 2024
c515de0
Review Changes
dirtycajunrice Jul 30, 2024
08e1ac2
Refactor:
dirtycajunrice Jul 30, 2024
fa258f1
OAuth flow added to InApp Wallet widget
dirtycajunrice Aug 3, 2024
8803374
Added most of smart wallet implementation, waiting on create function…
dirtycajunrice Aug 3, 2024
98c80a8
Fix get_default_headers panic
0xFirekeeper Aug 4, 2024
28e6c99
rust utils
0xFirekeeper Aug 4, 2024
5518159
Improved AccountPermissions DX
0xFirekeeper Aug 7, 2024
065ab68
Update thirdweb.lib
0xFirekeeper Aug 7, 2024
d9f4643
Final sweep!
dirtycajunrice Aug 7, 2024
7fd00dc
remade otp input key to handle focus better
dirtycajunrice Aug 8, 2024
845202d
update error messages
0xFirekeeper Aug 8, 2024
075921d
Fix packaging errors
0xFirekeeper Aug 9, 2024
3701482
update category to show thirdweb
dirtycajunrice Aug 9, 2024
c4d7559
cleanup to support linux+linuxarm64+mac+ios+tvos
dirtycajunrice Aug 24, 2024
7997c05
Add libs, update gitignore, lfs libs
dirtycajunrice Aug 26, 2024
4297f4a
Merge remote-tracking branch 'origin/main'
dirtycajunrice Aug 26, 2024
f58479a
Merge branch 'main' into feat/cross-platform
dirtycajunrice Aug 26, 2024
99cf5a9
update uplugin post-merge
dirtycajunrice Aug 26, 2024
040aa9b
update readme
dirtycajunrice Aug 26, 2024
3ca4810
Migrate to using internal browser, update async task, expose oauth fl…
dirtycajunrice Aug 26, 2024
f9e4a24
add webbrowserwidget to required plugin deps in uplugin
dirtycajunrice Aug 26, 2024
cec1908
fix urldecode input
dirtycajunrice Aug 26, 2024
cec0555
remove latant onSuccess
dirtycajunrice Aug 26, 2024
f0a71e0
add android to allowed targets, with build.cs and lib
dirtycajunrice Aug 26, 2024
11673bc
Update all builds
0xFirekeeper Aug 26, 2024
e2fe938
migrate web browser plugin to local
dirtycajunrice Aug 30, 2024
d4301b4
update readme
dirtycajunrice Aug 30, 2024
5493259
add missing headers for build
dirtycajunrice Aug 30, 2024
0eaeb51
remove android / ios from supported list, and mark ready for merge
dirtycajunrice Aug 30, 2024
678106b
disable transparency of browser (uwidget) to address latent transpare…
dirtycajunrice Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# Auto detect text files and perform LF normalization
* text=auto
*.a filter=lfs diff=lfs merge=lfs -text
*.lib filter=lfs diff=lfs merge=lfs -text
12 changes: 8 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Config/
*.a
*.lib

# Don't ignore Thirdparty libs
!Source/ThirdParty/**/*.a
!Source/ThirdParty/**/*.lib

# Executables
*.exe
*.out
Expand Down Expand Up @@ -58,10 +62,10 @@ Plugins/*/Binaries/*
# Builds
Build/*

# Whitelist PakBlacklist-<BuildConfiguration>.txt files
# Allowlist PakDenylist-<BuildConfiguration>.txt files
!Build/*/
Build/*/**
!Build/*/PakBlacklist*.txt
!Build/*/PakDenylist*.txt

# Don't ignore icon files in Build
!Build/**/*.ico
Expand All @@ -79,5 +83,5 @@ Plugins/*/Intermediate/*
# Cache files for the editor to use
DerivedDataCache/*

# Include specific thirdweb Rust library file
!Plugins/Thirdweb/lib/thirdweb.lib
# ignore ue4cli dist folder
dist/
Binary file modified Content/Examples/Widgets/WBP_Thirdweb_InApp.uasset
Binary file not shown.
62 changes: 55 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,72 @@
![unreal-featuregraphic](https://github.com/user-attachments/assets/a3d3a83f-fbbe-4ad1-9c68-b115f8992087)
![unreal-hero](https://github.com/user-attachments/assets/a3d3a83f-fbbe-4ad1-9c68-b115f8992087)

[<img alt="Unreal Documentation" src="https://img.shields.io/badge/Unreal SDK-Documentation-red?logo=unreal&style=for-the-badge" height="30">](https://portal.thirdweb.com/unreal)
[![Static Badge](https://img.shields.io/badge/Unreal%20SDK-Documentation-red?style=for-the-badge)][documentation-url]
[![Static Badge](https://img.shields.io/badge/Unreal%20SDK-Marketplace-purple?style=for-the-badge)][marketplace-url]

# Thirdweb Unreal Engine SDK Plugin

A Code Plugin for Unreal Engine that enables developers to create thirdweb Private Key Wallets, In-App Wallets and Smart Wallets for their games and applications. The plugin provides a simple API and blueprints to interact with wallets, login with email or socials as well as create Smart Wallet session keys.
A Code Plugin for Unreal Engine that enables developers to create thirdweb Private Key Wallets, In-App Wallets and
Smart Wallets for their games and applications. The plugin provides a simple API and blueprints to interact with
wallets, login with email or socials as well as create Smart Wallet session keys.

With this plugin, you can keep the onboarding of your users in-client and grant a session key to your [thirdweb Engine](https://portal.thirdweb.com/engine) powered backend to interact with the blockchain, no signature in sight.
With this plugin, you can keep the onboarding of your users in-client and grant a session key to your
[thirdweb Engine](https://portal.thirdweb.com/engine) powered backend to interact with the blockchain,
no signature in sight.

The Thirdweb Unreal Plugin is distributed as an [Unreal Marketplace Code Plugin](https://www.unrealengine.com/marketplace/en-US/product/f21200c2610146f3888172994448e50d).
## Supported Unreal Engine Versions

Thirdweb adheres to Unreal Engine's Marketplace Guidelines in supporting the 3 most recent versions[^1].
We aim to release an updated plugin version as expediently as possible once a minor version change has released.
If you or your company require compatibility with an older version, please reach out to [support][support-url].

## Installation

The Unreal SDK is distributed as an [Unreal Marketplace Plugin](https://www.unrealengine.com/marketplace/).
In addition to the source code provided, the plugin is available on the [Unreal Marketplace][marketplace-url].

The plugin's core logic is built from Rust, making it lightning-fast and minimizing the clutter in your Unreal project.

It currently is built to support Win64 platforms and was tested in UE 5.4.
## Packaging

Windows, Linux, and Android all package natively and can be used in blueprint-only projects

### Mac

The plugin currently makes use of the [Web Browser][web-browser-doc] runtime module which bundles [CEF3][cef-forum].
Changes to the engine are currently required for modern xcode targets as Unreal Engine does not properly link
the framework during packaging. This requires either an engine patch or moving the framework post-build and
then codesigning your build again.

In the [CEF3.build.cs][cef3-build-cs] you need to bypass the `if` check for modern xcode. This can be accomplished by
adding `|| true` to the end of the if check on line 102.

Additionally, you need to add `bCompileCEF3 = true;` to your build target

## Supported Platforms & Architectures

| Platform | x64 | arm64 |
|----------|:---:|:-----:|
| Windows | ✅ | ➖ |
| Linux | ✅ | ✅ |
| Mac | ✅ | ✅ |
| IOS | ➖ | ❌ |
| TVOS | ➖ | ❌ |
| VisionOS | ➖ | ❌ |
| Android | ➖ | ❌ |

Legend: ✅ Supported | ❌ Unsupported | ➖ Not applicable




## Documentation

Documentation is available at https://portal.thirdweb.com/unreal

[^1]: Unreal Engine's Marketplace Guidelines - Section 3.1.b https://www.unrealengine.com/en-US/marketplace-guidelines#31b

[support-url]: https://thirdweb.com/support
[documentation-url]: https://portal.thirdweb.com/unreal
[marketplace-url]: https://www.unrealengine.com/marketplace/en-US/product/f21200c2610146f3888172994448e50d
[web-browser-doc]: https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/WebBrowser
[cef-forum]: https://www.magpcss.org/ceforum/index.php
[cef3-build-cs]: https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/ThirdParty/CEF3/CEF3.build.cs#L102
3 changes: 3 additions & 0 deletions Source/ThirdParty/Android/libthirdweb.a
Git LFS file not shown
3 changes: 3 additions & 0 deletions Source/ThirdParty/IOS/libthirdweb.a
Git LFS file not shown
3 changes: 3 additions & 0 deletions Source/ThirdParty/Linux/libthirdweb.a
Git LFS file not shown
3 changes: 3 additions & 0 deletions Source/ThirdParty/LinuxARM64/libthirdweb.a
Git LFS file not shown
3 changes: 3 additions & 0 deletions Source/ThirdParty/Mac/libthirdweb.a
Git LFS file not shown
3 changes: 3 additions & 0 deletions Source/ThirdParty/Win64/thirdweb.lib
Git LFS file not shown
114 changes: 14 additions & 100 deletions Source/Thirdweb/Private/AsyncTasks/AsyncTaskThirdwebLoginWithOAuth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,132 +2,46 @@

#include "AsyncTasks/AsyncTaskThirdwebLoginWithOAuth.h"

#include "HttpServerModule.h"
#include "IHttpRouter.h"
#include "ThirdwebLog.h"
#include "ThirdwebOAuthBrowserUserWidget.h"
#include "ThirdwebSubsystem.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"
#include "Misc/DateTime.h"
#include "TimerManager.h"
#include "Blueprint/UserWidget.h"

void UAsyncTaskThirdwebLoginWithOAuth::Activate()
{
// Fetch the OAuth login URL
FString LoginURL;
if (FString Error; !Wallet.FetchOAuthLoginURL(TEXT("http://localhost:8789/callback"), LoginURL, Error))
{
return HandleFailed(Error);
}

// Ensure the HTTP server module is loaded
EModuleLoadResult ModuleResult;
FModuleManager::Get().LoadModuleWithFailureReason(FName(TEXT("HTTPServer")), ModuleResult);
if (ModuleResult != EModuleLoadResult::Success)
{
return HandleFailed(TEXT("Failed to load HTTPServer"));
}

Router = FHttpServerModule::Get().GetHttpRouter(8789, true);
if (!Router.IsValid())
{
return HandleFailed(TEXT("Failed to get HTTP Router"));
}

AuthEvent = FPlatformProcess::GetSynchEventFromPool(false);
RouteHandle = Router->BindRoute(FHttpPath(TEXT("/callback")), EHttpServerRequestVerbs::VERB_GET, FHttpRequestHandler::CreateUObject(this, &ThisClass::CallbackRequestHandler));

if (!RouteHandle.IsValid())
{
FPlatformProcess::ReturnSynchEventToPool(AuthEvent);
return HandleFailed(TEXT("Failed to bind route"));
}

// Start the HTTP server
FHttpServerModule::Get().StartAllListeners();
TW_LOG(Verbose, TEXT("HTTP Server started and listening on port 8789"));

// Open the browser with the login URL
FPlatformProcess::LaunchURL(*LoginURL, nullptr, nullptr);
TW_LOG(Log, TEXT("Browser opened with URL: %s"), *LoginURL);

// Start a timer to periodically check for the OAuth completion
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::CheckOAuthCompletion);
Browser->OnSuccess.AddDynamic(this, &ThisClass::HandleSuccess);
Browser->OnError.AddDynamic(this, &ThisClass::HandleFailed);
Browser->AddToViewport(10000);
Browser->Authenticate(Wallet);
}

UAsyncTaskThirdwebLoginWithOAuth *UAsyncTaskThirdwebLoginWithOAuth::LoginWithOAuth(UObject *WorldContextObject, const FWalletHandle &Wallet)
UAsyncTaskThirdwebLoginWithOAuth* UAsyncTaskThirdwebLoginWithOAuth::LoginWithOAuth(UObject* WorldContextObject, const FWalletHandle& Wallet)
{
if (!WorldContextObject)
{
return nullptr;
}
UAsyncTaskThirdwebLoginWithOAuth *Task = NewObject<UAsyncTaskThirdwebLoginWithOAuth>(WorldContextObject);
UAsyncTaskThirdwebLoginWithOAuth* Task = NewObject<UAsyncTaskThirdwebLoginWithOAuth>(WorldContextObject);
Task->Wallet = Wallet;
Task->Browser = CreateWidget<UThirdwebOAuthBrowserUserWidget>(UGameplayStatics::GetGameInstance(WorldContextObject), UThirdwebOAuthBrowserUserWidget::StaticClass());
Task->RegisterWithGameInstance(WorldContextObject);
return Task;
}

void UAsyncTaskThirdwebLoginWithOAuth::CheckOAuthCompletion()
{
if (!bAuthComplete)
{
// Continue checking
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::CheckOAuthCompletion);
return;
}

// Stop the HTTP listener
FHttpServerModule::Get().StopAllListeners();
TW_LOG(Verbose, TEXT("HTTP Server stopped listening"));

// Unbind the route
Router->UnbindRoute(RouteHandle);
TW_LOG(Verbose, TEXT("Route unbound"));

FPlatformProcess::ReturnSynchEventToPool(AuthEvent);

// Set the results based on the authentication
if (bAuthComplete && !OAuthResult.IsEmpty())
{
FString Error;
if (Wallet.SignInWithOAuth(OAuthResult, Error))
{
return HandleSuccess(TEXT("Successfully signed in with OAuth."));
}
return HandleFailed(FString::Printf(TEXT("OAuth login flow failed: %s"), *Error));
}
HandleFailed(TEXT("OAuth login flow did not complete in time"));
}

bool UAsyncTaskThirdwebLoginWithOAuth::CallbackRequestHandler(const FHttpServerRequest &Request, const FHttpResultCallback &OnComplete)
{
OAuthResult = Request.QueryParams.FindRef(TEXT("authResult"));

if (OAuthResult.IsEmpty())
{
FString Error = TEXT("AuthResult query parameter is missing.");
TUniquePtr<FHttpServerResponse> Response = FHttpServerResponse::Create(Error, TEXT("text/plain"));
OnComplete(MoveTemp(Response));
HandleFailed(Error);
}
else
{
bAuthComplete = true;
AuthEvent->Trigger();
TUniquePtr<FHttpServerResponse> Response = FHttpServerResponse::Create(TEXT("<html><body><h1>DONE!</h1><p>You can close this tab/window now.</p></body></html>"), TEXT("text/html"));
OnComplete(MoveTemp(Response));
}
return true;
}

void UAsyncTaskThirdwebLoginWithOAuth::HandleFailed(const FString &Error)
void UAsyncTaskThirdwebLoginWithOAuth::HandleFailed(const FString& Error)
{
Failed.Broadcast(Error);
Browser->RemoveFromParent();
SetReadyToDestroy();
}

void UAsyncTaskThirdwebLoginWithOAuth::HandleSuccess(const FString &Output)
void UAsyncTaskThirdwebLoginWithOAuth::HandleSuccess()
{
Success.Broadcast(Output);
Success.Broadcast(TEXT(""));
Browser->RemoveFromParent();
SetReadyToDestroy();
}
4 changes: 2 additions & 2 deletions Source/Thirdweb/Private/Thirdweb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "Thirdweb.h"
#include "ThirdwebLog.h"

bool Thirdweb::FFIResult::AssignResult(FString& Output, bool bErrorOnlyResult) const
bool Thirdweb::FFIResult::AssignResult(FString& Output, const bool bErrorOnlyResult) const
{
Log();
bool bSuccess = success;
Expand All @@ -17,7 +17,7 @@ bool Thirdweb::FFIResult::AssignResult(FString& Output, bool bErrorOnlyResult) c
return bSuccess;
}

bool Thirdweb::FFIResult::AssignRetryResult(bool& bCanRetry, FString& Output, bool bErrorOnlyResult) const
bool Thirdweb::FFIResult::AssignRetryResult(bool& bCanRetry, FString& Output, const bool bErrorOnlyResult) const
{
Log();
bool bSuccess = success;
Expand Down
28 changes: 28 additions & 0 deletions Source/Thirdweb/Private/ThirdwebFunctionLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "Thirdweb.h"
#include "ThirdwebCommon.h"
#include "ThirdwebSigner.h"
#include "ThirdwebWalletHandle.h"

FWalletHandle UThirdwebFunctionLibrary::Conv_StringToWalletHandle(FString PrivateKey)
Expand Down Expand Up @@ -68,6 +69,11 @@ EFunctionResult UThirdwebFunctionLibrary::BP_FetchOAuthLoginLink(FWalletHandle W
return Wallet.FetchOAuthLoginURL(RedirectUrl, LoginLink, Error) ? EFunctionResult::Success : EFunctionResult::Failed;
}

EFunctionResult UThirdwebFunctionLibrary::BP_SignInWithOAuth(FWalletHandle Wallet, const FString& AuthResult, FString& Error)
{
return Wallet.SignInWithOAuth(AuthResult, Error) ? EFunctionResult::Success : EFunctionResult::Failed;
}

bool UThirdwebFunctionLibrary::BP_WalletIsValid(const FWalletHandle& Wallet)
{
return Wallet.IsValid();
Expand Down Expand Up @@ -166,3 +172,25 @@ FText UThirdwebFunctionLibrary::Conv_TextAddressToStringChecksummedAddress(const
{
return Address.IsEmpty() ? FText::GetEmpty() : FText::FromString(Thirdweb::ToChecksummedAddress(Address.ToString()));
}

bool UThirdwebFunctionLibrary::BP_IsActiveSigner(FWalletHandle Wallet, const FString& BackendWallet)
{
FString Error;
if (bool bDeployed; Wallet.IsDeployed(bDeployed, Error))
{
if (bDeployed)
{
if (TArray<FSigner> Signers; Wallet.GetActiveSigners(Signers, Error))
{
for (int i = 0; i < Signers.Num(); i++)
{
if (Signers[i].Address.Equals(BackendWallet, ESearchCase::IgnoreCase))
{
return true;
}
}
}
}
}
return false;
}
Loading