contract

package
v0.10.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package contract manages the lifecycle of contracts derived from an identity for a Wallet: it owns the per-type handlers, persists the contracts via a pluggable store, and rescans for not-yet tracked contracts to store.

Components

Manager is the only surface external callers touch. Concrete callers go through the Manager interface returned by NewManager; everything else in this package is wiring it depends on.

  • Handlers live in contract/handlers/ and provide per-type strategies for building a contract from an identity.KeyRef and for the standard getters (GetKeyRefs / GetSignerKey / GetTapscripts / GetExitDelay). The "default" (offchain) and "boarding" (onchain) handlers are the same implementation parameterized at construction with an isOnchain bool — see contract/handlers/default.NewHandler. There is no per-call IsOnchain option: handler identity carries the kind, and Manager.GetHandler dispatches on the contract's Type.

  • Store is the persistence layer (types.ContractStore, backed by KV or SQL). Contracts are keyed by Script, with secondary access by type/state/scripts. types.ContractStore.GetLatestContract(type) resolves the latest stored contract for a given type by key index, which the manager uses to find the next free derivation index without scanning the entire row set.

  • infoCache (see info_cache.go) memoizes client.Client.GetInfo responses. NewManager wraps args.Client once with a cachingClient and hands the wrapped client to the built-in handlers (default, boarding) so they share a single GetInfo cache. Handlers added by callers via WithHandler are constructed outside the manager and own their own client wiring — see the Extending section below.

  • keyProvider (unexported, defined in types.go) is the subset of the identity.Identity surface the manager needs to derive contracts: GetKeyIndex, NextKeyId, GetKey. Keeping it unexported decouples the manager from the identity implementation and lets us grow the surface without leaking new methods to callers of NewManager.

Creating a contract

Manager.NewContract(ctx, contractType, opts...):

  1. Look up the handler registered for contractType.
  2. Ask the store for GetLatestContract(contractType) and resolve the last-used keyId from it (or start at the first key id when the pool is empty).
  3. Advance with keyProvider.NextKeyId, then GetKey to derive the new KeyRef.
  4. Hand the KeyRef to handler.NewContract — the handler builds the script, address, and contract-type-specific params.
  5. Persist via store.AddContract with the resolved key index.

Recovery (ScanContracts)

Manager.ScanContracts(ctx, gapLimit) runs a gap-limit HD scan against every registered handler's external data source:

  • ContractTypeDefault uses the indexer's batched GetVtxos endpoint (one round-trip per batch).
  • ContractTypeBoarding uses the explorer's per-address GetTxs endpoint (throttled to dodge rate limits).

The scan derives gapLimit contracts at a time, asks the data source which scripts have been used, and stops once it has seen gapLimit consecutive unused addresses. It then persists every derived contract from startIdx up to and including the highest used index — the intermediate unused addresses are persisted too so subsequent NewContract calls advance past them.

Recoverability follows BIP-44 semantics: only externally observable (used) addresses are recoverable from a mnemonic. Addresses that the original identity derived but never received funds at are not visible to the indexer/explorer and are therefore unreachable on restore.

Concurrency

The handler map (the Registry) is sealed at NewManager time and needs no lock — reads are concurrent-safe by virtue of immutability. The manager guards store and key-provider access with an sync.RWMutex: mutations (NewContract, ScanContracts, Clean, Close) hold the write lock; reads (GetContracts) hold the read lock. The store and the info cache have their own internal locking on top.

Extending with new contract types

New handler kinds (vhtlc, delegate, custom user-defined contracts, …) plug in by:

  1. Implementing handlers.Handler (see contract/handlers/handler.go).
  2. Passing the handler to NewManager via WithHandler, keyed by a new types.ContractType. WithHandler rejects empty types, nil handlers, typed-nil concrete values, and duplicates in the same options list. NewManager additionally rejects any type that collides with a built-in (default, boarding).
  3. If the new type's "has this contract been used externally?" probe differs from the indexer or explorer paths the dispatcher already knows about, adding a branch in ScanContracts that selects the correct findUsedFn. By default ScanContracts uses the indexer (offchain) path for any non-boarding type.

