Skip to content

Commit 85aeb65

Browse files
committed
ThirdwebBridge - Onramp Integration & Improved APIs
1 parent ea4d093 commit 85aeb65

File tree

4 files changed

+576
-150
lines changed

4 files changed

+576
-150
lines changed

Thirdweb.Console/Program.cs

Lines changed: 117 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -45,88 +45,123 @@
4545

4646
#region Bridge
4747

48-
// // Create a ThirdwebBridge instance
49-
// var bridge = await ThirdwebBridge.Create(client);
50-
51-
// // Buy - Get a quote for buying a specific amount of tokens
52-
// var buyQuote = await bridge.Buy_Quote(
53-
// originChainId: 1,
54-
// originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
55-
// destinationChainId: 324,
56-
// destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
57-
// buyAmountWei: BigInteger.Parse("0.1".ToWei())
58-
// );
59-
// Console.WriteLine($"Buy quote: {JsonConvert.SerializeObject(buyQuote, Formatting.Indented)}");
60-
61-
// // Buy - Get an executable set of transactions (alongside a quote) for buying a specific amount of tokens
62-
// var preparedBuy = await bridge.Buy_Prepare(
63-
// originChainId: 1,
64-
// originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
65-
// destinationChainId: 324,
66-
// destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
67-
// buyAmountWei: BigInteger.Parse("0.1".ToWei()),
68-
// sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
69-
// receiver: await myWallet.GetAddress()
70-
// );
71-
// Console.WriteLine($"Prepared Buy contains {preparedBuy.Transactions.Count} transaction(s)!");
72-
73-
// // Sell - Get a quote for selling a specific amount of tokens
74-
// var sellQuote = await bridge.Sell_Quote(
75-
// originChainId: 324,
76-
// originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
77-
// destinationChainId: 1,
78-
// destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
79-
// sellAmountWei: BigInteger.Parse("0.1".ToWei())
80-
// );
81-
// Console.WriteLine($"Sell quote: {JsonConvert.SerializeObject(sellQuote, Formatting.Indented)}");
82-
83-
// // Sell - Get an executable set of transactions (alongside a quote) for selling a specific amount of tokens
84-
// var preparedSell = await bridge.Sell_Prepare(
85-
// originChainId: 324,
86-
// originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
87-
// destinationChainId: 1,
88-
// destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
89-
// sellAmountWei: BigInteger.Parse("0.1".ToWei()),
90-
// sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
91-
// receiver: await myWallet.GetAddress()
92-
// );
93-
// Console.WriteLine($"Prepared Sell contains {preparedSell.Transactions.Count} transaction(s)!");
94-
95-
// // Transfer - Get an executable transaction for transferring a specific amount of tokens
96-
// var preparedTransfer = await bridge.Transfer_Prepare(
97-
// chainId: 137,
98-
// tokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
99-
// transferAmountWei: BigInteger.Parse("0.1".ToWei()),
100-
// sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
101-
// receiver: await myWallet.GetAddress()
102-
// );
103-
// Console.WriteLine($"Prepared Transfer: {JsonConvert.SerializeObject(preparedTransfer, Formatting.Indented)}");
104-
105-
// // You may use our extensions to execute yourself...
106-
// var myTx = await preparedTransfer.Transactions[0].ToThirdwebTransaction(myWallet);
107-
// var myHash = await ThirdwebTransaction.Send(myTx);
108-
109-
// // ...and poll for the status...
110-
// var status = await bridge.Status(transactionHash: myHash, chainId: 1);
111-
// var isComplete = status.StatusType == StatusType.COMPLETED;
112-
// Console.WriteLine($"Status: {JsonConvert.SerializeObject(status, Formatting.Indented)}");
113-
114-
// // Or use our Execute extensions directly to handle everything for you!
115-
116-
// // Execute a prepared Buy
117-
// var buyResult = await bridge.Execute(myWallet, preparedBuy);
118-
// var buyHashes = buyResult.Select(receipt => receipt.TransactionHash).ToList();
119-
// Console.WriteLine($"Buy hashes: {JsonConvert.SerializeObject(buyHashes, Formatting.Indented)}");
120-
121-
// // Execute a prepared Sell
122-
// var sellResult = await bridge.Execute(myWallet, preparedSell);
123-
// var sellHashes = sellResult.Select(receipt => receipt.TransactionHash).ToList();
124-
// Console.WriteLine($"Sell hashes: {JsonConvert.SerializeObject(sellHashes, Formatting.Indented)}");
125-
126-
// // Execute a prepared Transfer
127-
// var transferResult = await bridge.Execute(myWallet, preparedTransfer);
128-
// var transferHashes = transferResult.Select(receipt => receipt.TransactionHash).ToList();
129-
// Console.WriteLine($"Transfer hashes: {JsonConvert.SerializeObject(transferHashes, Formatting.Indented)}");
48+
var myWallet = await PrivateKeyWallet.Generate(client);
49+
50+
// Create a ThirdwebBridge instance
51+
var bridge = await ThirdwebBridge.Create(client);
52+
53+
// Buy - Get a quote for buying a specific amount of tokens
54+
var buyQuote = await bridge.Buy_Quote(
55+
originChainId: 1,
56+
originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
57+
destinationChainId: 324,
58+
destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
59+
buyAmountWei: BigInteger.Parse("0.01".ToWei())
60+
);
61+
Console.WriteLine($"Buy quote: {JsonConvert.SerializeObject(buyQuote, Formatting.Indented)}");
62+
63+
// Buy - Get an executable set of transactions (alongside a quote) for buying a specific amount of tokens
64+
var preparedBuy = await bridge.Buy_Prepare(
65+
originChainId: 1,
66+
originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
67+
destinationChainId: 324,
68+
destinationTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
69+
buyAmountWei: BigInteger.Parse("0.01".ToWei()),
70+
sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
71+
receiver: await myWallet.GetAddress()
72+
);
73+
Console.WriteLine($"Prepared Buy contains {preparedBuy.Steps.Count} steps(s) with a total of {preparedBuy.Steps.Sum(step => step.Transactions.Count)} transactions!");
74+
75+
// Sell - Get a quote for selling a specific amount of tokens
76+
var sellQuote = await bridge.Sell_Quote(
77+
originChainId: 324,
78+
originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
79+
destinationChainId: 1,
80+
destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
81+
sellAmountWei: BigInteger.Parse("0.01".ToWei())
82+
);
83+
Console.WriteLine($"Sell quote: {JsonConvert.SerializeObject(sellQuote, Formatting.Indented)}");
84+
85+
// Sell - Get an executable set of transactions (alongside a quote) for selling a specific amount of tokens
86+
var preparedSell = await bridge.Sell_Prepare(
87+
originChainId: 324,
88+
originTokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // ETH on zkSync
89+
destinationChainId: 1,
90+
destinationTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
91+
sellAmountWei: BigInteger.Parse("0.01".ToWei()),
92+
sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
93+
receiver: await myWallet.GetAddress()
94+
);
95+
Console.WriteLine($"Prepared Sell contains {preparedBuy.Steps.Count} steps(s) with a total of {preparedBuy.Steps.Sum(step => step.Transactions.Count)} transactions!");
96+
97+
// Transfer - Get an executable transaction for transferring a specific amount of tokens
98+
var preparedTransfer = await bridge.Transfer_Prepare(
99+
chainId: 137,
100+
tokenAddress: Constants.NATIVE_TOKEN_ADDRESS, // POL on Polygon
101+
transferAmountWei: BigInteger.Parse("0.01".ToWei()),
102+
sender: await Utils.GetAddressFromENS(client, "vitalik.eth"),
103+
receiver: await myWallet.GetAddress()
104+
);
105+
Console.WriteLine($"Prepared Transfer: {JsonConvert.SerializeObject(preparedTransfer, Formatting.Indented)}");
106+
107+
// You may use our extensions to execute yourself...
108+
var myTx = await preparedTransfer.Transactions[0].ToThirdwebTransaction(myWallet);
109+
var myHash = await ThirdwebTransaction.Send(myTx);
110+
111+
// ...and poll for the status...
112+
var status = await bridge.Status(transactionHash: myHash, chainId: 1);
113+
var isComplete = status.StatusType == StatusType.COMPLETED;
114+
Console.WriteLine($"Status: {JsonConvert.SerializeObject(status, Formatting.Indented)}");
115+
116+
// Or use our Execute extensions directly to handle everything for you!
117+
118+
// Execute a prepared Buy
119+
var buyResult = await bridge.Execute(myWallet, preparedBuy);
120+
var buyHashes = buyResult.Select(receipt => receipt.TransactionHash).ToList();
121+
Console.WriteLine($"Buy hashes: {JsonConvert.SerializeObject(buyHashes, Formatting.Indented)}");
122+
123+
// Execute a prepared Sell
124+
var sellResult = await bridge.Execute(myWallet, preparedSell);
125+
var sellHashes = sellResult.Select(receipt => receipt.TransactionHash).ToList();
126+
Console.WriteLine($"Sell hashes: {JsonConvert.SerializeObject(sellHashes, Formatting.Indented)}");
127+
128+
// Execute a prepared Transfer
129+
var transferResult = await bridge.Execute(myWallet, preparedTransfer);
130+
var transferHashes = transferResult.Select(receipt => receipt.TransactionHash).ToList();
131+
Console.WriteLine($"Transfer hashes: {JsonConvert.SerializeObject(transferHashes, Formatting.Indented)}");
132+
133+
// Onramp - Get a quote for buying crypto with Fiat
134+
var preparedOnramp = await bridge.Onramp_Prepare(
135+
onramp: OnrampProvider.Coinbase,
136+
chainId: 8453,
137+
tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
138+
amount: "10000000",
139+
receiver: await myWallet.GetAddress()
140+
);
141+
Console.WriteLine($"Onramp link: {preparedOnramp.Link}");
142+
Console.WriteLine($"Full onramp quote and steps data: {JsonConvert.SerializeObject(preparedOnramp, Formatting.Indented)}");
143+
144+
while (true)
145+
{
146+
var onrampStatus = await bridge.Onramp_Status(id: preparedOnramp.Id);
147+
Console.WriteLine($"Full Onramp Status: {JsonConvert.SerializeObject(onrampStatus, Formatting.Indented)}");
148+
if (onrampStatus.StatusType is StatusType.COMPLETED or StatusType.FAILED)
149+
{
150+
break;
151+
}
152+
await ThirdwebTask.Delay(5000);
153+
}
154+
155+
if (preparedOnramp.IsSwapRequiredPostOnramp())
156+
{
157+
// Execute additional steps that are required post-onramp to get to your token, manually or via the Execute extension
158+
var receipts = await bridge.Execute(myWallet, preparedOnramp);
159+
Console.WriteLine($"Onramp receipts: {JsonConvert.SerializeObject(receipts, Formatting.Indented)}");
160+
}
161+
else
162+
{
163+
Console.WriteLine("No additional steps required post-onramp, you can use the tokens directly!");
164+
}
130165

