minter/minter-php-bip-44
PHP library for BIP-44 hierarchical deterministic key derivation on the Minter blockchain. Generate mnemonics, derive private and public keys, and produce Minter addresses from a seed, with support for multiple coins and account indexes in PHP applications.
What it is: A thin PHP wrapper around bitwasp/bip44 (or a compatible secp256k1-based derivation) that derives BIP-44 keys/addresses in the Minter format. It is not a wallet SDK — it just turns a mnemonic + index into a private key, public key, and Mx… address. Pair it with minter/php-sdk for signing/transactions.
Install
composer require minter/minter-php-bip-44
Confirm the platform requirements first — the package needs PHP with the gmp (and usually mbstring) extension, and it was last tagged in March 2021, so it constrains you to PHP 7.x/8.0-ish dependency ranges. On PHP 8.2+, run composer update minter/minter-php-bip-44 and check for #[\AllowDynamicProperties] / deprecated-callback warnings before shipping.
Where to look first
README.md — the public surface is tiny (typically a Bip44 constructor taking a mnemonic, plus getPrivateKey, getPublicKey, getAddress by index). Read it fully; it is the API.src/Bip44.php — the derivation loop and the default derivation path constant (m/44'/60'/0'/0/{index} in Minter-land, because Minter reuses the Ethereum coin type). If you need chain_id-style separation, override the path here.require block — tells you which upstream library actually does the cryptography, and therefore which security advisories apply to you.First use case — derive account 0 from a mnemonic
use Minter\Bip44\Bip44;
$bip44 = new Bip44('abandon abandon ... about'); // mnemonic
$privateKey = $bip44->getPrivateKey(0); // hex, no 0x
$publicKey = $bip44->getPublicKey(0); // compressed or uncompressed — check README
$address = $bip44->getAddress(0); // Mx…
Sanity check by re-deriving the same mnemonic in minter-js-sdk or the Minter web wallet and comparing the Mx… address. If they match, you are on the right derivation path and endianness. Do that before writing any production code — a one-character path difference produces a valid but wrong wallet.
Pattern 1 — Deposit-address generation (the classic use case).
Treat the mnemonic as a single "hot wallet root". For each new customer, allocate the next integer index, persist (user_id, index), and derive on demand:
$address = (new Bip44($secretMnemonic))->getAddress($index);
Store only the address and index in the DB — never store the derived private key. To sweep later, re-derive from the same mnemonic. Guard the index allocation with a DB sequence / SELECT ... FOR UPDATE so two customers never share an index, and keep a gap-limit-style rule if you ever restore from chain history.
Pattern 2 — Deterministic signing service.
Create a small service that takes (mnemonic, index, txPayload) and returns a signed transaction via minter/php-sdk:
$pk = (new Bip44($mnemonic))->getPrivateKey($index);
$tx = (new MinterTx())->sign($payload, $pk);
Keep the mnemonic in an env var or a KMS/secret manager not in the same process as the web tier. The library gives you the private key in plaintext as a hex string — that string will end up in memory, logs, exception traces if you are careless, so scope the service tightly.
Pattern 3 — Watch-only / read paths. If you only need addresses (e.g., rendering a receive screen), derive from a public-only path if the package exposes neutered derivation; otherwise derive the address from a server-side call and return it over an internal API. Do not ship the mnemonic to a browser or mobile client under any circumstances.
Pattern 4 — Bulk generation & fixtures. For tests/CI, generate a handful of known mnemonics + indexes once and assert the derived addresses. This turns the library into a checked dependency: if a Composer update silently changes the path, your test suite catches it.
$vectors = [
['mnemonic' => 'abandon ... about', 'index' => 0, 'address' => 'Mx…'],
// ...
];
Integration tips
WalletDeriver interface. The package is a one-release-per-two-years dependency; you want to be able to swap it for bitwasp/bip44 + a Minter address encoder without touching 40 call sites.scrypt/PBKDF2 at the root), so re-instantiating Bip44 and calling getAddress(12345) for every request is wasteful. Derive once, memoize the address string.ext-gmp and ext-mbstring in composer.json require so a fresh CI image fails loudly rather than at the first getAddress() call.60 (Ethereum's, because the address format is Ethereum-compatible). If you change the constant to something else "for cleanliness," you will produce addresses that the Minter wallet and explorer cannot recover from the user's mnemonic. Leave the default path alone.0x, sometimes as a binary string. minter/php-sdk expects one specific form. Normalize explicitly (e.g. ltrim($pk, '0x')) rather than assuming — this is the #1 source of "transaction signs but is rejected" bugs.keccak256, taking the last 20 bytes — the same as Ethereum. If the library exposes a compressed key and you hand it to the SDK, the address will be wrong. Inspect what getPublicKey() returns before trusting it..env committed to git.bitwasp/bitcoin-php dependencies — several have had CVEs and API breaks. If you are building anything holding real value, spend 1–2 days evaluating whether to fork this 200-line wrapper and own it, rather than depend on a 23-star repo.Mx mainnet, Mx/0x variants depending on the chain config). Derivation itself is identical, but do not assume getAddress() returns a testnet-ready string without checking against the current minter/php-sdk network constants.m/44'/60'/0'/0) if you need multi-account (m/44'/60'/1'/0/0) for organizational separation of a treasury cold wallet. Keep the last two levels (change/index) standard, otherwise wallets that follow BIP-44 recovery conventions cannot find the funds.xprv/xpub at each level, and compare against a reference implementation (e.g. bip32.org or iancoleman/bip39). Nine times out of ten the discrepancy is at the path level, not the crypto./\b[0-9a-f]{64}\b/ from stack traces, and never dd($bip44), var_dump($privateKey), or let the object be serialized into an exception message.Bip44 is stateless per derivation but stateful per mnemonic — do not share a single instance across long-lived processes (Octane/Swoole) if it caches internally, and never mutate "current index" state on the object; always pass the index explicitly.How can I help you explore Laravel packages today?