User-registered handlers are responsible for their own client caching. The manager wraps args.Client with a shared GetInfo cache and hands the wrapped client to the built-in handlers only — handlers constructed by callers were built before the manager existed and need not depend on client.Client at all.

At the wallet layer, callers use [arksdk.WithContractHandler] to register handlers at NewWallet time; the wallet translates them to contract.WithHandler options inside Unlock.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Args

type Args struct {
	Store       types.ContractStore
	KeyProvider keyProvider
	Client      client.Client
	Indexer     offchainDataProvider
	Explorer    onchainDataProvider
	Network     arklib.Network
}

Args contains all services and params required to create a new contract manager.

type ContractOption

type ContractOption interface {
	// contains filtered or unexported methods
}

ContractOption configures the creation of a new contract.

func WithLabel

func WithLabel(label string) ContractOption

func WithServerParams added in v0.10.1

func WithServerParams(serverParams *client.Info) ContractOption

type FilterOption

type FilterOption interface {
	// contains filtered or unexported methods
}

FilterOption configures a filter for Manager query methods. Pass no options to return all contracts.

func WithScripts

func WithScripts(scripts []string) FilterOption

func WithState

func WithState(state types.ContractState) FilterOption

func WithType

func WithType(contractType types.ContractType) FilterOption

type Manager

type Manager interface {
	// Registry returns the sealed handler registry. Use it to discover which contract types this
	// manager supports.
	Registry() Registry
	// ScanContracts looks for untracked contracts to store of any type, and for each of them stops
	// when gapLimit consecutive unused contracts have been found.
	ScanContracts(ctx context.Context, gapLimit uint32) error
	// NewContract creates and stores a new contract. The key is derived from the key provider,
	// all required parameters are fetched by the proper handler based on the contract type.
	NewContract(
		ctx context.Context, contractType types.ContractType, opts ...ContractOption,
	) (*types.Contract, error)
	// GetContracts returns all contracts matching the given filter option.
	// All filters are mutually exclusive, i.e. only one filter can be set at a time.
	// Pass no options to return all contracts.
	GetContracts(ctx context.Context, opts ...FilterOption) ([]types.Contract, error)
	// GetHandler returns the handler responsible for the given contract's type.
	// Errors when the contract type is not registered.
	// Delegates to Registry().GetHandler(contract.Type).
	GetHandler(ctx context.Context, contract types.Contract) (handlers.Handler, error)
	// Clean removes all contracts from the store. Must be used only at
	// wallet reset.
	Clean(ctx context.Context) error
	// Close releases any resources held by the manager.
	Close()
}

Manager manages the lifecycle of contracts derived from wallet keys. Constructed by NewManager; the registered handler set is sealed at that point — see Registry() and contract.WithHandler.

func NewManager

func NewManager(args Args, opts ...ManagerOption) (Manager, error)

type ManagerOption

type ManagerOption func(*managerOptions) error

ManagerOption configures NewManager. The only currently defined option is WithHandler.

func WithHandler

WithHandler registers a custom handler for a non-built-in contract type. Errors if the type is empty, the handler is nil/typed-nil, or the same type was passed to a previous WithHandler in the same NewManager call. Collision with built-in types is detected later, in contract manager.

type Registry

type Registry interface {
	// GetHandler returns the handler for the given contract type, or a descriptive error if none
	// is registered.
	GetHandler(t types.ContractType) (handlers.Handler, error)
	// SupportedTypes returns all registered contract types in deterministic (alphabetical) order.
	// Built-ins are included.
	SupportedTypes() []types.ContractType
}

Registry maps contract types to their handler implementations. Constructed once by NewManager; immutable for its lifetime.

func NewRegistry

func NewRegistry(builtIns, customs map[types.ContractType]handlers.Handler) (Registry, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL