Add airdrop support for Solana networks
This commit is contained in:
parent
e15b290882
commit
97709d197f
|
@ -1,3 +1,4 @@
|
|||
using CSharpFunctionalExtensions;
|
||||
using NftFaucet.Domain.Models.Abstraction;
|
||||
using NftFaucet.Domain.Models.Enums;
|
||||
|
||||
|
@ -13,4 +14,6 @@ public interface INetwork : INamedEntity, IEntityWithOrder
|
|||
public Uri PublicRpcUrl { get; }
|
||||
public Uri ExplorerUrl { get; }
|
||||
public IReadOnlyCollection<IContract> DeployedContracts { get; }
|
||||
public bool SupportsAirdrop { get; }
|
||||
public Task<Result> Airdrop(string address);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using CSharpFunctionalExtensions;
|
||||
using NftFaucet.Domain.Models.Abstraction;
|
||||
using NftFaucet.Domain.Models.Enums;
|
||||
using NftFaucet.Plugins.Models.Abstraction;
|
||||
|
@ -14,4 +15,6 @@ public abstract class Network : DefaultEntity, INetwork
|
|||
public abstract Uri PublicRpcUrl { get; }
|
||||
public abstract Uri ExplorerUrl { get; }
|
||||
public virtual IReadOnlyCollection<IContract> DeployedContracts { get; } = new List<IContract>();
|
||||
public virtual bool SupportsAirdrop { get; } = false;
|
||||
public virtual Task<Result> Airdrop(string address) => Task.FromResult(Result.Failure("Airdrop is not supported in this network"));
|
||||
}
|
||||
|
|
|
@ -60,7 +60,11 @@ else if (State == MintingState.CheckingNetwork)
|
|||
</text>
|
||||
</p>
|
||||
<div style="display: flex; justify-content: end;">
|
||||
<RadzenButton Click="@(async (args) => await CheckNetwork())" Text="Try again" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" />
|
||||
@if (AppState.SelectedNetwork.SupportsAirdrop)
|
||||
{
|
||||
<RadzenButton Click="@(async (args) => await RequestAirdrop())" Text="Request airdrop" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" Style="margin-right: 1rem;" />
|
||||
}
|
||||
<RadzenButton Click="@(async (args) => await CheckNetwork())" Text="Try again" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))"/>
|
||||
</div>
|
||||
</RadzenContent>
|
||||
} else if (State == MintingState.SendingTransaction)
|
||||
|
|
|
@ -4,6 +4,7 @@ using NftFaucet.Domain.Models;
|
|||
using NftFaucet.Domain.Utils;
|
||||
using NftFaucet.Plugins.Models;
|
||||
using NftFaucet.Plugins.Models.Abstraction;
|
||||
using Radzen;
|
||||
|
||||
#pragma warning disable CS4014
|
||||
|
||||
|
@ -159,7 +160,29 @@ public partial class MintDialog : BasicComponent
|
|||
CheckingNetwork,
|
||||
CheckingAddress,
|
||||
CheckingBalance,
|
||||
RequestingAirdrop,
|
||||
SendingTransaction,
|
||||
Done,
|
||||
}
|
||||
|
||||
private async Task RequestAirdrop()
|
||||
{
|
||||
State = MintingState.RequestingAirdrop;
|
||||
ProgressBarText = "Requesting airdrop...";
|
||||
RefreshMediator.NotifyStateHasChangedSafe();
|
||||
|
||||
var result = await AppState.SelectedNetwork.Airdrop(AppState.UserStorage.DestinationAddress);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
NotificationService.Notify(NotificationSeverity.Success, "Airdrop succeeded");
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationService.Notify(NotificationSeverity.Error, "Airdrop failed", result.Error);
|
||||
}
|
||||
|
||||
ProgressBarText = null;
|
||||
RefreshMediator.NotifyStateHasChangedSafe();
|
||||
CheckNetwork();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
using NftFaucet.Domain.Models;
|
||||
using NftFaucet.Domain.Models.Abstraction;
|
||||
using NftFaucet.Domain.Models.Enums;
|
||||
using NftFaucet.Plugins.Models;
|
||||
|
||||
namespace NftFaucet.NetworkPlugins.Solana.Networks;
|
||||
|
||||
public sealed class SolanaDevnetNetwork : Network
|
||||
public sealed class SolanaDevnetNetwork : SolanaNetwork
|
||||
{
|
||||
public override Guid Id { get; } = Guid.Parse("da9f269a-b53e-492a-be07-b4aadc2aae83");
|
||||
public override string Name { get; } = "Solana Devnet";
|
||||
public override string ShortName { get; } = "SolDevnet";
|
||||
public override int? Order { get; } = 2;
|
||||
public override string Currency { get; } = "SOL";
|
||||
public override string ImageName { get; } = "solana-black.svg";
|
||||
public override NetworkType Type { get; } = NetworkType.Solana;
|
||||
public override NetworkSubtype SubType { get; } = NetworkSubtype.Solana;
|
||||
public override Uri PublicRpcUrl { get; } = new Uri("https://api.devnet.solana.com");
|
||||
public override Uri ExplorerUrl { get; } = new Uri("https://explorer.solana.com/?cluster=devnet");
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
using CSharpFunctionalExtensions;
|
||||
using NftFaucet.Domain.Models.Enums;
|
||||
using NftFaucet.Domain.Utils;
|
||||
using NftFaucet.Plugins.Models;
|
||||
using Solnet.Rpc;
|
||||
|
||||
namespace NftFaucet.NetworkPlugins.Solana.Networks;
|
||||
|
||||
public abstract class SolanaNetwork : Network
|
||||
{
|
||||
public override string Currency { get; } = "SOL";
|
||||
public override NetworkType Type { get; } = NetworkType.Solana;
|
||||
public override NetworkSubtype SubType { get; } = NetworkSubtype.Solana;
|
||||
public override bool SupportsAirdrop { get; } = true;
|
||||
|
||||
public override async Task<Result> Airdrop(string address)
|
||||
{
|
||||
var client = ClientFactory.GetClient(PublicRpcUrl.OriginalString);
|
||||
var airdropResult = await client.RequestAirdropAsync(address, 50000000);
|
||||
if (!airdropResult.WasSuccessful)
|
||||
{
|
||||
return Result.Failure("Failed to request airdrop: " + airdropResult.Reason);
|
||||
}
|
||||
|
||||
var wasSuccessful = await RepeatAsync(async () =>
|
||||
{
|
||||
var transaction = await client.GetTransactionAsync(airdropResult.Result);
|
||||
return transaction.WasRequestSuccessfullyHandled && transaction.ErrorData == null;
|
||||
}, TimeSpan.FromSeconds(1), 100);
|
||||
if (!wasSuccessful)
|
||||
{
|
||||
return Result.Failure("Failed to retrieve airdrop tx");
|
||||
}
|
||||
|
||||
wasSuccessful = await RepeatAsync(async () =>
|
||||
{
|
||||
var balanceRes = await client.GetBalanceAsync(address);
|
||||
return balanceRes.Result.Value > 0;
|
||||
}, TimeSpan.FromSeconds(1), 5);
|
||||
if (!wasSuccessful)
|
||||
{
|
||||
return Result.Failure("Airdrop failed: balance is still zero");
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
private static async Task<bool> RepeatAsync(Func<Task<bool>> work, TimeSpan retryInterval, int maxExecutionCount = 3)
|
||||
{
|
||||
for (var i = 0; i < maxExecutionCount; ++i)
|
||||
{
|
||||
var result = await ResultWrapper.Wrap(work);
|
||||
if (result.IsSuccess && result.Value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
await Task.Delay(retryInterval);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,20 +1,16 @@
|
|||
using NftFaucet.Domain.Models;
|
||||
using NftFaucet.Domain.Models.Abstraction;
|
||||
using NftFaucet.Domain.Models.Enums;
|
||||
using NftFaucet.Plugins.Models;
|
||||
|
||||
namespace NftFaucet.NetworkPlugins.Solana.Networks;
|
||||
|
||||
public sealed class SolanaTestnetNetwork : Network
|
||||
public sealed class SolanaTestnetNetwork : SolanaNetwork
|
||||
{
|
||||
public override Guid Id { get; } = Guid.Parse("12d13a34-689c-4fb1-84c0-7fcb719ef5b0");
|
||||
public override string Name { get; } = "Solana Testnet";
|
||||
public override string ShortName { get; } = "SolTestnet";
|
||||
public override int? Order { get; } = 3;
|
||||
public override string Currency { get; } = "SOL";
|
||||
public override string ImageName { get; } = "solana-black.svg";
|
||||
public override NetworkType Type { get; } = NetworkType.Solana;
|
||||
public override NetworkSubtype SubType { get; } = NetworkSubtype.Solana;
|
||||
public override Uri PublicRpcUrl { get; } = new Uri("https://api.testnet.solana.com");
|
||||
public override Uri ExplorerUrl { get; } = new Uri("https://explorer.solana.com/?cluster=testnet");
|
||||
|
||||
|
|
|
@ -10,4 +10,8 @@
|
|||
<ProjectReference Include="..\..\..\NftFaucet.Plugins\NftFaucet.Plugins.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Solana.Metaplex" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue