added full usage documentation
This commit is contained in:
parent
005f132758
commit
9808302ba8
711
README.md
711
README.md
|
|
@ -1,18 +1,26 @@
|
|||
# PHP Contractless RPC
|
||||
|
||||
`contractless-php-rpc` connects PHP applications directly to a Contractless
|
||||
node. It implements the authenticated binary RPC protocol without invoking
|
||||
Contractless CLI programs.
|
||||
`contractless-php-rpc` allows PHP applications to connect directly to a
|
||||
Contractless node through its authenticated binary RPC protocol. It does not
|
||||
invoke Contractless CLI programs and does not depend on centralized blockchain
|
||||
APIs.
|
||||
|
||||
The package is intended for faucets, block explorers, wallets, application
|
||||
backends, and other public Contractless integrations.
|
||||
The package can be used to build faucets, block explorers, wallets, application
|
||||
backends, storage applications, and other public Contractless integrations.
|
||||
|
||||
## Scope
|
||||
|
||||
The client includes public blockchain lookups, wallet and address lookups,
|
||||
mempool lookups, asset lookups, loan lookups, storage lookups, governance
|
||||
lookups, wallet registration, signed transaction submission, and an RPC
|
||||
interface restricted to application-safe commands.
|
||||
The library exposes application-safe public RPC commands, including:
|
||||
|
||||
- Network and blockchain information
|
||||
- Blocks, headers, and torrents
|
||||
- Confirmed transactions and mempool records
|
||||
- Wallet balances, registration, addresses, and history
|
||||
- Tokens, NFTs, RWAs, swaps, and loans
|
||||
- Marketing records
|
||||
- On-chain data storage
|
||||
- Governance proposals
|
||||
- Transaction creation, signing, and broadcasting
|
||||
|
||||
Miner relay commands, network mapping changes, monitor-state commands, node
|
||||
setup synchronization, and server-owner IP controls are intentionally not
|
||||
|
|
@ -20,50 +28,32 @@ exposed.
|
|||
|
||||
## Requirements
|
||||
|
||||
- PHP 8.1 or newer, running as a 64-bit build
|
||||
- The PHP GD extension
|
||||
- The PHP OpenSSL extension
|
||||
- The `skein` module from `contractless-php-crypto`
|
||||
- The `oqsphp` module from `contractless-php-crypto`
|
||||
- PHP 8.1 or newer using a 64-bit build
|
||||
- PHP GD
|
||||
- PHP OpenSSL
|
||||
- The `skein` module from Contractless PHP Modules
|
||||
- The `oqsphp` module from Contractless PHP Modules
|
||||
|
||||
Contractless uses the same Falcon key and signature sizes as
|
||||
`Falcon-padded-512`, but its FN-DSA signing mode requires a small message
|
||||
adaptation. `NativeCrypto` performs that adaptation automatically.
|
||||
|
||||
Install and test the Contractless PHP Modules before installing this package:
|
||||
|
||||
https://contractless.dev/contractless/Contractless-PHP-Modules
|
||||
|
||||
## Installation
|
||||
|
||||
Install and enable `contractless-php-crypto` first. Its repository contains the
|
||||
native module build, installation, and compatibility-test instructions.
|
||||
|
||||
When this package is available through Packagist:
|
||||
Install version `0.2` through Packagist:
|
||||
|
||||
```bash
|
||||
composer require contractless/contractless-php-rpc
|
||||
composer require contractless/contractless-php-rpc:^0.2.0 -W
|
||||
```
|
||||
|
||||
Until the package is published, add it as a Composer path repository:
|
||||
Composer installs the package under `vendor/` and generates the required
|
||||
autoload files.
|
||||
|
||||
```json
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "path",
|
||||
"url": "../contractless-php-rpc"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"contractless/contractless-php-rpc": "@dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then install it:
|
||||
|
||||
```bash
|
||||
composer update contractless/contractless-php-rpc
|
||||
```
|
||||
|
||||
## Basic Use
|
||||
## Create A Client
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
|
@ -75,112 +65,634 @@ use Contractless\Rpc\Crypto\NativeCrypto;
|
|||
use Contractless\Rpc\Transport\StreamTransport;
|
||||
use Contractless\Rpc\Wallet\Credentials;
|
||||
|
||||
$credentials = Credentials::fromHex(
|
||||
getenv('CONTRACTLESS_PUBLIC_KEY'),
|
||||
getenv('CONTRACTLESS_PRIVATE_KEY'),
|
||||
$credentials = Credentials::fromWalletFile(
|
||||
'/private/path/contractless.wallet',
|
||||
'wallet decryption key',
|
||||
);
|
||||
|
||||
$crypto = new NativeCrypto();
|
||||
|
||||
$client = new Client(
|
||||
new StreamTransport('127.0.0.1', 50050),
|
||||
new StreamTransport(
|
||||
host: '127.0.0.1',
|
||||
port: 50050,
|
||||
timeout: 10.0,
|
||||
),
|
||||
$crypto,
|
||||
$credentials,
|
||||
);
|
||||
```
|
||||
|
||||
echo $client->totalBalance(
|
||||
'ab13318c26250b048db92920a80a86127c933b0c.cltc',
|
||||
Use port `50050` for the default testnet RPC and `50055` for the default
|
||||
mainnet RPC unless the node operator configured another port.
|
||||
|
||||
Every RPC call performs a signed client handshake. The wallet used for the
|
||||
handshake does not have to own an address being looked up.
|
||||
|
||||
## RPC Replies
|
||||
|
||||
RPC methods return the node's original reply as a PHP string. Depending on the
|
||||
command, that string may contain text, JSON, or raw binary data.
|
||||
|
||||
Decode JSON replies when appropriate:
|
||||
|
||||
```php
|
||||
$data = json_decode(
|
||||
$client->networkInfo(),
|
||||
true,
|
||||
flags: JSON_THROW_ON_ERROR,
|
||||
);
|
||||
```
|
||||
|
||||
RPC replies are returned as their original bytes. Commands that return JSON can
|
||||
be decoded with `json_decode($reply, true, flags: JSON_THROW_ON_ERROR)`. Binary
|
||||
block, torrent, header, and transaction replies remain available without a
|
||||
lossy conversion.
|
||||
Do not apply text conversion to raw blocks, headers, torrents, transactions, or
|
||||
binary integer replies.
|
||||
|
||||
## Transaction Submission
|
||||
A coin balance is returned as an eight-byte little-endian unsigned integer:
|
||||
|
||||
`submitTransaction()` accepts the complete serialized signed transaction as a
|
||||
hexadecimal string:
|
||||
```php
|
||||
$reply = $client->coinBalance('CLTC', $address);
|
||||
$balanceAtomic = unpack('Pbalance', $reply)['balance'];
|
||||
$balance = number_format($balanceAtomic / 100_000_000, 8, '.', '');
|
||||
```
|
||||
|
||||
All transaction amounts and fees supplied to builders use atomic units.
|
||||
Contractless has `100,000,000` atomic units per `CLC` or `CLTC`.
|
||||
|
||||
## Network And Chain RPC
|
||||
|
||||
```php
|
||||
$network = $client->networkInfo();
|
||||
$height = $client->blockHeight();
|
||||
$nodeTime = $client->nodeTime();
|
||||
$difficulty = $client->difficulty();
|
||||
$latestBlock = $client->latestBlock();
|
||||
$largestFee = $client->largestTransactionFee();
|
||||
$confirmedCount = $client->totalConfirmedTransactions();
|
||||
```
|
||||
|
||||
- `networkInfo()` returns the node's public network information.
|
||||
- `blockHeight()` returns the current saved chain height.
|
||||
- `nodeTime()` returns the node's current time.
|
||||
- `difficulty()` returns the current mining difficulty.
|
||||
- `latestBlock()` returns the latest saved block.
|
||||
- `largestTransactionFee()` returns the largest eligible mempool fee.
|
||||
- `totalConfirmedTransactions()` returns the confirmed transaction count.
|
||||
|
||||
## Block, Header, And Torrent RPC
|
||||
|
||||
Hashes passed to these methods are 64-character hexadecimal hashes:
|
||||
|
||||
```php
|
||||
$block = $client->blockByHeight(1000);
|
||||
$block = $client->blockByHash($blockHash);
|
||||
$torrent = $client->torrentByHeight(1000);
|
||||
$header = $client->headerByHeight(1000);
|
||||
$header = $client->headerByHash($headerHash);
|
||||
$blockHash = $client->blockHashAtHeight(1000);
|
||||
$headers = $client->allHeaders();
|
||||
```
|
||||
|
||||
- `blockByHeight()` returns the raw saved block at a height.
|
||||
- `blockByHash()` returns the raw block matching a block hash.
|
||||
- `torrentByHeight()` returns the raw torrent metadata for a block.
|
||||
- `headerByHeight()` returns the raw block header at a height.
|
||||
- `headerByHash()` returns the raw header matching a header hash.
|
||||
- `blockHashAtHeight()` returns the hash recorded at a height.
|
||||
- `allHeaders()` returns the saved block-header history.
|
||||
|
||||
## Transaction And Mempool RPC
|
||||
|
||||
```php
|
||||
$mempoolCount = $client->mempoolCount();
|
||||
$mempoolTransaction = $client->mempoolTransactionBySignature(
|
||||
$signatureHex,
|
||||
);
|
||||
$addressMempool = $client->mempoolTransactionsByAddress($address);
|
||||
$transaction = $client->transactionById($transactionId);
|
||||
```
|
||||
|
||||
- `mempoolCount()` returns the number of pending transactions.
|
||||
- `mempoolTransactionBySignature()` accepts a 1,332-character hexadecimal
|
||||
Falcon signature and returns the matching pending transaction.
|
||||
- `mempoolTransactionsByAddress()` returns pending transactions involving an
|
||||
address.
|
||||
- `transactionById()` accepts a 64-character hexadecimal transaction ID and
|
||||
returns the confirmed transaction and its recorded location.
|
||||
|
||||
Submit a complete serialized signed transaction represented as hexadecimal:
|
||||
|
||||
```php
|
||||
$reply = $client->submitTransaction($signedTransactionHex);
|
||||
```
|
||||
|
||||
The first transaction builder covers the transfer used by faucets and ordinary
|
||||
currency, token, NFT, and RWA transfers:
|
||||
Submit a `SignedTransaction` produced by one of the included builders:
|
||||
|
||||
```php
|
||||
$reply = $client->submitSignedTransaction($signedTransaction);
|
||||
```
|
||||
|
||||
## Wallet And Address RPC
|
||||
|
||||
```php
|
||||
$coinBalance = $client->coinBalance('CLTC', $address);
|
||||
$allBalances = $client->totalBalance($address);
|
||||
$addressValid = $client->validateAddress($address);
|
||||
$registration = $client->walletRegistrationStatus($address);
|
||||
$history = $client->addressHistory($address, skip: 0, limit: 100);
|
||||
$latest = $client->latestAddressTransactions($address, limit: 25);
|
||||
$canonical = $client->vanityLookup('my-vanity-address.cltc');
|
||||
$vanityOwner = $client->vanityOwner('my-vanity-address.cltc');
|
||||
```
|
||||
|
||||
- `coinBalance()` returns one confirmed asset balance as an eight-byte integer.
|
||||
- `totalBalance()` returns all confirmed balances owned by an address.
|
||||
- `validateAddress()` asks the node to validate an address.
|
||||
- `walletRegistrationStatus()` returns whether the wallet is registered.
|
||||
- `addressHistory()` returns a paginated confirmed transaction history.
|
||||
- `latestAddressTransactions()` returns the newest confirmed transactions.
|
||||
- `vanityLookup()` resolves a vanity address to its canonical wallet.
|
||||
- `vanityOwner()` returns the canonical owner of a vanity address.
|
||||
|
||||
Verify a signed message:
|
||||
|
||||
```php
|
||||
$result = $client->validateMessage(
|
||||
message: 'Message to verify',
|
||||
address: $address,
|
||||
signature: $signatureHex,
|
||||
);
|
||||
```
|
||||
|
||||
Register the wallet loaded into `$credentials`:
|
||||
|
||||
```php
|
||||
$result = $client->registerOwnedWallet($address);
|
||||
```
|
||||
|
||||
The lower-level registration method is available when an application has
|
||||
already created the required signature:
|
||||
|
||||
```php
|
||||
$result = $client->registerWallet($address, $signatureHex);
|
||||
```
|
||||
|
||||
## Token, NFT, RWA, Swap, And Loan RPC
|
||||
|
||||
```php
|
||||
$tokens = $client->tokenList();
|
||||
$tokenCatalog = $client->tokenCatalog();
|
||||
$token = $client->tokenDetails('TOKEN');
|
||||
|
||||
$nfts = $client->nftList();
|
||||
$nft = $client->nftDetails('ART', series: 1);
|
||||
|
||||
$loan = $client->loanByHash($loanHash);
|
||||
$addressContracts = $client->contractsByAddress($address);
|
||||
$collateral = $client->collateralStatus($loanHash);
|
||||
```
|
||||
|
||||
- `tokenList()` returns the node's token list.
|
||||
- `tokenCatalog()` returns token catalog data.
|
||||
- `tokenDetails()` returns details for one token ticker.
|
||||
- `nftList()` returns the node's NFT and RWA list.
|
||||
- `nftDetails()` returns one NFT/RWA name and series record.
|
||||
- `loanByHash()` returns a loan using its contract hash.
|
||||
- `contractsByAddress()` returns loan contracts involving an address.
|
||||
- `collateralStatus()` returns the collateral state of a loan.
|
||||
|
||||
## Marketing RPC
|
||||
|
||||
```php
|
||||
$records = $client->marketingCampaignHistory(
|
||||
advertiser: $address,
|
||||
campaign: 42,
|
||||
skip: 0,
|
||||
limit: 100,
|
||||
);
|
||||
```
|
||||
|
||||
`marketingCampaignHistory()` returns paginated marketing records for one
|
||||
advertiser and campaign number.
|
||||
|
||||
## Data Storage RPC
|
||||
|
||||
Get the cost of returning one data key:
|
||||
|
||||
```php
|
||||
$quote = $client->storageLookupCost(
|
||||
storageKey: $storageKeyHash,
|
||||
dataKey: 'username',
|
||||
address: $address,
|
||||
);
|
||||
```
|
||||
|
||||
Get the cost of returning every key stored under the storage key and address:
|
||||
|
||||
```php
|
||||
$quote = $client->storageLookupCost(
|
||||
storageKey: $storageKeyHash,
|
||||
dataKey: 'all',
|
||||
address: $address,
|
||||
);
|
||||
```
|
||||
|
||||
Perform a paid lookup using a complete signed type-2 transfer represented as
|
||||
hexadecimal:
|
||||
|
||||
```php
|
||||
$data = $client->storageLookup(
|
||||
storageKey: $storageKeyHash,
|
||||
dataKey: 'all',
|
||||
address: $address,
|
||||
paymentTransactionHex: $paymentTransactionHex,
|
||||
);
|
||||
```
|
||||
|
||||
The payment must match the quote returned by the selected node. A node operator
|
||||
looking up data through their own node may receive a zero-cost quote.
|
||||
|
||||
## Governance RPC
|
||||
|
||||
```php
|
||||
$proposal = $client->governanceProposal($proposalKeyHash);
|
||||
```
|
||||
|
||||
`governanceProposal()` returns the recorded state of a Contractless proposal,
|
||||
including its voting and activation information.
|
||||
|
||||
## Create And Broadcast A Transfer
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Transaction\TransferBuilder;
|
||||
|
||||
$transfer = (new TransferBuilder($crypto, $credentials))->create(
|
||||
sender: 'sender-address.cltc',
|
||||
receiver: 'receiver-address.cltc',
|
||||
sender: $sender,
|
||||
receiver: $receiver,
|
||||
coin: 'CLTC',
|
||||
value: 2_500_000_000,
|
||||
fee: 2_500,
|
||||
fee: 25_000_000,
|
||||
nftSeries: 0,
|
||||
);
|
||||
|
||||
$reply = $client->submitSignedTransaction($transfer);
|
||||
```
|
||||
|
||||
Transaction builders are a separate layer over the protocol client. This keeps
|
||||
transport and node access stable when new transaction types are introduced.
|
||||
The same builder transfers base currency, tokens, NFTs, and RWAs. Set
|
||||
`nftSeries` to the applicable series number for an NFT/RWA transfer.
|
||||
|
||||
## Transaction Builders
|
||||
|
||||
- `TransferBuilder`: currency, token, NFT, and RWA transfers
|
||||
- `AssetBuilder`: token creation, token issuance, NFT/RWA creation, burns, and vanity addresses
|
||||
- `AgreementBuilder`: two-party swaps and loan contracts
|
||||
- `MiscellaneousBuilder`: marketing records, loan payments, and collateral claims
|
||||
- `StorageBuilder`: storage keys, bool, signed and unsigned integers, strings, and deletion
|
||||
- `GovernanceBuilder`: proposal keys, proposal votes, and activation votes
|
||||
|
||||
Swaps and loans return `DualSignedTransaction`. The first signer exports it,
|
||||
the second signer imports it and calls `signNext()`, and only a complete result
|
||||
can be converted with `toSignedTransaction()` and broadcast.
|
||||
|
||||
The 128-bit storage methods accept decimal strings. This preserves the complete
|
||||
Rust `u128` and `i128` ranges instead of silently losing precision through PHP
|
||||
integers or floating-point numbers.
|
||||
|
||||
## Wallet Material
|
||||
|
||||
The library can load a normal Contractless wallet file directly:
|
||||
## Token, NFT, RWA, Burn, And Vanity Builders
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Wallet\Credentials;
|
||||
use Contractless\Rpc\Transaction\AssetBuilder;
|
||||
|
||||
$assets = new AssetBuilder($crypto, $credentials);
|
||||
|
||||
$createToken = $assets->createToken(
|
||||
creator: $address,
|
||||
ticker: 'TOKEN',
|
||||
number: 1_000_000_000,
|
||||
hardLimit: true,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$issueToken = $assets->issueToken(
|
||||
creator: $address,
|
||||
ticker: 'TOKEN',
|
||||
number: 500_000_000,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$createNft = $assets->createNft(
|
||||
creator: $address,
|
||||
series: false,
|
||||
fractionalOwnership: false,
|
||||
name: 'ART',
|
||||
ipfs: 'ipfs-content-identifier',
|
||||
count: 1,
|
||||
description: 'Description of the NFT or RWA',
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$burn = $assets->burn(
|
||||
address: $address,
|
||||
coin: 'TOKEN',
|
||||
nftSeries: 0,
|
||||
value: 100_000_000,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$vanity = $assets->vanity(
|
||||
address: $address,
|
||||
vanityAddress: 'my-address.cltc',
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$client->submitSignedTransaction($createToken);
|
||||
```
|
||||
|
||||
## Swap And Loan Builders
|
||||
|
||||
Swaps and loans require two wallet signatures. The first wallet creates and
|
||||
signs the transaction:
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Transaction\AgreementBuilder;
|
||||
|
||||
$agreements = new AgreementBuilder($crypto, $credentials);
|
||||
|
||||
$swap = $agreements->swap(
|
||||
expiration: time() + 3600,
|
||||
ticker1: 'CLTC',
|
||||
series1: 0,
|
||||
value1: 100_000_000,
|
||||
ticker2: 'TOKEN',
|
||||
series2: 0,
|
||||
value2: 500_000_000,
|
||||
sender1: $firstAddress,
|
||||
sender2: $secondAddress,
|
||||
tip1: 1_000_000,
|
||||
tip2: 1_000_000,
|
||||
fee1: 2_500,
|
||||
fee2: 2_500,
|
||||
);
|
||||
|
||||
$loan = $agreements->loan(
|
||||
loanCoin: 'CLTC',
|
||||
loanAmount: 1_000_000_000,
|
||||
lender: $lender,
|
||||
collateral: 'TOKEN',
|
||||
collateralAmount: 2_000_000_000,
|
||||
borrower: $borrower,
|
||||
paymentPeriod: 'm',
|
||||
paymentNumber: 12,
|
||||
paymentAmount: 100_000_000,
|
||||
gracePeriod: 2,
|
||||
maxLateValue: 300_000_000,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
```
|
||||
|
||||
Export the partially signed transaction for the second party:
|
||||
|
||||
```php
|
||||
$exported = json_encode(
|
||||
$swap->export(),
|
||||
JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR,
|
||||
);
|
||||
```
|
||||
|
||||
The second party imports and signs it using their own `$crypto` and
|
||||
`$credentials`:
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Transaction\DualSignedTransaction;
|
||||
|
||||
$swap = DualSignedTransaction::import(
|
||||
json_decode($exported, true, flags: JSON_THROW_ON_ERROR),
|
||||
);
|
||||
|
||||
$swap = $swap->signNext($crypto, $credentials);
|
||||
|
||||
if ($swap->isComplete()) {
|
||||
$client->submitSignedTransaction($swap->toSignedTransaction());
|
||||
}
|
||||
```
|
||||
|
||||
## Loan Payment And Collateral Builders
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Transaction\MiscellaneousBuilder;
|
||||
|
||||
$miscellaneous = new MiscellaneousBuilder($crypto, $credentials);
|
||||
|
||||
$payment = $miscellaneous->loanPayment(
|
||||
amount: 100_000_000,
|
||||
contractHash: $loanHash,
|
||||
address: $borrower,
|
||||
tip: 1_000_000,
|
||||
fee: 2_500,
|
||||
);
|
||||
|
||||
$claim = $miscellaneous->collateralClaim(
|
||||
contractHash: $loanHash,
|
||||
address: $lender,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$client->submitSignedTransaction($payment);
|
||||
```
|
||||
|
||||
## Marketing Transaction Builder
|
||||
|
||||
```php
|
||||
$marketing = $miscellaneous->marketing(
|
||||
campaign: 42,
|
||||
adType: 'banner',
|
||||
keyword: 'blockchain',
|
||||
displayed: 'https://example.com/page',
|
||||
impressions: 1,
|
||||
clicks: 0,
|
||||
impressionValue: 100,
|
||||
clickValue: 500,
|
||||
advertiser: $address,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$client->submitSignedTransaction($marketing);
|
||||
```
|
||||
|
||||
## Data Storage Transaction Builders
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Transaction\StorageBuilder;
|
||||
|
||||
$storage = new StorageBuilder($crypto, $credentials);
|
||||
|
||||
$createKey = $storage->createKey(
|
||||
address: $address,
|
||||
fee: 50_000_000_000,
|
||||
);
|
||||
|
||||
$boolean = $storage->boolean(
|
||||
$storageKey, 'active', true, $address, 100_000_000,
|
||||
);
|
||||
|
||||
$u8 = $storage->u8($storageKey, 'u8-key', 255, $address, 100_000_000);
|
||||
$u16 = $storage->u16($storageKey, 'u16-key', 65535, $address, 100_000_000);
|
||||
$u32 = $storage->u32($storageKey, 'u32-key', 100000, $address, 100_000_000);
|
||||
$u64 = $storage->u64($storageKey, 'u64-key', 100000, $address, 100_000_000);
|
||||
$u128 = $storage->u128(
|
||||
$storageKey,
|
||||
'u128-key',
|
||||
'340282366920938463463374607431768211455',
|
||||
$address,
|
||||
100_000_000,
|
||||
);
|
||||
|
||||
$i8 = $storage->i8($storageKey, 'i8-key', -128, $address, 100_000_000);
|
||||
$i16 = $storage->i16($storageKey, 'i16-key', -32768, $address, 100_000_000);
|
||||
$i32 = $storage->i32($storageKey, 'i32-key', -100000, $address, 100_000_000);
|
||||
$i64 = $storage->i64($storageKey, 'i64-key', -100000, $address, 100_000_000);
|
||||
$i128 = $storage->i128(
|
||||
$storageKey,
|
||||
'i128-key',
|
||||
'-170141183460469231731687303715884105728',
|
||||
$address,
|
||||
100_000_000,
|
||||
);
|
||||
|
||||
$string = $storage->string(
|
||||
storageKey: $storageKey,
|
||||
key: 'username',
|
||||
value: 'contractless-user',
|
||||
address: $address,
|
||||
previousHash: str_repeat('0', 64),
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$delete = $storage->delete(
|
||||
storageKey: $storageKey,
|
||||
key: 'username',
|
||||
address: $address,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$client->submitSignedTransaction($string);
|
||||
```
|
||||
|
||||
The `u128()` and `i128()` methods accept decimal strings so PHP does not lose
|
||||
precision. String values may contain up to 180 bytes per linked transaction.
|
||||
The first string uses 64 zeroes for `previousHash`; later chunks use the hash
|
||||
of the preceding string transaction.
|
||||
|
||||
## Governance Transaction Builders
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Transaction\GovernanceBuilder;
|
||||
|
||||
$governance = new GovernanceBuilder($crypto, $credentials);
|
||||
|
||||
$proposal = $governance->proposal(
|
||||
proposalHash: $proposalDocumentHash,
|
||||
address: $address,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$proposalVote = $governance->proposalVote(
|
||||
proposalKey: $proposalKey,
|
||||
address: $address,
|
||||
approve: true,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$activationVote = $governance->activationVote(
|
||||
proposalKey: $proposalKey,
|
||||
developmentHash: $implementationDocumentHash,
|
||||
developmentLocation: 'CLP-0001-IMPLEMENTATION.md',
|
||||
address: $address,
|
||||
approve: true,
|
||||
fee: 100_000_000,
|
||||
);
|
||||
|
||||
$client->submitSignedTransaction($proposalVote);
|
||||
```
|
||||
|
||||
The network still determines whether the submitting wallet is an eligible node
|
||||
for proposal and activation voting.
|
||||
|
||||
## Signed Transaction Data
|
||||
|
||||
Every single-signature builder returns `SignedTransaction`:
|
||||
|
||||
```php
|
||||
$rawBytes = $transaction->bytes;
|
||||
$hexadecimal = $transaction->toHex();
|
||||
$signatureBytes = $transaction->signature;
|
||||
$unsignedSigningJson = $transaction->unsignedJson;
|
||||
```
|
||||
|
||||
Use `submitSignedTransaction()` to broadcast the exact bytes without converting
|
||||
them to hexadecimal first.
|
||||
|
||||
## Wallet Files
|
||||
|
||||
Load a normal Contractless wallet:
|
||||
|
||||
```php
|
||||
$credentials = Credentials::fromWalletFile(
|
||||
'/private/path/contractless.wallet',
|
||||
'wallet decryption key',
|
||||
);
|
||||
```
|
||||
|
||||
The loader decodes all supported Contractless private-key image orientations,
|
||||
verifies the encrypted payload HMAC, decrypts the Falcon private key, proves
|
||||
that the public and private keys match, and verifies the wallet's canonical
|
||||
short address.
|
||||
The loader:
|
||||
|
||||
- Decodes every supported Contractless private-key image orientation
|
||||
- Verifies the encrypted payload HMAC
|
||||
- Decrypts the Falcon private key
|
||||
- Proves that the public and private keys match
|
||||
- Verifies the wallet's canonical short address
|
||||
|
||||
Wallet files and decryption keys must remain outside public web directories,
|
||||
logs, repositories, and client-visible configuration.
|
||||
|
||||
Verify a wallet file and decryption key before using them in an application:
|
||||
Verify a wallet before using it:
|
||||
|
||||
```bash
|
||||
php examples/load_wallet.php /private/path/contractless.wallet 'wallet decryption key'
|
||||
php vendor/contractless/contractless-php-rpc/examples/load_wallet.php \
|
||||
/private/path/contractless.wallet \
|
||||
'wallet decryption key'
|
||||
```
|
||||
|
||||
Successful output confirms that the image was decoded, its encrypted payload
|
||||
was authenticated and decrypted, the Falcon keypair matches, and the wallet
|
||||
address is valid.
|
||||
|
||||
Raw Falcon key bytes remain supported for applications that already manage
|
||||
their own protected key storage:
|
||||
Applications that already manage protected raw keys may load hexadecimal
|
||||
Falcon keys directly:
|
||||
|
||||
```php
|
||||
$credentials = Credentials::fromHex($publicKeyHex, $privateKeyHex);
|
||||
```
|
||||
|
||||
## Direct Public Command Calls
|
||||
|
||||
The named methods above are wrappers around `call()`. Applications may invoke
|
||||
an exposed public command directly when they need to construct its binary
|
||||
payload themselves:
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Protocol\Command;
|
||||
|
||||
$reply = $client->call(Command::BLOCK_HEIGHT);
|
||||
```
|
||||
|
||||
With a payload:
|
||||
|
||||
```php
|
||||
use Contractless\Rpc\Protocol\Binary;
|
||||
|
||||
$reply = $client->call(
|
||||
Command::BLOCK_BY_HEIGHT,
|
||||
Binary::u32(1000),
|
||||
);
|
||||
```
|
||||
|
||||
`call()` rejects internal, administrative, mining, monitor, and unsupported
|
||||
command numbers. Prefer the named methods whenever one exists because they
|
||||
validate and encode the command payload correctly.
|
||||
|
||||
## Error Handling
|
||||
|
||||
RPC transport, protocol, validation, and wallet failures throw exceptions:
|
||||
|
||||
```php
|
||||
try {
|
||||
$height = $client->blockHeight();
|
||||
} catch (Throwable $error) {
|
||||
error_log($error->getMessage());
|
||||
}
|
||||
```
|
||||
|
||||
Do not expose wallet errors, filesystem paths, private material, or internal
|
||||
node replies directly to public users.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
|
|
@ -188,17 +700,8 @@ composer install
|
|||
composer test
|
||||
```
|
||||
|
||||
## Preparing The Composer Package
|
||||
|
||||
Validate the package before publishing:
|
||||
Validate package metadata before publishing a release:
|
||||
|
||||
```bash
|
||||
composer validate --strict
|
||||
composer install
|
||||
composer test
|
||||
```
|
||||
|
||||
Commit `composer.json`, `README.md`, `src/`, and `tests/` to the
|
||||
`contractless-php-rpc` repository. Create a release tag such as `v0.1.0`, then
|
||||
submit the repository URL to Packagist. Future version tags become installable
|
||||
Composer releases.
|
||||
|
|
|
|||
Loading…
Reference in New Issue