package wallet import ( "context" "fmt" "os" "strings" "time" ) const ( WalletConnectStatusStub = "stub" WalletConnectStatusDisabled = "disabled" ) // Config describes the public WalletConnect v2 posture exposed to clients. type Config struct { Status string `json:"status"` Enabled bool `json:"enabled"` ProjectID string `json:"projectId"` RelayURL string `json:"relayUrl"` MetadataURL string `json:"metadataUrl"` RequiredNamespaces []string `json:"requiredNamespaces"` SupportedChains []int `json:"supportedChains"` FallbackAuth string `json:"fallbackAuth"` Message string `json:"message"` UpdatedAt string `json:"updatedAt"` } // ConnectResponse is returned while WalletConnect session bridging remains a stub. type ConnectResponse struct { Status string `json:"status"` Enabled bool `json:"enabled"` URI string `json:"uri,omitempty"` SessionID string `json:"sessionId,omitempty"` ExpiresAt string `json:"expiresAt,omitempty"` FallbackAuth string `json:"fallbackAuth"` Message string `json:"message"` } // Session represents a wallet session snapshot for future WalletConnect integration. type Session struct { SessionID string `json:"sessionId"` Address string `json:"address,omitempty"` ChainID int `json:"chainId,omitempty"` Connected bool `json:"connected"` Status string `json:"status"` Message string `json:"message"` } // WalletConnect handles WalletConnect v2 integration posture for the explorer API. type WalletConnect struct { projectID string relayURL string chainID int } // NewWalletConnect creates a WalletConnect handler using deployment env vars. func NewWalletConnect(chainID int) *WalletConnect { projectID := strings.TrimSpace(os.Getenv("WALLETCONNECT_PROJECT_ID")) relayURL := strings.TrimSpace(os.Getenv("WALLETCONNECT_RELAY_URL")) if relayURL == "" { relayURL = "wss://relay.walletconnect.org" } return &WalletConnect{ projectID: projectID, relayURL: relayURL, chainID: chainID, } } func (wc *WalletConnect) enabled() bool { return wc.projectID != "" } // PublicConfig returns the read-only WalletConnect config surface for clients. func (wc *WalletConnect) PublicConfig() Config { status := WalletConnectStatusStub if !wc.enabled() { status = WalletConnectStatusDisabled } return Config{ Status: status, Enabled: wc.enabled(), ProjectID: wc.projectID, RelayURL: wc.relayURL, MetadataURL: "/api/v1/walletconnect/metadata", RequiredNamespaces: []string{"eip155"}, SupportedChains: []int{wc.chainID, 1}, FallbackAuth: "/api/v1/auth/wallet", Message: wc.publicMessage(), UpdatedAt: time.Now().UTC().Format(time.RFC3339), } } func (wc *WalletConnect) publicMessage() string { if wc.enabled() { return "WalletConnect v2 config is published, but session bridging is still stubbed. Use browser wallet auth at /api/v1/auth/wallet until mobile QR sessions ship." } return "WalletConnect v2 is not configured. Set WALLETCONNECT_PROJECT_ID to publish relay config; browser wallet auth remains available at /api/v1/auth/wallet." } // Connect initiates a wallet connection. Live QR sessions are not implemented yet. func (wc *WalletConnect) Connect(_ context.Context) (*ConnectResponse, error) { return &ConnectResponse{ Status: WalletConnectStatusStub, Enabled: wc.enabled(), FallbackAuth: "/api/v1/auth/wallet", Message: "WalletConnect session creation is stubbed. Use browser extension wallet auth until the relay bridge is enabled.", }, fmt.Errorf("walletconnect session bridge not implemented") } // GetSession gets a wallet session snapshot. Storage is not implemented yet. func (wc *WalletConnect) GetSession(_ context.Context, sessionID string) (*Session, error) { if strings.TrimSpace(sessionID) == "" { return nil, fmt.Errorf("session id is required") } return &Session{ SessionID: sessionID, Connected: false, Status: WalletConnectStatusStub, Message: "WalletConnect session lookup is stubbed.", }, fmt.Errorf("walletconnect session storage not implemented") }