Releases: thirdweb-dev/dotnet
v2.23.1
What's Changed
Building on top of the new EIP-7702 In-App or Ecosystem Wallet CreateSessionKey functionality, new utilities were added:
v2.23.0
Session Key Support for EIP-7702 Smart EOAs
Building on the EIP-7702 Account Abstraction and transaction sponsorship introduced in v2.21.0, this release extends Session Key functionality to EIP-7702 Smart EOAs. Session Keys enable granular permission delegation to external wallets for executing transactions on your behalf.
Key Features
- Permission Delegation: Grant time-limited access to external wallets
- Granular Controls: Define precise execution permissions per session
- Smart EOA Integration: Now available for EIP-7702-powered In-App Wallets, inspired by and improved from Smart Wallet session keys
- Backend Flexibility: Delegate transaction execution to external systems while maintaining control
Usage Example
Create session keys with either full permissions or custom policies:
// Grant full permissions for 24 hours
var sessionKeyReceipt = await smartEoa.CreateSessionKey(
chainId: chain,
signerAddress: await Utils.GetAddressFromENS(client, "vitalik.eth"),
durationInSeconds: 86400,
grantFullPermissions: true
);
// Grant custom permissions
var sessionKeyReceipt = await smartEoa.CreateSessionKey(
chainId: chain,
signerAddress: await Utils.GetAddressFromENS(client, "vitalik.eth"),
durationInSeconds: 86400,
grantFullPermissions: false,
callPolicies: new List<CallSpec>(), // Contract interaction rules
transferPolicies: new List<TransferSpec>() // Value transfer rules
);
Implementation Workflow
const int chain = 11155111; // 7702-compatible chain
// Initialize EIP-7702 wallet
var smartEoa = await InAppWallet.Create(
client,
authProvider: AuthProvider.Guest,
executionMode: ExecutionMode.EIP7702Sponsored
);
// Authenticate and upgrade EOA
if (!await smartEoa.IsConnected())
{
await smartEoa.LoginWithGuest(defaultSessionIdOverride: Guid.NewGuid().ToString());
}
// Execute upgrade transaction (optional)
var receipt = await smartEoa.Transfer(
chainId: chain,
toAddress: await Utils.GetAddressFromENS(client, "vitalik.eth"),
weiAmount: 0
);
// Verify account delegation (optional)
var isDelegated = await Utils.IsDelegatedAccount(client, chain, await smartEoa.GetAddress());
// Create session key
var sessionKeyReceipt = await smartEoa.CreateSessionKey(
chainId: chain,
signerAddress: await Utils.GetAddressFromENS(client, "vitalik.eth"),
durationInSeconds: 86400,
grantFullPermissions: true
);
Practical Applications
Once created, these session keys can be used to:
- Delegate transactions to backend services while maintaining security boundaries
- Enable temporary access for third-party services
- Create specialized roles with limited permissions
- Automate workflows without exposing primary wallet credentials
The session key mechanism opens up numerous possibilities for secure, permissioned delegation patterns while maintaining full control over your assets and contracts.
v2.22.0
ThirdwebBridge - Onramp Integration & Improved APIs
This release brings you new Onramp APIs, as well as new QOL parameters for the general ThirdwebBridge
APIs, in line with the latest changes made to the service.
Do note that some responses now have obsolete propeties. Generally the main change is that instead of returning a list of transactions, Prepare endpoints will return a set of Steps. You can still use our extensions to Execute any prepared quote you receive directly, we'll handle the steps for you as usual.
Basic example of using the new Onramp APIs in .NET
The onramp flow will return a link for you to display/open as you please. You may poll the status of that onramp by its ID.
In some cases, you may receive an additional set of onchain transactions required to get to your destination token post on-ramp, in such cases, you may use our extension IsSwapRequiredPostOnramp
to check, and if a swap is indeed required, you may use our Execute
extensions to execute the transactions, or manually execute them by going through each Step
.
// Onramp - Get a quote for buying crypto with Fiat
var preparedOnramp = await bridge.Onramp_Prepare(
onramp: OnrampProvider.Coinbase,
chainId: 8453,
tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
amount: "10000000",
receiver: await myWallet.GetAddress()
);
Console.WriteLine($"Onramp link: {preparedOnramp.Link}");
Console.WriteLine($"Full onramp quote and steps data: {JsonConvert.SerializeObject(preparedOnramp, Formatting.Indented)}");
while (true)
{
var onrampStatus = await bridge.Onramp_Status(id: preparedOnramp.Id);
Console.WriteLine($"Full Onramp Status: {JsonConvert.SerializeObject(onrampStatus, Formatting.Indented)}");
if (onrampStatus.StatusType is StatusType.COMPLETED or StatusType.FAILED)
{
break;
}
await ThirdwebTask.Delay(5000);
}
if (preparedOnramp.IsSwapRequiredPostOnramp())
{
// Execute additional steps that are required post-onramp to get to your token, manually or via the Execute extension
var receipts = await bridge.Execute(myWallet, preparedOnramp);
Console.WriteLine($"Onramp receipts: {JsonConvert.SerializeObject(receipts, Formatting.Indented)}");
}
else
{
Console.WriteLine("No additional steps required post-onramp, you can use the tokens directly!");
}
v2.21.0
[Beta] Your In-App Wallets can now be upgraded directly to an EIP7702 Smart Account with a simple creation flag!
With the recent Ethereum upgrade Pectra, EIP-7702 allows you - on chains that support it - to upgrade your EOA and get SmartWallet-like functionality with:
- Much cheaper gas costs, batching functionality
- No account separation - your wallet address does not change, not even on zksync chains (once they implement EIP-7702)
- Much faster execution, with the option of paying for gas yourself or having thirdweb manage gas sponsorship, similar to SmartWallet.
And here's how simple it is!
ExecutionMode.EIP7702Sponsored**
Upgrade to an EIP7702 smart account, unlocking all functionality of 4337 without the downsides, and thirdweb handles the execution and gas sponsorship for you!
var smartEoa = await InAppWallet.Create(
client: thirdwebClient,
authProvider: AuthProvider.Google,
executionMode: ExecutionMode.EIP7702Sponsored
);
ExecutionMode.EIP7702
Upgrade to an EIP7702 smart account, unlocking all functionality of 4337 without the downsides, but sponsoring gas yourself.
var smartEoa = await InAppWallet.Create(
client: thirdwebClient,
authProvider: AuthProvider.Google,
executionMode: ExecutionMode.EIP7702
);
ExecutionMode.EOA
Normal" EOA Execution, no smart account functionality
var basicEoa = await InAppWallet.Create(
client: thirdwebClient,
authProvider: AuthProvider.Google,
// does not need to be explicitly passed, is the default but we're showing it here
executionMode: ExecutionMode.EOA
);
When using EIP-7702 execution modes, upon your first transaction - if not already delegated to a smart account - an EIP-7702 authorization will be signed and bundled with your first transaction, similar to how 4337 works with initcode, but without the large gas costs, slower execution and chain specific requirements.
We hope you enjoy not having to pass your signers to yet another SmartWallet class, dealing with entrypoint versions, bundlers, paymasters, slow execution and higher gas fees.
This Smart EOA contract also comes with granular session keys that we'll showcase in a later release!
More chains will have support for EIP-7702 very soon with various chain stacks upgrading to Pectra, and we will support each and every one of them.
Additional updates
InAppWallet
andEcosystemWallet
'sSignAuthorization
low level methods have been integrated.- Fixed an issue where using
SmartWallet
on Hedera or Hedera Testnet where HBAR (native token) is involved could cause silent revets.
v2.20.1
v2.20.0
What's Changed
- Nebula model
t0-003
Integration- New
NebulaContext
constructor, only a list of chain ids are now needed, and an optional wallet address can be passed. - Results are a lot more predictable now, model t0-003 is now the default model when creating a
ThirdwebNebula
instance. - Execution methods returns receipts more consistently.
- Session is now properly passed to every call, improving contextual responses.
- New
- Fixed an bug where using the extension
DropERC20_Claim
would fail if the claim condition was set to be in native tokens.
v2.19.1
What's Changed
- Fixed an encoding issue when using Account Abstraction v0.7 (Smart Wallet with Entrypoint v0.7) without a paymaster (gasless: false).
- Fixed an issue where
ThirdwebInsight
'sToNFT
andToNFTList
extensions would throw "Unknown NFT type" due to new response model.
v2.19.0
[Beta] Universal Bridge .NET Integration - Successor to Thirdweb Pay
We've built Universal Bridge to allow your users to use any asset on any chain, and it's ready for you to try.
This integration simplifies onchain asset trading, and we've added extensions in .NET to integrate with any IThirdwebWallet
nicely.
Core APIs
The design is akin to letting us know what your intent is.
- Buy: "I want to buy x USDC on y Chain using z Token"
- Sell: "I want to sell x USDC on y Chain for z Token"
- Transfer: "Just transfer all my money to vitalik"
We will return the transactions needed to achieve whatever you desire.
You may then handle execution yourself or use our extensions.
Instantiation
using Thirdweb.Bridge;
// Create a ThirdwebBridge instance
var bridge = await ThirdwebBridge.Create(client);
Buy - Get a quote for buying a specific amount of tokens
var buyQuote = await bridge.Buy_Quote(
originChainId: 1,
originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
destinationChainId: 324,
destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
buyAmountWei: BigInteger.Parse("0.1".ToWei())
);
Console.WriteLine($"Buy quote: {JsonConvert.SerializeObject(buyQuote, Formatting.Indented)}");
Buy - Get an executable set of transactions (alongside a quote) for buying a specific amount of tokens
var preparedBuy = await bridge.Buy_Prepare(
originChainId: 1,
originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
destinationChainId: 324,
destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
buyAmountWei: BigInteger.Parse("0.1".ToWei()),
sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
receiver: await myWallet.GetAddress()
);
Console.WriteLine($"Prepared Buy contains {preparedBuy.Transactions.Count} transaction(s)!");
Sell - Get a quote for selling a specific amount of tokens
var sellQuote = await bridge.Sell_Quote(
originChainId: 324,
originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
destinationChainId: 1,
destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
sellAmountWei: BigInteger.Parse("0.1".ToWei())
);
Console.WriteLine($"Sell quote: {JsonConvert.SerializeObject(sellQuote, Formatting.Indented)}");
Sell - Get an executable set of transactions (alongside a quote) for selling a specific amount of tokens
var preparedSell = await bridge.Sell_Prepare(
originChainId: 324,
originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
destinationChainId: 1,
destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
sellAmountWei: BigInteger.Parse("0.1".ToWei()),
sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
receiver: await myWallet.GetAddress()
);
Console.WriteLine($"Prepared Sell contains {preparedSell.Transactions.Count} transaction(s)!");
Transfer - Get an executable transaction for transferring a specific amount of tokens
Why not just transfer with the SDK? Stay tuned for webhooks, think direct payments!
var preparedTransfer = await bridge.Transfer_Prepare(
chainId: 137,
tokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
transferAmountWei: BigInteger.Parse("0.1".ToWei()),
sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
receiver: await myWallet.GetAddress()
);
Console.WriteLine($"Prepared Transfer: {JsonConvert.SerializeObject(preparedTransfer, Formatting.Indented)}");
Manual Execution
This is not production code, we're just showcasing some of the APIs that would help you execute and poll status here.
// You may use our extensions to execute yourself...
var myTx = await preparedTransfer.Transactions[0].ToThirdwebTransaction(myWallet);
var myHash = await ThirdwebTransaction.Send(myTx);
// ...and poll for the status...
var status = await bridge.Status(transactionHash: myHash, chainId: 1);
var isComplete = status.StatusType == StatusType.COMPLETED;
Console.WriteLine($"Status: {JsonConvert.SerializeObject(status, Formatting.Indented)}");
// Or use our Execute extensions directly to handle everything for you!
Managed Execution
The SDK comes with some extensions that you'll see on a lot of ThirdwebBridge
objects, and the main one is Execute.
// Execute a prepared Buy
var buyResult = await bridge.Execute(myWallet, preparedBuy);
var buyHashes = buyResult.Select(receipt => receipt.TransactionHash).ToList();
Console.WriteLine($"Buy hashes: {JsonConvert.SerializeObject(buyHashes, Formatting.Indented)}");
// Execute a prepared Sell
var sellResult = await bridge.Execute(myWallet, preparedSell);
var sellHashes = sellResult.Select(receipt => receipt.TransactionHash).ToList();
Console.WriteLine($"Sell hashes: {JsonConvert.SerializeObject(sellHashes, Formatting.Indented)}");
// Execute a prepared Transfer
var transferResult = await bridge.Execute(myWallet, preparedTransfer);
var transferHashes = transferResult.Select(receipt => receipt.TransactionHash).ToList();
Console.WriteLine($"Transfer hashes: {JsonConvert.SerializeObject(transferHashes, Formatting.Indented)}");
v2.18.6
What's Changed
EngineWallet
connections will now show up in your dashboard analytics, alongside other wallets.- Fixes issues fetching
ERC721A
NFTs (without theERC721AQueryable
extension) where burned token ids would throw an error due to aOwnerQueryForNonexistentToken
revert. GetEcosystemDetails
now throws early if called on anInAppWallet
rather than fetching and failing, slightly improvingSmartWallet
creation speed when usingInAppWallet
.
v2.18.5
What's Changed
ThirdwebInsight
can now return additional Token Prices using GetTokenPrice or GetTokenPrices.
// Instantiate Insight
var insight = await ThirdwebInsight.Create(client);
// Fetch current token price
var ethPriceToday = await insight.GetTokenPrice(addressOrSymbol: "ETH", chainId: 1);
Console.WriteLine($"ETH price today: {ethPriceToday.PriceUsd}");
// Fetch token price at specified timestamp
var ethPriceYesterday = await insight.GetTokenPrice(
addressOrSymbol: "ETH",
chainId: 1,
timestamp: Utils.GetUnixTimeStampNow() - 86400
);
Console.WriteLine($"ETH price yesterday: {ethPriceYesterday.PriceUsd}");
// Fetch multiple token prices across multiple chains
var multiTokenPrices = await insight.GetTokenPrices(
addressOrSymbols: new string[] { "POL", "APE" },
chainIds: new BigInteger[] { 137, 33139 }
);
Console.WriteLine($"Multi token prices: {JsonConvert.SerializeObject(multiTokenPrices, Formatting.Indented)}");