Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .github/prompts/plan-methodNamingConsistency.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Method Naming Consistency Review

## Angor Investment Flow Method Mapping

### Flow Step → Method Mapping

| Step | Description | Interface | Actual Method(s) |
|------|-------------|-----------|------------------|
| **1** | Founder creates project (invest/fund/subscribe) | `IProjectAppService`, `IFounderAppService` | `CreateNewProjectKeysAsync()` → `CreateProjectProfile()` → `CreateProjectInfo()` → `CreateProject()` |
| **2** | Investor invests in project | `IInvestmentAppService` | `CreateInvestmentDraft()` |
| **3** | Below threshold - no penalty needed | `IInvestmentAppService` | `IsInvestmentAbovePenaltyThreshold()` → `ConfirmInvestment()` (direct publish) |
| **4** | Above threshold - request via Nostr | `IInvestmentAppService` | `IsInvestmentAbovePenaltyThreshold()` → `SubmitInvestment()` |
| **4b** | Investor cancels pending investment | `IInvestmentAppService` | `CancelInvestment()` |
| **5** | Founder lists & approves requests | `IFounderAppService` | `GetInvestments()` → `ApproveInvestment()` |
| **6** | Investor asks for approved projects | `IInvestmentAppService` | `GetInvestorProjects()` |
| **7** | Investor publishes approved investment | `IInvestmentAppService` | `ConfirmInvestment()` |
| **8** | Founder gets list of all investments | `IFounderAppService`, `IProjectInvestmentsService` | `GetClaimableTransactions()` / `ScanFullInvestments()` |
| **9** | Founder spends unlocked stage UTXOs | `IFounderAppService` | `GetClaimableTransactions()` → `Spend()` → `SubmitTransactionFromDraft()` |
| **10** | Investor claims back funds | `IInvestmentAppService` | `GetInvestorProjectRecovery()` |
| **11** | Below threshold - claim immediately | `IInvestmentAppService` | `BuildReleaseInvestorFunds()` → `SubmitTransactionFromDraft()` |
| **12** | Above threshold - penalty transaction | `IInvestmentAppService` | `BuildRecoverInvestorFunds()` → `SubmitTransactionFromDraft()` |
| **13** | After penalty timelock - claim | `IInvestmentAppService` | `GetPenalties()` → `BuildClaimInvestorEndOfProjectFunds()` → `SubmitTransactionFromDraft()` |
| **14** | Founder releases funds to investor | `IFounderAppService` | `GetReleasableTransactions()` → `ReleaseInvestorTransactions()` |

---

## High Priority Issues

| Current Name | Issue | Proposed Name |
|--------------|-------|---------------|
| `GetReleasableTransactions` | Misspelled ("Releaseable" in type) | Fix typo: `GetReleaseableTransactionsRequest` → `GetReleasableTransactionsRequest` |
| `SubmitInvestment` | Confusing - actually requests signatures, not publishes | `RequestInvestmentApproval` |
| `ConfirmInvestment` | Actually publishes to blockchain | `PublishInvestment` |
| `SubmitTransactionFromDraft` (both interfaces) | "Submit" is vague | `PublishTransaction` |

---

## Medium Priority Issues

| Current Name | Issue | Proposed Name |
|--------------|-------|---------------|
| `Latest` | Missing verb | `GetLatestProjects` |
| `Spend` | Missing noun, too generic | `SpendStageFunds` or `ClaimStageFunds` |
| `CreateInvestmentDraft` | Inconsistent with `Build*` pattern | `BuildInvestmentDraft` (align with other Build methods) |
| `BuildRecoverInvestorFunds` | "Recover" is verb, should match pattern | `BuildRecoveryTransaction` |
| `BuildReleaseInvestorFunds` | Redundant "Investor" (already in interface) | `BuildReleaseTransaction` |
| `BuildClaimInvestorEndOfProjectFunds` | Too long, redundant role | `BuildEndOfProjectClaim` |

---

## Proposed Consistent Naming Conventions

**1. Verb Categories:**
- `Get*` - Query/retrieve data
- `Build*` - Create transaction drafts (not yet published)
- `Publish*` - Broadcast to blockchain
- `Request*` - Send request to another party (Nostr)
- `Approve*` / `Cancel*` - State change actions
- `Scan*` / `Check*` - Blockchain state queries

