Documentation
¶
Overview ¶
Package tempo contains the Tempo-specific charge method built on top of the generic pkg/client and pkg/server HTTP 402 flow, including request helpers, attribution utilities, and replay stores for single-process and Redis-backed deployments.
Index ¶
- Constants
- Variables
- func ChargeProofStoreKey(challengeID string) string
- func ChargeSponsoredTransactionStoreKey(hash string) string
- func ChargeStoreKey(hash string) string
- func DefaultCurrenciesForChain(chainID int64) []string
- func DefaultCurrencyForChain(chainID int64) string
- func EncodeAttribution(serverID, clientID, challengeID string) string
- func EncodeTransfer(recipient string, amount *big.Int) string
- func EncodeTransferWithMemo(recipient string, amount *big.Int, memo string) (string, error)
- func InferChainIDFromRPCURL(rpcURL string) int64
- func IsAttributionMemo(memo string) bool
- func IsKnownChainID(chainID int64) bool
- func MatchTransferCalldata(dataHex string, request ChargeRequest, realm, challengeID string) bool
- func ParseHexBigInt(value string) (*big.Int, error)
- func ParseHexUint64(value string) (uint64, error)
- func ParseTopicAddress(topic string) string
- func ProofSource(chainID int64, address common.Address) string
- func ProofTypedData(chainID int64, account common.Address, challengeID, realm string) apitypes.TypedData
- func ProofTypedDataHash(chainID int64, account common.Address, challengeID, realm string) (common.Hash, error)
- func RPCURLForChain(chainID int64) (string, error)
- func VerifyAttributionChallenge(memo, challengeID string) bool
- func VerifyAttributionServer(memo, serverID string) bool
- type ChargeCredentialPayload
- type ChargeMode
- type ChargeRequest
- type ChargeRequestParams
- type CredentialType
- type ExpiringStore
- type MemoryStore
- func (s *MemoryStore) Delete(_ context.Context, key string) error
- func (s *MemoryStore) Get(_ context.Context, key string) (string, bool, error)
- func (s *MemoryStore) Put(_ context.Context, key, value string) error
- func (s *MemoryStore) PutIfAbsent(_ context.Context, key, value string) (bool, error)
- func (s *MemoryStore) PutIfAbsentUntil(_ context.Context, key, value string, expiresAt time.Time) (bool, error)
- type MethodDetails
- type RPCClient
- type RedisStore
- func (s *RedisStore) Delete(ctx context.Context, key string) error
- func (s *RedisStore) Get(ctx context.Context, key string) (string, bool, error)
- func (s *RedisStore) Put(ctx context.Context, key, value string) error
- func (s *RedisStore) PutIfAbsent(ctx context.Context, key, value string) (bool, error)
- func (s *RedisStore) PutIfAbsentUntil(ctx context.Context, key, value string, expiresAt time.Time) (bool, error)
- type Split
- type SplitParams
- type Store
Constants ¶
const ( // ChainIDMainnet is the chain ID of Tempo mainnet. ChainIDMainnet int64 = 4217 // ChainIDModerato is the chain ID of the Tempo Moderato testnet. ChainIDModerato int64 = 42431 )
Tempo chain IDs. They have the type of the ChainID fields in the Tempo client and server configs.
const ( // MethodName is the Tempo payment method token. MethodName = "tempo" // IntentCharge is the Tempo charge intent token. It is mpp.IntentCharge. IntentCharge = mpp.IntentCharge // DefaultDecimals is the default TIP-20 decimal precision used for charges. DefaultDecimals = 6 // DefaultGasLimit is the fallback gas limit when estimation is unavailable. DefaultGasLimit = uint64(1_000_000) // SponsoredValidBeforeWindow is how far ahead of now a client sets the // expiring-nonce valid_before of a sponsored (fee-payer) charge // transaction, capped at FeePayerMinRemainingValidity past the second // after the challenge expiry. Servers require FeePayerMinRemainingValidity // of validity left when they verify, co-sign and broadcast, so a 60 second // window leaves about 45 seconds for clock skew between client and server, // signing and round trips. This is deliberately wider than mppx's 25 // seconds, which leaves only about 10. SponsoredValidBeforeWindow = 60 * time.Second // FeePayerMinRemainingValidity is the minimum validity a sponsored charge // must keep: servers reject sponsored transactions with less remaining // before valid_before, refuse to issue sponsored challenges that expire // sooner than this after issuance, and clients refuse to sign sponsored // challenges that expire sooner than this. FeePayerMinRemainingValidity = 15 * time.Second // ReplayKeyPrefix is the storage namespace for charge replay protection. ReplayKeyPrefix = "mppx:charge:" // TransferSelector is the TIP-20 transfer selector. TransferSelector = "a9059cbb" // TransferWithMemoSelector is the Tempo transfer-with-memo selector. TransferWithMemoSelector = "95777d59" // TransferCalldataLength is the exact byte length of transfer(address,uint256). TransferCalldataLength = 4 + 32 + 32 // TransferWithMemoCalldataLength is the exact byte length of transferWithMemo(address,uint256,bytes32). TransferWithMemoCalldataLength = 4 + 32 + 32 + 32 // USDCEAddress is the bridged USDC (USDC.e) contract on Tempo mainnet. USDCEAddress = "0x20C000000000000000000000b9537d11c60E8b50" // OUSDAddress is the OpenUSD (OUSD) contract, deployed at the same address // on Tempo mainnet and Moderato. OUSDAddress = "0x20c0000000000000000000006a37DA5C996874BE" // PathUSDAddress is the pathUSD contract on Tempo networks. PathUSDAddress = "0x20c0000000000000000000000000000000000000" )
Variables ¶
var ( // TransferTopic is the TIP-20 Transfer event topic hash. TransferTopic = crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")) // TransferWithMemoTopic is the Tempo TransferWithMemo event topic hash. TransferWithMemoTopic = crypto.Keccak256Hash([]byte("TransferWithMemo(address,address,uint256,bytes32)")) )
var ExpiringNonceKey = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
ExpiringNonceKey is the reserved nonce key used for fee-payer transactions.
Functions ¶
func ChargeProofStoreKey ¶
ChargeProofStoreKey normalizes a replay-protection key for a Tempo proof credential.
func ChargeSponsoredTransactionStoreKey ¶ added in v0.4.0
ChargeSponsoredTransactionStoreKey normalizes a replay-protection key for a sponsored Tempo transaction awaiting co-signature, keyed by the keccak256 hash of the verified payer and canonical sender signing payload.
func ChargeStoreKey ¶
ChargeStoreKey normalizes a replay-protection key for a Tempo transaction hash.
func DefaultCurrenciesForChain ¶ added in v0.4.0
DefaultCurrenciesForChain returns the ordered stablecoins a server accepts by default on a chain. Mainnet offers OUSD, then USDC.e; Moderato offers OUSD, then pathUSD. Other chains fall back to the single DefaultCurrencyForChain token. The returned slice is a fresh copy.
func DefaultCurrencyForChain ¶
DefaultCurrencyForChain returns the default stablecoin used for charges on a chain.
func EncodeAttribution ¶
EncodeAttribution builds the 32-byte Tempo attribution memo for a Challenge.
func EncodeTransfer ¶
EncodeTransfer builds TIP-20 transfer calldata for the supplied recipient and amount.
func EncodeTransferWithMemo ¶
EncodeTransferWithMemo builds Tempo transfer-with-memo calldata for the supplied values.
func InferChainIDFromRPCURL ¶
InferChainIDFromRPCURL maps known Tempo RPC URLs back to their chain IDs. It returns 0 when the URL does not match a known Tempo endpoint.
func IsAttributionMemo ¶
IsAttributionMemo reports whether a memo uses the Tempo attribution layout.
func IsKnownChainID ¶
IsKnownChainID reports whether the chain ID maps to a built-in Tempo network.
func MatchTransferCalldata ¶
func MatchTransferCalldata(dataHex string, request ChargeRequest, realm, challengeID string) bool
MatchTransferCalldata reports whether calldata satisfies the canonical Tempo charge transfer.
func ParseHexBigInt ¶
ParseHexBigInt decodes a JSON-RPC hex integer into a big.Int.
func ParseHexUint64 ¶
ParseHexUint64 decodes a JSON-RPC hex integer into uint64. A bare "0x" (or an empty string) decodes to 0, matching ParseHexBigInt; some JSON-RPC nodes return "0x" for a zero-valued quantity.
func ParseTopicAddress ¶
ParseTopicAddress extracts the address stored in an indexed log topic.
func ProofSource ¶
ProofSource formats the canonical did:pkh source used by Tempo proof credentials.
func ProofTypedData ¶ added in v0.2.0
func ProofTypedData(chainID int64, account common.Address, challengeID, realm string) apitypes.TypedData
ProofTypedData builds the EIP-712 typed data for a Tempo zero-amount proof credential. The Proof struct includes the payer wallet (account) so the signature commits to a specific payer and cannot be replayed against a different account.
func ProofTypedDataHash ¶
func ProofTypedDataHash(chainID int64, account common.Address, challengeID, realm string) (common.Hash, error)
ProofTypedDataHash returns the EIP-712 digest for a Tempo proof credential, bound to the payer wallet (account). See ProofTypedData.
func RPCURLForChain ¶
RPCURLForChain returns the canonical Tempo RPC URL for a known chain ID. Chain ID 0 falls back to mainnet defaults.
func VerifyAttributionChallenge ¶
VerifyAttributionChallenge reports whether a memo matches a specific Challenge ID.
func VerifyAttributionServer ¶
VerifyAttributionServer reports whether a memo matches the Challenge realm fingerprint.
Types ¶
type ChargeCredentialPayload ¶
type ChargeCredentialPayload struct {
// Type selects the payload variant.
Type CredentialType
// Hash stores the submitted transaction hash for push flows.
Hash string
// Signature stores the raw transaction or proof signature.
Signature string
}
ChargeCredentialPayload is the Tempo-specific payload carried by a Credential.
func ParseChargeCredentialPayload ¶
func ParseChargeCredentialPayload(input map[string]any) (ChargeCredentialPayload, error)
ParseChargeCredentialPayload parses a generic payload map into a Tempo payload.
func (ChargeCredentialPayload) Map ¶
func (p ChargeCredentialPayload) Map() map[string]any
Map converts a Tempo payload back into the generic Credential payload shape.
type ChargeMode ¶
type ChargeMode string
ChargeMode declares which Tempo credential flow a challenge accepts.
const ( // ChargeModePull allows clients to return a signed transaction credential. ChargeModePull ChargeMode = "pull" // ChargeModePush allows clients to broadcast first and return a hash credential. ChargeModePush ChargeMode = "push" )
type ChargeRequest ¶
type ChargeRequest struct {
// Amount is the canonical base-unit amount.
Amount string
// Currency is the token contract address.
Currency string
// Recipient is the primary payee address.
Recipient string
// Description is a human-readable challenge description.
Description string
// ExternalID is application metadata echoed into receipts.
ExternalID string
// MethodDetails stores Tempo-specific extensions for the charge.
MethodDetails MethodDetails
}
ChargeRequest is the canonical Tempo request shape embedded in a Challenge.
func NormalizeChargeRequest ¶
func NormalizeChargeRequest(params ChargeRequestParams) (ChargeRequest, error)
NormalizeChargeRequest validates request parameters and produces the canonical Tempo shape.
func ParseChargeRequest ¶
func ParseChargeRequest(input map[string]any) (ChargeRequest, error)
ParseChargeRequest parses a generic request map into the canonical Tempo shape.
func (ChargeRequest) Allows ¶
func (r ChargeRequest) Allows(credentialType CredentialType) bool
Allows reports whether the request accepts the supplied credential type.
func (ChargeRequest) Map ¶
func (r ChargeRequest) Map() map[string]any
Map converts a ChargeRequest into the generic request map embedded in a Challenge.
type ChargeRequestParams ¶
type ChargeRequestParams struct {
// Amount is the human-readable decimal amount.
Amount string
// Currency is the token contract address.
Currency string
// Recipient is the primary payee address.
Recipient string
// Decimals controls how Amount values are converted to base units.
Decimals int
// Description is copied into the challenge for display.
Description string
// ExternalID is opaque application metadata echoed into receipts.
ExternalID string
// ChainID binds the charge to a specific Tempo chain when set.
ChainID int64
// FeePayer enables the sponsored transaction flow.
FeePayer bool
// FeePayerURL points at a remote fee-payer signer.
FeePayerURL string
// Splits lists any additional transfers included in the charge.
Splits []SplitParams
// SupportedModes restricts which credential submission modes are allowed.
SupportedModes []ChargeMode
}
ChargeRequestParams are the human-friendly inputs used to build a ChargeRequest.
type CredentialType ¶
type CredentialType string
CredentialType identifies the Tempo credential payload shape.
const ( // CredentialTypeTransaction carries a signed Tempo transaction. CredentialTypeTransaction CredentialType = "transaction" // CredentialTypeHash carries a submitted Tempo transaction hash. CredentialTypeHash CredentialType = "hash" // CredentialTypeProof carries a zero-amount proof signature. CredentialTypeProof CredentialType = "proof" )
type ExpiringStore ¶ added in v0.5.1
type ExpiringStore interface {
Store
// PutIfAbsentUntil atomically stores value only when key is unused or its
// previous entry has expired, and keeps the entry until expiresAt. It must
// return false, storing nothing, when expiresAt is not in the future. An
// entry must never be dropped before expiresAt, and Get must report it as
// missing once it has expired.
PutIfAbsentUntil(ctx context.Context, key, value string, expiresAt time.Time) (bool, error)
}
ExpiringStore is an optional extension of Store for replay keys that only need to outlive the credential they protect, like mppx's Store.tryClaim with a ReplayMarker expiry.
The Tempo charge verifier calls PutIfAbsentUntil instead of PutIfAbsent only when configured with ExpireReplayKeys, passing a moment after which the credential behind key can no longer be accepted. Otherwise, and for stores that do not implement it, replay keys are kept forever with PutIfAbsent. A store that embeds MemoryStore or RedisStore and overrides PutIfAbsent must also override PutIfAbsentUntil before enabling ExpireReplayKeys.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is an opt-in development replay store. It does not protect across processes or restarts; production replicas must share a persistent Store. PutIfAbsent keeps keys until they are deleted; only keys stored with PutIfAbsentUntil expire.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
NewMemoryStore constructs an in-memory replay-protection store.
func (*MemoryStore) Delete ¶
func (s *MemoryStore) Delete(_ context.Context, key string) error
Delete removes a replay-protection value.
func (*MemoryStore) Put ¶
func (s *MemoryStore) Put(_ context.Context, key, value string) error
Put stores a replay-protection value, replacing any existing entry.
func (*MemoryStore) PutIfAbsent ¶
PutIfAbsent stores a replay-protection value only when the key is unused.
func (*MemoryStore) PutIfAbsentUntil ¶ added in v0.5.1
func (s *MemoryStore) PutIfAbsentUntil(_ context.Context, key, value string, expiresAt time.Time) (bool, error)
PutIfAbsentUntil stores a replay-protection value only when the key is unused or expired, and keeps it until expiresAt. It stores nothing and returns false when expiresAt is not in the future.
type MethodDetails ¶
type MethodDetails struct {
// ChainID binds the charge to a specific Tempo chain when present.
ChainID *int64
// FeePayer enables the sponsored transaction flow.
FeePayer bool
// FeePayerURL points at a remote fee-payer signer.
FeePayerURL string
// Splits lists any additional transfers included in the charge.
Splits []Split
// SupportedModes restricts which credential submission modes are allowed.
SupportedModes []ChargeMode
}
MethodDetails holds Tempo-specific request fields nested under methodDetails.
type RPCClient ¶
type RPCClient interface {
GetChainID(ctx context.Context) (uint64, error)
GetTransactionCount(ctx context.Context, address string) (uint64, error)
SendRawTransaction(ctx context.Context, serializedTx string) (string, error)
SendRequest(ctx context.Context, method string, params ...interface{}) (*temporpc.JSONRPCResponse, error)
}
RPCClient is the subset of Tempo JSON-RPC used by the charge flow.
func NewRPCClient ¶
NewRPCClient constructs a Tempo JSON-RPC client. Use NewRPCClientWithOptions to set a timeout, an *http.Client or credentials.
func NewRPCClientWithOptions ¶ added in v0.6.0
NewRPCClientWithOptions constructs a Tempo JSON-RPC client with tempo-go client options, such as client.WithTimeout or client.WithHTTPClient from github.com/tempoxyz/tempo-go/pkg/client. Build it once and reuse it: pass it as the RPC field of the Tempo client or server config.
type RedisStore ¶
type RedisStore struct {
// contains filtered or unexported fields
}
RedisStore implements Store with Redis commands.
func NewRedisStore ¶
func NewRedisStore(client redis.Cmdable, ttl time.Duration) *RedisStore
NewRedisStore constructs a Redis-backed replay-protection store.
When ttl is zero, stored values do not expire automatically. PutIfAbsent always keeps replay-protection keys persistent so used payment proofs and hashes cannot become valid again after a TTL window. Only PutIfAbsentUntil, which the Tempo charge verifier calls when configured with ExpireReplayKeys, stores keys that expire.
func (*RedisStore) Delete ¶
func (s *RedisStore) Delete(ctx context.Context, key string) error
Delete removes a replay-protection value.
func (*RedisStore) Put ¶
func (s *RedisStore) Put(ctx context.Context, key, value string) error
Put stores a replay-protection value, replacing any existing entry.
func (*RedisStore) PutIfAbsent ¶
PutIfAbsent stores a replay-protection value only when the key is unused.
func (*RedisStore) PutIfAbsentUntil ¶ added in v0.5.1
func (s *RedisStore) PutIfAbsentUntil(ctx context.Context, key, value string, expiresAt time.Time) (bool, error)
PutIfAbsentUntil stores a replay-protection value only when the key is unused, with SET NX EXAT, so Redis expires it at the absolute deadline expiresAt rather than after a TTL measured with this process's clock. It stores nothing and returns false when expiresAt is not in the future. EXAT requires Redis 6.2 or later.
type Split ¶
type Split struct {
// Amount is the base-unit amount routed to this split recipient.
Amount string
// Memo is an optional split-specific Tempo memo.
Memo string
// Recipient is the split recipient address.
Recipient string
}
Split is a canonical additional transfer nested under methodDetails.splits.
type SplitParams ¶
type SplitParams struct {
// Amount is the human-readable amount routed to this split recipient.
Amount string
// Memo is an optional split-specific Tempo memo.
Memo string
// Recipient is the split recipient address.
Recipient string
}
SplitParams are the human-friendly inputs used to build a canonical Split.
type Store ¶
type Store interface {
// Get loads a replay-protection value.
Get(ctx context.Context, key string) (value string, ok bool, err error)
// Put stores a replay-protection value, replacing any existing entry.
Put(ctx context.Context, key, value string) error
// PutIfAbsent stores a replay-protection value only when the key is unused.
PutIfAbsent(ctx context.Context, key, value string) (bool, error)
// Delete removes a replay-protection value.
Delete(ctx context.Context, key string) error
}
Store is the minimal replay-protection contract used by the Tempo charge verifier.
Unlike the verifier-specific helpers elsewhere in this package, Store stays close to common key-value backends so in-memory, Redis, and SQL-backed implementations can all satisfy the same interface. Implementations must not allow PutIfAbsent replay keys to become reusable after later expiry.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package chargeclient contains the Tempo charge client method for creating Payment Credentials that satisfy Tempo HTTP 402 Challenges.
|
Package chargeclient contains the Tempo charge client method for creating Payment Credentials that satisfy Tempo HTTP 402 Challenges. |
|
internal
|
|
|
rpccache
Package rpccache reuses the Tempo JSON-RPC clients that the charge client and server build from URLs, so each is built once rather than per request.
|
Package rpccache reuses the Tempo JSON-RPC clients that the charge client and server build from URLs, so each is built once rather than per request. |
|
Package chargeserver contains the Tempo charge server method and verifier.
|
Package chargeserver contains the Tempo charge server method and verifier. |