internal/domain/donations.go
Rama main · réplica de lectura del repositorio original.
package domain
import (
"errors"
"time"
)
// Crypto donations: one deterministic BIP32 deposit address per user, the
// same address on every EVM-compatible chain (same secp256k1 curve and
// derivation path; only the RPC endpoint and token contracts differ per
// chain). A background watcher per enabled chain detects incoming native
// currency or stablecoin transfers to a user's address and credits Stars
// once the deposit reaches the chain's configured confirmation depth. See
// docs/donations.md.
var (
ErrDonationWalletNotConfigured = errors.New("donations: wallet not configured")
ErrDonationWalletAlreadyExists = errors.New("donations: wallet already exists")
ErrDonationChainDisabled = errors.New("donations: chain disabled or misconfigured")
ErrDonationChainNotFound = errors.New("donations: chain not found")
ErrDonationDepositInvalid = errors.New("donations: deposit invalid")
)
// DonationDepositStatus is the deposit's lifecycle: pending (seen, not yet
// at the required confirmation depth) -> confirmed (depth reached, about to
// be priced and credited) -> credited (Stars granted; terminal) or orphaned
// (the block it was in was reorganized out of the canonical chain before
// reaching depth; terminal, never credited).
type DonationDepositStatus string
const (
DonationDepositPending DonationDepositStatus = "pending"
DonationDepositConfirmed DonationDepositStatus = "confirmed"
DonationDepositCredited DonationDepositStatus = "credited"
DonationDepositOrphaned DonationDepositStatus = "orphaned"
)
// DonationChain is one configured EVM-compatible network. Disabled chains
// (Enabled false, typically because RPCURL is still empty) are never
// watched. ManualUSDRateMicros is USD per one whole native unit, scaled by
// 1e6, used when PriceFeedAddress has no reachable Chainlink aggregator
// (e.g. local Ganache, or before an operator sets one).
type DonationChain struct {
Key string
Name string
ChainID int64
RPCURL string
WSURL string
NativeSymbol string
NativeDecimals int
ConfirmationsRequired int
PriceFeedAddress string
ManualUSDRateMicros int64
Enabled bool
}
// Valid reports whether the chain is enabled and has enough configuration
// to actually be watched.
func (c DonationChain) Valid() bool {
return c.Key != "" && c.ChainID > 0 && c.NativeDecimals > 0 && c.ConfirmationsRequired > 0
}
// Watchable reports whether the chain is enabled and has an RPC endpoint to
// connect to -- the two things a migration-seeded placeholder row lacks
// until an operator fills them in.
func (c DonationChain) Watchable() bool {
return c.Valid() && c.Enabled && c.RPCURL != ""
}
// DonationToken is a stablecoin contract watched on one chain. An empty
// ContractAddress means the operator hasn't filled it in yet (e.g. no
// citable official Sepolia USDT deployment, or Ganache's mock not deployed
// yet) -- the watcher skips it rather than watching a zero address.
type DonationToken struct {
ChainKey string
Symbol string
ContractAddress string
Decimals int
}
// Watchable reports whether this token has a contract address to watch.
func (t DonationToken) Watchable() bool {
return t.ContractAddress != ""
}
// DonationAddress is a user's permanent, chain-agnostic deposit address.
type DonationAddress struct {
UserID int64
DerivationIndex int64
Address string
CreatedAt time.Time
}
// DonationDeposit is one detected on-chain transfer to a user's deposit
// address, tracked from first sight through crediting (or orphaning on a
// reorg). TokenSymbol is empty for a native-currency deposit; LogIndex is
// -1 for one (there is no ERC-20 Transfer log to index).
type DonationDeposit struct {
ID int64
UserID int64
ChainKey string
TokenSymbol string
TxHash string
LogIndex int
BlockNumber int64
AmountRaw string // decimal string; arbitrary precision, smallest unit (wei etc.)
USDValueMicros int64
StarsCredited int64
Status DonationDepositStatus
Confirmations int
DetectedAt time.Time
CreditedAt time.Time
}
// DonationChainConfigUpdate is the operator-editable subset of a
// DonationChain: everything except the chain's identity (Key/Name/ChainID)
// and its currency's fixed properties (NativeSymbol/NativeDecimals), which
// describe the network itself rather than how this server watches it.
type DonationChainConfigUpdate struct {
ChainKey string
RPCURL string
WSURL string
ConfirmationsRequired int
PriceFeedAddress string
ManualUSDRateMicros int64
Enabled bool
}
// DonationCreditNotice is everything a "your deposit was credited" chat
// notification needs, handed from the watcher (which knows the chain/asset
// it just priced) to whatever sends the actual message (app/bots.Service,
// via the donations.CreditNotifier port) without either side depending on
// the other's package.
type DonationCreditNotice struct {
UserID int64
ChainName string
AssetSymbol string
AssetDecimals int
Deposit DonationDeposit // already Status == DonationDepositCredited
}