-
Notifications
You must be signed in to change notification settings - Fork 0
feat(s3,s4): project scaffolds — S3 Fiat On-Ramp + S4 Blockchain & Custody (STA-119, STA-131) #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| plugins { | ||
| `java-library` | ||
| } | ||
|
|
||
| dependencies { | ||
| api("jakarta.validation:jakarta.validation-api") | ||
| api("com.fasterxml.jackson.core:jackson-annotations") | ||
|
|
||
| testImplementation("org.junit.jupiter:junit-jupiter") | ||
| testImplementation("org.assertj:assertj-core") | ||
| testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||
| } | ||
|
|
||
| tasks.withType<Test> { | ||
| useJUnitPlatform() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.stablecoin.payments.custody.api; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public record ApiError( | ||
| String code, | ||
| String status, | ||
| String message, | ||
| Map<String, List<String>> errors | ||
| ) { | ||
| public static ApiError of(String code, String status, String message) { | ||
| return new ApiError(code, status, message, Map.of()); | ||
| } | ||
|
|
||
| public static ApiError withErrors(String code, String status, String message, | ||
| Map<String, List<String>> errors) { | ||
| return new ApiError(code, status, message, errors); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.stablecoin.payments.custody.api; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.UUID; | ||
|
|
||
| public record TransferResponse( | ||
| UUID transferId, | ||
| UUID paymentId, | ||
| String status, | ||
| String chainId, | ||
| String stablecoin, | ||
| String amount, | ||
| String fromAddress, | ||
| String toAddress, | ||
| String txHash, | ||
| String blockNumber, | ||
| Integer confirmations, | ||
| String gasUsed, | ||
| String gasPriceGwei, | ||
| Integer estimatedConfirmationS, | ||
| Instant confirmedAt, | ||
| Instant createdAt | ||
| ) {} | ||
|
Comment on lines
+6
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Clean DTO structure — consider typed status enum. Using Consider defining a 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.stablecoin.payments.custody.api; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.List; | ||
| import java.util.UUID; | ||
|
|
||
| public record WalletBalanceResponse( | ||
| UUID walletId, | ||
| String address, | ||
| String chainId, | ||
| List<BalanceEntry> balances, | ||
| Instant updatedAt | ||
| ) { | ||
| public record BalanceEntry( | ||
| String stablecoin, | ||
| String availableBalance, | ||
| String reservedBalance, | ||
| String blockchainBalance, | ||
| String lastIndexedBlock | ||
| ) {} | ||
|
Comment on lines
+14
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider type consistency across API modules.
For cross-module consistency, consider aligning on one approach for monetary values across both service APIs. 🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.stablecoin.payments.custody.api.events; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.UUID; | ||
|
|
||
| public record ChainReturnConfirmed( | ||
| String schemaVersion, | ||
| UUID eventId, | ||
| String eventType, | ||
| UUID transferId, | ||
| UUID parentTransferId, | ||
| UUID paymentId, | ||
| UUID correlationId, | ||
| String chainId, | ||
| String txHash, | ||
| Instant confirmedAt | ||
| ) { | ||
| public static final String EVENT_TYPE = "chain.return.confirmed"; | ||
| public static final String SCHEMA_VERSION = "1.0"; | ||
| } | ||
|
Comment on lines
+6
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lock down the event identity fields. Line 7 and Line 9 duplicate metadata that is already fixed by Line 18 and Line 19. Leaving those fields unconstrained means 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.stablecoin.payments.custody.api.events; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.UUID; | ||
|
|
||
| public record ChainTransferConfirmed( | ||
| String schemaVersion, | ||
| UUID eventId, | ||
| String eventType, | ||
| UUID transferId, | ||
| UUID paymentId, | ||
| UUID correlationId, | ||
| String chainId, | ||
| String txHash, | ||
| String blockNumber, | ||
| Integer confirmations, | ||
| String gasUsed, | ||
| Instant confirmedAt | ||
| ) { | ||
| public static final String EVENT_TYPE = "chain.transfer.confirmed"; | ||
| public static final String SCHEMA_VERSION = "1.0"; | ||
| } | ||
|
Comment on lines
+6
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Protect the event contract from mismatched metadata. Line 7 and Line 9 should not be freely settable when this record already defines 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.stablecoin.payments.custody.api.events; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.Instant; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public record ChainTransferFailed( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String schemaVersion, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UUID eventId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String eventType, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UUID transferId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UUID paymentId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UUID correlationId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String chainId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String reason, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String errorCode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Instant failedAt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static final String EVENT_TYPE = "chain.transfer.failed"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static final String SCHEMA_VERSION = "1.0"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce fixed event metadata in the record API. Line 7 and Line 9 make Suggested fix public record ChainTransferFailed(
String schemaVersion,
UUID eventId,
String eventType,
UUID transferId,
UUID paymentId,
UUID correlationId,
String chainId,
String reason,
String errorCode,
Instant failedAt
) {
public static final String EVENT_TYPE = "chain.transfer.failed";
public static final String SCHEMA_VERSION = "1.0";
+
+ public ChainTransferFailed {
+ if (!SCHEMA_VERSION.equals(schemaVersion)) {
+ throw new IllegalArgumentException("Unsupported schemaVersion: " + schemaVersion);
+ }
+ if (!EVENT_TYPE.equals(eventType)) {
+ throw new IllegalArgumentException("Unexpected eventType: " + eventType);
+ }
+ }
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.stablecoin.payments.custody.api.events; | ||
|
|
||
| import java.time.Instant; | ||
| import java.util.UUID; | ||
|
|
||
| public record ChainTransferSubmitted( | ||
| String schemaVersion, | ||
| UUID eventId, | ||
| String eventType, | ||
| UUID transferId, | ||
| UUID paymentId, | ||
| UUID correlationId, | ||
| String chainId, | ||
| String stablecoin, | ||
| String amount, | ||
| String txHash, | ||
| String fromAddress, | ||
| String toAddress, | ||
| Instant submittedAt | ||
| ) { | ||
| public static final String EVENT_TYPE = "chain.transfer.submitted"; | ||
| public static final String SCHEMA_VERSION = "1.0"; | ||
| } | ||
|
Comment on lines
+6
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not let producers override Line 7 and Line 9 should be invariant contract metadata for this event, not arbitrary inputs. As written, a producer can instantiate 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| plugins { | ||
| `java-library` | ||
| } | ||
|
|
||
| dependencies { | ||
| api(project(":blockchain-custody:blockchain-custody-api")) | ||
| implementation("org.springframework.cloud:spring-cloud-starter-openfeign") | ||
|
|
||
| testImplementation("org.junit.jupiter:junit-jupiter") | ||
| testImplementation("org.assertj:assertj-core") | ||
| testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||
| } | ||
|
|
||
| tasks.withType<Test> { | ||
| useJUnitPlatform() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.stablecoin.payments.custody.client; | ||
|
|
||
| import com.stablecoin.payments.custody.api.TransferResponse; | ||
| import com.stablecoin.payments.custody.api.WalletBalanceResponse; | ||
| import org.springframework.cloud.openfeign.FeignClient; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @FeignClient(name = "blockchain-custody-service", url = "${app.services.blockchain-custody.url}") | ||
| public interface BlockchainCustodyClient { | ||
|
|
||
| @GetMapping(value = "/v1/transfers/{transferId}", produces = "application/json") | ||
| TransferResponse getTransfer(@PathVariable("transferId") UUID transferId); | ||
|
|
||
| @GetMapping(value = "/v1/wallets/{walletId}/balance", produces = "application/json") | ||
| WalletBalanceResponse getWalletBalance(@PathVariable("walletId") UUID walletId); | ||
| } | ||
|
Comment on lines
+11
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider adding resilience configuration for production. The Feign client scaffold is clean. For production readiness, consider adding:
These can be added incrementally as the service matures. 🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider defensive copy of
errorsmap.withErrorspasses the map directly. If the caller retains a reference and mutates it, theApiErrorinstance becomes inconsistent. Wrap withMap.copyOf():♻️ Suggested fix
public static ApiError withErrors(String code, String status, String message, Map<String, List<String>> errors) { - return new ApiError(code, status, message, errors); + return new ApiError(code, status, message, errors == null ? Map.of() : Map.copyOf(errors)); }📝 Committable suggestion
🤖 Prompt for AI Agents