├── toc.yml ├── .gitattributes ├── Thirdweb.Tests ├── GlobalUsings.cs ├── xunit.runner.json ├── Thirdweb.Tests.csproj ├── BaseTests.cs ├── Thirdweb.Client │ └── Thirdweb.Client.Tests.cs ├── Thirdweb.Wallets │ └── Thirdweb.Wallets.Tests.cs ├── Thirdweb.Transactions │ └── Thirdweb.ZkSmartWallet.Tests.cs ├── Thirdweb.RPC │ └── Thirdweb.RPC.Tests.cs └── Thirdweb.Storage │ └── Thirdweb.Storage.Tests.cs ├── resources ├── icon.png └── favicon.ico ├── codecov.yml ├── global.json ├── Thirdweb ├── Thirdweb.Wallets │ ├── InAppWallet │ │ ├── Thirdweb.EWS │ │ │ ├── EmbeddedWallet │ │ │ │ ├── EmbeddedWallet.JWT.cs │ │ │ │ ├── EmbeddedWallet.Guest.cs │ │ │ │ ├── EmbeddedWallet.Backend.cs │ │ │ │ ├── EmbeddedWallet.AuthEndpoint.cs │ │ │ │ ├── EmbeddedWallet.PhoneOTP.cs │ │ │ │ ├── EmbeddedWallet.OAuth.cs │ │ │ │ ├── EmbeddedWallet.EmailOTP.cs │ │ │ │ ├── EmbeddedWallet.Misc.cs │ │ │ │ ├── EmbeddedWallet.SIWE.cs │ │ │ │ ├── EmbeddedWallet.AccountLinking.cs │ │ │ │ └── EmbeddedWallet.cs │ │ │ ├── EmbeddedWallet.Storage │ │ │ │ ├── LocalStorage.Types.cs │ │ │ │ └── LocalStorage.cs │ │ │ └── EmbeddedWallet.Authentication │ │ │ │ └── Server.Types.cs │ │ ├── InAppWallet.Types.cs │ │ ├── IThirdwebBrowser.cs │ │ ├── EcosystemWallet │ │ │ └── EcosystemWallet.Types.cs │ │ ├── InAppWallet.cs │ │ └── InAppWalletBrowser.cs │ ├── ServerWallet │ │ └── ServerWallet.Types.cs │ ├── SmartWallet │ │ └── Thirdweb.AccountAbstraction │ │ │ └── ThirdwebBundler.cs │ └── IThirdwebWallet.cs ├── Thirdweb.Storage │ ├── ThirdwebStorage.Types.cs │ └── ThirdwebStorage.cs ├── Thirdweb.Utils │ ├── ThirdwebTask.cs │ ├── RLP.cs │ └── Utils.Types.cs ├── Thirdweb.Http │ ├── ThirdwebHttpResponseMessage.cs │ ├── ThirdwebHttpContent.cs │ ├── IThirdwebHttpClient.cs │ └── ThirdwebHttpClient.cs ├── Thirdweb.csproj ├── Thirdweb.Client │ ├── ThirdwebClient.Types.cs │ └── ThirdwebClient.cs ├── Thirdweb.Api │ └── ThirdwebHttpClientWrapper.cs ├── Thirdweb.RPC │ └── ThirdwebRPC.Types.cs └── Thirdweb.Transactions │ └── ThirdwebTransaction.Types.cs ├── Thirdweb.Generator └── Thirdweb.Generator.csproj ├── Thirdweb.Console └── Thirdweb.Console.csproj ├── docfx.json ├── index.md ├── .github └── workflows │ ├── release.yml │ ├── docfx.yml │ └── dotnet-ci.yml ├── README.md ├── thirdweb.sln ├── .editorconfig ├── .gitignore ├── Makefile └── LICENSE /toc.yml: -------------------------------------------------------------------------------- 1 | - name: Full API Reference 2 | href: docs/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Thirdweb.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using Xunit.Abstractions; 3 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/dotnet/HEAD/resources/icon.png -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "Thirdweb/Thirdweb.Wallets/InAppWallet" 3 | - "Thirdweb/Thirdweb.Api" -------------------------------------------------------------------------------- /resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/dotnet/HEAD/resources/favicon.ico -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.401", 4 | "rollForward": "latestPatch", 5 | "allowPrerelease": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Thirdweb.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", 3 | "parallelizeTestCollections": false, 4 | "longRunningTestSeconds": 90, 5 | "diagnosticMessages": true, 6 | "showLiveOutput": true, 7 | "stopOnFail": true 8 | } 9 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.JWT.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task SignInWithJwtAsync(string jwt) 6 | { 7 | return await this._server.VerifyJwtAsync(jwt).ConfigureAwait(false); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.Guest.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task SignInWithGuestAsync(string sessionId) 6 | { 7 | return await this._server.VerifyGuestAsync(sessionId).ConfigureAwait(false); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.Backend.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task SignInWithBackendAsync(string walletSecret) 6 | { 7 | return await this._server.VerifyBackendAsync(walletSecret).ConfigureAwait(false); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.AuthEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task SignInWithAuthEndpointAsync(string payload) 6 | { 7 | return await this._server.VerifyAuthEndpointAsync(payload).ConfigureAwait(false); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Thirdweb.Generator/Thirdweb.Generator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.PhoneOTP.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task SendPhoneOtpAsync(string phoneNumber) 6 | { 7 | _ = await this._server.SendPhoneOtpAsync(phoneNumber).ConfigureAwait(false); 8 | } 9 | 10 | public async Task VerifyPhoneOtpAsync(string phoneNumber, string otp) 11 | { 12 | return await this._server.VerifyPhoneOtpAsync(phoneNumber, otp).ConfigureAwait(false); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.OAuth.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public Server.VerifyResult SignInWithOauthAsync(string authResult) 6 | { 7 | return this._server.VerifyOAuthAsync(authResult); 8 | } 9 | 10 | public async Task FetchHeadlessOauthLoginLinkAsync(string authProvider, string platform) 11 | { 12 | return await this._server.FetchHeadlessOauthLoginLinkAsync(authProvider, platform).ConfigureAwait(false); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.EmailOTP.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task SendEmailOtpAsync(string emailAddress) 6 | { 7 | emailAddress = emailAddress.ToLower(); 8 | _ = await this._server.SendEmailOtpAsync(emailAddress).ConfigureAwait(false); 9 | } 10 | 11 | public async Task VerifyEmailOtpAsync(string emailAddress, string otp) 12 | { 13 | emailAddress = emailAddress.ToLower(); 14 | return await this._server.VerifyEmailOtpAsync(emailAddress, otp).ConfigureAwait(false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.Misc.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | internal LocalStorage.DataStorage GetSessionData() 6 | { 7 | return this._localStorage.Data ?? null; 8 | } 9 | 10 | internal async void UpdateSessionData(LocalStorage.DataStorage data) 11 | { 12 | await this._localStorage.SaveDataAsync(data).ConfigureAwait(false); 13 | } 14 | 15 | public async Task SignOutAsync() 16 | { 17 | await this._localStorage.SaveDataAsync(new LocalStorage.DataStorage(null, null, null, null, null, null, null)).ConfigureAwait(false); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.SIWE.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Thirdweb.EWS; 4 | 5 | internal partial class EmbeddedWallet 6 | { 7 | public async Task SignInWithSiweAsync(IThirdwebWallet signer, BigInteger chainId) 8 | { 9 | var address = await signer.GetAddress().ConfigureAwait(false); 10 | var payload = await this._server.FetchSiwePayloadAsync(address, chainId.ToString()).ConfigureAwait(false); 11 | var payloadMsg = Utils.GenerateSIWE(payload); 12 | var signature = await signer.PersonalSign(payloadMsg).ConfigureAwait(false); 13 | 14 | return await this._server.VerifySiweAsync(payload, signature).ConfigureAwait(false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Thirdweb.Console/Thirdweb.Console.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | latest 6 | true 7 | enable 8 | enable 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Storage/ThirdwebStorage.Types.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | /// 4 | /// Represents the result of an IPFS upload. 5 | /// 6 | [Serializable] 7 | public struct IPFSUploadResult 8 | { 9 | /// 10 | /// Gets or sets the IPFS hash of the uploaded content. 11 | /// 12 | public string IpfsHash { get; set; } 13 | 14 | /// 15 | /// Gets or sets the size of the pinned content. 16 | /// 17 | public string PinSize { get; set; } 18 | 19 | /// 20 | /// Gets or sets the timestamp of the upload. 21 | /// 22 | public string Timestamp { get; set; } 23 | 24 | /// 25 | /// Gets or sets the preview URL of the uploaded content. 26 | /// 27 | public string PreviewUrl { get; set; } 28 | } 29 | -------------------------------------------------------------------------------- /docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "Thirdweb", 7 | "files": ["**/*.csproj"] 8 | } 9 | ], 10 | "dest": "docs" 11 | } 12 | ], 13 | "build": { 14 | "content": [ 15 | { 16 | "files": ["**/*.{md,yml}"], 17 | "exclude": ["_site/**"] 18 | } 19 | ], 20 | "resource": [ 21 | { 22 | "files": ["resources/**"] 23 | } 24 | ], 25 | "output": "_site", 26 | "template": ["default", "modern"], 27 | "globalMetadata": { 28 | "_appName": "Thirdweb API Reference", 29 | "_appTitle": "Thirdweb API Reference", 30 | "_appLogoPath": "resources/favicon.ico", 31 | "_appFaviconPath": "resources/favicon.ico", 32 | "_enableSearch": true, 33 | "pdf": false 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | _layout: landing 3 | --- 4 | 5 | # Thirdweb .NET SDK Full API Reference 6 | 7 | Welcome to the comprehensive API reference for the Thirdweb .NET SDK. Here, you can explore detailed documentation for all aspects of our SDK's functionality. Whether you're integrating blockchain capabilities, managing transactions, or leveraging our advanced features, you'll find everything you need to know right here. 8 | 9 | This API reference is designed for developers looking to deeply integrate and understand the core functionality of our SDK. If you're looking for more user-friendly and conceptual documentation, including tutorials and guides, please visit our [Thirdweb Developer Portal](https://portal.thirdweb.com/dotnet). 10 | 11 | Explore the structured API documentation below to dive into specific namespaces, classes, methods, and properties offered by our SDK. 12 | 13 | --- 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@b2ace4b12f4cec1b96b6361ff2694ba9e931ceb4 # v3.3.1 18 | with: 19 | dotnet-version: "8.0.x" 20 | 21 | - name: Restore dependencies 22 | run: dotnet restore 23 | working-directory: ./ 24 | 25 | - name: Build Release 26 | run: dotnet build -c Release --no-restore 27 | working-directory: ./ 28 | 29 | - name: Push to NuGet 30 | run: dotnet nuget push "./Thirdweb/bin/Release/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json 31 | 32 | - name: Upload build artifacts 33 | uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 34 | with: 35 | name: build-artifacts 36 | path: | 37 | ./Thirdweb/bin/**/**/*.dll 38 | -------------------------------------------------------------------------------- /.github/workflows/docfx.yml: -------------------------------------------------------------------------------- 1 | name: Publish API Reference 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | actions: read 10 | pages: write 11 | id-token: write 12 | 13 | concurrency: 14 | group: "pages" 15 | cancel-in-progress: false 16 | 17 | jobs: 18 | publish-docs: 19 | environment: 20 | name: github-pages 21 | url: ${{ steps.deployment.outputs.page_url }} 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: Dotnet Setup 27 | uses: actions/setup-dotnet@b2ace4b12f4cec1b96b6361ff2694ba9e931ceb4 # v3.3.1 28 | with: 29 | dotnet-version: 8.x 30 | 31 | - run: dotnet tool update -g docfx 32 | - run: docfx docfx.json 33 | 34 | - name: Upload artifact 35 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 36 | with: 37 | path: "_site" 38 | - name: Deploy to GitHub Pages 39 | id: deployment 40 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 41 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.AccountLinking.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | public async Task> UnlinkAccountAsync(string currentAccountToken, LinkedAccount linkedAccount) 6 | { 7 | var serverLinkedAccount = new Server.LinkedAccount 8 | { 9 | Type = linkedAccount.Type, 10 | Details = new Server.LinkedAccount.LinkedAccountDetails 11 | { 12 | Email = linkedAccount.Details.Email, 13 | Address = linkedAccount.Details.Address, 14 | Phone = linkedAccount.Details.Phone, 15 | Id = linkedAccount.Details.Id, 16 | }, 17 | }; 18 | return await this._server.UnlinkAccountAsync(currentAccountToken, serverLinkedAccount).ConfigureAwait(false); 19 | } 20 | 21 | public async Task> LinkAccountAsync(string currentAccountToken, string authTokenToConnect) 22 | { 23 | return await this._server.LinkAccountAsync(currentAccountToken, authTokenToConnect).ConfigureAwait(false); 24 | } 25 | 26 | public async Task> GetLinkedAccountsAsync(string currentAccountToken) 27 | { 28 | return await this._server.GetLinkedAccountsAsync(currentAccountToken).ConfigureAwait(false); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Thirdweb.Tests/Thirdweb.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | latest 5 | true 6 | enable 7 | enable 8 | false 9 | true 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | 18 | 19 | all 20 | 21 | 22 | all 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | PreserveNewest 33 | 34 | 35 | 36 | 37 | PreserveNewest 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet.Storage/LocalStorage.Types.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Thirdweb.EWS; 4 | 5 | internal partial class LocalStorage : LocalStorageBase 6 | { 7 | [DataContract] 8 | internal class DataStorage 9 | { 10 | [field: DataMember(Name = "authToken")] 11 | internal string AuthToken { get; } 12 | 13 | [field: DataMember(Name = "deviceShare")] 14 | internal string DeviceShare { get; } 15 | 16 | [field: DataMember(Name = "emailAddress")] 17 | internal string EmailAddress { get; } 18 | 19 | [field: DataMember(Name = "phoneNumber")] 20 | internal string PhoneNumber { get; } 21 | 22 | [field: DataMember(Name = "walletUserId")] 23 | internal string WalletUserId { get; } 24 | 25 | [field: DataMember(Name = "authProvider")] 26 | internal string AuthProvider { get; } 27 | 28 | [field: DataMember(Name = "authIdentifier")] 29 | internal string AuthIdentifier { get; } 30 | 31 | internal DataStorage(string authToken, string deviceShare, string emailAddress, string phoneNumber, string walletUserId, string authProvider, string authIdentifier) 32 | { 33 | this.AuthToken = authToken; 34 | this.DeviceShare = deviceShare; 35 | this.EmailAddress = emailAddress; 36 | this.PhoneNumber = phoneNumber; 37 | this.WalletUserId = walletUserId; 38 | this.AuthProvider = authProvider; 39 | this.AuthIdentifier = authIdentifier; 40 | } 41 | } 42 | 43 | [DataContract] 44 | private class Storage 45 | { 46 | [DataMember] 47 | internal DataStorage Data { get; set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet/EmbeddedWallet.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.EWS; 2 | 3 | internal partial class EmbeddedWallet 4 | { 5 | private readonly LocalStorage _localStorage; 6 | private readonly Server _server; 7 | 8 | public EmbeddedWallet(ThirdwebClient client, string storageDirectoryPath = null, string ecosystemId = null, string ecosystemPartnerId = null) 9 | { 10 | this._localStorage = new LocalStorage(client.ClientId, storageDirectoryPath); 11 | 12 | // Create a new client of same type with extra needed headers for EWS 13 | var headers = client.HttpClient.Headers.ToDictionary(entry => entry.Key, entry => entry.Value); 14 | var platform = client.HttpClient.Headers["x-sdk-platform"]; 15 | var version = client.HttpClient.Headers["x-sdk-version"]; 16 | if (!string.IsNullOrEmpty(client.ClientId)) 17 | { 18 | headers.Add("x-thirdweb-client-id", client.ClientId); 19 | } 20 | if (!string.IsNullOrEmpty(client.SecretKey)) 21 | { 22 | headers.Add("x-thirdweb-secret-key", client.SecretKey); 23 | } 24 | headers.Add("x-session-nonce", Guid.NewGuid().ToString()); 25 | headers.Add("x-embedded-wallet-version", $"{platform}:{version}"); 26 | if (!string.IsNullOrEmpty(ecosystemId)) 27 | { 28 | headers.Add("x-ecosystem-id", ecosystemId); 29 | 30 | if (!string.IsNullOrEmpty(ecosystemPartnerId)) 31 | { 32 | headers.Add("x-ecosystem-partner-id", ecosystemPartnerId); 33 | } 34 | } 35 | 36 | var ewsHttpClient = Utils.ReconstructHttpClient(client.HttpClient, headers); 37 | 38 | this._server = new Server(client, ewsHttpClient); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-ci.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | types: [opened, synchronize] 8 | 9 | concurrency: 10 | group: build-and-test-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build-test-cov: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - name: Setup 21 | uses: actions/setup-dotnet@b2ace4b12f4cec1b96b6361ff2694ba9e931ceb4 # v3.3.1 22 | with: 23 | dotnet-version: | 24 | 8.0.x 25 | 26 | - name: Restore 27 | run: dotnet restore 28 | working-directory: ./ 29 | 30 | - name: Build 31 | run: dotnet build --no-restore -c Release 32 | working-directory: ./ 33 | 34 | - name: Test 35 | id: test 36 | run: | 37 | dotnet tool install --global coverlet.console 38 | timeout 30m dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage.info 39 | shell: bash 40 | env: 41 | THIRDWEB_SECRET_KEY: ${{ secrets.THIRDWEB_SECRET_KEY }} 42 | THIRDWEB_CLIENT_ID_BUNDLE_ID_ONLY: ${{ secrets.THIRDWEB_CLIENT_ID_BUNDLE_ID_ONLY }} 43 | THIRDWEB_BUNDLE_ID_BUNDLE_ID_ONLY: ${{ secrets.THIRDWEB_BUNDLE_ID_BUNDLE_ID_ONLY }} 44 | PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} 45 | 46 | - name: Codecov 47 | if: always() && steps.test.outcome != 'cancelled' 48 | uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 49 | with: 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | directory: ./ 52 | verbose: true 53 | slug: thirdweb-dev/dotnet 54 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.Types.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Thirdweb; 4 | 5 | /// 6 | /// Specifies the authentication providers available for the in-app wallet. 7 | /// 8 | public enum AuthProvider 9 | { 10 | Default, 11 | Google, 12 | Apple, 13 | Facebook, 14 | JWT, 15 | AuthEndpoint, 16 | Discord, 17 | Farcaster, 18 | Telegram, 19 | Siwe, 20 | Line, 21 | Guest, 22 | X, 23 | TikTok, 24 | Epic, 25 | Coinbase, 26 | Github, 27 | Twitch, 28 | Steam, 29 | Backend, 30 | } 31 | 32 | /// 33 | /// Represents a linked account. 34 | /// 35 | public struct LinkedAccount 36 | { 37 | /// 38 | /// The auth provider method used to create or link this account. 39 | /// 40 | [JsonProperty("type")] 41 | public string Type { get; set; } 42 | 43 | /// 44 | /// Additional details about the linked account. 45 | /// 46 | [JsonProperty("details")] 47 | public LinkedAccountDetails Details { get; set; } 48 | 49 | /// 50 | /// The email, address, phone and id related to the linked account, where applicable. 51 | /// 52 | public struct LinkedAccountDetails 53 | { 54 | [JsonProperty("email")] 55 | public string Email { get; set; } 56 | 57 | [JsonProperty("name")] 58 | public string Address { get; set; } 59 | 60 | [JsonProperty("phone")] 61 | public string Phone { get; set; } 62 | 63 | [JsonProperty("id")] 64 | public string Id { get; set; } 65 | } 66 | 67 | public override readonly string ToString() 68 | { 69 | return JsonConvert.SerializeObject(this); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Thirdweb.Tests/BaseTests.cs: -------------------------------------------------------------------------------- 1 | using dotenv.net; 2 | 3 | namespace Thirdweb.Tests; 4 | 5 | public class BaseTests 6 | { 7 | protected ITestOutputHelper Output { get; } 8 | protected string? SecretKey { get; } 9 | protected string? ClientIdBundleIdOnly { get; } 10 | protected string? BundleIdBundleIdOnly { get; } 11 | 12 | protected ThirdwebClient Client { get; } 13 | 14 | public BaseTests(ITestOutputHelper output) 15 | { 16 | DotEnv.Load(); 17 | this.Output = output; 18 | this.SecretKey = Environment.GetEnvironmentVariable("THIRDWEB_SECRET_KEY"); 19 | this.ClientIdBundleIdOnly = Environment.GetEnvironmentVariable("THIRDWEB_CLIENT_ID_BUNDLE_ID_ONLY"); 20 | this.BundleIdBundleIdOnly = Environment.GetEnvironmentVariable("THIRDWEB_BUNDLE_ID_BUNDLE_ID_ONLY"); 21 | 22 | this.Client = ThirdwebClient.Create(secretKey: this.SecretKey); 23 | 24 | this.Output.WriteLine($"Started {this.GetType().FullName}"); 25 | } 26 | 27 | [Fact(Timeout = 120000)] 28 | public void DotEnvTest() 29 | { 30 | Assert.NotNull(this.SecretKey); 31 | } 32 | 33 | public async Task GetGuestAccount() 34 | { 35 | var iaw = await InAppWallet.Create(this.Client, authProvider: AuthProvider.Guest); 36 | if (await iaw.IsConnected()) 37 | { 38 | await iaw.Disconnect(); 39 | } 40 | _ = await iaw.LoginWithGuest(defaultSessionIdOverride: Guid.NewGuid().ToString()); 41 | return iaw; 42 | } 43 | 44 | public async Task GetSmartAccount(int chainId = 421614) 45 | { 46 | var guestAccount = await this.GetGuestAccount(); 47 | var smartAccount = await SmartWallet.Create(personalWallet: guestAccount, chainId: chainId); 48 | return smartAccount; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Utils/ThirdwebTask.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Thirdweb; 4 | 5 | public static class ThirdwebTask 6 | { 7 | /// 8 | /// Simulates a delay without using Task.Delay or System.Threading.Timer, specifically designed to avoid clashing with WebGL threading. 9 | /// 10 | /// The number of milliseconds to delay. 11 | /// A cancellation token to cancel the delay. 12 | /// A task that completes after the specified delay. 13 | public static async Task Delay(int millisecondsDelay, CancellationToken cancellationToken = default) 14 | { 15 | var startTime = DateTime.UtcNow; 16 | var endTime = startTime.AddMilliseconds(millisecondsDelay); 17 | var currentDelay = 10; 18 | 19 | while (DateTime.UtcNow < endTime && !cancellationToken.IsCancellationRequested) 20 | { 21 | await MinimalDelay(currentDelay); 22 | 23 | if (DateTime.UtcNow.AddMilliseconds(currentDelay) < endTime) 24 | { 25 | currentDelay = Math.Min(currentDelay * 2, 100); 26 | } 27 | else 28 | { 29 | currentDelay = (int)(endTime - DateTime.UtcNow).TotalMilliseconds; 30 | } 31 | } 32 | } 33 | 34 | /// 35 | /// Provides a minimal delay using a manual loop with short sleeps to reduce CPU usage. 36 | /// 37 | /// The number of milliseconds to delay. 38 | private static async Task MinimalDelay(int milliseconds) 39 | { 40 | var stopwatch = Stopwatch.StartNew(); 41 | while (stopwatch.ElapsedMilliseconds < milliseconds) 42 | { 43 | Thread.Sleep(1); 44 | await Task.Yield(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Http/ThirdwebHttpResponseMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | /// 4 | /// Represents an HTTP response message used in the Thirdweb SDK. 5 | /// 6 | /// 7 | /// Initializes a new instance of the class. 8 | /// 9 | /// The status code of the HTTP response. 10 | /// The content of the HTTP response. 11 | /// A value indicating whether the HTTP response is successful. 12 | public class ThirdwebHttpResponseMessage(long statusCode, ThirdwebHttpContent content, bool isSuccessStatusCode) 13 | { 14 | /// 15 | /// Gets or sets the status code of the HTTP response. 16 | /// 17 | public long StatusCode { get; set; } = statusCode; 18 | 19 | /// 20 | /// Gets or sets the content of the HTTP response. 21 | /// 22 | public ThirdwebHttpContent Content { get; set; } = content; 23 | 24 | /// 25 | /// Gets or sets a value indicating whether the HTTP response is successful. 26 | /// 27 | public bool IsSuccessStatusCode { get; set; } = isSuccessStatusCode; 28 | 29 | /// 30 | /// Ensures that the HTTP response was successful. 31 | /// 32 | /// The instance. 33 | /// Thrown if the HTTP response was not successful. 34 | public ThirdwebHttpResponseMessage EnsureSuccessStatusCode() 35 | { 36 | if (!this.IsSuccessStatusCode) 37 | { 38 | // TODO: Custom exception 39 | throw new Exception($"Request failed with status code {this.StatusCode} and content: {this.Content.ReadAsStringAsync().Result}"); 40 | } 41 | return this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1;net6.0;net7.0;net8.0 4 | 3.1.1 5 | 3.1.1 6 | 3.1.1 7 | latest 8 | true 9 | enable 10 | Thirdweb 11 | Thirdweb 12 | 0xfirekeeper 13 | Best in class Web3 .NET SDK, powered by thirdweb. 14 | Copyright (c) thirdweb 2024 15 | https://thirdweb.com/ 16 | icon.png 17 | https://github.com/thirdweb-dev/dotnet 18 | git 19 | thirdweb wallet contracts client web3 ethereum blockchain game unity godot 20 | true 21 | Apache-2.0 22 | 23 | 24 | README.md 25 | true 26 | $(NoWarn);1591 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Client/ThirdwebClient.Types.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | /// 4 | /// Represents the timeout options for different types of operations. 5 | /// 6 | /// 7 | /// Initializes a new instance of the class. 8 | /// 9 | /// The timeout for storage operations (optional). 10 | /// The timeout for RPC operations (optional). 11 | /// The timeout for other operations (optional). 12 | public class TimeoutOptions(int? storage = null, int? rpc = null, int? other = null) 13 | { 14 | internal int? Storage { get; private set; } = storage; 15 | internal int? Rpc { get; private set; } = rpc; 16 | internal int? Other { get; private set; } = other; 17 | 18 | /// 19 | /// Gets the timeout value for the specified operation type. 20 | /// 21 | /// The type of operation. 22 | /// The fallback timeout value if none is specified (default is ). 23 | /// The timeout value for the specified operation type. 24 | public int GetTimeout(TimeoutType type, int fallback = Constants.DEFAULT_FETCH_TIMEOUT) 25 | { 26 | return type switch 27 | { 28 | TimeoutType.Storage => this.Storage ?? fallback, 29 | TimeoutType.Rpc => this.Rpc ?? fallback, 30 | TimeoutType.Other => this.Other ?? fallback, 31 | _ => fallback, 32 | }; 33 | } 34 | } 35 | 36 | /// 37 | /// Specifies the type of timeout for various operations. 38 | /// 39 | public enum TimeoutType 40 | { 41 | /// 42 | /// Timeout for storage operations. 43 | /// 44 | Storage, 45 | 46 | /// 47 | /// Timeout for RPC operations. 48 | /// 49 | Rpc, 50 | 51 | /// 52 | /// Timeout for other types of operations. 53 | /// 54 | Other, 55 | } 56 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet.Storage/LocalStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization.Json; 2 | 3 | namespace Thirdweb.EWS; 4 | 5 | internal abstract class LocalStorageBase 6 | { 7 | internal abstract LocalStorage.DataStorage Data { get; } 8 | 9 | internal abstract Task SaveDataAsync(LocalStorage.DataStorage data); 10 | } 11 | 12 | internal partial class LocalStorage : LocalStorageBase 13 | { 14 | internal override DataStorage Data => this._storage.Data; 15 | private readonly Storage _storage; 16 | private readonly string _filePath; 17 | 18 | internal LocalStorage(string clientId, string storageDirectoryPath) 19 | { 20 | if (string.IsNullOrEmpty(storageDirectoryPath)) 21 | { 22 | throw new ArgumentException("Storage directory path is required", nameof(storageDirectoryPath)); 23 | } 24 | _ = Directory.CreateDirectory(storageDirectoryPath); 25 | this._filePath = Path.Combine(storageDirectoryPath, $"{clientId}.txt"); 26 | try 27 | { 28 | var json = File.ReadAllBytes(this._filePath); 29 | DataContractJsonSerializer serializer = new(typeof(Storage)); 30 | MemoryStream fin = new(json); 31 | this._storage = (Storage)serializer.ReadObject(fin); 32 | } 33 | catch (Exception) 34 | { 35 | this._storage = new Storage(); 36 | } 37 | } 38 | 39 | private async Task UpdateDataAsync(Func fn) 40 | { 41 | if (fn()) 42 | { 43 | DataContractJsonSerializer serializer = new(typeof(Storage)); 44 | MemoryStream fout = new(); 45 | serializer.WriteObject(fout, this._storage); 46 | await File.WriteAllBytesAsync(this._filePath, fout.ToArray()).ConfigureAwait(false); 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | internal override Task SaveDataAsync(DataStorage data) 53 | { 54 | return this.UpdateDataAsync(() => 55 | { 56 | this._storage.Data = data; 57 | return true; 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Api/ThirdwebHttpClientWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.Api; 2 | 3 | /// 4 | /// Wrapper class that adapts IThirdwebHttpClient to work with System.Net.Http.HttpClient expectations 5 | /// 6 | public class ThirdwebHttpClientWrapper : HttpClient 7 | { 8 | private readonly IThirdwebHttpClient _thirdwebClient; 9 | 10 | public ThirdwebHttpClientWrapper(IThirdwebHttpClient thirdwebClient) 11 | { 12 | this._thirdwebClient = thirdwebClient ?? throw new ArgumentNullException(nameof(thirdwebClient)); 13 | } 14 | 15 | public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 16 | { 17 | return await this.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken); 18 | } 19 | 20 | public new async Task SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken) 21 | { 22 | _ = completionOption; 23 | 24 | var content = request.Content; 25 | 26 | ThirdwebHttpResponseMessage thirdwebResponse; 27 | 28 | switch (request.Method.Method.ToUpperInvariant()) 29 | { 30 | case "GET": 31 | thirdwebResponse = await this._thirdwebClient.GetAsync(request.RequestUri.ToString(), cancellationToken); 32 | break; 33 | case "POST": 34 | thirdwebResponse = await this._thirdwebClient.PostAsync(request.RequestUri.ToString(), content, cancellationToken); 35 | break; 36 | case "PUT": 37 | thirdwebResponse = await this._thirdwebClient.PutAsync(request.RequestUri.ToString(), content, cancellationToken); 38 | break; 39 | case "DELETE": 40 | thirdwebResponse = await this._thirdwebClient.DeleteAsync(request.RequestUri.ToString(), cancellationToken); 41 | break; 42 | default: 43 | throw new NotSupportedException($"HTTP method {request.Method} is not supported"); 44 | } 45 | 46 | var response = new HttpResponseMessage((System.Net.HttpStatusCode)thirdwebResponse.StatusCode) { Content = new StringContent(await thirdwebResponse.Content.ReadAsStringAsync()) }; 47 | 48 | return response; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Http/ThirdwebHttpContent.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Thirdweb; 4 | 5 | /// 6 | /// Represents HTTP content used in the Thirdweb SDK. 7 | /// 8 | public class ThirdwebHttpContent 9 | { 10 | private readonly byte[] _content; 11 | 12 | /// 13 | /// Initializes a new instance of the class from a string. 14 | /// 15 | /// The content string. 16 | /// Thrown if the content is null. 17 | public ThirdwebHttpContent(string content) 18 | { 19 | if (content == null) 20 | { 21 | throw new ArgumentNullException(nameof(content)); 22 | } 23 | 24 | this._content = Encoding.UTF8.GetBytes(content); 25 | } 26 | 27 | /// 28 | /// Initializes a new instance of the class from a byte array. 29 | /// 30 | /// The content byte array. 31 | /// Thrown if the content is null. 32 | public ThirdwebHttpContent(byte[] content) 33 | { 34 | this._content = content ?? throw new ArgumentNullException(nameof(content)); 35 | } 36 | 37 | /// 38 | /// Initializes a new instance of the class from a stream. 39 | /// 40 | /// The content stream. 41 | /// Thrown if the content is null. 42 | public ThirdwebHttpContent(Stream content) 43 | { 44 | if (content == null) 45 | { 46 | throw new ArgumentNullException(nameof(content)); 47 | } 48 | 49 | using var memoryStream = new MemoryStream(); 50 | content.CopyTo(memoryStream); 51 | this._content = memoryStream.ToArray(); 52 | } 53 | 54 | /// 55 | /// Reads the content as a string. 56 | /// 57 | /// A task that represents the asynchronous operation. The task result contains the content string. 58 | public Task ReadAsStringAsync() 59 | { 60 | return Task.FromResult(Encoding.UTF8.GetString(this._content)); 61 | } 62 | 63 | /// 64 | /// Reads the content as a byte array. 65 | /// 66 | /// A task that represents the asynchronous operation. The task result contains the content byte array. 67 | public Task ReadAsByteArrayAsync() 68 | { 69 | return Task.FromResult(this._content); 70 | } 71 | 72 | /// 73 | /// Reads the content as a stream. 74 | /// 75 | /// A task that represents the asynchronous operation. The task result contains the content stream. 76 | public Task ReadAsStreamAsync() 77 | { 78 | var stream = new MemoryStream(this._content); 79 | return Task.FromResult(stream); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![net-banner](https://github.com/thirdweb-dev/dotnet/assets/43042585/6abcdae9-b49f-492a-98de-b01756e21798) 2 | 3 | [.NET Documentation](https://portal.thirdweb.com/dotnet) 4 | [NuGet Version](https://www.nuget.org/packages/Thirdweb) 5 | [NuGet Downloads](https://www.nuget.org/packages/Thirdweb) 6 | [Codecov](https://app.codecov.io/gh/thirdweb-dev/dotnet) 7 | 8 | ## Overview 9 | 10 | The Thirdweb .NET SDK is a comprehensive and easy to use library that allows developers to interact with the blockchain using the .NET framework. It simplifies the integration of all [thirdweb](https://thirdweb.com/) functionality with a minimal set of dependencies. 11 | 12 | ## Core Features 13 | 14 | - **Connect to any EVM network:** Easily connect to blockchain network with its chain id alone. 15 | - **Interact with smart contracts:** Simplified read and write operations for smart contracts, with various out-of-the-box extensions provided. 16 | - **In-App Wallets:** Integrate user-friendly wallets within your applications, supporting email, phone, OAuth login or plug your own auth in. 17 | - **Ecosystem Wallets:** Basically In-App Wallets functionality wise, with the added benefit of being able to securely share your wallets with third party partners. 18 | - **Account Abstraction:** Turn any wallet into a programmable smart wallet (EIP-4337 or EIP-7702) with built-in gas sponsorship and granular session key features. 19 | - **Storage Solutions:** Download and upload files using IPFS or HTTPS. 20 | - **Transaction Builder:** Create, manipulate and send low level transactions. 21 | - **Session Keys:** Advanced control for smart wallets to manage permissions and session durations. 22 | - **Thirdweb Bridge:** Universal interface to use any asset onchain. 23 | - **Thirdweb Nebula:** Create blockchain-powered AI Agents. 24 | - **Thirdweb Insight:** Query blockchain data at the speed of light. 25 | - **Thirdweb Engine:** Interact in creative ways from your backend. 26 | - **Unity Compatibility**: This SDK has been tested successfully in [Unity 2022.3+](https://portal.thirdweb.com/unity/v5) (All build targets). 27 | - **Godot Compatibility**: This SDK has been tested successfully in [Godot .NET](https://portal.thirdweb.com/dotnet/godot) 28 | - **MAUI Compatibility**: This SDK has been tested successfully in [MAUI](https://portal.thirdweb.com/dotnet/maui) 29 | 30 | ## Installation 31 | 32 | To use the Thirdweb .NET SDK in your project, you can either download the source code and build it manually, or install it via NuGet package manager. 33 | 34 | Run the following command to install: 35 | 36 | ``` 37 | dotnet add package Thirdweb 38 | ``` 39 | 40 | ## Documentation 41 | 42 | [Documentation Portal](https://portal.thirdweb.com/dotnet) 43 | 44 | [Full API Reference](https://thirdweb-dev.github.io/dotnet/) 45 | 46 | ## Need Help? 47 | 48 | For any questions or support, visit our [Support Portal](https://thirdweb.com/support). 49 | 50 | Thank you for trying out the Thirdweb .NET SDK! 51 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Http/IThirdwebHttpClient.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | /// 4 | /// Interface for a HTTP client used in the Thirdweb SDK. 5 | /// 6 | public interface IThirdwebHttpClient : IDisposable 7 | { 8 | /// 9 | /// Gets the headers for the HTTP client. 10 | /// 11 | public Dictionary Headers { get; } 12 | 13 | /// 14 | /// Sets the headers for the HTTP client. 15 | /// 16 | /// The headers to set. 17 | public void SetHeaders(Dictionary headers); 18 | 19 | /// 20 | /// Clears all headers from the HTTP client. 21 | /// 22 | public void ClearHeaders(); 23 | 24 | /// 25 | /// Adds a header to the HTTP client. 26 | /// 27 | /// The header key. 28 | /// The header value. 29 | public void AddHeader(string key, string value); 30 | 31 | /// 32 | /// Removes a header from the HTTP client. 33 | /// 34 | /// The header key. 35 | public void RemoveHeader(string key); 36 | 37 | /// 38 | /// Sends a GET request to the specified URI. 39 | /// 40 | /// The request URI. 41 | /// The cancellation token. 42 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 43 | public Task GetAsync(string requestUri, CancellationToken cancellationToken = default); 44 | 45 | /// 46 | /// Sends a POST request to the specified URI. 47 | /// 48 | /// The request URI. 49 | /// The HTTP content to send. 50 | /// The cancellation token. 51 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 52 | public Task PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken = default); 53 | 54 | /// 55 | /// Sends a PUT request to the specified URI. 56 | /// 57 | /// The request URI. 58 | /// The HTTP content to send. 59 | /// The cancellation token. 60 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 61 | public Task PutAsync(string requestUri, HttpContent content, CancellationToken cancellationToken = default); 62 | 63 | /// 64 | /// Sends a DELETE request to the specified URI. 65 | /// 66 | /// The request URI. 67 | /// The cancellation token. 68 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 69 | public Task DeleteAsync(string requestUri, CancellationToken cancellationToken = default); 70 | } 71 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/IThirdwebBrowser.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | /// 4 | /// Defines an interface for handling browser-based login for Thirdweb. 5 | /// 6 | public interface IThirdwebBrowser 7 | { 8 | /// 9 | /// Initiates a login process using the browser. 10 | /// 11 | /// The Thirdweb client instance. 12 | /// The URL to initiate the login process. 13 | /// The URL to redirect to after login. 14 | /// An action to open the browser with the login URL. 15 | /// Optional cancellation token to cancel the operation. 16 | /// A task representing the asynchronous operation. The task result contains the login result. 17 | Task Login(ThirdwebClient client, string loginUrl, string redirectUrl, Action browserOpenAction, CancellationToken cancellationToken = default); 18 | } 19 | 20 | /// 21 | /// Enumerates the possible statuses of a browser operation. 22 | /// 23 | public enum BrowserStatus 24 | { 25 | /// 26 | /// The operation was successful. 27 | /// 28 | Success, 29 | 30 | /// 31 | /// The user canceled the operation. 32 | /// 33 | UserCanceled, 34 | 35 | /// 36 | /// The operation timed out. 37 | /// 38 | Timeout, 39 | 40 | /// 41 | /// An unknown error occurred during the operation. 42 | /// 43 | UnknownError, 44 | } 45 | 46 | /// 47 | /// Represents the result of a browser-based login operation. 48 | /// 49 | public class BrowserResult 50 | { 51 | /// 52 | /// Gets the status of the browser operation. 53 | /// 54 | public BrowserStatus Status { get; } 55 | 56 | /// 57 | /// Gets the callback URL returned from the browser operation. 58 | /// 59 | public string CallbackUrl { get; } 60 | 61 | /// 62 | /// Gets the error message, if any, from the browser operation. 63 | /// 64 | public string Error { get; } 65 | 66 | /// 67 | /// Initializes a new instance of the class with the specified status and callback URL. 68 | /// 69 | /// The status of the browser operation. 70 | /// The callback URL returned from the browser operation. 71 | public BrowserResult(BrowserStatus status, string callbackUrl) 72 | { 73 | this.Status = status; 74 | this.CallbackUrl = callbackUrl; 75 | } 76 | 77 | /// 78 | /// Initializes a new instance of the class with the specified status, callback URL, and error message. 79 | /// 80 | /// The status of the browser operation. 81 | /// The callback URL returned from the browser operation. 82 | /// The error message from the browser operation. 83 | public BrowserResult(BrowserStatus status, string callbackUrl, string error) 84 | { 85 | this.Status = status; 86 | this.CallbackUrl = callbackUrl; 87 | this.Error = error; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.Types.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Newtonsoft.Json; 3 | 4 | namespace Thirdweb; 5 | 6 | public partial class EcosystemWallet 7 | { 8 | /// 9 | /// User linked account details. 10 | /// 11 | public class UserStatusResponse 12 | { 13 | /// 14 | /// The user's linked accounts. 15 | /// 16 | [JsonProperty("linkedAccounts")] 17 | public List LinkedAccounts { get; set; } 18 | 19 | /// 20 | /// The user's wallets, generally only one wallet is returned. 21 | /// 22 | [JsonProperty("wallets")] 23 | public List Wallets { get; set; } 24 | } 25 | 26 | /// 27 | /// Represents a user's embedded wallet. 28 | /// 29 | public class ShardedOrEnclaveWallet 30 | { 31 | /// 32 | /// The public address of the wallet. 33 | /// 34 | [JsonProperty("address")] 35 | public string Address { get; set; } 36 | 37 | /// 38 | /// The wallet's creation date. 39 | /// 40 | [JsonProperty("createdAt")] 41 | public DateTime CreatedAt { get; set; } 42 | 43 | [JsonProperty("type")] 44 | internal string Type { get; set; } 45 | } 46 | 47 | internal class EnclaveGenerateResponse 48 | { 49 | [JsonProperty("wallet")] 50 | internal EnclaveWallet Wallet { get; set; } 51 | } 52 | 53 | internal class EnclaveWallet 54 | { 55 | [JsonProperty("address")] 56 | internal string Address { get; set; } 57 | } 58 | 59 | internal class EnclaveSignResponse 60 | { 61 | [JsonProperty("r")] 62 | internal string R { get; set; } 63 | 64 | [JsonProperty("s")] 65 | internal string S { get; set; } 66 | 67 | [JsonProperty("v")] 68 | internal string V { get; set; } 69 | 70 | [JsonProperty("signature")] 71 | internal string Signature { get; set; } 72 | 73 | [JsonProperty("hash")] 74 | internal string Hash { get; set; } 75 | } 76 | 77 | public class EcosystemDetails 78 | { 79 | [JsonProperty("thirdwebAccountId")] 80 | public string ThirdwebAccountId { get; set; } 81 | 82 | [JsonProperty("permission")] 83 | public string Permission { get; set; } 84 | 85 | [JsonProperty("authOptions")] 86 | public List AuthOptions { get; set; } 87 | 88 | [JsonProperty("name")] 89 | public string Name { get; set; } 90 | 91 | [JsonProperty("slug")] 92 | public string Slug { get; set; } 93 | 94 | [JsonProperty("imageUrl")] 95 | public string ImageUrl { get; set; } 96 | 97 | [JsonProperty("smartAccountOptions")] 98 | public EcosystemDetails_SmartAccountOptions? SmartAccountOptions { get; set; } 99 | } 100 | 101 | public struct EcosystemDetails_SmartAccountOptions 102 | { 103 | [JsonProperty("chainIds")] 104 | public List ChainIds { get; set; } 105 | 106 | [JsonProperty("sponsorGas")] 107 | public bool SponsorGas { get; set; } 108 | 109 | [JsonProperty("accountFactoryAddress")] 110 | public string AccountFactoryAddress { get; set; } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs: -------------------------------------------------------------------------------- 1 | using Thirdweb.EWS; 2 | 3 | namespace Thirdweb; 4 | 5 | /// 6 | /// Represents an in-app wallet that supports email, phone, social, SIWE and custom authentication. 7 | /// 8 | public class InAppWallet : EcosystemWallet 9 | { 10 | public override string WalletId => "inApp"; 11 | 12 | internal InAppWallet( 13 | ThirdwebClient client, 14 | EmbeddedWallet embeddedWallet, 15 | IThirdwebHttpClient httpClient, 16 | string email, 17 | string phoneNumber, 18 | string authProvider, 19 | IThirdwebWallet siweSigner, 20 | string address, 21 | string walletSecret, 22 | ExecutionMode executionMode, 23 | string delegationContractAddress 24 | ) 25 | : base(null, null, client, embeddedWallet, httpClient, email, phoneNumber, authProvider, siweSigner, walletSecret, executionMode, delegationContractAddress) 26 | { 27 | this.Address = address; 28 | } 29 | 30 | /// 31 | /// Creates a new instance of the class. 32 | /// 33 | /// The Thirdweb client instance. 34 | /// The email address for Email OTP authentication. 35 | /// The phone number for Phone OTP authentication. 36 | /// The authentication provider to use. 37 | /// The path to the storage directory. 38 | /// The SIWE signer wallet for SIWE authentication. 39 | /// The wallet secret for backend authentication. 40 | /// The auth token to use for the session. This will automatically connect using a raw thirdweb auth token. 41 | /// The execution mode for the wallet. EOA represents traditional direct calls, EIP7702 represents upgraded account self sponsored calls, and EIP7702Sponsored represents upgraded account calls with managed/sponsored execution. 42 | /// A task that represents the asynchronous operation. The task result contains the created in-app wallet. 43 | /// Thrown when required parameters are not provided. 44 | public static async Task Create( 45 | ThirdwebClient client, 46 | string email = null, 47 | string phoneNumber = null, 48 | AuthProvider authProvider = Thirdweb.AuthProvider.Default, 49 | string storageDirectoryPath = null, 50 | IThirdwebWallet siweSigner = null, 51 | string walletSecret = null, 52 | string twAuthTokenOverride = null, 53 | ExecutionMode executionMode = ExecutionMode.EOA 54 | ) 55 | { 56 | storageDirectoryPath ??= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Thirdweb", "InAppWallet"); 57 | var ecoWallet = await Create(client, null, null, email, phoneNumber, authProvider, storageDirectoryPath, siweSigner, walletSecret, twAuthTokenOverride, executionMode); 58 | return new InAppWallet( 59 | ecoWallet.Client, 60 | ecoWallet.EmbeddedWallet, 61 | ecoWallet.HttpClient, 62 | ecoWallet.Email, 63 | ecoWallet.PhoneNumber, 64 | ecoWallet.AuthProvider, 65 | ecoWallet.SiweSigner, 66 | ecoWallet.Address, 67 | ecoWallet.WalletSecret, 68 | ecoWallet.ExecutionMode, 69 | ecoWallet.DelegationContractAddress 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/ServerWallet/ServerWallet.Types.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Newtonsoft.Json; 3 | 4 | namespace Thirdweb; 5 | 6 | /// 7 | /// Base class for execution options 8 | /// 9 | [JsonObject] 10 | public class ExecutionOptions 11 | { 12 | [JsonProperty("chainId")] 13 | public BigInteger? ChainId { get; set; } = null; 14 | 15 | [JsonProperty("idempotencyKey")] 16 | public string IdempotencyKey { get; set; } 17 | } 18 | 19 | /// 20 | /// Auto determine execution options 21 | /// 22 | [JsonObject] 23 | public class AutoExecutionOptions : ExecutionOptions 24 | { 25 | [JsonProperty("type")] 26 | public string Type { get; set; } = "auto"; 27 | 28 | [JsonProperty("from")] 29 | public string From { get; set; } 30 | } 31 | 32 | /// 33 | /// Externally Owned Account (EOA) execution options 34 | /// 35 | [JsonObject] 36 | public class EIP7702ExecutionOptions : ExecutionOptions 37 | { 38 | [JsonProperty("type")] 39 | public string Type { get; set; } = "EIP7702"; 40 | 41 | [JsonProperty("from")] 42 | public string From { get; set; } 43 | } 44 | 45 | /// 46 | /// Externally Owned Account (EOA) execution options 47 | /// 48 | [JsonObject] 49 | public class EOAExecutionOptions : ExecutionOptions 50 | { 51 | [JsonProperty("type")] 52 | public string Type { get; set; } = "EOA"; 53 | 54 | [JsonProperty("from")] 55 | public string From { get; set; } 56 | } 57 | 58 | /// 59 | /// ERC-4337 execution options 60 | /// 61 | [JsonObject] 62 | public class ERC4337ExecutionOptions : ExecutionOptions 63 | { 64 | [JsonProperty("type")] 65 | public string Type { get; set; } = "ERC4337"; 66 | 67 | [JsonProperty("signerAddress")] 68 | public string SignerAddress { get; set; } 69 | 70 | [JsonProperty("accountSalt")] 71 | public string AccountSalt { get; set; } 72 | 73 | [JsonProperty("smartAccountAddress")] 74 | public string SmartAccountAddress { get; set; } 75 | 76 | [JsonProperty("entrypointAddress")] 77 | public string EntrypointAddress { get; set; } 78 | 79 | [JsonProperty("entrypointVersion")] 80 | public string EntrypointVersion { get; set; } 81 | 82 | [JsonProperty("factoryAddress")] 83 | public string FactoryAddress { get; set; } 84 | 85 | public ERC4337ExecutionOptions(BigInteger chainId, string signerAddress) 86 | { 87 | this.ChainId = chainId; 88 | this.SignerAddress = signerAddress; 89 | } 90 | } 91 | 92 | /// 93 | /// Response wrapper for queued transactions 94 | /// 95 | [JsonObject] 96 | internal class QueuedTransactionResponse 97 | { 98 | [JsonProperty("result")] 99 | public QueuedTransactionResult Result { get; set; } 100 | } 101 | 102 | /// 103 | /// Result containing the transactions array 104 | /// 105 | [JsonObject] 106 | internal class QueuedTransactionResult 107 | { 108 | [JsonProperty("transactions")] 109 | public QueuedTransaction[] Transactions { get; set; } 110 | } 111 | 112 | /// 113 | /// Queued transaction response 114 | /// 115 | [JsonObject] 116 | internal class QueuedTransaction 117 | { 118 | [JsonProperty("id")] 119 | public string Id { get; set; } 120 | 121 | [JsonProperty("batchIndex")] 122 | public long BatchIndex { get; set; } 123 | 124 | [JsonProperty("executionParams")] 125 | public ExecutionOptions ExecutionParams { get; set; } 126 | 127 | [JsonProperty("transactionParams")] 128 | public InnerTransaction[] TransactionParams { get; set; } 129 | } 130 | 131 | /// 132 | /// Inner transaction data 133 | /// 134 | [JsonObject] 135 | internal class InnerTransaction 136 | { 137 | [JsonProperty("to")] 138 | public string To { get; set; } 139 | 140 | [JsonProperty("data")] 141 | public string Data { get; set; } 142 | 143 | [JsonProperty("value")] 144 | public string Value { get; set; } 145 | } 146 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.RPC/ThirdwebRPC.Types.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | 4 | namespace Thirdweb; 5 | 6 | [JsonObject] 7 | public class RpcError 8 | { 9 | [JsonConstructor] 10 | private RpcError() { } 11 | 12 | [JsonProperty("code")] 13 | public int Code { get; private set; } 14 | 15 | [JsonProperty("message")] 16 | public string Message { get; private set; } 17 | 18 | [JsonProperty("data")] 19 | public JToken Data { get; private set; } 20 | } 21 | 22 | [JsonObject] 23 | public class RpcResponseMessage 24 | { 25 | [JsonProperty("id")] 26 | public object Id { get; private set; } 27 | 28 | [JsonProperty("jsonrpc")] 29 | public string JsonRpcVersion { get; private set; } 30 | 31 | [JsonProperty("result")] 32 | public JToken Result { get; private set; } 33 | 34 | [JsonProperty("error")] 35 | public RpcError Error { get; protected set; } 36 | 37 | [JsonIgnore] 38 | public bool HasError => this.Error != null; 39 | } 40 | 41 | public class RpcResponse 42 | { 43 | [JsonProperty("jsonrpc")] 44 | public string Jsonrpc { get; set; } 45 | 46 | [JsonProperty("id")] 47 | public int Id { get; set; } 48 | 49 | [JsonProperty("result")] 50 | public T Result { get; set; } 51 | 52 | [JsonProperty("error")] 53 | public RpcError Error { get; set; } 54 | } 55 | 56 | public class RpcRequest 57 | { 58 | [JsonProperty("jsonrpc")] 59 | public string Jsonrpc { get; set; } = "2.0"; 60 | 61 | [JsonProperty("method")] 62 | public string Method { get; set; } 63 | 64 | [JsonProperty("params")] 65 | public object[] Params { get; set; } 66 | 67 | [JsonProperty("id")] 68 | public int Id { get; set; } 69 | } 70 | 71 | [JsonObject] 72 | public class RpcRequestMessage 73 | { 74 | [JsonConstructor] 75 | private RpcRequestMessage() { } 76 | 77 | public RpcRequestMessage(object id, string method, params object[] parameterList) 78 | { 79 | this.Id = id; 80 | this.JsonRpcVersion = "2.0"; 81 | this.Method = method; 82 | this.RawParameters = parameterList; 83 | } 84 | 85 | [JsonProperty("id")] 86 | public object Id { get; set; } 87 | 88 | [JsonProperty("jsonrpc")] 89 | public string JsonRpcVersion { get; private set; } 90 | 91 | [JsonProperty("method")] 92 | public string Method { get; private set; } 93 | 94 | [JsonProperty("params")] 95 | [JsonConverter(typeof(RpcParameterJsonConverter))] 96 | public object RawParameters { get; private set; } 97 | } 98 | 99 | public class RpcParameterJsonConverter : JsonConverter 100 | { 101 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 102 | { 103 | serializer.Serialize(writer, value); 104 | } 105 | 106 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 107 | { 108 | switch (reader.TokenType) 109 | { 110 | case JsonToken.StartObject: 111 | try 112 | { 113 | var jObject = JObject.Load(reader); 114 | return jObject.ToObject>(); 115 | } 116 | catch (Exception) 117 | { 118 | throw new Exception("Request parameters can only be an associative array, list or null."); 119 | } 120 | case JsonToken.StartArray: 121 | return JArray.Load(reader).ToObject(serializer); 122 | case JsonToken.Null: 123 | case JsonToken.None: 124 | case JsonToken.StartConstructor: 125 | case JsonToken.PropertyName: 126 | case JsonToken.Comment: 127 | case JsonToken.Raw: 128 | case JsonToken.Integer: 129 | case JsonToken.Float: 130 | case JsonToken.String: 131 | case JsonToken.Boolean: 132 | case JsonToken.Undefined: 133 | case JsonToken.EndObject: 134 | case JsonToken.EndArray: 135 | case JsonToken.EndConstructor: 136 | case JsonToken.Date: 137 | case JsonToken.Bytes: 138 | default: 139 | throw new Exception("Request parameters can only be an associative array, list or null."); 140 | } 141 | } 142 | 143 | public override bool CanConvert(Type objectType) 144 | { 145 | return true; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Thirdweb.Tests/Thirdweb.Client/Thirdweb.Client.Tests.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.Tests.Client; 2 | 3 | public class ClientTests : BaseTests 4 | { 5 | public ClientTests(ITestOutputHelper output) 6 | : base(output) { } 7 | 8 | [Fact(Timeout = 120000)] 9 | public void NoSecretKeyNoClientId() 10 | { 11 | _ = Assert.Throws(() => ThirdwebClient.Create()); 12 | } 13 | 14 | [Fact(Timeout = 120000)] 15 | public void SecretKeyInitialization() 16 | { 17 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 18 | Assert.NotNull(client.ClientId); 19 | Assert.NotNull(client.SecretKey); 20 | Assert.Null(client.BundleId); 21 | Assert.Equal(client.ClientId, Utils.ComputeClientIdFromSecretKey(client.SecretKey)); 22 | Assert.Equal(client.SecretKey, this.SecretKey); 23 | } 24 | 25 | [Fact(Timeout = 120000)] 26 | public void ClientIdInitialization() 27 | { 28 | var clientId = "test-client-id"; 29 | var client = ThirdwebClient.Create(clientId: clientId); 30 | Assert.NotNull(client.ClientId); 31 | Assert.Null(client.SecretKey); 32 | Assert.Null(client.BundleId); 33 | Assert.Equal(client.ClientId, clientId); 34 | } 35 | 36 | [Fact(Timeout = 120000)] 37 | public void BundleIdInitialization() 38 | { 39 | var bundleId = "test-bundle-id"; 40 | var exception = Assert.Throws(() => ThirdwebClient.Create(bundleId: bundleId)); 41 | Assert.Equal("ClientId or SecretKey must be provided", exception.Message); 42 | } 43 | 44 | [Fact(Timeout = 120000)] 45 | public void ClientIdAndSecretKeyInitialization() 46 | { 47 | var clientId = "test-client-id"; 48 | var client = ThirdwebClient.Create(clientId: clientId, secretKey: this.SecretKey); 49 | Assert.NotNull(client.ClientId); 50 | Assert.NotNull(client.SecretKey); 51 | Assert.Null(client.BundleId); 52 | Assert.Equal(client.ClientId, clientId); 53 | Assert.NotEqual(client.ClientId, Utils.ComputeClientIdFromSecretKey(client.SecretKey)); 54 | Assert.Equal(client.SecretKey, this.SecretKey); 55 | } 56 | 57 | [Fact(Timeout = 120000)] 58 | public void ClientIdAndBundleIdInitialization() 59 | { 60 | var clientId = "test-client-id"; 61 | var bundleId = "test-bundle-id"; 62 | var client = ThirdwebClient.Create(clientId: clientId, bundleId: bundleId); 63 | Assert.NotNull(client.ClientId); 64 | Assert.NotNull(client.BundleId); 65 | Assert.Null(client.SecretKey); 66 | Assert.Equal(client.ClientId, clientId); 67 | Assert.Equal(client.BundleId, bundleId); 68 | } 69 | 70 | [Fact(Timeout = 120000)] 71 | public void SecretKeyAndBundleIdInitialization() 72 | { 73 | var bundleId = "test-bundle-id"; 74 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, bundleId: bundleId); 75 | Assert.NotNull(client.SecretKey); 76 | Assert.NotNull(client.BundleId); 77 | Assert.NotNull(client.ClientId); 78 | Assert.Equal(client.SecretKey, this.SecretKey); 79 | Assert.Equal(client.BundleId, bundleId); 80 | Assert.Equal(client.ClientId, Utils.ComputeClientIdFromSecretKey(client.SecretKey)); 81 | } 82 | 83 | [Fact(Timeout = 120000)] 84 | public void TimeoutOptions() 85 | { 86 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 30000, rpc: 30000, other: 30000)); 87 | Assert.NotNull(client.FetchTimeoutOptions); 88 | Assert.Equal(30000, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); 89 | Assert.Equal(30000, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); 90 | Assert.Equal(30000, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other)); 91 | Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other + 1)); 92 | } 93 | 94 | [Fact(Timeout = 120000)] 95 | public void NoTimeoutOptions() 96 | { 97 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 98 | Assert.NotNull(client.FetchTimeoutOptions); 99 | Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); 100 | Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); 101 | Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other)); 102 | Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other + 1)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Storage/ThirdwebStorage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Thirdweb; 4 | 5 | /// 6 | /// Provides methods for downloading and uploading data to Thirdweb storage. 7 | /// 8 | public static class ThirdwebStorage 9 | { 10 | /// 11 | /// Downloads data from the specified URI. 12 | /// 13 | /// The type of data to download. 14 | /// The Thirdweb client. 15 | /// The URI to download from. 16 | /// The optional request timeout in milliseconds. 17 | /// The downloaded data. 18 | /// Thrown if the URI is null or empty. 19 | /// Thrown if the download fails. 20 | public static async Task Download(ThirdwebClient client, string uri, int? requestTimeout = null) 21 | { 22 | if (string.IsNullOrEmpty(uri)) 23 | { 24 | throw new ArgumentNullException(nameof(uri)); 25 | } 26 | 27 | if (uri.StartsWith("data:application/json;base64,")) 28 | { 29 | var base64Data = uri["data:application/json;base64,".Length..]; 30 | var jsonData = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(base64Data)); 31 | return JsonConvert.DeserializeObject(jsonData); 32 | } 33 | 34 | uri = uri.ReplaceIPFS($"https://{client.ClientId}.ipfscdn.io/ipfs/"); 35 | 36 | using var cts = new CancellationTokenSource(requestTimeout ?? client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); 37 | 38 | var httpClient = client.HttpClient; 39 | 40 | var response = await httpClient.GetAsync(uri, cts.Token).ConfigureAwait(false); 41 | 42 | if (!response.IsSuccessStatusCode) 43 | { 44 | throw new Exception($"Failed to download {uri}: {response.StatusCode} | {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}"); 45 | } 46 | 47 | if (typeof(T) == typeof(byte[])) 48 | { 49 | return (T)(object)await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); 50 | } 51 | else if (typeof(T) == typeof(string)) 52 | { 53 | return (T)(object)await response.Content.ReadAsStringAsync().ConfigureAwait(false); 54 | } 55 | else 56 | { 57 | var content = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); 58 | return JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(content)); 59 | } 60 | } 61 | 62 | /// 63 | /// Uploads raw byte data to Thirdweb storage. 64 | /// 65 | /// The Thirdweb client. 66 | /// The raw byte data to upload. 67 | /// The result of the upload. 68 | /// Thrown if the raw byte data is null or empty. 69 | /// Thrown if the upload fails. 70 | public static async Task UploadRaw(ThirdwebClient client, byte[] rawBytes) 71 | { 72 | if (rawBytes == null || rawBytes.Length == 0) 73 | { 74 | throw new ArgumentNullException(nameof(rawBytes)); 75 | } 76 | 77 | using var form = new MultipartFormDataContent { { new ByteArrayContent(rawBytes), "file", "file" } }; 78 | 79 | var httpClient = client.HttpClient; 80 | 81 | var response = await httpClient.PostAsync(Constants.PIN_URI, form).ConfigureAwait(false); 82 | 83 | if (!response.IsSuccessStatusCode) 84 | { 85 | throw new Exception($"Failed to upload raw bytes: {response.StatusCode} | {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}"); 86 | } 87 | 88 | var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 89 | 90 | var res = JsonConvert.DeserializeObject(result); 91 | res.PreviewUrl = $"https://{client.ClientId}.ipfscdn.io/ipfs/{res.IpfsHash}"; 92 | return res; 93 | } 94 | 95 | /// 96 | /// Uploads a file to Thirdweb storage from the specified path. 97 | /// 98 | /// The Thirdweb client. 99 | /// The path to the file. 100 | /// The result of the upload. 101 | /// Thrown if the path is null or empty. 102 | public static async Task Upload(ThirdwebClient client, string path) 103 | { 104 | return string.IsNullOrEmpty(path) ? throw new ArgumentNullException(nameof(path)) : await UploadRaw(client, await File.ReadAllBytesAsync(path).ConfigureAwait(false)).ConfigureAwait(false); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWalletBrowser.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Thirdweb; 4 | 5 | /// 6 | /// Represents an in-app browser for handling wallet login. 7 | /// 8 | public class InAppWalletBrowser : IThirdwebBrowser 9 | { 10 | private TaskCompletionSource _taskCompletionSource; 11 | private static readonly HttpListener _httpListener = new(); 12 | 13 | private readonly string _redirectHtmlOverride; 14 | 15 | public InAppWalletBrowser(string redirectHtmlOverride = null) 16 | { 17 | _httpListener.Prefixes.Add("http://localhost:8080/"); 18 | this._redirectHtmlOverride = redirectHtmlOverride; 19 | } 20 | 21 | /// 22 | /// Initiates a login process using the in-app browser. 23 | /// 24 | /// The Thirdweb client instance. 25 | /// The URL to initiate the login process. 26 | /// The URL to redirect to after login. 27 | /// An action to open the browser with the login URL. 28 | /// Optional cancellation token to cancel the operation. 29 | /// A task representing the asynchronous operation. The task result contains the login result. 30 | public async Task Login(ThirdwebClient client, string loginUrl, string redirectUrl, Action browserOpenAction, CancellationToken cancellationToken = default) 31 | { 32 | this._taskCompletionSource = new TaskCompletionSource(); 33 | 34 | _ = cancellationToken.Register(() => 35 | { 36 | _ = (this._taskCompletionSource?.TrySetCanceled()); 37 | 38 | StopHttpListener(); 39 | }); 40 | 41 | try 42 | { 43 | redirectUrl = AddForwardSlashIfNecessary(redirectUrl); 44 | if (_httpListener.Prefixes.Count == 0 || !_httpListener.Prefixes.Contains(redirectUrl)) 45 | { 46 | _httpListener.Prefixes.Clear(); 47 | _httpListener.Prefixes.Add(redirectUrl); 48 | } 49 | _httpListener.Start(); 50 | _ = _httpListener.BeginGetContext(this.IncomingHttpRequest, _httpListener); 51 | 52 | browserOpenAction.Invoke(loginUrl); 53 | 54 | var completedTask = await Task.WhenAny(this._taskCompletionSource.Task, Task.Delay(TimeSpan.FromSeconds(120), cancellationToken)); 55 | return completedTask == this._taskCompletionSource.Task ? await this._taskCompletionSource.Task : new BrowserResult(BrowserStatus.Timeout, null, "The operation timed out."); 56 | } 57 | catch (TaskCanceledException) 58 | { 59 | return new BrowserResult(BrowserStatus.UserCanceled, null, "The operation was cancelled."); 60 | } 61 | catch (Exception ex) 62 | { 63 | return new BrowserResult(BrowserStatus.UnknownError, null, $"An error occurred: {ex.Message}"); 64 | } 65 | finally 66 | { 67 | StopHttpListener(); 68 | } 69 | } 70 | 71 | /// 72 | /// Stops the HTTP listener. 73 | /// 74 | private static void StopHttpListener() 75 | { 76 | if (_httpListener != null && _httpListener.IsListening) 77 | { 78 | _httpListener.Stop(); 79 | } 80 | } 81 | 82 | /// 83 | /// Handles incoming HTTP requests. 84 | /// 85 | /// The result of the asynchronous operation. 86 | private void IncomingHttpRequest(IAsyncResult result) 87 | { 88 | var httpListener = (HttpListener)result.AsyncState; 89 | if (!httpListener.IsListening) 90 | { 91 | return; 92 | } 93 | 94 | var httpContext = httpListener.EndGetContext(result); 95 | var httpRequest = httpContext.Request; 96 | var httpResponse = httpContext.Response; 97 | var buffer = System.Text.Encoding.UTF8.GetBytes(this._redirectHtmlOverride ?? Constants.REDIRECT_HTML); 98 | 99 | httpResponse.ContentLength64 = buffer.Length; 100 | var output = httpResponse.OutputStream; 101 | output.Write(buffer, 0, buffer.Length); 102 | output.Close(); 103 | 104 | this._taskCompletionSource.SetResult(new BrowserResult(BrowserStatus.Success, httpRequest.Url.ToString())); 105 | } 106 | 107 | /// 108 | /// Adds a forward slash to the URL if necessary. 109 | /// 110 | /// The URL to check. 111 | /// The URL with a forward slash added if necessary. 112 | private static string AddForwardSlashIfNecessary(string url) 113 | { 114 | var forwardSlash = "/"; 115 | if (!url.EndsWith(forwardSlash)) 116 | { 117 | url += forwardSlash; 118 | } 119 | return url; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /thirdweb.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.0.31903.59 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thirdweb", "Thirdweb\Thirdweb.csproj", "{98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thirdweb.Console", "Thirdweb.Console\Thirdweb.Console.csproj", "{D78B4271-7DE9-4C54-BB97-31FBBD25A093}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thirdweb.Tests", "Thirdweb.Tests\Thirdweb.Tests.csproj", "{7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thirdweb.Generator", "Thirdweb.Generator\Thirdweb.Generator.csproj", "{FC27BC73-7F36-4658-9CBD-940957EE6795}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionProperties) = preSolution 15 | HideSolutionNode = FALSE 16 | EndGlobalSection 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x64 = Release|x64 23 | Release|x86 = Release|x86 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Debug|x64.ActiveCfg = Debug|Any CPU 29 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Debug|x64.Build.0 = Debug|Any CPU 30 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Debug|x86.ActiveCfg = Debug|Any CPU 31 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Debug|x86.Build.0 = Debug|Any CPU 32 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Release|x64.ActiveCfg = Release|Any CPU 35 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Release|x64.Build.0 = Release|Any CPU 36 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Release|x86.ActiveCfg = Release|Any CPU 37 | {98BA8071-A8BF-44A5-9DDC-7BBDE4E732E8}.Release|x86.Build.0 = Release|Any CPU 38 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Debug|x64.ActiveCfg = Debug|Any CPU 41 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Debug|x64.Build.0 = Debug|Any CPU 42 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Debug|x86.ActiveCfg = Debug|Any CPU 43 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Debug|x86.Build.0 = Debug|Any CPU 44 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Release|x64.ActiveCfg = Release|Any CPU 47 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Release|x64.Build.0 = Release|Any CPU 48 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Release|x86.ActiveCfg = Release|Any CPU 49 | {D78B4271-7DE9-4C54-BB97-31FBBD25A093}.Release|x86.Build.0 = Release|Any CPU 50 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Debug|x64.Build.0 = Debug|Any CPU 54 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Debug|x86.ActiveCfg = Debug|Any CPU 55 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Debug|x86.Build.0 = Debug|Any CPU 56 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Release|x64.ActiveCfg = Release|Any CPU 59 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Release|x64.Build.0 = Release|Any CPU 60 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Release|x86.ActiveCfg = Release|Any CPU 61 | {7CEBE316-4F2E-433B-8B1D-CBE8F8EE328F}.Release|x86.Build.0 = Release|Any CPU 62 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Debug|x64.ActiveCfg = Debug|Any CPU 65 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Debug|x64.Build.0 = Debug|Any CPU 66 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Debug|x86.ActiveCfg = Debug|Any CPU 67 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Debug|x86.Build.0 = Debug|Any CPU 68 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Release|x64.ActiveCfg = Release|Any CPU 71 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Release|x64.Build.0 = Release|Any CPU 72 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Release|x86.ActiveCfg = Release|Any CPU 73 | {FC27BC73-7F36-4658-9CBD-940957EE6795}.Release|x86.Build.0 = Release|Any CPU 74 | EndGlobalSection 75 | EndGlobal 76 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Client/ThirdwebClient.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Thirdweb.Api; 3 | 4 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Thirdweb.Tests")] 5 | 6 | namespace Thirdweb; 7 | 8 | /// 9 | /// Represents a client for interacting with the Thirdweb API. 10 | /// 11 | public class ThirdwebClient 12 | { 13 | /// 14 | /// Gets the HTTP client used by the Thirdweb client. 15 | /// 16 | public IThirdwebHttpClient HttpClient { get; } 17 | 18 | /// 19 | /// Gets the client ID. 20 | /// 21 | public string ClientId { get; } 22 | 23 | /// 24 | /// Low-level interaction with https://api.thirdweb.com 25 | /// Used in some places to enhance the core SDK functionality, or even extend it 26 | /// 27 | public ThirdwebApiClient Api { get; internal set; } 28 | 29 | internal string SecretKey { get; } 30 | internal string BundleId { get; } 31 | internal TimeoutOptions FetchTimeoutOptions { get; } 32 | internal Dictionary RpcOverrides { get; } 33 | 34 | private ThirdwebClient( 35 | string clientId = null, 36 | string secretKey = null, 37 | string bundleId = null, 38 | TimeoutOptions fetchTimeoutOptions = null, 39 | IThirdwebHttpClient httpClient = null, 40 | string sdkName = null, 41 | string sdkOs = null, 42 | string sdkPlatform = null, 43 | string sdkVersion = null, 44 | Dictionary rpcOverrides = null 45 | ) 46 | { 47 | if (string.IsNullOrEmpty(clientId) && string.IsNullOrEmpty(secretKey)) 48 | { 49 | throw new InvalidOperationException("ClientId or SecretKey must be provided"); 50 | } 51 | 52 | // Respects provided clientId if any, otherwise computes it from secretKey 53 | this.ClientId = !string.IsNullOrEmpty(clientId) ? clientId : (string.IsNullOrEmpty(secretKey) ? null : Utils.ComputeClientIdFromSecretKey(secretKey)); 54 | this.SecretKey = secretKey; 55 | this.BundleId = bundleId; 56 | 57 | this.FetchTimeoutOptions = fetchTimeoutOptions ?? new TimeoutOptions(); 58 | 59 | var defaultHeaders = new Dictionary 60 | { 61 | { "x-sdk-name", sdkName ?? "Thirdweb.NET" }, 62 | { "x-sdk-os", sdkOs ?? System.Runtime.InteropServices.RuntimeInformation.OSDescription }, 63 | { "x-sdk-platform", sdkPlatform ?? "dotnet" }, 64 | { "x-sdk-version", sdkVersion ?? Constants.VERSION }, 65 | { "x-client-id", this.ClientId }, 66 | }; 67 | if (!string.IsNullOrEmpty(this.BundleId)) 68 | { 69 | defaultHeaders.Add("x-bundle-id", this.BundleId); 70 | } 71 | if (!string.IsNullOrEmpty(this.SecretKey)) 72 | { 73 | defaultHeaders.Add("x-secret-key", this.SecretKey); 74 | } 75 | 76 | this.HttpClient = httpClient ?? new ThirdwebHttpClient(); 77 | this.HttpClient.SetHeaders(defaultHeaders); 78 | 79 | this.RpcOverrides = rpcOverrides; 80 | 81 | // Initialize the API client with the wrapped HTTP client 82 | var wrappedHttpClient = new ThirdwebHttpClientWrapper(this.HttpClient); 83 | this.Api = new ThirdwebApiClient(wrappedHttpClient); 84 | } 85 | 86 | /// 87 | /// Creates a new instance of . 88 | /// 89 | /// The client ID (optional). 90 | /// The secret key (optional). 91 | /// The bundle ID (optional). 92 | /// The fetch timeout options (optional). 93 | /// The HTTP client (optional). 94 | /// The SDK name (optional). 95 | /// The SDK OS (optional). 96 | /// The SDK platform (optional). 97 | /// The SDK version (optional). 98 | /// Mapping of chain id to your custom rpc for that chain id (optional, defaults to thirdweb RPC). 99 | /// A new instance of . 100 | public static ThirdwebClient Create( 101 | string clientId = null, 102 | string secretKey = null, 103 | string bundleId = null, 104 | TimeoutOptions fetchTimeoutOptions = null, 105 | IThirdwebHttpClient httpClient = null, 106 | string sdkName = null, 107 | string sdkOs = null, 108 | string sdkPlatform = null, 109 | string sdkVersion = null, 110 | Dictionary rpcOverrides = null 111 | ) 112 | { 113 | return new ThirdwebClient(clientId, secretKey, bundleId, fetchTimeoutOptions, httpClient, sdkName, sdkOs, sdkPlatform, sdkVersion, rpcOverrides); 114 | } 115 | 116 | internal void UpdateApiClient(IThirdwebHttpClient httpClient) 117 | { 118 | var wrappedHttpClient = new ThirdwebHttpClientWrapper(httpClient); 119 | this.Api = new ThirdwebApiClient(wrappedHttpClient); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Thirdweb.Tests/Thirdweb.Wallets/Thirdweb.Wallets.Tests.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.Tests.Wallets; 2 | 3 | public class WalletTests : BaseTests 4 | { 5 | public WalletTests(ITestOutputHelper output) 6 | : base(output) { } 7 | 8 | [Fact(Timeout = 120000)] 9 | public async Task GetAddress() 10 | { 11 | var wallet = await this.GetSmartAccount(); 12 | Assert.Equal(await wallet.GetAddress(), await wallet.GetAddress()); 13 | } 14 | 15 | [Fact(Timeout = 120000)] 16 | public async Task PersonalSignRaw() 17 | { 18 | var wallet = await this.GetGuestAccount(); 19 | var message = "Hello, world!"; 20 | var signature = await wallet.PersonalSign(System.Text.Encoding.UTF8.GetBytes(message)); 21 | Assert.NotNull(signature); 22 | } 23 | 24 | [Fact(Timeout = 120000)] 25 | public async Task PersonalSign() 26 | { 27 | var wallet = await this.GetGuestAccount(); 28 | var message = "Hello, world!"; 29 | var signature = await wallet.PersonalSign(message); 30 | Assert.NotNull(signature); 31 | } 32 | 33 | [Fact(Timeout = 120000)] 34 | public async Task SignTypedDataV4() 35 | { 36 | var wallet = await this.GetSmartAccount(); 37 | var json = 38 | /*lang=json,strict*/ 39 | "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbBBbBbbBbBbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}"; 40 | var signature = await wallet.SignTypedDataV4(json); 41 | Assert.NotNull(signature); 42 | } 43 | 44 | [Fact(Timeout = 120000)] 45 | public async Task SignTypedDataV4_Typed() 46 | { 47 | var wallet = await this.GetSmartAccount(); 48 | var typedData = EIP712.GetTypedDefinition_SmartAccount_AccountMessage("Account", "1", 421614, await wallet.GetAddress()); 49 | var accountMessage = new AccountAbstraction.AccountMessage { Message = System.Text.Encoding.UTF8.GetBytes("Hello, world!").HashPrefixedMessage() }; 50 | var signature = await wallet.SignTypedDataV4(accountMessage, typedData); 51 | Assert.NotNull(signature); 52 | 53 | var signerAcc = await wallet.GetPersonalWallet(); 54 | var gen1 = await EIP712.GenerateSignature_SmartAccount_AccountMessage( 55 | "Account", 56 | "1", 57 | 421614, 58 | await wallet.GetAddress(), 59 | System.Text.Encoding.UTF8.GetBytes("Hello, world!").HashPrefixedMessage(), 60 | signerAcc 61 | ); 62 | Assert.Equal(gen1, signature); 63 | 64 | var req = new AccountAbstraction.SignerPermissionRequest() 65 | { 66 | Signer = await wallet.GetAddress(), 67 | IsAdmin = 0, 68 | ApprovedTargets = new List() { Constants.ADDRESS_ZERO }, 69 | NativeTokenLimitPerTransaction = 0, 70 | PermissionStartTimestamp = 0, 71 | ReqValidityStartTimestamp = 0, 72 | PermissionEndTimestamp = 0, 73 | Uid = new byte[32], 74 | }; 75 | 76 | var typedData2 = EIP712.GetTypedDefinition_SmartAccount("Account", "1", 421614, await wallet.GetAddress()); 77 | var signature2 = await wallet.SignTypedDataV4(req, typedData2); 78 | Assert.NotNull(signature2); 79 | 80 | var gen2 = await EIP712.GenerateSignature_SmartAccount("Account", "1", 421614, await wallet.GetAddress(), req, signerAcc); 81 | Assert.Equal(gen2, signature2); 82 | } 83 | 84 | [Fact(Timeout = 120000)] 85 | public async Task SignTransaction() 86 | { 87 | var wallet = await this.GetSmartAccount(); 88 | var transaction = new ThirdwebTransactionInput(chainId: 421614, to: await wallet.GetAddress(), data: "0x", value: 0, gas: 21000, gasPrice: 10000000000, nonce: 9999999999999); 89 | _ = ThirdwebRPC.GetRpcInstance(this.Client, 421614); 90 | var signature = await wallet.SignTransaction(transaction); 91 | Assert.NotNull(signature); 92 | } 93 | 94 | [Fact(Timeout = 120000)] 95 | public async Task SwitchNetwork_Success() 96 | { 97 | var smartWallet = await this.GetSmartAccount(); 98 | var wrappedSmartWallet = await SmartWallet.Create(personalWallet: smartWallet, chainId: 421614); 99 | 100 | Assert.Equal(421614, smartWallet.ActiveChainId); 101 | Assert.Equal(421614, wrappedSmartWallet.ActiveChainId); 102 | 103 | await wrappedSmartWallet.SwitchNetwork(11155111); 104 | 105 | Assert.Equal(11155111, wrappedSmartWallet.ActiveChainId); 106 | Assert.Equal(11155111, smartWallet.ActiveChainId); 107 | 108 | await (await this.GetGuestAccount()).SwitchNetwork(11155111); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Thirdweb.Tests/Thirdweb.Transactions/Thirdweb.ZkSmartWallet.Tests.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb.Tests.Wallets; 2 | 3 | public class ZkSmartWalletTests : BaseTests 4 | { 5 | public ZkSmartWalletTests(ITestOutputHelper output) 6 | : base(output) { } 7 | 8 | private async Task GetSmartAccount(int zkChainId = 300, bool gasless = true) 9 | { 10 | var privateKeyAccount = await this.GetGuestAccount(); 11 | var smartAccount = await SmartWallet.Create(personalWallet: privateKeyAccount, gasless: gasless, chainId: zkChainId); 12 | return smartAccount; 13 | } 14 | 15 | [Fact(Timeout = 120000)] 16 | public async Task GetAddress_Success() 17 | { 18 | var account = await this.GetSmartAccount(); 19 | Assert.NotNull(await account.GetAddress()); 20 | } 21 | 22 | [Fact(Timeout = 120000)] 23 | public async Task PersonalSign_Success() 24 | { 25 | var account = await this.GetSmartAccount(zkChainId: 300); 26 | var message = "Hello, World!"; 27 | var signature = await account.PersonalSign(message); 28 | Assert.NotNull(signature); 29 | Assert.True(signature.Length > 0); 30 | } 31 | 32 | [Fact(Timeout = 120000)] 33 | public async Task CreateSessionKey_Throws() 34 | { 35 | var account = await this.GetSmartAccount(); 36 | _ = await Assert.ThrowsAsync(async () => 37 | await account.CreateSessionKey( 38 | signerAddress: await account.GetAddress(), 39 | approvedTargets: new List() { Constants.ADDRESS_ZERO }, 40 | nativeTokenLimitPerTransactionInWei: "0", 41 | permissionStartTimestamp: "0", 42 | permissionEndTimestamp: (Utils.GetUnixTimeStampNow() + 86400).ToString(), 43 | reqValidityStartTimestamp: "0", 44 | reqValidityEndTimestamp: Utils.GetUnixTimeStampIn10Years().ToString() 45 | ) 46 | ); 47 | } 48 | 49 | [Fact(Timeout = 120000)] 50 | public async Task AddAdmin_Throws() 51 | { 52 | var account = await this.GetSmartAccount(); 53 | _ = await Assert.ThrowsAsync(async () => await account.AddAdmin(Constants.ADDRESS_ZERO)); 54 | } 55 | 56 | [Fact(Timeout = 120000)] 57 | public async Task RemoveAdmin_Throws() 58 | { 59 | var account = await this.GetSmartAccount(); 60 | _ = await Assert.ThrowsAsync(async () => await account.RemoveAdmin(Constants.ADDRESS_ZERO)); 61 | } 62 | 63 | [Fact(Timeout = 120000)] 64 | public async Task IsDeployed_ReturnsTrue() 65 | { 66 | var account = await this.GetSmartAccount(); 67 | Assert.True(await account.IsDeployed()); 68 | } 69 | 70 | [Fact(Timeout = 120000)] 71 | public async Task SendGaslessZkTx_Success() 72 | { 73 | var account = await this.GetSmartAccount(); 74 | var hash = await account.SendTransaction( 75 | new ThirdwebTransactionInput(300) 76 | { 77 | From = await account.GetAddress(), 78 | To = await account.GetAddress(), 79 | Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), 80 | Data = "0x", 81 | } 82 | ); 83 | Assert.NotNull(hash); 84 | Assert.True(hash.Length == 66); 85 | } 86 | 87 | // 88 | // public async Task SendGaslessZkTx_ZkCandy_Success() 89 | // { 90 | // var account = await this.GetSmartAccount(zkChainId: 302); 91 | // var hash = await account.SendTransaction( 92 | // new ThirdwebTransactionInput(302) 93 | // { 94 | // From = await account.GetAddress(), 95 | // To = await account.GetAddress(), 96 | // Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), 97 | // Data = "0x" 98 | // } 99 | // ); 100 | // Assert.NotNull(hash); 101 | // Assert.True(hash.Length == 66); 102 | // } 103 | 104 | // [Fact(Timeout = 120000)] 105 | // public async Task SendGaslessZkTx_Abstract_Success() 106 | // { 107 | // var account = await this.GetSmartAccount(zkChainId: 11124); 108 | // var hash = await account.SendTransaction( 109 | // new ThirdwebTransactionInput(11124) 110 | // { 111 | // From = await account.GetAddress(), 112 | // To = await account.GetAddress(), 113 | // Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), 114 | // Data = "0x" 115 | // } 116 | // ); 117 | // Assert.NotNull(hash); 118 | // Assert.True(hash.Length == 66); 119 | // } 120 | 121 | // [Fact(Timeout = 120000)] 122 | // public async Task SendGaslessZkTx_Creator_Success() 123 | // { 124 | // var account = await this.GetSmartAccount(zkChainId: 4654); 125 | // var hash = await account.SendTransaction( 126 | // new ThirdwebTransactionInput(4654) 127 | // { 128 | // From = await account.GetAddress(), 129 | // To = await account.GetAddress(), 130 | // Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), 131 | // Data = "0x" 132 | // } 133 | // ); 134 | // Assert.NotNull(hash); 135 | // Assert.True(hash.Length == 66); 136 | // } 137 | 138 | [Fact(Timeout = 120000)] 139 | public async Task ZkSync_Switch() 140 | { 141 | var account = await this.GetSmartAccount(zkChainId: 300); 142 | _ = await account.SendTransaction( 143 | new ThirdwebTransactionInput(11124) 144 | { 145 | From = await account.GetAddress(), 146 | To = await account.GetAddress(), 147 | Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), 148 | Data = "0x", 149 | } 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Http/ThirdwebHttpClient.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | /// 4 | /// Represents a HTTP client for the Thirdweb SDK. 5 | /// 6 | public class ThirdwebHttpClient : IThirdwebHttpClient 7 | { 8 | /// 9 | /// Gets the headers for the HTTP client. 10 | /// 11 | public Dictionary Headers { get; private set; } 12 | 13 | private readonly HttpClient _httpClient; 14 | private bool _disposed; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public ThirdwebHttpClient() 20 | { 21 | this._httpClient = new HttpClient(); 22 | this.Headers = new Dictionary(); 23 | } 24 | 25 | /// 26 | /// Sets the headers for the HTTP client. 27 | /// 28 | /// The headers to set. 29 | public void SetHeaders(Dictionary headers) 30 | { 31 | this.Headers = new Dictionary(headers); 32 | } 33 | 34 | /// 35 | /// Clears all headers from the HTTP client. 36 | /// 37 | public void ClearHeaders() 38 | { 39 | this.Headers.Clear(); 40 | } 41 | 42 | /// 43 | /// Adds a header to the HTTP client. 44 | /// 45 | /// The header key. 46 | /// The header value. 47 | public void AddHeader(string key, string value) 48 | { 49 | this.Headers.Add(key, value); 50 | } 51 | 52 | /// 53 | /// Removes a header from the HTTP client. 54 | /// 55 | /// The header key. 56 | public void RemoveHeader(string key) 57 | { 58 | _ = this.Headers.Remove(key); 59 | } 60 | 61 | private void AddHeaders(HttpRequestMessage request) 62 | { 63 | foreach (var header in this.Headers) 64 | { 65 | _ = request.Headers.TryAddWithoutValidation(header.Key, header.Value); 66 | } 67 | } 68 | 69 | /// 70 | /// Sends a GET request to the specified URI. 71 | /// 72 | /// The request URI. 73 | /// The cancellation token. 74 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 75 | public async Task GetAsync(string requestUri, CancellationToken cancellationToken = default) 76 | { 77 | var request = new HttpRequestMessage(HttpMethod.Get, requestUri); 78 | this.AddHeaders(request); 79 | var result = await this._httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); 80 | #pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods 81 | var resultContent = new ThirdwebHttpContent(await result.Content.ReadAsByteArrayAsync().ConfigureAwait(false)); 82 | #pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods 83 | return new ThirdwebHttpResponseMessage((long)result.StatusCode, resultContent, result.IsSuccessStatusCode); 84 | } 85 | 86 | /// 87 | /// Sends a POST request to the specified URI. 88 | /// 89 | /// The request URI. 90 | /// The HTTP content to send. 91 | /// The cancellation token. 92 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 93 | public async Task PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken = default) 94 | { 95 | var request = new HttpRequestMessage(HttpMethod.Post, requestUri) { Content = content }; 96 | this.AddHeaders(request); 97 | var result = await this._httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); 98 | #pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods 99 | var resultContent = new ThirdwebHttpContent(await result.Content.ReadAsByteArrayAsync().ConfigureAwait(false)); 100 | #pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods 101 | return new ThirdwebHttpResponseMessage((long)result.StatusCode, resultContent, result.IsSuccessStatusCode); 102 | } 103 | 104 | /// 105 | /// Sends a PUT request to the specified URI. 106 | /// 107 | /// The request URI. 108 | /// The HTTP content to send. 109 | /// The cancellation token. 110 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 111 | public Task PutAsync(string requestUri, HttpContent content, CancellationToken cancellationToken = default) 112 | { 113 | throw new NotImplementedException(); 114 | } 115 | 116 | /// 117 | /// Sends a DELETE request to the specified URI. 118 | /// 119 | /// The request URI. 120 | /// The cancellation token. 121 | /// A task that represents the asynchronous operation. The task result contains the HTTP response message. 122 | public Task DeleteAsync(string requestUri, CancellationToken cancellationToken = default) 123 | { 124 | throw new NotImplementedException(); 125 | } 126 | 127 | /// 128 | /// Disposes the HTTP client. 129 | /// 130 | /// Whether the client is being disposed. 131 | protected virtual void Dispose(bool disposing) 132 | { 133 | if (!this._disposed) 134 | { 135 | if (disposing) 136 | { 137 | this._httpClient.Dispose(); 138 | } 139 | this._disposed = true; 140 | } 141 | } 142 | 143 | /// 144 | /// Disposes the HTTP client. 145 | /// 146 | public void Dispose() 147 | { 148 | this.Dispose(true); 149 | GC.SuppressFinalize(this); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Thirdweb.Tests/Thirdweb.RPC/Thirdweb.RPC.Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Thirdweb.Tests.RPC; 4 | 5 | public class RpcTests : BaseTests 6 | { 7 | public RpcTests(ITestOutputHelper output) 8 | : base(output) { } 9 | 10 | [Fact] 11 | public void RpcOverride_None() 12 | { 13 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 14 | var thirdwebRpc = $"https://1.rpc.thirdweb.com/{client.ClientId}"; 15 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 16 | Assert.Equal(thirdwebRpc, rpc.RpcUrl.AbsoluteUri); 17 | } 18 | 19 | [Fact] 20 | public void RpcOverride_Single() 21 | { 22 | var customRpc = "https://eth.llamarpc.com/"; 23 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, rpcOverrides: new Dictionary { { 1, customRpc } }); 24 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 25 | Assert.Equal(customRpc, client.RpcOverrides[1]); 26 | Assert.Equal(customRpc, rpc.RpcUrl.AbsoluteUri); 27 | } 28 | 29 | [Fact] 30 | public void RpcOverride_Multiple() 31 | { 32 | var customRpc1 = "https://eth.llamarpc.com/"; 33 | var customRpc42161 = "https://arbitrum.llamarpc.com/"; 34 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, rpcOverrides: new Dictionary { { 1, customRpc1 }, { 42161, customRpc42161 } }); 35 | var rpc1 = ThirdwebRPC.GetRpcInstance(client, 1); 36 | var rpc42161 = ThirdwebRPC.GetRpcInstance(client, 42161); 37 | Assert.Equal(customRpc1, client.RpcOverrides[1]); 38 | Assert.Equal(customRpc1, rpc1.RpcUrl.AbsoluteUri); 39 | Assert.Equal(customRpc42161, client.RpcOverrides[42161]); 40 | Assert.Equal(customRpc42161, rpc42161.RpcUrl.AbsoluteUri); 41 | } 42 | 43 | [Fact] 44 | public void RpcOverride_Single_Default() 45 | { 46 | var customRpc = "https://eth.llamarpc.com/"; 47 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, rpcOverrides: new Dictionary { { 1, customRpc } }); 48 | 49 | var thirdwebRpc = $"https://42161.rpc.thirdweb.com/{client.ClientId}"; 50 | 51 | var rpc1 = ThirdwebRPC.GetRpcInstance(client, 1); 52 | Assert.Equal(customRpc, rpc1.RpcUrl.AbsoluteUri); 53 | 54 | var rpc42161 = ThirdwebRPC.GetRpcInstance(client, 42161); 55 | Assert.Equal(thirdwebRpc, rpc42161.RpcUrl.AbsoluteUri); 56 | } 57 | 58 | [Fact] 59 | public void RpcOverride_Multiple_Default() 60 | { 61 | var customRpc1 = "https://eth.llamarpc.com/"; 62 | var customRpc42161 = "https://arbitrum.llamarpc.com/"; 63 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, rpcOverrides: new Dictionary { { 1, customRpc1 }, { 42161, customRpc42161 } }); 64 | 65 | var thirdwebRpc = $"https://421614.rpc.thirdweb.com/{client.ClientId}"; 66 | 67 | var rpc1 = ThirdwebRPC.GetRpcInstance(client, 1); 68 | Assert.Equal(customRpc1, rpc1.RpcUrl.AbsoluteUri); 69 | 70 | var rpc42161 = ThirdwebRPC.GetRpcInstance(client, 42161); 71 | Assert.Equal(customRpc42161, rpc42161.RpcUrl.AbsoluteUri); 72 | 73 | var rpc421614 = ThirdwebRPC.GetRpcInstance(client, 421614); 74 | Assert.Equal(thirdwebRpc, rpc421614.RpcUrl.AbsoluteUri); 75 | } 76 | 77 | [Fact(Timeout = 120000)] 78 | public async Task Request_WithRpcOverride() 79 | { 80 | var customRpc = "https://eth.llamarpc.com/"; 81 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, rpcOverrides: new Dictionary { { 1, customRpc } }); 82 | 83 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 84 | var blockNumber = await rpc.SendRequestAsync("eth_blockNumber"); 85 | Assert.NotNull(blockNumber); 86 | Assert.StartsWith("0x", blockNumber); 87 | 88 | var rpc2 = ThirdwebRPC.GetRpcInstance(client, 42161); 89 | var blockNumber2 = await rpc2.SendRequestAsync("eth_blockNumber"); 90 | Assert.NotNull(blockNumber2); 91 | Assert.StartsWith("0x", blockNumber2); 92 | } 93 | 94 | [Fact(Timeout = 120000)] 95 | public async Task GetBlockNumber() 96 | { 97 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, fetchTimeoutOptions: new TimeoutOptions(rpc: 10000)); 98 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 99 | var blockNumber = await rpc.SendRequestAsync("eth_blockNumber"); 100 | Assert.NotNull(blockNumber); 101 | Assert.StartsWith("0x", blockNumber); 102 | } 103 | 104 | [Fact(Timeout = 120000)] 105 | public async Task TestAuth() 106 | { 107 | var client = ThirdwebClient.Create(clientId: "hi", fetchTimeoutOptions: new TimeoutOptions(rpc: 60000)); 108 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 109 | var ex = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_blockNumber")); 110 | Assert.Contains("401", ex.Message); 111 | } 112 | 113 | [Fact(Timeout = 120000)] 114 | public async Task TestTimeout() 115 | { 116 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, fetchTimeoutOptions: new TimeoutOptions(rpc: 0)); 117 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 118 | _ = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_chainId")); 119 | } 120 | 121 | [Fact(Timeout = 120000)] 122 | public async Task TestDeserialization() 123 | { 124 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 125 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 126 | var exception = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_blockNumber")); 127 | Assert.Equal("Failed to deserialize RPC response.", exception.Message); 128 | } 129 | 130 | [Fact(Timeout = 120000)] 131 | public void TestBadInitialization() 132 | { 133 | var clientException = Assert.Throws(() => ThirdwebRPC.GetRpcInstance(null, 0)); 134 | Assert.Equal("client", clientException.ParamName); 135 | var chainIdException = Assert.Throws(() => ThirdwebRPC.GetRpcInstance(ThirdwebClient.Create(secretKey: this.SecretKey), 0)); 136 | Assert.Equal("Invalid Chain ID", chainIdException.Message); 137 | } 138 | 139 | [Fact(Timeout = 120000)] 140 | public async Task TestBundleIdRpc() 141 | { 142 | var client = ThirdwebClient.Create(clientId: this.ClientIdBundleIdOnly, bundleId: this.BundleIdBundleIdOnly); 143 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 144 | var blockNumber = await rpc.SendRequestAsync("eth_blockNumber"); 145 | Assert.NotNull(blockNumber); 146 | Assert.StartsWith("0x", blockNumber); 147 | } 148 | 149 | [Fact(Timeout = 120000)] 150 | public async Task TestRpcError() 151 | { 152 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 153 | var rpc = ThirdwebRPC.GetRpcInstance(client, 1); 154 | var exception = await Assert.ThrowsAsync(async () => await rpc.SendRequestAsync("eth_invalidMethod")); 155 | Assert.Contains("RPC Error for request", exception.Message); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/ThirdwebBundler.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Thirdweb.AccountAbstraction; 4 | 5 | public static class ThirdwebBundler 6 | { 7 | // EIP 7702 requests 8 | public static async Task TwGetDelegationContract(ThirdwebClient client, string url, int requestId) 9 | { 10 | var response = await BundlerRequest(client, url, requestId, "tw_getDelegationContract").ConfigureAwait(false); 11 | return JsonConvert.DeserializeObject(response.Result.ToString()); 12 | } 13 | 14 | public static async Task TwExecute( 15 | ThirdwebClient client, 16 | string url, 17 | object requestId, 18 | string eoaAddress, 19 | WrappedCalls wrappedCalls, 20 | string signature, 21 | EIP7702Authorization? authorization 22 | ) 23 | { 24 | var response = await BundlerRequest( 25 | client, 26 | url, 27 | requestId, 28 | "tw_execute", 29 | eoaAddress, 30 | wrappedCalls.EncodeForHttp(), 31 | signature, 32 | authorization == null 33 | ? null 34 | : new 35 | { 36 | chainId = authorization?.ChainId.HexToNumber(), 37 | address = authorization?.Address, 38 | nonce = authorization?.Nonce.HexToNumber().ToString(), 39 | yParity = authorization?.YParity.HexToNumber(), 40 | r = authorization?.R.HexToNumber().ToString(), 41 | s = authorization?.S.HexToNumber().ToString(), 42 | } 43 | ) 44 | .ConfigureAwait(false); 45 | return JsonConvert.DeserializeObject(response.Result.ToString()); 46 | } 47 | 48 | public static async Task TwGetTransactionHash(ThirdwebClient client, string url, int requestId, string queueId) 49 | { 50 | var response = await BundlerRequest(client, url, requestId, "tw_getTransactionHash", queueId).ConfigureAwait(false); 51 | return JsonConvert.DeserializeObject(response.Result.ToString()); 52 | } 53 | 54 | // Bundler requests 55 | 56 | public static async Task EthGetUserOperationReceipt(ThirdwebClient client, string bundlerUrl, object requestId, string userOpHash) 57 | { 58 | var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_getUserOperationReceipt", userOpHash).ConfigureAwait(false); 59 | return JsonConvert.DeserializeObject(response.Result.ToString()); 60 | } 61 | 62 | public static async Task EthSendUserOperation(ThirdwebClient client, string bundlerUrl, object requestId, object userOp, string entryPoint) 63 | { 64 | var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_sendUserOperation", userOp, entryPoint).ConfigureAwait(false); 65 | return response.Result.ToString(); 66 | } 67 | 68 | public static async Task EthEstimateUserOperationGas( 69 | ThirdwebClient client, 70 | string bundlerUrl, 71 | object requestId, 72 | object userOp, 73 | string entryPoint, 74 | object stateOverrides = null 75 | ) 76 | { 77 | var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_estimateUserOperationGas", userOp, entryPoint, stateOverrides).ConfigureAwait(false); 78 | return JsonConvert.DeserializeObject(response.Result.ToString()); 79 | } 80 | 81 | public static async Task ThirdwebGetUserOperationGasPrice(ThirdwebClient client, string bundlerUrl, object requestId) 82 | { 83 | var response = await BundlerRequest(client, bundlerUrl, requestId, "thirdweb_getUserOperationGasPrice").ConfigureAwait(false); 84 | return JsonConvert.DeserializeObject(response.Result.ToString()); 85 | } 86 | 87 | // Paymaster requests 88 | 89 | public static async Task PMSponsorUserOperation(ThirdwebClient client, string paymasterUrl, object requestId, object userOp, string entryPoint) 90 | { 91 | var response = await BundlerRequest(client, paymasterUrl, requestId, "pm_sponsorUserOperation", userOp, entryPoint).ConfigureAwait(false); 92 | return JsonConvert.DeserializeObject(response.Result.ToString()); 93 | } 94 | 95 | public static async Task ZkPaymasterData(ThirdwebClient client, string paymasterUrl, object requestId, ThirdwebTransactionInput txInput) 96 | { 97 | var response = await BundlerRequest(client, paymasterUrl, requestId, "zk_paymasterData", txInput).ConfigureAwait(false); 98 | try 99 | { 100 | return JsonConvert.DeserializeObject(response.Result.ToString()); 101 | } 102 | catch 103 | { 104 | return new ZkPaymasterDataResponse() { Paymaster = null, PaymasterInput = null }; 105 | } 106 | } 107 | 108 | public static async Task ZkBroadcastTransaction(ThirdwebClient client, string paymasterUrl, object requestId, object txInput) 109 | { 110 | var response = await BundlerRequest(client, paymasterUrl, requestId, "zk_broadcastTransaction", txInput).ConfigureAwait(false); 111 | return JsonConvert.DeserializeObject(response.Result.ToString()); 112 | } 113 | 114 | // Request 115 | 116 | private static async Task BundlerRequest(ThirdwebClient client, string url, object requestId, string method, params object[] args) 117 | { 118 | var cts = new CancellationTokenSource(client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other)); 119 | 120 | var httpClient = client.HttpClient; 121 | var requestMessage = new RpcRequestMessage(requestId, method, args = args.Where(a => a != null).ToArray()); 122 | var requestMessageJson = JsonConvert.SerializeObject(requestMessage); 123 | 124 | var httpContent = new StringContent(requestMessageJson, System.Text.Encoding.UTF8, "application/json"); 125 | 126 | ThirdwebHttpResponseMessage httpResponse; 127 | try 128 | { 129 | httpResponse = await httpClient.PostAsync(url, httpContent, cts.Token).ConfigureAwait(false); 130 | } 131 | catch (TaskCanceledException) 132 | { 133 | throw new TimeoutException("The request timed out."); 134 | } 135 | 136 | if (!httpResponse.IsSuccessStatusCode) 137 | { 138 | throw new Exception($"Bundler Request Failed. Error: {httpResponse.StatusCode} - {await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false)}"); 139 | } 140 | 141 | var httpResponseJson = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); 142 | 143 | var response = JsonConvert.DeserializeObject(httpResponseJson); 144 | return response.Error != null ? throw new Exception($"Bundler Request Failed. Error: {response.Error.Code} - {response.Error.Message} - {response.Error.Data}") : response; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Thirdweb.Tests/Thirdweb.Storage/Thirdweb.Storage.Tests.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Thirdweb.Tests.Storage; 4 | 5 | public class StorageTests : BaseTests 6 | { 7 | public StorageTests(ITestOutputHelper output) 8 | : base(output) { } 9 | 10 | [Fact(Timeout = 120000)] 11 | public async Task DownloadTest_SecretKey() 12 | { 13 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 14 | var res = await ThirdwebStorage.Download(client, "https://1.rpc.thirdweb.com/providers"); 15 | Assert.NotNull(res); 16 | } 17 | 18 | [Fact(Timeout = 120000)] 19 | public async Task DownloadTest_Client_BundleId() 20 | { 21 | var client = ThirdwebClient.Create(clientId: this.ClientIdBundleIdOnly, bundleId: this.BundleIdBundleIdOnly); 22 | var res = await ThirdwebStorage.Download(client, "https://1.rpc.thirdweb.com/providers"); 23 | Assert.NotNull(res); 24 | } 25 | 26 | [Fact(Timeout = 120000)] 27 | public async Task DownloadTest_Deserialization() 28 | { 29 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 30 | var res = await ThirdwebStorage.Download>(client, "https://1.rpc.thirdweb.com/providers"); 31 | Assert.NotNull(res); 32 | Assert.NotEmpty(res); 33 | } 34 | 35 | [Fact(Timeout = 120000)] 36 | public async Task DownloadTest_NullUri() 37 | { 38 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 39 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.Download(client, null)); 40 | Assert.Equal("uri", exception.ParamName); 41 | } 42 | 43 | [Fact(Timeout = 120000)] 44 | public async Task DownloadTest_ThirdwebIPFS() 45 | { 46 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 47 | var res = await ThirdwebStorage.Download(client, "ipfs://QmRHf3sBEAaSkaPdjrnYZS7VH1jVgvNBJNoUXmiUyvUpNM/8"); 48 | Assert.NotNull(res); 49 | } 50 | 51 | [Fact(Timeout = 120000)] 52 | public async Task DownloadTest_Bytes() 53 | { 54 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 55 | var res = await ThirdwebStorage.Download(client, "https://1.rpc.thirdweb.com/providers"); 56 | Assert.NotNull(res); 57 | Assert.NotEmpty(res); 58 | } 59 | 60 | [Fact(Timeout = 120000)] 61 | public async Task DownloadTest_Base64Uri() 62 | { 63 | var uri = 64 | "data:application/json;base64,eyJuYW1lIjogIllvbml4IiwgImRlc2NyaXB0aW9uIjogIkZyb20gYSBkaXN0YW50IGZ1dHVyZSwgVGFpa28gTGFicyBoYXMgZGV2ZWxvcGVkIGEgc2VjcmV0IHdlYXBvbiB0byBjb21iYXQgdGhlIGdyZWF0IGNhbGFtaXR5IGtub3duIGFzICdGZWVzLCcgYSBtYXNzaXZlIGdhcyBjbG91ZCB0aGF0IHBvaXNvbnMgdGhlIGVudGlyZSBwbGFuZXQsIHB1dHRpbmcgYWxsIGNpdGl6ZW5zIGF0IHJpc2suIEFsdGhvdWdoIHRoZSBpZGVhIGlzIGJyaWxsaWFudCwgdGhlIFRhaWtvIE1vbmtleXMgYXJlIHN0aWxsIGltbWF0dXJlLCBhbG1vc3Qgc3R1cGlkISBUaGUgb25seSB3YXkgdG8gZmlnaHQgdGhpcyBzY291cmdlIGlzIHRvIGZpbmQgYSB3YXkgdG8gdHJhaW4gdGhlbSwgdHVybmluZyB0aGVtIGludG8gdGhlIG1vc3QgcG93ZXJmdWwgc2VjcmV0IHdlYXBvbiBvZiBhbGwuIEJ5IGNoYW5jZSwgYSBicmlkZ2UgaGFzIG9wZW5lZCBiZXR3ZWVuIHR3byB3b3JsZHMsIGFuZCBpdCBzZWVtcyB0aGF0IHRoZSBNb25rZXkgUmVhbG0gaXMgdGhlIGJlc3QgcGxhY2UgdG8gaW1wbGVtZW50IHRoZSBncmFuZCBwbGFuISBGaWdodCBhbmQgYmVjb21lIHRoZSBiZXN0IG1vbmtleTsgb25seSB5b3UgY2FuIG1ha2UgR2FzIEZlZXMgYSBkaXN0YW50IG1lbW9yeS4iLCAiaW1hZ2UiOiAiaXBmczovL1FtVzFqOFRKZGRSOVdVWEhDODFlb05SWGoyYVY4NndzRnJWWnJVYTRyY1dyejIvU01UYWlrb0xvdy5qcGciLCAiYW5pbWF0aW9uX3VybCI6ICJpcGZzOi8vUW1XMWo4VEpkZFI5V1VYSEM4MWVvTlJYajJhVjg2d3NGclZaclVhNHJjV3J6Mi9TTVRhaWtvSGlnaC5qcGciLCAicHJvcGVydGllcyI6IHsiTnVtYmVyIjogMSwgIlJQRyBDbGFzcyI6ICJOb25lIiwgIkNsYW4iOiAiU3R1cGlkIE1vbmtzIiwgIkxldmVsIjogMX19"; 65 | 66 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 67 | var res = await ThirdwebStorage.Download(client, uri); 68 | Assert.NotNull(res.Properties); 69 | var propertiesObj = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(res.Properties)); 70 | Assert.True(propertiesObj!.Count > 0); 71 | } 72 | 73 | [Fact(Timeout = 120000)] 74 | public async Task DownloadTest_404() 75 | { 76 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 77 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.Download(client, "https://example.com/invalid-file")); 78 | Assert.Contains("Failed to download", exception.Message); 79 | Assert.Contains("404", exception.Message); 80 | } 81 | 82 | [Fact(Timeout = 120000)] 83 | public async Task DownloadTest_Timeout() 84 | { 85 | var client = ThirdwebClient.Create(secretKey: this.SecretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 0)); 86 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.Download(client, "https://1.rpc.thirdweb.com/providers", 1)); 87 | Assert.Contains("A task was canceled", exception.Message); 88 | } 89 | 90 | [Fact(Timeout = 120000)] 91 | public async Task UploadTest_SecretKey() 92 | { 93 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 94 | var path = Path.Combine(Path.GetTempPath(), "testJson.json"); 95 | File.WriteAllText( 96 | path, /*lang=json,strict*/ 97 | "{\"test\": \"test\"}" 98 | ); 99 | var res = await ThirdwebStorage.Upload(client, path); 100 | Assert.StartsWith($"https://{client.ClientId}.ipfscdn.io/ipfs/", res.PreviewUrl); 101 | } 102 | 103 | [Fact(Timeout = 120000)] 104 | public async Task UploadTest_Client_BundleId() 105 | { 106 | var client = ThirdwebClient.Create(clientId: this.ClientIdBundleIdOnly, bundleId: this.BundleIdBundleIdOnly); 107 | var path = Path.Combine(Path.GetTempPath(), "testJson.json"); 108 | File.WriteAllText( 109 | path, /*lang=json,strict*/ 110 | "{\"test\": \"test\"}" 111 | ); 112 | var res = await ThirdwebStorage.Upload(client, path); 113 | Assert.StartsWith($"https://{client.ClientId}.ipfscdn.io/ipfs/", res.PreviewUrl); 114 | } 115 | 116 | [Fact(Timeout = 120000)] 117 | public async Task UploadTest_NullPath() 118 | { 119 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 120 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.Upload(client, null)); 121 | Assert.Equal("path", exception.ParamName); 122 | } 123 | 124 | [Fact(Timeout = 120000)] 125 | public async Task UploadTest_401() 126 | { 127 | var client = ThirdwebClient.Create(clientId: "invalid", bundleId: "hello"); 128 | var path = Path.Combine(Path.GetTempPath(), "testJson.json"); 129 | File.WriteAllText( 130 | path, /*lang=json,strict*/ 131 | "{\"test\": \"test\"}" 132 | ); 133 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.Upload(client, path)); 134 | Assert.Contains("Failed to upload", exception.Message); 135 | Assert.Contains("401", exception.Message); 136 | } 137 | 138 | [Fact(Timeout = 120000)] 139 | public async Task UploadTest_RawBytes_Null() 140 | { 141 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 142 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.UploadRaw(client, null)); 143 | Assert.Equal("rawBytes", exception.ParamName); 144 | } 145 | 146 | [Fact(Timeout = 120000)] 147 | public async Task UploadTest_RawBytes_Empty() 148 | { 149 | var client = ThirdwebClient.Create(secretKey: this.SecretKey); 150 | var exception = await Assert.ThrowsAsync(() => ThirdwebStorage.UploadRaw(client, Array.Empty())); 151 | Assert.Equal("rawBytes", exception.ParamName); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/InAppWallet/Thirdweb.EWS/EmbeddedWallet.Authentication/Server.Types.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace Thirdweb.EWS; 4 | 5 | internal partial class Server 6 | { 7 | internal class VerifyResult 8 | { 9 | internal VerifyResult(string authProvider, bool isNewUser, string authToken, string walletUserId, string email, string phoneNumber, string authIdentifier) 10 | { 11 | this.AuthProvider = authProvider; 12 | this.IsNewUser = isNewUser; 13 | this.AuthToken = authToken; 14 | this.WalletUserId = walletUserId; 15 | this.Email = email; 16 | this.PhoneNumber = phoneNumber; 17 | this.AuthIdentifier = authIdentifier; 18 | } 19 | 20 | internal string AuthProvider { get; } 21 | internal bool IsNewUser { get; } 22 | internal string AuthToken { get; } 23 | internal string WalletUserId { get; } 24 | internal string Email { get; } 25 | internal string PhoneNumber { get; } 26 | internal string AuthIdentifier { get; } 27 | } 28 | 29 | [DataContract] 30 | internal class AccountConnectResponse 31 | { 32 | [DataMember(Name = "linkedAccounts", IsRequired = true)] 33 | public List LinkedAccounts { get; set; } 34 | } 35 | 36 | [DataContract] 37 | public class LinkedAccount 38 | { 39 | [DataMember(Name = "type", IsRequired = true)] 40 | public string Type { get; set; } 41 | 42 | [DataMember(Name = "details", IsRequired = true)] 43 | public LinkedAccountDetails Details { get; set; } 44 | 45 | [DataContract] 46 | public class LinkedAccountDetails 47 | { 48 | [DataMember(Name = "email", EmitDefaultValue = false)] 49 | public string Email { get; set; } 50 | 51 | [DataMember(Name = "address", EmitDefaultValue = false)] 52 | public string Address { get; set; } 53 | 54 | [DataMember(Name = "phone", EmitDefaultValue = false)] 55 | public string Phone { get; set; } 56 | 57 | [DataMember(Name = "id", EmitDefaultValue = false)] 58 | public string Id { get; set; } 59 | } 60 | } 61 | 62 | [DataContract] 63 | private class SendEmailOtpReturnType 64 | { 65 | [DataMember(Name = "email")] 66 | internal string Email { get; set; } 67 | } 68 | 69 | [DataContract] 70 | private class SendPhoneOtpReturnType 71 | { 72 | [DataMember(Name = "phone")] 73 | internal string Phone { get; set; } 74 | } 75 | 76 | #pragma warning disable CS0169, CS8618, IDE0051 // Deserialization will construct the following classes. 77 | [DataContract] 78 | private class AuthVerifiedTokenReturnType 79 | { 80 | [DataMember(Name = "verifiedToken")] 81 | internal VerifiedTokenType VerifiedToken { get; set; } 82 | 83 | [DataMember(Name = "verifiedTokenJwtString")] 84 | internal string VerifiedTokenJwtString { get; set; } 85 | 86 | [DataContract] 87 | internal class VerifiedTokenType 88 | { 89 | [DataMember(Name = "authDetails")] 90 | internal AuthDetailsType AuthDetails { get; set; } 91 | 92 | [DataMember(Name = "authProvider")] 93 | internal string AuthProvider { get; set; } 94 | 95 | [DataMember(Name = "developerClientId")] 96 | private string _developerClientId; 97 | 98 | [DataMember(Name = "isNewUser")] 99 | internal bool IsNewUser { get; set; } 100 | 101 | [DataMember(Name = "rawToken")] 102 | private string _rawToken; 103 | 104 | [DataMember(Name = "userId")] 105 | private string _userId; 106 | } 107 | } 108 | 109 | [DataContract] 110 | private class HttpErrorWithMessage 111 | { 112 | [DataMember(Name = "error")] 113 | internal string Error { get; set; } = ""; 114 | 115 | [DataMember(Name = "message")] 116 | internal string Message { get; set; } = ""; 117 | } 118 | 119 | [DataContract] 120 | private class SharesGetResponse 121 | { 122 | [DataMember(Name = "authShare")] 123 | internal string AuthShare { get; set; } 124 | 125 | [DataMember(Name = "maybeEncryptedRecoveryShares")] 126 | internal string[] MaybeEncryptedRecoveryShares { get; set; } 127 | } 128 | 129 | [DataContract] 130 | internal class UserWallet 131 | { 132 | [DataMember(Name = "status")] 133 | internal string Status { get; set; } 134 | 135 | [DataMember(Name = "isNewUser")] 136 | internal bool IsNewUser { get; set; } 137 | 138 | [DataMember(Name = "walletUserId")] 139 | internal string WalletUserId { get; set; } 140 | 141 | [DataMember(Name = "recoveryShareManagement")] 142 | internal string RecoveryShareManagement { get; set; } 143 | 144 | [DataMember(Name = "storedToken")] 145 | internal StoredTokenType StoredToken { get; set; } 146 | } 147 | 148 | [DataContract] 149 | internal class IdTokenResponse 150 | { 151 | [DataMember(Name = "token")] 152 | internal string Token { get; set; } 153 | 154 | [DataMember(Name = "identityId")] 155 | internal string IdentityId { get; set; } 156 | 157 | [DataMember(Name = "lambdaToken")] 158 | internal string LambdaToken { get; set; } 159 | } 160 | 161 | [DataContract] 162 | private class RecoverySharePasswordResponse 163 | { 164 | [DataMember(Name = "body")] 165 | internal string Body { get; set; } 166 | 167 | [DataMember(Name = "recoveryShareEncKey")] 168 | internal string RecoverySharePassword { get; set; } 169 | } 170 | 171 | [DataContract] 172 | internal class AuthResultType 173 | { 174 | [DataMember(Name = "storedToken")] 175 | internal StoredTokenType StoredToken { get; set; } 176 | } 177 | 178 | [DataContract] 179 | internal class StoredTokenType 180 | { 181 | [DataMember(Name = "jwtToken")] 182 | internal string JwtToken { get; set; } 183 | 184 | [DataMember(Name = "authProvider")] 185 | internal string AuthProvider { get; set; } 186 | 187 | [DataMember(Name = "authDetails")] 188 | internal AuthDetailsType AuthDetails { get; set; } 189 | 190 | [DataMember(Name = "developerClientId")] 191 | internal string DeveloperClientId { get; set; } 192 | 193 | [DataMember(Name = "cookieString")] 194 | internal string CookieString { get; set; } 195 | 196 | [DataMember(Name = "shouldStoreCookieString")] 197 | internal bool ShouldStoreCookieString { get; set; } 198 | 199 | [DataMember(Name = "isNewUser")] 200 | internal bool IsNewUser { get; set; } 201 | } 202 | 203 | [DataContract] 204 | internal class AuthDetailsType 205 | { 206 | [DataMember(Name = "phoneNumber")] 207 | internal string PhoneNumber { get; set; } 208 | 209 | [DataMember(Name = "email")] 210 | internal string Email { get; set; } 211 | 212 | [DataMember(Name = "userWalletId")] 213 | internal string UserWalletId { get; set; } 214 | 215 | [DataMember(Name = "authIdentifier")] 216 | internal string AuthIdentifier { get; set; } 217 | 218 | [DataMember(Name = "recoveryCode")] 219 | internal string RecoveryCode { get; set; } 220 | 221 | [DataMember(Name = "recoveryShareManagement")] 222 | internal string RecoveryShareManagement { get; set; } 223 | } 224 | 225 | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. 226 | #pragma warning restore CS0169 // The field 'Server.*' is never used 227 | #pragma warning restore IDE0051 // The field 'Server.*' is unused 228 | } 229 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Utils/RLP.cs: -------------------------------------------------------------------------------- 1 | namespace Thirdweb; 2 | 3 | public class RLP 4 | { 5 | /// 6 | /// Reason for threshold according to Vitalik Buterin: 7 | /// - 56 bytes maximizes the benefit of both options 8 | /// - if we went with 60 then we would have only had 4 slots for long strings 9 | /// so RLP would not have been able to store objects above 4gb 10 | /// - if we went with 48 then RLP would be fine for 2^128 space, but that's way too much 11 | /// - so 56 and 2^64 space seems like the right place to put the cutoff 12 | /// - also, that's where Bitcoin's varint does the cutof 13 | /// 14 | private const int SIZE_THRESHOLD = 56; 15 | 16 | /* RLP encoding rules are defined as follows: 17 | * For a single byte whose value is in the [0x00, 0x7f] range, that byte is 18 | * its own RLP encoding. 19 | */ 20 | 21 | /// 22 | /// [0x80] 23 | /// If a string is 0-55 bytes long, the RLP encoding consists of a single 24 | /// byte with value 0x80 plus the length of the string followed by the 25 | /// string. The range of the first byte is thus [0x80, 0xb7]. 26 | /// 27 | private const byte OFFSET_SHORT_ITEM = 0x80; 28 | 29 | /// 30 | /// [0xb7] 31 | /// If a string is more than 55 bytes long, the RLP encoding consists of a 32 | /// single byte with value 0xb7 plus the length of the length of the string 33 | /// in binary form, followed by the length of the string, followed by the 34 | /// string. For example, a length-1024 string would be encoded as 35 | /// \xb9\x04\x00 followed by the string. The range of the first byte is thus 36 | /// [0xb8, 0xbf]. 37 | /// 38 | private const byte OFFSET_LONG_ITEM = 0xb7; 39 | 40 | /// 41 | /// [0xc0] 42 | /// If the total payload of a list (i.e. the combined length of all its 43 | /// items) is 0-55 bytes long, the RLP encoding consists of a single byte 44 | /// with value 0xc0 plus the length of the list followed by the concatenation 45 | /// of the RLP encodings of the items. The range of the first byte is thus 46 | /// [0xc0, 0xf7]. 47 | /// 48 | public const byte OFFSET_SHORT_LIST = 0xc0; 49 | 50 | /// 51 | /// [0xf7] 52 | /// If the total payload of a list is more than 55 bytes long, the RLP 53 | /// encoding consists of a single byte with value 0xf7 plus the length of the 54 | /// length of the list in binary form, followed by the length of the list, 55 | /// followed by the concatenation of the RLP encodings of the items. The 56 | /// range of the first byte is thus [0xf8, 0xff]. 57 | /// 58 | private const byte OFFSET_LONG_LIST = 0xf7; 59 | 60 | public static readonly byte[] EMPTY_BYTE_ARRAY = Array.Empty(); 61 | public static readonly byte[] ZERO_BYTE_ARRAY = { 0 }; 62 | 63 | public static int ByteArrayToInt(byte[] bytes) 64 | { 65 | if (BitConverter.IsLittleEndian) 66 | { 67 | Array.Reverse(bytes); 68 | } 69 | 70 | return BitConverter.ToInt32(bytes, 0); 71 | } 72 | 73 | public static byte[] EncodeByte(byte singleByte) 74 | { 75 | if (singleByte == 0) 76 | { 77 | return new[] { OFFSET_SHORT_ITEM }; 78 | } 79 | 80 | if (singleByte <= 0x7F) 81 | { 82 | return new[] { singleByte }; 83 | } 84 | 85 | return new[] { (byte)(OFFSET_SHORT_ITEM + 1), singleByte }; 86 | } 87 | 88 | public static byte[] EncodeElement(byte[] srcData) 89 | { 90 | // null or empty 91 | if (srcData == null || srcData.Length == 0) 92 | { 93 | return new[] { OFFSET_SHORT_ITEM }; 94 | } 95 | 96 | // single zero 97 | if (srcData.Length == 1 && srcData[0] == 0) 98 | { 99 | return srcData; 100 | } 101 | 102 | if (srcData.Length == 1 && srcData[0] < 0x80) 103 | { 104 | return srcData; 105 | } 106 | 107 | if (srcData.Length < SIZE_THRESHOLD) 108 | { 109 | // length = 8X 110 | var length = (byte)(OFFSET_SHORT_ITEM + srcData.Length); 111 | var data = new byte[srcData.Length + 1]; 112 | Array.Copy(srcData, 0, data, 1, srcData.Length); 113 | data[0] = length; 114 | 115 | return data; 116 | } 117 | else 118 | { 119 | // length of length = BX 120 | // prefix = [BX, [length]] 121 | var tmpLength = srcData.Length; 122 | byte byteNum = 0; 123 | while (tmpLength != 0) 124 | { 125 | ++byteNum; 126 | tmpLength >>= 8; 127 | } 128 | var lenBytes = new byte[byteNum]; 129 | for (var i = 0; i < byteNum; ++i) 130 | { 131 | lenBytes[byteNum - 1 - i] = (byte)(srcData.Length >> (8 * i)); 132 | } 133 | // first byte = F7 + bytes.length 134 | var data = new byte[srcData.Length + 1 + byteNum]; 135 | Array.Copy(srcData, 0, data, 1 + byteNum, srcData.Length); 136 | data[0] = (byte)(OFFSET_LONG_ITEM + byteNum); 137 | Array.Copy(lenBytes, 0, data, 1, lenBytes.Length); 138 | 139 | return data; 140 | } 141 | } 142 | 143 | public static byte[] EncodeDataItemsAsElementOrListAndCombineAsList(byte[][] dataItems, int[] indexOfListDataItems = null) 144 | { 145 | if (indexOfListDataItems == null) 146 | { 147 | return EncodeList(dataItems.Select(EncodeElement).ToArray()); 148 | } 149 | 150 | var encodedData = new List(); 151 | 152 | for (var i = 0; i < dataItems.Length; i++) 153 | { 154 | if (indexOfListDataItems.Contains(i)) 155 | { 156 | var item = dataItems[i]; 157 | encodedData.Add(EncodeList(item)); 158 | } 159 | else 160 | { 161 | encodedData.Add(EncodeElement(dataItems[i])); 162 | } 163 | } 164 | 165 | return EncodeList(encodedData.ToArray()); 166 | } 167 | 168 | public static byte[] EncodeList(params byte[][] items) 169 | { 170 | if (items == null || (items.Length == 1 && items[0] == null)) 171 | { 172 | return new[] { OFFSET_SHORT_LIST }; 173 | } 174 | 175 | var totalLength = 0; 176 | for (var i = 0; i < items.Length; i++) 177 | { 178 | totalLength += items[i].Length; 179 | } 180 | 181 | byte[] data; 182 | 183 | int copyPos; 184 | 185 | if (totalLength < SIZE_THRESHOLD) 186 | { 187 | var dataLength = 1 + totalLength; 188 | data = new byte[dataLength]; 189 | 190 | //single byte length 191 | data[0] = (byte)(OFFSET_SHORT_LIST + totalLength); 192 | copyPos = 1; 193 | } 194 | else 195 | { 196 | // length of length = BX 197 | // prefix = [BX, [length]] 198 | var tmpLength = totalLength; 199 | byte byteNum = 0; 200 | 201 | while (tmpLength != 0) 202 | { 203 | ++byteNum; 204 | tmpLength >>= 8; 205 | } 206 | 207 | tmpLength = totalLength; 208 | 209 | var lenBytes = new byte[byteNum]; 210 | for (var i = 0; i < byteNum; ++i) 211 | { 212 | lenBytes[byteNum - 1 - i] = (byte)(tmpLength >> (8 * i)); 213 | } 214 | // first byte = F7 + bytes.length 215 | data = new byte[1 + lenBytes.Length + totalLength]; 216 | 217 | data[0] = (byte)(OFFSET_LONG_LIST + byteNum); 218 | Array.Copy(lenBytes, 0, data, 1, lenBytes.Length); 219 | 220 | copyPos = lenBytes.Length + 1; 221 | } 222 | 223 | //Combine all elements 224 | foreach (var item in items) 225 | { 226 | Array.Copy(item, 0, data, copyPos, item.Length); 227 | copyPos += item.Length; 228 | } 229 | return data; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Utils/Utils.Types.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Newtonsoft.Json; 3 | 4 | namespace Thirdweb; 5 | 6 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 7 | public class ThirdwebChainDataResponse 8 | { 9 | [JsonProperty("data")] 10 | public ThirdwebChainData Data { get; set; } 11 | 12 | [JsonProperty("error")] 13 | public object Error { get; set; } 14 | } 15 | 16 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 17 | public class ThirdwebChainData 18 | { 19 | [JsonProperty("name")] 20 | public string Name { get; set; } 21 | 22 | [JsonProperty("chain")] 23 | public string Chain { get; set; } 24 | 25 | [JsonProperty("rpc")] 26 | public List Rpc { get; set; } 27 | 28 | [JsonProperty("nativeCurrency")] 29 | public ThirdwebChainNativeCurrency NativeCurrency { get; set; } 30 | 31 | [JsonProperty("shortName")] 32 | public string ShortName { get; set; } 33 | 34 | [JsonProperty("chainId")] 35 | public BigInteger ChainId { get; set; } 36 | 37 | [JsonProperty("networkId")] 38 | public BigInteger NetworkId { get; set; } 39 | 40 | [JsonProperty("slug")] 41 | public string Slug { get; set; } 42 | 43 | [JsonProperty("infoURL")] 44 | public string InfoURL { get; set; } 45 | 46 | [JsonProperty("icon")] 47 | public ThirdwebChainIcon Icon { get; set; } 48 | 49 | [JsonProperty("ens")] 50 | public ThirdwebChainEns Ens { get; set; } 51 | 52 | [JsonProperty("explorers")] 53 | public List Explorers { get; set; } 54 | 55 | [JsonProperty("testnet")] 56 | public bool Testnet { get; set; } 57 | 58 | [JsonProperty("stackType")] 59 | public string StackType { get; set; } 60 | } 61 | 62 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 63 | public class ThirdwebChainNativeCurrency 64 | { 65 | [JsonProperty("name")] 66 | public string Name { get; set; } 67 | 68 | [JsonProperty("symbol")] 69 | public string Symbol { get; set; } 70 | 71 | [JsonProperty("decimals")] 72 | public int Decimals { get; set; } 73 | } 74 | 75 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 76 | public class ThirdwebChainIcon 77 | { 78 | [JsonProperty("url")] 79 | public string Url { get; set; } 80 | 81 | [JsonProperty("width")] 82 | public int Width { get; set; } 83 | 84 | [JsonProperty("height")] 85 | public int Height { get; set; } 86 | 87 | [JsonProperty("format")] 88 | public string Format { get; set; } 89 | } 90 | 91 | public class ThirdwebChainEns 92 | { 93 | [JsonProperty("registry")] 94 | public string Registry { get; set; } 95 | } 96 | 97 | public class ThirdwebChainExplorer 98 | { 99 | [JsonProperty("name")] 100 | public string Name { get; set; } 101 | 102 | [JsonProperty("url")] 103 | public string Url { get; set; } 104 | 105 | [JsonProperty("standard")] 106 | public string Standard { get; set; } 107 | 108 | [JsonProperty("icon")] 109 | public ThirdwebChainIcon Icon { get; set; } 110 | } 111 | 112 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 113 | public class FarcasterProfile 114 | { 115 | [JsonProperty("fid")] 116 | public int? Fid { get; set; } 117 | 118 | [JsonProperty("bio")] 119 | public string Bio { get; set; } 120 | 121 | [JsonProperty("pfp")] 122 | public string Pfp { get; set; } 123 | 124 | [JsonProperty("display")] 125 | public string Display { get; set; } 126 | 127 | [JsonProperty("username")] 128 | public string Username { get; set; } 129 | 130 | [JsonProperty("custodyAddress")] 131 | public string CustodyAddress { get; set; } 132 | 133 | [JsonProperty("addresses")] 134 | public List Addresses { get; set; } 135 | } 136 | 137 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 138 | public class LensProfile 139 | { 140 | [JsonProperty("name")] 141 | public string Name { get; set; } 142 | 143 | [JsonProperty("bio")] 144 | public string Bio { get; set; } 145 | 146 | [JsonProperty("picture")] 147 | public string Picture { get; set; } 148 | 149 | [JsonProperty("coverPicture")] 150 | public string CoverPicture { get; set; } 151 | } 152 | 153 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 154 | public class EnsProfile 155 | { 156 | [JsonProperty("name")] 157 | public string Name { get; set; } 158 | 159 | [JsonProperty("address")] 160 | public string Address { get; set; } 161 | 162 | [JsonProperty("avatar")] 163 | public string Avatar { get; set; } 164 | 165 | [JsonProperty("display")] 166 | public string Display { get; set; } 167 | 168 | [JsonProperty("description")] 169 | public string Description { get; set; } 170 | 171 | [JsonProperty("keywords")] 172 | public List Keywords { get; set; } 173 | 174 | [JsonProperty("email")] 175 | public string Email { get; set; } 176 | 177 | [JsonProperty("mail")] 178 | public string Mail { get; set; } 179 | 180 | [JsonProperty("notice")] 181 | public string Notice { get; set; } 182 | 183 | [JsonProperty("location")] 184 | public string Location { get; set; } 185 | 186 | [JsonProperty("phone")] 187 | public string Phone { get; set; } 188 | 189 | [JsonProperty("url")] 190 | public string Url { get; set; } 191 | 192 | [JsonProperty("twitter")] 193 | public string Twitter { get; set; } 194 | 195 | [JsonProperty("github")] 196 | public string Github { get; set; } 197 | 198 | [JsonProperty("discord")] 199 | public string Discord { get; set; } 200 | 201 | [JsonProperty("telegram")] 202 | public string Telegram { get; set; } 203 | } 204 | 205 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 206 | public class SocialProfileGeneric 207 | { 208 | [JsonProperty("type")] 209 | public string Type { get; set; } 210 | 211 | [JsonProperty("name")] 212 | public string Name { get; set; } 213 | 214 | [JsonProperty("avatar")] 215 | public string Avatar { get; set; } 216 | 217 | [JsonProperty("bio")] 218 | public string Bio { get; set; } 219 | 220 | [JsonProperty("metadata")] 221 | public object Metadata { get; set; } 222 | } 223 | 224 | [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] 225 | public class SocialProfileResponse 226 | { 227 | [JsonProperty("data")] 228 | public List Data { get; set; } 229 | 230 | [JsonProperty("error")] 231 | public string Error { get; set; } 232 | } 233 | 234 | /// 235 | /// SocialProfiles object that contains all the different types of social profiles and their respective metadata. 236 | /// 237 | public class SocialProfiles 238 | { 239 | public List EnsProfiles { get; set; } 240 | public List FarcasterProfiles { get; set; } 241 | public List LensProfiles { get; set; } 242 | public List OtherProfiles { get; set; } 243 | 244 | public SocialProfiles(List profiles) 245 | { 246 | this.EnsProfiles = new List(); 247 | this.FarcasterProfiles = new List(); 248 | this.LensProfiles = new List(); 249 | this.OtherProfiles = new List(); 250 | 251 | foreach (var profile in profiles) 252 | { 253 | switch (profile.Type) 254 | { 255 | case "ens": 256 | this.EnsProfiles.Add(JsonConvert.DeserializeObject(JsonConvert.SerializeObject(profile.Metadata))); 257 | break; 258 | case "farcaster": 259 | this.FarcasterProfiles.Add(JsonConvert.DeserializeObject(JsonConvert.SerializeObject(profile.Metadata))); 260 | break; 261 | case "lens": 262 | this.LensProfiles.Add(JsonConvert.DeserializeObject(JsonConvert.SerializeObject(profile.Metadata))); 263 | break; 264 | default: 265 | this.OtherProfiles.Add(profile); 266 | break; 267 | } 268 | } 269 | } 270 | 271 | public override string ToString() 272 | { 273 | return JsonConvert.SerializeObject(this, Formatting.Indented); 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Core EditorConfig Options # 3 | ############################### 4 | 5 | root = true 6 | 7 | ############################### 8 | # Generated Code Exclusions # 9 | ############################### 10 | 11 | # Exclude Thirdweb.Api generated files from all linting and formatting rules 12 | [Thirdweb/Thirdweb.Api/ThirdwebApi.cs] 13 | generated_code = true 14 | dotnet_analyzer_diagnostic.severity = none 15 | dotnet_style_qualification_for_field = false 16 | dotnet_style_qualification_for_property = false 17 | dotnet_style_qualification_for_method = false 18 | dotnet_style_qualification_for_event = false 19 | csharp_style_namespace_declarations = block_scoped 20 | dotnet_diagnostic.IDE0130.severity = none 21 | dotnet_diagnostic.IDE0046.severity = none 22 | dotnet_diagnostic.IDE0045.severity = none 23 | dotnet_diagnostic.IDE0066.severity = none 24 | dotnet_diagnostic.IDE0028.severity = none 25 | dotnet_diagnostic.CA1822.severity = none 26 | dotnet_diagnostic.IDE0290.severity = none 27 | # Disable all naming convention rules for generated code 28 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = none 29 | dotnet_naming_rule.public_members_should_be_pascal_case.severity = none 30 | dotnet_naming_rule.private_fields_should_have_underscore_prefix.severity = none 31 | 32 | ############################### 33 | # Configurable behaviors # 34 | ############################### 35 | 36 | [*.{cs,csx}] 37 | end_of_line = crlf 38 | indent_style = space 39 | indent_size = 4 40 | max_line_length = 200 41 | 42 | ############################### 43 | # .NET Coding Conventions # 44 | ############################### 45 | 46 | [*.{cs,vb}] 47 | # Organize usings 48 | dotnet_sort_system_directives_first = true:warning 49 | dotnet_separate_import_directive_groups = true:warning 50 | 51 | # this. preferences 52 | dotnet_style_qualification_for_field = true:warning 53 | dotnet_style_qualification_for_property = true:warning 54 | dotnet_style_qualification_for_method = true:warning 55 | dotnet_style_qualification_for_event = true:warning 56 | 57 | # Language keywords vs BCL types preferences 58 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning 59 | dotnet_style_predefined_type_for_member_access = true:warning 60 | 61 | # Parentheses preferences 62 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning 63 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning 64 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning 65 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning 66 | 67 | # Modifier preferences 68 | dotnet_style_require_accessibility_modifiers = always:warning 69 | dotnet_style_readonly_field = true:warning 70 | 71 | # Expression-level preferences 72 | dotnet_style_object_initializer = true:warning 73 | dotnet_style_collection_initializer = false:warning 74 | dotnet_style_prefer_collection_expression = false:warning 75 | dotnet_style_explicit_tuple_names = true:warning 76 | dotnet_style_null_propagation = true:warning 77 | dotnet_style_coalesce_expression = true:warning 78 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning 79 | dotnet_style_prefer_inferred_tuple_names = true:warning 80 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning 81 | dotnet_style_prefer_auto_properties = true:warning 82 | dotnet_style_prefer_conditional_expression_over_assignment = true:warning 83 | dotnet_style_prefer_conditional_expression_over_return = true:warning 84 | 85 | # Code Quality 86 | dotnet_code_quality_unused_parameters = all:warning 87 | 88 | # Namespace preferences 89 | csharp_style_namespace_declarations = file_scoped:warning 90 | 91 | ############################### 92 | # Naming Conventions # 93 | ############################### 94 | 95 | # Style Definitions 96 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 97 | dotnet_style_allow_multiple_blank_lines_experimental = false 98 | 99 | # Use PascalCase for constant fields 100 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning 101 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 102 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 103 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 104 | dotnet_naming_symbols.constant_fields.applicable_accessibilities = * 105 | dotnet_naming_symbols.constant_fields.required_modifiers = const 106 | 107 | # Use PascalCase for public members (properties, methods, events) 108 | dotnet_naming_rule.public_members_should_be_pascal_case.severity = warning 109 | dotnet_naming_rule.public_members_should_be_pascal_case.symbols = public_members 110 | dotnet_naming_rule.public_members_should_be_pascal_case.style = pascal_case_style 111 | dotnet_naming_symbols.public_members.applicable_kinds = property,method,event,field 112 | dotnet_naming_symbols.public_members.applicable_accessibilities = public,protected,internal,protected_internal 113 | 114 | # Use camelCase with '_' prefix for private fields 115 | dotnet_naming_style.underscore_prefix_style.capitalization = camel_case 116 | dotnet_naming_style.underscore_prefix_style.required_prefix = _ 117 | dotnet_naming_rule.private_fields_should_have_underscore_prefix.severity = warning 118 | dotnet_naming_rule.private_fields_should_have_underscore_prefix.symbols = private_fields 119 | dotnet_naming_rule.private_fields_should_have_underscore_prefix.style = underscore_prefix_style 120 | dotnet_naming_symbols.private_fields.applicable_kinds = field 121 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private 122 | 123 | ############################### 124 | # Analyzers # 125 | ############################### 126 | 127 | dotnet_analyzer_diagnostic.category-CodeQuality.severity = warning 128 | dotnet_analyzer_diagnostic.category-Documentation.severity = warning 129 | dotnet_analyzer_diagnostic.category-Design.severity = warning 130 | dotnet_analyzer_diagnostic.category-Performance.severity = warning 131 | dotnet_analyzer_diagnostic.category-Reliability.severity = warning 132 | dotnet_analyzer_diagnostic.category-Security.severity = warning 133 | dotnet_analyzer_diagnostic.category-Style.severity = warning 134 | 135 | # Explicit code exclusions 136 | # Namespace does not match folder structure 137 | dotnet_diagnostic.IDE0130.severity = none 138 | # If statement can be simplified 139 | dotnet_diagnostic.IDE0046.severity = none 140 | dotnet_diagnostic.IDE0045.severity = none 141 | # Use switch expression 142 | dotnet_diagnostic.IDE0066.severity = none 143 | # Use collection initializers or expressions 144 | # dotnet_diagnostic.IDE0028.severity = none 145 | 146 | ############################### 147 | # C# Coding Conventions # 148 | ############################### 149 | [*.cs] 150 | # var preferences 151 | csharp_style_var_for_built_in_types = true:warning 152 | csharp_style_var_when_type_is_apparent = true:warning 153 | csharp_style_var_elsewhere = true:warning 154 | 155 | # Expression-bodied members 156 | csharp_style_expression_bodied_methods = when_possible:warning 157 | csharp_style_expression_bodied_constructors = when_possible:warning 158 | csharp_style_expression_bodied_operators = when_possible:warning 159 | csharp_style_expression_bodied_properties = when_possible:warning 160 | csharp_style_expression_bodied_indexers = when_possible:warning 161 | csharp_style_expression_bodied_accessors = when_possible:warning 162 | 163 | # Pattern matching preferences 164 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning 165 | csharp_style_pattern_matching_over_as_with_null_check = true:warning 166 | 167 | # Null-checking preferences 168 | csharp_style_throw_expression = true:warning 169 | csharp_style_conditional_delegate_call = true:warning 170 | 171 | # Modifier preferences 172 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:error 173 | 174 | # Expression-level preferences 175 | csharp_prefer_braces = true:warning 176 | csharp_style_deconstructed_variable_declaration = true:warning 177 | csharp_prefer_simple_default_expression = false:warning 178 | csharp_style_pattern_local_over_anonymous_function = true:warning 179 | csharp_style_inlined_variable_declaration = true:warning 180 | 181 | # CA1822: Mark members as static 182 | dotnet_diagnostic.CA1822.severity = suggestion 183 | 184 | # IDE0290: Use primary constructor 185 | dotnet_diagnostic.IDE0290.severity = silent 186 | 187 | # JSON002: Probable JSON string detected 188 | dotnet_diagnostic.JSON002.severity = silent 189 | 190 | # CS8981: The type name only contains lower-cased ascii characters. Such names may become reserved for the language. 191 | dotnet_diagnostic.CS8981.severity = suggestion 192 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Nethereum.ABI.EIP712; 3 | using Newtonsoft.Json; 4 | 5 | namespace Thirdweb; 6 | 7 | /// 8 | /// Interface for a Thirdweb wallet. 9 | /// 10 | public interface IThirdwebWallet 11 | { 12 | /// 13 | /// Gets the Thirdweb client associated with the wallet. 14 | /// 15 | public ThirdwebClient Client { get; } 16 | 17 | /// 18 | /// Gets the account type of the wallet. 19 | /// 20 | public ThirdwebAccountType AccountType { get; } 21 | 22 | /// 23 | /// String identifier for the wallet to be used in analytics. 24 | /// 25 | public string WalletId { get; } 26 | 27 | /// 28 | /// Gets the address of the wallet. 29 | /// 30 | /// The wallet address. 31 | public Task GetAddress(); 32 | 33 | /// 34 | /// Signs a raw message using personal signing. 35 | /// 36 | /// The raw message to sign. 37 | /// The signed message. 38 | public Task PersonalSign(byte[] rawMessage); 39 | 40 | /// 41 | /// Signs a message using personal signing. 42 | /// 43 | /// The message to sign. 44 | /// The signed message. 45 | public Task PersonalSign(string message); 46 | 47 | /// 48 | /// Signs typed data (version 4). 49 | /// 50 | /// The JSON representation of the typed data. 51 | /// The signed data. 52 | public Task SignTypedDataV4(string json); 53 | 54 | /// 55 | /// Signs typed data (version 4). 56 | /// 57 | /// The type of the data. 58 | /// The type of the domain. 59 | /// The data to sign. 60 | /// The typed data. 61 | /// The signed data. 62 | public Task SignTypedDataV4(T data, TypedData typedData) 63 | where TDomain : IDomain; 64 | 65 | /// 66 | /// Checks if the wallet is connected. 67 | /// 68 | /// True if connected, otherwise false. 69 | public Task IsConnected(); 70 | 71 | /// 72 | /// Signs a transaction. 73 | /// 74 | /// The transaction to sign. 75 | /// The signed transaction. 76 | public Task SignTransaction(ThirdwebTransactionInput transaction); 77 | 78 | /// 79 | /// Sends a transaction. 80 | /// 81 | /// The transaction to send. 82 | /// The transaction hash. 83 | public Task SendTransaction(ThirdwebTransactionInput transaction); 84 | 85 | /// 86 | /// Sends a transaction and waits for its receipt. 87 | /// 88 | /// The transaction to execute. 89 | /// The transaction receipt. 90 | public Task ExecuteTransaction(ThirdwebTransactionInput transaction); 91 | 92 | /// 93 | /// Disconnects the wallet (if using InAppWallet, clears session) 94 | /// 95 | public Task Disconnect(); 96 | 97 | /// 98 | /// Links a new account (auth method) to the current wallet. The current wallet must be connected and the wallet being linked must not be fully connected ie created. 99 | /// 100 | /// The wallet to link. 101 | /// The OTP code if the wallet to link is an email or phone wallet. 102 | /// Set to true if linking OAuth on mobile. 103 | /// The action to open the browser if linking OAuth. 104 | /// The redirect scheme if linking OAuth on mobile. 105 | /// The browser to use if linking OAuth. 106 | /// The chain ID if linking an external wallet (SIWE). 107 | /// The JWT token if linking custom JWT auth. 108 | /// The login payload if linking custom AuthEndpoint auth. 109 | /// The default session ID override if linking Guest auth. 110 | /// A list of objects. 111 | public Task> LinkAccount( 112 | IThirdwebWallet walletToLink, 113 | string otp = null, 114 | bool? isMobile = null, 115 | Action browserOpenAction = null, 116 | string mobileRedirectScheme = "thirdweb://", 117 | IThirdwebBrowser browser = null, 118 | BigInteger? chainId = null, 119 | string jwt = null, 120 | string payload = null, 121 | string defaultSessionIdOverride = null 122 | ); 123 | 124 | /// 125 | /// Unlinks an account (auth method) from the current wallet. 126 | /// 127 | /// The linked account to unlink. Same type returned by . 128 | public Task> UnlinkAccount(LinkedAccount accountToUnlink); 129 | 130 | /// 131 | /// Returns a list of linked accounts to the current wallet. 132 | /// 133 | /// A list of objects. 134 | public Task> GetLinkedAccounts(); 135 | 136 | /// 137 | /// Signs an EIP-7702 authorization to invoke contract functions to an externally owned account. 138 | /// 139 | /// The chain ID of the contract. 140 | /// The address of the contract. 141 | /// Set to true if the wallet will also be the executor of the transaction, otherwise false. 142 | /// The signed authorization as an that can be used with . 143 | public Task SignAuthorization(BigInteger chainId, string contractAddress, bool willSelfExecute); 144 | 145 | /// 146 | /// Attempts to set the active network to the specified chain ID. 147 | /// 148 | /// The chain ID to switch to. 149 | public Task SwitchNetwork(BigInteger chainId); 150 | } 151 | 152 | /// 153 | /// Enum for the types of Thirdweb accounts. 154 | /// 155 | public enum ThirdwebAccountType 156 | { 157 | PrivateKeyAccount, 158 | SmartAccount, 159 | ExternalAccount, 160 | } 161 | 162 | /// 163 | /// Represents a login payload. 164 | /// 165 | [Serializable] 166 | public struct LoginPayload 167 | { 168 | public LoginPayloadData Payload { get; set; } 169 | public string Signature { get; set; } 170 | } 171 | 172 | /// 173 | /// Represents login payload data. 174 | /// 175 | [Serializable] 176 | public class LoginPayloadData 177 | { 178 | /// 179 | /// Gets or sets the domain of the login payload. 180 | /// 181 | [JsonProperty("domain")] 182 | public string Domain { get; set; } 183 | 184 | /// 185 | /// Gets or sets the address of the login payload. 186 | /// 187 | [JsonProperty("address")] 188 | public string Address { get; set; } 189 | 190 | /// 191 | /// Gets or sets the statement of the login payload. 192 | /// 193 | [JsonProperty("statement")] 194 | public string Statement { get; set; } 195 | 196 | /// 197 | /// Gets or sets the URI of the login payload. 198 | /// 199 | [JsonProperty("uri", NullValueHandling = NullValueHandling.Ignore)] 200 | public string Uri { get; set; } 201 | 202 | /// 203 | /// Gets or sets the version of the login payload. 204 | /// 205 | [JsonProperty("version", NullValueHandling = NullValueHandling.Ignore)] 206 | public string Version { get; set; } 207 | 208 | /// 209 | /// Gets or sets the chain ID of the login payload. 210 | /// 211 | [JsonProperty("chain_id", NullValueHandling = NullValueHandling.Ignore)] 212 | public string ChainId { get; set; } 213 | 214 | /// 215 | /// Gets or sets the nonce of the login payload. 216 | /// 217 | [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] 218 | public string Nonce { get; set; } 219 | 220 | /// 221 | /// Gets or sets the issued at timestamp of the login payload. 222 | /// 223 | [JsonProperty("issued_at", NullValueHandling = NullValueHandling.Ignore)] 224 | public string IssuedAt { get; set; } 225 | 226 | /// 227 | /// Gets or sets the expiration time of the login payload. 228 | /// 229 | [JsonProperty("expiration_time", NullValueHandling = NullValueHandling.Ignore)] 230 | public string ExpirationTime { get; set; } 231 | 232 | /// 233 | /// Gets or sets the invalid before timestamp of the login payload. 234 | /// 235 | [JsonProperty("invalid_before", NullValueHandling = NullValueHandling.Ignore)] 236 | public string InvalidBefore { get; set; } 237 | 238 | /// 239 | /// Gets or sets the resources of the login payload. 240 | /// 241 | [JsonProperty("resources", NullValueHandling = NullValueHandling.Ignore)] 242 | public List Resources { get; set; } 243 | 244 | /// 245 | /// Initializes a new instance of the class. 246 | /// 247 | public LoginPayloadData() { } 248 | 249 | public override string ToString() 250 | { 251 | return JsonConvert.SerializeObject(this); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # dotenv 7 | .env 8 | .env.dev 9 | .env.test 10 | .env.local 11 | .env.development 12 | .env.production 13 | 14 | # User-specific files 15 | *.rsuser 16 | *.suo 17 | *.user 18 | *.userosscache 19 | *.sln.docstates 20 | 21 | # User-specific files (MonoDevelop/Xamarin Studio) 22 | *.userprefs 23 | 24 | # Mono auto generated files 25 | mono_crash.* 26 | 27 | # Build results 28 | [Dd]ebug/ 29 | [Dd]ebugPublic/ 30 | [Rr]elease/ 31 | [Rr]eleases/ 32 | x64/ 33 | x86/ 34 | [Ww][Ii][Nn]32/ 35 | [Aa][Rr][Mm]/ 36 | [Aa][Rr][Mm]64/ 37 | bld/ 38 | [Bb]in/ 39 | [Oo]bj/ 40 | [Ll]og/ 41 | [Ll]ogs/ 42 | 43 | # Visual Studio 2015/2017 cache/options directory 44 | .vs/ 45 | # Uncomment if you have tasks that create the project's static files in wwwroot 46 | #wwwroot/ 47 | 48 | # Visual Studio 2017 auto generated files 49 | Generated\ Files/ 50 | 51 | # MSTest test Results 52 | [Tt]est[Rr]esult*/ 53 | [Bb]uild[Ll]og.* 54 | 55 | # NUnit 56 | *.VisualState.xml 57 | TestResult.xml 58 | nunit-*.xml 59 | 60 | # Build Results of an ATL Project 61 | [Dd]ebugPS/ 62 | [Rr]eleasePS/ 63 | dlldata.c 64 | 65 | # Benchmark Results 66 | BenchmarkDotNet.Artifacts/ 67 | 68 | # .NET 69 | project.lock.json 70 | project.fragment.lock.json 71 | artifacts/ 72 | 73 | # Tye 74 | .tye/ 75 | 76 | # ASP.NET Scaffolding 77 | ScaffoldingReadMe.txt 78 | 79 | # StyleCop 80 | StyleCopReport.xml 81 | 82 | # Files built by Visual Studio 83 | *_i.c 84 | *_p.c 85 | *_h.h 86 | *.ilk 87 | *.meta 88 | *.obj 89 | *.iobj 90 | *.pch 91 | *.pdb 92 | *.ipdb 93 | *.pgc 94 | *.pgd 95 | *.rsp 96 | *.sbr 97 | *.tlb 98 | *.tli 99 | *.tlh 100 | *.tmp 101 | *.tmp_proj 102 | *_wpftmp.csproj 103 | *.log 104 | *.tlog 105 | *.vspscc 106 | *.vssscc 107 | .builds 108 | *.pidb 109 | *.svclog 110 | *.scc 111 | 112 | # Chutzpah Test files 113 | _Chutzpah* 114 | 115 | # Visual C++ cache files 116 | ipch/ 117 | *.aps 118 | *.ncb 119 | *.opendb 120 | *.opensdf 121 | *.sdf 122 | *.cachefile 123 | *.VC.db 124 | *.VC.VC.opendb 125 | 126 | # Visual Studio profiler 127 | *.psess 128 | *.vsp 129 | *.vspx 130 | *.sap 131 | 132 | # Visual Studio Trace Files 133 | *.e2e 134 | 135 | # TFS 2012 Local Workspace 136 | $tf/ 137 | 138 | # Guidance Automation Toolkit 139 | *.gpState 140 | 141 | # ReSharper is a .NET coding add-in 142 | _ReSharper*/ 143 | *.[Rr]e[Ss]harper 144 | *.DotSettings.user 145 | 146 | # TeamCity is a build add-in 147 | _TeamCity* 148 | 149 | # DotCover is a Code Coverage Tool 150 | *.dotCover 151 | 152 | # AxoCover is a Code Coverage Tool 153 | .axoCover/* 154 | !.axoCover/settings.json 155 | 156 | # Coverlet is a free, cross platform Code Coverage Tool 157 | coverage*.json 158 | coverage*.xml 159 | coverage*.info 160 | 161 | # Visual Studio code coverage results 162 | *.coverage 163 | *.coveragexml 164 | 165 | # NCrunch 166 | _NCrunch_* 167 | .*crunch*.local.xml 168 | nCrunchTemp_* 169 | 170 | # MightyMoose 171 | *.mm.* 172 | AutoTest.Net/ 173 | 174 | # Web workbench (sass) 175 | .sass-cache/ 176 | 177 | # Installshield output folder 178 | [Ee]xpress/ 179 | 180 | # DocProject is a documentation generator add-in 181 | DocProject/buildhelp/ 182 | DocProject/Help/*.HxT 183 | DocProject/Help/*.HxC 184 | DocProject/Help/*.hhc 185 | DocProject/Help/*.hhk 186 | DocProject/Help/*.hhp 187 | DocProject/Help/Html2 188 | DocProject/Help/html 189 | 190 | # Click-Once directory 191 | publish/ 192 | 193 | # Publish Web Output 194 | *.[Pp]ublish.xml 195 | *.azurePubxml 196 | # Note: Comment the next line if you want to checkin your web deploy settings, 197 | # but database connection strings (with potential passwords) will be unencrypted 198 | *.pubxml 199 | *.publishproj 200 | 201 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 202 | # checkin your Azure Web App publish settings, but sensitive information contained 203 | # in these scripts will be unencrypted 204 | PublishScripts/ 205 | 206 | # NuGet Packages 207 | *.nupkg 208 | # NuGet Symbol Packages 209 | *.snupkg 210 | # The packages folder can be ignored because of Package Restore 211 | **/[Pp]ackages/* 212 | # except build/, which is used as an MSBuild target. 213 | !**/[Pp]ackages/build/ 214 | # Uncomment if necessary however generally it will be regenerated when needed 215 | #!**/[Pp]ackages/repositories.config 216 | # NuGet v3's project.json files produces more ignorable files 217 | *.nuget.props 218 | *.nuget.targets 219 | 220 | # Microsoft Azure Build Output 221 | csx/ 222 | *.build.csdef 223 | 224 | # Microsoft Azure Emulator 225 | ecf/ 226 | rcf/ 227 | 228 | # Windows Store app package directories and files 229 | AppPackages/ 230 | BundleArtifacts/ 231 | Package.StoreAssociation.xml 232 | _pkginfo.txt 233 | *.appx 234 | *.appxbundle 235 | *.appxupload 236 | 237 | # Visual Studio cache files 238 | # files ending in .cache can be ignored 239 | *.[Cc]ache 240 | # but keep track of directories ending in .cache 241 | !?*.[Cc]ache/ 242 | 243 | # Others 244 | ClientBin/ 245 | ~$* 246 | *~ 247 | *.dbmdl 248 | *.dbproj.schemaview 249 | *.jfm 250 | *.pfx 251 | *.publishsettings 252 | orleans.codegen.cs 253 | 254 | # Including strong name files can present a security risk 255 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 256 | #*.snk 257 | 258 | # Since there are multiple workflows, uncomment next line to ignore bower_components 259 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 260 | #bower_components/ 261 | 262 | # RIA/Silverlight projects 263 | Generated_Code/ 264 | 265 | # Backup & report files from converting an old project file 266 | # to a newer Visual Studio version. Backup files are not needed, 267 | # because we have git ;-) 268 | _UpgradeReport_Files/ 269 | Backup*/ 270 | UpgradeLog*.XML 271 | UpgradeLog*.htm 272 | ServiceFabricBackup/ 273 | *.rptproj.bak 274 | 275 | # SQL Server files 276 | *.mdf 277 | *.ldf 278 | *.ndf 279 | 280 | # Business Intelligence projects 281 | *.rdl.data 282 | *.bim.layout 283 | *.bim_*.settings 284 | *.rptproj.rsuser 285 | *- [Bb]ackup.rdl 286 | *- [Bb]ackup ([0-9]).rdl 287 | *- [Bb]ackup ([0-9][0-9]).rdl 288 | 289 | # Microsoft Fakes 290 | FakesAssemblies/ 291 | 292 | # GhostDoc plugin setting file 293 | *.GhostDoc.xml 294 | 295 | # Node.js Tools for Visual Studio 296 | .ntvs_analysis.dat 297 | node_modules/ 298 | 299 | # Visual Studio 6 build log 300 | *.plg 301 | 302 | # Visual Studio 6 workspace options file 303 | *.opt 304 | 305 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 306 | *.vbw 307 | 308 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 309 | *.vbp 310 | 311 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 312 | *.dsw 313 | *.dsp 314 | 315 | # Visual Studio 6 technical files 316 | *.ncb 317 | *.aps 318 | 319 | # Visual Studio LightSwitch build output 320 | **/*.HTMLClient/GeneratedArtifacts 321 | **/*.DesktopClient/GeneratedArtifacts 322 | **/*.DesktopClient/ModelManifest.xml 323 | **/*.Server/GeneratedArtifacts 324 | **/*.Server/ModelManifest.xml 325 | _Pvt_Extensions 326 | 327 | # Paket dependency manager 328 | .paket/paket.exe 329 | paket-files/ 330 | 331 | # FAKE - F# Make 332 | .fake/ 333 | 334 | # CodeRush personal settings 335 | .cr/personal 336 | 337 | # Python Tools for Visual Studio (PTVS) 338 | __pycache__/ 339 | *.pyc 340 | 341 | # Cake - Uncomment if you are using it 342 | # tools/** 343 | # !tools/packages.config 344 | 345 | # Tabs Studio 346 | *.tss 347 | 348 | # Telerik's JustMock configuration file 349 | *.jmconfig 350 | 351 | # BizTalk build output 352 | *.btp.cs 353 | *.btm.cs 354 | *.odx.cs 355 | *.xsd.cs 356 | 357 | # OpenCover UI analysis results 358 | OpenCover/ 359 | 360 | # Azure Stream Analytics local run output 361 | ASALocalRun/ 362 | 363 | # MSBuild Binary and Structured Log 364 | *.binlog 365 | 366 | # NVidia Nsight GPU debugger configuration file 367 | *.nvuser 368 | 369 | # MFractors (Xamarin productivity tool) working folder 370 | .mfractor/ 371 | 372 | # Local History for Visual Studio 373 | .localhistory/ 374 | 375 | # Visual Studio History (VSHistory) files 376 | .vshistory/ 377 | 378 | # BeatPulse healthcheck temp database 379 | healthchecksdb 380 | 381 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 382 | MigrationBackup/ 383 | 384 | # Ionide (cross platform F# VS Code tools) working folder 385 | .ionide/ 386 | 387 | # Fody - auto-generated XML schema 388 | FodyWeavers.xsd 389 | 390 | # VS Code files for those working on multiple tools 391 | .vscode/* 392 | !.vscode/settings.json 393 | !.vscode/tasks.json 394 | !.vscode/launch.json 395 | !.vscode/extensions.json 396 | *.code-workspace 397 | 398 | # Local History for Visual Studio Code 399 | .history/ 400 | 401 | # Windows Installer files from build outputs 402 | *.cab 403 | *.msi 404 | *.msix 405 | *.msm 406 | *.msp 407 | 408 | # JetBrains Rider 409 | *.sln.iml 410 | 411 | ## 412 | ## Visual studio for Mac 413 | ## 414 | 415 | 416 | # globs 417 | Makefile.in 418 | *.userprefs 419 | *.usertasks 420 | config.make 421 | config.status 422 | aclocal.m4 423 | install-sh 424 | autom4te.cache/ 425 | *.tar.gz 426 | tarballs/ 427 | test-results/ 428 | 429 | # Mac bundle stuff 430 | *.dmg 431 | *.app 432 | 433 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 434 | # General 435 | .DS_Store 436 | .AppleDouble 437 | .LSOverride 438 | 439 | # Icon must end with two \r 440 | Icon 441 | 442 | 443 | # Thumbnails 444 | ._* 445 | 446 | # Files that might appear in the root of a volume 447 | .DocumentRevisions-V100 448 | .fseventsd 449 | .Spotlight-V100 450 | .TemporaryItems 451 | .Trashes 452 | .VolumeIcon.icns 453 | .com.apple.timemachine.donotpresent 454 | 455 | # Directories potentially created on remote AFP share 456 | .AppleDB 457 | .AppleDesktop 458 | Network Trash Folder 459 | Temporary Items 460 | .apdisk 461 | 462 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 463 | # Windows thumbnail cache files 464 | Thumbs.db 465 | ehthumbs.db 466 | ehthumbs_vista.db 467 | 468 | # Dump file 469 | *.stackdump 470 | 471 | # Folder config file 472 | [Dd]esktop.ini 473 | 474 | # Recycle Bin used on file shares 475 | $RECYCLE.BIN/ 476 | 477 | # Windows Installer files 478 | *.cab 479 | *.msi 480 | *.msix 481 | *.msm 482 | *.msp 483 | 484 | # Windows shortcuts 485 | *.lnk 486 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Thirdweb Makefile 2 | # Cross-platform targets to mirror tw.bat functionality 3 | # Requires: GNU Make, dotnet SDK, optional CSharpier 4 | 5 | # Use bash for consistent behavior across platforms (Git Bash/MSYS2/WSL/macOS/Linux) 6 | SHELL := bash 7 | .SHELLFLAGS := -o pipefail -c 8 | 9 | # Default target 10 | .DEFAULT_GOAL := help 11 | 12 | # Tools and paths 13 | DOTNET := dotnet 14 | API_CLIENT := Thirdweb/Thirdweb.Api/ThirdwebApi.cs 15 | CONSOLE_PROJ := Thirdweb.Console 16 | GENERATOR_PROJ := Thirdweb.Generator 17 | LIB_PROJ := Thirdweb/Thirdweb.csproj 18 | 19 | # Defaults for publishing/building 20 | CONFIG ?= Release 21 | TFM ?= netstandard2.1 22 | RID ?= 23 | OUT ?= 24 | 25 | # Colors (best effort; will be empty if tput is unavailable) 26 | C_RST := $(shell tput sgr0 2>/dev/null || echo "") 27 | C_BOLD := $(shell tput bold 2>/dev/null || echo "") 28 | C_DIM := $(shell tput dim 2>/dev/null || echo "") 29 | C_RED := $(shell tput setaf 1 2>/dev/null || echo "") 30 | C_GRN := $(shell tput setaf 2 2>/dev/null || echo "") 31 | C_YEL := $(shell tput setaf 3 2>/dev/null || echo "") 32 | C_BLU := $(shell tput setaf 4 2>/dev/null || echo "") 33 | C_MAG := $(shell tput setaf 5 2>/dev/null || echo "") 34 | C_CYN := $(shell tput setaf 6 2>/dev/null || echo "") 35 | 36 | # Icons 37 | IC_BUILD := BUILD 38 | IC_CLEAN := CLEAN 39 | IC_RESTORE := RESTORE 40 | IC_TEST := TEST 41 | IC_PACK := PACK 42 | IC_RUN := RUN 43 | IC_GEN := GEN 44 | IC_INFO := INFO 45 | IC_OK := OK 46 | IC_WARN := WARN 47 | IC_ERR := ERR 48 | IC_FMT := FMT 49 | IC_PUB := PUBLISH 50 | 51 | hr = printf '$(C_DIM)%s$(C_RST)\n' '--------------------------------------------------------------------' 52 | msg = printf '%s[%s]%s %s\n' '$(1)' '$(2)' '$(C_RST)' '$(3)' 53 | 54 | .PHONY: help 55 | help: 56 | @printf '\n$(C_CYN)$(C_BOLD)%s$(C_RST)\n' 'Thirdweb Tools' 57 | @$(hr) 58 | @printf 'Usage: $(C_BOLD)make$(C_RST) $(C_CYN)[target]$(C_RST)\n\n' 59 | @printf '$(C_BOLD)Targets:$(C_RST)\n' 60 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'build' 'Generate API and build the solution' 61 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'clean' 'Clean build artifacts' 62 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'restore' 'Restore NuGet packages' 63 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'test' 'Run tests' 64 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'pack' 'Generate API (if needed) and create NuGet package' 65 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'publish' 'Publish the Thirdweb project (dotnet publish)' 66 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'run' 'Run the console application' 67 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'generate' 'Generate API client from OpenAPI spec' 68 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'generate-llms' 'Generate llms.txt from XML documentation' 69 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'bump' 'Bump version (BUMP=major|minor|patch, default: patch)' 70 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'lint' 'Check code formatting (dry run)' 71 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'fix' 'Fix code formatting issues' 72 | @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'help' 'Show this help message' 73 | @$(hr) 74 | 75 | .PHONY: publish 76 | # Publish the Thirdweb library project 77 | # Usage examples: 78 | # make publish # Release publish 79 | # make publish CONFIG=Debug # Debug config 80 | # make publish RID=win-x64 # Target runtime 81 | # make publish OUT=artifacts/publish # Custom output dir 82 | publish: 83 | @if [ ! -f '$(API_CLIENT)' ]; then \ 84 | $(call msg,$(C_YEL),$(IC_WARN),API client not found, generating it first) ; \ 85 | $(MAKE) --no-print-directory generate ; \ 86 | fi 87 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_PUB) Publishing Thirdweb project) 88 | @CMD="$(DOTNET) publish '$(LIB_PROJ)' -c '$(CONFIG)' -f '$(TFM)'"; \ 89 | if [ -n "$(RID)" ]; then CMD="$$CMD -r '$(RID)'"; fi; \ 90 | if [ -n "$(OUT)" ]; then CMD="$$CMD -o '$(OUT)'"; fi; \ 91 | echo $$CMD; eval $$CMD && \ 92 | $(call msg,$(C_GRN),$(IC_OK),Publish succeeded) || \ 93 | $(call msg,$(C_RED),$(IC_ERR),Publish failed) 94 | 95 | .PHONY: generate 96 | # Clean previous file and generate API client 97 | generate: 98 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_GEN) Cleaning generated API files) 99 | @rm -f '$(API_CLIENT)' 2>/dev/null || true 100 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_GEN) Generating Thirdweb API client with custom generator) 101 | @$(DOTNET) run --project '$(GENERATOR_PROJ)' --no-build >/dev/null 2>&1 \ 102 | || ( \ 103 | $(call msg,$(C_MAG),>> ,Building generator) ; \ 104 | $(DOTNET) build '$(GENERATOR_PROJ)' ; \ 105 | $(call msg,$(C_MAG),>> ,Running generator) ; \ 106 | $(DOTNET) run --project '$(GENERATOR_PROJ)' \ 107 | ) 108 | @$(call msg,$(C_GRN),$(IC_OK),API client generation complete) 109 | 110 | .PHONY: generate-llms 111 | # Generate llms.txt from XML documentation 112 | generate-llms: 113 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_BUILD) Building Thirdweb in Release mode) 114 | @$(DOTNET) build '$(LIB_PROJ)' -c Release >/dev/null 2>&1 || { \ 115 | $(call msg,$(C_MAG),>> ,Building Thirdweb project) ; \ 116 | $(DOTNET) build '$(LIB_PROJ)' -c Release ; \ 117 | } 118 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_GEN) Generating llms.txt from XML documentation) 119 | @$(DOTNET) run --project '$(GENERATOR_PROJ)' -- --llms && \ 120 | $(call msg,$(C_GRN),$(IC_OK),llms.txt generation complete) || \ 121 | $(call msg,$(C_RED),$(IC_ERR),llms.txt generation failed) 122 | 123 | .PHONY: build 124 | build: 125 | @$(MAKE) --no-print-directory generate 126 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_BUILD) Building with dotnet build) 127 | @$(DOTNET) build && \ 128 | $(call msg,$(C_GRN),$(IC_OK),Build succeeded) || \ 129 | $(call msg,$(C_RED),$(IC_ERR),Build failed) 130 | @$(MAKE) --no-print-directory generate-llms 131 | 132 | .PHONY: clean 133 | clean: 134 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_CLEAN) Cleaning with dotnet clean) 135 | @$(DOTNET) clean && \ 136 | $(call msg,$(C_GRN),$(IC_OK),Clean completed) || \ 137 | $(call msg,$(C_RED),$(IC_ERR),Clean failed) 138 | 139 | .PHONY: restore 140 | restore: 141 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_RESTORE) Restoring with dotnet restore) 142 | @$(DOTNET) restore && \ 143 | $(call msg,$(C_GRN),$(IC_OK),Restore completed) || \ 144 | $(call msg,$(C_RED),$(IC_ERR),Restore failed) 145 | 146 | .PHONY: test 147 | test: 148 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_TEST) Running dotnet test) 149 | @$(DOTNET) test && \ 150 | $(call msg,$(C_GRN),$(IC_OK),All tests passed) || \ 151 | $(call msg,$(C_RED),$(IC_ERR),Some tests failed) 152 | 153 | .PHONY: pack 154 | pack: 155 | @if [ ! -f '$(API_CLIENT)' ]; then \ 156 | $(call msg,$(C_YEL),$(IC_WARN),API client not found, generating it first) ; \ 157 | $(MAKE) --no-print-directory generate ; \ 158 | fi 159 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_BUILD) Building Release) 160 | @$(DOTNET) build --configuration Release || { $(call msg,$(C_RED),$(IC_ERR),Build (Release) failed); exit 1; } 161 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_PACK) Packing NuGet package(s)) 162 | @$(DOTNET) pack --configuration Release && \ 163 | $(call msg,$(C_GRN),$(IC_OK),Pack completed) || \ 164 | $(call msg,$(C_RED),$(IC_ERR),Packing failed) 165 | 166 | .PHONY: run 167 | run: 168 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_RUN) dotnet run --project $(CONSOLE_PROJ)) 169 | @$(DOTNET) run --project '$(CONSOLE_PROJ)' && \ 170 | $(call msg,$(C_GRN),$(IC_OK),Application exited) || \ 171 | $(call msg,$(C_RED),$(IC_ERR),Application exited with errors) 172 | 173 | .PHONY: lint 174 | lint: 175 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_FMT) Checking code formatting with CSharpier) 176 | @csharpier --help >/dev/null 2>&1 || { \ 177 | $(call msg,$(C_YEL),$(IC_WARN),CSharpier is not installed) ; \ 178 | printf ' Install it with: dotnet tool install -g csharpier\n' ; \ 179 | exit 0 ; \ 180 | } 181 | @csharpier check . >/dev/null 2>&1 || { \ 182 | $(call msg,$(C_YEL),$(IC_WARN),Formatting issues found) ; \ 183 | printf ' Run "make fix" to automatically fix them.\n' ; \ 184 | exit 0 ; \ 185 | } 186 | @$(call msg,$(C_GRN),$(IC_OK),Code formatting is correct) 187 | 188 | .PHONY: fix 189 | fix: 190 | @$(call msg,$(C_BLU),$(IC_INFO),$(IC_FMT) Running CSharpier formatter) 191 | @csharpier --help >/dev/null 2>&1 || { \ 192 | $(call msg,$(C_YEL),$(IC_WARN),CSharpier is not installed) ; \ 193 | printf ' Install it with: dotnet tool install -g csharpier\n' ; \ 194 | exit 0 ; \ 195 | } 196 | @csharpier format . >/dev/null 2>&1 || { \ 197 | $(call msg,$(C_RED),$(IC_ERR),CSharpier formatting failed) ; \ 198 | exit 1 ; \ 199 | } 200 | @$(call msg,$(C_GRN),$(IC_OK),Code formatting completed) 201 | 202 | .PHONY: bump 203 | # Bump version in .csproj and Constants.cs 204 | # Usage: make bump [BUMP=major|minor|patch] (defaults to patch) 205 | bump: 206 | @BUMP_TYPE="$(BUMP)"; \ 207 | if [ -z "$$BUMP_TYPE" ]; then \ 208 | BUMP_TYPE="patch"; \ 209 | printf '%s[%s]%s %s\n' '$(C_BLU)' '$(IC_INFO)' '$(C_RST)' "No BUMP specified, defaulting to patch"; \ 210 | fi; \ 211 | if [ "$$BUMP_TYPE" != "major" ] && [ "$$BUMP_TYPE" != "minor" ] && [ "$$BUMP_TYPE" != "patch" ]; then \ 212 | printf '%s[%s]%s %s\n' '$(C_RED)' '$(IC_ERR)' '$(C_RST)' "Invalid BUMP value: $$BUMP_TYPE"; \ 213 | printf ' Valid values: major, minor, patch\n'; \ 214 | exit 1; \ 215 | fi; \ 216 | printf '%s[%s]%s %s\n' '$(C_BLU)' '$(IC_INFO)' '$(C_RST)' "Reading current version"; \ 217 | CURRENT=$$(grep -oP '\K[^<]+' '$(LIB_PROJ)' | head -1); \ 218 | if [ -z "$$CURRENT" ]; then \ 219 | printf '%s[%s]%s %s\n' '$(C_RED)' '$(IC_ERR)' '$(C_RST)' "Could not read current version"; \ 220 | exit 1; \ 221 | fi; \ 222 | MAJOR=$$(echo $$CURRENT | cut -d. -f1); \ 223 | MINOR=$$(echo $$CURRENT | cut -d. -f2); \ 224 | PATCH=$$(echo $$CURRENT | cut -d. -f3); \ 225 | if [ "$$BUMP_TYPE" = "major" ]; then \ 226 | MAJOR=$$((MAJOR + 1)); MINOR=0; PATCH=0; \ 227 | elif [ "$$BUMP_TYPE" = "minor" ]; then \ 228 | MINOR=$$((MINOR + 1)); PATCH=0; \ 229 | elif [ "$$BUMP_TYPE" = "patch" ]; then \ 230 | PATCH=$$((PATCH + 1)); \ 231 | fi; \ 232 | NEW_VERSION="$$MAJOR.$$MINOR.$$PATCH"; \ 233 | printf '%s[%s]%s %s\n' '$(C_MAG)' '$(IC_INFO)' '$(C_RST)' "Bumping version: $$CURRENT -> $$NEW_VERSION"; \ 234 | sed -i "s|$$CURRENT|$$NEW_VERSION|" '$(LIB_PROJ)'; \ 235 | sed -i "s|$$CURRENT|$$NEW_VERSION|" '$(LIB_PROJ)'; \ 236 | sed -i "s|$$CURRENT|$$NEW_VERSION|" '$(LIB_PROJ)'; \ 237 | sed -i 's|public const string VERSION = "'"$$CURRENT"'";|public const string VERSION = "'"$$NEW_VERSION"'";|' 'Thirdweb/Thirdweb.Utils/Constants.cs'; \ 238 | printf '%s[%s]%s %s\n' '$(C_GRN)' '$(IC_OK)' '$(C_RST)' "Version bumped to $$NEW_VERSION"; \ 239 | printf ' Updated files:\n'; \ 240 | printf ' - $(LIB_PROJ)\n'; \ 241 | printf ' - Thirdweb/Thirdweb.Utils/Constants.cs\n' 242 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Thirdweb/Thirdweb.Transactions/ThirdwebTransaction.Types.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Nethereum.Hex.HexConvertors.Extensions; 3 | using Nethereum.Hex.HexTypes; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace Thirdweb; 8 | 9 | /// 10 | /// Represents the input parameters for a Thirdweb transaction. 11 | /// 12 | public class ThirdwebTransactionInput 13 | { 14 | internal ThirdwebTransactionInput() { } 15 | 16 | public ThirdwebTransactionInput(BigInteger chainId) 17 | { 18 | this.ChainId = chainId > 0 ? new HexBigInteger(chainId) : throw new ArgumentException("Invalid Chain ID"); 19 | } 20 | 21 | public ThirdwebTransactionInput( 22 | BigInteger chainId, 23 | string from = null, 24 | string to = null, 25 | BigInteger? nonce = null, 26 | BigInteger? gas = null, 27 | BigInteger? gasPrice = null, 28 | BigInteger? value = null, 29 | string data = null, 30 | BigInteger? maxFeePerGas = null, 31 | BigInteger? maxPriorityFeePerGas = null, 32 | ZkSyncOptions? zkSync = null, 33 | EIP7702Authorization? authorization = null 34 | ) 35 | { 36 | this.ChainId = chainId > 0 ? new HexBigInteger(chainId) : throw new ArgumentException("Invalid Chain ID"); 37 | this.From = string.IsNullOrEmpty(from) ? Constants.ADDRESS_ZERO : from; 38 | this.To = string.IsNullOrEmpty(to) ? Constants.ADDRESS_ZERO : to; 39 | this.Nonce = nonce == null ? null : new HexBigInteger(nonce.Value); 40 | this.Gas = gas == null ? null : new HexBigInteger(gas.Value); 41 | this.GasPrice = gasPrice == null ? null : new HexBigInteger(gasPrice.Value); 42 | this.Value = value == null ? null : new HexBigInteger(value.Value); 43 | this.Data = string.IsNullOrEmpty(data) ? "0x" : data; 44 | this.MaxFeePerGas = maxFeePerGas == null ? null : new HexBigInteger(maxFeePerGas.Value); 45 | this.MaxPriorityFeePerGas = maxPriorityFeePerGas == null ? null : new HexBigInteger(maxPriorityFeePerGas.Value); 46 | this.ZkSync = zkSync; 47 | this.AuthorizationList = authorization == null ? null : new List { authorization.Value }; 48 | } 49 | 50 | /// 51 | /// Gets or sets the nonce of the transaction. 52 | /// 53 | [JsonProperty(PropertyName = "nonce")] 54 | public HexBigInteger Nonce { get; set; } 55 | 56 | private string _from; 57 | private string _to; 58 | private string _data; 59 | 60 | /// 61 | /// Gets or sets the sender address of the transaction. 62 | /// 63 | [JsonProperty(PropertyName = "from")] 64 | public string From 65 | { 66 | get => this._from.EnsureHexPrefix(); 67 | set => this._from = value; 68 | } 69 | 70 | /// 71 | /// Gets or sets the recipient address of the transaction. 72 | /// 73 | [JsonProperty(PropertyName = "to")] 74 | public string To 75 | { 76 | get => this._to.EnsureHexPrefix(); 77 | set => this._to = value; 78 | } 79 | 80 | /// 81 | /// Gets or sets the gas limit for the transaction. 82 | /// 83 | [JsonProperty(PropertyName = "gas")] 84 | public HexBigInteger Gas { get; set; } 85 | 86 | /// 87 | /// Gets or sets the gas price for the transaction. 88 | /// 89 | [JsonProperty(PropertyName = "gasPrice")] 90 | public HexBigInteger GasPrice { get; set; } 91 | 92 | /// 93 | /// Gets or sets the value to be transferred in the transaction. 94 | /// 95 | [JsonProperty(PropertyName = "value")] 96 | public HexBigInteger Value { get; set; } 97 | 98 | /// 99 | /// Gets or sets the data to be sent with the transaction. 100 | /// 101 | [JsonProperty(PropertyName = "data")] 102 | public string Data 103 | { 104 | get => this._data.EnsureHexPrefix(); 105 | set => this._data = value; 106 | } 107 | 108 | /// 109 | /// Gets or sets the maximum fee per gas for the transaction. 110 | /// 111 | [JsonProperty(PropertyName = "maxFeePerGas")] 112 | public HexBigInteger MaxFeePerGas { get; set; } 113 | 114 | /// 115 | /// Gets or sets the maximum priority fee per gas for the transaction. 116 | /// 117 | [JsonProperty(PropertyName = "maxPriorityFeePerGas")] 118 | public HexBigInteger MaxPriorityFeePerGas { get; set; } 119 | 120 | /// 121 | /// Gets or sets the chain ID for the transaction. 122 | /// 123 | [JsonProperty(PropertyName = "chainId")] 124 | public HexBigInteger ChainId { get; set; } 125 | 126 | /// 127 | /// Gets or sets the zkSync options for the transaction. 128 | /// 129 | [JsonProperty(PropertyName = "zkSyncOptions", NullValueHandling = NullValueHandling.Ignore)] 130 | public ZkSyncOptions? ZkSync { get; set; } 131 | 132 | #nullable enable 133 | [JsonProperty(PropertyName = "authorizationList", NullValueHandling = NullValueHandling.Ignore)] 134 | public List? AuthorizationList { get; set; } 135 | #nullable disable 136 | } 137 | 138 | /// 139 | /// Represents the zkSync options for a transaction. 140 | /// 141 | public struct ZkSyncOptions 142 | { 143 | /// 144 | /// Gets or sets the gas limit per pubdata byte. 145 | /// 146 | [JsonProperty(PropertyName = "gasPerPubdataByteLimit")] 147 | public BigInteger? GasPerPubdataByteLimit { get; set; } 148 | 149 | /// 150 | /// Gets or sets the factory dependencies. 151 | /// 152 | [JsonProperty(PropertyName = "factoryDeps")] 153 | public List FactoryDeps { get; set; } 154 | 155 | /// 156 | /// Gets or sets the paymaster. 157 | /// 158 | [JsonProperty(PropertyName = "paymaster")] 159 | public BigInteger Paymaster { get; set; } 160 | 161 | /// 162 | /// Gets or sets the paymaster input data. 163 | /// 164 | [JsonProperty(PropertyName = "paymasterInput")] 165 | public byte[] PaymasterInput { get; set; } 166 | 167 | /// 168 | /// Initializes a new instance of the struct. 169 | /// 170 | /// The paymaster. 171 | /// The paymaster input data. 172 | /// The gas limit per pubdata byte. 173 | /// The factory dependencies. 174 | public ZkSyncOptions(string paymaster = null, string paymasterInput = null, BigInteger? gasPerPubdataByteLimit = null, List factoryDeps = null) 175 | { 176 | if (string.IsNullOrEmpty(paymaster) || string.IsNullOrEmpty(paymasterInput)) 177 | { 178 | this.Paymaster = 0; 179 | this.PaymasterInput = Array.Empty(); 180 | this.GasPerPubdataByteLimit = gasPerPubdataByteLimit; 181 | this.FactoryDeps = factoryDeps ?? new List(); 182 | } 183 | else 184 | { 185 | this.Paymaster = new HexBigInteger(paymaster).Value; 186 | this.PaymasterInput = paymasterInput.HexToByteArray(); 187 | this.GasPerPubdataByteLimit = gasPerPubdataByteLimit; 188 | this.FactoryDeps = factoryDeps ?? new List(); 189 | } 190 | } 191 | } 192 | 193 | public struct EIP7702Authorization 194 | { 195 | [JsonProperty(PropertyName = "chainId")] 196 | public string ChainId { get; set; } 197 | 198 | [JsonProperty(PropertyName = "address")] 199 | public string Address { get; set; } 200 | 201 | [JsonProperty(PropertyName = "nonce")] 202 | public string Nonce { get; set; } 203 | 204 | [JsonProperty(PropertyName = "yParity")] 205 | public string YParity { get; set; } 206 | 207 | [JsonProperty(PropertyName = "r")] 208 | public string R { get; set; } 209 | 210 | [JsonProperty(PropertyName = "s")] 211 | public string S { get; set; } 212 | 213 | public EIP7702Authorization(BigInteger chainId, string address, BigInteger nonce, byte[] yParity, byte[] r, byte[] s) 214 | { 215 | this.ChainId = new HexBigInteger(chainId).HexValue; 216 | this.Address = address; 217 | this.Nonce = new HexBigInteger(nonce).HexValue; 218 | this.YParity = yParity.BytesToHex() == "0x00" ? "0x0" : "0x1"; 219 | this.R = r.BytesToHex(); 220 | this.S = s.BytesToHex(); 221 | } 222 | } 223 | 224 | /// 225 | /// Represents the receipt of a transaction. 226 | /// 227 | public class ThirdwebTransactionReceipt 228 | { 229 | /// 230 | /// Gets or sets the transaction hash. 231 | /// 232 | [JsonProperty(PropertyName = "transactionHash")] 233 | public string TransactionHash { get; set; } 234 | 235 | /// 236 | /// Gets or sets the transaction index within the block. 237 | /// 238 | [JsonProperty(PropertyName = "transactionIndex")] 239 | public HexBigInteger TransactionIndex { get; set; } 240 | 241 | /// 242 | /// Gets or sets the hash of the block containing the transaction. 243 | /// 244 | [JsonProperty(PropertyName = "blockHash")] 245 | public string BlockHash { get; set; } 246 | 247 | /// 248 | /// Gets or sets the number of the block containing the transaction. 249 | /// 250 | [JsonProperty(PropertyName = "blockNumber")] 251 | public HexBigInteger BlockNumber { get; set; } 252 | 253 | /// 254 | /// Gets or sets the address of the sender. 255 | /// 256 | [JsonProperty(PropertyName = "from")] 257 | public string From { get; set; } 258 | 259 | /// 260 | /// Gets or sets the address of the recipient. 261 | /// 262 | [JsonProperty(PropertyName = "to")] 263 | public string To { get; set; } 264 | 265 | /// 266 | /// Gets or sets the cumulative gas used by the transaction. 267 | /// 268 | [JsonProperty(PropertyName = "cumulativeGasUsed")] 269 | public HexBigInteger CumulativeGasUsed { get; set; } 270 | 271 | /// 272 | /// Gets or sets the gas used by the transaction. 273 | /// 274 | [JsonProperty(PropertyName = "gasUsed")] 275 | public HexBigInteger GasUsed { get; set; } 276 | 277 | /// 278 | /// Gets or sets the effective gas price for the transaction. 279 | /// 280 | [JsonProperty(PropertyName = "effectiveGasPrice")] 281 | public HexBigInteger EffectiveGasPrice { get; set; } 282 | 283 | /// 284 | /// Gets or sets the contract address created by the transaction, if applicable. 285 | /// 286 | [JsonProperty(PropertyName = "contractAddress")] 287 | public string ContractAddress { get; set; } 288 | 289 | /// 290 | /// Gets or sets the status of the transaction. 291 | /// 292 | [JsonProperty(PropertyName = "status")] 293 | public HexBigInteger Status { get; set; } 294 | 295 | /// 296 | /// Gets or sets the logs generated by the transaction. 297 | /// 298 | [JsonProperty(PropertyName = "logs")] 299 | public JArray Logs { get; set; } 300 | 301 | /// 302 | /// Gets or sets the transaction type. 303 | /// 304 | [JsonProperty(PropertyName = "type")] 305 | public HexBigInteger Type { get; set; } 306 | 307 | /// 308 | /// Gets or sets the logs bloom filter. 309 | /// 310 | [JsonProperty(PropertyName = "logsBloom")] 311 | public string LogsBloom { get; set; } 312 | 313 | /// 314 | /// Gets or sets the root of the transaction. 315 | /// 316 | [JsonProperty(PropertyName = "root")] 317 | public string Root { get; set; } 318 | 319 | public override string ToString() 320 | { 321 | return JsonConvert.SerializeObject(this, Formatting.Indented); 322 | } 323 | } 324 | --------------------------------------------------------------------------------