mirror of
https://github.com/status-im/nft-faucet.git
synced 2025-02-23 03:58:23 +00:00
commit
dfadcdb446
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NftFaucet", "NftFaucet\NftFaucet.csproj", "{EBEB2AEE-0DC0-4F3D-9675-680D5BCD8A01}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NftFaucet", "NftFaucet\NftFaucet.csproj", "{E113DAEE-A1E4-4BE2-8CDA-6E06245A471A}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -8,9 +8,9 @@ Global
|
|||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{EBEB2AEE-0DC0-4F3D-9675-680D5BCD8A01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{E113DAEE-A1E4-4BE2-8CDA-6E06245A471A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{EBEB2AEE-0DC0-4F3D-9675-680D5BCD8A01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E113DAEE-A1E4-4BE2-8CDA-6E06245A471A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EBEB2AEE-0DC0-4F3D-9675-680D5BCD8A01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E113DAEE-A1E4-4BE2-8CDA-6E06245A471A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EBEB2AEE-0DC0-4F3D-9675-680D5BCD8A01}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E113DAEE-A1E4-4BE2-8CDA-6E06245A471A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
using NftFaucet.ApiClients.Models;
|
|
||||||
using RestEase;
|
|
||||||
|
|
||||||
namespace NftFaucet.ApiClients;
|
|
||||||
|
|
||||||
[BaseAddress("https://gw.crustapps.net")]
|
|
||||||
public interface ICrustUploadApiClient
|
|
||||||
{
|
|
||||||
[Header("Authorization")]
|
|
||||||
public string Auth { get; set; }
|
|
||||||
|
|
||||||
[Post("api/v0/add")]
|
|
||||||
Task<UploadResponse> UploadFile([Body] MultipartContent content, [Query("pin")] bool pin = true);
|
|
||||||
}
|
|
@ -10,5 +10,3 @@
|
|||||||
</LayoutView>
|
</LayoutView>
|
||||||
</NotFound>
|
</NotFound>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
||||||
<AntContainer />
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
using AntDesign;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using NftFaucet.Models;
|
using NftFaucet.Models.State;
|
||||||
using NftFaucet.Options;
|
using NftFaucet.Options;
|
||||||
using NftFaucet.Services;
|
using NftFaucet.Services;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
namespace NftFaucet.Components;
|
namespace NftFaucet.Components;
|
||||||
|
|
||||||
public abstract class BasicComponent : ComponentBase
|
public abstract class BasicComponent : ComponentBase
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
protected NavigationManager UriHelper { get; set; }
|
protected NavigationManager NavigationManager { get; set; }
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
protected ScopedAppState AppState { get; set; }
|
protected ScopedAppState AppState { get; set; }
|
||||||
@ -17,16 +17,23 @@ public abstract class BasicComponent : ComponentBase
|
|||||||
[Inject]
|
[Inject]
|
||||||
protected RefreshMediator RefreshMediator { get; set; }
|
protected RefreshMediator RefreshMediator { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected DialogService DialogService { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected TooltipService TooltipService { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected NotificationService NotificationService { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
protected ContextMenuService ContextMenuService { get; set; }
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
protected Settings Settings { get; set; }
|
protected Settings Settings { get; set; }
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
protected MessageService MessageService { get; set; }
|
protected StateRepository StateRepository { get; set; }
|
||||||
|
|
||||||
[Inject]
|
|
||||||
protected IIpfsService IpfsService { get; set; }
|
|
||||||
|
|
||||||
protected MetamaskInfo Metamask => AppState?.Metamask;
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
@ -44,4 +51,9 @@ public abstract class BasicComponent : ComponentBase
|
|||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task SaveAppState()
|
||||||
|
{
|
||||||
|
await StateRepository.SaveAppState(AppState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using NftFaucet.Models;
|
using NftFaucet.Models.State;
|
||||||
|
using NftFaucet.Services;
|
||||||
|
|
||||||
namespace NftFaucet.Components;
|
namespace NftFaucet.Components;
|
||||||
|
|
||||||
public abstract class LayoutBasicComponent : LayoutComponentBase
|
public abstract class BasicLayout : LayoutComponentBase
|
||||||
{
|
{
|
||||||
[Inject]
|
[Inject]
|
||||||
protected NavigationManager UriHelper { get; set; }
|
protected NavigationManager UriHelper { get; set; }
|
74
NftFaucet/Components/CardList/CardList.razor
Normal file
74
NftFaucet/Components/CardList/CardList.razor
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<RadzenDataList WrapItems="true" Data="@Data" TItem="CardListItem" >
|
||||||
|
<Template Context="cardListItem">
|
||||||
|
<RadzenCard class=@("box"
|
||||||
|
+ (cardListItem.IsDisabled ? " box-disabled" : string.Empty)
|
||||||
|
+ (SelectedItems != null && SelectedItems.Contains(cardListItem.Id)
|
||||||
|
? (cardListItem.SelectionIcon == CardListItemSelectionIcon.Checkmark ? " box-checkmark" : " box-warning")
|
||||||
|
: string.Empty))
|
||||||
|
Style="width: 250px;" onclick="@(async () => await ToggleSelection(cardListItem))">
|
||||||
|
<div class="d-flex flex-row align-items-center">
|
||||||
|
@if (SelectedItems != null && SelectedItems.Contains(cardListItem.Id))
|
||||||
|
{
|
||||||
|
if (cardListItem.SelectionIcon == CardListItemSelectionIcon.Checkmark)
|
||||||
|
{
|
||||||
|
<CheckmarkIcon Style="position: absolute; top: 1em; right: 1em;"/>
|
||||||
|
}
|
||||||
|
else if (cardListItem.SelectionIcon == CardListItemSelectionIcon.Warning)
|
||||||
|
{
|
||||||
|
<WarningIcon Style="position: absolute; top: 1em; right: 1em;"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrEmpty(cardListItem.ImageLocation))
|
||||||
|
{
|
||||||
|
<RadzenImage Path=@(cardListItem.ImageLocation) Class="float-left mr-3" Style="width: 80px; height: 80px; margin-right: 1em;"/>
|
||||||
|
}
|
||||||
|
<div style="width: 100%;">
|
||||||
|
@if (!string.IsNullOrEmpty(cardListItem.Header))
|
||||||
|
{
|
||||||
|
<h4 class="mb-0">@cardListItem.Header</h4>
|
||||||
|
}
|
||||||
|
@foreach (var property in cardListItem.Properties ?? Array.Empty<CardListItemProperty>())
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(property?.Value))
|
||||||
|
{
|
||||||
|
var namePart = property.Name != null ? $"{property.Name}: " : string.Empty;
|
||||||
|
var valueStyle = property.ValueColor != null ? $"color: {property.ValueColor}" : string.Empty;
|
||||||
|
<div style="font-size: .8em">
|
||||||
|
<text style="font-weight: bold;">
|
||||||
|
@namePart
|
||||||
|
</text>
|
||||||
|
<text style="@valueStyle">
|
||||||
|
@if (!string.IsNullOrEmpty(property.Link))
|
||||||
|
{
|
||||||
|
<RadzenLink Icon="open_in_new" Path="@property.Value" Text="@property.Value" Target="_blank"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@property.Value
|
||||||
|
}
|
||||||
|
</text>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<div>
|
||||||
|
@foreach (var badge in cardListItem.Badges ?? Array.Empty<CardListItemBadge>())
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(badge?.Text))
|
||||||
|
{
|
||||||
|
<RadzenBadge BadgeStyle="@badge.Style" IsPill="true" Text="@badge.Text"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (cardListItem.Configuration != null)
|
||||||
|
{
|
||||||
|
<RadzenButton Icon="build" Click="@(async () => await OpenItemConfigurationDialog(cardListItem))" ButtonStyle="@ButtonStyle.Secondary"
|
||||||
|
Disabled="SelectedItems == null || !SelectedItems.Contains(cardListItem.Id)"
|
||||||
|
Style="position: absolute; bottom: 1em; right: 1em;" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</RadzenCard>
|
||||||
|
</Template>
|
||||||
|
</RadzenDataList>
|
57
NftFaucet/Components/CardList/CardList.razor.cs
Normal file
57
NftFaucet/Components/CardList/CardList.razor.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public partial class CardList : BasicComponent
|
||||||
|
{
|
||||||
|
[Parameter] public CardListItem[] Data { get; set; }
|
||||||
|
[Parameter] public Guid[] SelectedItems { get; set; }
|
||||||
|
[Parameter] public EventCallback<Guid[]> SelectedItemsChanged { get; set; }
|
||||||
|
[Parameter] public EventCallback<Guid[]> OnSelectedChange { get; set; }
|
||||||
|
[Parameter] public bool AllowMultipleSelection { get; set; }
|
||||||
|
[Parameter] public bool AllowUnselect { get; set; }
|
||||||
|
|
||||||
|
public async Task ToggleSelection(CardListItem item)
|
||||||
|
{
|
||||||
|
if (item.IsDisabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedItems = SelectedItems?.ToList() ?? new List<Guid>();
|
||||||
|
var isAlreadySelected = selectedItems.Contains(item.Id);
|
||||||
|
if (isAlreadySelected && AllowUnselect)
|
||||||
|
{
|
||||||
|
selectedItems.Remove(item.Id);
|
||||||
|
}
|
||||||
|
else if (!isAlreadySelected)
|
||||||
|
{
|
||||||
|
if (!AllowMultipleSelection)
|
||||||
|
{
|
||||||
|
selectedItems.Clear();
|
||||||
|
}
|
||||||
|
selectedItems.Add(item.Id);
|
||||||
|
}
|
||||||
|
SelectedItems = selectedItems.ToArray();
|
||||||
|
await SelectedItemsChanged.InvokeAsync(SelectedItems);
|
||||||
|
await OnSelectedChange.InvokeAsync(SelectedItems);
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task OpenItemConfigurationDialog(CardListItem item)
|
||||||
|
{
|
||||||
|
var result = (bool?) await DialogService.OpenAsync<CardListItemConfigurationDialog>("Configuration",
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "CardListItemId", item.Id },
|
||||||
|
{ "CardListItem", item },
|
||||||
|
},
|
||||||
|
new DialogOptions() {Width = "700px", Height = "570px", Resizable = true, Draggable = true});
|
||||||
|
|
||||||
|
if (result != null && result.Value)
|
||||||
|
{
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
NftFaucet/Components/CardList/CardListItem.cs
Normal file
13
NftFaucet/Components/CardList/CardListItem.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public class CardListItem
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string ImageLocation { get; set; }
|
||||||
|
public string Header { get; set; }
|
||||||
|
public bool IsDisabled { get; set; }
|
||||||
|
public CardListItemSelectionIcon SelectionIcon { get; set; }
|
||||||
|
public CardListItemProperty[] Properties { get; set; } = Array.Empty<CardListItemProperty>();
|
||||||
|
public CardListItemBadge[] Badges { get; set; } = Array.Empty<CardListItemBadge>();
|
||||||
|
public CardListItemConfiguration Configuration { get; set; }
|
||||||
|
}
|
9
NftFaucet/Components/CardList/CardListItemBadge.cs
Normal file
9
NftFaucet/Components/CardList/CardListItemBadge.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public class CardListItemBadge
|
||||||
|
{
|
||||||
|
public BadgeStyle Style { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using CSharpFunctionalExtensions;
|
||||||
|
|
||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public class CardListItemConfiguration
|
||||||
|
{
|
||||||
|
public CardListItemConfigurationObject[] Objects { get; set; }
|
||||||
|
public Func<CardListItemConfigurationObject[], Task<Result>> ConfigureAction { get; set; }
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
@page "/configuration/{CardListItemId}"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Configuration</PageTitle>
|
||||||
|
|
||||||
|
<form onsubmit="@OnSavePressed">
|
||||||
|
@foreach (var configurationObject in CardListItem.Configuration.Objects)
|
||||||
|
{
|
||||||
|
switch (configurationObject.Type)
|
||||||
|
{
|
||||||
|
case CardListItemConfigurationObjectType.Input:
|
||||||
|
{
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4>@configurationObject.Name</h4>
|
||||||
|
<RadzenTextBox Placeholder=@configurationObject.Placeholder @bind-Value="@configurationObject.Value" Disabled="@configurationObject.IsDisabled" Class="w-100"/>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CardListItemConfigurationObjectType.Button:
|
||||||
|
{
|
||||||
|
<div class="mb-4">
|
||||||
|
<RadzenButton Text="@configurationObject.Name" Click="@(() => configurationObject.ClickAction())" Disabled="@configurationObject.IsDisabled" Class="w-100"/>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@((args) => OnSavePressed())" Text="Save" Style="width: 120px"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public partial class CardListItemConfigurationDialog : BasicComponent
|
||||||
|
{
|
||||||
|
[Parameter] public Guid CardListItemId { get; set; }
|
||||||
|
[Parameter] public CardListItem CardListItem { get; set; }
|
||||||
|
|
||||||
|
private async Task OnSavePressed()
|
||||||
|
{
|
||||||
|
var result = await CardListItem.Configuration.ConfigureAction(CardListItem.Configuration.Objects);
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
NotificationService.Notify(NotificationSeverity.Error, "Invalid configuration", result.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await CardListItem.Configuration.ConfigureAction(CardListItem.Configuration.Objects);
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
|
||||||
|
DialogService.Close((bool?)true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public class CardListItemConfigurationObject
|
||||||
|
{
|
||||||
|
public CardListItemConfigurationObjectType Type { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string Icon { get; set; }
|
||||||
|
public string Placeholder { get; set; }
|
||||||
|
public bool IsDisabled { get; set; }
|
||||||
|
|
||||||
|
// for type=Button only
|
||||||
|
public Action ClickAction { get; set; }
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public enum CardListItemConfigurationObjectType
|
||||||
|
{
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
}
|
9
NftFaucet/Components/CardList/CardListItemProperty.cs
Normal file
9
NftFaucet/Components/CardList/CardListItemProperty.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public class CardListItemProperty
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public string ValueColor { get; set; }
|
||||||
|
public string Link { get; set; }
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace NftFaucet.Components.CardList;
|
||||||
|
|
||||||
|
public enum CardListItemSelectionIcon
|
||||||
|
{
|
||||||
|
Checkmark,
|
||||||
|
Warning
|
||||||
|
}
|
5
NftFaucet/Components/CheckmarkIcon/CheckmarkIcon.razor
Normal file
5
NftFaucet/Components/CheckmarkIcon/CheckmarkIcon.razor
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<span class="checkmark" style=@Style>
|
||||||
|
<div class="checkmark_circle"></div>
|
||||||
|
<div class="checkmark_stem"></div>
|
||||||
|
<div class="checkmark_kick"></div>
|
||||||
|
</span>
|
@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace NftFaucet.Components.CheckmarkIcon;
|
||||||
|
|
||||||
|
public partial class CheckmarkIcon
|
||||||
|
{
|
||||||
|
[Parameter] public string Style { get; set; }
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
@inherits ComponentBase
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter] public string Icon { get; set; }
|
|
||||||
[Parameter] public string Text { get; set; }
|
|
||||||
[Parameter] public string Color { get; set; } = "black";
|
|
||||||
[Parameter] public int? Level { get; set; }
|
|
||||||
[Parameter] public string FontSize { get; set; } = "1em";
|
|
||||||
|
|
||||||
private string IconStyle => $"display: inline; font-size: {FontSize};";
|
|
||||||
private string TitleStyle => $"color: {Color};" + (Level.HasValue ? "margin-bottom: 0;" : $"font-size: {FontSize};");
|
|
||||||
}
|
|
||||||
|
|
||||||
<Space Align="center">
|
|
||||||
@if (!string.IsNullOrEmpty(Icon))
|
|
||||||
{
|
|
||||||
<SpaceItem>
|
|
||||||
<div class="space-item">
|
|
||||||
<Icon Type="@Icon" Style="@IconStyle"></Icon>
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
@if (!string.IsNullOrEmpty(Text))
|
|
||||||
{
|
|
||||||
if (Level == null)
|
|
||||||
{
|
|
||||||
<SpaceItem>
|
|
||||||
<Text Style="@TitleStyle">@Text</Text>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<Title Level="@Level.Value" Style="@TitleStyle">@Text</Title>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</Space>
|
|
@ -1,4 +0,0 @@
|
|||||||
.space-item {
|
|
||||||
align-items: center !important;
|
|
||||||
display: flex !important;
|
|
||||||
}
|
|
3
NftFaucet/Components/WarningIcon/WarningIcon.razor
Normal file
3
NftFaucet/Components/WarningIcon/WarningIcon.razor
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div style="@Style">
|
||||||
|
<span style="color: #FBBD08; font-size: 2em; vertical-align: middle; line-height: 0.6; display: inline-block; width: 22px; height: 22px;">⚠</span>
|
||||||
|
</div>
|
8
NftFaucet/Components/WarningIcon/WarningIcon.razor.cs
Normal file
8
NftFaucet/Components/WarningIcon/WarningIcon.razor.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace NftFaucet.Components.WarningIcon;
|
||||||
|
|
||||||
|
public partial class WarningIcon
|
||||||
|
{
|
||||||
|
[Parameter] public string Style { get; set; }
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
namespace NftFaucet.Constants;
|
|
||||||
|
|
||||||
public static class UploadConstants
|
|
||||||
{
|
|
||||||
public const int MaxFileSizeInMegabytes = 20;
|
|
||||||
public const long MaxFileSizeInBytes = MaxFileSizeInMegabytes * 1024 * 1024;
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "@openzeppelin/contracts@4.5.0/access/AccessControlEnumerable.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/token/ERC1155/extensions/ERC1155Supply.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/utils/Context.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/utils/Counters.sol";
|
|
||||||
|
|
||||||
contract Erc1155Faucet is Context, AccessControlEnumerable, ERC1155Supply {
|
|
||||||
string internal nftName;
|
|
||||||
string internal nftSymbol;
|
|
||||||
|
|
||||||
using Counters for Counters.Counter;
|
|
||||||
Counters.Counter private _tokenIdCounter;
|
|
||||||
|
|
||||||
mapping(uint256 => string) internal _uriDict;
|
|
||||||
|
|
||||||
constructor() ERC1155("https://token-cdn-domain/{id}.json")
|
|
||||||
{
|
|
||||||
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
|
|
||||||
nftName = "ERC-1155 Faucet";
|
|
||||||
nftSymbol = "FA1155";
|
|
||||||
}
|
|
||||||
|
|
||||||
function mint(
|
|
||||||
address to,
|
|
||||||
uint256 amount,
|
|
||||||
string calldata tokenUri)
|
|
||||||
public
|
|
||||||
virtual
|
|
||||||
{
|
|
||||||
uint256 tokenId = _tokenIdCounter.current();
|
|
||||||
_tokenIdCounter.increment();
|
|
||||||
_mint(to, tokenId, amount, "0x1234");
|
|
||||||
_uriDict[tokenId] = tokenUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
function uri(uint256 id)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
virtual
|
|
||||||
override
|
|
||||||
returns (string memory)
|
|
||||||
{
|
|
||||||
return _uriDict[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
function name()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (string memory _name)
|
|
||||||
{
|
|
||||||
_name = nftName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function symbol()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (string memory _symbol)
|
|
||||||
{
|
|
||||||
_symbol = nftSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
function supportsInterface(bytes4 interfaceId)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
override(ERC1155, AccessControlEnumerable)
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return ERC1155.supportsInterface(interfaceId) || AccessControlEnumerable.supportsInterface(interfaceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function totalSupply()
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
uint256 result = 0;
|
|
||||||
uint256 maxId = _tokenIdCounter.current();
|
|
||||||
|
|
||||||
for(uint256 i = 0; i < maxId; i++)
|
|
||||||
{
|
|
||||||
result = result + totalSupply(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.7;
|
|
||||||
|
|
||||||
import "@openzeppelin/contracts@4.5.0/token/ERC721/ERC721.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/token/ERC721/extensions/ERC721Enumerable.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/token/ERC721/extensions/ERC721URIStorage.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/access/Ownable.sol";
|
|
||||||
import "@openzeppelin/contracts@4.5.0/utils/Counters.sol";
|
|
||||||
|
|
||||||
contract Erc721Faucet is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
|
|
||||||
using Counters for Counters.Counter;
|
|
||||||
|
|
||||||
Counters.Counter private _tokenIdCounter;
|
|
||||||
|
|
||||||
constructor() ERC721("ERC-721 Faucet", "FA721") {}
|
|
||||||
|
|
||||||
function safeMint(address to, string memory uri)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
uint256 tokenId = _tokenIdCounter.current();
|
|
||||||
_tokenIdCounter.increment();
|
|
||||||
_safeMint(to, tokenId);
|
|
||||||
_setTokenURI(tokenId, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _beforeTokenTransfer(address from, address to, uint256 tokenId)
|
|
||||||
internal
|
|
||||||
override(ERC721, ERC721Enumerable)
|
|
||||||
{
|
|
||||||
super._beforeTokenTransfer(from, to, tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _burn(uint256 tokenId)
|
|
||||||
internal
|
|
||||||
override(ERC721, ERC721URIStorage)
|
|
||||||
{
|
|
||||||
super._burn(tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenURI(uint256 tokenId)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
override(ERC721, ERC721URIStorage)
|
|
||||||
returns (string memory)
|
|
||||||
{
|
|
||||||
return super.tokenURI(tokenId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function supportsInterface(bytes4 interfaceId)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
override(ERC721, ERC721Enumerable)
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return ERC721.supportsInterface(interfaceId) || ERC721Enumerable.supportsInterface(interfaceId);
|
|
||||||
}
|
|
||||||
}
|
|
7
NftFaucet/Extensions/EnumerableExtensions.cs
Normal file
7
NftFaucet/Extensions/EnumerableExtensions.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace NftFaucet.Extensions;
|
||||||
|
|
||||||
|
public static class EnumerableExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<T> Duplicates<T>(this IEnumerable<T> source)
|
||||||
|
=> source.GroupBy(x => x).Where(kvp => kvp.Count() > 1).Select(kvp => kvp.Key);
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace NftFaucet.Extensions;
|
|
||||||
|
|
||||||
public static class StringExtensions
|
|
||||||
{
|
|
||||||
public static bool IsValidJson(this string str)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(str))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
str = str.Trim();
|
|
||||||
if ((!str.StartsWith("{") || !str.EndsWith("}")) && (!str.StartsWith("[") || !str.EndsWith("]")))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var _ = JToken.Parse(str);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (JsonReaderException)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
using CSharpFunctionalExtensions;
|
|
||||||
using Nethereum.Hex.HexConvertors.Extensions;
|
|
||||||
using Nethereum.Util;
|
|
||||||
|
|
||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class Address : ValueObject<Address>
|
|
||||||
{
|
|
||||||
private const string LongFormatPrefix = "0x000000000000000000000000";
|
|
||||||
|
|
||||||
private Address(string value)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public static implicit operator string(Address address) => address.Value;
|
|
||||||
public static explicit operator Address(string address) => Create(address).Value;
|
|
||||||
|
|
||||||
public static Result<Address> Create(string address)
|
|
||||||
{
|
|
||||||
const int longFormatLength = 66;
|
|
||||||
|
|
||||||
if (address.IsAnEmptyAddress())
|
|
||||||
return Result.Failure<Address>("Address is empty");
|
|
||||||
|
|
||||||
address = address.EnsureHexPrefix();
|
|
||||||
if (address.Length == longFormatLength && address.StartsWith(LongFormatPrefix))
|
|
||||||
address = address.Substring(LongFormatPrefix.Length).EnsureHexPrefix();
|
|
||||||
|
|
||||||
if (!address.IsValidEthereumAddressHexFormat() || !address.IsValidEthereumAddressLength())
|
|
||||||
return Result.Failure<Address>("Invalid address value");
|
|
||||||
|
|
||||||
return new Address(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => Value;
|
|
||||||
|
|
||||||
protected override bool EqualsCore(Address other)
|
|
||||||
=> string.Equals(Value, other.Value, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
|
|
||||||
protected override int GetHashCodeCore() => Value.GetHashCode(StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
|
7
NftFaucet/Models/Balance.cs
Normal file
7
NftFaucet/Models/Balance.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
|
public class Balance
|
||||||
|
{
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
public string Currency { get; set; }
|
||||||
|
}
|
13
NftFaucet/Models/Dto/AppStateDto.cs
Normal file
13
NftFaucet/Models/Dto/AppStateDto.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace NftFaucet.Models.Dto;
|
||||||
|
|
||||||
|
public class AppStateDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.Parse("621a252e-1d8d-4225-a045-b470469730cb");
|
||||||
|
public Guid? SelectedNetwork { get; set; }
|
||||||
|
public Guid? SelectedProvider { get; set; }
|
||||||
|
public Guid? SelectedContract { get; set; }
|
||||||
|
public Guid? SelectedToken { get; set; }
|
||||||
|
public Guid? SelectedUploadLocation { get; set; }
|
||||||
|
public string DestinationAddress { get; set; }
|
||||||
|
public int? TokenAmount { get; set; }
|
||||||
|
}
|
7
NftFaucet/Models/Dto/ProviderStateDto.cs
Normal file
7
NftFaucet/Models/Dto/ProviderStateDto.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace NftFaucet.Models.Dto;
|
||||||
|
|
||||||
|
public class ProviderStateDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
}
|
13
NftFaucet/Models/Dto/TokenDto.cs
Normal file
13
NftFaucet/Models/Dto/TokenDto.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace NftFaucet.Models.Dto;
|
||||||
|
|
||||||
|
public class TokenDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public string ImageFileName { get; set; }
|
||||||
|
public string ImageFileType { get; set; }
|
||||||
|
public string ImageFileData { get; set; }
|
||||||
|
public long? ImageFileSize { get; set; }
|
||||||
|
}
|
11
NftFaucet/Models/Dto/UploadLocationDto.cs
Normal file
11
NftFaucet/Models/Dto/UploadLocationDto.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace NftFaucet.Models.Dto;
|
||||||
|
|
||||||
|
public class UploadLocationDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid TokenId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Location { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public Guid UploaderId { get; set; }
|
||||||
|
}
|
7
NftFaucet/Models/Dto/UploaderStateDto.cs
Normal file
7
NftFaucet/Models/Dto/UploaderStateDto.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace NftFaucet.Models.Dto;
|
||||||
|
|
||||||
|
public class UploaderStateDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
namespace NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
public class EnumWrapper<T> where T : Enum
|
|
||||||
{
|
|
||||||
public T Value { get; set; }
|
|
||||||
public string ValueString { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
public EnumWrapper(T value, string description)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
ValueString = value.ToString();
|
|
||||||
Description = description;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
public enum IpfsGatewayType : byte
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
IpfsOfficial = 1,
|
|
||||||
NftStorage = 3,
|
|
||||||
Crust = 4,
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
namespace NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
public enum NetworkChain : long
|
|
||||||
{
|
|
||||||
EthereumMainnet = 1,
|
|
||||||
Ropsten = 3,
|
|
||||||
Rinkeby = 4,
|
|
||||||
Goerli = 5,
|
|
||||||
Kovan = 42,
|
|
||||||
OptimismMainnet = 10,
|
|
||||||
OptimismKovan = 69,
|
|
||||||
PolygonMainnet = 137,
|
|
||||||
PolygonMumbai = 80001,
|
|
||||||
MoonbeamMainnet = 1284,
|
|
||||||
MoonbaseAlpha = 1287,
|
|
||||||
ArbitrumMainnetBeta = 42161,
|
|
||||||
ArbitrumRinkeby = 421611,
|
|
||||||
ArbitrumGoerli = 421612,
|
|
||||||
AvalancheMainnet = 43114,
|
|
||||||
AvalancheFuji = 43113,
|
|
||||||
|
|
||||||
// BNB Smart Chain
|
|
||||||
BnbChainMainnet = 56,
|
|
||||||
BnbChainTestnet = 97,
|
|
||||||
|
|
||||||
// solana
|
|
||||||
SolanaMainnet = 11100,
|
|
||||||
SolanaDevnet = 11101,
|
|
||||||
SolanaTestnet = 11110,
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
namespace NftFaucet.Models.Enums;
|
|
||||||
public enum NetworkType
|
|
||||||
{
|
|
||||||
Ethereum,
|
|
||||||
Solana
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
public enum TokenType : byte
|
|
||||||
{
|
|
||||||
ERC721 = 0,
|
|
||||||
ERC1155 = 1,
|
|
||||||
}
|
|
35
NftFaucet/Models/EthereumKey.cs
Normal file
35
NftFaucet/Models/EthereumKey.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Cryptography.ECDSA;
|
||||||
|
using Nethereum.Hex.HexConvertors.Extensions;
|
||||||
|
using Nethereum.Util;
|
||||||
|
|
||||||
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
|
public class EthereumKey
|
||||||
|
{
|
||||||
|
public string PrivateKey { get; }
|
||||||
|
public string Address { get; }
|
||||||
|
|
||||||
|
public EthereumKey(string privateKey)
|
||||||
|
{
|
||||||
|
PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey));
|
||||||
|
Address = GetAddressFromPrivateKey(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EthereumKey GenerateNew()
|
||||||
|
{
|
||||||
|
var privateKeyBytes = Secp256K1Manager.GenerateRandomKey();
|
||||||
|
var privateKeyString = privateKeyBytes.ToHex(prefix: false);
|
||||||
|
return new EthereumKey(privateKeyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAddressFromPrivateKey(string privateKey)
|
||||||
|
{
|
||||||
|
var privateKeyBytes = privateKey.HexToByteArray();
|
||||||
|
var publicKeyBytes = Secp256K1Manager.GetPublicKey(privateKeyBytes, false).Skip(1).ToArray();
|
||||||
|
var publicKeyHash = new Sha3Keccack().CalculateHash(publicKeyBytes);
|
||||||
|
var addressBytes = new byte[publicKeyHash.Length - 12];
|
||||||
|
Array.Copy(publicKeyHash, 12, addressBytes, 0, publicKeyHash.Length - 12);
|
||||||
|
var addressString = new AddressUtil().ConvertToChecksumAddress(addressBytes.ToHex());
|
||||||
|
return addressString;
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class IpfsBlockchainContext
|
|
||||||
{
|
|
||||||
public string Address { get; private set; }
|
|
||||||
public string SignedMessage { get; private set; }
|
|
||||||
|
|
||||||
public bool IsInitialized => !string.IsNullOrEmpty(Address) && !string.IsNullOrEmpty(SignedMessage);
|
|
||||||
|
|
||||||
public void Initialize(string address, string signedMessage)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(address))
|
|
||||||
throw new ArgumentNullException(nameof(address));
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(signedMessage))
|
|
||||||
throw new ArgumentNullException(nameof(signedMessage));
|
|
||||||
|
|
||||||
Address = address;
|
|
||||||
SignedMessage = signedMessage;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Numerics;
|
|
||||||
using Ethereum.MetaMask.Blazor;
|
|
||||||
using Nethereum.Hex.HexTypes;
|
|
||||||
using NftFaucet.Models.Enums;
|
|
||||||
using NftFaucet.Services;
|
|
||||||
using NftFaucet.Utils;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class MetamaskInfo
|
|
||||||
{
|
|
||||||
private readonly RefreshMediator _refreshMediator;
|
|
||||||
|
|
||||||
public MetamaskInfo(IMetaMaskService service, MetamaskSigningService signingService, RefreshMediator refreshMediator)
|
|
||||||
{
|
|
||||||
Service = service;
|
|
||||||
SigningService = signingService;
|
|
||||||
_refreshMediator = refreshMediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMetaMaskService Service { get; }
|
|
||||||
public MetamaskSigningService SigningService { get; }
|
|
||||||
|
|
||||||
public bool? HasMetaMask { get; private set; }
|
|
||||||
public bool? IsMetaMaskConnected { get; private set; }
|
|
||||||
|
|
||||||
public string Address { get; private set; }
|
|
||||||
public BigInteger ChainId { get; private set; }
|
|
||||||
public NetworkChain? Network { get; private set; }
|
|
||||||
|
|
||||||
public async Task<bool> IsConnected()
|
|
||||||
{
|
|
||||||
HasMetaMask ??= await Service.IsMetaMaskAvailableAsync();
|
|
||||||
IsMetaMaskConnected ??= await Service.IsSiteConnectedAsync();
|
|
||||||
|
|
||||||
var isConnected = HasMetaMask.Value && IsMetaMaskConnected.Value;
|
|
||||||
return isConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsReady()
|
|
||||||
{
|
|
||||||
if (!await IsConnected())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Address) || ChainId == 0)
|
|
||||||
{
|
|
||||||
await RefreshAddress();
|
|
||||||
SubscribeToEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
var isReady = HasMetaMask!.Value && IsMetaMaskConnected!.Value && ChainId != 0;
|
|
||||||
return isReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> Connect()
|
|
||||||
{
|
|
||||||
var result = await ResultWrapper.Wrap(() => Service.ConnectAsync());
|
|
||||||
if (result.IsFailure)
|
|
||||||
{
|
|
||||||
Log.Error(result.Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HasMetaMask = true;
|
|
||||||
IsMetaMaskConnected = true;
|
|
||||||
await RefreshAddress();
|
|
||||||
SubscribeToEvents();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RefreshAddress()
|
|
||||||
{
|
|
||||||
Address = await Service.GetSelectedAccountAsync();
|
|
||||||
var chainIdHex = await Service.GetSelectedChainAsync();
|
|
||||||
ChainId = !string.IsNullOrEmpty(chainIdHex) ? new HexBigInteger(chainIdHex).Value : BigInteger.Zero;
|
|
||||||
Network = long.TryParse(ChainId.ToString(), out var longChainId) && Enum.IsDefined(typeof(NetworkChain), longChainId) ? (NetworkChain) longChainId : null;
|
|
||||||
_refreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SubscribeToEvents()
|
|
||||||
{
|
|
||||||
Service.AccountsChanged += OnAccountChangedEvent;
|
|
||||||
Service.ChainChanged += OnChainChangedEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAccountChangedEvent(object sender, string[] args)
|
|
||||||
{
|
|
||||||
Address = args.FirstOrDefault();
|
|
||||||
_refreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChainChangedEvent(object sender, string chainIdHex)
|
|
||||||
{
|
|
||||||
ChainId = !string.IsNullOrEmpty(chainIdHex) ? new HexBigInteger(chainIdHex).Value : BigInteger.Zero;
|
|
||||||
Network = long.TryParse(ChainId.ToString(), out var longChainId) && Enum.IsDefined(typeof(NetworkChain), longChainId) ? (NetworkChain) longChainId : null;
|
|
||||||
_refreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
}
|
|
||||||
}
|
|
14
NftFaucet/Models/MintRequest.cs
Normal file
14
NftFaucet/Models/MintRequest.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using NftFaucet.Plugins;
|
||||||
|
using NftFaucet.Plugins.NetworkPlugins;
|
||||||
|
using NftFaucet.Plugins.ProviderPlugins;
|
||||||
|
|
||||||
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
|
public record MintRequest(
|
||||||
|
INetwork Network,
|
||||||
|
IProvider Provider,
|
||||||
|
IContract Contract,
|
||||||
|
IToken Token,
|
||||||
|
ITokenUploadLocation UploadLocation,
|
||||||
|
string DestinationAddress,
|
||||||
|
int TokensAmount);
|
10
NftFaucet/Models/MintingState.cs
Normal file
10
NftFaucet/Models/MintingState.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
|
public enum MintingState
|
||||||
|
{
|
||||||
|
CheckingNetwork,
|
||||||
|
CheckingAddress,
|
||||||
|
CheckingBalance,
|
||||||
|
SendingTransaction,
|
||||||
|
Done,
|
||||||
|
}
|
@ -1,80 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
|
|
||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class NavigationWrapper
|
|
||||||
{
|
|
||||||
private readonly NavigationManager _uriHelper;
|
|
||||||
private string _currentUri;
|
|
||||||
private int _currentStep;
|
|
||||||
private Func<Task<bool>> _beforeGoBack;
|
|
||||||
private Func<Task<bool>> _beforeGoForward;
|
|
||||||
|
|
||||||
public NavigationWrapper(NavigationManager uriHelper)
|
|
||||||
{
|
|
||||||
_uriHelper = uriHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CurrentStep
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var uri = _uriHelper.ToBaseRelativePath(_uriHelper.Uri);
|
|
||||||
if (uri == _currentUri)
|
|
||||||
return _currentStep;
|
|
||||||
|
|
||||||
_currentUri = uri;
|
|
||||||
if (!_currentUri.StartsWith("step") || uri.Length < 5)
|
|
||||||
{
|
|
||||||
_currentStep = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_currentStep = int.Parse(uri.Substring(4).First().ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return _currentStep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBackHandler(Func<Task<bool>> handler)
|
|
||||||
{
|
|
||||||
_beforeGoBack = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetForwardHandler(Func<Task<bool>> handler)
|
|
||||||
{
|
|
||||||
_beforeGoForward = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task GoBack()
|
|
||||||
{
|
|
||||||
if (_beforeGoBack != null)
|
|
||||||
{
|
|
||||||
var shouldGoBack = await _beforeGoBack();
|
|
||||||
if (!shouldGoBack)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousStep = CurrentStep - 1;
|
|
||||||
_beforeGoBack = null;
|
|
||||||
_beforeGoForward = null;
|
|
||||||
_uriHelper.NavigateToRelative("/step" + previousStep);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task GoForward()
|
|
||||||
{
|
|
||||||
if (_beforeGoForward != null)
|
|
||||||
{
|
|
||||||
var shouldGoForward = await _beforeGoForward();
|
|
||||||
if (!shouldGoForward)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextStep = CurrentStep + 1;
|
|
||||||
_beforeGoBack = null;
|
|
||||||
_beforeGoForward = null;
|
|
||||||
_uriHelper.NavigateToRelative("/step" + nextStep);
|
|
||||||
}
|
|
||||||
}
|
|
10
NftFaucet/Models/NewFileModel.cs
Normal file
10
NftFaucet/Models/NewFileModel.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
|
public class NewFileModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string FileData { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public long? FileSize { get; set; }
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class ScopedAppState
|
|
||||||
{
|
|
||||||
public ScopedAppState(IpfsBlockchainContext context, MetamaskInfo metamask, NavigationWrapper navigationWrapper)
|
|
||||||
{
|
|
||||||
IpfsContext = context;
|
|
||||||
Metamask = metamask;
|
|
||||||
Navigation = navigationWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IpfsBlockchainContext IpfsContext { get; }
|
|
||||||
public MetamaskInfo Metamask { get; }
|
|
||||||
public NavigationWrapper Navigation { get; }
|
|
||||||
public StateStorage Storage { get; private set; } = new();
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
Storage = new StateStorage();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
using System.Text.RegularExpressions;
|
|
||||||
using CSharpFunctionalExtensions;
|
|
||||||
|
|
||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class SolanaAddress : ValueObject<SolanaAddress>
|
|
||||||
{
|
|
||||||
private SolanaAddress(string value)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public static implicit operator string(SolanaAddress address) => address.Value;
|
|
||||||
public static explicit operator SolanaAddress(string address) => Create(address).Value;
|
|
||||||
|
|
||||||
public static Result<SolanaAddress> Create(string value)
|
|
||||||
{
|
|
||||||
var regex = "^[1-9A-HJ-NP-Za-km-z]{32,44}$";
|
|
||||||
|
|
||||||
if (!Regex.IsMatch(value, regex))
|
|
||||||
{
|
|
||||||
return Result.Failure<SolanaAddress>("Invalid base58 string");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SolanaAddress(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => Value;
|
|
||||||
|
|
||||||
protected override bool EqualsCore(SolanaAddress other)
|
|
||||||
=> string.Equals(Value, other.Value, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
|
|
||||||
protected override int GetHashCodeCore() => Value.GetHashCode(StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
|
39
NftFaucet/Models/SolanaKey.cs
Normal file
39
NftFaucet/Models/SolanaKey.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using Solnet.Wallet;
|
||||||
|
using Solnet.Wallet.Bip39;
|
||||||
|
|
||||||
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
|
public class SolanaKey
|
||||||
|
{
|
||||||
|
public string MnemonicPhrase { get; }
|
||||||
|
public string PrivateKey { get; }
|
||||||
|
public string Address { get; }
|
||||||
|
|
||||||
|
public SolanaKey(string mnemonicPhrase)
|
||||||
|
{
|
||||||
|
MnemonicPhrase = mnemonicPhrase ?? throw new ArgumentNullException(nameof(mnemonicPhrase));
|
||||||
|
PrivateKey = GetPrivateKeyFromMnemonicPhrase(mnemonicPhrase);
|
||||||
|
Address = GetAddressFromMnemonicPhrase(mnemonicPhrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SolanaKey GenerateNew()
|
||||||
|
{
|
||||||
|
var words = new Mnemonic(WordList.English, WordCount.Twelve).Words;
|
||||||
|
var mnemonicPhrase = string.Join(" ", words);
|
||||||
|
return new SolanaKey(mnemonicPhrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetPrivateKeyFromMnemonicPhrase(string mnemonicPhrase)
|
||||||
|
{
|
||||||
|
var mnemonic = new Mnemonic(mnemonicPhrase, WordList.English);
|
||||||
|
var wallet = new Wallet(mnemonic);
|
||||||
|
return wallet.Account.PrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAddressFromMnemonicPhrase(string mnemonicPhrase)
|
||||||
|
{
|
||||||
|
var mnemonic = new Mnemonic(mnemonicPhrase, WordList.English);
|
||||||
|
var wallet = new Wallet(mnemonic);
|
||||||
|
return wallet.Account.PublicKey.Key;
|
||||||
|
}
|
||||||
|
}
|
13
NftFaucet/Models/State/PluginStateStorage.cs
Normal file
13
NftFaucet/Models/State/PluginStateStorage.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using NftFaucet.Plugins.NetworkPlugins;
|
||||||
|
using NftFaucet.Plugins.ProviderPlugins;
|
||||||
|
using NftFaucet.Plugins.UploadPlugins;
|
||||||
|
|
||||||
|
namespace NftFaucet.Models.State;
|
||||||
|
|
||||||
|
public class PluginStateStorage
|
||||||
|
{
|
||||||
|
public ICollection<INetwork> Networks { get; set; }
|
||||||
|
public ICollection<IProvider> Providers { get; set; }
|
||||||
|
public ICollection<IUploader> Uploaders { get; set; }
|
||||||
|
public ICollection<IContract> Contracts { get; set; }
|
||||||
|
}
|
22
NftFaucet/Models/State/ScopedAppState.cs
Normal file
22
NftFaucet/Models/State/ScopedAppState.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using NftFaucet.Plugins;
|
||||||
|
using NftFaucet.Plugins.NetworkPlugins;
|
||||||
|
using NftFaucet.Plugins.ProviderPlugins;
|
||||||
|
|
||||||
|
namespace NftFaucet.Models.State;
|
||||||
|
|
||||||
|
public class ScopedAppState
|
||||||
|
{
|
||||||
|
public PluginStateStorage PluginStorage { get; private set; } = new();
|
||||||
|
public UserStateStorage UserStorage { get; private set; } = new();
|
||||||
|
|
||||||
|
public INetwork SelectedNetwork => PluginStorage?.Networks?.FirstOrDefault(x => x.Id == UserStorage?.SelectedNetworks?.FirstOrDefault());
|
||||||
|
public IProvider SelectedProvider => PluginStorage?.Providers?.FirstOrDefault(x => x.Id == UserStorage?.SelectedProviders?.FirstOrDefault());
|
||||||
|
public IContract SelectedContract => PluginStorage?.Contracts?.FirstOrDefault(x => x.Id == UserStorage?.SelectedContracts?.FirstOrDefault());
|
||||||
|
public IToken SelectedToken => UserStorage?.Tokens?.FirstOrDefault(x => x.Id == UserStorage?.SelectedTokens?.FirstOrDefault());
|
||||||
|
public ITokenUploadLocation SelectedUploadLocation => UserStorage?.UploadLocations?.FirstOrDefault(x => x.Id == UserStorage?.SelectedUploadLocations?.FirstOrDefault());
|
||||||
|
|
||||||
|
public void LoadUserStorage(UserStateStorage userStorage)
|
||||||
|
{
|
||||||
|
UserStorage = userStorage ?? new();
|
||||||
|
}
|
||||||
|
}
|
17
NftFaucet/Models/State/UserStateStorage.cs
Normal file
17
NftFaucet/Models/State/UserStateStorage.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using NftFaucet.Plugins;
|
||||||
|
|
||||||
|
namespace NftFaucet.Models.State;
|
||||||
|
|
||||||
|
public class UserStateStorage
|
||||||
|
{
|
||||||
|
public ICollection<IToken> Tokens { get; set; }
|
||||||
|
public ICollection<ITokenUploadLocation> UploadLocations { get; set; }
|
||||||
|
|
||||||
|
public Guid[] SelectedNetworks { get; set; }
|
||||||
|
public Guid[] SelectedProviders { get; set; }
|
||||||
|
public Guid[] SelectedContracts { get; set; }
|
||||||
|
public Guid[] SelectedTokens { get; set; }
|
||||||
|
public Guid[] SelectedUploadLocations { get; set; }
|
||||||
|
public string DestinationAddress { get; set; }
|
||||||
|
public int TokenAmount { get; set; } = 1;
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
using NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
namespace NftFaucet.Models;
|
|
||||||
|
|
||||||
public class StateStorage
|
|
||||||
{
|
|
||||||
public string TokenName { get; set; }
|
|
||||||
public string TokenDescription { get; set; }
|
|
||||||
public IpfsGatewayType IpfsGatewayType { get; set; } = IpfsGatewayType.Crust;
|
|
||||||
public TokenType TokenType { get; set; } = TokenType.ERC721;
|
|
||||||
public double TokenAmount { get; set; } = 1;
|
|
||||||
public Uri LocalImageUrl { get; set; }
|
|
||||||
public bool CanPreviewTokenFile { get; set; }
|
|
||||||
public bool UploadIsInProgress { get; set; }
|
|
||||||
public Uri IpfsImageUrl { get; set; }
|
|
||||||
public string TokenMetadata { get; set; }
|
|
||||||
public string TokenUrl { get; set; }
|
|
||||||
public string DestinationAddress { get; set; }
|
|
||||||
public NetworkType NetworkType { get; set; }
|
|
||||||
public NetworkChain NetworkChain { get; set; }
|
|
||||||
public string TokenSymbol { get; set; } = "DFNT";
|
|
||||||
public bool IsTokenMutable { get; set; } = true;
|
|
||||||
public double SellerFeeBasisPoints { get; set; } = 88;
|
|
||||||
public bool IncludeMasterEdition { get; set; } = true;
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace NftFaucet.Models.Token;
|
namespace NftFaucet.Models;
|
||||||
|
|
||||||
public class TokenMetadata
|
public class TokenMetadata
|
||||||
{
|
{
|
@ -2,27 +2,26 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AntDesign" Version="0.10.6-alpha.2" />
|
<PackageReference Include="ByteSize" Version="2.1.1" />
|
||||||
<PackageReference Include="BlazorMonaco" Version="2.1.0" />
|
<PackageReference Include="Cryptography.ECDSA.Secp256K1" Version="1.1.3" />
|
||||||
<PackageReference Include="CSharpFunctionalExtensions" Version="2.29.0" />
|
<PackageReference Include="CSharpFunctionalExtensions" Version="2.33.2" />
|
||||||
<PackageReference Include="Ethereum.MetaMask.Blazor" Version="1.0.0" />
|
<PackageReference Include="Ethereum.MetaMask.Blazor" Version="1.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.2" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="Nethereum.ABI" Version="4.7.0" />
|
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
|
||||||
<PackageReference Include="Nethereum.JsonRpc.Client" Version="4.7.0" />
|
<PackageReference Include="Nethereum.ABI" Version="4.8.0" />
|
||||||
<PackageReference Include="RestEase" Version="1.5.5" />
|
<PackageReference Include="Nethereum.JsonRpc.Client" Version="4.8.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.BrowserConsole" Version="1.0.0" />
|
<PackageReference Include="Nethereum.Web3" Version="4.8.0" />
|
||||||
|
<PackageReference Include="Radzen.Blazor" Version="3.20.4" />
|
||||||
|
<PackageReference Include="RestEase" Version="1.5.7" />
|
||||||
<PackageReference Include="Solana.Metaplex" Version="1.2.0" />
|
<PackageReference Include="Solana.Metaplex" Version="1.2.0" />
|
||||||
<PackageReference Include="Toolbelt.Blazor.FileDropZone" Version="1.0.1" />
|
<PackageReference Include="TG.Blazor.IndexedDB" Version="1.5.0-preview" />
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<_ContentIncludedByDefault Remove="wwwroot\sample-data\weather.json" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -31,4 +30,8 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_ContentIncludedByDefault Remove="wwwroot\sample-data\weather.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
using NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
namespace NftFaucet.Options;
|
|
||||||
|
|
||||||
public class IpfsGatewayOptions
|
|
||||||
{
|
|
||||||
public IpfsGatewayType Id { get; set; }
|
|
||||||
public string BaseUrl { get; set; }
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
using NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
namespace NftFaucet.Options;
|
|
||||||
|
|
||||||
public class NetworkOptions
|
|
||||||
{
|
|
||||||
public NetworkChain Id { get; set; }
|
|
||||||
public string Erc721ContractAddress { get; set; }
|
|
||||||
public string Erc1155ContractAddress { get; set; }
|
|
||||||
}
|
|
@ -1,15 +1,8 @@
|
|||||||
using NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
namespace NftFaucet.Options;
|
namespace NftFaucet.Options;
|
||||||
|
|
||||||
public class Settings
|
public class Settings
|
||||||
{
|
{
|
||||||
public NetworkOptions[] Networks { get; set; }
|
public Guid[] RecommendedNetworks { get; set; }
|
||||||
public IpfsGatewayOptions[] IpfsGateways { get; set; }
|
public Guid[] RecommendedProviders { get; set; }
|
||||||
|
public Guid[] RecommendedUploaders { get; set; }
|
||||||
public NetworkOptions GetEthereumNetworkOptions(NetworkChain network)
|
|
||||||
=> Networks.FirstOrDefault(x => x.Id == network);
|
|
||||||
|
|
||||||
public IpfsGatewayOptions GetIpfsGatewayOptions(IpfsGatewayType gateway)
|
|
||||||
=> IpfsGateways.FirstOrDefault(x => x.Id == gateway);
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
@page "/connect-ipfs"
|
|
||||||
@layout EmptyLayout
|
|
||||||
@inherits ConnectIpfsComponent
|
|
||||||
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Horizontal" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem Class="drk-full-height">
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Vertical" Class="drk-vertical-space-center" Style="align-items: center !important;">
|
|
||||||
<SpaceItem Style="height: 50%">
|
|
||||||
<img src="./images/crust.svg" alt="crust" style="height: 100%"/>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="1">@TitleText</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Button Type="@ButtonType.Primary" Size="@ButtonSize.Large" OnClick="Sign">@ButtonText</Button>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
@ -1,43 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Microsoft.JSInterop;
|
|
||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
using NftFaucet.Utils;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class ConnectIpfsComponent : BasicComponent
|
|
||||||
{
|
|
||||||
[Inject]
|
|
||||||
protected IJSRuntime JsRuntime { get; set; }
|
|
||||||
|
|
||||||
protected string TitleText => "In order to use Crust (IPFS provider), please sign the message";
|
|
||||||
protected string ButtonText => "Sign message";
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (await Metamask.IsConnected())
|
|
||||||
{
|
|
||||||
await Metamask.RefreshAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppState.IpfsContext.IsInitialized)
|
|
||||||
{
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task Sign()
|
|
||||||
{
|
|
||||||
var address = Metamask.Address.ToLowerInvariant();
|
|
||||||
var signedMessageResult = await ResultWrapper.Wrap(Metamask.SigningService.SignAsync(address));
|
|
||||||
if (signedMessageResult.IsFailure)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var signedMessage = signedMessageResult.Value;
|
|
||||||
AppState.IpfsContext.Initialize(address, signedMessage);
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
@page "/connect-metamask"
|
|
||||||
@layout EmptyLayout
|
|
||||||
@inherits ConnectMetamaskComponent
|
|
||||||
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Horizontal" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem Class="drk-full-height">
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Vertical" Class="drk-vertical-space-center" Style="align-items: center !important;">
|
|
||||||
<SpaceItem Style="height: 50%">
|
|
||||||
<img src="./images/metamask_fox.svg" alt="metamask" style="height: 100%"/>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="1">@TitleText</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Button Type="@ButtonType.Primary" Size="@ButtonSize.Large" OnClick="Connect">@ButtonText</Button>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
@ -1,43 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Microsoft.JSInterop;
|
|
||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class ConnectMetamaskComponent : BasicComponent
|
|
||||||
{
|
|
||||||
[Inject]
|
|
||||||
protected IJSRuntime JsRuntime { get; set; }
|
|
||||||
|
|
||||||
protected bool HasMetaMask { get; set; }
|
|
||||||
protected string TitleText => $"You should {(HasMetaMask ? "connect" : "install")} MetaMask first";
|
|
||||||
protected string ButtonText => HasMetaMask ? "Connect" : "Install";
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (await Metamask.IsConnected())
|
|
||||||
{
|
|
||||||
await Metamask.RefreshAddress();
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
HasMetaMask = Metamask.HasMetaMask ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task Connect()
|
|
||||||
{
|
|
||||||
if (!HasMetaMask)
|
|
||||||
{
|
|
||||||
string url = "https://metamask.io/download/";
|
|
||||||
await JsRuntime.InvokeAsync<object>("open", url, "_blank");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isConnected = await Metamask.Connect();
|
|
||||||
if (isConnected)
|
|
||||||
{
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
19
NftFaucet/Pages/ContractsPage.razor
Normal file
19
NftFaucet/Pages/ContractsPage.razor
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@page "/contracts"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Contracts</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<RadzenHeading Size="H1" Text="Select contract" />
|
||||||
|
@if (AppState.SelectedNetwork == null)
|
||||||
|
{
|
||||||
|
<RadzenHeading Size="H3" Text="Please choose network first!" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div style="width: 100%; display: flex; flex-direction: row; justify-content: end;">
|
||||||
|
<RadzenButton Text="Deploy New" Icon="add_circle_outline" ButtonStyle="ButtonStyle.Secondary"
|
||||||
|
Click="@(() => NotificationService.Notify(NotificationSeverity.Warning, "NOT IMPLEMENTED", "Will be implemented later"))" />
|
||||||
|
</div>
|
||||||
|
<CardList Data="@ContractCards" OnSelectedChange="@(async _ => await OnContractChange())" @bind-SelectedItems="@AppState.UserStorage.SelectedContracts" />
|
||||||
|
}
|
||||||
|
</RadzenContent>
|
66
NftFaucet/Pages/ContractsPage.razor.cs
Normal file
66
NftFaucet/Pages/ContractsPage.razor.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Components.CardList;
|
||||||
|
using NftFaucet.Plugins.NetworkPlugins;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class ContractsPage : BasicComponent
|
||||||
|
{
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Contracts = AppState.SelectedNetwork?.DeployedContracts?.ToArray() ?? Array.Empty<IContract>();
|
||||||
|
RefreshCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IContract[] Contracts { get; set; }
|
||||||
|
private CardListItem[] ContractCards { get; set; }
|
||||||
|
|
||||||
|
private void RefreshCards()
|
||||||
|
{
|
||||||
|
ContractCards = Contracts.Select(MapCardListItem).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem MapCardListItem(IContract contract)
|
||||||
|
=> new CardListItem
|
||||||
|
{
|
||||||
|
Id = contract.Id,
|
||||||
|
Header = contract.Name,
|
||||||
|
Properties = new[]
|
||||||
|
{
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "Symbol",
|
||||||
|
Value = contract.Symbol,
|
||||||
|
},
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "Address",
|
||||||
|
Value = contract.Address,
|
||||||
|
},
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "TxHash",
|
||||||
|
Value = contract.DeploymentTxHash ?? "<unknown>",
|
||||||
|
},
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "DeployedAt",
|
||||||
|
Value = contract.DeployedAt?.ToString(CultureInfo.InvariantCulture) ?? "<unknown>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Badges = new[]
|
||||||
|
{
|
||||||
|
contract.IsVerified
|
||||||
|
? new CardListItemBadge {Style = BadgeStyle.Success, Text = "Verified"}
|
||||||
|
: null,
|
||||||
|
new CardListItemBadge {Style = BadgeStyle.Light, Text = contract.Type.ToString()},
|
||||||
|
}.Where(x => x != null).ToArray(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task OnContractChange()
|
||||||
|
{
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
}
|
25
NftFaucet/Pages/CreateTokenDialog.razor
Normal file
25
NftFaucet/Pages/CreateTokenDialog.razor
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
@page "/tokens/new"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Create token</PageTitle>
|
||||||
|
|
||||||
|
<form onsubmit="@OnSavePressed">
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4>Image</h4>
|
||||||
|
<RadzenFileInput @bind-Value=@Model.FileData @bind-FileName=@Model.FileName @bind-FileSize=@Model.FileSize TValue="string" Class="w-100"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4>Name</h4>
|
||||||
|
<RadzenTextBox Placeholder="YING #668" MaxLength="50" @bind-Value="@Model.Name" Class="w-100"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4>Description</h4>
|
||||||
|
<RadzenTextArea Placeholder="YING, ecotype enterprises IP of Inkeverse and the avatar spokesperson for Inkeverse." MaxLength="250" @bind-Value="@Model.Description" Class="w-100"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@((args) => OnSavePressed())" Disabled="!ModelIsValid" Text="Save" Style="width: 120px"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
65
NftFaucet/Pages/CreateTokenDialog.razor.cs
Normal file
65
NftFaucet/Pages/CreateTokenDialog.razor.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using MimeTypes;
|
||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Models;
|
||||||
|
using NftFaucet.Plugins;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class CreateTokenDialog : BasicComponent
|
||||||
|
{
|
||||||
|
private NewFileModel Model { get; set; } = new NewFileModel();
|
||||||
|
private bool ModelIsValid => IsValid();
|
||||||
|
|
||||||
|
private void OnSavePressed()
|
||||||
|
{
|
||||||
|
if (!IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var token = new Token
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Name = Model.Name,
|
||||||
|
Description = Model.Description,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
Image = new TokenMedia
|
||||||
|
{
|
||||||
|
FileName = Model.FileName,
|
||||||
|
FileType = DetermineFileType(Model.FileName),
|
||||||
|
FileData = Model.FileData,
|
||||||
|
FileSize = Model.FileSize!.Value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
DialogService.Close(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DetermineFileType(string fileName)
|
||||||
|
{
|
||||||
|
var extension = fileName.Split('.', StringSplitOptions.RemoveEmptyEntries).Last();
|
||||||
|
if (!MimeTypeMap.TryGetMimeType(extension, out var mimeType))
|
||||||
|
{
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValid()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Model.Name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Model.Description))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Model.FileData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Model.FileName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Model.FileSize is null or 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
14
NftFaucet/Pages/CreateUploadDialog.razor
Normal file
14
NftFaucet/Pages/CreateUploadDialog.razor
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
@page "/uploads/new"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Create upload</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<h3>Select uploader</h3>
|
||||||
|
<CardList Data="@UploaderCards" @bind-SelectedItems="@SelectedUploaderIds"/>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 text-right">
|
||||||
|
<RadzenButton Text="Cancel" Click="@(args => DialogService.Close())" ButtonStyle="ButtonStyle.Secondary" Disabled="IsUploading" Style="width: 120px" Class="mr-1"/>
|
||||||
|
<RadzenButton Text="Upload" Icon="eject" BusyText="Uploading..." IsBusy=@IsUploading Click="@(async args => await OnSavePressed())" Disabled="@(SelectedUploader == null || !SelectedUploader.IsConfigured)" Style="width: 180px"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</RadzenContent>
|
132
NftFaucet/Pages/CreateUploadDialog.razor.cs
Normal file
132
NftFaucet/Pages/CreateUploadDialog.razor.cs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Components.CardList;
|
||||||
|
using NftFaucet.Models;
|
||||||
|
using NftFaucet.Plugins;
|
||||||
|
using NftFaucet.Plugins.UploadPlugins;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class CreateUploadDialog : BasicComponent
|
||||||
|
{
|
||||||
|
[Parameter] public IToken Token { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
RefreshCards();
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem[] UploaderCards { get; set; }
|
||||||
|
private Guid[] SelectedUploaderIds { get; set; }
|
||||||
|
private IUploader SelectedUploader => AppState?.PluginStorage?.Uploaders?.FirstOrDefault(x => x.Id == SelectedUploaderIds?.FirstOrDefault());
|
||||||
|
private bool IsUploading { get; set; }
|
||||||
|
|
||||||
|
private void RefreshCards()
|
||||||
|
{
|
||||||
|
UploaderCards = AppState.PluginStorage.Uploaders.Select(MapCardListItem).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem MapCardListItem(IUploader uploader)
|
||||||
|
{
|
||||||
|
var configuration = uploader.GetConfiguration();
|
||||||
|
return new CardListItem
|
||||||
|
{
|
||||||
|
Id = uploader.Id,
|
||||||
|
ImageLocation = uploader.ImageName != null ? "./images/" + uploader.ImageName : null,
|
||||||
|
Header = uploader.Name,
|
||||||
|
Properties = uploader.GetProperties(),
|
||||||
|
IsDisabled = !uploader.IsSupported,
|
||||||
|
SelectionIcon = uploader.IsConfigured ? CardListItemSelectionIcon.Checkmark : CardListItemSelectionIcon.Warning,
|
||||||
|
Badges = new[]
|
||||||
|
{
|
||||||
|
(Settings?.RecommendedUploaders?.Contains(uploader.Id) ?? false)
|
||||||
|
? new CardListItemBadge {Style = BadgeStyle.Success, Text = "Recommended"}
|
||||||
|
: null,
|
||||||
|
!uploader.IsSupported
|
||||||
|
? new CardListItemBadge {Style = BadgeStyle.Light, Text = "Not Supported"}
|
||||||
|
: null,
|
||||||
|
}.Where(x => x != null).ToArray(),
|
||||||
|
Configuration = configuration == null
|
||||||
|
? null
|
||||||
|
: new CardListItemConfiguration
|
||||||
|
{
|
||||||
|
Objects = configuration.Objects,
|
||||||
|
ConfigureAction = async x =>
|
||||||
|
{
|
||||||
|
var result = await configuration.ConfigureAction(x);
|
||||||
|
RefreshCards();
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
await StateRepository.SaveUploaderState(uploader);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSavePressed()
|
||||||
|
{
|
||||||
|
IsUploading = true;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
|
||||||
|
var imageLocationResult = await SelectedUploader.Upload(Token.Image.FileName, Token.Image.FileType, Base64DataToBytes(Token.Image.FileData));
|
||||||
|
if (imageLocationResult.IsFailure)
|
||||||
|
{
|
||||||
|
IsUploading = false;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
NotificationService.Notify(NotificationSeverity.Error, "Uploading image failed", imageLocationResult.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var imageLocation = imageLocationResult.Value;
|
||||||
|
var tokenMetadata = GenerateTokenMetadata(Token, imageLocation);
|
||||||
|
var tokenMetadataBytes = Encoding.UTF8.GetBytes(tokenMetadata);
|
||||||
|
var tokenLocationResult = await SelectedUploader.Upload($"{Token.Id}.json", "application/json", tokenMetadataBytes);
|
||||||
|
if (tokenLocationResult.IsFailure)
|
||||||
|
{
|
||||||
|
IsUploading = false;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
NotificationService.Notify(NotificationSeverity.Error, "Uploading metadata failed", tokenLocationResult.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsUploading = false;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
NotificationService.Notify(NotificationSeverity.Success, "Upload succeeded", tokenLocationResult.Value.OriginalString);
|
||||||
|
var uploadLocation = new TokenUploadLocation
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
TokenId = Token.Id,
|
||||||
|
Name = SelectedUploader.ShortName,
|
||||||
|
Location = tokenLocationResult.Value.OriginalString,
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
UploaderId = SelectedUploader.Id,
|
||||||
|
};
|
||||||
|
DialogService.Close(uploadLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] Base64DataToBytes(string fileData)
|
||||||
|
{
|
||||||
|
var index = fileData.IndexOf(';');
|
||||||
|
var encoded = fileData.Substring(index + 8);
|
||||||
|
return Convert.FromBase64String(encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateTokenMetadata(IToken token, Uri imageLocation)
|
||||||
|
{
|
||||||
|
var tokenMetadata = new TokenMetadata
|
||||||
|
{
|
||||||
|
Name = token.Name,
|
||||||
|
Description = token.Description,
|
||||||
|
Image = imageLocation.OriginalString,
|
||||||
|
ExternalUrl = "https://darkcodi.github.io/nft-faucet/",
|
||||||
|
};
|
||||||
|
var metadataJson = JsonConvert.SerializeObject(tokenMetadata, Formatting.Indented);
|
||||||
|
return metadataJson;
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class IndexComponent : BasicComponent
|
|
||||||
{
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (!await Metamask.IsReady())
|
|
||||||
{
|
|
||||||
UriHelper.NavigateToRelative("/connect-metamask");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AppState.IpfsContext.IsInitialized)
|
|
||||||
{
|
|
||||||
UriHelper.NavigateToRelative("/connect-ipfs");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UriHelper.NavigateToRelative("/step1");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@layout EmptyLayout
|
@layout EmptyLayout
|
||||||
@inherits IndexComponent
|
@inherits BasicComponent
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="loading">Loading...</div>
|
<div class="loading">Loading...</div>
|
24
NftFaucet/Pages/IndexPage.razor.cs
Normal file
24
NftFaucet/Pages/IndexPage.razor.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Extensions;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class IndexPage : BasicComponent
|
||||||
|
{
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
// if (!await Metamask.IsReady())
|
||||||
|
// {
|
||||||
|
// UriHelper.NavigateToRelative("/connect-metamask");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!AppState.IpfsContext.IsInitialized)
|
||||||
|
// {
|
||||||
|
// UriHelper.NavigateToRelative("/connect-ipfs");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
NavigationManager.NavigateToRelative("/networks");
|
||||||
|
}
|
||||||
|
}
|
93
NftFaucet/Pages/MintDialog.razor
Normal file
93
NftFaucet/Pages/MintDialog.razor
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
@page "/mint/in-progress"
|
||||||
|
@using NftFaucet.Models
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Minting...</PageTitle>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(ProgressBarText))
|
||||||
|
{
|
||||||
|
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">@ProgressBarText</RadzenText>
|
||||||
|
<RadzenProgressBar Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate" />
|
||||||
|
}
|
||||||
|
else if (State == MintingState.CheckingNetwork)
|
||||||
|
{
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<h4>Network mismatch!</h4>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Selected network: </text>
|
||||||
|
<text style="color: green;">@(AppState?.SelectedNetwork?.Name ?? "<unknown>")</text>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Provider network: </text>
|
||||||
|
<text style="color: red;">@(ProviderNetwork?.Name ?? "<unknown>")</text>
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@(async (args) => await CheckNetwork())" Text="Try again" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" />
|
||||||
|
</div>
|
||||||
|
</RadzenContent>
|
||||||
|
} else if (State == MintingState.CheckingAddress)
|
||||||
|
{
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<h3>Address is not available!</h3>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Provider address: </text>
|
||||||
|
<text style="color: red;"><null></text>
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@(async (args) => await CheckNetwork())" Text="Try again" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" />
|
||||||
|
</div>
|
||||||
|
</RadzenContent>
|
||||||
|
} else if (State == MintingState.CheckingBalance)
|
||||||
|
{
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<h3>Not enough balance!</h3>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Balance</text>
|
||||||
|
<text> (@SourceAddress)</text>
|
||||||
|
<text style="font-weight: bold;">: </text>
|
||||||
|
<text style="color: red;">
|
||||||
|
@if (Balance != null)
|
||||||
|
{
|
||||||
|
@(Balance.Amount + " " + Balance.Currency)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:<unknown>
|
||||||
|
}
|
||||||
|
</text>
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@(async (args) => await CheckNetwork())" Text="Try again" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" />
|
||||||
|
</div>
|
||||||
|
</RadzenContent>
|
||||||
|
} else if (State == MintingState.SendingTransaction)
|
||||||
|
{
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<h3>Failed to send transaction!</h3>
|
||||||
|
<p>
|
||||||
|
<text>Error message:</text>
|
||||||
|
<br/>
|
||||||
|
<text style="font-weight: bold; color: red;">@SendTransactionError</text>
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@(async (args) => await CheckNetwork())" Text="Try again" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" />
|
||||||
|
</div>
|
||||||
|
</RadzenContent>
|
||||||
|
} else if (State == MintingState.Done)
|
||||||
|
{
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<h3>Success</h3>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Token minted!</text>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<text style="font-weight: bold;">Transaction hash: </text>
|
||||||
|
<br/>
|
||||||
|
<RadzenLink Icon="open_in_new" Path="@(new Uri(AppState.SelectedNetwork.ExplorerUrl, "tx/" + TransactionHash).ToString())" Text="@TransactionHash" Target="_blank"/>
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; justify-content: end;">
|
||||||
|
<RadzenButton Click="@(async (args) => await Close())" Text="Close" Disabled="@(!string.IsNullOrEmpty(ProgressBarText))" />
|
||||||
|
</div>
|
||||||
|
</RadzenContent>
|
||||||
|
}
|
146
NftFaucet/Pages/MintDialog.razor.cs
Normal file
146
NftFaucet/Pages/MintDialog.razor.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Models;
|
||||||
|
using NftFaucet.Plugins.NetworkPlugins;
|
||||||
|
using NftFaucet.Utils;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class MintDialog : BasicComponent
|
||||||
|
{
|
||||||
|
private const int MinDelayInMilliseconds = 300;
|
||||||
|
|
||||||
|
private string ProgressBarText { get; set; } = "Checking network...";
|
||||||
|
private MintingState State { get; set; } = MintingState.CheckingNetwork;
|
||||||
|
private INetwork ProviderNetwork { get; set; }
|
||||||
|
private string SourceAddress { get; set; }
|
||||||
|
private Balance Balance { get; set; }
|
||||||
|
private string TransactionHash { get; set; }
|
||||||
|
private string SendTransactionError { get; set; }
|
||||||
|
|
||||||
|
protected override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
CheckNetwork();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckNetwork()
|
||||||
|
{
|
||||||
|
State = MintingState.CheckingNetwork;
|
||||||
|
ProgressBarText = "Checking network...";
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
|
||||||
|
var providerNetworkResult = await ResultWrapper.Wrap(async () =>
|
||||||
|
{
|
||||||
|
var task1 = AppState.SelectedProvider.GetNetwork(AppState.PluginStorage.Networks.ToArray(), AppState.SelectedNetwork);
|
||||||
|
var task2 = Task.Delay(TimeSpan.FromMilliseconds(MinDelayInMilliseconds));
|
||||||
|
await Task.WhenAll(task1, task2);
|
||||||
|
return task1.Result;
|
||||||
|
});
|
||||||
|
ProviderNetwork = providerNetworkResult.IsSuccess ? providerNetworkResult.Value : null;
|
||||||
|
|
||||||
|
if (ProviderNetwork?.Id == AppState.SelectedNetwork.Id)
|
||||||
|
{
|
||||||
|
CheckAddress();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProgressBarText = null;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckAddress()
|
||||||
|
{
|
||||||
|
State = MintingState.CheckingAddress;
|
||||||
|
ProgressBarText = "Checking address...";
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
|
||||||
|
var sourceAddressResult = await ResultWrapper.Wrap(async () =>
|
||||||
|
{
|
||||||
|
var task1 = AppState.SelectedProvider.GetAddress();
|
||||||
|
var task2 = Task.Delay(TimeSpan.FromMilliseconds(MinDelayInMilliseconds));
|
||||||
|
await Task.WhenAll(task1, task2);
|
||||||
|
return task1.Result;
|
||||||
|
});
|
||||||
|
SourceAddress = sourceAddressResult.IsSuccess ? sourceAddressResult.Value : null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(SourceAddress))
|
||||||
|
{
|
||||||
|
CheckBalance();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProgressBarText = null;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckBalance()
|
||||||
|
{
|
||||||
|
State = MintingState.CheckingBalance;
|
||||||
|
ProgressBarText = "Checking balance...";
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
|
||||||
|
var balanceResult = await ResultWrapper.Wrap(async () =>
|
||||||
|
{
|
||||||
|
var task1 = AppState.SelectedProvider.GetBalance(AppState.SelectedNetwork);
|
||||||
|
var task2 = Task.Delay(TimeSpan.FromMilliseconds(MinDelayInMilliseconds));
|
||||||
|
await Task.WhenAll(task1, task2);
|
||||||
|
return task1.Result;
|
||||||
|
});
|
||||||
|
Balance = balanceResult.IsSuccess ? balanceResult.Value : null;
|
||||||
|
var amount = Math.Max(Balance?.Amount ?? 0, 0);
|
||||||
|
|
||||||
|
if (amount != 0)
|
||||||
|
{
|
||||||
|
SendTransaction();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProgressBarText = null;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendTransaction()
|
||||||
|
{
|
||||||
|
State = MintingState.SendingTransaction;
|
||||||
|
ProgressBarText = "Sending transaction...";
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
|
||||||
|
var sendTransactionResult = await ResultWrapper.Wrap(async () =>
|
||||||
|
{
|
||||||
|
var mintRequest = new MintRequest(AppState.SelectedNetwork, AppState.SelectedProvider,
|
||||||
|
AppState.SelectedContract, AppState.SelectedToken, AppState.SelectedUploadLocation,
|
||||||
|
AppState.UserStorage.DestinationAddress, AppState.UserStorage.TokenAmount);
|
||||||
|
var task1 = AppState.SelectedProvider.Mint(mintRequest);
|
||||||
|
var task2 = Task.Delay(TimeSpan.FromMilliseconds(MinDelayInMilliseconds));
|
||||||
|
await Task.WhenAll(task1, task2);
|
||||||
|
return task1.Result;
|
||||||
|
});
|
||||||
|
if (sendTransactionResult.IsSuccess)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(sendTransactionResult.Value))
|
||||||
|
{
|
||||||
|
TransactionHash = sendTransactionResult.Value;
|
||||||
|
State = MintingState.Done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendTransactionError = "Tx hash is null or empty";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendTransactionError = sendTransactionResult.Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBarText = null;
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Close()
|
||||||
|
{
|
||||||
|
DialogService.Close(TransactionHash);
|
||||||
|
}
|
||||||
|
}
|
82
NftFaucet/Pages/MintPage.razor
Normal file
82
NftFaucet/Pages/MintPage.razor
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
@page "/mint"
|
||||||
|
@using NftFaucet.Plugins.NetworkPlugins
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Mint</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<RadzenHeading Size="H1" Text="Mint" />
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Network: </text>
|
||||||
|
@if (AppState?.SelectedNetwork == null)
|
||||||
|
{
|
||||||
|
@:<text style="color: red;">NOT SELECTED</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:<text style="color: green;">OK</text>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Provider: </text>
|
||||||
|
@if (AppState?.SelectedProvider == null)
|
||||||
|
{
|
||||||
|
@:<text style="color: red;">NOT SELECTED</text>
|
||||||
|
}
|
||||||
|
else if (!AppState.SelectedProvider.IsConfigured)
|
||||||
|
{
|
||||||
|
@:<text style="color: red;">NOT CONFIGURED</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:<text style="color: green;">OK</text>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Contract: </text>
|
||||||
|
@if (AppState?.SelectedContract == null)
|
||||||
|
{
|
||||||
|
@:<text style="color: red;">NOT SELECTED</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:<text style="color: green;">OK</text>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Token: </text>
|
||||||
|
@if (AppState?.SelectedToken == null)
|
||||||
|
{
|
||||||
|
@:<text style="color: red;">NOT SELECTED</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:<text style="color: green;">OK</text>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<text style="font-weight: bold;">Upload location: </text>
|
||||||
|
@if (AppState?.SelectedUploadLocation == null)
|
||||||
|
{
|
||||||
|
@:<text style="color: red;">NOT SELECTED</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@:<text style="color: green;">OK</text>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
@if (IsReadyToMint)
|
||||||
|
{
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4>Destination address</h4>
|
||||||
|
<RadzenTextBox Placeholder="<null>" @bind-Value="@AppState.UserStorage.DestinationAddress" Class="w-100"/>
|
||||||
|
</div>
|
||||||
|
if (AppState!.SelectedContract!.Type == ContractType.Erc1155)
|
||||||
|
{
|
||||||
|
<div class="mb-4">
|
||||||
|
<h4>Tokens amount</h4>
|
||||||
|
<RadzenNumeric TValue="int" Min="1" Max="100000" @bind-Value="@AppState.UserStorage.TokenAmount" Class="w-100" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<RadzenButton Text="Mint" Disabled="@string.IsNullOrEmpty(AppState.UserStorage.DestinationAddress)" Click="@(async () => await Mint())" />
|
||||||
|
}
|
||||||
|
</RadzenContent>
|
35
NftFaucet/Pages/MintPage.razor.cs
Normal file
35
NftFaucet/Pages/MintPage.razor.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using CSharpFunctionalExtensions;
|
||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Utils;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class MintPage : BasicComponent
|
||||||
|
{
|
||||||
|
private string SourceAddress { get; set; }
|
||||||
|
private bool IsReadyToMint => AppState != null &&
|
||||||
|
AppState.SelectedNetwork != null &&
|
||||||
|
AppState.SelectedProvider != null &&
|
||||||
|
AppState.SelectedProvider.IsConfigured &&
|
||||||
|
AppState.SelectedContract != null &&
|
||||||
|
AppState.SelectedToken != null &&
|
||||||
|
AppState.SelectedUploadLocation != null &&
|
||||||
|
AppState.UserStorage.DestinationAddress != null;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
if (AppState?.SelectedProvider?.IsConfigured ?? false)
|
||||||
|
{
|
||||||
|
SourceAddress = await ResultWrapper.Wrap(() => AppState.SelectedProvider.GetAddress()).Match(x => x, _ => null);
|
||||||
|
AppState.UserStorage.DestinationAddress = SourceAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Mint()
|
||||||
|
{
|
||||||
|
await DialogService.OpenAsync<MintDialog>("Minting...",
|
||||||
|
new Dictionary<string, object>(),
|
||||||
|
new DialogOptions() { Width = "700px", Height = "570px", Resizable = true, Draggable = true });
|
||||||
|
}
|
||||||
|
}
|
22
NftFaucet/Pages/NetworksPage.razor
Normal file
22
NftFaucet/Pages/NetworksPage.razor
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@page "/networks"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Networks</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<RadzenHeading Size="H1" Text="Select network" />
|
||||||
|
<RadzenTabs RenderMode="TabRenderMode.Client">
|
||||||
|
<Tabs>
|
||||||
|
@if (Networks != null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in Networks.OrderBy(x => x.Key))
|
||||||
|
{
|
||||||
|
var networkType = kvp.Key;
|
||||||
|
var networkCards = kvp.Value;
|
||||||
|
<RadzenTabsItem Text="@networkType.ToString()">
|
||||||
|
<CardList Data="@networkCards" OnSelectedChange="@(async _ => await OnNetworkChange())" @bind-SelectedItems="@AppState.UserStorage.SelectedNetworks"/>
|
||||||
|
</RadzenTabsItem>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</Tabs>
|
||||||
|
</RadzenTabs>
|
||||||
|
</RadzenContent>
|
55
NftFaucet/Pages/NetworksPage.razor.cs
Normal file
55
NftFaucet/Pages/NetworksPage.razor.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Components.CardList;
|
||||||
|
using NftFaucet.Plugins.NetworkPlugins;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class NetworksPage : BasicComponent
|
||||||
|
{
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Networks = AppState.PluginStorage.Networks
|
||||||
|
.GroupBy(x => x.SubType)
|
||||||
|
.ToDictionary(x => x.Key, x => x.OrderBy(v => v.Order ?? int.MaxValue).Select(MapCardListItem).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<NetworkSubtype, CardListItem[]> Networks { get; set; }
|
||||||
|
|
||||||
|
private CardListItem MapCardListItem(INetwork model)
|
||||||
|
=> new CardListItem
|
||||||
|
{
|
||||||
|
Id = model.Id,
|
||||||
|
ImageLocation = model.ImageName != null ? "./images/" + model.ImageName : null,
|
||||||
|
Header = model.Name,
|
||||||
|
IsDisabled = !model.IsSupported,
|
||||||
|
Properties = new[]
|
||||||
|
{
|
||||||
|
new CardListItemProperty { Name = "ChainID", Value = model.ChainId?.ToString() },
|
||||||
|
new CardListItemProperty { Name = "Currency", Value = model.Currency },
|
||||||
|
},
|
||||||
|
Badges = new[]
|
||||||
|
{
|
||||||
|
(Settings?.RecommendedNetworks?.Contains(model.Id) ?? false)
|
||||||
|
? new CardListItemBadge {Style = BadgeStyle.Success, Text = "Recommended"}
|
||||||
|
: null,
|
||||||
|
!model.IsSupported ? new CardListItemBadge { Style = BadgeStyle.Light, Text = "Not Supported" } : null,
|
||||||
|
!model.IsTestnet ? new CardListItemBadge { Style = BadgeStyle.Danger, Text = "Mainnet" } : null,
|
||||||
|
model.IsDeprecated ? new CardListItemBadge { Style = BadgeStyle.Warning, Text = "Deprecated" } : null,
|
||||||
|
}.Where(x => x != null).ToArray(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task OnNetworkChange()
|
||||||
|
{
|
||||||
|
var currentNetwork = AppState.SelectedNetwork;
|
||||||
|
AppState.UserStorage.SelectedContracts = Array.Empty<Guid>();
|
||||||
|
|
||||||
|
var currentProvider = AppState.SelectedProvider;
|
||||||
|
if (currentNetwork == null || (currentProvider != null && !currentProvider.IsNetworkSupported(currentNetwork)))
|
||||||
|
{
|
||||||
|
AppState.UserStorage.SelectedProviders = Array.Empty<Guid>();
|
||||||
|
}
|
||||||
|
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
}
|
15
NftFaucet/Pages/ProvidersPage.razor
Normal file
15
NftFaucet/Pages/ProvidersPage.razor
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@page "/providers"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Providers</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<RadzenHeading Size="H1" Text="Select network provider" />
|
||||||
|
@if (AppState.SelectedNetwork == null)
|
||||||
|
{
|
||||||
|
<RadzenHeading Size="H3" Text="Please choose network first!" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<CardList Data="@ProviderCards" OnSelectedChange="@(async _ => await OnProviderChange())" @bind-SelectedItems="@AppState.UserStorage.SelectedProviders" />
|
||||||
|
}
|
||||||
|
</RadzenContent>
|
67
NftFaucet/Pages/ProvidersPage.razor.cs
Normal file
67
NftFaucet/Pages/ProvidersPage.razor.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Components.CardList;
|
||||||
|
using NftFaucet.Plugins.ProviderPlugins;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class ProvidersPage : BasicComponent
|
||||||
|
{
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Providers = AppState.PluginStorage.Providers.Where(x => AppState.SelectedNetwork != null && x.IsNetworkSupported(AppState.SelectedNetwork)).ToArray();
|
||||||
|
RefreshCards();
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IProvider[] Providers { get; set; }
|
||||||
|
private CardListItem[] ProviderCards { get; set; }
|
||||||
|
|
||||||
|
private void RefreshCards()
|
||||||
|
{
|
||||||
|
ProviderCards = Providers.Select(MapCardListItem).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem MapCardListItem(IProvider provider)
|
||||||
|
{
|
||||||
|
var configuration = provider.GetConfiguration();
|
||||||
|
return new CardListItem
|
||||||
|
{
|
||||||
|
Id = provider.Id,
|
||||||
|
ImageLocation = provider.ImageName != null ? "./images/" + provider.ImageName : null,
|
||||||
|
Header = provider.Name,
|
||||||
|
IsDisabled = !provider.IsSupported,
|
||||||
|
Properties = provider.GetProperties().ToArray(),
|
||||||
|
SelectionIcon = provider.IsConfigured ? CardListItemSelectionIcon.Checkmark : CardListItemSelectionIcon.Warning,
|
||||||
|
Badges = new[]
|
||||||
|
{
|
||||||
|
(Settings?.RecommendedProviders?.Contains(provider.Id) ?? false)
|
||||||
|
? new CardListItemBadge {Style = BadgeStyle.Success, Text = "Recommended"}
|
||||||
|
: null,
|
||||||
|
!provider.IsSupported
|
||||||
|
? new CardListItemBadge {Style = BadgeStyle.Light, Text = "Not Supported"}
|
||||||
|
: null,
|
||||||
|
}.Where(x => x != null).ToArray(),
|
||||||
|
Configuration = configuration == null ? null : new CardListItemConfiguration
|
||||||
|
{
|
||||||
|
Objects = configuration.Objects,
|
||||||
|
ConfigureAction = async x =>
|
||||||
|
{
|
||||||
|
var result = await configuration.ConfigureAction(x);
|
||||||
|
RefreshCards();
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
await StateRepository.SaveProviderState(provider);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnProviderChange()
|
||||||
|
{
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
@page "/step1"
|
|
||||||
@using NftFaucet.Models.Enums
|
|
||||||
@using Microsoft.AspNetCore.Components
|
|
||||||
@inherits Step1Component
|
|
||||||
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Vertical" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Direction="DirectionVHType.Horizontal" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem>
|
|
||||||
<Button Type="@ButtonType.Primary" Style="width: 150px; height: 100px" Size="large" OnClick="OnEthereumSelected">
|
|
||||||
<div>Ethereum</div>
|
|
||||||
</Button>
|
|
||||||
<Button Type="@ButtonType.Primary" Style="width: 150px; height: 100px" Color="Color.Red5" Size="large" OnClick="OnSolanaSelected">
|
|
||||||
<div>Solana</div>
|
|
||||||
</Button>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
<Space Direction="DirectionVHType.Horizontal" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem>
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
<Select DataSource="@ChainTypes"
|
|
||||||
DefaultValue="@(nameof(NetworkChain.SolanaDevnet))"
|
|
||||||
ValueName="@nameof(EnumWrapper<NetworkChain>.ValueString)"
|
|
||||||
LabelName="@nameof(EnumWrapper<NetworkChain>.Description)"
|
|
||||||
OnSelectedItemChanged="OnNetworkChange">
|
|
||||||
</Select>
|
|
||||||
}
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
|||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class Step1Component : BasicComponent
|
|
||||||
{
|
|
||||||
protected EnumWrapper<NetworkType>[] NetworkTypes { get; } = Enum.GetValues<NetworkType>()
|
|
||||||
.Select(x => new EnumWrapper<NetworkType>(x, x.ToString())).ToArray();
|
|
||||||
|
|
||||||
protected EnumWrapper<NetworkChain>[] ChainTypes { get; } = new List<NetworkChain>() {NetworkChain.SolanaTestnet, NetworkChain.SolanaDevnet, NetworkChain.SolanaMainnet}
|
|
||||||
.Select(x => new EnumWrapper<NetworkChain>(x, x.ToString())).ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
protected void OnEthereumSelected()
|
|
||||||
{
|
|
||||||
AppState.Storage.NetworkType = NetworkType.Ethereum;
|
|
||||||
|
|
||||||
AppState.Navigation.GoForward();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnSolanaSelected()
|
|
||||||
{
|
|
||||||
AppState.Storage.NetworkType = NetworkType.Solana;
|
|
||||||
AppState.Storage.NetworkChain = NetworkChain.SolanaDevnet;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnNetworkChange(EnumWrapper<NetworkChain> network)
|
|
||||||
{
|
|
||||||
AppState.Storage.NetworkChain = network.Value;
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
AppState.Navigation.GoForward();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
@page "/step2"
|
|
||||||
@using NftFaucet.Models.Enums
|
|
||||||
@using Microsoft.AspNetCore.Components
|
|
||||||
@inherits Step2Component
|
|
||||||
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Vertical" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Direction="DirectionVHType.Horizontal" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem>
|
|
||||||
<FileDropZone class="drop-zone">
|
|
||||||
<Upload Name="file" Class="@ImageClass" ListType="picture-card"
|
|
||||||
ShowUploadList="false" BeforeAllUploadAsync="BeforeUpload">
|
|
||||||
@if (AppState?.Storage?.UploadIsInProgress ?? false)
|
|
||||||
{
|
|
||||||
<div>
|
|
||||||
<Icon Spin="true" Type="loading" Style="font-size: 2rem;"></Icon>
|
|
||||||
<Title Level="4">Uploading...</Title>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else if (AppState?.Storage?.LocalImageUrl != null && AppState.Storage.CanPreviewTokenFile)
|
|
||||||
{
|
|
||||||
<img src="@AppState?.Storage?.LocalImageUrl" alt="avatar" style="width: 100%"/>
|
|
||||||
}
|
|
||||||
else if (AppState?.Storage?.LocalImageUrl != null)
|
|
||||||
{
|
|
||||||
<div>
|
|
||||||
<Icon Type="eye-invisible" Style="font-size: 2rem;"></Icon>
|
|
||||||
<Title Level="4">Unable to preview</Title>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div>
|
|
||||||
<Icon Type="plus" Style="font-size: 2rem;"></Icon>
|
|
||||||
<Title Level="4">Choose or drag & drop file</Title>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Upload>
|
|
||||||
</FileDropZone>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-grow">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-vertical-space">
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Name:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<div class="@NameClass">
|
|
||||||
<Input Size="medium" @bind-Value="@AppState.Storage.TokenName" OnInput="@OnNameInputChange" />
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Description:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<div class="@DescriptionClass">
|
|
||||||
<TextArea ShowCount MaxLength="255" MinRows="3" MaxRows="5" OnInput="@OnDescriptionInputChange" @bind-Value="@AppState.Storage.TokenDescription" />
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Token symbol:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<div class="@SymbolClass">
|
|
||||||
<Input Size="medium" @bind-Value="@AppState.Storage.TokenSymbol" OnInput="@OnSymbolInputChange" />
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">IPFS Gateway:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Select DataSource="@IpfsGateways"
|
|
||||||
DefaultValue="@(nameof(IpfsGatewayType.Crust))"
|
|
||||||
ValueName="@nameof(EnumWrapper<IpfsGatewayType>.ValueString)"
|
|
||||||
LabelName="@nameof(EnumWrapper<IpfsGatewayType>.Description)"
|
|
||||||
OnSelectedItemChanged="OnIpfsGatewayChange">
|
|
||||||
</Select>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Ethereum)
|
|
||||||
{
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Token type:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Select DataSource="@TokenTypes"
|
|
||||||
DefaultValue="@(nameof(TokenType.ERC721))"
|
|
||||||
ValueName="@nameof(EnumWrapper<TokenType>.ValueString)"
|
|
||||||
LabelName="@nameof(EnumWrapper<TokenType>.Description)"
|
|
||||||
OnSelectedItemChanged="OnTokenTypeChange">
|
|
||||||
</Select>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Horizontal" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Include master edition:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Popover Title="Warning" Content="Notice that for master edition token amount should always be 1">
|
|
||||||
<Checkbox Style="margin-top: auto; margin-bottom: auto" @bind-Value="AppState.Storage.IncludeMasterEdition" OnChange="OnMasterEditionCheck"/>
|
|
||||||
</Popover>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Horizontal" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Is token mutable:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Checkbox Style="margin-top: auto; margin-bottom: auto" @bind-Value="AppState.Storage.IsTokenMutable"/>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Seller fee basis points:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<div>
|
|
||||||
<AntDesign.InputNumber @bind-Value="@AppState.Storage.SellerFeeBasisPoints" Min="1" Max="10000" DefaultValue="88"></AntDesign.InputNumber>
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Amount:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<div>
|
|
||||||
<AntDesign.InputNumber @bind-Value="@AppState.Storage.TokenAmount" Disabled="@((AppState.Storage.NetworkType == NetworkType.Ethereum
|
|
||||||
&& AppState.Storage.TokenType == TokenType.ERC721) ||
|
|
||||||
(AppState.Storage.NetworkType == NetworkType.Solana &&
|
|
||||||
AppState.Storage.IncludeMasterEdition))" Min="1" Max="1000" DefaultValue="1"></AntDesign.InputNumber>
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
@ -1,141 +0,0 @@
|
|||||||
using AntDesign;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Constants;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
using NftFaucet.Models.Enums;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class Step2Component : BasicComponent
|
|
||||||
{
|
|
||||||
protected string NameErrorMessage { get; set; }
|
|
||||||
protected string DescriptionErrorMessage { get; set; }
|
|
||||||
protected string SymbolErrorMessage { get; set; }
|
|
||||||
protected string ImageErrorMessage { get; set; }
|
|
||||||
protected string NameClass => string.IsNullOrWhiteSpace(NameErrorMessage) ? null : "invalid-input";
|
|
||||||
protected string DescriptionClass => string.IsNullOrWhiteSpace(DescriptionErrorMessage) ? null : "invalid-input";
|
|
||||||
protected string SymbolClass => string.IsNullOrWhiteSpace(SymbolErrorMessage) ? null : "invalid-input";
|
|
||||||
protected string ImageClass => "file-uploader" + (string.IsNullOrWhiteSpace(ImageErrorMessage) ? string.Empty : " invalid-input");
|
|
||||||
|
|
||||||
protected EnumWrapper<IpfsGatewayType>[] IpfsGateways { get; } = Enum.GetValues<IpfsGatewayType>()
|
|
||||||
.Select(x => new EnumWrapper<IpfsGatewayType>(x, x.ToString())).ToArray();
|
|
||||||
|
|
||||||
protected EnumWrapper<TokenType>[] TokenTypes { get; } = Enum.GetValues<TokenType>()
|
|
||||||
.Select(x => new EnumWrapper<TokenType>(x, x.ToString())).ToArray();
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (!await AppState.Metamask.IsReady() || !AppState.IpfsContext.IsInitialized)
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
|
|
||||||
AppState.Navigation.SetForwardHandler(ForwardHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Task<bool> ForwardHandler()
|
|
||||||
{
|
|
||||||
var isValidName = !string.IsNullOrWhiteSpace(AppState.Storage.TokenName);
|
|
||||||
var isValidDescription = !string.IsNullOrWhiteSpace(AppState.Storage.TokenDescription);
|
|
||||||
var isValidTokenSymbol = AppState.Storage.NetworkType != NetworkType.Solana || !string.IsNullOrEmpty(AppState.Storage.TokenSymbol);
|
|
||||||
var isValidFile = AppState.Storage.IpfsImageUrl != null;
|
|
||||||
var isNotUploading = !AppState.Storage.UploadIsInProgress;
|
|
||||||
|
|
||||||
if (!isValidName)
|
|
||||||
{
|
|
||||||
NameErrorMessage = "Invalid name";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidDescription || !isValidTokenSymbol)
|
|
||||||
{
|
|
||||||
DescriptionErrorMessage = "Invalid description";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidFile)
|
|
||||||
{
|
|
||||||
ImageErrorMessage = "Invalid file";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNotUploading)
|
|
||||||
{
|
|
||||||
ImageErrorMessage = "Upload is still in progress";
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
var canProceed = isValidName && isValidDescription && isValidTokenSymbol && isValidFile && isNotUploading;
|
|
||||||
return Task.FromResult(canProceed);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnNameInputChange(ChangeEventArgs args)
|
|
||||||
{
|
|
||||||
NameErrorMessage = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnDescriptionInputChange(ChangeEventArgs args)
|
|
||||||
{
|
|
||||||
DescriptionErrorMessage = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnSymbolInputChange(ChangeEventArgs args)
|
|
||||||
{
|
|
||||||
DescriptionErrorMessage = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnMasterEditionCheck(bool value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
AppState.Storage.TokenAmount = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnIpfsGatewayChange(EnumWrapper<IpfsGatewayType> ipfsGatewayItem)
|
|
||||||
{
|
|
||||||
AppState.Storage.IpfsGatewayType = ipfsGatewayItem.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnTokenTypeChange(EnumWrapper<TokenType> tokenTypeItem)
|
|
||||||
{
|
|
||||||
AppState.Storage.TokenType = tokenTypeItem.Value;
|
|
||||||
if (AppState.Storage.TokenType == TokenType.ERC721)
|
|
||||||
{
|
|
||||||
AppState.Storage.TokenAmount = 1;
|
|
||||||
}
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task<bool> BeforeUpload(List<UploadFileItem> files)
|
|
||||||
{
|
|
||||||
var file = files.FirstOrDefault();
|
|
||||||
if (file == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasValidSize = file.Size < UploadConstants.MaxFileSizeInBytes;
|
|
||||||
if (!hasValidSize)
|
|
||||||
{
|
|
||||||
MessageService.Error($"File must be smaller than {UploadConstants.MaxFileSizeInMegabytes} MB!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageErrorMessage = string.Empty;
|
|
||||||
AppState.Storage.UploadIsInProgress = true;
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
AppState.Storage.IpfsImageUrl = await IpfsService.Upload(file.FileName, file.Type, file.ObjectURL);
|
|
||||||
AppState.Storage.LocalImageUrl = new Uri(file.ObjectURL);
|
|
||||||
ImageErrorMessage = string.Empty;
|
|
||||||
AppState.Storage.UploadIsInProgress = false;
|
|
||||||
AppState.Storage.CanPreviewTokenFile = file.IsPicture();
|
|
||||||
|
|
||||||
if (!AppState.Storage.CanPreviewTokenFile)
|
|
||||||
{
|
|
||||||
MessageService.Warning("Can't preview this file. Tho you can still mint a NFT with it.");
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
@page "/step3"
|
|
||||||
@inherits Step3Component
|
|
||||||
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Token metadata:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<div class="@EditorClass">
|
|
||||||
<MonacoEditor @ref="Editor" Id="metadata-monaco-editor" ConstructionOptions="EditorConstructionOptions"/>
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
@ -1,84 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using BlazorMonaco;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
using NftFaucet.Models.Token;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class Step3Component : BasicComponent
|
|
||||||
{
|
|
||||||
protected MonacoEditor Editor { get; set; }
|
|
||||||
protected string EditorErrorMessage { get; set; }
|
|
||||||
protected string EditorClass => string.IsNullOrWhiteSpace(EditorErrorMessage) ? null : "invalid-input";
|
|
||||||
|
|
||||||
protected StandaloneEditorConstructionOptions EditorConstructionOptions(MonacoEditor editor)
|
|
||||||
{
|
|
||||||
return new StandaloneEditorConstructionOptions
|
|
||||||
{
|
|
||||||
Language = "json",
|
|
||||||
GlyphMargin = true,
|
|
||||||
Value = GetCurrentMetadataJson(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (!await AppState.Metamask.IsReady() || !AppState.IpfsContext.IsInitialized || AppState.Storage.IpfsImageUrl == null)
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
|
|
||||||
AppState.Navigation.SetForwardHandler(ForwardHandler);
|
|
||||||
|
|
||||||
await Task.Yield();
|
|
||||||
await Editor.SetValue(GetCurrentMetadataJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task<bool> ForwardHandler()
|
|
||||||
{
|
|
||||||
var metadataJson = await Editor.GetValue();
|
|
||||||
if (!metadataJson.IsValidJson())
|
|
||||||
{
|
|
||||||
EditorErrorMessage = "Invalid JSON";
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppState.Storage.TokenMetadata == metadataJson)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppState.Storage.UploadIsInProgress = true;
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
var metadataBytes = Encoding.UTF8.GetBytes(metadataJson);
|
|
||||||
var tokenUri = await IpfsService.Upload("token.json", "application/json", metadataBytes);
|
|
||||||
tokenUri = IpfsService.GetUrlToGateway(tokenUri, AppState.Storage.IpfsGatewayType);
|
|
||||||
AppState.Storage.TokenMetadata = metadataJson;
|
|
||||||
AppState.Storage.TokenUrl = tokenUri.OriginalString;
|
|
||||||
|
|
||||||
AppState.Storage.UploadIsInProgress = false;
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetCurrentMetadataJson()
|
|
||||||
{
|
|
||||||
var imageUrl = AppState?.Storage?.IpfsImageUrl != null
|
|
||||||
? IpfsService.GetUrlToGateway(AppState.Storage.IpfsImageUrl, AppState.Storage.IpfsGatewayType)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
var metadata = new TokenMetadata
|
|
||||||
{
|
|
||||||
Name = AppState?.Storage?.TokenName,
|
|
||||||
Description = AppState?.Storage?.TokenDescription,
|
|
||||||
Image = imageUrl?.OriginalString,
|
|
||||||
ExternalUrl = "https://darkcodi.github.io/nft-faucet/",
|
|
||||||
};
|
|
||||||
|
|
||||||
var metadataJson = JsonConvert.SerializeObject(metadata, Formatting.Indented);
|
|
||||||
return metadataJson;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
@page "/step4"
|
|
||||||
@inherits Step4Component
|
|
||||||
|
|
||||||
<Space Align="start" Direction="DirectionVHType.Vertical" Class="drk-full-width">
|
|
||||||
@if (!IsSupportedNetwork)
|
|
||||||
{
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="2" Strong="true" Style="color: red;">Selected network is not supported. Please change network in Metamask!</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Token URI:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<div class="@TokenUrlClass">
|
|
||||||
<Input Size="medium" @bind-Value="@AppState.Storage.TokenUrl" OnInput="@OnTokenUrlInputChange"/>
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="4" Style="margin-bottom: 0;">Destination address:</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
<SpaceItem Class="drk-full-width">
|
|
||||||
<div class="@DestinationAddressClass">
|
|
||||||
<Input Size="medium" @bind-Value="@AppState.Storage.DestinationAddress" OnInput="@OnDestinationAddressInputChange"/>
|
|
||||||
</div>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
@ -1,59 +0,0 @@
|
|||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
using NftFaucet.Models;
|
|
||||||
using NftFaucet.Models.Enums;
|
|
||||||
using Solnet.Wallet;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class Step4Component : BasicComponent
|
|
||||||
{
|
|
||||||
protected string TokenUrlErrorMessage { get; set; }
|
|
||||||
protected string DestinationAddressErrorMessage { get; set; }
|
|
||||||
protected string TokenUrlClass => string.IsNullOrWhiteSpace(TokenUrlErrorMessage) ? null : "invalid-input";
|
|
||||||
protected string DestinationAddressClass => string.IsNullOrWhiteSpace(DestinationAddressErrorMessage) ? null : "invalid-input";
|
|
||||||
protected bool IsSupportedNetwork => AppState?.Metamask?.Network != null && Settings?.GetEthereumNetworkOptions(AppState.Metamask.Network!.Value) != null;
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (!await AppState.Metamask.IsReady() || !AppState.IpfsContext.IsInitialized || string.IsNullOrEmpty(AppState.Storage.TokenUrl))
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
|
|
||||||
AppState.Navigation.SetForwardHandler(ForwardHandler);
|
|
||||||
AppState.Storage.DestinationAddress = AppState.Storage.NetworkType == NetworkType.Solana
|
|
||||||
? "51CNhAWJ94HrvXLNJrbXzhzgSixpwvwYvXTA9U6itENE"
|
|
||||||
: AppState.Metamask.Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnTokenUrlInputChange()
|
|
||||||
{
|
|
||||||
TokenUrlErrorMessage = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnDestinationAddressInputChange()
|
|
||||||
{
|
|
||||||
DestinationAddressErrorMessage = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Task<bool> ForwardHandler()
|
|
||||||
{
|
|
||||||
var isValidTokenUri = !string.IsNullOrWhiteSpace(AppState.Storage.TokenUrl);
|
|
||||||
var isValidDestinationAddress = AppState.Storage.NetworkType == NetworkType.Ethereum
|
|
||||||
? Address.Create(AppState.Storage.DestinationAddress).IsSuccess
|
|
||||||
: SolanaAddress.Create(AppState.Storage.DestinationAddress).IsSuccess;
|
|
||||||
|
|
||||||
if (!isValidTokenUri)
|
|
||||||
{
|
|
||||||
TokenUrlErrorMessage = "Invalid token URI";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidDestinationAddress)
|
|
||||||
{
|
|
||||||
DestinationAddressErrorMessage = "Invalid destination address";
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
|
|
||||||
return Task.FromResult(isValidTokenUri && isValidDestinationAddress && IsSupportedNetwork);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
@page "/step5"
|
|
||||||
@using NftFaucet.Models.Enums
|
|
||||||
@inherits Step5Component
|
|
||||||
|
|
||||||
@if (TransactionHash == null)
|
|
||||||
{
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Horizontal" Class="drk-vertical-space-center">
|
|
||||||
<SpaceItem Class="drk-full-height">
|
|
||||||
<Space Align="center" Direction="DirectionVHType.Vertical" Class="drk-vertical-space-center" Style="align-items: center !important;">
|
|
||||||
<SpaceItem>
|
|
||||||
<Spin size="large" />
|
|
||||||
</SpaceItem>
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Ethereum)
|
|
||||||
{
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="3">MetaMask will show a transaction signing pop-up. Please approve.</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
@if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
<SpaceItem>
|
|
||||||
<Title Level="3">Solana mint in progress. Please wait...</Title>
|
|
||||||
</SpaceItem>
|
|
||||||
}
|
|
||||||
</Space>
|
|
||||||
</SpaceItem>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
else if (TransactionHash.Value.IsSuccess)
|
|
||||||
{
|
|
||||||
<Result Status="success"
|
|
||||||
Title="Transaction was successfully created!"
|
|
||||||
SubTitle="@($"Transaction: {TransactionHash.Value.Value}. Please wait for 1-5 minutes till transaction is completed.")">
|
|
||||||
<Extra>
|
|
||||||
<Button OnClick="@ResetState">Start a new mint</Button>
|
|
||||||
<Button Type="@ButtonType.Primary" OnClick="@ViewOnExplorer">View on explorer</Button>
|
|
||||||
</Extra>
|
|
||||||
</Result>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<Result Status="error"
|
|
||||||
Title="Failed to create transaction"
|
|
||||||
SubTitle="@("Please ensure that you approve created transactions in Metamask")">
|
|
||||||
<Extra>
|
|
||||||
<Button Type="primary" OnClick="@RetryTransaction">Try again</Button>
|
|
||||||
</Extra>
|
|
||||||
</Result>
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
using CSharpFunctionalExtensions;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Microsoft.JSInterop;
|
|
||||||
using NftFaucet.Components;
|
|
||||||
using NftFaucet.Extensions;
|
|
||||||
using NftFaucet.Models.Enums;
|
|
||||||
using NftFaucet.Services;
|
|
||||||
using NftFaucet.Utils;
|
|
||||||
|
|
||||||
namespace NftFaucet.Pages;
|
|
||||||
|
|
||||||
public class Step5Component : BasicComponent
|
|
||||||
{
|
|
||||||
[Inject]
|
|
||||||
public IIpfsService IpfsService { get; set; }
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
public IEthereumTransactionService TransactionService { get; set; }
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
public ISolanaTransactionService SolanaTransactionService { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
protected IJSRuntime JsRuntime { get; set; }
|
|
||||||
|
|
||||||
protected Result<string>? TransactionHash { get; set; }
|
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
if (!await AppState.Metamask.IsReady() || !AppState.IpfsContext.IsInitialized || string.IsNullOrEmpty(AppState.Storage.DestinationAddress))
|
|
||||||
{
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Task.Run(Mint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Mint()
|
|
||||||
{
|
|
||||||
var network = AppState.Metamask.Network!.Value;
|
|
||||||
var address = AppState.Storage.DestinationAddress;
|
|
||||||
var uri = AppState.Storage.TokenUrl;
|
|
||||||
|
|
||||||
if (AppState.Storage.NetworkType == NetworkType.Ethereum && AppState.Storage.TokenType == TokenType.ERC721)
|
|
||||||
{
|
|
||||||
TransactionHash = await ResultWrapper.Wrap(TransactionService.MintErc721Token(network, address, uri));
|
|
||||||
}
|
|
||||||
else if (AppState.Storage.NetworkType == NetworkType.Ethereum && AppState.Storage.TokenType == TokenType.ERC1155)
|
|
||||||
{
|
|
||||||
var amount = (int) AppState.Storage.TokenAmount;
|
|
||||||
TransactionHash = await ResultWrapper.Wrap(TransactionService.MintErc1155Token(network, address, amount, uri));
|
|
||||||
}
|
|
||||||
else if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
TransactionHash =
|
|
||||||
await ResultWrapper.Wrap(SolanaTransactionService.MintNft(AppState.Storage.NetworkChain,
|
|
||||||
address,
|
|
||||||
uri,
|
|
||||||
AppState.Storage.TokenName,
|
|
||||||
AppState.Storage.TokenSymbol,
|
|
||||||
AppState.Storage.IsTokenMutable,
|
|
||||||
AppState.Storage.IncludeMasterEdition,
|
|
||||||
(uint)AppState.Storage.SellerFeeBasisPoints,
|
|
||||||
(ulong)AppState.Storage.TokenAmount));
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ResetState()
|
|
||||||
{
|
|
||||||
AppState.Reset();
|
|
||||||
UriHelper.NavigateToRelative("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task ViewOnExplorer()
|
|
||||||
{
|
|
||||||
var network = AppState.Metamask.Network;
|
|
||||||
|
|
||||||
|
|
||||||
if (AppState.Storage.NetworkType == NetworkType.Solana)
|
|
||||||
{
|
|
||||||
network = AppState.Storage.NetworkChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseUrl = network switch
|
|
||||||
{
|
|
||||||
NetworkChain.EthereumMainnet => "https://etherscan.io/tx/",
|
|
||||||
NetworkChain.Ropsten => "https://ropsten.etherscan.io/tx/",
|
|
||||||
NetworkChain.Rinkeby => "https://rinkeby.etherscan.io/tx/",
|
|
||||||
NetworkChain.Goerli => "https://goerli.etherscan.io/tx/",
|
|
||||||
NetworkChain.Kovan => "https://kovan.etherscan.io/tx/",
|
|
||||||
NetworkChain.OptimismMainnet => "https://optimistic.etherscan.io/tx/",
|
|
||||||
NetworkChain.OptimismKovan => "https://kovan-optimistic.etherscan.io/tx/",
|
|
||||||
NetworkChain.PolygonMainnet => "https://polygonscan.com/tx/",
|
|
||||||
NetworkChain.PolygonMumbai => "https://mumbai.polygonscan.com/tx/",
|
|
||||||
NetworkChain.MoonbeamMainnet => "https://blockscout.moonbeam.network/tx/",
|
|
||||||
NetworkChain.MoonbaseAlpha => "https://moonbase.moonscan.io/tx/",
|
|
||||||
NetworkChain.ArbitrumMainnetBeta => "https://explorer.arbitrum.io/tx/",
|
|
||||||
NetworkChain.ArbitrumRinkeby => "https://testnet.arbiscan.io/tx/",
|
|
||||||
NetworkChain.ArbitrumGoerli => "https://nitro-devnet-explorer.arbitrum.io/tx/",
|
|
||||||
NetworkChain.AvalancheMainnet => "https://snowtrace.io/tx/",
|
|
||||||
NetworkChain.AvalancheFuji => "https://testnet.snowtrace.io/tx/",
|
|
||||||
NetworkChain.SolanaDevnet => "https://explorer.solana.com/tx/",
|
|
||||||
NetworkChain.SolanaTestnet => "https://explorer.solana.com/tx/",
|
|
||||||
NetworkChain.SolanaMainnet => "https://explorer.solana.com/tx/",
|
|
||||||
NetworkChain.BnbChainMainnet => "https://bscscan.com/tx/",
|
|
||||||
NetworkChain.BnbChainTestnet => "https://testnet.bscscan.com/tx/",
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
if (baseUrl == null && !network.HasValue)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var txHash = TransactionHash!.Value!.Value;
|
|
||||||
var txUrl = BuildTxUrl(network.Value, baseUrl, txHash);
|
|
||||||
await JsRuntime.InvokeAsync<object>("open", txUrl, "_blank");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task RetryTransaction()
|
|
||||||
{
|
|
||||||
TransactionHash = null;
|
|
||||||
RefreshMediator.NotifyStateHasChangedSafe();
|
|
||||||
Mint();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildTxUrl(NetworkChain chain, string baseUrl, string txHash)
|
|
||||||
{
|
|
||||||
return chain switch
|
|
||||||
{
|
|
||||||
NetworkChain.SolanaDevnet => baseUrl + txHash + "?cluster=devnet",
|
|
||||||
NetworkChain.SolanaTestnet => baseUrl + txHash + "?cluster=testnet",
|
|
||||||
NetworkChain.SolanaMainnet => baseUrl + txHash,
|
|
||||||
_ => baseUrl + txHash,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
20
NftFaucet/Pages/TokensPage.razor
Normal file
20
NftFaucet/Pages/TokensPage.razor
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
@page "/tokens"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Tokens</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<RadzenHeading Size="H1" Text="Select or create a token to mint" />
|
||||||
|
<div style="width: 100%; display: flex; flex-direction: row; justify-content: end;">
|
||||||
|
<RadzenSplitButton Text="Import from..." Icon="backup" Style="margin-right: 1rem;" Click="@(() => NotificationService.Notify(NotificationSeverity.Warning, "NOT IMPLEMENTED", "Will be implemented later"))" >
|
||||||
|
<ChildContent>
|
||||||
|
<RadzenSplitButtonItem Text="OpenSea" Value="1" />
|
||||||
|
<RadzenSplitButtonItem Text="Rarible" Value="2" />
|
||||||
|
<RadzenSplitButtonItem Text="Nifty" Value="3" />
|
||||||
|
<RadzenSplitButtonItem Text="SuperRare" Value="4" />
|
||||||
|
</ChildContent>
|
||||||
|
</RadzenSplitButton>
|
||||||
|
<RadzenButton Text="Create New" Icon="add_circle_outline" ButtonStyle="ButtonStyle.Secondary"
|
||||||
|
Click="@OpenCreateTokenDialog"/>
|
||||||
|
</div>
|
||||||
|
<CardList Data="@TokenCards" OnSelectedChange="@(async _ => await OnTokenChange())" @bind-SelectedItems="@AppState.UserStorage.SelectedTokens" />
|
||||||
|
</RadzenContent>
|
70
NftFaucet/Pages/TokensPage.razor.cs
Normal file
70
NftFaucet/Pages/TokensPage.razor.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using ByteSizeLib;
|
||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Components.CardList;
|
||||||
|
using NftFaucet.Plugins;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class TokensPage : BasicComponent
|
||||||
|
{
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
RefreshCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem[] TokenCards { get; set; }
|
||||||
|
|
||||||
|
private void RefreshCards()
|
||||||
|
{
|
||||||
|
TokenCards = AppState?.UserStorage?.Tokens?.Select(MapCardListItem).ToArray() ?? Array.Empty<CardListItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem MapCardListItem(IToken token)
|
||||||
|
=> new CardListItem
|
||||||
|
{
|
||||||
|
Id = token.Id,
|
||||||
|
Header = token.Name,
|
||||||
|
ImageLocation = token.Image.FileData,
|
||||||
|
Properties = new[]
|
||||||
|
{
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "Description",
|
||||||
|
Value = token.Description,
|
||||||
|
},
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "Size",
|
||||||
|
Value = ByteSize.FromBytes(token.Image.FileSize).ToString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
private async Task OpenCreateTokenDialog()
|
||||||
|
{
|
||||||
|
var token = (IToken) await DialogService.OpenAsync<CreateTokenDialog>("Create new token",
|
||||||
|
new Dictionary<string, object>(),
|
||||||
|
new DialogOptions() { Width = "700px", Height = "570px", Resizable = true, Draggable = true });
|
||||||
|
|
||||||
|
if (token == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppState.UserStorage.Tokens ??= new List<IToken>();
|
||||||
|
AppState.UserStorage.Tokens.Add(token);
|
||||||
|
AppState.UserStorage.SelectedTokens = new[] { token.Id };
|
||||||
|
AppState.UserStorage.SelectedUploadLocations = Array.Empty<Guid>();
|
||||||
|
RefreshCards();
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
await StateRepository.SaveToken(token);
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnTokenChange()
|
||||||
|
{
|
||||||
|
AppState.UserStorage.SelectedUploadLocations = Array.Empty<Guid>();
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
}
|
19
NftFaucet/Pages/UploadLocationsPage.razor
Normal file
19
NftFaucet/Pages/UploadLocationsPage.razor
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@page "/uploads"
|
||||||
|
@inherits BasicComponent
|
||||||
|
|
||||||
|
<PageTitle>Token upload locations</PageTitle>
|
||||||
|
<RadzenContent Container="main">
|
||||||
|
<RadzenHeading Size="H1" Text="Select or create a token upload" />
|
||||||
|
@if (AppState.SelectedToken == null)
|
||||||
|
{
|
||||||
|
<RadzenHeading Size="H3" Text="Please choose token first!" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div style="width: 100%; display: flex; flex-direction: row; justify-content: end;">
|
||||||
|
<RadzenButton Text="Create New" Icon="add_circle_outline" ButtonStyle="ButtonStyle.Secondary"
|
||||||
|
Click="@OpenCreateUploadDialog"/>
|
||||||
|
</div>
|
||||||
|
<CardList Data="@UploadCards" OnSelectedChange="@(async _ => await OnUploadLocationChange())" @bind-SelectedItems="@AppState.UserStorage.SelectedUploadLocations"/>
|
||||||
|
}
|
||||||
|
</RadzenContent>
|
89
NftFaucet/Pages/UploadLocationsPage.razor.cs
Normal file
89
NftFaucet/Pages/UploadLocationsPage.razor.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using NftFaucet.Components;
|
||||||
|
using NftFaucet.Components.CardList;
|
||||||
|
using NftFaucet.Plugins;
|
||||||
|
using Radzen;
|
||||||
|
|
||||||
|
namespace NftFaucet.Pages;
|
||||||
|
|
||||||
|
public partial class UploadLocationsPage : BasicComponent
|
||||||
|
{
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
RefreshCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem[] UploadCards { get; set; }
|
||||||
|
|
||||||
|
private void RefreshCards()
|
||||||
|
{
|
||||||
|
var selectedTokenId = AppState?.UserStorage?.SelectedTokens?.FirstOrDefault();
|
||||||
|
UploadCards = AppState?.UserStorage?.UploadLocations?.Where(x => x.TokenId == selectedTokenId).Select(MapCardListItem).ToArray() ?? Array.Empty<CardListItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardListItem MapCardListItem(ITokenUploadLocation uploadLocation)
|
||||||
|
=> new CardListItem
|
||||||
|
{
|
||||||
|
Id = uploadLocation.Id,
|
||||||
|
Header = uploadLocation.Name,
|
||||||
|
ImageLocation = GetUploaderImageLocation(uploadLocation.UploaderId),
|
||||||
|
Properties = new[]
|
||||||
|
{
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "Id",
|
||||||
|
Value = uploadLocation.Id.ToString(),
|
||||||
|
},
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "Location",
|
||||||
|
Value = uploadLocation.Location,
|
||||||
|
Link = uploadLocation.Location,
|
||||||
|
},
|
||||||
|
new CardListItemProperty
|
||||||
|
{
|
||||||
|
Name = "CreatedAt",
|
||||||
|
Value = uploadLocation.CreatedAt.ToString(CultureInfo.InvariantCulture),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
private string GetUploaderImageLocation(Guid uploaderId)
|
||||||
|
{
|
||||||
|
var uploader = AppState?.PluginStorage?.Uploaders?.FirstOrDefault(x => x.Id == uploaderId);
|
||||||
|
if (uploader == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "./images/" + uploader.ImageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OpenCreateUploadDialog()
|
||||||
|
{
|
||||||
|
var uploadLocation = (ITokenUploadLocation) await DialogService.OpenAsync<CreateUploadDialog>("Create new upload",
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "Token", AppState.SelectedToken },
|
||||||
|
},
|
||||||
|
new DialogOptions() { Width = "1000px", Height = "700px", Resizable = true, Draggable = true });
|
||||||
|
|
||||||
|
if (uploadLocation == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppState.UserStorage.UploadLocations ??= new List<ITokenUploadLocation>();
|
||||||
|
AppState.UserStorage.UploadLocations.Add(uploadLocation);
|
||||||
|
AppState.UserStorage.SelectedUploadLocations = new[] { uploadLocation.Id };
|
||||||
|
RefreshCards();
|
||||||
|
RefreshMediator.NotifyStateHasChangedSafe();
|
||||||
|
await StateRepository.SaveUploadLocation(uploadLocation);
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnUploadLocationChange()
|
||||||
|
{
|
||||||
|
await SaveAppState();
|
||||||
|
}
|
||||||
|
}
|
10
NftFaucet/Plugins/IToken.cs
Normal file
10
NftFaucet/Plugins/IToken.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace NftFaucet.Plugins;
|
||||||
|
|
||||||
|
public interface IToken
|
||||||
|
{
|
||||||
|
public Guid Id { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public string Description { get; }
|
||||||
|
public DateTime CreatedAt { get; }
|
||||||
|
public ITokenMedia Image { get; }
|
||||||
|
}
|
9
NftFaucet/Plugins/ITokenMedia.cs
Normal file
9
NftFaucet/Plugins/ITokenMedia.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace NftFaucet.Plugins;
|
||||||
|
|
||||||
|
public interface ITokenMedia
|
||||||
|
{
|
||||||
|
public string FileName { get; }
|
||||||
|
public string FileType { get; }
|
||||||
|
public string FileData { get; }
|
||||||
|
public long FileSize { get; }
|
||||||
|
}
|
11
NftFaucet/Plugins/ITokenUploadLocation.cs
Normal file
11
NftFaucet/Plugins/ITokenUploadLocation.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace NftFaucet.Plugins;
|
||||||
|
|
||||||
|
public interface ITokenUploadLocation
|
||||||
|
{
|
||||||
|
public Guid Id { get; }
|
||||||
|
public Guid TokenId { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public string Location { get; }
|
||||||
|
public DateTime CreatedAt { get; }
|
||||||
|
public Guid UploaderId { get; }
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
using NftFaucet.Plugins.NetworkPlugins.Arbitrum.Networks;
|
||||||
|
|
||||||
|
namespace NftFaucet.Plugins.NetworkPlugins.Arbitrum;
|
||||||
|
|
||||||
|
public class ArbitrumNetworkPlugin : INetworkPlugin
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<INetwork> Networks { get; } = new INetwork[]
|
||||||
|
{
|
||||||
|
new ArbitrumOneNetwork(),
|
||||||
|
new ArbitrumNovaNetwork(),
|
||||||
|
new ArbitrumRinkebyNetwork(),
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
namespace NftFaucet.Plugins.NetworkPlugins.Arbitrum.Networks;
|
||||||
|
|
||||||
|
public class ArbitrumNovaNetwork : INetwork
|
||||||
|
{
|
||||||
|
public Guid Id { get; } = Guid.Parse("e2f056f8-1c5c-494f-9e88-96213a2009d4");
|
||||||
|
public string Name { get; } = "Arbitrum Nova";
|
||||||
|
public string ShortName { get; } = "ArbNova";
|
||||||
|
public ulong? ChainId { get; } = 42170;
|
||||||
|
public int? Order { get; } = 2;
|
||||||
|
public string Currency { get; } = "ETH";
|
||||||
|
public string ImageName { get; } = "arbitrum-black.svg";
|
||||||
|
public bool IsSupported { get; } = false;
|
||||||
|
public bool IsTestnet { get; } = true;
|
||||||
|
public bool IsDeprecated { get; } = false;
|
||||||
|
public NetworkType Type { get; } = NetworkType.Ethereum;
|
||||||
|
public NetworkSubtype SubType { get; } = NetworkSubtype.Arbitrum;
|
||||||
|
public Uri PublicRpcUrl { get; } = null;
|
||||||
|
public Uri ExplorerUrl { get; } = new Uri("https://nova-explorer.arbitrum.io");
|
||||||
|
public IReadOnlyCollection<IContract> DeployedContracts { get; } = Array.Empty<IContract>();
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
namespace NftFaucet.Plugins.NetworkPlugins.Arbitrum.Networks;
|
||||||
|
|
||||||
|
public class ArbitrumOneNetwork : INetwork
|
||||||
|
{
|
||||||
|
public Guid Id { get; } = Guid.Parse("4f0be8b9-dda1-4598-88b9-d4ba77f4c30e");
|
||||||
|
public string Name { get; } = "Arbitrum One";
|
||||||
|
public string ShortName { get; } = "Arbitrum";
|
||||||
|
public ulong? ChainId { get; } = 42161;
|
||||||
|
public int? Order { get; } = 1;
|
||||||
|
public string Currency { get; } = "ETH";
|
||||||
|
public string ImageName { get; } = "arbitrum.svg";
|
||||||
|
public bool IsSupported { get; } = false;
|
||||||
|
public bool IsTestnet { get; } = false;
|
||||||
|
public bool IsDeprecated { get; } = false;
|
||||||
|
public NetworkType Type { get; } = NetworkType.Ethereum;
|
||||||
|
public NetworkSubtype SubType { get; } = NetworkSubtype.Arbitrum;
|
||||||
|
public Uri PublicRpcUrl { get; } = null;
|
||||||
|
public Uri ExplorerUrl { get; } = new Uri("https://arbiscan.io/");
|
||||||
|
public IReadOnlyCollection<IContract> DeployedContracts { get; } = Array.Empty<IContract>();
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace NftFaucet.Plugins.NetworkPlugins.Arbitrum.Networks;
|
||||||
|
|
||||||
|
public class ArbitrumRinkebyNetwork : INetwork
|
||||||
|
{
|
||||||
|
public Guid Id { get; } = Guid.Parse("8189f9cd-14fc-41ab-9418-ca472ab15873");
|
||||||
|
public string Name { get; } = "Arbitrum Rinkeby";
|
||||||
|
public string ShortName { get; } = "ArbRinkeby";
|
||||||
|
public ulong? ChainId { get; } = 421611;
|
||||||
|
public int? Order { get; } = 3;
|
||||||
|
public string Currency { get; } = "ETH";
|
||||||
|
public string ImageName { get; } = "arbitrum-black.svg";
|
||||||
|
public bool IsSupported { get; } = true;
|
||||||
|
public bool IsTestnet { get; } = true;
|
||||||
|
public bool IsDeprecated { get; } = false;
|
||||||
|
public NetworkType Type { get; } = NetworkType.Ethereum;
|
||||||
|
public NetworkSubtype SubType { get; } = NetworkSubtype.Arbitrum;
|
||||||
|
public Uri PublicRpcUrl { get; } = new Uri("https://rinkeby.arbitrum.io/rpc");
|
||||||
|
public Uri ExplorerUrl { get; } = new Uri("https://rinkeby-explorer.arbitrum.io/");
|
||||||
|
public IReadOnlyCollection<IContract> DeployedContracts { get; } = new[]
|
||||||
|
{
|
||||||
|
new Contract
|
||||||
|
{
|
||||||
|
Id = Guid.Parse("f82fc396-1cfd-4db7-af27-9e3e7d4264f6"),
|
||||||
|
Name = "ERC-721 Faucet",
|
||||||
|
Symbol = "FA721",
|
||||||
|
Address = "0x9F64932Be34D5D897C4253D17707b50921f372B6",
|
||||||
|
Type = ContractType.Erc721,
|
||||||
|
DeploymentTxHash = "0x506e9c08cb360eb85b0b5d1e86615178ff1e4c0fab05297e725e9d227f45fe6b",
|
||||||
|
DeployedAt = DateTime.Parse("Apr-17-2022 01:45:55 PM", CultureInfo.InvariantCulture),
|
||||||
|
IsVerified = true,
|
||||||
|
},
|
||||||
|
new Contract
|
||||||
|
{
|
||||||
|
Id = Guid.Parse("6e125364-b8fd-49fe-b453-cfa69ebad811"),
|
||||||
|
Name = "ERC-1155 Faucet",
|
||||||
|
Symbol = "FA1155",
|
||||||
|
Address = "0xf67C575502fc1cE399a3e1895dDf41847185D7bD",
|
||||||
|
Type = ContractType.Erc1155,
|
||||||
|
DeploymentTxHash = "0xe62c31c70fd37a4b984b4b51d2ee5e08cd6bc53983200ca0041656f57f6dc8de",
|
||||||
|
DeployedAt = DateTime.Parse("Apr-17-2022 01:49:41 PM", CultureInfo.InvariantCulture),
|
||||||
|
IsVerified = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user