Skip to content

ThirdwebBridge - Onramp Integration & Improved APIs #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 41 additions & 70 deletions Thirdweb.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
// originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
// destinationChainId: 324,
// destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
// buyAmountWei: BigInteger.Parse("0.1".ToWei())
// buyAmountWei: BigInteger.Parse("0.01".ToWei())
// );
// Console.WriteLine($"Buy quote: {JsonConvert.SerializeObject(buyQuote, Formatting.Indented)}");

Expand All @@ -64,19 +64,19 @@
// originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
// destinationChainId: 324,
// destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
// buyAmountWei: BigInteger.Parse("0.1".ToWei()),
// buyAmountWei: BigInteger.Parse("0.01".ToWei()),
// sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
// receiver: await myWallet.GetAddress()
// );
// Console.WriteLine($"Prepared Buy contains {preparedBuy.Transactions.Count} transaction(s)!");
// Console.WriteLine($"Prepared Buy contains {preparedBuy.Steps.Count} steps(s) with a total of {preparedBuy.Steps.Sum(step => step.Transactions.Count)} transactions!");

// // 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())
// sellAmountWei: BigInteger.Parse("0.01".ToWei())
// );
// Console.WriteLine($"Sell quote: {JsonConvert.SerializeObject(sellQuote, Formatting.Indented)}");

Expand All @@ -86,17 +86,17 @@
// originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
// destinationChainId: 1,
// destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
// sellAmountWei: BigInteger.Parse("0.1".ToWei()),
// sellAmountWei: BigInteger.Parse("0.01".ToWei()),
// sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
// receiver: await myWallet.GetAddress()
// );
// Console.WriteLine($"Prepared Sell contains {preparedSell.Transactions.Count} transaction(s)!");
// Console.WriteLine($"Prepared Sell contains {preparedBuy.Steps.Count} steps(s) with a total of {preparedBuy.Steps.Sum(step => step.Transactions.Count)} transactions!");

// // Transfer - Get an executable transaction for transferring a specific amount of tokens
// var preparedTransfer = await bridge.Transfer_Prepare(
// chainId: 137,
// tokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
// transferAmountWei: BigInteger.Parse("0.1".ToWei()),
// tokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // POL on Polygon
// transferAmountWei: BigInteger.Parse("0.01".ToWei()),
// sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
// receiver: await myWallet.GetAddress()
// );
Expand Down Expand Up @@ -128,6 +128,39 @@
// var transferHashes = transferResult.Select(receipt => receipt.TransactionHash).ToList();
// Console.WriteLine($"Transfer hashes: {JsonConvert.SerializeObject(transferHashes, Formatting.Indented)}");

// // 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!");
// }

#endregion

#region Indexer
Expand Down Expand Up @@ -728,68 +761,6 @@

#endregion

#region Buy with Fiat

// // Supported currencies
// var supportedCurrencies = await ThirdwebPay.GetBuyWithFiatCurrencies(client);
// Console.WriteLine($"Supported currencies: {JsonConvert.SerializeObject(supportedCurrencies, Formatting.Indented)}");

// // Get a Buy with Fiat quote
// var fiatQuoteParamsWithProvider = new BuyWithFiatQuoteParams(fromCurrencySymbol: "USD", toAddress: walletAddress, toChainId: "137", toTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, toAmount: "20", preferredProvider: "STRIPE");
// var fiatQuoteParams = new BuyWithFiatQuoteParams(fromCurrencySymbol: "USD", toAddress: walletAddress, toChainId: "137", toTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, toAmount: "20");
// var fiatOnrampQuote = await ThirdwebPay.GetBuyWithFiatQuote(client, fiatQuoteParams);
// Console.WriteLine($"Fiat onramp quote: {JsonConvert.SerializeObject(fiatOnrampQuote, Formatting.Indented)}");

// // Get a Buy with Fiat link
// var onRampLink = ThirdwebPay.BuyWithFiat(fiatOnrampQuote);
// Console.WriteLine($"Fiat onramp link: {onRampLink}");

// // Open onramp link to start the process (use your framework's version of this)
// var psi = new ProcessStartInfo { FileName = onRampLink, UseShellExecute = true };
// _ = Process.Start(psi);

// // Poll for status
// var currentOnRampStatus = OnRampStatus.NONE;
// while (currentOnRampStatus is not OnRampStatus.ON_RAMP_TRANSFER_COMPLETED and not OnRampStatus.ON_RAMP_TRANSFER_FAILED)
// {
// var onRampStatus = await ThirdwebPay.GetBuyWithFiatStatus(client, fiatOnrampQuote.IntentId);
// currentOnRampStatus = Enum.Parse<OnRampStatus>(onRampStatus.Status);
// Console.WriteLine($"Fiat onramp status: {JsonConvert.SerializeObject(onRampStatus, Formatting.Indented)}");
// await Task.Delay(5000);
// }

#endregion

#region Buy with Crypto

// // Swap Polygon MATIC to Base ETH
// var swapQuoteParams = new BuyWithCryptoQuoteParams(
// fromAddress: walletAddress,
// fromChainId: 137,
// fromTokenAddress: Constants.NATIVE_TOKEN_ADDRESS,
// toTokenAddress: Constants.NATIVE_TOKEN_ADDRESS,
// toChainId: 8453,
// toAmount: "0.1"
// );
// var swapQuote = await ThirdwebPay.GetBuyWithCryptoQuote(client, swapQuoteParams);
// Console.WriteLine($"Swap quote: {JsonConvert.SerializeObject(swapQuote, Formatting.Indented)}");

