@@ -12,6 +12,7 @@ import (
1212 "github.com/ethereum/go-ethereum/common"
1313 "github.com/nathfavour/settlerengine/internal/ports"
1414 "github.com/nathfavour/settlerengine/pkg/crypto"
15+ "github.com/nathfavour/settlerengine/pkg/crypto/casper"
1516 "github.com/nathfavour/settlerengine/pkg/storage"
1617)
1718
@@ -24,25 +25,28 @@ const (
2425
2526// Config defines the configuration for the x402 middleware.
2627type Config struct {
27- DomainParams crypto.DomainParams
28- NonceExpiry time.Duration
29- Recipient string
30- Asset string
31- Amount string
32- PriceResolver PriceResolver
33- DB * storage.DB
34- Registry ports.AgentRegistry
35- MinReputation * big.Int // Optional: block agents with score below this
28+ DomainParams crypto.DomainParams
29+ NonceExpiry time.Duration
30+ Recipient string
31+ Asset string
32+ Amount string
33+ PriceResolver PriceResolver
34+ DB * storage.DB
35+ Registry ports.AgentRegistry
36+ MinReputation * big.Int // Optional: block agents with score below this
37+ CasperFacilitatorURL string
38+ CasperFacilitatorToken string
3639}
3740
3841// PriceResolver dynamically determines the payment requirements for a request.
3942type PriceResolver func (r * http.Request ) (amount , asset , recipient string , err error )
4043
4144// Middleware handles the x402 handshake.
4245type Middleware struct {
43- config Config
44- nonces * NonceManager
45- verified sync.Map // Map of signature hash to Address
46+ config Config
47+ nonces * NonceManager
48+ verified sync.Map // Map of signature hash to Address
49+ casperClient * casper.CasperFacilitatorClient
4650}
4751
4852func NewMiddleware (cfg Config ) * Middleware {
@@ -53,8 +57,9 @@ func NewMiddleware(cfg Config) *Middleware {
5357 }
5458
5559 return & Middleware {
56- config : cfg ,
57- nonces : NewNonceManager (),
60+ config : cfg ,
61+ nonces : NewNonceManager (),
62+ casperClient : casper .NewCasperFacilitatorClient (cfg .CasperFacilitatorURL , cfg .CasperFacilitatorToken ),
5863 }
5964}
6065
@@ -76,6 +81,38 @@ func (m *Middleware) Handler(next http.Handler) http.Handler {
7681 // 1. Try to parse payment header
7782 payload , err := ParseHeader (r )
7883 if err == nil {
84+ // Check if we are running in Casper mode (either explicitly marked, or chain-id is casper-testnet)
85+ isCasper := payload .Scheme == "casper-native" ||
86+ m .config .DomainParams .ChainID != nil && m .config .DomainParams .ChainID .String () == "0" && m .config .Asset == "CSPR" ||
87+ payload .Intent .Asset == "CSPR"
88+
89+ if isCasper {
90+ // Verify via Casper Facilitator
91+ details := casper.PaymentDetails {
92+ Recipient : m .config .Recipient ,
93+ Amount : m .config .Amount ,
94+ Asset : "CSPR" ,
95+ Nonce : payload .Intent .Nonce ,
96+ Network : "casper-testnet" ,
97+ }
98+ if details .Nonce == "" {
99+ details .Nonce = "demo-nonce"
100+ }
101+
102+ valid , err := m .casperClient .VerifyPayload (payload .Signature , details )
103+ if err == nil && valid {
104+ // Auto settle/forward payment via facilitator
105+ txHash , err := m .casperClient .SettlePayload (payload .Signature , details )
106+ if err == nil && txHash != "" {
107+ fmt .Printf ("✅ Casper Facilitator: Verified and settled transaction: %s\n " , txHash )
108+ // Store dummy address for signer in context for downstream handlers
109+ ctx := context .WithValue (r .Context (), SignerContextKey , common .HexToAddress ("0x0" ))
110+ next .ServeHTTP (w , r .WithContext (ctx ))
111+ return
112+ }
113+ }
114+ }
115+
79116 // 2. Check Cache & DB (Idempotency)
80117 if addr , ok := m .verified .Load (payload .Signature ); ok {
81118 ctx := context .WithValue (r .Context (), SignerContextKey , addr .(common.Address ))
@@ -145,6 +182,20 @@ func (m *Middleware) Handler(next http.Handler) http.Handler {
145182
146183 nonce , _ := m .nonces .Generate (m .config .NonceExpiry )
147184
185+ // Set the Payment-Required header formatted for Casper native agents
186+ casperChallenge := map [string ]interface {}{
187+ "accepts" : []map [string ]interface {}{
188+ {
189+ "scheme" : "casper-native" ,
190+ "networkId" : "casper-testnet" ,
191+ "amount" : amount ,
192+ "recipient" : recipient ,
193+ },
194+ },
195+ }
196+ challengeBytes , _ := json .Marshal (casperChallenge )
197+ w .Header ().Set (HeaderPaymentRequired , string (challengeBytes ))
198+
148199 resp := ChallengeResponse {
149200 Status : http .StatusPaymentRequired ,
150201 Title : "Payment Required" ,
0 commit comments