Skip to content

Commit 03a1a42

Browse files
authored
Thirdweb Pay (#31)
* Thirdweb Pay * checksum util * hex str -> bigint -> hexbigint * static * Update codecov.yml * min gas per pub 10k * Update codecov.yml * Update codecov.yml
1 parent df96324 commit 03a1a42

22 files changed

+1114
-3
lines changed

Thirdweb.Tests/Thirdweb.Utils.Tests.cs

+8
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,12 @@ public void GenerateSIWE_ThrowsOnNullIssuedAt()
386386
};
387387
_ = Assert.Throws<ArgumentNullException>(() => Utils.GenerateSIWE(loginPayloadData));
388388
}
389+
390+
[Fact]
391+
public void ToChecksumAddress_ReturnsCorrectValue()
392+
{
393+
var address = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed".ToLower();
394+
var checksumAddress = Utils.ToChecksumAddress(address);
395+
Assert.Equal("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", checksumAddress);
396+
}
389397
}

Thirdweb/Thirdweb.Pay/Constants.cs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Thirdweb.Pay
2+
{
3+
public static class Constants
4+
{
5+
public const string THIRDWEB_PAY_BASE_URL = "https://pay.thirdweb.com";
6+
7+
public const string THIRDWEB_PAY_CRYPTO_QUOTE_ENDPOINT = THIRDWEB_PAY_BASE_URL + "/buy-with-crypto/quote/v1";
8+
public const string THIRDWEB_PAY_CRYPTO_STATUS_ENDPOINT = THIRDWEB_PAY_BASE_URL + "/buy-with-crypto/status/v1";
9+
10+
public const string THIRDWEB_PAY_FIAT_QUOTE_ENDPOINT = THIRDWEB_PAY_BASE_URL + "/buy-with-fiat/quote/v1";
11+
public const string THIRDWEB_PAY_FIAT_STATUS_ENDPOINT = THIRDWEB_PAY_BASE_URL + "/buy-with-fiat/status/v1";
12+
13+
public const string THIRDWEB_PAY_FIAT_CURRENCIES_ENDPOINT = THIRDWEB_PAY_BASE_URL + "/buy-with-fiat/currency/v1";
14+
15+
public const string THIRDWEB_PAY_HISTORY_ENDPOINT = THIRDWEB_PAY_BASE_URL + "/wallet/history/v1";
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Numerics;
2+
using System.Threading.Tasks;
3+
using Nethereum.Hex.HexTypes;
4+
5+
namespace Thirdweb.Pay
6+
{
7+
public partial class ThirdwebPay
8+
{
9+
public static async Task<string> BuyWithCrypto(ThirdwebClient client, IThirdwebWallet wallet, BuyWithCryptoQuoteResult buyWithCryptoQuote)
10+
{
11+
if (buyWithCryptoQuote.Approval != null)
12+
{
13+
var erc20ToApprove = await ThirdwebContract.Create(client, buyWithCryptoQuote.Approval.TokenAddress, buyWithCryptoQuote.Approval.ChainId);
14+
var currentAllowance = await erc20ToApprove.ERC20_Allowance(await wallet.GetAddress(), buyWithCryptoQuote.Approval.SpenderAddress);
15+
if (currentAllowance < BigInteger.Parse(buyWithCryptoQuote.Approval.AmountWei))
16+
{
17+
_ = await erc20ToApprove.ERC20_Approve(wallet, buyWithCryptoQuote.Approval.SpenderAddress, BigInteger.Parse(buyWithCryptoQuote.Approval.AmountWei));
18+
}
19+
}
20+
21+
var txInput = new ThirdwebTransactionInput()
22+
{
23+
From = buyWithCryptoQuote.TransactionRequest.From,
24+
To = buyWithCryptoQuote.TransactionRequest.To,
25+
Data = buyWithCryptoQuote.TransactionRequest.Data,
26+
Value = new HexBigInteger(BigInteger.Parse(buyWithCryptoQuote.TransactionRequest.Value)),
27+
Gas = new HexBigInteger(BigInteger.Parse(buyWithCryptoQuote.TransactionRequest.GasLimit)),
28+
GasPrice = new HexBigInteger(BigInteger.Parse(buyWithCryptoQuote.TransactionRequest.GasPrice)),
29+
};
30+
31+
var tx = await ThirdwebTransaction.Create(client, wallet, txInput, buyWithCryptoQuote.TransactionRequest.ChainId);
32+
33+
var hash = await ThirdwebTransaction.Send(tx);
34+
35+
return hash;
36+
}
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace Thirdweb.Pay
5+
{
6+
public partial class ThirdwebPay
7+
{
8+
public static string BuyWithFiat(BuyWithFiatQuoteResult buyWithFiatQuote)
9+
{
10+
if (string.IsNullOrEmpty(buyWithFiatQuote.OnRampLink))
11+
{
12+
throw new ArgumentException("On-ramp link cannot be null or empty.");
13+
}
14+
15+
var onRampLink = buyWithFiatQuote.OnRampLink;
16+
17+
return onRampLink;
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Thirdweb.Pay
4+
{
5+
public partial class ThirdwebPay
6+
{
7+
public static async Task<BuyHistoryResult> GetBuyHistory(ThirdwebClient client, string walletAddress, int start, int count, string cursor = null, int? pageSize = null)
8+
{
9+
var queryString = new Dictionary<string, string>
10+
{
11+
{ "walletAddress", walletAddress },
12+
{ "start", start.ToString() },
13+
{ "count", count.ToString() },
14+
{ "cursor", cursor },
15+
{ "pageSize", pageSize?.ToString() }
16+
};
17+
18+
var queryStringFormatted = string.Join("&", queryString.Where(kv => kv.Value != null).Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
19+
var url = $"{Constants.THIRDWEB_PAY_HISTORY_ENDPOINT}?{queryStringFormatted}";
20+
21+
var getResponse = await client.HttpClient.GetAsync(url);
22+
23+
var content = await getResponse.Content.ReadAsStringAsync();
24+
25+
if (!getResponse.IsSuccessStatusCode)
26+
{
27+
ErrorResponse error;
28+
try
29+
{
30+
error = JsonConvert.DeserializeObject<ErrorResponse>(content);
31+
}
32+
catch
33+
{
34+
error = new ErrorResponse
35+
{
36+
Error = new ErrorDetails
37+
{
38+
Message = "Unknown error",
39+
Reason = "Unknown",
40+
Code = "Unknown",
41+
Stack = "Unknown",
42+
StatusCode = (int)getResponse.StatusCode
43+
}
44+
};
45+
}
46+
47+
throw new Exception(
48+
$"HTTP error! Code: {error.Error.Code} Message: {error.Error.Message} Reason: {error.Error.Reason} StatusCode: {error.Error.StatusCode} Stack: {error.Error.Stack}"
49+
);
50+
}
51+
52+
var data = JsonConvert.DeserializeObject<BuyHistoryResponse>(content);
53+
return data.Result;
54+
}
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Thirdweb.Pay
4+
{
5+
public partial class ThirdwebPay
6+
{
7+
public static async Task<BuyWithCryptoQuoteResult> GetBuyWithCryptoQuote(ThirdwebClient client, BuyWithCryptoQuoteParams buyWithCryptoParams)
8+
{
9+
var queryString = new Dictionary<string, string>
10+
{
11+
{ "fromAddress", buyWithCryptoParams.FromAddress },
12+
{ "fromChainId", buyWithCryptoParams.FromChainId?.ToString() },
13+
{ "fromTokenAddress", buyWithCryptoParams.FromTokenAddress },
14+
{ "fromAmount", buyWithCryptoParams.FromAmount },
15+
{ "fromAmountWei", buyWithCryptoParams.FromAmountWei },
16+
{ "toChainId", buyWithCryptoParams.ToChainId?.ToString() },
17+
{ "toTokenAddress", buyWithCryptoParams.ToTokenAddress },
18+
{ "toAmount", buyWithCryptoParams.ToAmount },
19+
{ "toAmountWei", buyWithCryptoParams.ToAmountWei },
20+
{ "maxSlippageBPS", buyWithCryptoParams.MaxSlippageBPS?.ToString() },
21+
{ "intentId", buyWithCryptoParams.IntentId }
22+
};
23+
24+
var queryStringFormatted = string.Join("&", queryString.Where(kv => kv.Value != null).Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
25+
var url = $"{Constants.THIRDWEB_PAY_CRYPTO_QUOTE_ENDPOINT}?{queryStringFormatted}";
26+
27+
var getResponse = await client.HttpClient.GetAsync(url);
28+
29+
var content = await getResponse.Content.ReadAsStringAsync();
30+
31+
if (!getResponse.IsSuccessStatusCode)
32+
{
33+
ErrorResponse error;
34+
try
35+
{
36+
error = JsonConvert.DeserializeObject<ErrorResponse>(content);
37+
}
38+
catch
39+
{
40+
error = new ErrorResponse
41+
{
42+
Error = new ErrorDetails
43+
{
44+
Message = "Unknown error",
45+
Reason = "Unknown",
46+
Code = "Unknown",
47+
Stack = "Unknown",
48+
StatusCode = (int)getResponse.StatusCode
49+
}
50+
};
51+
}
52+
53+
throw new Exception(
54+
$"HTTP error! Code: {error.Error.Code} Message: {error.Error.Message} Reason: {error.Error.Reason} StatusCode: {error.Error.StatusCode} Stack: {error.Error.Stack}"
55+
);
56+
}
57+
58+
var data = JsonConvert.DeserializeObject<GetSwapQuoteResponse>(content);
59+
return data.Result;
60+
}
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Thirdweb.Pay
4+
{
5+
public partial class ThirdwebPay
6+
{
7+
public static async Task<BuyWithCryptoStatusResult> GetBuyWithCryptoStatus(ThirdwebClient client, string transactionHash)
8+
{
9+
if (string.IsNullOrEmpty(transactionHash))
10+
{
11+
throw new ArgumentException(nameof(transactionHash), "Transaction hash cannot be null or empty.");
12+
}
13+
14+
var queryString = new Dictionary<string, string> { { "transactionHash", transactionHash } };
15+
16+
var queryStringFormatted = string.Join("&", queryString.Where(kv => kv.Value != null).Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
17+
var url = $"{Constants.THIRDWEB_PAY_CRYPTO_STATUS_ENDPOINT}?{queryStringFormatted}";
18+
19+
var getResponse = await client.HttpClient.GetAsync(url);
20+
21+
var content = await getResponse.Content.ReadAsStringAsync();
22+
23+
if (!getResponse.IsSuccessStatusCode)
24+
{
25+
ErrorResponse error;
26+
try
27+
{
28+
error = JsonConvert.DeserializeObject<ErrorResponse>(content);
29+
}
30+
catch
31+
{
32+
error = new ErrorResponse
33+
{
34+
Error = new ErrorDetails
35+
{
36+
Message = "Unknown error",
37+
Reason = "Unknown",
38+
Code = "Unknown",
39+
Stack = "Unknown",
40+
StatusCode = (int)getResponse.StatusCode
41+
}
42+
};
43+
}
44+
45+
throw new Exception(
46+
$"HTTP error! Code: {error.Error.Code} Message: {error.Error.Message} Reason: {error.Error.Reason} StatusCode: {error.Error.StatusCode} Stack: {error.Error.Stack}"
47+
);
48+
}
49+
50+
var data = JsonConvert.DeserializeObject<SwapStatusResponse>(content);
51+
return data.Result;
52+
}
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Thirdweb.Pay
4+
{
5+
public partial class ThirdwebPay
6+
{
7+
public static async Task<List<string>> GetBuyWithFiatCurrencies(ThirdwebClient client)
8+
{
9+
var url = $"{Constants.THIRDWEB_PAY_FIAT_CURRENCIES_ENDPOINT}";
10+
11+
var getResponse = await client.HttpClient.GetAsync(url);
12+
13+
var content = await getResponse.Content.ReadAsStringAsync();
14+
15+
if (!getResponse.IsSuccessStatusCode)
16+
{
17+
ErrorResponse error;
18+
try
19+
{
20+
error = JsonConvert.DeserializeObject<ErrorResponse>(content);
21+
}
22+
catch
23+
{
24+
error = new ErrorResponse
25+
{
26+
Error = new ErrorDetails
27+
{
28+
Message = "Unknown error",
29+
Reason = "Unknown",
30+
Code = "Unknown",
31+
Stack = "Unknown",
32+
StatusCode = (int)getResponse.StatusCode
33+
}
34+
};
35+
}
36+
37+
throw new Exception(
38+
$"HTTP error! Code: {error.Error.Code} Message: {error.Error.Message} Reason: {error.Error.Reason} StatusCode: {error.Error.StatusCode} Stack: {error.Error.Stack}"
39+
);
40+
}
41+
42+
var data = JsonConvert.DeserializeObject<FiatCurrenciesResponse>(content);
43+
return data.Result.FiatCurrencies;
44+
}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Thirdweb.Pay
4+
{
5+
public partial class ThirdwebPay
6+
{
7+
public static async Task<BuyWithFiatQuoteResult> GetBuyWithFiatQuote(ThirdwebClient client, BuyWithFiatQuoteParams buyWithFiatParams)
8+
{
9+
var queryString = new Dictionary<string, string>
10+
{
11+
{ "fromCurrencySymbol", buyWithFiatParams.FromCurrencySymbol },
12+
{ "fromAmount", buyWithFiatParams.FromAmount },
13+
{ "fromAmountUnits", buyWithFiatParams.FromAmountUnits },
14+
{ "toAddress", buyWithFiatParams.ToAddress },
15+
{ "toChainId", buyWithFiatParams.ToChainId },
16+
{ "toTokenAddress", buyWithFiatParams.ToTokenAddress },
17+
{ "toAmount", buyWithFiatParams.ToAmount },
18+
{ "toAmountWei", buyWithFiatParams.ToAmountWei },
19+
{ "maxSlippageBPS", buyWithFiatParams.MaxSlippageBPS?.ToString() }
20+
};
21+
22+
var queryStringFormatted = string.Join("&", queryString.Where(kv => kv.Value != null).Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
23+
var url = $"{Constants.THIRDWEB_PAY_FIAT_QUOTE_ENDPOINT}?{queryStringFormatted}";
24+
url += buyWithFiatParams.IsTestMode ? "&isTestMode=true" : "&isTestMode=false";
25+
26+
var getResponse = await client.HttpClient.GetAsync(url);
27+
28+
var content = await getResponse.Content.ReadAsStringAsync();
29+
30+
if (!getResponse.IsSuccessStatusCode)
31+
{
32+
ErrorResponse error;
33+
try
34+
{
35+
error = JsonConvert.DeserializeObject<ErrorResponse>(content);
36+
}
37+
catch
38+
{
39+
error = new ErrorResponse
40+
{
41+
Error = new ErrorDetails
42+
{
43+
Message = "Unknown error",
44+
Reason = "Unknown",
45+
Code = "Unknown",
46+
Stack = "Unknown",
47+
StatusCode = (int)getResponse.StatusCode
48+
}
49+
};
50+
}
51+
52+
throw new Exception(
53+
$"HTTP error! Code: {error.Error.Code} Message: {error.Error.Message} Reason: {error.Error.Reason} StatusCode: {error.Error.StatusCode} Stack: {error.Error.Stack}"
54+
);
55+
}
56+
57+
var data = JsonConvert.DeserializeObject<GetFiatQuoteResponse>(content);
58+
return data.Result;
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)