Install the package:
composer require amashukov/ton-wallet-php
Ensure your project meets the requirements: PHP 8.3+, ext-sodium, and ext-bcmath.
Initialize a wallet:
use Amashukov\TonCrypto\Mnemonic;
use Amashukov\TonWallet\WalletV4R2;
$keys = Mnemonic::toKeyPair('your 24 mnemonic words here');
$wallet = new WalletV4R2($keys);
Derive and display the wallet address:
echo $wallet->address()->toString(); // Outputs user-friendly UQ... address
Use the sendTransfer method to send a transfer with a single message:
use Amashukov\TonWallet\Address;
use Amashukov\TonWallet\InternalMessage;
$messages = [
new InternalMessage(
dest: Address::parse('EQDrjaLahLkMB-hMCmkzOyBuHJ139ZUYmPHu6RRBKnbdLIYI'),
value: '1000000000', // 1 TON in nano-TON
bounce: false,
),
];
// Assuming $rpc is an instance of WalletRpcInterface
$wallet->sendTransfer($rpc, $messages, validUntil: time() + 60);
Offline Transfer Assembly:
createTransfer to build and sign a transfer without network interaction.wrapExternalInMessage to wrap the transfer in an external message.$seqno = $wallet->getSeqno($rpc); // Fetch sequence number from RPC
$body = $wallet->createTransfer($seqno, time() + 60, $messages);
$ext = $wallet->wrapExternalInMessage($body);
Batch Transfers:
InternalMessage objects in a single transfer.$messages = [
new InternalMessage(dest: $addr1, value: '1000000000', bounce: false),
new InternalMessage(dest: $addr2, value: '2000000000', bounce: true),
];
Address Handling:
$address = Address::parse('UQ...');
echo $address->toString(userFriendly: false); // Raw format
echo $address->toTonscanFormat(); // URL-safe format
Pluggable RPC: Implement WalletRpcInterface to integrate with any RPC provider (e.g., Toncenter, custom node).
class CustomRpc implements WalletRpcInterface {
public function getSeqno(Address $address): int { /* ... */ }
public function sendBoc(string $boc): string { /* ... */ }
}
Dependency Injection: Use Laravel's service container to bind the wallet and RPC interfaces.
$this->app->bind(WalletRpcInterface::class, function ($app) {
return new ToncenterRpc($app['config']['toncenter.api_key']);
});
Testing: Use the WalletRpcInterface to mock RPC calls for unit testing.
$mockRpc = Mockery::mock(WalletRpcInterface::class);
$mockRpc->shouldReceive('getSeqno')->andReturn(123);
$mockRpc->shouldReceive('sendBoc')->andReturn('success');
Sequence Number Management:
Address Validation:
if (!Address::isValid('invalid-address')) {
throw new \InvalidArgumentException('Invalid TON address');
}
Bounceable vs Non-Bounceable:
bounce flag in InternalMessage matches the recipient's address type to avoid funds being lost.Time Validation:
validUntil timestamp must be in the future. If set too far in the past, the transfer may be rejected by the network.Check BOC Encoding:
Boc::encodeBase64($ext) and decode it using a TON blockchain explorer.Sequence Number Mismatch:
Key Generation:
Mnemonic::toKeyPair to generate keys from a mnemonic phrase. Ensure the mnemonic is secure and backed up.Workchain Handling:
0. If you need to use a different workchain, ensure it’s correctly set in the address.Custom RPC Providers:
WalletRpcInterface to support additional RPC providers or custom logic.Address Serialization:
Address::toString() to customize address formatting for specific use cases.Transfer Customization:
InternalMessage or WalletV4R2 to add custom payloads or additional metadata to transfers.Service Provider Binding:
public function register() {
$this->app->singleton(WalletV4R2::class, function ($app) {
$keys = Mnemonic::toKeyPair(config('ton.wallet.mnemonic'));
return new WalletV4R2($keys);
});
}
Configuration:
// config/ton.php
return [
'wallet' => [
'mnemonic' => env('TON_WALLET_MNEMONIC'),
],
'rpc' => [
'provider' => 'toncenter',
'api_key' => env('TONCENTER_API_KEY'),
],
];
Jobs for Async Transfers:
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendTonTransfer implements ShouldQueue {
use Queueable;
public function handle() {
$wallet = app(WalletV4R2::class);
$rpc = app(WalletRpcInterface::class);
$wallet->sendTransfer($rpc, $this->messages, validUntil: time() + 60);
}
}
How can I help you explore Laravel packages today?