131166
#endregion
132167

Thirdweb/Thirdweb.Bridge/ThirdwebBridge.Extensions.cs

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static class ThirdwebBridgeExtensions
1616
/// <returns>The transaction receipts as a list of <see cref="ThirdwebTransactionReceipt"/>.</returns>
1717
public static async Task<List<ThirdwebTransactionReceipt>> Execute(this ThirdwebBridge bridge, IThirdwebWallet executor, BuyPrepareData preparedBuy, CancellationToken cancellationToken = default)
1818
{
19-
return await ExecuteInternal(bridge, executor, preparedBuy.Transactions, cancellationToken);
19+
return await ExecuteInternal(bridge, executor, preparedBuy.Steps, cancellationToken);
2020
}
2121

2222
/// <summary>
@@ -34,7 +34,7 @@ public static async Task<List<ThirdwebTransactionReceipt>> Execute(
3434
CancellationToken cancellationToken = default
3535
)
3636
{
37-
return await ExecuteInternal(bridge, executor, preparedSell.Transactions, cancellationToken);
37+
return await ExecuteInternal(bridge, executor, preparedSell.Steps, cancellationToken);
3838
}
3939

4040
/// <summary>
@@ -52,23 +52,48 @@ public static Task<List<ThirdwebTransactionReceipt>> Execute(
5252
CancellationToken cancellationToken = default
5353
)
5454
{
55-
return ExecuteInternal(bridge, executor, preparedTransfer.Transactions, cancellationToken);
55+
var steps = new List<Step>() { new() { Transactions = preparedTransfer.Transactions } };
56+
return ExecuteInternal(bridge, executor, steps, cancellationToken);
5657
}
5758

