A powerful Laravel package for managing blockchain wallets supporting Ethereum and Solana protocols. Features a modern two-table architecture with global wallet registry and ownership records, providing secure wallet management, multi-tenancy support, and flexible control models.
- Two-Table Architecture: Global wallet registry with separate ownership records for better scalability
- Multi-Protocol Support: Create wallets for Ethereum and Solana blockchains with extensible protocol system
- Flexible Control Models: Support for custodial, external, and shared wallets
- Secure Key Management: Automatic private key generation and encryption with per-ownership storage
- Multi-Tenancy: Built-in support for multi-tenant applications using
roberts/laravel-singledb-tenancy
- Advanced Wallet Service: Comprehensive service layer for wallet creation, import, and management
- Legacy Compatibility: Maintains backward compatibility through the
HasWallets
trait - Test-Driven Development: Comprehensive test suite covering all architecture components
The package uses a two-table architecture:
wallets
table: Global registry of all wallet addresses across all protocolswallet_owners
table: Controls who has access to which wallets in which tenants
This design enables:
- Address Deduplication: Same address can be shared across different owners/tenants
- Flexible Ownership: Multiple ownership models (custodial, external, shared)
- Enhanced Security: Encrypted private keys stored per ownership record
- Better Scalability: Optimized queries and reduced data duplication
You can install the package via composer:
composer require roberts/laravel-wallets
You can publish and run the migrations with:
php artisan vendor:publish --tag="laravel-wallets-migrations"
php artisan migrate
You can publish the config file with:
php artisan vendor:publish --tag="laravel-wallets-config"
First, add the HasWallets
trait to any model that should own wallets (typically your User
model):
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Roberts\LaravelWallets\Concerns\HasWallets;
class User extends Authenticatable
{
use HasWallets;
// ... rest of your model
}
The package provides a powerful WalletService
for all wallet operations:
use Roberts\LaravelWallets\Services\WalletService;
use Roberts\LaravelWallets\Enums\Protocol;
$walletService = app(WalletService::class);
$user = User::find(1);
// Create a custodial Ethereum wallet (we control the private key)
$result = $walletService->createCustodialWallet(
protocol: Protocol::ETH,
owner: $user,
tenantId: 1,
metadata: ['purpose' => 'main wallet']
);
echo "Wallet Address: " . $result['wallet']->address;
echo "Private Key Available: " . ($result['walletOwner']->hasControl() ? 'Yes' : 'No');
// Create a custodial Solana wallet
$result = $walletService->createCustodialWallet(
protocol: Protocol::SOL,
owner: $user,
tenantId: 1
);
echo "Solana Address: " . $result['wallet']->address;
Import wallets you already have private keys for:
// Import an existing Ethereum wallet
$result = $walletService->importExternalWallet(
protocol: Protocol::ETH,
address: '0x742d35Cc0b3E7C3f8f9E7aD0e1C5C3F5e0E8c8B7',
privateKey: '0xa2fd51b96dc55aeb14b30d55a6b3121c7b9c599500c1bbc92a22208d5dc73134',
owner: $user,
tenantId: 1,
metadata: ['imported_from' => 'MetaMask']
);
echo "Imported Wallet: " . $result['wallet']->address;
echo "Control Type: " . $result['wallet']->control_type->value; // 'EXTERNAL'
The package maintains backward compatibility through the HasWallets
trait:
// Legacy-style wallet creation (uses new architecture internally)
$result = $user->createEthereumWallet();
echo "Address: " . $result['wallet']->address;
// Access wallets through relationships
$userWallets = $user->wallets()->get(); // All accessible wallets
$custodialWallets = $user->custodialWallets(tenantId: 1); // Only custodial wallets
$externalWallets = $user->externalWallets(tenantId: 1); // Only external wallets
// Get all wallets for a user in a tenant
$userWallets = $user->walletsForTenant(1);
// Filter by protocol
$ethWallets = $user->walletsForTenant(1)->where('protocol', Protocol::ETH)->get();
// Get wallet ownership details
$ownership = $user->getWalletOwnership($walletId, $tenantId);
if ($ownership && $ownership->hasControl()) {
echo "User has private key access to this wallet";
}
When using roberts/laravel-singledb-tenancy
, wallets are automatically scoped to the current tenant:
// Wallets are automatically scoped to the current tenant
$userWallets = $user->walletsForTenant($currentTenantId);
// Check access to specific wallet
if ($user->hasWalletAccess($walletId, $tenantId)) {
echo "User has access to this wallet in this tenant";
}
The package provides a comprehensive command-line interface for wallet management:
# List all wallets with filtering options
php artisan wallets list
php artisan wallets list --protocol=eth
php artisan wallets list --control-type=custodial
php artisan wallets list --tenant=1
# Show wallet statistics
php artisan wallets stats
php artisan wallets stats --tenant=1
# Validate wallet data integrity
php artisan wallets validate
The package uses a sophisticated two-table architecture:
wallets
Table (Global Registry):
id
- Primary keyprotocol
- Blockchain protocol (ETH, SOL)address
- Wallet addresscontrol_type
- Control model (CUSTODIAL, EXTERNAL, SHARED)metadata
- JSON metadatacreated_at
,updated_at
wallet_owners
Table (Ownership Records):
id
- Primary keywallet_id
- References wallets tabletenant_id
- Tenant identifierowner_id
- Owner model IDowner_type
- Owner model classencrypted_private_key
- Encrypted private key (nullable for watch-only)created_at
,updated_at
CUSTODIAL
: Package generates and controls private keysEXTERNAL
: Wallets added through token snapshot or user submissionSHARED
: Laravel Application shares private key with owner or user
- Service Layer:
WalletService
handles all wallet operations - Key Generation: Protocol-specific key pair generation
- Global Registry: Wallet address stored in
wallets
table - Ownership Records: Access control stored in
wallet_owners
table - Encryption: Private keys encrypted per ownership record
- Encrypted Private Keys: Per-ownership encryption using Laravel's encryption
- Secure Key Generation: Industry-standard libraries (web3p/web3.php, solana-php/solana-php)
- Address Validation: Built-in validation for both Ethereum and Solana
- Tenant Isolation: Multi-tenancy support with proper data scoping
- Access Control: Fine-grained permissions through ownership records
use Roberts\LaravelWallets\Enums\Protocol;
use Roberts\LaravelWallets\Enums\ControlType;
Protocol::ETH; // Ethereum protocol
Protocol::SOL; // Solana protocol
ControlType::CUSTODIAL; // We control the private key
ControlType::EXTERNAL; // Wallets not controlled by app
ControlType::SHARED; // Custodial wallets that have shared private key with user
$walletService = app(WalletService::class);
// Create custodial wallet
$walletService->createCustodialWallet($protocol, $owner, $tenantId, $metadata);
// Add external wallets from snapshot
$walletService->addExternalWalletsFromSnapshot($protocol, $addresses, $metadata);
$wallet->protocol; // Protocol enum (ETH or SOL)
$wallet->address; // Public wallet address
$wallet->control_type; // Control type enum
$wallet->metadata; // JSON metadata array
$wallet->owners; // HasMany relationship to WalletOwner
$walletOwner->wallet; // BelongsTo relationship to Wallet
$walletOwner->owner; // MorphTo relationship to owner model
$walletOwner->tenant_id; // Tenant identifier
$walletOwner->hasControl(); // Has private key access
$walletOwner->getPrivateKey(); // Decrypt and return private key
$model->wallets(); // HasManyThrough to wallets
$model->walletOwnerships(); // HasMany to wallet owners
$model->walletsForTenant($id); // Wallets for specific tenant
$model->custodialWallets($id); // Custodial wallets only
$model->externalWallets($id); // External wallets only
$model->createWallet($protocol); // Legacy-compatible creation
The major change in v2.x is the move from single-table to two-table architecture. Here's how to migrate:
-
Run New Migrations:
php artisan migrate
-
Update Usage Patterns:
// OLD (v1.x) - Single table approach $wallet = $user->wallets()->create(['protocol' => Protocol::ETH]); // NEW (v2.x) - Service-based approach (recommended) $result = $walletService->createCustodialWallet(Protocol::ETH, $user, 1); $wallet = $result['wallet']; // NEW (v2.x) - Legacy-compatible approach $result = $user->createEthereumWallet(); $wallet = $result['wallet'];
-
Update Relationships:
// OLD - Direct relationship foreach ($user->wallets as $wallet) { ... } // NEW - Through ownership records foreach ($user->wallets as $wallet) { ... } // Still works! // Or more explicit: foreach ($user->walletsForTenant(1) as $wallet) { ... }
-
PHP 8.4+
-
Laravel 10.0+
-
MySQL 8.0+ or PostgreSQL 12.0+
-
PHP Extensions:
gmp
,sodium
-
PHP 8.4+
-
Laravel 11.0+
-
Required PHP extensions:
sodium
(for Solana wallet operations)gmp
(for cryptographic operations)
composer test
The package includes a comprehensive test suite with:
- Feature tests for wallet creation and management
- Unit tests for all services and models
- Integration tests for multi-tenancy
- Architecture tests to ensure code quality
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.