improves error reporting for discord bot
This commit is contained in:
parent
e29ffe4f9c
commit
9e842207ab
|
@ -4,6 +4,7 @@
|
||||||
{
|
{
|
||||||
public RewardUsersCommand[] Rewards { get; set; } = Array.Empty<RewardUsersCommand>();
|
public RewardUsersCommand[] Rewards { get; set; } = Array.Empty<RewardUsersCommand>();
|
||||||
public ChainEventMessage[] EventsOverview { get; set; } = Array.Empty<ChainEventMessage>();
|
public ChainEventMessage[] EventsOverview { get; set; } = Array.Empty<ChainEventMessage>();
|
||||||
|
public string[] Errors { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
public bool HasAny()
|
public bool HasAny()
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,8 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex);
|
void OnSlotFilled(RequestEvent requestEvent, EthAddress host, BigInteger slotIndex);
|
||||||
void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex);
|
void OnSlotFreed(RequestEvent requestEvent, BigInteger slotIndex);
|
||||||
void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex);
|
void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex);
|
||||||
|
|
||||||
|
void OnError(string msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RequestEvent
|
public class RequestEvent
|
||||||
|
@ -67,7 +69,11 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
private void Apply(ChainEvents events)
|
private void Apply(ChainEvents events)
|
||||||
{
|
{
|
||||||
if (events.BlockInterval.TimeRange.From < TotalSpan.From)
|
if (events.BlockInterval.TimeRange.From < TotalSpan.From)
|
||||||
throw new Exception("Attempt to update ChainState with set of events from before its current record.");
|
{
|
||||||
|
var msg = "Attempt to update ChainState with set of events from before its current record.";
|
||||||
|
handler.OnError(msg);
|
||||||
|
throw new Exception(msg);
|
||||||
|
}
|
||||||
|
|
||||||
log.Log($"ChainState updating: {events.BlockInterval}");
|
log.Log($"ChainState updating: {events.BlockInterval}");
|
||||||
|
|
||||||
|
@ -110,7 +116,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
|
|
||||||
private void ApplyEvent(RequestFulfilledEventDTO @event)
|
private void ApplyEvent(RequestFulfilledEventDTO @event)
|
||||||
{
|
{
|
||||||
var r = FindRequest(@event.RequestId);
|
var r = FindRequest(@event);
|
||||||
if (r == null) return;
|
if (r == null) return;
|
||||||
r.UpdateState(@event.Block.BlockNumber, RequestState.Started);
|
r.UpdateState(@event.Block.BlockNumber, RequestState.Started);
|
||||||
handler.OnRequestFulfilled(new RequestEvent(@event.Block, r));
|
handler.OnRequestFulfilled(new RequestEvent(@event.Block, r));
|
||||||
|
@ -118,7 +124,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
|
|
||||||
private void ApplyEvent(RequestCancelledEventDTO @event)
|
private void ApplyEvent(RequestCancelledEventDTO @event)
|
||||||
{
|
{
|
||||||
var r = FindRequest(@event.RequestId);
|
var r = FindRequest(@event);
|
||||||
if (r == null) return;
|
if (r == null) return;
|
||||||
r.UpdateState(@event.Block.BlockNumber, RequestState.Cancelled);
|
r.UpdateState(@event.Block.BlockNumber, RequestState.Cancelled);
|
||||||
handler.OnRequestCancelled(new RequestEvent(@event.Block, r));
|
handler.OnRequestCancelled(new RequestEvent(@event.Block, r));
|
||||||
|
@ -126,7 +132,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
|
|
||||||
private void ApplyEvent(RequestFailedEventDTO @event)
|
private void ApplyEvent(RequestFailedEventDTO @event)
|
||||||
{
|
{
|
||||||
var r = FindRequest(@event.RequestId);
|
var r = FindRequest(@event);
|
||||||
if (r == null) return;
|
if (r == null) return;
|
||||||
r.UpdateState(@event.Block.BlockNumber, RequestState.Failed);
|
r.UpdateState(@event.Block.BlockNumber, RequestState.Failed);
|
||||||
handler.OnRequestFailed(new RequestEvent(@event.Block, r));
|
handler.OnRequestFailed(new RequestEvent(@event.Block, r));
|
||||||
|
@ -134,7 +140,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
|
|
||||||
private void ApplyEvent(SlotFilledEventDTO @event)
|
private void ApplyEvent(SlotFilledEventDTO @event)
|
||||||
{
|
{
|
||||||
var r = FindRequest(@event.RequestId);
|
var r = FindRequest(@event);
|
||||||
if (r == null) return;
|
if (r == null) return;
|
||||||
r.Hosts.Add(@event.Host, (int)@event.SlotIndex);
|
r.Hosts.Add(@event.Host, (int)@event.SlotIndex);
|
||||||
r.Log($"[{@event.Block.BlockNumber}] SlotFilled (host:'{@event.Host}', slotIndex:{@event.SlotIndex})");
|
r.Log($"[{@event.Block.BlockNumber}] SlotFilled (host:'{@event.Host}', slotIndex:{@event.SlotIndex})");
|
||||||
|
@ -143,7 +149,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
|
|
||||||
private void ApplyEvent(SlotFreedEventDTO @event)
|
private void ApplyEvent(SlotFreedEventDTO @event)
|
||||||
{
|
{
|
||||||
var r = FindRequest(@event.RequestId);
|
var r = FindRequest(@event);
|
||||||
if (r == null) return;
|
if (r == null) return;
|
||||||
r.Hosts.RemoveHost((int)@event.SlotIndex);
|
r.Hosts.RemoveHost((int)@event.SlotIndex);
|
||||||
r.Log($"[{@event.Block.BlockNumber}] SlotFreed (slotIndex:{@event.SlotIndex})");
|
r.Log($"[{@event.Block.BlockNumber}] SlotFreed (slotIndex:{@event.SlotIndex})");
|
||||||
|
@ -152,7 +158,7 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
|
|
||||||
private void ApplyEvent(SlotReservationsFullEventDTO @event)
|
private void ApplyEvent(SlotReservationsFullEventDTO @event)
|
||||||
{
|
{
|
||||||
var r = FindRequest(@event.RequestId);
|
var r = FindRequest(@event);
|
||||||
if (r == null) return;
|
if (r == null) return;
|
||||||
r.Log($"[{@event.Block.BlockNumber}] SlotReservationsFull (slotIndex:{@event.SlotIndex})");
|
r.Log($"[{@event.Block.BlockNumber}] SlotReservationsFull (slotIndex:{@event.SlotIndex})");
|
||||||
handler.OnSlotReservationsFull(new RequestEvent(@event.Block, r), @event.SlotIndex);
|
handler.OnSlotReservationsFull(new RequestEvent(@event.Block, r), @event.SlotIndex);
|
||||||
|
@ -171,10 +177,23 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChainStateRequest? FindRequest(byte[] requestId)
|
private ChainStateRequest? FindRequest(IHasRequestId request)
|
||||||
{
|
{
|
||||||
var r = requests.SingleOrDefault(r => Equal(r.Request.RequestId, requestId));
|
var r = requests.SingleOrDefault(r => Equal(r.Request.RequestId, request.RequestId));
|
||||||
if (r == null) log.Log("Unable to find request by ID!");
|
if (r == null)
|
||||||
|
{
|
||||||
|
var blockNumber = "unknown";
|
||||||
|
if (request is IHasBlock blk)
|
||||||
|
{
|
||||||
|
blockNumber = blk.Block.BlockNumber.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = $"Received event of type '{request.GetType()}' in block '{blockNumber}' for request by Id: '{request.RequestId}'. " +
|
||||||
|
$"Failed to find request. Request creation event not seen! (Tracker start time: {TotalSpan.From})";
|
||||||
|
|
||||||
|
log.Error(msg);
|
||||||
|
handler.OnError(msg);
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
using GethPlugin;
|
using GethPlugin;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CodexContractsPlugin.ChainMonitor
|
namespace CodexContractsPlugin.ChainMonitor
|
||||||
{
|
{
|
||||||
|
@ -56,5 +51,10 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
{
|
{
|
||||||
foreach (var handler in Handlers) handler.OnSlotReservationsFull(requestEvent, slotIndex);
|
foreach (var handler in Handlers) handler.OnSlotReservationsFull(requestEvent, slotIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnError(string msg)
|
||||||
|
{
|
||||||
|
foreach (var handler in Handlers) handler.OnError(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,5 +36,9 @@ namespace CodexContractsPlugin.ChainMonitor
|
||||||
public void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex)
|
public void OnSlotReservationsFull(RequestEvent requestEvent, BigInteger slotIndex)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnError(string msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,12 @@ namespace CodexContractsPlugin.Marketplace
|
||||||
BlockTimeEntry Block { get; set; }
|
BlockTimeEntry Block { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Request : RequestBase, IHasBlock
|
public interface IHasRequestId
|
||||||
|
{
|
||||||
|
byte[] RequestId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Request : RequestBase, IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
|
@ -28,38 +33,38 @@ namespace CodexContractsPlugin.Marketplace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class RequestFulfilledEventDTO : IHasBlock
|
public partial class RequestFulfilledEventDTO : IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class RequestCancelledEventDTO : IHasBlock
|
public partial class RequestCancelledEventDTO : IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class RequestFailedEventDTO : IHasBlock
|
public partial class RequestFailedEventDTO : IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SlotFilledEventDTO : IHasBlock
|
public partial class SlotFilledEventDTO : IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
public EthAddress Host { get; set; }
|
public EthAddress Host { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SlotFreedEventDTO : IHasBlock
|
public partial class SlotFreedEventDTO : IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SlotReservationsFullEventDTO : IHasBlock
|
public partial class SlotReservationsFullEventDTO : IHasBlock, IHasRequestId
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BlockTimeEntry Block { get; set; }
|
public BlockTimeEntry Block { get; set; }
|
||||||
|
|
|
@ -17,8 +17,10 @@ namespace BiblioTech.Rewards
|
||||||
this.eventsChannel = eventsChannel;
|
this.eventsChannel = eventsChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ProcessChainEvents(ChainEventMessage[] eventsOverview)
|
public async Task ProcessChainEvents(ChainEventMessage[] eventsOverview, string[] errors)
|
||||||
{
|
{
|
||||||
|
await SendErrorsToAdminChannel(errors);
|
||||||
|
|
||||||
if (eventsChannel == null || eventsOverview == null || !eventsOverview.Any()) return;
|
if (eventsChannel == null || eventsOverview == null || !eventsOverview.Any()) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -34,6 +36,22 @@ namespace BiblioTech.Rewards
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SendErrorsToAdminChannel(string[] errors)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var error in errors)
|
||||||
|
{
|
||||||
|
await Program.AdminChecker.SendInAdminChannel(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
log.Error("Failed to send error messages to admin channel. " + exc);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SendChainEventsInOrder(ChainEventMessage[] eventsOverview, SocketTextChannel eventsChannel, UserData[] users)
|
private async Task SendChainEventsInOrder(ChainEventMessage[] eventsOverview, SocketTextChannel eventsChannel, UserData[] users)
|
||||||
{
|
{
|
||||||
eventsOverview = eventsOverview.OrderBy(e => e.BlockNumber).ToArray();
|
eventsOverview = eventsOverview.OrderBy(e => e.BlockNumber).ToArray();
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace BiblioTech.Rewards
|
||||||
await ProcessRewards(rewards);
|
await ProcessRewards(rewards);
|
||||||
}
|
}
|
||||||
|
|
||||||
await eventsSender.ProcessChainEvents(rewards.EventsOverview);
|
await eventsSender.ProcessChainEvents(rewards.EventsOverview, rewards.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessRewards(GiveRewardsCommand rewards)
|
private async Task ProcessRewards(GiveRewardsCommand rewards)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using CodexContractsPlugin;
|
using CodexContractsPlugin;
|
||||||
using CodexContractsPlugin.ChainMonitor;
|
using CodexContractsPlugin.ChainMonitor;
|
||||||
using Nethereum.Model;
|
|
||||||
using TestNetRewarder;
|
using TestNetRewarder;
|
||||||
using Utils;
|
using Utils;
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ namespace MarketInsights
|
||||||
|
|
||||||
private MarketTimeSegment BuildContribution(TimeRange timeRange)
|
private MarketTimeSegment BuildContribution(TimeRange timeRange)
|
||||||
{
|
{
|
||||||
var builder = new ContributionBuilder(timeRange);
|
var builder = new ContributionBuilder(appState.Log, timeRange);
|
||||||
mux.Handlers.Add(builder);
|
mux.Handlers.Add(builder);
|
||||||
chainState.Update(timeRange.To);
|
chainState.Update(timeRange.To);
|
||||||
mux.Handlers.Remove(builder);
|
mux.Handlers.Remove(builder);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using CodexContractsPlugin.ChainMonitor;
|
using CodexContractsPlugin.ChainMonitor;
|
||||||
using GethPlugin;
|
using GethPlugin;
|
||||||
|
using Logging;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Utils;
|
using Utils;
|
||||||
|
|
||||||
|
@ -8,14 +9,16 @@ namespace MarketInsights
|
||||||
public class ContributionBuilder : IChainStateChangeHandler
|
public class ContributionBuilder : IChainStateChangeHandler
|
||||||
{
|
{
|
||||||
private readonly MarketTimeSegment segment = new MarketTimeSegment();
|
private readonly MarketTimeSegment segment = new MarketTimeSegment();
|
||||||
|
private readonly ILog log;
|
||||||
|
|
||||||
public ContributionBuilder(TimeRange timeRange)
|
public ContributionBuilder(ILog log, TimeRange timeRange)
|
||||||
{
|
{
|
||||||
segment = new MarketTimeSegment
|
segment = new MarketTimeSegment
|
||||||
{
|
{
|
||||||
FromUtc = timeRange.From,
|
FromUtc = timeRange.From,
|
||||||
ToUtc = timeRange.To
|
ToUtc = timeRange.To
|
||||||
};
|
};
|
||||||
|
this.log = log;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNewRequest(RequestEvent requestEvent)
|
public void OnNewRequest(RequestEvent requestEvent)
|
||||||
|
@ -55,6 +58,11 @@ namespace MarketInsights
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnError(string msg)
|
||||||
|
{
|
||||||
|
log.Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
public MarketTimeSegment GetSegment()
|
public MarketTimeSegment GetSegment()
|
||||||
{
|
{
|
||||||
return segment;
|
return segment;
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace TestNetRewarder
|
||||||
{
|
{
|
||||||
private static readonly string nl = Environment.NewLine;
|
private static readonly string nl = Environment.NewLine;
|
||||||
private readonly List<ChainEventMessage> events = new List<ChainEventMessage>();
|
private readonly List<ChainEventMessage> events = new List<ChainEventMessage>();
|
||||||
|
private readonly List<string> errors = new List<string>();
|
||||||
private readonly EmojiMaps emojiMaps = new EmojiMaps();
|
private readonly EmojiMaps emojiMaps = new EmojiMaps();
|
||||||
|
|
||||||
public ChainEventMessage[] GetEvents()
|
public ChainEventMessage[] GetEvents()
|
||||||
|
@ -21,9 +22,11 @@ namespace TestNetRewarder
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddError(string error)
|
public string[] GetErrors()
|
||||||
{
|
{
|
||||||
AddBlock(1, "📢 **Error**", error);
|
var result = errors.ToArray();
|
||||||
|
errors.Clear();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNewRequest(RequestEvent requestEvent)
|
public void OnNewRequest(RequestEvent requestEvent)
|
||||||
|
@ -83,6 +86,11 @@ namespace TestNetRewarder
|
||||||
$"Slot Index: {slotIndex}"
|
$"Slot Index: {slotIndex}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnError(string msg)
|
||||||
|
{
|
||||||
|
errors.Add(msg);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddRequestBlock(RequestEvent requestEvent, string eventName, params string[] content)
|
private void AddRequestBlock(RequestEvent requestEvent, string eventName, params string[] content)
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace TestNetRewarder
|
||||||
{
|
{
|
||||||
var msg = "Exception processing time segment: " + ex;
|
var msg = "Exception processing time segment: " + ex;
|
||||||
log.Error(msg);
|
log.Error(msg);
|
||||||
eventsFormatter.AddError(msg);
|
eventsFormatter.OnError(msg);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,9 @@ namespace TestNetRewarder
|
||||||
var numberOfChainEvents = chainState.Update(timeRange.To);
|
var numberOfChainEvents = chainState.Update(timeRange.To);
|
||||||
|
|
||||||
var events = eventsFormatter.GetEvents();
|
var events = eventsFormatter.GetEvents();
|
||||||
|
var errors = eventsFormatter.GetErrors();
|
||||||
|
|
||||||
var request = builder.Build(events);
|
var request = builder.Build(events, errors);
|
||||||
if (request.HasAny())
|
if (request.HasAny())
|
||||||
{
|
{
|
||||||
await client.SendRewards(request);
|
await client.SendRewards(request);
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace TestNetRewarder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GiveRewardsCommand Build(ChainEventMessage[] lines)
|
public GiveRewardsCommand Build(ChainEventMessage[] lines, string[] errors)
|
||||||
{
|
{
|
||||||
var result = new GiveRewardsCommand
|
var result = new GiveRewardsCommand
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,8 @@ namespace TestNetRewarder
|
||||||
RewardId = p.Key,
|
RewardId = p.Key,
|
||||||
UserAddresses = p.Value.Select(v => v.Address).ToArray()
|
UserAddresses = p.Value.Select(v => v.Address).ToArray()
|
||||||
}).ToArray(),
|
}).ToArray(),
|
||||||
EventsOverview = lines
|
EventsOverview = lines,
|
||||||
|
Errors = errors
|
||||||
};
|
};
|
||||||
|
|
||||||
rewards.Clear();
|
rewards.Clear();
|
||||||
|
|
|
@ -76,6 +76,10 @@ namespace TestNetRewarder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnError(string msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private void GiveReward(RewardConfig reward, EthAddress receiver)
|
private void GiveReward(RewardConfig reward, EthAddress receiver)
|
||||||
{
|
{
|
||||||
giver.Give(reward, receiver);
|
giver.Give(reward, receiver);
|
||||||
|
|
Loading…
Reference in New Issue