**2. Remove redundant role prefixes** when method is already in a role-specific interface:
- `IInvestmentAppService.GetInvestorProjects` → `GetProjects`
- `IInvestmentAppService.GetInvestorProjectRecovery` → `GetProjectRecovery`
- `IFounderAppService.ReleaseInvestorTransactions` → `ReleaseTransactions`

**3. Use Request/Response DTO pattern consistently** across all interfaces (IProjectAppService currently mixes styles)

---

## Full Proposed Rename Table (Flow Order)

| Step | Current | Proposed | Rationale |
|------|---------|----------|-----------|
| 1 | `CreateNewProjectKeysAsync` | `CreateProjectKeys` | Remove "New", drop Async suffix (C# convention) |
| 1 | `CreateProjectProfile` | ✓ Keep | |
| 1 | `CreateProjectInfo` | ✓ Keep | |
| 1 | `CreateProject` | ✓ Keep | |
| 2 | `CreateInvestmentDraft` | `BuildInvestmentDraft` | Align with other Build* methods |
| 3 | `IsInvestmentAbovePenaltyThreshold` | `CheckPenaltyThreshold` | Consistent with Check* pattern |
| 4 | `SubmitInvestment` | `RequestInvestmentApproval` | Clarifies it's a Nostr request |
| 4b | `CancelInvestment` | `CancelInvestmentRequest` | Clarifies what's being cancelled |
| 5 | `GetInvestments` | `GetPendingApprovals` | Clarifies these are pending requests |
| 5 | `ApproveInvestment` | ✓ Keep | |
| 6 | `GetInvestorProjects` | `GetInvestments` | Simpler, role is in interface |
| 7 | `ConfirmInvestment` | `PublishInvestment` | Clarifies blockchain action |
| 8 | `GetClaimableTransactions` | ✓ Keep | |
| 8 | `ScanFullInvestments` | `ScanInvestments` | Remove "Full" |
| 9 | `Spend` | `SpendStageFunds` | Add missing noun |
| 9 | `SubmitTransactionFromDraft` | `PublishTransaction` | Simpler, clearer |
| 10 | `GetInvestorProjectRecovery` | `GetRecoveryStatus` | Simpler |
| 11 | `BuildReleaseInvestorFunds` | `BuildReleaseTransaction` | Remove redundant role |
| 12 | `BuildRecoverInvestorFunds` | `BuildRecoveryTransaction` | Noun form consistency |
| 13 | `GetPenalties` | ✓ Keep | |
| 13 | `BuildClaimInvestorEndOfProjectFunds` | `BuildPenaltyClaim` | Shorter, clearer |
| 14 | `GetReleasableTransactions` | `GetReleasableTransactions` | Just fix typo in DTO |
| 14 | `ReleaseInvestorTransactions` | `ReleaseFunds` | Shorter |

---

## Open Questions

1. **Breaking changes**: These renames would be breaking changes. Do you want a migration strategy (e.g., add new methods with old as `[Obsolete]`)?

2. **Request/Response DTO alignment**: `IProjectAppService` uses direct parameters while others use DTOs. Should we align all to the DTO pattern?

3. **"Build" vs "Create" decision**: Proposed aligning on `Build*` for transaction drafts. Do you prefer `Create*Draft` instead for all?
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace Angor.Sdk.Funding.Founder.Dtos;

public record ReleaseableTransactionDto
public record ReleasableTransactionDto
{
public DateTime? Released { get; init; }
public required DateTime Arrived { get; init; }
public required DateTime Approved { get; init; }
public required string InvestmentEventId { get; init; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class FounderAppService(IMediator mediator) : IFounderAppService

public Task<Result<GetClaimableTransactions.GetClaimableTransactionsResponse>> GetClaimableTransactions(GetClaimableTransactions.GetClaimableTransactionsRequest request) => mediator.Send(request);

public Task<Result<GetReleaseableTransactions.GetReleaseableTransactionsResponse>> GetReleasableTransactions(GetReleaseableTransactions.GetReleaseableTransactionsRequest request) => mediator.Send(request);
public Task<Result<GetReleasableTransactions.GetReleasableTransactionsResponse>> GetReleasableTransactions(GetReleasableTransactions.GetReleasableTransactionsRequest request) => mediator.Send(request);

public Task<Result<ReleaseInvestorTransaction.ReleaseInvestorTransactionResponse>> ReleaseInvestorTransactions(ReleaseInvestorTransaction.ReleaseInvestorTransactionRequest request) => mediator.Send(request);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public interface IFounderAppService
Task<Result<ApproveInvestment.ApproveInvestmentResponse>> ApproveInvestment(ApproveInvestment.ApproveInvestmentRequest request);
Task<Result<SpendFounderStageTransaction.SpendFounderStageTransactionResponse>> Spend(SpendFounderStageTransaction.SpendFounderStageTransactionRequest request);
Task<Result<GetClaimableTransactions.GetClaimableTransactionsResponse>> GetClaimableTransactions(GetClaimableTransactions.GetClaimableTransactionsRequest request);
Task<Result<GetReleaseableTransactions.GetReleaseableTransactionsResponse>> GetReleasableTransactions(GetReleaseableTransactions.GetReleaseableTransactionsRequest request);
Task<Result<GetReleasableTransactions.GetReleasableTransactionsResponse>> GetReleasableTransactions(GetReleasableTransactions.GetReleasableTransactionsRequest request);
Task<Result<ReleaseInvestorTransaction.ReleaseInvestorTransactionResponse>> ReleaseInvestorTransactions(ReleaseInvestorTransaction.ReleaseInvestorTransactionRequest request);

Task<Result<CreateProjectNewKeys.CreateProjectNewKeysResponse>> CreateNewProjectKeysAsync(CreateProjectNewKeys.CreateProjectNewKeysRequest request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class GetClaimableTransactionsHandler(IProjectInvestmentsService projectI
{
public async Task<Result<GetClaimableTransactionsResponse>> Handle(GetClaimableTransactionsRequest request, CancellationToken cancellationToken)
{
var resultList = await projectInvestmentsService.ScanFullInvestments(request.ProjectId.Value);
var resultList = await projectInvestmentsService.ScanInvestments(request.ProjectId.Value);

if (resultList.IsFailure)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,43 @@

namespace Angor.Sdk.Funding.Founder.Operations;

public static class GetReleaseableTransactions
public static class GetReleasableTransactions
{
public record GetReleaseableTransactionsRequest(WalletId WalletId, ProjectId ProjectId) : IRequest<Result<GetReleaseableTransactionsResponse>>;
public record GetReleasableTransactionsRequest(WalletId WalletId, ProjectId ProjectId) : IRequest<Result<GetReleasableTransactionsResponse>>;

public record GetReleaseableTransactionsResponse(IEnumerable<ReleaseableTransactionDto> Transactions);
public record GetReleasableTransactionsResponse(IEnumerable<ReleasableTransactionDto> Transactions);

public class GetClaimableTransactionsHandler(ISignService signService, IProjectService projectService,
INostrDecrypter nostrDecrypter, ISerializer serializer) : IRequestHandler<GetReleaseableTransactionsRequest, Result<GetReleaseableTransactionsResponse>>
public class GetReleasableTransactionsHandler(ISignService signService, IProjectService projectService,
INostrDecrypter nostrDecrypter, ISerializer serializer) : IRequestHandler<GetReleasableTransactionsRequest, Result<GetReleasableTransactionsResponse>>
{
public async Task<Result<GetReleaseableTransactionsResponse>> Handle(GetReleaseableTransactionsRequest request, CancellationToken cancellationToken)
public async Task<Result<GetReleasableTransactionsResponse>> Handle(GetReleasableTransactionsRequest request, CancellationToken cancellationToken)
{

var projectResult = await projectService.GetAsync(request.ProjectId);
if (projectResult.IsFailure)
return Result.Failure<GetReleaseableTransactionsResponse>(projectResult.Error);
return Result.Failure<GetReleasableTransactionsResponse>(projectResult.Error);

var requests = await FetchSignatureRequestsAsync(projectResult.Value.NostrPubKey);

var items = requests.ToList();

var decryptResult = DecryptMessages(request.WalletId.Value, request.ProjectId, items);
var approvalTask = FetchFounderApprovalsSignaturesAsync(projectResult.Value.NostrPubKey, items);
var releaseTask = FetchFounderReleaseSignaturesAsync(projectResult.Value.NostrPubKey, items);

await Task.WhenAll(approvalTask, releaseTask,decryptResult);

var list = requests.Select(x => new ReleaseableTransactionDto
await Task.WhenAll(approvalTask, releaseTask, decryptResult);

var list = requests.Select(x => new ReleasableTransactionDto
{
Approved = x.ApprovaleTime,
Arrived = x.InvestmentRequestTime,
Released = x.ReleaseSignaturesTime,
InvestmentEventId = x.SignRecoveryRequestEventId
});
return Result.Success(new GetReleaseableTransactionsResponse(list));

return Result.Success(new GetReleasableTransactionsResponse(list));
}



public Task<IEnumerable<SignatureReleaseItem>> FetchSignatureRequestsAsync(string projectNostrPubKey)
Expand Down Expand Up @@ -107,10 +107,10 @@ private async Task<Result> DecryptMessages(string walletId, ProjectId projectId,

if (sigResJson.IsFailure)
continue;

var testingValidity = serializer.Deserialize<SignRecoveryRequest>(sigResJson.Value);
if (testingValidity is not null &&

if (testingValidity is not null &&
testingValidity.ProjectIdentifier == projectId.Value &&
!string.IsNullOrWhiteSpace(testingValidity.InvestmentTransactionHex) &&
!string.IsNullOrWhiteSpace(testingValidity.UnfundedReleaseAddress))
Expand All @@ -123,7 +123,7 @@ private async Task<Result> DecryptMessages(string walletId, ProjectId projectId,
signatureReleaseItem.SignRecoveryRequestEventId = null; // should we remove the item instead?
}
}

return Result.Success();
}

Expand All @@ -150,10 +150,10 @@ protected Task FetchFounderReleaseSignaturesAsync(string nostrPubKey, List<Signa
{
tcs.SetResult();
});

return tcs.Task;
}

private Task FetchFounderApprovalsSignaturesAsync(string nostrPubKey, List<SignatureReleaseItem> signaturesReleaseItems)
{
var tcs = new TaskCompletionSource();
Expand Down Expand Up @@ -183,7 +183,7 @@ private Task FetchFounderApprovalsSignaturesAsync(string nostrPubKey, List<Signa
{
tcs.SetResult();
});

return tcs.Task;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ namespace Angor.Sdk.Funding.Investor;
public interface IInvestmentAppService
{
Task<Result<CreateInvestment.CreateInvestmentTransactionResponse>> CreateInvestmentDraft(CreateInvestment.CreateInvestmentTransactionRequest request);
Task<Result<RequestInvestmentSignatures.RequestFounderSignaturesResponse>> SubmitInvestment(RequestInvestmentSignatures.RequestFounderSignaturesRequest request);
Task<Result<RequestInvestmentApproval.RequestInvestmentApprovalResponse>> RequestInvestmentApproval(RequestInvestmentApproval.RequestInvestmentApprovalRequest request);
Task<Result<CancelInvestmentSignatures.CancelInvestmentSignaturesResponse>> CancelInvestment(CancelInvestmentSignatures.CancelInvestmentSignaturesRequest request);
Task<Result<Investments.InvestmentsPortfolioResponse>> GetInvestorProjects(Investments.InvestmentsPortfolioRequest request);
Task<Result<PublishInvestment.PublishInvestmentResponse>> ConfirmInvestment(PublishInvestment.PublishInvestmentRequest request);
Task<Result<PublishInvestment.PublishInvestmentResponse>> PublishInvestment(PublishInvestment.PublishInvestmentRequest request);
Task<Result<GetPenalties.GetPenaltiesResponse>> GetPenalties(GetPenalties.GetPenaltiesRequest request);

// Methods for Investor - Manage funds
Expand All @@ -23,9 +23,9 @@ public interface IInvestmentAppService
Task<Result<ReleaseFunds.ReleaseFundsResponse>> BuildReleaseInvestorFunds(ReleaseFunds.ReleaseFundsRequest request);
Task<Result<ClaimEndOfProject.ClaimEndOfProjectResponse>> BuildClaimInvestorEndOfProjectFunds(ClaimEndOfProject.ClaimEndOfProjectRequest request);

Task<Result<PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionResponse>> SubmitTransactionFromDraft(PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionRequest request);
Task<Result<PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionResponse>> PublishTransaction(PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionRequest request);

Task<Result<CheckPenaltyThreshold.CheckPenaltyThresholdResponse>> IsInvestmentAbovePenaltyThreshold(CheckPenaltyThreshold.CheckPenaltyThresholdRequest request);
Task<Result<CheckPenaltyThreshold.CheckPenaltyThresholdResponse>> CheckPenaltyThreshold(CheckPenaltyThreshold.CheckPenaltyThresholdRequest request);

// Methods for monitoring external funding
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class InvestmentAppService(IMediator mediator) : IInvestmentAppService
public Task<Result<CreateInvestment.CreateInvestmentTransactionResponse>> CreateInvestmentDraft(CreateInvestment.CreateInvestmentTransactionRequest request)
=> mediator.Send(request);

public Task<Result<RequestInvestmentSignatures.RequestFounderSignaturesResponse>> SubmitInvestment(RequestInvestmentSignatures.RequestFounderSignaturesRequest request)
public Task<Result<RequestInvestmentApproval.RequestInvestmentApprovalResponse>> RequestInvestmentApproval(RequestInvestmentApproval.RequestInvestmentApprovalRequest request)
=> mediator.Send(request);

public Task<Result<CancelInvestmentSignatures.CancelInvestmentSignaturesResponse>> CancelInvestment(CancelInvestmentSignatures.CancelInvestmentSignaturesRequest request)
Expand All @@ -23,14 +23,14 @@ public class InvestmentAppService(IMediator mediator) : IInvestmentAppService
public Task<Result<Investments.InvestmentsPortfolioResponse>> GetInvestorProjects(Investments.InvestmentsPortfolioRequest request)
=> mediator.Send(request);

public Task<Result<PublishInvestment.PublishInvestmentResponse>> ConfirmInvestment(PublishInvestment.PublishInvestmentRequest request)
public Task<Result<PublishInvestment.PublishInvestmentResponse>> PublishInvestment(PublishInvestment.PublishInvestmentRequest request)
=> mediator.Send(request);

public Task<Result<GetPenalties.GetPenaltiesResponse>> GetPenalties(GetPenalties.GetPenaltiesRequest request)
=> mediator.Send(request);

public Task<Result<CheckPenaltyThreshold.CheckPenaltyThresholdResponse>> IsInvestmentAbovePenaltyThreshold(CheckPenaltyThreshold.CheckPenaltyThresholdRequest request)
=> mediator.Send(request);
public Task<Result<CheckPenaltyThreshold.CheckPenaltyThresholdResponse>> CheckPenaltyThreshold(CheckPenaltyThreshold.CheckPenaltyThresholdRequest request)
=> mediator.Send(request);

#region Methods for Investor/Manage funds. Remove this region ASAP. It's only for clarity.

Expand All @@ -41,13 +41,13 @@ public class InvestmentAppService(IMediator mediator) : IInvestmentAppService
=> mediator.Send(request);

public Task<Result<ReleaseFunds.ReleaseFundsResponse>> BuildReleaseInvestorFunds(ReleaseFunds.ReleaseFundsRequest request)
=> mediator.Send(request);
=> mediator.Send(request);

public Task<Result<ClaimEndOfProject.ClaimEndOfProjectResponse>> BuildClaimInvestorEndOfProjectFunds(ClaimEndOfProject.ClaimEndOfProjectRequest request)
=> mediator.Send(request);

public Task<Result<PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionResponse>> SubmitTransactionFromDraft(PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionRequest request)
=> mediator.Send(request);
public Task<Result<PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionResponse>> PublishTransaction(PublishAndStoreInvestorTransaction.PublishAndStoreInvestorTransactionRequest request)
=> mediator.Send(request);

#endregion

Expand Down
Loading
Loading