Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions apps/portal/src/app/dotnet/client/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
}
);
```

</Details>
100 changes: 100 additions & 0 deletions apps/portal/src/app/dotnet/nebula/quickstart/page.mdx
Original file line number Diff line number Diff line change
@@ -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<string> { myContractAddress }, chainIds: new List<BigInteger> { 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<NebulaChatMessage>
{
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<string> { myContractAddress }, chainIds: new List<BigInteger> { 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<string>() { 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<NebulaChatMessage>
{
new("What's the address of vitalik.eth", NebulaChatRole.User),
new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Ethereum address contains an invalid character G in 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298. The correct address for vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7A3a3298. This typo would cause the transaction to fail since Ethereum addresses must only contain hexadecimal characters (0-9, a-f).

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

new("Approve 1 USDC to them", NebulaChatRole.User),
},
wallet: myWallet,
context: new NebulaContext(contractAddresses: new List<string>() { usdcAddress })
);
if (batchExecutionResult.TransactionReceipts != null && batchExecutionResult.TransactionReceipts.Count > 0)
{
Console.WriteLine($"Receipts: {JsonConvert.SerializeObject(batchExecutionResult.TransactionReceipts, Formatting.Indented)}");
}
else
{
Console.WriteLine($"Message: {batchExecutionResult.Message}");
}
```
48 changes: 35 additions & 13 deletions apps/portal/src/app/dotnet/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -261,16 +261,29 @@ export const sidebar: SideBar = {
icon: <ZapIcon />,
},
{
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: <ExternalLink />,
},
{
name: "MAUI Integration",
href: "/dotnet/maui",
name: "Integrations",
isCollapsible: true,
icon: <CodeIcon />,
links: [
{
name: "Unity",
href: "/unity/v5",
},
{
name: "Godot",
href: "/dotnet/godot",
},
{
name: "MAUI",
href: "/dotnet/maui",
},
],
},
{
name: "Core",
Expand All @@ -291,7 +304,6 @@ export const sidebar: SideBar = {
isCollapsible: false,
links: [walletProviders, walletActions],
},
pay,
{
name: "Blockchain API",
isCollapsible: false,
Expand All @@ -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,
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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();
Expand All @@ -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");

Expand All @@ -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<string> { "io.metamask", "com.coinbase.wallet", "xyz.abs" }
);
```

<Callout variant="info" title="Client-Side Use">
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.
</Callout>
<Details summary="Parameters">

### client (required)
Expand Down Expand Up @@ -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.

</Details>

<Details summary="Return Value">
Expand Down Expand Up @@ -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<string> { "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.
Expand Down
Loading
Loading