From 1c56f0d95d5af6553a9ad7f62750716581dc4910 Mon Sep 17 00:00:00 2001 From: ilior <78041027+itailiors@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:31:56 +0300 Subject: [PATCH] Notify investor signature signed (#129) * notify investor of refresh * add nostr pubkey to storage * Update Invest.razor * fix for merge * fix tests, pr go-over * Update Investor.razor * Some fixes to the Investor page --------- Co-authored-by: dangershony --- src/Angor.Test/DerivationOperationsTest.cs | 46 +++--- src/Angor/Client/Pages/Invest.razor | 11 +- src/Angor/Client/Pages/Investor.razor | 165 ++++++++++++++------- src/Angor/Client/Storage/ClientStorage.cs | 10 ++ src/Angor/Client/Storage/IClientStorage.cs | 3 +- 5 files changed, 154 insertions(+), 81 deletions(-) diff --git a/src/Angor.Test/DerivationOperationsTest.cs b/src/Angor.Test/DerivationOperationsTest.cs index 3a49b591..8864fa34 100644 --- a/src/Angor.Test/DerivationOperationsTest.cs +++ b/src/Angor.Test/DerivationOperationsTest.cs @@ -51,36 +51,36 @@ public ScriptTDerivationOperationsTestest() _logger = mockLogger.Object; } - //[Fact] - //public void BuildKeys() - //{ + [Fact] + public void BuildKeys() + { - // var derivationOperations = new DerivationOperations(new HdOperations(), null, _networkConfiguration.Object); - // var rootKey = CreateAngorRootKey(); - // var mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve); + var derivationOperations = new DerivationOperations(new HdOperations(), _logger, _networkConfiguration.Object); + var rootKey = CreateAngorRootKey(); + var mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve); - // var founderKey = derivationOperations.DeriveFounderKey(new WalletWords { Words = mnemonic.ToString() }, 1); - // var projectId = derivationOperations.DeriveProjectId(founderKey); - // var angorKey = derivationOperations.DeriveAngorKey(founderKey, rootKey); - // var script = derivationOperations.AngorKeyToScript(angorKey); - //} + var founderKey = derivationOperations.DeriveFounderKey(new WalletWords { Words = mnemonic.ToString() }, 1); + var projectId = derivationOperations.DeriveProjectId(founderKey); + var angorKey = derivationOperations.DeriveAngorKey(founderKey, rootKey); + var script = derivationOperations.AngorKeyToScript(angorKey); + } - //[Fact] - //public void BuildKeysFromExisitData() - //{ - // var derivationOperations = new DerivationOperations(new HdOperations(), null, _networkConfiguration.Object); - // var rootKey = CreateAngorRootKey("area frost rapid guitar salon tower bless fly where inmate trouble daughter"); + [Fact] + public void BuildKeysFromExisitData() + { + var derivationOperations = new DerivationOperations(new HdOperations(), _logger, _networkConfiguration.Object); + var rootKey = CreateAngorRootKey("area frost rapid guitar salon tower bless fly where inmate trouble daughter"); - // var words = "gospel awkward uphold orchard spike elite inform danger sheriff lens power monitor"; - // var founderKey = derivationOperations.DeriveFounderKey(new WalletWords { Words = words }, 1); - // var founderRecoveryKey = derivationOperations.DeriveFounderRecoveryKey(new WalletWords { Words = words }, 1); - // var projectId = derivationOperations.DeriveProjectId(founderKey); - // var angorKey = derivationOperations.DeriveAngorKey(founderKey, rootKey); - // var script = derivationOperations.AngorKeyToScript(angorKey); + var words = "gospel awkward uphold orchard spike elite inform danger sheriff lens power monitor"; + var founderKey = derivationOperations.DeriveFounderKey(new WalletWords { Words = words }, 1); + var founderRecoveryKey = derivationOperations.DeriveFounderRecoveryKey(new WalletWords { Words = words }, 1); + var projectId = derivationOperations.DeriveProjectId(founderKey); + var angorKey = derivationOperations.DeriveAngorKey(founderKey, rootKey); + var script = derivationOperations.AngorKeyToScript(angorKey); - //} + } [Fact] public void DeriveFounderKey_InvalidMnemonicWords_ThrowsException() diff --git a/src/Angor/Client/Pages/Invest.razor b/src/Angor/Client/Pages/Invest.razor index 75e8036b..aed4f011 100644 --- a/src/Angor/Client/Pages/Invest.razor +++ b/src/Angor/Client/Pages/Invest.razor @@ -58,7 +58,13 @@

Investment Page

Here is a small explanation of the project. You can view more details about the project here.

- +

To invest in this project, the founder must sign a recovery agreement. +
+ This agreement ensures that in the event the project does not succeed, you will be able to recover your funds. +
+ This provides a safety net for your investment, giving you peace of mind that your financial contribution is protected.

+ +

ProjectId: @ProjectId

Target amount: @project.ProjectInfo.TargetAmount BTC

Starting in: @project.ProjectInfo.StartDate.ToString("dd/MM/yyyy")

@@ -230,7 +236,7 @@
- +
-
- - + + +
+
+
+ + + + + + + + + + + + + + + + + @foreach (var project in projects) + { + Stats.TryGetValue(project.ProjectInfo.ProjectIdentifier, out var stats); + var nostrPubKey = project.ProjectInfo.NostrPubKey; + investmentRequestsMap.TryGetValue(nostrPubKey, out bool hasInvestmentRequests); + - - - - - - - - - + + + + + + + + + + - - - - @foreach (var project in projects) - { - Stats.TryGetValue(project.ProjectInfo.ProjectIdentifier, out var stats); - - - - - - - - - - - - - } - -
NameFunding Target (@network.CoinTicker)Raised (@network.CoinTicker)Raised (% Target)Project StatusMy Investment (@network.CoinTicker)Spent by FounderAvailable to FounderIn RecoveryFounder Approval
NameFunding Target (@network.CoinTicker)Raised (@network.CoinTicker)Raised (% Target)Project StatusMy Investment (@network.CoinTicker)Spent by FounderAvailable to FounderIn Recovery + @project.Metadata?.Name + @project.ProjectInfo.TargetAmount @network.CoinTicker@Money.Satoshis(stats?.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker @((stats?.AmountInvested ?? 0) * 100 / Money.Coins(project.ProjectInfo.TargetAmount).Satoshi) % + @if (project.ProjectInfo.StartDate < DateTime.UtcNow) + { +

Funding

+ } + else + { +

Live

+ } +
+ @Money.Satoshis(project.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker + @if (!project.SignaturesInfo?.Signatures.Any() ?? false) + { + + } + --@Money.Satoshis(project.AmountInRecovery ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker + @if (hasInvestmentRequests) + { + Approved + } + +
- @project.Metadata?.Name - @project.ProjectInfo.TargetAmount @network.CoinTicker@Money.Satoshis(stats?.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker @((stats?.AmountInvested ?? 0) * 100 / Money.Coins(project.ProjectInfo.TargetAmount).Satoshi) % - @if (project.ProjectInfo.StartDate < DateTime.UtcNow) - { - Funding - } - else - { - Live - } - - @Money.Satoshis(project.AmountInvested ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker - @if (!project.SignaturesInfo?.Signatures.Any() ?? false) - { - - } - --@Money.Satoshis(project.AmountInRecovery ?? 0).ToUnit(MoneyUnit.BTC) @network.CoinTicker
-
+ } + + - + + + @@ -209,6 +224,8 @@ long TotalWallet = 0; int TotalFundedProjects = 0; long TotalInRecovery = 0; + + private Dictionary investmentRequestsMap = new Dictionary(); public Dictionary Stats = new(); @@ -234,6 +251,7 @@ TotalInRecovery = projects.Sum(s => s.AmountInRecovery ?? 0); await RefreshBalance(); + await checkSignatureFromFounder(); } } @@ -277,9 +295,38 @@ } } + + private async Task HandleSignatureReceivedAsync(string nostrPubKey, string signatureContent) + { + if (investmentRequestsMap.ContainsKey(nostrPubKey)) + { + investmentRequestsMap[nostrPubKey] = true; + StateHasChanged(); + } + } + + private async Task checkSignatureFromFounder() + { + foreach (var project in projects) + { + investmentRequestsMap[project.ProjectInfo.NostrPubKey] = false; + + var investorNostrPubKey = storage.GetNostrPublicKeyPerProject(project.ProjectInfo.ProjectIdentifier); + if (!string.IsNullOrEmpty(investorNostrPubKey)) + { + _SignService.LookupSignatureForInvestmentRequest( + investorNostrPubKey, + project.ProjectInfo.NostrPubKey, + project.SignaturesInfo.TimeOfSignatureRequest.Value, + project.SignaturesInfo.SignatureRequestEventId, + async signatureContent => await HandleSignatureReceivedAsync(project.ProjectInfo.NostrPubKey, signatureContent) + ); + } + } + } - + private async Task RefreshBalance() { try @@ -311,11 +358,15 @@ RefreshBalanceTriggered = false; return; } - + + RefreshBalanceTriggered = true; var words = await passwordComponent.GetWalletAsync(); var NostrDMPrivateKey = await _DerivationOperations.DeriveProjectNostrPrivateKeyAsync(words, 1); var NostrDMPrivateKeyHex = Encoders.Hex.EncodeData(NostrDMPrivateKey.ToBytes()); - + var NostrDMPubkey = _DerivationOperations.DeriveNostrPubKey(words, 1); + + await checkSignatureFromFounder(); + var rootNostrPubKeyHex = _DerivationOperations.DeriveNostrPubKey(words, 0); @@ -431,6 +482,10 @@ { projects.Add(investorProject); storage.AddInvestmentProject(investorProject); + + // todo: David to check this, not sure this is correct. + // storage.SetNostrPublicKeyPerProject(investorProject.ProjectInfo.ProjectIdentifier, investorNostrPrivateKey.PubKey.ToHex()[2..]); + RefreshBalanceTriggered = false; StateHasChanged(); } diff --git a/src/Angor/Client/Storage/ClientStorage.cs b/src/Angor/Client/Storage/ClientStorage.cs index c66420c0..68f57d5e 100644 --- a/src/Angor/Client/Storage/ClientStorage.cs +++ b/src/Angor/Client/Storage/ClientStorage.cs @@ -214,4 +214,14 @@ public string GetNetwork() { return _storage.GetItem("network"); } + + public void SetNostrPublicKeyPerProject(string projectId,string nostrPubKey) + { + _storage.SetItem($"project:{projectId}:nostrKey", nostrPubKey); + } + + public string GetNostrPublicKeyPerProject(string projectId) + { + return _storage.GetItem($"project:{projectId}:nostrKey"); + } } \ No newline at end of file diff --git a/src/Angor/Client/Storage/IClientStorage.cs b/src/Angor/Client/Storage/IClientStorage.cs index 7f1f572a..45fb8d26 100644 --- a/src/Angor/Client/Storage/IClientStorage.cs +++ b/src/Angor/Client/Storage/IClientStorage.cs @@ -27,7 +27,8 @@ public interface IClientStorage // void RemoveSignatures(SignatureInfo signatureInfo); // List GetSignatures(); // void DeleteSignatures(); - + void SetNostrPublicKeyPerProject(string projectId, string nostrPubKey); + string GetNostrPublicKeyPerProject(string projectId); SettingsInfo GetSettingsInfo(); void SetSettingsInfo(SettingsInfo settingsInfo);