// // Initiate swap
// var txHash3 = await ThirdwebPay.BuyWithCrypto(wallet: privateKeyWallet, buyWithCryptoQuote: swapQuote);
// Console.WriteLine($"Swap transaction hash: {txHash3}");

// // Poll for status
// var currentSwapStatus = SwapStatus.NONE;
// while (currentSwapStatus is not SwapStatus.COMPLETED and not SwapStatus.FAILED)
// {
// var swapStatus = await ThirdwebPay.GetBuyWithCryptoStatus(client, txHash3);
// currentSwapStatus = Enum.Parse<SwapStatus>(swapStatus.Status);
// Console.WriteLine($"Swap status: {JsonConvert.SerializeObject(swapStatus, Formatting.Indented)}");
// await Task.Delay(5000);
// }

#endregion

#region Storage Actions

// // Will download from IPFS or normal urls
Expand Down
58 changes: 44 additions & 14 deletions Thirdweb/Thirdweb.Bridge/ThirdwebBridge.Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static class ThirdwebBridgeExtensions
/// <returns>The transaction receipts as a list of <see cref="ThirdwebTransactionReceipt"/>.</returns>
public static async Task<List<ThirdwebTransactionReceipt>> Execute(this ThirdwebBridge bridge, IThirdwebWallet executor, BuyPrepareData preparedBuy, CancellationToken cancellationToken = default)
{
return await ExecuteInternal(bridge, executor, preparedBuy.Transactions, cancellationToken);
return await ExecuteInternal(bridge, executor, preparedBuy.Steps, cancellationToken);
}

/// <summary>
Expand All @@ -34,7 +34,7 @@ public static async Task<List<ThirdwebTransactionReceipt>> Execute(
CancellationToken cancellationToken = default
)
{
return await ExecuteInternal(bridge, executor, preparedSell.Transactions, cancellationToken);
return await ExecuteInternal(bridge, executor, preparedSell.Steps, cancellationToken);
}

/// <summary>
Expand All @@ -52,23 +52,48 @@ public static Task<List<ThirdwebTransactionReceipt>> Execute(
CancellationToken cancellationToken = default
)
{
return ExecuteInternal(bridge, executor, preparedTransfer.Transactions, cancellationToken);
var steps = new List<Step>() { new() { Transactions = preparedTransfer.Transactions } };
return ExecuteInternal(bridge, executor, steps, cancellationToken);
}

private static async Task<List<ThirdwebTransactionReceipt>> ExecuteInternal(
this ThirdwebBridge bridge,
IThirdwebWallet executor,
List<Transaction> transactions,
CancellationToken cancellationToken = default
)
/// <summary>
/// Executes a set of post-onramp transactions and handles status polling.
/// </summary>
/// <param name="bridge">The Thirdweb bridge.</param>
/// <param name="executor">The executor wallet.</param>
/// <param name="preparedOnRamp">The prepared onramp data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The transaction receipts as a list of <see cref="ThirdwebTransactionReceipt"/>.</returns>
/// <remarks>Note: This method is used for executing transactions after an onramp process.</remarks>
public static Task<List<ThirdwebTransactionReceipt>> Execute(this ThirdwebBridge bridge, IThirdwebWallet executor, OnrampPrepareData preparedOnRamp, CancellationToken cancellationToken = default)
{
return ExecuteInternal(bridge, executor, preparedOnRamp.Steps, cancellationToken);
}

/// <summary>
/// Executes a set of transactions and handles status polling.
/// </summary>
/// /// <param name="bridge">The Thirdweb bridge.</param>
/// <param name="executor">The executor wallet.</param>
/// <param name="steps">The steps containing transactions to execute.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public static Task<List<ThirdwebTransactionReceipt>> Execute(this ThirdwebBridge bridge, IThirdwebWallet executor, List<Step> steps, CancellationToken cancellationToken = default)
{
return ExecuteInternal(bridge, executor, steps, cancellationToken);
}

private static async Task<List<ThirdwebTransactionReceipt>> ExecuteInternal(this ThirdwebBridge bridge, IThirdwebWallet executor, List<Step> steps, CancellationToken cancellationToken = default)
{
var receipts = new List<ThirdwebTransactionReceipt>();
foreach (var tx in transactions)
foreach (var step in steps)
{
var thirdwebTx = await tx.ToThirdwebTransaction(executor);
var hash = await ThirdwebTransaction.Send(thirdwebTx);
receipts.Add(await ThirdwebTransaction.WaitForTransactionReceipt(executor.Client, tx.ChainId, hash, cancellationToken));
_ = await bridge.WaitForStatusCompletion(hash, tx.ChainId, cancellationToken);
foreach (var tx in step.Transactions)
{
var thirdwebTx = await tx.ToThirdwebTransaction(executor);
var hash = await ThirdwebTransaction.Send(thirdwebTx);
receipts.Add(await ThirdwebTransaction.WaitForTransactionReceipt(executor.Client, tx.ChainId, hash, cancellationToken));
_ = await bridge.WaitForStatusCompletion(hash, tx.ChainId, cancellationToken);
}
}
return receipts;
}
Expand Down Expand Up @@ -117,5 +142,10 @@ public static async Task<StatusData> WaitForStatusCompletion(this ThirdwebBridge
return status;
}

public static bool IsSwapRequiredPostOnramp(this OnrampPrepareData preparedOnramp)
{
return preparedOnramp.Steps == null || preparedOnramp.Steps.Count == 0 || !preparedOnramp.Steps.Any(step => step.Transactions?.Count > 0);
}

#endregion
}
Loading