Documentation
¶
Overview ¶
Package sol implements the chain-agnostic adapter interfaces (see pkg/core) against Solana, over the custody Anchor program. The program bindings in ./custody are generated; the depositor/withdrawal-finalizer adapters and the Ed25519-precompile / digest helpers are hand-written on top of them.
Index ¶
- func BuildEd25519Instruction(pubkeys, sigs [][]byte, message []byte) (solana.Instruction, error)
- func ConfigPDA(programID solana.PublicKey) solana.PublicKey
- func RotateDigest(chainID uint64, programID, config solana.PublicKey, commitment [32]byte, ...) [32]byte
- func SignersCommitment(newSigners []solana.PublicKey, newThreshold uint8) [32]byte
- func VaultLookupAddresses(programID, mint solana.PublicKey) []solana.PublicKey
- func VaultPDA(programID solana.PublicKey) solana.PublicKey
- func WithdrawDigest(chainID uint64, programID, vault, to, mint solana.PublicKey, amount uint64, ...) [32]byte
- func WithdrawalPDA(programID solana.PublicKey, withdrawalID [32]byte) solana.PublicKey
- type AssetResolver
- type Config
- type Depositor
- type RotationFinalizer
- func (f *RotationFinalizer) Pack(ctx context.Context, _ [32]byte, newSigners []string, newThreshold int) ([]byte, error)
- func (f *RotationFinalizer) Sign(ctx context.Context, packed []byte) ([]byte, error)
- func (f *RotationFinalizer) Submit(ctx context.Context, packed []byte, shares [][]byte) (string, error)
- func (f *RotationFinalizer) Validate(ctx context.Context, _ [32]byte, packed []byte, newSigners []string, ...) error
- func (f *RotationFinalizer) VerifyRotation(ctx context.Context, newSigners []string, newThreshold int) (string, bool, error)
- type WithdrawalFinalizer
- func (f *WithdrawalFinalizer) Pack(ctx context.Context, op *core.WithdrawalOp, withdrawalID [32]byte, ...) ([]byte, error)
- func (f *WithdrawalFinalizer) Sign(ctx context.Context, packed []byte) ([]byte, error)
- func (f *WithdrawalFinalizer) Submit(ctx context.Context, packed []byte, shares [][]byte) (string, error)
- func (f *WithdrawalFinalizer) Validate(ctx context.Context, packed []byte, op *core.WithdrawalOp, ...) error
- func (f *WithdrawalFinalizer) VerifyExecution(ctx context.Context, withdrawalID [32]byte) (string, bool, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildEd25519Instruction ¶
func BuildEd25519Instruction(pubkeys, sigs [][]byte, message []byte) (solana.Instruction, error)
BuildEd25519Instruction frames the quorum's signatures for the native Ed25519SigVerify precompile, all offsets self-referencing (instruction index 0xFFFF) so the verified data cannot be smuggled from another instruction. pubkeys/sigs are parallel (32-byte / 64-byte); message is the 32-byte digest.
func ConfigPDA ¶
ConfigPDA / VaultPDA / WithdrawalPDA / eventAuthorityPDA derive the program's deterministic accounts.
func RotateDigest ¶
func RotateDigest(chainID uint64, programID, config solana.PublicKey, commitment [32]byte, signerNonce uint64) [32]byte
RotateDigest computes the 32-byte digest the providers sign for a signer rotation, matching the program's `rotate_digest`:
sha256(ROTATE_DOMAIN ‖ chainID(BE) ‖ programID ‖ config
‖ signersCommitment ‖ signerNonce(BE))
signerNonce is the on-chain Config.SignerNonce — the rotation replay token.
func SignersCommitment ¶
SignersCommitment computes sha256(newSigners ‖ newThreshold), matching the program's `signers_commitment`. It is the payload the rotation digest binds.
func VaultLookupAddresses ¶
VaultLookupAddresses returns the invariant accounts of the execute instruction — the ones that recur across every withdrawal and are therefore eligible to populate an Address Lookup Table, letting large quorums fit a v0 transaction (set the table via WithdrawalFinalizer's Config.AddressLookupTable). A zero mint returns the native-SOL set; a non-zero mint adds the token program and the vault's associated token account. Per-withdrawal accounts (the recipient, its token account, the Withdrawal PDA, the fee payer) vary each call and are intentionally excluded.
Build the lookup table from this set so it stays in lockstep with the instruction's account layout (both live here, in the SDK).
func WithdrawDigest ¶
func WithdrawDigest(chainID uint64, programID, vault, to, mint solana.PublicKey, amount uint64, withdrawalID [32]byte, deadline int64) [32]byte
WithdrawDigest computes the 32-byte digest the providers sign for a withdrawal, matching the program's `withdraw_digest`:
sha256(WITHDRAW_DOMAIN ‖ chainID(BE) ‖ programID ‖ vault
‖ to ‖ mint ‖ amount(BE) ‖ withdrawalID ‖ deadline(BE))
mint == the zero pubkey denotes native SOL. deadline is the unix-second time bound, encoded as an int64 big-endian so it matches Solana's Clock::unix_timestamp (i64) and the block SealedAt it derives from.
Types ¶
type AssetResolver ¶ added in v0.5.0
type AssetResolver struct {
// contains filtered or unexported fields
}
func NewAssetResolver ¶ added in v0.5.0
func NewAssetResolver(rpcURL string, commitment rpc.CommitmentType) *AssetResolver
func (*AssetResolver) AssetDecimals ¶ added in v0.5.0
func (*AssetResolver) ValidateAssetAddress ¶ added in v0.5.0
func (r *AssetResolver) ValidateAssetAddress(_ context.Context, assetAddress string) error
type Config ¶
type Config struct {
ChainID uint64
ComputeUnitLimit uint32 // 0 → 200k
ComputeUnitPrice uint64 // micro-lamports per CU (priority fee)
// Commitment is the level at which on-chain reads (Config, the Withdrawal
// PDA) are observed. Empty → CommitmentFinalized.
//
// NOTE: production should use CommitmentFinalized — a withdrawal is only
// truly settled once finalized (no rollback). CommitmentConfirmed is a
// devnet/test speed tradeoff: it observes results in ~1-2 slots instead of
// waiting ~32 for finality, cutting the local flow from ~16s to a few.
Commitment rpc.CommitmentType
// AddressLookupTable, when set, makes the submit emit a v0 transaction using
// this ALT. Required for large quorums: the Ed25519 instruction grows ~112
// bytes per signer, so beyond ~8-9 signers a legacy transaction exceeds the
// 1232-byte packet limit. Zero → legacy transaction.
AddressLookupTable solana.PublicKey
}
Config tunes the submit transaction.
type Depositor ¶
type Depositor struct {
// contains filtered or unexported fields
}
Depositor moves funds into the custody vault on Solana, signed by the depositor's own ed25519 key. It implements core.VaultDepositor. Native SOL and SPL tokens are both supported. The deposit credits the 20-byte clearnet account encoded in `account` (hex).
func NewDepositor ¶
func NewDepositor(rpcURL string, programID solana.PublicKey, signer sign.Signer, commitment rpc.CommitmentType, assets blockchain.AssetResolver) (*Depositor, error)
NewDepositor builds the Solana depositor over the JSON-RPC at rpcURL. signer is the depositor's ed25519 key (it pays + funds). commitment is the level the deposit tx's blockhash + preflight use; empty → CommitmentFinalized (see the NOTE on the withdrawal finalizer's Config.Commitment for the test tradeoff).
func (*Depositor) DepositorAddress ¶
DepositorAddress returns the depositor's Solana address.
func (*Depositor) SubmitDeposit ¶
func (d *Depositor) SubmitDeposit(ctx context.Context, assetAddress string, amount decimal.Decimal, dest core.DepositDestination) (string, error)
SubmitDeposit transfers amount of assetAddress into the vault, crediting clearnet dest.Account (20-byte hex) with the optional ADR-015 dest.Ref sub-account reference. assetAddress is "" for native or a base58 mint.
func (*Depositor) VerifyDeposit ¶
func (d *Depositor) VerifyDeposit(ctx context.Context, txID string, minConf uint64) (core.DepositStatus, error)
VerifyDeposit reports the on-chain status of the deposit txID (the base58 signature). minConf maps onto Solana's commitment ladder, which has no numeric depth: minConf 0 accepts the optimistic "confirmed" level (~1-2 slots), while minConf >= 1 requires "finalized" (irreversible). A failed tx reads as DepositAbsent (it credited nothing).
type RotationFinalizer ¶
type RotationFinalizer struct {
// contains filtered or unexported fields
}
RotationFinalizer rotates the custody program's signer set via update_signers, authorized by the current (outgoing) ed25519 quorum and verified on-chain via the Ed25519 precompile — the rotation analogue of WithdrawalFinalizer. The node's signer contributes one share; a separate fee-payer pays + submits. It implements core.SignerRotationFinalizer.
func NewRotationFinalizer ¶
func NewRotationFinalizer(rpcURL string, programID solana.PublicKey, signer, feePayer sign.Signer, cfg Config) (*RotationFinalizer, error)
NewRotationFinalizer builds the finalizer. signer is this node's ed25519 custody key (one quorum share); feePayer pays for and submits the update_signers transaction. cfg reuses the withdrawal Config (chain id, compute budget, commitment).
func (*RotationFinalizer) Pack ¶
func (f *RotationFinalizer) Pack(ctx context.Context, _ [32]byte, newSigners []string, newThreshold int) ([]byte, error)
Pack reads the live signer nonce and returns the canonical JSON for rotating to newSigners / newThreshold. opID is ignored: Solana binds rotation replay to the on-chain program signer nonce, so the operation identity is not embedded in the payload.
func (*RotationFinalizer) Sign ¶
Sign returns this node's share: nodePubkey(32) ‖ ed25519 signature(64) over the rotation digest.
func (*RotationFinalizer) Submit ¶
func (f *RotationFinalizer) Submit(ctx context.Context, packed []byte, shares [][]byte) (string, error)
Submit filters the collected shares against the live (outgoing) signer set, assembles the Ed25519-precompile + update_signers transaction, and broadcasts it (fee-payer signed). Idempotent: if the rotation already applied it returns without re-submitting.
func (*RotationFinalizer) Validate ¶
func (f *RotationFinalizer) Validate(ctx context.Context, _ [32]byte, packed []byte, newSigners []string, newThreshold int) error
Validate re-derives the rotation target from newSigners / newThreshold, asserts the packed payload matches it, and re-reads the live nonce to reject a packer that bound a stale or wrong signer nonce.
func (*RotationFinalizer) VerifyRotation ¶
func (f *RotationFinalizer) VerifyRotation(ctx context.Context, newSigners []string, newThreshold int) (string, bool, error)
VerifyRotation reports whether the on-chain Config now holds exactly the requested signer set + threshold. Binary: no txID is recoverable from the Config account, so an empty txID is returned with done=true.
type WithdrawalFinalizer ¶
type WithdrawalFinalizer struct {
// contains filtered or unexported fields
}
WithdrawalFinalizer executes a withdrawal against the custody Anchor program: a digest signed by the ed25519 quorum, verified on-chain via the Ed25519 precompile. The node's signer contributes one share; a separate fee-payer signer pays + submits. It implements core.VaultWithdrawalFinalizer, for both native SOL and SPL tokens (the SPL path adds the recipient-ATA creation and the token remaining-accounts the program's execute expects).
func NewWithdrawalFinalizer ¶
func NewWithdrawalFinalizer(rpcURL string, programID solana.PublicKey, signer, feePayer sign.Signer, cfg Config, assets blockchain.AssetResolver) (*WithdrawalFinalizer, error)
NewWithdrawalFinalizer builds the finalizer. signer is this node's ed25519 custody key (contributes a quorum share); feePayer is a distinct ed25519 key that pays for and submits the execute transaction.
func (*WithdrawalFinalizer) Pack ¶
func (f *WithdrawalFinalizer) Pack(ctx context.Context, op *core.WithdrawalOp, withdrawalID [32]byte, deadline int64) ([]byte, error)
Pack resolves the withdrawal target and returns the canonical JSON.
func (*WithdrawalFinalizer) Sign ¶
Sign returns this node's share: nodePubkey(32) ‖ ed25519 signature(64) over the withdrawal digest.
func (*WithdrawalFinalizer) Submit ¶
func (f *WithdrawalFinalizer) Submit(ctx context.Context, packed []byte, shares [][]byte) (string, error)
Submit filters + orders the collected shares against the live signer set, assembles the Ed25519-precompile + execute transaction, and broadcasts it (fee-payer signed), then waits for the Withdrawal PDA to appear.
func (*WithdrawalFinalizer) Validate ¶
func (f *WithdrawalFinalizer) Validate(ctx context.Context, packed []byte, op *core.WithdrawalOp, withdrawalID [32]byte, deadline int64) error
Validate re-derives the canonical payload from the op and the caller-supplied deadline and asserts a match — a peer packing a different deadline than the quorum agreed is rejected here, before Sign.
func (*WithdrawalFinalizer) VerifyExecution ¶
func (f *WithdrawalFinalizer) VerifyExecution(ctx context.Context, withdrawalID [32]byte) (string, bool, error)
VerifyExecution reports whether the Withdrawal PDA exists (the on-chain executed flag). The txID is not recoverable from the PDA alone, so an empty txID is returned with executed=true.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Command idl_refresher regenerates the Solana program bindings (the pkg/blockchain/sol/custody package) from the vendored Anchor IDL using anchor-go's generator library directly — the Solana analog of the EVM abi_refresher (which drives go-ethereum's abigen).
|
Command idl_refresher regenerates the Solana program bindings (the pkg/blockchain/sol/custody package) from the vendored Anchor IDL using anchor-go's generator library directly — the Solana analog of the EVM abi_refresher (which drives go-ethereum's abigen). |