58-
private static async Task<List<ThirdwebTransactionReceipt>> ExecuteInternal(
59-
this ThirdwebBridge bridge,
60-
IThirdwebWallet executor,
61-
List<Transaction> transactions,
62-
CancellationToken cancellationToken = default
63-
)
59+
/// <summary>
60+
/// Executes a set of post-onramp transactions and handles status polling.
61+
/// </summary>
62+
/// <param name="bridge">The Thirdweb bridge.</param>
63+
/// <param name="executor">The executor wallet.</param>
64+
/// <param name="preparedOnRamp">The prepared onramp data.</param>
65+
/// <param name="cancellationToken">The cancellation token.</param>
66+
/// <returns>The transaction receipts as a list of <see cref="ThirdwebTransactionReceipt"/>.</returns>
67+
/// <remarks>Note: This method is used for executing transactions after an onramp process.</remarks>
68+
public static Task<List<ThirdwebTransactionReceipt>> Execute(this ThirdwebBridge bridge, IThirdwebWallet executor, OnrampPrepareData preparedOnRamp, CancellationToken cancellationToken = default)
69+
{
70+
return ExecuteInternal(bridge, executor, preparedOnRamp.Steps, cancellationToken);
71+
}
72+
73+
/// <summary>
74+
/// Executes a set of transactions and handles status polling.
75+
/// </summary>
76+
/// /// <param name="bridge">The Thirdweb bridge.</param>
77+
/// <param name="executor">The executor wallet.</param>
78+
/// <param name="steps">The steps containing transactions to execute.</param>
79+
/// <param name="cancellationToken">The cancellation token.</param>
80+
public static Task<List<ThirdwebTransactionReceipt>> Execute(this ThirdwebBridge bridge, IThirdwebWallet executor, List<Step> steps, CancellationToken cancellationToken = default)
81+
{
82+
return ExecuteInternal(bridge, executor, steps, cancellationToken);
83+
}
84+
85+
private static async Task<List<ThirdwebTransactionReceipt>> ExecuteInternal(this ThirdwebBridge bridge, IThirdwebWallet executor, List<Step> steps, CancellationToken cancellationToken = default)
6486
{
6587
var receipts = new List<ThirdwebTransactionReceipt>();
66-
foreach (var tx in transactions)
88+
foreach (var step in steps)
6789
{
68-
var thirdwebTx = await tx.ToThirdwebTransaction(executor);
69-
var hash = await ThirdwebTransaction.Send(thirdwebTx);
70-
receipts.Add(await ThirdwebTransaction.WaitForTransactionReceipt(executor.Client, tx.ChainId, hash, cancellationToken));
71-
_ = await bridge.WaitForStatusCompletion(hash, tx.ChainId, cancellationToken);
90+
foreach (var tx in step.Transactions)
91+
{
92+
var thirdwebTx = await tx.ToThirdwebTransaction(executor);
93+
var hash = await ThirdwebTransaction.Send(thirdwebTx);
94+
receipts.Add(await ThirdwebTransaction.WaitForTransactionReceipt(executor.Client, tx.ChainId, hash, cancellationToken));
95+
_ = await bridge.WaitForStatusCompletion(hash, tx.ChainId, cancellationToken);
96+
}
7297
}
7398
return receipts;
7499
}
@@ -117,5 +142,10 @@ public static async Task<StatusData> WaitForStatusCompletion(this ThirdwebBridge
117142
return status;
118143
}
119144

145+
public static bool IsSwapRequiredPostOnramp(this OnrampPrepareData preparedOnramp)
146+
{
147+
return preparedOnramp.Steps == null || preparedOnramp.Steps.Count == 0 || !preparedOnramp.Steps.Any(step => step.Transactions?.Count > 0);
148+
}
149+
120150
#endregion
121151
}

0 commit comments

Comments
 (0)