Add airdrop support for Solana networks

This commit is contained in:
Ivan Yaremenchuk 2022-10-05 20:25:02 -05:00
parent e15b290882
commit 97709d197f
8 changed files with 103 additions and 11 deletions

View File

@ -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);
}

View File

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

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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");

View File

@ -10,4 +10,8 @@
<ProjectReference Include="..\..\..\NftFaucet.Plugins\NftFaucet.Plugins.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Solana.Metaplex" Version="1.2.0" />
</ItemGroup>
</Project>