PHPackages                             chemaclass/unspent - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. chemaclass/unspent

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

chemaclass/unspent
==================

A PHP library for UTXO-like bookkeeping using unspent entries.

150PHPCI passing

Since May 13Pushed 1mo agoCompare

[ Source](https://github.com/Chemaclass/unspent)[ Packagist](https://packagist.org/packages/chemaclass/unspent)[ RSS](/packages/chemaclass-unspent/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

Unspent
=======

[](#unspent)

[![PHP 8.4+](https://camo.githubusercontent.com/25012e98b640e282284d84348b9c496c315c270928666c27e504e5fa10802102/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e342b2d3737374242343f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://www.php.net/)[![MIT License](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/f281727a0668f038c0b21fe7ebb041d6aa3d30c3f535d0bfad4c10fd88292cfe/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4368656d61636c6173732f756e7370656e742f6261646765732f7175616c6974792d73636f72652e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/Chemaclass/unspent/?branch=main)[![Code Coverage](https://camo.githubusercontent.com/9f4efc4a0e8ed2145a976e4e3c59f5c80bd1dfe2192978f5efa07aad2644df63/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4368656d61636c6173732f756e7370656e742f6261646765732f636f7665726167652e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/Chemaclass/unspent/?branch=main)[![Mutation testing badge](https://camo.githubusercontent.com/f2da5e60191f88a8b0b948edf83987e4dcbb1d611ce4e98521952bf0419d70c6/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f7374796c653d666c61742675726c3d687474707325334125324625324662616467652d6170692e737472796b65722d6d757461746f722e696f2532466769746875622e636f6d2532464368656d61636c617373253246756e7370656e742532466d61696e)](https://dashboard.stryker-mutator.io/reports/github.com/Chemaclass/unspent/main)

**Track value like physical cash in your PHP apps.** Every unit has an origin, can only be spent once, and leaves a complete audit trail.

```
$ledger = Ledger::inMemory();
$ledger->credit('alice', 100)->transfer('alice', 'bob', 25);
echo $ledger->totalUnspentByOwner('bob'); // 25
```

Zero-dependency PHP 8.4+ library implementing the UTXO model, inspired by Bitcoin and decoupled as a standalone library.

Table of Contents
-----------------

[](#table-of-contents)

- [Why?](#why)
- [When to use it](#when-to-use-it)
- [Install](#install)
- [Quick Start](#quick-start)
- [Output types](#output-types)
- [Examples](#examples)
- [Documentation](#documentation)
- [Learning Path](#learning-path)
- [FAQ](#faq)
- [Contributing](#contributing)

Why?
----

[](#why)

Traditional balance tracking (`balance: 500`) is just a number you mutate. There's no history, no proof of where it came from, and race conditions can corrupt it.

**Unspent** tracks value like physical cash. You can't photocopy a $20 bill - you spend it and get change back. This gives you:

- **Double-spend prevention** — a unit can only be spent once, ever
- **Complete audit trail** — trace any value back to its origin
- **Immutable history** — state changes are additive, never mutated
- **Advanced locks** — timelocks, multisig, hash-locked outputs (HTLCs)
- **Zero external dependencies** — pure PHP 8.4+

When to use it
--------------

[](#when-to-use-it)

NeedTraditional BalanceUnspentSimple spendingEasyOverkill"Who authorized this?"Requires extra loggingBuilt-in"Trace this value's origin"Requires event sourcingBuilt-inConcurrent spending safetyRace conditionsAtomicConditional spending rulesCustom logic neededLock systemRegulatory audit trailReconstruct from logsNative**Use Unspent when** value moves between parties, you need to prove who authorized what, or audit trail is a requirement.

**Skip it when** you only need a simple counter, single-user balance, or no audit requirements.

### Limitations

[](#limitations)

LimitationDetails**Integer bounds**Amounts bounded by `PHP_INT_MAX` (~9.2e18). Wrap for arbitrary precision.**Single-node model**Single-node operation. For distributed consensus, add Raft/blockchain infrastructure.**No built-in rate limiting**Your application must implement rate limiting.**Memory for large datasets**In-memory ~1MB/1k outputs. Use store-backed mode for &gt;100k outputs.**Not for sub-second precision**Timestamps not enforced; not a real-time trading engine.Install
-------

[](#install)

```
composer require chemaclass/unspent
```

Quick Start
-----------

[](#quick-start)

### In-Memory (prototyping)

[](#in-memory-prototyping)

```
use Chemaclass\Unspent\Ledger;

$ledger = Ledger::inMemory();
$ledger->credit('alice', 1000)
    ->credit('bob', 500)
    ->transfer('alice', 'bob', 200)
    ->transfer('alice', 'bob', 100, fee: 5)
    ->debit('bob', 50);

$ledger->totalUnspentByOwner('alice');  // 695
$ledger->totalUnspentByOwner('bob');    // 750
```

MethodDescription`credit($owner, $amount)`Mint new value to owner`transfer($from, $to, $amount, $fee)`Move value between owners`debit($owner, $amount, $fee)`Burn value from owner### SQLite persistence (production)

[](#sqlite-persistence-production)

```
use Chemaclass\Unspent\Persistence\Sqlite\SqliteRepositoryFactory;
use Chemaclass\Unspent\Output;

$repo = SqliteRepositoryFactory::createFromFile('ledger.db');

$ledger = $repo->find('my-ledger')
    ?? Ledger::withGenesis(Output::ownedBy('alice', 1000));

$ledger->transfer('alice', 'bob', 200);
$repo->save('my-ledger', $ledger);
```

### Coin Control (full input/output control)

[](#coin-control-full-inputoutput-control)

```
use Chemaclass\Unspent\Tx;

$ledger = Ledger::withGenesis(
    Output::ownedBy('alice', 500, 'alice-savings'),
    Output::ownedBy('alice', 300, 'alice-checking'),
);

$ledger->apply(Tx::create(
    spendIds: ['alice-checking'],
    outputs: [
        Output::ownedBy('bob', 200),
        Output::ownedBy('alice', 100, 'alice-change'),
    ],
    signedBy: 'alice',
));
```

Use Coin Control for: specific output selection, custom IDs, multiple recipients, complex fees.

### Batch operations

[](#batch-operations)

```
$ledger->consolidate('alice', fee: 10);

$ledger->batchTransfer('alice', [
    'bob' => 100,
    'charlie' => 200,
    'dave' => 300,
], fee: 5);
```

### Transaction Mempool

[](#transaction-mempool)

Stage transactions for validation before commit:

```
use Chemaclass\Unspent\Mempool;

$mempool = new Mempool($ledger);
$mempool->add($tx1);
$mempool->add($tx2);
// $mempool->add($conflictingTx); // Throws OutputAlreadySpentException
$mempool->commit();
```

Output types
------------

[](#output-types)

MethodUse case`Output::open(100)`No lock - pure bookkeeping`Output::ownedBy('alice', 100)`Server-side auth (sessions, JWT)`Output::signedBy($pubKey, 100)`Ed25519 crypto (trustless)`Output::timelocked('alice', 100, $time)`Vesting, delayed payments`Output::multisig(2, ['a','b','c'], 100)`Joint accounts, escrow`Output::hashlocked($hash, 100)`Atomic swaps, HTLCs`Output::lockedWith($lock, 100)`Custom lock implementationsExamples
--------

[](#examples)

Runnable demos under [`example/`](example/):

```
php example/run               # List all
php example/run game          # Run a demo (also: loyalty, accounting, events, btc, wallet, locks, sqlite)
php example/run game --reset  # Reset state
```

AliasDemonstrates`game`In-game currency, ownership, double-spend, fees`loyalty`Customer rewards, minting, redemption, audit`accounting`Department budgets, multi-party auth, reconcile`events`Order lifecycle as state transitions`btc`Bitcoin simulation, mining, fees, consolidation`wallet`Ed25519 signatures, trustless verification`locks`Custom time-locked outputs, serialization`sqlite`SQLite persistence, querying, historySee [example/README.md](example/README.md) for full output samples and the web API demo.

Documentation
-------------

[](#documentation)

Start at [`docs/README.md`](docs/README.md) for the full index.

TopicWhat you'll learn[Core Concepts](docs/concepts.md)How outputs, transactions, and the ledger work[Ownership](docs/ownership.md)Locks (owner, timelock, multisig, hashlock), authorization[History](docs/history.md)Tracing value through transactions[Fees &amp; Minting](docs/fees-and-minting.md)Implicit fees, coinbase transactions[Selection Strategies](docs/selection-strategies.md)FIFO, largest-first, exact-match, random, custom[Persistence](docs/persistence.md)JSON, SQLite, custom storage[Scalability](docs/scalability.md)In-memory vs store-backed mode[Events](docs/events.md)PSR-14 event dispatching, integrations[Migration Guide](docs/migration.md)Moving from balance-based systems to UTXO[Troubleshooting](docs/troubleshooting.md)Common issues and solutions[API Reference](docs/api-reference.md)Ledger, Output, Tx, Mempool, UtxoAnalyticsLearning Path
-------------

[](#learning-path)

LevelTopicDocsExample1. BasicsOutputs, transactions[Concepts](docs/concepts.md)`php example/run game`2. OwnershipLocks, authorization[Ownership](docs/ownership.md)`php example/run wallet`3. PersistenceSQLite storage[Persistence](docs/persistence.md)`php example/run sqlite`4. ScaleMode selection[Scalability](docs/scalability.md)-5. AdvancedTimelocks, multisig, HTLC[Ownership](docs/ownership.md)`php example/run locks`6. OperationsBatch, mempool, analytics[API Reference](docs/api-reference.md)-FAQ
---

[](#faq)

**Can two outputs have the same ID?**No. Output IDs must be unique across the ledger. If you omit the ID, a unique one is auto-generated (128-bit random entropy). Custom IDs that collide throw `DuplicateOutputIdException`. This mirrors Bitcoin's `txid:vout` model.

**When should I use in-memory vs store-backed mode?**ScenarioRecommendation&lt; 100k total outputs`Ledger::inMemory()` or `Ledger::withGenesis(...)`&gt; 100k total outputs`Ledger::withRepository($repository)`Need full history in memory`Ledger::inMemory()`Memory-constrained environment`Ledger::withRepository($repository)`See [Scalability docs](docs/scalability.md).

**How are fees calculated?**Fees are implicit, like in Bitcoin. The difference between inputs and outputs is the fee:

```
$ledger->apply(Tx::create(
    spendIds: ['input-100'],       // Spending 100
    outputs: [Output::open(95)],   // Creating 95
));
// Fee = 100 - 95 = 5 (implicit)
```

See [Fees &amp; Minting docs](docs/fees-and-minting.md).

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for setup, TDD workflow, quality gates, and commit format.

```
composer install     # Installs dependencies + pre-commit hook
composer check:quick # Fast feedback: cs-fixer + phpunit
composer test        # Full: cs-fixer + rector + phpstan + phpunit
```

Docker workflow available via `make help`.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

21

—

LowBetter than 18% of packages

Maintenance59

Moderate activity, may be stable

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/3d166420c6770c5941e10bd68b2d26501eabb432e280d8e6eba0a344bcc1e5ae?d=identicon)[Chemaclass](/maintainers/Chemaclass)

---

Top Contributors

[![Chemaclass](https://avatars.githubusercontent.com/u/5256287?v=4)](https://github.com/Chemaclass "Chemaclass (99 commits)")

---

Tags

accountingbookkeepingevent-stormingledgerlibraryphputxo

### Embed Badge

![Health badge](/badges/chemaclass-unspent/health.svg)

```
[![Health](https://phpackages.com/badges/chemaclass-unspent/health.svg)](https://phpackages.com/packages/chemaclass-unspent)
```

###  Alternatives

[mynaparrot/plugnmeet-sdk

plugNmeet PHP SDK

102.8k](/packages/mynaparrot-plugnmeet-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
