diff --git a/.csharpierrc.json b/.csharpierrc.json
deleted file mode 100644
index 4d807a7e..00000000
--- a/.csharpierrc.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "printWidth": 200,
- "useTabs": false,
- "tabWidth": 4,
- "preprocessorSymbolSets": ["", "DEBUG", "RELEASE"]
-}
diff --git a/.editorconfig b/.editorconfig
index 11926f13..79e1b281 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,143 +1,191 @@
###############################
-# Core EditorConfig Options #
+# Core EditorConfig Options #
###############################
root = true
-# XML project files
-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
-indent_size = 2
+###############################
+# Generated Code Exclusions #
+###############################
-# XML config files
-[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
-indent_size = 2
+# Exclude Thirdweb.Api generated files from all linting and formatting rules
+[Thirdweb/Thirdweb.Api/ThirdwebApi.cs]
+generated_code = true
+dotnet_analyzer_diagnostic.severity = none
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_property = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_event = false
+csharp_style_namespace_declarations = block_scoped
+dotnet_diagnostic.IDE0130.severity = none
+dotnet_diagnostic.IDE0046.severity = none
+dotnet_diagnostic.IDE0045.severity = none
+dotnet_diagnostic.IDE0066.severity = none
+dotnet_diagnostic.IDE0028.severity = none
+dotnet_diagnostic.CA1822.severity = none
+dotnet_diagnostic.IDE0290.severity = none
+# Disable all naming convention rules for generated code
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = none
+dotnet_naming_rule.public_members_should_be_pascal_case.severity = none
+dotnet_naming_rule.private_fields_should_have_underscore_prefix.severity = none
-# Code files
-[*.{cs,csx,vb,vbx}]
+###############################
+# Configurable behaviors #
+###############################
+
+[*.{cs,csx}]
+end_of_line = crlf
+indent_style = space
indent_size = 4
-insert_final_newline = true
-charset = utf-8-bom
+max_line_length = 200
###############################
-# .NET Coding Conventions #
+# .NET Coding Conventions #
###############################
+
[*.{cs,vb}]
# Organize usings
-dotnet_sort_system_directives_first = true
+dotnet_sort_system_directives_first = true:warning
+dotnet_separate_import_directive_groups = true:warning
+
# this. preferences
-dotnet_style_qualification_for_field = false:silent
-dotnet_style_qualification_for_property = false:silent
-dotnet_style_qualification_for_method = false:silent
-dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = true:warning
+dotnet_style_qualification_for_property = true:warning
+dotnet_style_qualification_for_method = true:warning
+dotnet_style_qualification_for_event = true:warning
+
# Language keywords vs BCL types preferences
-dotnet_style_predefined_type_for_locals_parameters_members = true:silent
-dotnet_style_predefined_type_for_member_access = true:silent
+dotnet_style_predefined_type_for_locals_parameters_members = true:warning
+dotnet_style_predefined_type_for_member_access = true:warning
+
# Parentheses preferences
-dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning
+
# Modifier preferences
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
-dotnet_style_readonly_field = true:suggestion
+dotnet_style_require_accessibility_modifiers = always:warning
+dotnet_style_readonly_field = true:warning
+
# Expression-level preferences
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_collection_initializer = true:suggestion
-dotnet_style_explicit_tuple_names = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
-dotnet_style_prefer_inferred_tuple_names = true:suggestion
-dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
-dotnet_style_prefer_auto_properties = true:silent
-dotnet_style_prefer_conditional_expression_over_assignment = true:silent
-dotnet_style_prefer_conditional_expression_over_return = true:silent
+dotnet_style_object_initializer = true:warning
+dotnet_style_collection_initializer = false:warning
+dotnet_style_prefer_collection_expression = false:warning
+dotnet_style_explicit_tuple_names = true:warning
+dotnet_style_null_propagation = true:warning
+dotnet_style_coalesce_expression = true:warning
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+dotnet_style_prefer_inferred_tuple_names = true:warning
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = true:warning
+dotnet_style_prefer_conditional_expression_over_return = true:warning
+
+# Code Quality
+dotnet_code_quality_unused_parameters = all:warning
+
+# Namespace preferences
+csharp_style_namespace_declarations = file_scoped:warning
###############################
# Naming Conventions #
###############################
+
# Style Definitions
-dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-# Use PascalCase for constant fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
-dotnet_naming_symbols.constant_fields.applicable_kinds = field
-dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
-dotnet_naming_symbols.constant_fields.required_modifiers = const
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+dotnet_style_allow_multiple_blank_lines_experimental = false
+
+# Use PascalCase for constant fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+
+# Use PascalCase for public members (properties, methods, events)
+dotnet_naming_rule.public_members_should_be_pascal_case.severity = warning
+dotnet_naming_rule.public_members_should_be_pascal_case.symbols = public_members
+dotnet_naming_rule.public_members_should_be_pascal_case.style = pascal_case_style
+dotnet_naming_symbols.public_members.applicable_kinds = property,method,event,field
+dotnet_naming_symbols.public_members.applicable_accessibilities = public,protected,internal,protected_internal
+
+# Use camelCase with '_' prefix for private fields
+dotnet_naming_style.underscore_prefix_style.capitalization = camel_case
+dotnet_naming_style.underscore_prefix_style.required_prefix = _
+dotnet_naming_rule.private_fields_should_have_underscore_prefix.severity = warning
+dotnet_naming_rule.private_fields_should_have_underscore_prefix.symbols = private_fields
+dotnet_naming_rule.private_fields_should_have_underscore_prefix.style = underscore_prefix_style
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private
###############################
# Analyzers #
###############################
-dotnet_analyzer_diagnostic.category-CodeQuality.severity = suggestion
-dotnet_analyzer_diagnostic.category-Documentation.severity = suggestion
-dotnet_analyzer_diagnostic.category-Design.severity = suggestion
-dotnet_analyzer_diagnostic.category-Performance.severity = suggestion
+
+dotnet_analyzer_diagnostic.category-CodeQuality.severity = warning
+dotnet_analyzer_diagnostic.category-Documentation.severity = warning
+dotnet_analyzer_diagnostic.category-Design.severity = warning
+dotnet_analyzer_diagnostic.category-Performance.severity = warning
dotnet_analyzer_diagnostic.category-Reliability.severity = warning
dotnet_analyzer_diagnostic.category-Security.severity = warning
-dotnet_analyzer_diagnostic.category-Style.severity = suggestion
+dotnet_analyzer_diagnostic.category-Style.severity = warning
# Explicit code exclusions
-dotnet_diagnostic.IDE0160.severity = none
-dotnet_diagnostic.CA1848.severity = none
+# Namespace does not match folder structure
+dotnet_diagnostic.IDE0130.severity = none
+# If statement can be simplified
+dotnet_diagnostic.IDE0046.severity = none
+dotnet_diagnostic.IDE0045.severity = none
+# Use switch expression
+dotnet_diagnostic.IDE0066.severity = none
+# Use collection initializers or expressions
+# dotnet_diagnostic.IDE0028.severity = none
###############################
# C# Coding Conventions #
###############################
[*.cs]
# var preferences
-csharp_style_var_for_built_in_types = true:silent
-csharp_style_var_when_type_is_apparent = true:silent
-csharp_style_var_elsewhere = true:silent
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+csharp_style_var_elsewhere = true:warning
+
# Expression-bodied members
-csharp_style_expression_bodied_methods = false:silent
-csharp_style_expression_bodied_constructors = false:silent
-csharp_style_expression_bodied_operators = false:silent
-csharp_style_expression_bodied_properties = true:silent
-csharp_style_expression_bodied_indexers = true:silent
-csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_methods = when_possible:warning
+csharp_style_expression_bodied_constructors = when_possible:warning
+csharp_style_expression_bodied_operators = when_possible:warning
+csharp_style_expression_bodied_properties = when_possible:warning
+csharp_style_expression_bodied_indexers = when_possible:warning
+csharp_style_expression_bodied_accessors = when_possible:warning
+
# Pattern matching preferences
-csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+
# Null-checking preferences
-csharp_style_throw_expression = true:suggestion
-csharp_style_conditional_delegate_call = true:suggestion
+csharp_style_throw_expression = true:warning
+csharp_style_conditional_delegate_call = true:warning
+
# Modifier preferences
-csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:error
+
# Expression-level preferences
-csharp_prefer_braces = true:silent
-csharp_style_deconstructed_variable_declaration = true:suggestion
-csharp_prefer_simple_default_expression = true:suggestion
-csharp_style_pattern_local_over_anonymous_function = true:suggestion
-csharp_style_inlined_variable_declaration = true:suggestion
+csharp_prefer_braces = true:warning
+csharp_style_deconstructed_variable_declaration = true:warning
+csharp_prefer_simple_default_expression = false:warning
+csharp_style_pattern_local_over_anonymous_function = true:warning
+csharp_style_inlined_variable_declaration = true:warning
-###############################
-# C# Formatting Rules #
-###############################
-# New line preferences
-csharp_new_line_before_open_brace = all
-csharp_new_line_before_else = true
-csharp_new_line_before_catch = true
-csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_members_in_anonymous_types = true
-csharp_new_line_between_query_expression_clauses = true
-# Indentation preferences
-csharp_indent_case_contents = true
-csharp_indent_switch_labels = true
-csharp_indent_labels = flush_left
-# Space preferences
-csharp_space_after_cast = false
-csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-csharp_space_between_parentheses = false
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_around_binary_operators = before_and_after
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-# Wrapping preferences
-csharp_preserve_single_line_statements = true
-csharp_preserve_single_line_blocks = true
\ No newline at end of file
+# CA1822: Mark members as static
+dotnet_diagnostic.CA1822.severity = suggestion
+
+# IDE0290: Use primary constructor
+dotnet_diagnostic.IDE0290.severity = silent
+
+# JSON002: Probable JSON string detected
+dotnet_diagnostic.JSON002.severity = silent
+
+# CS8981: The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
+dotnet_diagnostic.CS8981.severity = suggestion
diff --git a/.github/workflows/docfx.yml b/.github/workflows/docfx.yml
index 7a959496..e746ce8b 100644
--- a/.github/workflows/docfx.yml
+++ b/.github/workflows/docfx.yml
@@ -1,3 +1,5 @@
+name: Publish API Reference
+
on:
push:
branches:
@@ -11,7 +13,7 @@ permissions:
concurrency:
group: "pages"
cancel-in-progress: false
-
+
jobs:
publish-docs:
environment:
@@ -19,20 +21,20 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- - name: Checkout
- uses: actions/checkout@v3
- - name: Dotnet Setup
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 8.x
+ - name: Checkout
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - name: Dotnet Setup
+ uses: actions/setup-dotnet@b2ace4b12f4cec1b96b6361ff2694ba9e931ceb4 # v3.3.1
+ with:
+ dotnet-version: 8.x
- - run: dotnet tool update -g docfx
- - run: docfx docfx.json
+ - run: dotnet tool update -g docfx
+ - run: docfx docfx.json
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v3
- with:
- path: '_site'
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v4
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
+ with:
+ path: "_site"
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
diff --git a/.github/workflows/dotnet-ci.yml b/.github/workflows/dotnet-ci.yml
index 184414ff..3e76c809 100644
--- a/.github/workflows/dotnet-ci.yml
+++ b/.github/workflows/dotnet-ci.yml
@@ -6,15 +6,19 @@ on:
pull_request:
types: [opened, synchronize]
+concurrency:
+ group: build-and-test-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
build-test-cov:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@b2ace4b12f4cec1b96b6361ff2694ba9e931ceb4 # v3.3.1
with:
dotnet-version: |
8.0.x
@@ -28,9 +32,10 @@ jobs:
working-directory: ./
- name: Test
+ id: test
run: |
dotnet tool install --global coverlet.console
- dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage.info
+ timeout 30m dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage.info
shell: bash
env:
THIRDWEB_SECRET_KEY: ${{ secrets.THIRDWEB_SECRET_KEY }}
@@ -39,9 +44,10 @@ jobs:
PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
- name: Codecov
- uses: codecov/codecov-action@v4
+ if: always() && steps.test.outcome != 'cancelled'
+ uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./
verbose: true
- slug: thirdweb-dev/thirdweb-dotnet
+ slug: thirdweb-dev/dotnet
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6725b444..9df59033 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,10 +11,10 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup .NET
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@b2ace4b12f4cec1b96b6361ff2694ba9e931ceb4 # v3.3.1
with:
dotnet-version: "8.0.x"
@@ -30,7 +30,7 @@ jobs:
run: dotnet nuget push "./Thirdweb/bin/Release/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
- name: Upload build artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with:
name: build-artifacts
path: |
diff --git a/Directory.Build.props b/Directory.Build.props
deleted file mode 100644
index 147eebb7..00000000
--- a/Directory.Build.props
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- 0.4.0
- netstandard2.1;net6.0;net7.0;net8.0
-
-
- latest
- true
- enable
-
-
-
- $(DefaultVersion)
- $(DefaultVersion)
- $(DefaultVersion)
-
-
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
deleted file mode 100644
index fa995f44..00000000
--- a/Directory.Packages.props
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- true
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..c4559759
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,241 @@
+# Thirdweb Makefile
+# Cross-platform targets to mirror tw.bat functionality
+# Requires: GNU Make, dotnet SDK, optional CSharpier
+
+# Use bash for consistent behavior across platforms (Git Bash/MSYS2/WSL/macOS/Linux)
+SHELL := bash
+.SHELLFLAGS := -o pipefail -c
+
+# Default target
+.DEFAULT_GOAL := help
+
+# Tools and paths
+DOTNET := dotnet
+API_CLIENT := Thirdweb/Thirdweb.Api/ThirdwebApi.cs
+CONSOLE_PROJ := Thirdweb.Console
+GENERATOR_PROJ := Thirdweb.Generator
+LIB_PROJ := Thirdweb/Thirdweb.csproj
+
+# Defaults for publishing/building
+CONFIG ?= Release
+TFM ?= netstandard2.1
+RID ?=
+OUT ?=
+
+# Colors (best effort; will be empty if tput is unavailable)
+C_RST := $(shell tput sgr0 2>/dev/null || echo "")
+C_BOLD := $(shell tput bold 2>/dev/null || echo "")
+C_DIM := $(shell tput dim 2>/dev/null || echo "")
+C_RED := $(shell tput setaf 1 2>/dev/null || echo "")
+C_GRN := $(shell tput setaf 2 2>/dev/null || echo "")
+C_YEL := $(shell tput setaf 3 2>/dev/null || echo "")
+C_BLU := $(shell tput setaf 4 2>/dev/null || echo "")
+C_MAG := $(shell tput setaf 5 2>/dev/null || echo "")
+C_CYN := $(shell tput setaf 6 2>/dev/null || echo "")
+
+# Icons
+IC_BUILD := BUILD
+IC_CLEAN := CLEAN
+IC_RESTORE := RESTORE
+IC_TEST := TEST
+IC_PACK := PACK
+IC_RUN := RUN
+IC_GEN := GEN
+IC_INFO := INFO
+IC_OK := OK
+IC_WARN := WARN
+IC_ERR := ERR
+IC_FMT := FMT
+IC_PUB := PUBLISH
+
+hr = printf '$(C_DIM)%s$(C_RST)\n' '--------------------------------------------------------------------'
+msg = printf '%s[%s]%s %s\n' '$(1)' '$(2)' '$(C_RST)' '$(3)'
+
+.PHONY: help
+help:
+ @printf '\n$(C_CYN)$(C_BOLD)%s$(C_RST)\n' 'Thirdweb Tools'
+ @$(hr)
+ @printf 'Usage: $(C_BOLD)make$(C_RST) $(C_CYN)[target]$(C_RST)\n\n'
+ @printf '$(C_BOLD)Targets:$(C_RST)\n'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'build' 'Generate API and build the solution'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'clean' 'Clean build artifacts'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'restore' 'Restore NuGet packages'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'test' 'Run tests'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'pack' 'Generate API (if needed) and create NuGet package'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'publish' 'Publish the Thirdweb project (dotnet publish)'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'run' 'Run the console application'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'generate' 'Generate API client from OpenAPI spec'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'generate-llms' 'Generate llms.txt from XML documentation'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'bump' 'Bump version (BUMP=major|minor|patch, default: patch)'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'lint' 'Check code formatting (dry run)'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'fix' 'Fix code formatting issues'
+ @printf ' $(C_CYN)%-16s$(C_RST) - %s\n' 'help' 'Show this help message'
+ @$(hr)
+
+.PHONY: publish
+# Publish the Thirdweb library project
+# Usage examples:
+# make publish # Release publish
+# make publish CONFIG=Debug # Debug config
+# make publish RID=win-x64 # Target runtime
+# make publish OUT=artifacts/publish # Custom output dir
+publish:
+ @if [ ! -f '$(API_CLIENT)' ]; then \
+ $(call msg,$(C_YEL),$(IC_WARN),API client not found, generating it first) ; \
+ $(MAKE) --no-print-directory generate ; \
+ fi
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_PUB) Publishing Thirdweb project)
+ @CMD="$(DOTNET) publish '$(LIB_PROJ)' -c '$(CONFIG)' -f '$(TFM)'"; \
+ if [ -n "$(RID)" ]; then CMD="$$CMD -r '$(RID)'"; fi; \
+ if [ -n "$(OUT)" ]; then CMD="$$CMD -o '$(OUT)'"; fi; \
+ echo $$CMD; eval $$CMD && \
+ $(call msg,$(C_GRN),$(IC_OK),Publish succeeded) || \
+ $(call msg,$(C_RED),$(IC_ERR),Publish failed)
+
+.PHONY: generate
+# Clean previous file and generate API client
+generate:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_GEN) Cleaning generated API files)
+ @rm -f '$(API_CLIENT)' 2>/dev/null || true
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_GEN) Generating Thirdweb API client with custom generator)
+ @$(DOTNET) run --project '$(GENERATOR_PROJ)' --no-build >/dev/null 2>&1 \
+ || ( \
+ $(call msg,$(C_MAG),>> ,Building generator) ; \
+ $(DOTNET) build '$(GENERATOR_PROJ)' ; \
+ $(call msg,$(C_MAG),>> ,Running generator) ; \
+ $(DOTNET) run --project '$(GENERATOR_PROJ)' \
+ )
+ @$(call msg,$(C_GRN),$(IC_OK),API client generation complete)
+
+.PHONY: generate-llms
+# Generate llms.txt from XML documentation
+generate-llms:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_BUILD) Building Thirdweb in Release mode)
+ @$(DOTNET) build '$(LIB_PROJ)' -c Release >/dev/null 2>&1 || { \
+ $(call msg,$(C_MAG),>> ,Building Thirdweb project) ; \
+ $(DOTNET) build '$(LIB_PROJ)' -c Release ; \
+ }
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_GEN) Generating llms.txt from XML documentation)
+ @$(DOTNET) run --project '$(GENERATOR_PROJ)' -- --llms && \
+ $(call msg,$(C_GRN),$(IC_OK),llms.txt generation complete) || \
+ $(call msg,$(C_RED),$(IC_ERR),llms.txt generation failed)
+
+.PHONY: build
+build:
+ @$(MAKE) --no-print-directory generate
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_BUILD) Building with dotnet build)
+ @$(DOTNET) build && \
+ $(call msg,$(C_GRN),$(IC_OK),Build succeeded) || \
+ $(call msg,$(C_RED),$(IC_ERR),Build failed)
+ @$(MAKE) --no-print-directory generate-llms
+
+.PHONY: clean
+clean:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_CLEAN) Cleaning with dotnet clean)
+ @$(DOTNET) clean && \
+ $(call msg,$(C_GRN),$(IC_OK),Clean completed) || \
+ $(call msg,$(C_RED),$(IC_ERR),Clean failed)
+
+.PHONY: restore
+restore:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_RESTORE) Restoring with dotnet restore)
+ @$(DOTNET) restore && \
+ $(call msg,$(C_GRN),$(IC_OK),Restore completed) || \
+ $(call msg,$(C_RED),$(IC_ERR),Restore failed)
+
+.PHONY: test
+test:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_TEST) Running dotnet test)
+ @$(DOTNET) test && \
+ $(call msg,$(C_GRN),$(IC_OK),All tests passed) || \
+ $(call msg,$(C_RED),$(IC_ERR),Some tests failed)
+
+.PHONY: pack
+pack:
+ @if [ ! -f '$(API_CLIENT)' ]; then \
+ $(call msg,$(C_YEL),$(IC_WARN),API client not found, generating it first) ; \
+ $(MAKE) --no-print-directory generate ; \
+ fi
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_BUILD) Building Release)
+ @$(DOTNET) build --configuration Release || { $(call msg,$(C_RED),$(IC_ERR),Build (Release) failed); exit 1; }
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_PACK) Packing NuGet package(s))
+ @$(DOTNET) pack --configuration Release && \
+ $(call msg,$(C_GRN),$(IC_OK),Pack completed) || \
+ $(call msg,$(C_RED),$(IC_ERR),Packing failed)
+
+.PHONY: run
+run:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_RUN) dotnet run --project $(CONSOLE_PROJ))
+ @$(DOTNET) run --project '$(CONSOLE_PROJ)' && \
+ $(call msg,$(C_GRN),$(IC_OK),Application exited) || \
+ $(call msg,$(C_RED),$(IC_ERR),Application exited with errors)
+
+.PHONY: lint
+lint:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_FMT) Checking code formatting with CSharpier)
+ @csharpier --help >/dev/null 2>&1 || { \
+ $(call msg,$(C_YEL),$(IC_WARN),CSharpier is not installed) ; \
+ printf ' Install it with: dotnet tool install -g csharpier\n' ; \
+ exit 0 ; \
+ }
+ @csharpier check . >/dev/null 2>&1 || { \
+ $(call msg,$(C_YEL),$(IC_WARN),Formatting issues found) ; \
+ printf ' Run "make fix" to automatically fix them.\n' ; \
+ exit 0 ; \
+ }
+ @$(call msg,$(C_GRN),$(IC_OK),Code formatting is correct)
+
+.PHONY: fix
+fix:
+ @$(call msg,$(C_BLU),$(IC_INFO),$(IC_FMT) Running CSharpier formatter)
+ @csharpier --help >/dev/null 2>&1 || { \
+ $(call msg,$(C_YEL),$(IC_WARN),CSharpier is not installed) ; \
+ printf ' Install it with: dotnet tool install -g csharpier\n' ; \
+ exit 0 ; \
+ }
+ @csharpier format . >/dev/null 2>&1 || { \
+ $(call msg,$(C_RED),$(IC_ERR),CSharpier formatting failed) ; \
+ exit 1 ; \
+ }
+ @$(call msg,$(C_GRN),$(IC_OK),Code formatting completed)
+
+.PHONY: bump
+# Bump version in .csproj and Constants.cs
+# Usage: make bump [BUMP=major|minor|patch] (defaults to patch)
+bump:
+ @BUMP_TYPE="$(BUMP)"; \
+ if [ -z "$$BUMP_TYPE" ]; then \
+ BUMP_TYPE="patch"; \
+ printf '%s[%s]%s %s\n' '$(C_BLU)' '$(IC_INFO)' '$(C_RST)' "No BUMP specified, defaulting to patch"; \
+ fi; \
+ if [ "$$BUMP_TYPE" != "major" ] && [ "$$BUMP_TYPE" != "minor" ] && [ "$$BUMP_TYPE" != "patch" ]; then \
+ printf '%s[%s]%s %s\n' '$(C_RED)' '$(IC_ERR)' '$(C_RST)' "Invalid BUMP value: $$BUMP_TYPE"; \
+ printf ' Valid values: major, minor, patch\n'; \
+ exit 1; \
+ fi; \
+ printf '%s[%s]%s %s\n' '$(C_BLU)' '$(IC_INFO)' '$(C_RST)' "Reading current version"; \
+ CURRENT=$$(grep -oP '\K[^<]+' '$(LIB_PROJ)' | head -1); \
+ if [ -z "$$CURRENT" ]; then \
+ printf '%s[%s]%s %s\n' '$(C_RED)' '$(IC_ERR)' '$(C_RST)' "Could not read current version"; \
+ exit 1; \
+ fi; \
+ MAJOR=$$(echo $$CURRENT | cut -d. -f1); \
+ MINOR=$$(echo $$CURRENT | cut -d. -f2); \
+ PATCH=$$(echo $$CURRENT | cut -d. -f3); \
+ if [ "$$BUMP_TYPE" = "major" ]; then \
+ MAJOR=$$((MAJOR + 1)); MINOR=0; PATCH=0; \
+ elif [ "$$BUMP_TYPE" = "minor" ]; then \
+ MINOR=$$((MINOR + 1)); PATCH=0; \
+ elif [ "$$BUMP_TYPE" = "patch" ]; then \
+ PATCH=$$((PATCH + 1)); \
+ fi; \
+ NEW_VERSION="$$MAJOR.$$MINOR.$$PATCH"; \
+ printf '%s[%s]%s %s\n' '$(C_MAG)' '$(IC_INFO)' '$(C_RST)' "Bumping version: $$CURRENT -> $$NEW_VERSION"; \
+ sed -i "s|$$CURRENT|$$NEW_VERSION|" '$(LIB_PROJ)'; \
+ sed -i "s|$$CURRENT|$$NEW_VERSION|" '$(LIB_PROJ)'; \
+ sed -i "s|$$CURRENT|$$NEW_VERSION|" '$(LIB_PROJ)'; \
+ sed -i 's|public const string VERSION = "'"$$CURRENT"'";|public const string VERSION = "'"$$NEW_VERSION"'";|' 'Thirdweb/Thirdweb.Utils/Constants.cs'; \
+ printf '%s[%s]%s %s\n' '$(C_GRN)' '$(IC_OK)' '$(C_RST)' "Version bumped to $$NEW_VERSION"; \
+ printf ' Updated files:\n'; \
+ printf ' - $(LIB_PROJ)\n'; \
+ printf ' - Thirdweb/Thirdweb.Utils/Constants.cs\n'
diff --git a/README.md b/README.md
index c70cc060..a471dc71 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,31 @@
-
+
+[
](https://portal.thirdweb.com/dotnet)
[
](https://www.nuget.org/packages/Thirdweb)
[
](https://www.nuget.org/packages/Thirdweb)
-[
](https://app.codecov.io/gh/thirdweb-dev/thirdweb-dotnet)
-
+[
](https://app.codecov.io/gh/thirdweb-dev/dotnet)
## Overview
-The Thirdweb .NET SDK is a comprehensive library that allows developers to interact with the blockchain using the .NET framework. It simplifies the integration of Web3 functionality into your .NET applications with a robust set of methods and classes.
-
-
+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.
-## Features
+## Core Features
-- **Connect to any EVM network:** Easily connect to Ethereum and other EVM-compatible networks.
-- **Query blockchain data:** Use Thirdweb RPC to fetch blockchain data efficiently.
-- **Interact with smart contracts:** Simplified read and write operations for smart contracts.
-- **In-App Wallets:** Integrate user-friendly wallets within your applications, supporting email, phone, and OAuth login.
-- **Account Abstraction:** Simplify complex account management tasks with smart wallets.
-- **Gasless Transactions:** Enable transactions without requiring users to pay gas fees.
-- **Storage Solutions:** Download and upload files using IPFS.
-- **Transaction Builder:** Easily build and send transactions.
+- **Connect to any EVM network:** Easily connect to blockchain network with its chain id alone.
+- **Interact with smart contracts:** Simplified read and write operations for smart contracts, with various out-of-the-box extensions provided.
+- **In-App Wallets:** Integrate user-friendly wallets within your applications, supporting email, phone, OAuth login or plug your own auth in.
+- **Ecosystem Wallets:** Basically In-App Wallets functionality wise, with the added benefit of being able to securely share your wallets with third party partners.
+- **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.
+- **Storage Solutions:** Download and upload files using IPFS or HTTPS.
+- **Transaction Builder:** Create, manipulate and send low level transactions.
- **Session Keys:** Advanced control for smart wallets to manage permissions and session durations.
+- **Thirdweb Bridge:** Universal interface to use any asset onchain.
+- **Thirdweb Nebula:** Create blockchain-powered AI Agents.
+- **Thirdweb Insight:** Query blockchain data at the speed of light.
+- **Thirdweb Engine:** Interact in creative ways from your backend.
+- **Unity Compatibility**: This SDK has been tested successfully in [Unity 2022.3+](https://portal.thirdweb.com/unity/v5) (All build targets).
- **Godot Compatibility**: This SDK has been tested successfully in [Godot .NET](https://portal.thirdweb.com/dotnet/godot)
+- **MAUI Compatibility**: This SDK has been tested successfully in [MAUI](https://portal.thirdweb.com/dotnet/maui)
## Installation
@@ -34,213 +37,14 @@ Run the following command to install:
dotnet add package Thirdweb
```
-## Usage
-
-You can access the full documentation at https://portal.thirdweb.com/dotnet
-
-### Getting Started
-
-Initialize the Thirdweb client to connect to the blockchain.
-
-For frontend applications:
-
-```csharp
-var client = ThirdwebClient.Create(clientId: "myClientId", bundleId: "com.my.bundleid");
-```
-
-For backend applications:
-
-```csharp
-var secretKey = Environment.GetEnvironmentVariable("THIRDWEB_SECRET_KEY");
-var client = ThirdwebClient.Create(secretKey: secretKey);
-```
-
-### Interacting with Smart Contracts
-
-You can interact with smart contracts by creating a contract instance and calling read/write methods.
-
-**Reading Data**
-
-```csharp
-var contract = await ThirdwebContract.Create(client: client, address: "0x81ebd23aA79bCcF5AaFb9c9c5B0Db4223c39102e", chain: 421614);
-var readResult = await ThirdwebContract.Read(contract, "name");
-Console.WriteLine($"Contract read result: {readResult}");
-```
-
-**Writing Data**
-
-```csharp
-var writeResult = await ThirdwebContract.Write(smartWallet, contract, "mintTo", 0, await smartWallet.GetAddress(), 100);
-Console.WriteLine($"Contract write result: {writeResult}");
-```
-
-### Wallet Interactions
-
-#### In-App Wallets
-
-In-app wallets facilitate user authentication and transactions with support for email, phone, and OAuth logins.
-
-**Email Login**
-
-```csharp
-var inAppWallet = await InAppWallet.Create(client: client, email: "email@example.com");
-
-if (!await inAppWallet.IsConnected()) {
- await inAppWallet.SendOTP();
- Console.WriteLine("Please submit the OTP.");
- var otp = Console.ReadLine();
- (var inAppWalletAddress, var canRetry) = await inAppWallet.SubmitOTP(otp);
- if (inAppWalletAddress == null && canRetry) {
- Console.WriteLine("Please submit the OTP again.");
- otp = Console.ReadLine();
- (inAppWalletAddress, _) = await inAppWallet.SubmitOTP(otp);
- }
- if (inAppWalletAddress == null) {
- Console.WriteLine("OTP login failed. Please try again.");
- return;
- }
-}
-
-Console.WriteLine($"InAppWallet: {await inAppWallet.GetAddress()}");
-```
-
-**OAuth Login**
-
-```csharp
-var inAppWallet = await InAppWallet.Create(client, oauthProvider: OAuthProvider.Google);
-
-// Windows console app example
-var address = await inAppWallet.LoginWithOauth(
- isMobile: false,
- browserOpenAction: (url) =>
- {
- var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
- _ = Process.Start(psi);
- },
-);
-
-// Godot standalone example
-var address = await ThirdwebManager.Instance.InAppWallet.LoginWithOauth(
- isMobile: OS.GetName() == "Android" || OS.GetName() == "iOS",
- browserOpenAction: (url) => OS.ShellOpen(url),
- mobileRedirectScheme: "thirdweb://"
-);
-```
-
-#### Smart Wallets
-
-Smart wallets offer advanced functionalities such as gasless transactions and session keys.
-
-**Creating a Smart Wallet**
-
-```csharp
-var smartWallet = await SmartWallet.Create(client: client, personalWallet: inAppWallet, factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", gasless: true, chainId: 421614);
-
-Console.WriteLine($"Smart Wallet: {await smartWallet.GetAddress()}");
-```
-
-**Gasless Transactions**
-
-```csharp
-var writeResult = await ThirdwebContract.Write(smartWallet, contract, "mintTo", 0, await smartWallet.GetAddress(), 100);
-Console.WriteLine($"Gasless transaction result: {writeResult}");
-```
-
-**Session Key Creation**
-
-Session keys provide temporary keys for smart wallets with specific permissions and durations. This is useful for granting limited access to a wallet.
-
-```csharp
-var sessionKey = await smartWallet.CreateSessionKey(
- signerAddress: await privateKeyWallet.GetAddress(),
- approvedTargets: new List() { Constants.ADDRESS_ZERO },
- nativeTokenLimitPerTransactionInWei: "0",
- permissionStartTimestamp: "0",
- permissionEndTimestamp: (Utils.GetUnixTimeStampNow() + 86400).ToString(),
- reqValidityStartTimestamp: "0",
- reqValidityEndTimestamp: Utils.GetUnixTimeStampIn10Years().ToString()
-);
-```
-
-You may then connect to a specific smart wallet address by passing an account override.
-
-```csharp
-var smartWallet = await SmartWallet.Create(...same parameters with new signer, accountAddressOverride: "0xInitialSmartWalletAddress");
-```
-
-#### Using Private Key Wallets
-
-Private key wallets allow you to interact with the blockchain using a private key. This is useful for server-side applications.
-
-```csharp
-var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY");
-var privateKeyWallet = await PrivateKeyWallet.Create(client: client, privateKeyHex: privateKey);
-Console.WriteLine($"PrivateKey Wallet: {await privateKeyWallet.GetAddress()}");
-```
+## Documentation
-### Advanced Features
+[Documentation Portal](https://portal.thirdweb.com/dotnet)
-**RPC Direct Access**
+[Full API Reference](https://thirdweb-dev.github.io/dotnet/)
-Directly interact with the blockchain using the RPC instance. This allows for low-level access to blockchain data and functions.
+## Need Help?
-```csharp
-var rpc = ThirdwebRPC.GetRpcInstance(client, 421614);
-var blockNumber = await rpc.SendRequestAsync("eth_blockNumber");
-Console.WriteLine($"Block number: {blockNumber}");
-```
-
-**ZkSync Native Account Abstraction**
-
-ZkSync 0x71 (113) type transactions are supported through the Transaction Builder (DIY) or Smart Wallets (Managed).
-
-**DIY Approach**
-
-```csharp
-var tx = await ThirdwebTransaction.Create(
- client: client,
- wallet: privateKeyWallet,
- txInput: new ThirdwebTransactionInput()
- {
- From = await privateKeyWallet.GetAddress(),
- To = await privateKeyWallet.GetAddress(),
- Value = new HexBigInteger(BigInteger.Zero),
- },
- chainId: 300
-);
-tx.SetZkSyncOptions(
- new ZkSyncOptions(
- paymaster: "0xMyGaslessPaymaster",
- paymasterInput: "0x8c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"
- )
-);
-var txHash = await ThirdwebTransaction.Send(transaction: tx);
-Console.WriteLine($"Transaction hash: {txHash}");
-```
-
-**Managed Approach**
-
-With ZkSync, you don't need to pass an account factory address, and the rest works the same.
-
-```csharp
-var zkSyncWallet = await SmartWallet.Create(client: client, personalWallet: inAppWallet, gasless: true, chainId: 300);
-
-Console.WriteLine($"ZkSync Smart Wallet: {await zkSyncWallet.GetAddress()}");
-
-var zkSyncWriteResult = await ThirdwebContract.Write(zkSyncWallet, contract, "mintTo", 0, await zkSyncWallet.GetAddress(), 100);
-Console.WriteLine($"ZkSync gasless transaction result: {zkSyncWriteResult}");
-```
-
-**Storage Solutions**
-
-Download and upload files using IPFS. This is useful for decentralized storage solutions.
-
-```csharp
-var downloadResult = await ThirdwebStorage.Download(client: client, uri: "ipfs://exampleUri");
-Console.WriteLine($"Download result: {downloadResult}");
-
-var uploadResult = await ThirdwebStorage.Upload(client: client, path: "path/to/file");
-Console.WriteLine($"Upload result preview: {uploadResult.PreviewUrl}");
-```
+For any questions or support, visit our [Support Portal](https://thirdweb.com/support).
-For more information, please refer to the [official documentation](https://portal.thirdweb.com/dotnet).
+Thank you for trying out the Thirdweb .NET SDK!
diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs
index 5b05dfb5..f3ff8fef 100644
--- a/Thirdweb.Console/Program.cs
+++ b/Thirdweb.Console/Program.cs
@@ -1,184 +1,506 @@
-using Thirdweb;
-using dotenv.net;
+#pragma warning disable IDE0005
+#pragma warning disable IDE0059
+
using System.Diagnostics;
+using dotenv.net;
+using Newtonsoft.Json;
+using Thirdweb;
DotEnv.Load();
// Do not use secret keys client side, use client id/bundle id instead
var secretKey = Environment.GetEnvironmentVariable("THIRDWEB_SECRET_KEY");
-// Do not use private keys client side, use InAppWallet/SmartWallet instead
-var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY");
-
-// Fetch timeout options are optional, default is 60000ms
-var client = ThirdwebClient.Create(secretKey: secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 30000, rpc: 60000));
-
-var contract = await ThirdwebContract.Create(client: client, address: "0x81ebd23aA79bCcF5AaFb9c9c5B0Db4223c39102e", chain: 421614);
-var readResult = await contract.ERC20_Name();
-Console.WriteLine($"Contract read result: {readResult}");
-
-// Create wallets (this is an advanced use case, typically one wallet is plenty)
-var privateKeyWallet = await PrivateKeyWallet.Create(client: client, privateKeyHex: privateKey);
-
-// var inAppWallet = await InAppWallet.Create(client: client, email: "firekeeper+awsless@thirdweb.com"); // or email: null, phoneNumber: "+1234567890"
-
-var inAppWallet = await InAppWallet.Create(client: client, authprovider: AuthProvider.Google); // or email: null, phoneNumber: "+1234567890"
-
-// Reset InAppWallet (optional step for testing login flow)
-if (await inAppWallet.IsConnected())
-{
- await inAppWallet.Disconnect();
-}
-
-// Relog if InAppWallet not logged in
-if (!await inAppWallet.IsConnected())
-{
- var address = await inAppWallet.LoginWithOauth(
- isMobile: false,
- (url) =>
- {
- var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
- _ = Process.Start(psi);
- },
- "thirdweb://",
- new InAppWalletBrowser()
- );
- Console.WriteLine($"InAppWallet address: {address}");
-}
-
-// await inAppWallet.SendOTP();
-// Console.WriteLine("Please submit the OTP.");
-// retry:
-// var otp = Console.ReadLine();
-// (var inAppWalletAddress, var canRetry) = await inAppWallet.SubmitOTP(otp);
-// if (inAppWalletAddress == null && canRetry)
+// Fetch timeout options are optional, default is 120000ms
+var client = ThirdwebClient.Create(secretKey: secretKey);
+
+#region Signing Messages
+
+// Create a guest wallet
+var guestWallet = await InAppWallet.Create(client, authProvider: AuthProvider.Guest);
+var walletAddress = await guestWallet.LoginWithGuest();
+Console.WriteLine($"Guest Wallet address: {walletAddress}");
+
+var signature = await guestWallet.PersonalSign("Hello, Thirdweb!");
+Console.WriteLine($"Guest Wallet personal sign: {signature}");
+
+#endregion
+
+#region Reading from Contracts
+
+// var contract = await ThirdwebContract.Create(client: client, address: "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", chain: 1);
+// var nfts = await contract.ERC721_GetNFT(0);
+// Console.WriteLine($"NFTs: {JsonConvert.SerializeObject(nfts, Formatting.Indented)}");
+
+#endregion
+
+#region User Wallets (Social Auth Example)
+
+// var inAppWalletOAuth = await InAppWallet.Create(client: client, authProvider: AuthProvider.Google);
+// if (!await inAppWalletOAuth.IsConnected())
// {
-// Console.WriteLine("Please submit the OTP again.");
-// goto retry;
+// _ = await inAppWalletOAuth.LoginWithOauth(
+// isMobile: false,
+// (url) =>
+// {
+// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
+// _ = Process.Start(psi);
+// },
+// "thirdweb://",
+// new InAppWalletBrowser()
+// );
// }
-// if (inAppWalletAddress == null)
+// var inAppWalletOAuthAddress = await inAppWalletOAuth.GetAddress();
+// Console.WriteLine($"InAppWallet OAuth address: {inAppWalletOAuthAddress}");
+
+// var inAppWalletAuthDetails = inAppWalletOAuth.GetUserAuthDetails();
+// Console.WriteLine($"InAppWallet OAuth auth details: {JsonConvert.SerializeObject(inAppWalletAuthDetails, Formatting.Indented)}");
+
+#endregion
+
+#region Server Wallets
+
+// // ServerWallet is compatible with IThirdwebWallet and can be used with any SDK method/extension
+// var serverWallet = await ServerWallet.Create(
+// client: client,
+// label: "Test",
+// // Optional, defaults to Auto - we choose between EIP-7702, EIP-4337 or native zkSync AA execution / EOA is also available
+// executionOptions: new AutoExecutionOptions()
+// );
+
+// var serverWalletAddress = await serverWallet.GetAddress();
+// Console.WriteLine($"Server Wallet address: {serverWalletAddress}");
+
+// var serverWalletPersonalSig = await serverWallet.PersonalSign("Hello, Thirdweb!");
+// Console.WriteLine($"Server Wallet personal sign: {serverWalletPersonalSig}");
+
+// var json =
+// /*lang=json,strict*/
+// "{\"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\":84532,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbBBbBbbBbBbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}";
+// var serverWalletTypedDataSign = await serverWallet.SignTypedDataV4(json);
+// Console.WriteLine($"Server Wallet typed data sign: {serverWalletTypedDataSign}");
+
+// // Simple self transfer
+// var serverWalletReceipt = await serverWallet.Transfer(chainId: 84532, toAddress: await serverWallet.GetAddress(), weiAmount: 0);
+// Console.WriteLine($"Server Wallet Hash: {serverWalletReceipt.TransactionHash}");
+
+// // ServerWallet forcing ERC-4337 Execution Mode
+// var smartServerWallet = await ServerWallet.Create(client: client, label: "Test", executionOptions: new ERC4337ExecutionOptions(chainId: 84532, signerAddress: serverWalletAddress));
+// var smartServerWalletAddress = await smartServerWallet.GetAddress();
+// Console.WriteLine($"Smart Server Wallet address: {smartServerWalletAddress}");
+
+// var smartServerWalletPersonalSig = await smartServerWallet.PersonalSign("Hello, Thirdweb!");
+// Console.WriteLine($"Smart Server Wallet personal sign: {smartServerWalletPersonalSig}");
+
+// var smartServerWalletTypedDataSign = await smartServerWallet.SignTypedDataV4(json);
+// Console.WriteLine($"Smart Server Wallet typed data sign: {smartServerWalletTypedDataSign}");
+
+// // Simple self transfer
+// var smartServerWalletReceipt = await smartServerWallet.Transfer(chainId: 84532, toAddress: await smartServerWallet.GetAddress(), weiAmount: 0);
+// Console.WriteLine($"Server Wallet Hash: {smartServerWalletReceipt.TransactionHash}");
+
+#endregion
+
+#region Thirdweb API Wrapper
+
+// var metadata = await client.Api.GetContractMetadataAsync(chainId: 1, address: "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8");
+
+// Console.WriteLine($"ABI: {JsonConvert.SerializeObject(metadata.Result.Output.Abi, Formatting.Indented)}");
+// Console.WriteLine($"Compiler version: {metadata.Result.Compiler.Version}");
+
+#endregion
+
+#region AA 7702
+
+// var chain = 84532; // 7702-compatible chain
+
+// // Connect to EOA
+// var smartEoa = await InAppWallet.Create(client, authProvider: AuthProvider.Guest, executionMode: ExecutionMode.EIP7702Sponsored);
+// if (!await smartEoa.IsConnected())
// {
-// Console.WriteLine("OTP login failed. Please try again.");
-// return;
-// }
-// Console.WriteLine($"InAppWallet address: {inAppWalletAddress}");
+// _ = await smartEoa.LoginWithGuest(defaultSessionIdOverride: new Guid().ToString());
// }
+// var smartEoaAddress = await smartEoa.GetAddress();
+// Console.WriteLine($"User Wallet address: {await smartEoa.GetAddress()}");
+
+// // Transact, will upgrade EOA
+// var receipt = await smartEoa.Transfer(chainId: chain, toAddress: await Utils.GetAddressFromENS(client, "vitalik.eth"), weiAmount: 0);
+// Console.WriteLine($"Transfer Receipt: {receipt.TransactionHash}");
+
+#endregion
+
+#region AA 0.6
+
+// var smartWallet06 = await SmartWallet.Create(personalWallet: guestWallet, chainId: 421614, gasless: true);
+// var receipt06 = await smartWallet06.Transfer(chainId: 421614, toAddress: await smartWallet06.GetAddress(), weiAmount: 0);
+// Console.WriteLine($"Receipt: {receipt06}");
+
+#endregion
+
+#region AA 0.7
+
+// var smartWallet07 = await SmartWallet.Create(personalWallet: guestWallet, chainId: 421614, gasless: true, entryPoint: Constants.ENTRYPOINT_ADDRESS_V07);
+// var receipt07 = await smartWallet07.Transfer(chainId: 421614, toAddress: await smartWallet07.GetAddress(), weiAmount: 0);
+// Console.WriteLine($"Receipt: {receipt07}");
+
+#endregion
+
+#region AA ZkSync
+
+// var zkSmartWallet = await SmartWallet.Create(personalWallet: privateKeyWallet, chainId: 11124, gasless: true);
+
+// var hash = await zkSmartWallet.SendTransaction(new ThirdwebTransactionInput(chainId: 11124, to: await zkSmartWallet.GetAddress(), value: 0, data: "0x"));
+
+// Console.WriteLine($"Transaction hash: {hash}");
+
+#endregion
+
+#region Deploy Contract
+
+// var serverWallet = await ServerWallet.Create(client: client, label: "TestFromDotnet");
+
+// var abi =
+// "[ { \"inputs\": [], \"name\": \"welcome\", \"outputs\": [ { \"internalType\": \"string\", \"name\": \"\", \"type\": \"string\" } ], \"stateMutability\": \"pure\", \"type\": \"function\" } ]";
-// Prepare a transaction directly, or with Contract.Prepare
-// var tx = await ThirdwebTransaction.Create(
+// var contractAddress = await ThirdwebContract.Deploy(
// client: client,
-// wallet: privateKeyWallet,
-// txInput: new ThirdwebTransactionInput()
+// chainId: 11155111,
+// serverWalletAddress: await serverWallet.GetAddress(),
+// bytecode: "6080604052348015600e575f5ffd5b5061014e8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063b627cf3b1461002d575b5f5ffd5b61003561004b565b60405161004291906100f8565b60405180910390f35b60606040518060400160405280601481526020017f57656c636f6d6520746f20746869726477656221000000000000000000000000815250905090565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6100ca82610088565b6100d48185610092565b93506100e48185602086016100a2565b6100ed816100b0565b840191505092915050565b5f6020820190508181035f83015261011081846100c0565b90509291505056fea264697066735822122001498e9d7d6125ce22613ef32fdb7e8e03bf11ad361d7b00e210b82d7b7e0d4464736f6c634300081e0033",
+// abi: abi
+// );
+// Console.WriteLine($"Contract deployed at: {contractAddress}");
+
+// var contract = await ThirdwebContract.Create(client: client, address: contractAddress, chain: 11155111, abi: abi);
+// var welcomeMessage = await contract.Read("welcome");
+// Console.WriteLine($"Welcome message from deployed contract: {welcomeMessage}");
+
+#endregion
+
+#region Get Social Profiles
+
+// var socialProfiles = await Utils.GetSocialProfiles(client, "joenrv.eth");
+// Console.WriteLine($"Social Profiles: {socialProfiles}");
+
+#endregion
+
+#region EIP-7702 (Low Level)
+
+// var chain = 42220; // 7702-compatible chain
+
+// // Connect to EOA
+// var smartEoa = await InAppWallet.Create(client, authProvider: AuthProvider.Guest, executionMode: ExecutionMode.EIP7702Sponsored);
+// if (!await smartEoa.IsConnected())
+// {
+// _ = await smartEoa.LoginWithGuest(defaultSessionIdOverride: new Guid().ToString());
+// }
+// var smartEoaAddress = await smartEoa.GetAddress();
+// Console.WriteLine($"User Wallet address: {await smartEoa.GetAddress()}");
+
+// // Upgrade EOA - This wallet explicitly uses EIP-7702 delegation to the thirdweb MinimalAccount (will delegate upon first tx)
+// var signerAddress = await Utils.GetAddressFromENS(client, "vitalik.eth");
+
+// // Transact, will upgrade EOA
+// var receipt = await smartEoa.Transfer(chainId: chain, toAddress: signerAddress, weiAmount: 0);
+// Console.WriteLine($"Transfer Receipt: {receipt.TransactionHash}");
+
+// // Double check that it was upgraded
+// var isDelegated = await Utils.IsDeployed(client, chain, smartEoaAddress);
+// Console.WriteLine($"Is delegated: {isDelegated}");
+
+// // Create a session key
+// var sessionKeyReceipt = await smartEoa.CreateSessionKey(chainId: chain, signerAddress: signerAddress, durationInSeconds: 86400, grantFullPermissions: true);
+// Console.WriteLine($"Session key receipt: {sessionKeyReceipt.TransactionHash}");
+
+// // Validate session key config
+// var hasFullPermissions = await smartEoa.SignerHasFullPermissions(chain, signerAddress);
+// Console.WriteLine($"Signer has full permissions: {hasFullPermissions}");
+
+// var sessionExpiration = await smartEoa.GetSessionExpirationForSigner(chain, signerAddress);
+// Console.WriteLine($"Session expires in {sessionExpiration - Utils.GetUnixTimeStampNow()} seconds");
+
+// // Create a session key with granular permissions
+// var granularSessionKeyReceipt = await smartEoa.CreateSessionKey(
+// chainId: chain,
+// signerAddress: signerAddress,
+// durationInSeconds: 86400,
+// grantFullPermissions: false,
+// transferPolicies: new List
// {
-// From = await privateKeyWallet.GetAddress(),
-// To = await privateKeyWallet.GetAddress(),
-// Value = new HexBigInteger(BigInteger.Zero),
-// },
-// chainId: 300
+// new()
+// {
+// Target = signerAddress,
+// MaxValuePerUse = BigInteger.Parse("0.001".ToWei()),
+// ValueLimit = new UsageLimit
+// {
+// LimitType = 1, // Lifetime
+// Limit = BigInteger.Parse("0.01".ToWei()),
+// Period = 86400, // 1 day
+// }
+// }
+// }
// );
-// // Set zkSync options
-// tx.SetZkSyncOptions(
-// new ZkSyncOptions(
-// // Paymaster contract address
-// paymaster: "0xbA226d47Cbb2731CBAA67C916c57d68484AA269F",
-// // IPaymasterFlow interface encoded data
-// paymasterInput: "0x8c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"
-// )
+// // Validate session key config
+// var sessionState = await smartEoa.GetSessionStateForSigner(chain, signerAddress);
+// Console.WriteLine($"Session state: {JsonConvert.SerializeObject(sessionState, Formatting.Indented)}");
+
+// var transferPolcies = await smartEoa.GetTransferPoliciesForSigner(chain, signerAddress);
+// Console.WriteLine($"Transfer policies: {JsonConvert.SerializeObject(transferPolcies, Formatting.Indented)}");
+
+// var callPolicies = await smartEoa.GetCallPoliciesForSigner(chain, signerAddress);
+// Console.WriteLine($"Call policies: {JsonConvert.SerializeObject(callPolicies, Formatting.Indented)}");
+
+#endregion
+
+#region Smart Ecosystem Wallet
+
+// var eco = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.the-bonfire", authProvider: AuthProvider.Github);
+// if (!await eco.IsConnected())
+// {
+// _ = await eco.LoginWithOauth(
+// isMobile: false,
+// browserOpenAction: (url) =>
+// {
+// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
+// _ = Process.Start(psi);
+// }
+// );
+// }
+// var smartEco = await SmartWallet.Create(eco, 421614);
+// var addy = await smartEco.GetAddress();
+// Console.WriteLine($"Smart Ecosystem Wallet address: {addy}");
+
+#endregion
+
+#region Ecosystem Wallet
+
+// var ecosystemWallet = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.the-bonfire", authProvider: AuthProvider.Telegram);
+
+// if (!await ecosystemWallet.IsConnected())
+// {
+// _ = await ecosystemWallet.LoginWithOauth(
+// isMobile: false,
+// (url) =>
+// {
+// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
+// _ = Process.Start(psi);
+// },
+// "thirdweb://",
+// new InAppWalletBrowser()
+// );
+// }
+// var ecosystemWalletAddress = await ecosystemWallet.GetAddress();
+// Console.WriteLine($"Ecosystem Wallet address: {ecosystemWalletAddress}");
+
+// var ecosystemPersonalSignature = await ecosystemWallet.PersonalSign("Hello, Thirdweb!");
+// Console.WriteLine($"Ecosystem Wallet personal sign: {ecosystemPersonalSignature}");
+
+// var ecosystemTypedSignature = await ecosystemWallet.SignTypedDataV4(
+// /*lang=json,strict*/
+// "{\"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!\"}}"
// );
+// Console.WriteLine($"Ecosystem Wallet typed sign: {ecosystemTypedSignature}");
-// // Send as usual, it's now gasless!
-// var txHash = await ThirdwebTransaction.Send(transaction: tx);
-// Console.WriteLine($"Transaction hash: {txHash}");
-
-// var zkSmartWallet = await SmartWallet.Create(client: client, personalWallet: privateKeyWallet, chainId: 302, gasless: true);
-// Console.WriteLine($"Smart wallet address: {await zkSmartWallet.GetAddress()}");
-// var zkAaTx = await ThirdwebTransaction.Create(client, zkSmartWallet, new ThirdwebTransactionInput() { From = await zkSmartWallet.GetAddress(), To = await zkSmartWallet.GetAddress(), }, 302);
-// var zkSyncSignatureBasedAaTxHash = await ThirdwebTransaction.Send(zkAaTx);
-// Console.WriteLine($"Transaction hash: {zkSyncSignatureBasedAaTxHash}");
-
-// Create smart wallet with InAppWallet signer
-// var smartWallet = await SmartWallet.Create(client: client, personalWallet: inAppWallet, factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", gasless: true, chainId: 421614);
-// var res = await smartWallet.Authenticate("http://localhost:8000", 421614);
-// Console.WriteLine($"Smart wallet auth result: {res}");
-
-// // Grant a session key to pk wallet (advanced use case)
-// _ = await smartWallet.CreateSessionKey(
-// signerAddress: await privateKeyWallet.GetAddress(),
-// approvedTargets: new List() { Constants.ADDRESS_ZERO },
-// nativeTokenLimitPerTransactionInWei: "0",
-// permissionStartTimestamp: "0",
-// permissionEndTimestamp: (Utils.GetUnixTimeStampNow() + 86400).ToString(),
-// reqValidityStartTimestamp: "0",
-// reqValidityEndTimestamp: Utils.GetUnixTimeStampIn10Years().ToString()
+// var ecosystemWalletOther = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.the-bonfire", authProvider: AuthProvider.Telegram);
+// var linkedAccounts = await ecosystemWallet.LinkAccount(
+// walletToLink: ecosystemWalletOther,
+// browserOpenAction: (url) =>
+// {
+// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
+// _ = Process.Start(psi);
+// }
// );
+// Console.WriteLine($"Linked accounts: {JsonConvert.SerializeObject(linkedAccounts, Formatting.Indented)}");
-// // Reconnect to same smart wallet with pk wallet as signer (specifying wallet address override)
-// smartWallet = await SmartWallet.Create(
-// client: client,
-// personalWallet: privateKeyWallet,
-// factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052",
-// gasless: true,
-// chainId: 421614,
-// accountAddressOverride: await smartWallet.GetAddress()
+// var ecosystemSmartWallet = await SmartWallet.Create(ecosystemWallet, 421614);
+
+// var ecosystemTx = await ThirdwebTransaction.Create(wallet: ecosystemSmartWallet, txInput: new ThirdwebTransactionInput(chainId: 421614, to: await ecosystemWallet.GetAddress()));
+
+// var ecosystemTxHash = await ThirdwebTransaction.Send(ecosystemTx);
+// Console.WriteLine($"Ecosystem Wallet transaction hash: {ecosystemTxHash}");
+
+#endregion
+
+#region Maximum low level zksync tx
+
+// var chainId = 300;
+
+// var zkRawWallet = await PrivateKeyWallet.Generate(client: client);
+// var zkRawAddy = await zkRawWallet.GetAddress();
+// Console.WriteLine($"ZkSync raw address: {zkRawAddy}");
+
+// // Less raw example
+
+// var zkRawTx = await ThirdwebTransaction.Create(
+// wallet: zkRawWallet,
+// txInput: new ThirdwebTransactionInput(chainId: chainId, from: zkRawAddy, to: zkRawAddy, value: 0, data: "0x", zkSync: new ZkSyncOptions(gasPerPubdataByteLimit: 50000))
// );
-// // Log addresses
-// Console.WriteLine($"PrivateKey Wallet: {await privateKeyWallet.GetAddress()}");
-// Console.WriteLine($"InAppWallet: {await inAppWallet.GetAddress()}");
-// Console.WriteLine($"Smart Wallet: {await smartWallet.GetAddress()}");
-
-// // Sign, triggering deploy as needed and 1271 verification if it's a smart wallet
-// var message = "Hello, Thirdweb!";
-// var signature = await smartWallet.PersonalSign(message);
-// Console.WriteLine($"Signed message: {signature}");
-
-// var balanceBefore = await ThirdwebContract.Read(contract, "balanceOf", await smartWallet.GetAddress());
-// Console.WriteLine($"Balance before mint: {balanceBefore}");
-
-// var writeResult = await ThirdwebContract.Write(smartWallet, contract, "mintTo", 0, await smartWallet.GetAddress(), 100);
-// Console.WriteLine($"Contract write result: {writeResult}");
-
-// var balanceAfter = await ThirdwebContract.Read(contract, "balanceOf", await smartWallet.GetAddress());
-// Console.WriteLine($"Balance after mint: {balanceAfter}");
-
-// // Transaction Builder
-// var preparedTx = await ThirdwebContract.Prepare(wallet: smartWallet, contract: contract, method: "mintTo", weiValue: 0, parameters: new object[] { await smartWallet.GetAddress(), 100 });
-// Console.WriteLine($"Prepared transaction: {preparedTx}");
-// var estimatedCosts = await ThirdwebTransaction.EstimateGasCosts(preparedTx);
-// Console.WriteLine($"Estimated ETH gas cost: {estimatedCosts.ether}");
-// var totalCosts = await ThirdwebTransaction.EstimateTotalCosts(preparedTx);
-// Console.WriteLine($"Estimated ETH total cost: {totalCosts.ether}");
-// var simulationData = await ThirdwebTransaction.Simulate(preparedTx);
-// Console.WriteLine($"Simulation data: {simulationData}");
-// var txHash = await ThirdwebTransaction.Send(preparedTx);
-// Console.WriteLine($"Transaction hash: {txHash}");
-// var receipt = await ThirdwebTransaction.WaitForTransactionReceipt(client, 421614, txHash);
-// Console.WriteLine($"Transaction receipt: {JsonConvert.SerializeObject(receipt)}");
-
-// // Transaction Builder - raw transfer
-// var rawTx = new ThirdwebTransactionInput
+// zkRawTx = await ThirdwebTransaction.Prepare(zkRawTx);
+
+// Console.WriteLine($"ZkSync raw transaction: {zkRawTx}");
+// Console.WriteLine("Make sure you have enough funds!");
+// Console.ReadLine();
+
+// var receipt = await ThirdwebTransaction.SendAndWaitForTransactionReceipt(zkRawTx);
+// Console.WriteLine($"Receipt: {receipt}");
+
+// // Extremely raw example
+
+// var zkRawTx = new Thirdweb.AccountAbstraction.ZkSyncAATransaction
// {
-// From = await smartWallet.GetAddress(),
-// To = await smartWallet.GetAddress(),
-// Value = new HexBigInteger(BigInteger.Zero),
-// Data = "0x",
+// TxType = 0x71,
+// From = new HexBigInteger(zkRawAddy).Value,
+// To = new HexBigInteger(zkRawAddy).Value,
+// GasLimit = 250000,
+// GasPerPubdataByteLimit = 50000,
+// MaxFeePerGas = 1000000000,
+// MaxPriorityFeePerGas = 1000000000,
+// Paymaster = 0,
+// Nonce = 0,
+// Value = 0,
+// Data = new byte[] { 0x00 },
+// FactoryDeps = new List(),
+// PaymasterInput = Array.Empty(),
// };
-// var preparedRawTx = await ThirdwebTransaction.Create(client: client, wallet: smartWallet, txInput: rawTx, chainId: 421614);
-// Console.WriteLine($"Prepared raw transaction: {preparedRawTx}");
-// var estimatedCostsRaw = await ThirdwebTransaction.EstimateGasCosts(preparedRawTx);
-// Console.WriteLine($"Estimated ETH gas cost: {estimatedCostsRaw.ether}");
-// var totalCostsRaw = await ThirdwebTransaction.EstimateTotalCosts(preparedRawTx);
-// Console.WriteLine($"Estimated ETH total cost: {totalCostsRaw.ether}");
-// var simulationDataRaw = await ThirdwebTransaction.Simulate(preparedRawTx);
-// Console.WriteLine($"Simulation data: {simulationDataRaw}");
-// var txHashRaw = await ThirdwebTransaction.Send(preparedRawTx);
-// Console.WriteLine($"Raw transaction hash: {txHashRaw}");
-// var receiptRaw = await ThirdwebTransaction.WaitForTransactionReceipt(client, 421614, txHashRaw);
-// Console.WriteLine($"Raw transaction receipt: {JsonConvert.SerializeObject(receiptRaw)}");
-
-
-// Storage actions
+// var signedZkRawTx = await EIP712.GenerateSignature_ZkSyncTransaction("zkSync", "2", chainId, zkRawTx, zkRawWallet);
+
+// Console.WriteLine($"ZkSync raw transaction: {JsonConvert.SerializeObject(zkRawTx, Formatting.Indented)}");
+// Console.WriteLine("Make sure you have enough funds!");
+// Console.ReadLine();
+
+// var rpcInstance = ThirdwebRPC.GetRpcInstance(client, chainId);
+// var hash = await rpcInstance.SendRequestAsync("eth_sendRawTransaction", signedZkRawTx);
+// Console.WriteLine($"Transaction hash: {hash}");
+
+#endregion
+
+#region Backend Wallet Auth
+
+// var inAppWalletBackend = await InAppWallet.Create(client: client, authProvider: AuthProvider.Backend, walletSecret: "very-secret");
+// if (!await inAppWalletBackend.IsConnected())
+// {
+// _ = await inAppWalletBackend.LoginWithBackend();
+// }
+// var inAppWalletBackendAddress = await inAppWalletBackend.GetAddress();
+// Console.WriteLine($"InAppWallet Backend address: {inAppWalletBackendAddress}");
+
+#endregion
+
+#region Account Linking
+
+// var inAppWalletMain = await InAppWallet.Create(client: client, authProvider: AuthProvider.Telegram);
+// if (!await inAppWalletMain.IsConnected())
+// {
+// _ = await inAppWalletMain.LoginWithOauth(
+// isMobile: false,
+// (url) =>
+// {
+// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
+// _ = Process.Start(psi);
+// },
+// "thirdweb://",
+// new InAppWalletBrowser()
+// );
+// }
+// Console.WriteLine($"Main InAppWallet address: {await inAppWalletMain.GetAddress()}");
+
+// var oldLinkedAccounts = await inAppWalletMain.GetLinkedAccounts();
+// Console.WriteLine($"Old linked accounts: {JsonConvert.SerializeObject(oldLinkedAccounts, Formatting.Indented)}");
+
+// // External wallet variant
+// var externalWallet = await PrivateKeyWallet.Generate(client: client);
+// var inAppWalletToLink = await InAppWallet.Create(client: client, authProvider: AuthProvider.Siwe, siweSigner: externalWallet);
+// var linkedAccounts = await inAppWalletMain.LinkAccount(walletToLink: inAppWalletToLink, chainId: 421614);
+// Console.WriteLine($"Linked accounts: {JsonConvert.SerializeObject(linkedAccounts, Formatting.Indented)}");
+
+// var unlinkingResult = await inAppWalletMain.UnlinkAccount(linkedAccounts.First(linkedAccounts => linkedAccounts.Type == "siwe"));
+// Console.WriteLine($"Unlinking result: {JsonConvert.SerializeObject(unlinkingResult, Formatting.Indented)}");
+
+#endregion
+
+#region Smart Wallet - Authenticate
+
+// var appWallet = await InAppWallet.Create(client: client, authProvider: AuthProvider.Google);
+// if (!await appWallet.IsConnected())
+// {
+// _ = await appWallet.LoginWithOauth(
+// isMobile: false,
+// (url) =>
+// {
+// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
+// _ = Process.Start(psi);
+// },
+// "thirdweb://",
+// new InAppWalletBrowser()
+// );
+// }
+// var smartWallet = await SmartWallet.Create(appWallet, 37714555429);
+
+// var data = await smartWallet.Authenticate(
+// domain: "https://myepicdomain.com",
+// chainId: 37714555429,
+// authPayloadPath: "/my-epic-auth/login",
+// authLoginPath: "/my-epic-auth/login",
+// separatePayloadAndSignatureInBody: true,
+// authPayloadMethod: "GET",
+// authLoginMethod: "POST"
+// );
+// Console.WriteLine($"Token: {data["token"]}");
+
+#endregion
+
+#region TokenPaymaster - Celo CUSD
+
+// var chainId = 42220; // celo
+
+// var erc20SmartWallet = await SmartWallet.Create(personalWallet: privateKeyWallet, chainId: chainId, tokenPaymaster: TokenPaymaster.CELO_CUSD);
+
+// var erc20SmartWalletAddress = await erc20SmartWallet.GetAddress();
+// Console.WriteLine($"ERC20 Smart Wallet address: {erc20SmartWalletAddress}");
+
+// var receipt = await erc20SmartWallet.Transfer(chainId: chainId, toAddress: erc20SmartWalletAddress, weiAmount: 0);
+// Console.WriteLine($"Receipt: {JsonConvert.SerializeObject(receipt, Formatting.Indented)}");
+
+#endregion
+
+#region TokenPaymaster - Base USDC
+
+// var chainId = 8453; // base
+
+// var erc20SmartWallet = await SmartWallet.Create(personalWallet: privateKeyWallet, chainId: chainId, tokenPaymaster: TokenPaymaster.BASE_USDC);
+
+// var erc20SmartWalletAddress = await erc20SmartWallet.GetAddress();
+// Console.WriteLine($"ERC20 Smart Wallet address: {erc20SmartWalletAddress}");
+
+// var receipt = await erc20SmartWallet.Transfer(chainId: chainId, toAddress: erc20SmartWalletAddress, weiAmount: 0);
+// Console.WriteLine($"Receipt: {JsonConvert.SerializeObject(receipt, Formatting.Indented)}");
+
+#endregion
+
+#region TokenPaymaster - Lisk LSK
+
+// var chainId = 1135; // lisk
+
+// var erc20SmartWallet = await SmartWallet.Create(personalWallet: privateKeyWallet, chainId: chainId, tokenPaymaster: TokenPaymaster.LISK_LSK);
+
+// var erc20SmartWalletAddress = await erc20SmartWallet.GetAddress();
+// Console.WriteLine($"ERC20 Smart Wallet address: {erc20SmartWalletAddress}");
+
+// var receipt = await erc20SmartWallet.Transfer(chainId: chainId, toAddress: erc20SmartWalletAddress, weiAmount: 0);
+// Console.WriteLine($"Receipt: {JsonConvert.SerializeObject(receipt, Formatting.Indented)}");
+
+#endregion
+
+#region Chain Data Fetching
+
+// var chainData = await Utils.GetChainMetadata(client, 421614);
+// Console.WriteLine($"Chain data: {JsonConvert.SerializeObject(chainData, Formatting.Indented)}");
+
+#endregion
+
+#region Storage Actions
// // Will download from IPFS or normal urls
// var downloadResult = await ThirdwebStorage.Download(client: client, uri: "AnyUrlIncludingIpfs");
@@ -188,9 +510,13 @@
// var uploadResult = await ThirdwebStorage.Upload(client: client, path: "AnyPath");
// Console.WriteLine($"Upload result preview: {uploadResult.PreviewUrl}");
+#endregion
-// Access RPC directly if needed, generally not recommended
+#region RPC Access
+// // Access RPC directly if needed, generally not recommended
// var rpc = ThirdwebRPC.GetRpcInstance(client, 421614);
// var blockNumber = await rpc.SendRequestAsync("eth_blockNumber");
// Console.WriteLine($"Block number: {blockNumber}");
+
+#endregion
diff --git a/Thirdweb.Console/Thirdweb.Console.csproj b/Thirdweb.Console/Thirdweb.Console.csproj
index 2acaa4b5..899409eb 100644
--- a/Thirdweb.Console/Thirdweb.Console.csproj
+++ b/Thirdweb.Console/Thirdweb.Console.csproj
@@ -1,26 +1,22 @@
-
-
Exe
net8.0
+ latest
+ true
enable
enable
false
-
-
-
+
-
PreserveNewest
-
-
\ No newline at end of file
+
diff --git a/Thirdweb.Generator/Program.cs b/Thirdweb.Generator/Program.cs
new file mode 100644
index 00000000..41e29e0c
--- /dev/null
+++ b/Thirdweb.Generator/Program.cs
@@ -0,0 +1,458 @@
+using System.Globalization;
+using System.Text;
+using System.Xml.Linq;
+using NJsonSchema;
+using NSwag;
+using NSwag.CodeGeneration.CSharp;
+
+static string FindRepoRoot()
+{
+ var dir = new DirectoryInfo(Directory.GetCurrentDirectory());
+ while (dir != null)
+ {
+ if (File.Exists(Path.Combine(dir.FullName, "thirdweb.sln")))
+ {
+ return dir.FullName;
+ }
+ dir = dir.Parent;
+ }
+ // Fallback to current directory
+ return Directory.GetCurrentDirectory();
+}
+
+static void GenerateLlmsTxt(string repoRoot)
+{
+ var xmlPath = Path.Combine(repoRoot, "Thirdweb", "bin", "Release", "netstandard2.1", "Thirdweb.xml");
+ var outputPath = Path.Combine(repoRoot, "llms.txt");
+
+ if (!File.Exists(xmlPath))
+ {
+ Console.WriteLine($"XML documentation not found at {xmlPath}");
+ Console.WriteLine("Please build the project in Release mode first.");
+ Environment.Exit(1);
+ }
+
+ Console.WriteLine($"Reading XML documentation from {xmlPath}...");
+ var doc = XDocument.Load(xmlPath);
+ var sb = new StringBuilder();
+
+ _ = sb.AppendLine("THIRDWEB .NET SDK - API DOCUMENTATION");
+ _ = sb.AppendLine("=====================================");
+ _ = sb.AppendLine();
+
+ var assembly = doc.Root?.Element("assembly")?.Element("name")?.Value;
+ if (assembly != null)
+ {
+ _ = sb.AppendLine($"Assembly: {assembly}");
+ _ = sb.AppendLine();
+ }
+
+ var members = doc.Root?.Element("members")?.Elements("member");
+ if (members != null)
+ {
+ foreach (var member in members)
+ {
+ var name = member.Attribute("name")?.Value;
+ if (string.IsNullOrEmpty(name))
+ {
+ continue;
+ }
+
+ _ = sb.AppendLine(new string('-', 80));
+ _ = sb.AppendLine(name);
+ _ = sb.AppendLine(new string('-', 80));
+
+ // Parse signature for better display
+ var signature = ParseMemberSignature(name);
+ if (signature != null)
+ {
+ _ = sb.AppendLine();
+ _ = sb.AppendLine($"KIND: {signature.Kind}");
+ if (!string.IsNullOrEmpty(signature.ReturnType))
+ {
+ _ = sb.AppendLine($"RETURN TYPE: {signature.ReturnType}");
+ }
+ }
+
+ // Group param elements by name attribute
+ var paramDocs = member.Elements("param").Select(p => new { Name = p.Attribute("name")?.Value, Description = p.Value.Trim() }).Where(p => !string.IsNullOrEmpty(p.Name)).ToList();
+
+ // Display parameters with their names and types
+ if (signature?.Parameters != null && signature.Parameters.Count > 0)
+ {
+ _ = sb.AppendLine();
+ _ = sb.AppendLine("PARAMETERS:");
+ for (var i = 0; i < signature.Parameters.Count; i++)
+ {
+ var param = signature.Parameters[i];
+ // Try to get the actual parameter name from documentation
+ var paramDoc = i < paramDocs.Count ? paramDocs[i] : null;
+ var paramName = paramDoc?.Name ?? param.Name;
+
+ _ = sb.AppendLine($" - {paramName} ({param.Type})");
+ if (paramDoc != null && !string.IsNullOrEmpty(paramDoc.Description))
+ {
+ _ = sb.AppendLine($" {NormalizeXmlText(paramDoc.Description).Replace("\n", "\n ")}");
+ }
+ }
+ }
+
+ // Display other elements (summary, remarks, returns, exception, etc.)
+ foreach (var element in member.Elements())
+ {
+ if (element.Name.LocalName == "param")
+ {
+ continue; // Already handled above
+ }
+
+ var content = element.Value.Trim();
+ if (string.IsNullOrEmpty(content))
+ {
+ continue;
+ }
+
+ _ = sb.AppendLine();
+ var elementName = element.Name.LocalName.ToUpper();
+
+ // Add attribute info for exceptions and type params
+ var nameAttr = element.Attribute("name")?.Value;
+ var crefAttr = element.Attribute("cref")?.Value;
+ if (!string.IsNullOrEmpty(nameAttr))
+ {
+ elementName += $" ({nameAttr})";
+ }
+ else if (!string.IsNullOrEmpty(crefAttr))
+ {
+ elementName += $" ({crefAttr})";
+ }
+
+ _ = sb.AppendLine($"{elementName}:");
+ _ = sb.AppendLine(NormalizeXmlText(content));
+ }
+
+ _ = sb.AppendLine();
+ }
+ }
+
+ File.WriteAllText(outputPath, sb.ToString());
+ Console.WriteLine($"Generated llms.txt at {outputPath}");
+}
+
+static MemberSignature? ParseMemberSignature(string memberName)
+{
+ if (string.IsNullOrEmpty(memberName) || memberName.Length < 2)
+ {
+ return null;
+ }
+
+ var kind = memberName[0] switch
+ {
+ 'M' => "Method",
+ 'P' => "Property",
+ 'T' => "Type",
+ 'F' => "Field",
+ 'E' => "Event",
+ _ => "Unknown",
+ };
+
+ var fullSignature = memberName[2..]; // Remove "M:", "P:", etc.
+
+ // For methods, parse parameters and return type
+ if (memberName[0] == 'M')
+ {
+ var parameters = new List();
+ var methodName = fullSignature;
+ var returnType = "System.Threading.Tasks.Task"; // Default for async methods
+
+ // Extract parameters from signature
+ var paramStart = fullSignature.IndexOf('(');
+ if (paramStart >= 0)
+ {
+ methodName = fullSignature[..paramStart];
+ var paramEnd = fullSignature.LastIndexOf(')');
+ if (paramEnd > paramStart)
+ {
+ var paramString = fullSignature[(paramStart + 1)..paramEnd];
+ if (!string.IsNullOrEmpty(paramString))
+ {
+ var paramParts = SplitParameters(paramString);
+ for (var i = 0; i < paramParts.Count; i++)
+ {
+ var paramType = SimplifyTypeName(paramParts[i]);
+ parameters.Add(new ParameterInfo { Name = $"param{i + 1}", Type = paramType });
+ }
+ }
+ }
+
+ // Check if there's a return type after the parameters
+ if (paramEnd + 1 < fullSignature.Length && fullSignature[paramEnd + 1] == '~')
+ {
+ returnType = SimplifyTypeName(fullSignature[(paramEnd + 2)..]);
+ }
+ }
+
+ return new MemberSignature
+ {
+ Kind = kind,
+ Parameters = parameters,
+ ReturnType = parameters.Count > 0 || fullSignature.Contains("Async") ? returnType : null,
+ };
+ }
+
+ return new MemberSignature { Kind = kind };
+}
+
+static List SplitParameters(string paramString)
+{
+ var parameters = new List();
+ var current = new StringBuilder();
+ var depth = 0;
+
+ foreach (var c in paramString)
+ {
+ if (c is '{' or '<')
+ {
+ depth++;
+ }
+ else if (c is '}' or '>')
+ {
+ depth--;
+ }
+ else if (c == ',' && depth == 0)
+ {
+ parameters.Add(current.ToString());
+ _ = current.Clear();
+ continue;
+ }
+
+ _ = current.Append(c);
+ }
+
+ if (current.Length > 0)
+ {
+ parameters.Add(current.ToString());
+ }
+
+ return parameters;
+}
+
+static string SimplifyTypeName(string fullTypeName)
+{
+ // Remove assembly information
+ var typeName = fullTypeName.Split(',')[0];
+
+ // Simplify common generic types
+ typeName = typeName
+ .Replace("System.Threading.Tasks.Task{", "Task<")
+ .Replace("System.Collections.Generic.List{", "List<")
+ .Replace("System.Collections.Generic.Dictionary{", "Dictionary<")
+ .Replace("System.Nullable{", "Nullable<")
+ .Replace("System.String", "string")
+ .Replace("System.Int32", "int")
+ .Replace("System.Int64", "long")
+ .Replace("System.Boolean", "bool")
+ .Replace("System.Byte", "byte")
+ .Replace("System.Object", "object")
+ .Replace('{', '<')
+ .Replace('}', '>');
+
+ return typeName;
+}
+
+static string NormalizeXmlText(string text)
+{
+ var lines = text.Split('\n');
+ var normalized = new StringBuilder();
+
+ foreach (var line in lines)
+ {
+ var trimmed = line.Trim();
+ if (!string.IsNullOrEmpty(trimmed))
+ {
+ _ = normalized.AppendLine(trimmed);
+ }
+ }
+
+ return normalized.ToString().TrimEnd();
+}
+
+var cmdArgs = Environment.GetCommandLineArgs();
+if (cmdArgs.Length > 1 && cmdArgs[1] == "--llms")
+{
+ var root = FindRepoRoot();
+ GenerateLlmsTxt(root);
+ return;
+}
+
+var specUrl = "https://api.thirdweb.com/openapi.json";
+var repoRoot = FindRepoRoot();
+var outputPath = Path.Combine(repoRoot, "Thirdweb", "Thirdweb.Api", "ThirdwebApi.cs");
+
+Console.WriteLine($"Loading OpenAPI from {specUrl}...");
+var document = await OpenApiDocument.FromUrlAsync(specUrl);
+
+Console.WriteLine("Deduplicating enum values across all schemas (inline + components)...");
+
+var visited = new HashSet