From e50d77d28fd866efdfce706b73320541d260728d Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <43042585+0xFirekeeper@users.noreply.github.com> Date: Fri, 7 Feb 2025 21:06:11 +0000 Subject: [PATCH] Docs up to .NET 2.17.1 / Unity 5.17.1 (#6200) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes TOOL-2991 --- ## PR-Codex overview This PR introduces enhancements to the `sidebar` structure and API documentation, adds new wallet authentication options, and refines existing content for clarity. It also includes new RPC overrides and updates to the `InAppWallet` and `EcosystemWallet` functionalities. ### Detailed summary - Added `rpcOverrides` example to `page.mdx`. - Updated `sidebar.tsx` to include "API Reference" and "Blockchain API" sections. - Replaced "Blockchain API" with "Nebula AI" in `sidebar.tsx`. - Enhanced `EcosystemWallet` and `InAppWallet` functionalities with new login options. - Clarified documentation on wallet connections and authentication methods. - Added examples for `SiweExternal` login process. - Updated various sections in `page.mdx` for better clarity and detail. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- apps/portal/src/app/dotnet/client/page.mdx | 15 ++ .../src/app/dotnet/nebula/quickstart/page.mdx | 100 ++++++++ apps/portal/src/app/dotnet/sidebar.tsx | 48 ++-- .../providers/ecosystem-wallet/page.mdx | 75 +++++- .../wallets/providers/in-app-wallet/page.mdx | 218 +++--------------- apps/portal/src/app/unity/v5/sidebar.tsx | 46 ++-- .../src/app/unity/v5/thirdwebmanager/page.mdx | 8 +- .../thirdwebmanager/thirdwebmanager_misc.png | Bin 35636 -> 33987 bytes .../thirdwebmanager_preferences.png | Bin 30095 -> 24452 bytes .../v5/wallets/ecosystem-wallet/page.mdx | 45 +++- .../unity/v5/wallets/in-app-wallet/page.mdx | 49 +++- 11 files changed, 374 insertions(+), 230 deletions(-) create mode 100644 apps/portal/src/app/dotnet/nebula/quickstart/page.mdx diff --git a/apps/portal/src/app/dotnet/client/page.mdx b/apps/portal/src/app/dotnet/client/page.mdx index abacc0c9b7c..1be5fcd715e 100644 --- a/apps/portal/src/app/dotnet/client/page.mdx +++ b/apps/portal/src/app/dotnet/client/page.mdx @@ -64,4 +64,19 @@ Options for configuring timeouts for fetching data. Useful for fine-tuning reque TimeoutOptions(int? storage = null, int? rpc = null, int? other = null) ``` +### rpcOverrides (optional) + +Overrides for the default RPC endpoints. Will use your custom endpoints instead of thirdweb RPC endpoints. + +```csharp +var client = ThirdwebClient.Create( + ..., + rpcOverrides: new() + { + { 1, "https://eth.llamarpc.com" }, + { 42161, "https://arbitrum.llamarpc.com" } + } +); +``` + diff --git a/apps/portal/src/app/dotnet/nebula/quickstart/page.mdx b/apps/portal/src/app/dotnet/nebula/quickstart/page.mdx new file mode 100644 index 00000000000..0809e12122f --- /dev/null +++ b/apps/portal/src/app/dotnet/nebula/quickstart/page.mdx @@ -0,0 +1,100 @@ +import { Details, createMetadata } from "@doc"; + +export const metadata = createMetadata({ + title: "ThirdwebContract.Create | Thirdweb .NET SDK", + description: + "Instantiate a ThirdwebContract to interact with smart contracts.", +}); + +# [Nebula AI](https://thirdweb.com/nebula) .NET Integration +The last piece of the puzzle required to create .NET apps and games leveraging a blockchain-powered AI assistant or NPC that also has the power to fully execute transactions. + +## Read, Write, Reason. +The best way to understand is to look at examples. + +We'll prepare some context for the AI - here, we'll generate a basic `PrivateKeyWallet` and upgrade it to a `SmartWallet` on Sepolia. +```csharp +// Prepare some context +var myChain = 11155111; // Sepolia +var mySigner = await PrivateKeyWallet.Generate(client); +var myWallet = await SmartWallet.Create(mySigner, myChain); +var myContractAddress = "0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8"; // DropERC1155 +var usdcAddress = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"; // Sepolia USDC +``` + +A one liner creates a Nebula session. +```csharp +// Create a Nebula session +var nebula = await ThirdwebNebula.Create(client); +``` + +You can **Chat** with Nebula. The Chat method accepts any `IThirdwebWallet` as an optional parameter, context will automatically be updated. +```csharp +// Chat, passing wallet context +var response1 = await nebula.Chat(message: "What is my wallet address?", wallet: myWallet); +Console.WriteLine($"Response 1: {response1.Message}"); +``` + +You may also pass it smart contract context. +```csharp +// Chat, passing contract context +var response2 = await nebula.Chat( + message: "What's the total supply of token id 0 for this contract?", + context: new NebulaContext(contractAddresses: new List { myContractAddress }, chainIds: new List { myChain }) +); +Console.WriteLine($"Response 2: {response2.Message}"); +``` + +You can have a full on conversation with it with our **Chat** override accepting multiple messages. +```csharp +// Chat, passing multiple messages and context +var response3 = await nebula.Chat( + messages: new List + { + new($"Tell me the name of this contract: {myContractAddress}", NebulaChatRole.User), + new("The name of the contract is CatDrop", NebulaChatRole.Assistant), + new("What's the symbol of this contract?", NebulaChatRole.User), + }, + context: new NebulaContext(contractAddresses: new List { myContractAddress }, chainIds: new List { myChain }) +); +Console.WriteLine($"Response 3: {response3.Message}"); +``` + +Chatting is cool, but what if I just want it to _**do**_ things and stop talking so much? + +You can **Execute** transactions directly with a simple prompt. +```csharp +// Execute, this directly sends transactions +var executionResult = await nebula.Execute("Approve 1 USDC to vitalik.eth", wallet: myWallet, context: new NebulaContext(contractAddresses: new List() { usdcAddress })); +if (executionResult.TransactionReceipts != null && executionResult.TransactionReceipts.Count > 0) +{ + Console.WriteLine($"Receipt: {executionResult.TransactionReceipts[0]}"); +} +else +{ + Console.WriteLine($"Message: {executionResult.Message}"); +} +``` + +Similarly, **Execute** can take in multiple messages. +```csharp +// Batch execute +var batchExecutionResult = await nebula.Execute( + new List + { + new("What's the address of vitalik.eth", NebulaChatRole.User), + new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant), + new("Approve 1 USDC to them", NebulaChatRole.User), + }, + wallet: myWallet, + context: new NebulaContext(contractAddresses: new List() { usdcAddress }) +); +if (batchExecutionResult.TransactionReceipts != null && batchExecutionResult.TransactionReceipts.Count > 0) +{ + Console.WriteLine($"Receipts: {JsonConvert.SerializeObject(batchExecutionResult.TransactionReceipts, Formatting.Indented)}"); +} +else +{ + Console.WriteLine($"Message: {batchExecutionResult.Message}"); +} +``` \ No newline at end of file diff --git a/apps/portal/src/app/dotnet/sidebar.tsx b/apps/portal/src/app/dotnet/sidebar.tsx index 4f3f2a5085d..cc7e48c45c4 100644 --- a/apps/portal/src/app/dotnet/sidebar.tsx +++ b/apps/portal/src/app/dotnet/sidebar.tsx @@ -1,6 +1,6 @@ import type { SideBar } from "@/components/Layouts/DocLayout"; import type { SidebarLink } from "@/components/others/Sidebar"; -import { ZapIcon } from "lucide-react"; +import { CodeIcon, ExternalLink, ZapIcon } from "lucide-react"; const walletProviders: SidebarLink = (() => { const parentSlug = "/dotnet/wallets/providers"; @@ -261,16 +261,29 @@ export const sidebar: SideBar = { icon: , }, { - name: "Godot Integration", - href: "/dotnet/godot", - }, - { - name: "Unity Integration", - href: "/unity/v5", + name: "API Reference", + href: "https://thirdweb-dev.github.io/dotnet/index.html", + isCollapsible: false, + icon: , }, { - name: "MAUI Integration", - href: "/dotnet/maui", + name: "Integrations", + isCollapsible: true, + icon: , + links: [ + { + name: "Unity", + href: "/unity/v5", + }, + { + name: "Godot", + href: "/dotnet/godot", + }, + { + name: "MAUI", + href: "/dotnet/maui", + }, + ], }, { name: "Core", @@ -291,7 +304,6 @@ export const sidebar: SideBar = { isCollapsible: false, links: [walletProviders, walletActions], }, - pay, { name: "Blockchain API", isCollapsible: false, @@ -304,10 +316,20 @@ export const sidebar: SideBar = { }, ], }, - { separator: true }, { - name: "Full Reference", - href: "https://thirdweb-dev.github.io/dotnet/index.html", + name: "Nebula AI", + isCollapsible: false, + links: [ + { + name: "Quickstart", + href: "/dotnet/nebula/quickstart", + }, + { + name: "Nebula Full Reference", + href: "https://thirdweb-dev.github.io/dotnet/docs/Thirdweb.AI.ThirdwebNebula.html", + }, + ], }, + pay, ], }; diff --git a/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx b/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx index 6cd94a53c05..d86fdc15a64 100644 --- a/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx +++ b/apps/portal/src/app/dotnet/wallets/providers/ecosystem-wallet/page.mdx @@ -15,11 +15,13 @@ Create an instance of `EcosystemWallet` using a user's email, phone number or OA Ecosystem Wallets support a variety of login methods: - Email (OTP Login) - Phone (OTP Login) -- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam, etc.) -- SIWE (Sign-In with Ethereum) +- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam etc.) - Custom Auth (OIDC Compatible) - Custom Auth (Generic Auth Endpoint) - Guest (Onboard easily, link other accounts later) +- Backend (Server Wallets) +- Siwe (Login with a seperate wallet supported by the SDK) +- SiweExternal (Login with an external wallet that only supports web using a browser loading a static thirdweb React page temporarily) ## Usage @@ -32,14 +34,18 @@ var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosyste var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", phoneNumber: "+1234567890"); // Google, Apple, Facebook, etc. var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.Google); -// SIWE -var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.Siwe, siweSigner: anyExternalWallet); // Custom Auth - JWT var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.JWT); // Custom Auth - AuthEndpoint var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.AuthEndpoint); // Guest Login var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.Guest); +// Server Login +var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.Backend, walletSecret: "very-secret"); +// Siwe +var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.Siwe, siweSigner: anyExternalWallet); +// SiweExternal +var wallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.my-ecosystem", authProvider: AuthProvider.SiweExternal); // Session resuming supported for all methods var isConnected = await wallet.IsConnected(); @@ -62,9 +68,6 @@ var address = await wallet.LoginWithOauth( mobileRedirectScheme: "myBundleId://" ); -// SIWE (Wallet) -var address = await siweWallet.LoginWithSiwe(chainId: 1); - // Custom Auth (JWT) var address = await wallet.LoginWithCustomAuth(jwt: "myjwt"); @@ -73,11 +76,26 @@ var address = await wallet.LoginWithAuthEndpoint(payload: "mypayload"); // Guest Login (Easy onboarding) var address = await wallet.LoginWithGuest(); + +// Backend (Server Wallets) +var address = await wallet.LoginWithBackend(); + +// SIWE (Wallet) +var address = await siweWallet.LoginWithSiwe(chainId: 1); + +// SiweExternal (React-only wallet) +var address = await wallet.LoginWithSiweExternal( + // Windows console app example, adaptable to any runtime + isMobile: false, + browserOpenAction: (url) => + { + var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; + _ = Process.Start(psi); + }, + forceWalletIds: new List { "io.metamask", "com.coinbase.wallet", "xyz.abs" } +); ``` - - This wallet is designed for client-side use in applications where direct access to the user's private keys is not safe or necessary. It leverages OTP for secure authentication, allowing users to interact with blockchain applications seamlessly. -
### client (required) @@ -108,6 +126,18 @@ The OAuth provider to use for authentication. Supported values are `AuthProvider The path to the directory where the wallet data is stored. Defaults to the application's data directory. +### siweSigner (optional) + +An external wallet instance to use for SIWE authentication. + +### legacyEncryptionKey (optional) + +The encryption key that is no longer required but was used in the past. Only pass this if you had used custom auth before this was deprecated. + +### walletSecret (optional) + +The secret identifier to use when creating server-side wallets with backend authentication. +
@@ -246,6 +276,31 @@ if (!await ecosystemWallet.IsConnected()) **Note:** The `LoginWithOauth` API allows for custom browser handling, making it suitable for various application types and platforms. +## External Wallet Auth (Siwe & SiweExternal) + +**LoginWithSiwe:** Initiate the login process by calling LoginWithSiwe on the EcosystemWallet instance. This will prompt the external wallet to sign a message instantly. + +```csharp +var address = await siweWallet.LoginWithSiwe(chainId: 1); +``` + +**LoginWithSiweExternal:** Initiate the login process by calling LoginWithSiweExternal on the EcosystemWallet instance. This will initiate a browser-based login flow for external wallets that only support web platforms. + +```csharp +var address = await wallet.LoginWithSiweExternal( + // Windows console app example, adaptable to any runtime + isMobile: false, + browserOpenAction: (url) => + { + var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; + _ = Process.Start(psi); + }, + forceWalletIds: new List { "io.metamask", "com.coinbase.wallet", "xyz.abs" } +); +``` + +Note: The parameters are similar to the OAuth flow, with the addition of `forceWalletIds` to specify the wallet IDs to force the user to use. Using a single wallet id will skip the wallet selection screen and directly open the wallet. + ## Unified Identity - Account Linking EcosystemWallet supports linking multiple authentication methods to a single user account. This feature enables users to access their account using different authentication methods, such as email, phone, or OAuth, without creating separate accounts for each method. diff --git a/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx b/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx index ee772449234..a4a924d83b3 100644 --- a/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx +++ b/apps/portal/src/app/dotnet/wallets/providers/in-app-wallet/page.mdx @@ -15,11 +15,13 @@ Create an instance of `InAppWallet` using a user's email, phone number or OAuth. In-App Wallets support a variety of login methods: - Email (OTP Login) - Phone (OTP Login) -- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch etc.) -- SIWE (Sign-In with Ethereum) +- Socials (Google, Apple, Facebook, Telegram, Farcaster, Line, Github, Twitch, Steam etc.) - Custom Auth (OIDC Compatible) - Custom Auth (Generic Auth Endpoint) - Guest (Onboard easily, link other accounts later) +- Backend (Server Wallets) +- Siwe (Login with a seperate wallet supported by the SDK) +- SiweExternal (Login with an external wallet that only supports web using a browser loading a static thirdweb React page temporarily) ## Usage @@ -30,14 +32,18 @@ var wallet = await InAppWallet.Create(client: client, email: "userEmail"); var wallet = await InAppWallet.Create(client: client, phoneNumber: "+1234567890"); // Google, Apple, Facebook, etc. var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Google); -// SIWE -var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Siwe, siweSigner: anyExternalWallet); // Custom Auth - JWT var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.JWT); // Custom Auth - AuthEndpoint var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.AuthEndpoint); // Guest Login var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Guest); +// Server Login +var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Backend, walletSecret: "very-secret"); +// Siwe +var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Siwe, siweSigner: anyExternalWallet); +// SiweExternal +var wallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.SiweExternal); // Session resuming supported for all methods var isConnected = await wallet.IsConnected(); @@ -60,9 +66,6 @@ var address = await wallet.LoginWithOauth( mobileRedirectScheme: "myBundleId://" ); -// SIWE (Wallet) -var address = await siweWallet.LoginWithSiwe(chainId: 1); - // Custom Auth (JWT) var address = await wallet.LoginWithCustomAuth(jwt: "myjwt"); @@ -71,11 +74,26 @@ var address = await wallet.LoginWithAuthEndpoint(payload: "mypayload"); // Guest Login (Easy onboarding) var address = await wallet.LoginWithGuest(); + +// Backend (Server Wallets) +var address = await wallet.LoginWithBackend(); + +// SIWE (Wallet) +var address = await siweWallet.LoginWithSiwe(chainId: 1); + +// SiweExternal (React-only wallet) +var address = await wallet.LoginWithSiweExternal( + // Windows console app example, adaptable to any runtime + isMobile: false, + browserOpenAction: (url) => + { + var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; + _ = Process.Start(psi); + }, + forceWalletIds: new List { "io.metamask", "com.coinbase.wallet", "xyz.abs" } +); ``` - - This wallet is designed for client-side use in applications where direct access to the user's private keys is not safe or necessary. It leverages OTP for secure authentication, allowing users to interact with blockchain applications seamlessly. -
### client (required) @@ -98,189 +116,29 @@ The OAuth provider to use for authentication. Supported values are `AuthProvider The path to the directory where the wallet data is stored. Defaults to the application's data directory. -
- -
- -### InAppWallet - -Returns an instance of InAppWallet, initialized for the user based on the provided email or phone number. This wallet is ready for OTP authentication and further blockchain interactions. - -
- -## OTP Authentication Flow - -The OTP authentication flow involves sending an OTP to the user's email or phone and then verifying the OTP to complete authentication: - -**Send OTP:** Initiate the login process by calling SendOTP on the InAppWallet instance. This sends an OTP to the user's email or phone number. - -```csharp -await wallet.SendOTP(); -``` - -**Submit OTP:** Once the user receives the OTP, they submit it back to the application, which then calls LoginWithOtp on the InAppWallet instance to verify the OTP and complete the login process. - -```csharp -var address = await wallet.LoginWithOtp("userEnteredOTP"); -// If this fails, feel free to catch and take in another OTP and retry the login process -``` - -## Example - -Here's an example of creating an `InAppWallet` with a user's email and completing the OTP authentication flow: - -```csharp -// Create InAppWallet wallet as signer to unlock web2 auth -var inAppWallet = await InAppWallet.Create(client: client, email: "email@email.com"); // or email: null, phoneNumber: "+1234567890" - -// Resume session (if `InAppWallet` wallet was not logged in) -if (!await inAppWallet.IsConnected()) -{ - await inAppWallet.SendOTP(); - Console.WriteLine("Please submit the OTP."); - var otp = Console.ReadLine(); - var inAppWalletAddress = await inAppWallet.LoginWithOtp(otp); // try catch and retry if needed -} - -Console.WriteLine($"InAppWallet address: {await inAppWallet.GetAddress()}"); - -// Sign a message -var message = "Hello, Thirdweb!"; -var signature = await wallet.PersonalSign(message); -Console.WriteLine($"Signature: {signature}"); -``` - -**Note:** InAppWallet leverages the security of OTP-based authentication to ensure a secure and user-friendly experience in blockchain applications. - -## OAuth Authentication Flow - -**LoginWithOauth:** Initiate the login process by calling LoginWithOauth on the InAppWallet instance. This redirects the user to the OAuth provider's login page. - -```csharp -// Windows console app example -var address = await inAppWallet.LoginWithOauth( - isMobile: false, - browserOpenAction: (url) => - { - var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; - _ = Process.Start(psi); - }, -); -// Godot standalone example -var address = await ThirdwebManager.Instance.InAppWallet.LoginWithOauth( - isMobile: OS.GetName() == "Android" || OS.GetName() == "iOS", - browserOpenAction: (url) => OS.ShellOpen(url), - mobileRedirectScheme: "thirdweb://" -); -``` - -
- -### isMobile - -A `bool` indicating whether the application is running on a mobile platform. - -### browserOpenAction +### siweSigner (optional) -An `Action` that opens the OAuth provider's login page in a browser. +An external wallet instance to use for SIWE authentication. -### mobileRedirectScheme +### legacyEncryptionKey (optional) -The redirect scheme to use for mobile platforms. Defaults to `"thirdweb://"`. +The encryption key that is no longer required but was used in the past. Only pass this if you had used custom auth before this was deprecated. -### browser +### walletSecret (optional) -An instance of `IThirdwebBrowser` to use for the OAuth login process. Defaults to `null`. - -### cancellationToken - -A `CancellationToken` to cancel the operation. Defaults to `default`. +The secret identifier to use when creating server-side wallets with backend authentication.
-### string +### InAppWallet -The InAppWallet address as a hexadecimal `string`. +Returns an instance of InAppWallet, initialized for the user based on the provided email or phone number. This wallet is ready for OTP authentication and further blockchain interactions.
-## Example - -Here's an example of creating an `InAppWallet` using OAuth. - -```csharp -// Create InAppWallet wallet as signer to unlock web2 auth -var inAppWallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Google); - -// Resume session (if `InAppWallet` wallet was not logged in) -if (!await inAppWallet.IsConnected()) -{ - try { - var address = await inAppWallet.LoginWithOauth( - isMobile: false, - browserOpenAction: (url) => - { - var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; - _ = Process.Start(psi); - }, - ); - Console.WriteLine($"OAuth login successful. InAppWallet address: {address}"); - } catch (Exception ex) { - Console.WriteLine($"OAuth login failed: {ex.Message}"); - return; - } -} -``` - -**Note:** The `LoginWithOauth` API allows for custom browser handling, making it suitable for various application types and platforms. - -## Unified Identity - Account Linking +## Authentication, Linking and more. -InAppWallet supports linking multiple authentication methods to a single user account. This feature enables users to access their account using different authentication methods, such as email, phone, or OAuth, without creating separate accounts for each method. +`InAppWallet` extends `EcosystemWallet`, refer to the [EcosystemWallet](/dotnet/wallets/providers/ecosystem-wallet) documentation for information about authentication, linking multiple auth providers to the same wallet, and more. All functionality outside of creation and third-party integrations is the same as `EcosystemWallet`. -### Linking Accounts - -```csharp -var inAppWalletMain = await InAppWallet.Create(client: client, authProvider: AuthProvider.Google); -if (!await inAppWalletMain.IsConnected()) -{ - _ = await inAppWalletMain.LoginWithOauth( - isMobile: false, - (url) => - { - var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; - _ = Process.Start(psi); - }, - "thirdweb://", - new InAppWalletBrowser() - ); -} -Console.WriteLine($"Main InAppWallet address: {await inAppWalletMain.GetAddress()}"); - -// Prepare Telegram -var socialWallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Telegram); -// Link Telegram -_ = await inAppWalletMain.LinkAccount(walletToLink: socialWallet,); - -// Prepare Phone -var phoneWallet = await InAppWallet.Create(client: client, phoneNumber: "+1234567890"); -_ = await phoneWallet.SendOTP(); -var otp = Console.ReadLine(); -// Link Phone -_ = await inAppWalletMain.LinkAccount(walletToLink: phoneWallet, otp: otp); -``` - -### Getting Linked Accounts - -```csharp -List linkedAccounts = await inAppWalletMain.GetLinkedAccounts(); -``` - -### Unlinking Accounts - -```csharp -List linkedAccounts = await inAppWallet.GetLinkedAccounts(); -List linkedAccountsAfterUnlinking = await inAppWallet.UnlinkAccount(linkedAccounts[0]); -``` \ No newline at end of file diff --git a/apps/portal/src/app/unity/v5/sidebar.tsx b/apps/portal/src/app/unity/v5/sidebar.tsx index 57f798b2b87..8e2a5894bfe 100644 --- a/apps/portal/src/app/unity/v5/sidebar.tsx +++ b/apps/portal/src/app/unity/v5/sidebar.tsx @@ -1,5 +1,5 @@ import type { SideBar } from "@/components/Layouts/DocLayout"; -import { ZapIcon } from "lucide-react"; +import { CodeIcon, ZapIcon } from "lucide-react"; const sdkSlug = "/unity/v5"; const walletProvidersSlug = `${sdkSlug}/wallets`; @@ -17,6 +17,12 @@ export const sidebar: SideBar = { href: `${sdkSlug}/getting-started`, icon: , }, + { + name: "API Reference", + href: "/dotnet", + isCollapsible: false, + icon: , + }, { name: "Core", isCollapsible: false, @@ -61,6 +67,20 @@ export const sidebar: SideBar = { }, ], }, + { + name: "Blockchain API", + isCollapsible: false, + links: [ + { + name: "Interacting with Contracts", + href: `${sdkSlug}/contracts`, + }, + { + name: "Full Reference", + href: "https://thirdweb-dev.github.io/dotnet/index.html", + }, + ], + }, { name: "Pay", isCollapsible: false, @@ -72,27 +92,19 @@ export const sidebar: SideBar = { ], }, { - name: "Blockchain API", + name: "Nebula AI", isCollapsible: false, links: [ { - name: "Interacting with Contracts", - href: `${sdkSlug}/contracts`, - }, - { separator: true }, - { - name: "Migrate from v4", - href: `${sdkSlug}/migration-guide`, - }, - { - name: ".NET SDK Portal", - href: "/dotnet", - }, - { - name: "Full Reference", - href: "https://thirdweb-dev.github.io/dotnet/index.html", + name: ".NET SDK QuickStart", + href: "/dotnet/nebula/quickstart", }, ], }, + { separator: true }, + { + name: "Migrate from v4", + href: `${sdkSlug}/migration-guide`, + }, ], }; diff --git a/apps/portal/src/app/unity/v5/thirdwebmanager/page.mdx b/apps/portal/src/app/unity/v5/thirdwebmanager/page.mdx index 1c324575d03..86683182e5c 100644 --- a/apps/portal/src/app/unity/v5/thirdwebmanager/page.mdx +++ b/apps/portal/src/app/unity/v5/thirdwebmanager/page.mdx @@ -44,7 +44,7 @@ This section allows you to define the default preferences for the SDK: - `Initialize On Awake`: Whether the SDK should initialize on awake or not. If not, you can call `ThirdwebManager.Instance.Initialize()` to initialize it manually. - `Show Debug Logs`: Whether to show thirdweb SDK debug logs. -- `Opt Out Usage Analytics`: Whether to opt out of Connect usage analytics, such as the number of wallets connected to your game. +- `Auto-Connect Last Wallet`: If enabled, we will automatically connect to the last connected wallet on initialization (this behavior does not apply to the WalletConnectWallet provider option). If a wallet was connected as a non smart wallet, then later upgraded, the smart wallet is saved as the last wallet, and the next session will autoconnect to the smart wallet. Any failure during this entire flow should not throw. ### Miscellaneous @@ -52,8 +52,10 @@ This section allows you to define the default preferences for the SDK: This section allows you to customize the SDK's behavior: -- `Supported Chains`: A list of chains that the wallets should be able to connect to by default when using `WalletConnect` as a wallet provider. -- `Redirect Page HTML Override`: Raw HTML to override the default redirect page when connecting to `InAppWallet` or `EcosystemWallet` providers. +- `RPC Overrides`: A list of RPC overrides to use when connecting to a chain. You can bypass using thirdweb's RPC that way (not recommended). +- `OAuth Redirect Page HTML Override`: Raw HTML to override the default redirect page when connecting to `InAppWallet` or `EcosystemWallet` providers. +- `WalletConnect Supported Chains`: A list of chains that the wallets should be able to connect to by default when using `WalletConnect` as a wallet provider. +- `WalletConnect Included Wallet IDs`: A list of wallet IDs that should be included in the WalletConnect modal by default (optional override). ### Debug diff --git a/apps/portal/src/app/unity/v5/thirdwebmanager/thirdwebmanager_misc.png b/apps/portal/src/app/unity/v5/thirdwebmanager/thirdwebmanager_misc.png index a46d1770537617ef3855c4b8eb805c63eeca0968..16b9a3916073db38412a33e7e0314c180259d309 100644 GIT binary patch literal 33987 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 Hy7<2Ud@JW% literal 35636 zcmd43XEa=I_%57If~YZ3Vi-My5Z&mbMVUxZqD2d$6O1|u!VJ+{v=Jpiv?$SQ5Ti$l zE@G6?nb9)pyXE&kXPqza$FtV?V9j1*+xsc^^E}se-PaBMNLQWe2J?*z7cNj~YQP>} zxNr$`;ljlZ@@v31#$zNI;N!ZhhLOjG3p6_CKNoG^U;K07Limy)gmGFCigOPX9)uUuldp!xB=;>EY_{nA!bY*f!K)( z+xpRBWAz?#`^n0JqiMW6o>|g4{lS85y`j&)$3RT)YD4pM(w0t751Q+dXGgQ#L_#OH ze$33TPj0*A@}T?s!jigfQ;$CJ&APRc9wuUiX%Y0lMhXG1^H}OdhrfJ3d$Q^6+!}P1 zyBjarM2O5cNxC-G(cL3Cpm5gUw^@tNw$FC8AIyH|cTDJ%>#Ec*eqMY2bA~hqj(zPmmNU90>W*yP%{wx%;dj7z$Za+rm?51Iq{U=8|CgqMEmTHedYo*{_J=MIj!D&Qef=wiawe;JMJqEw2D0XV*su}9`AsSNUZYy zh)O}e2!t8(WN%{N6B@s}GCWh~xfX8I`$5Ec`DfPYP8_#Zwhj9g^=LNFu$y<>qviGu z#o&|z-YY|&26u_utA(Y9##}=?VSW(rN*qwQBIyLe~HtU9MVgDie;!nok$yPZf}^ zQ}7Mf;vdlEz|(s38g-sZPru*4*G^BP2SVp3c=Q<;?qVnjXNP0WUXs5NIp+PUP-<4G zKhlWpk4F;&fk~#<=;5OvUJPm~TQy?(n6xt!a4-Yvqi^drt#ULHYlO06xPM899J