Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions src/Extrinsic/Extrinsic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Substrate\ScaleCodec\Extrinsic;

use Substrate\ScaleCodec\Bytes\ScaleBytes;
use Substrate\ScaleCodec\Metadata\Metadata;
use Substrate\ScaleCodec\Types\{TypeRegistry, Compact};

/**
* Represents a Substrate extrinsic (transaction).
*/
class Extrinsic
{
/**
* @param array $call The call data [pallet, function, args]
* @param Signature|null $signature The signature (null for unsigned)
* @param array $extra Extra data (era, nonce, tip)
*/
public function __construct(
public readonly array $call,
public readonly ?Signature $signature = null,
public readonly array $extra = [],
) {}

/**
* Check if this extrinsic is signed.
*/
public function isSigned(): bool
{
return $this->signature !== null;
}

/**
* Get the pallet name.
*/
public function getPallet(): string
{
return $this->call['pallet'] ?? '';
}

/**
* Get the function name.
*/
public function getFunction(): string
{
return $this->call['function'] ?? '';
}

/**
* Get the call arguments.
*/
public function getArguments(): array
{
return $this->call['args'] ?? [];
}

/**
* Get the signer address (if signed).
*/
public function getSigner(): ?string
{
return $this->signature?->signer;
}

/**
* Get the signature bytes (if signed).
*/
public function getSignatureBytes(): ?string
{
return $this->signature?->signature;
}

/**
* Get the era (mortality) of the extrinsic.
*/
public function getEra(): ?array
{
return $this->extra['era'] ?? $this->signature?->getEra();
}

/**
* Get the nonce.
*/
public function getNonce(): int
{
return $this->extra['nonce'] ?? $this->signature?->getNonce() ?? 0;
}

/**
* Get the tip.
*/
public function getTip(): int|string
{
return $this->extra['tip'] ?? $this->signature?->getTip() ?? 0;
}
}
222 changes: 222 additions & 0 deletions src/Extrinsic/ExtrinsicBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<?php

declare(strict_types=1);

namespace Substrate\ScaleCodec\Extrinsic;

/**
* Builder for creating extrinsics.
*
* Usage:
* $extrinsic = ExtrinsicBuilder::create()
* ->pallet('Balances')
* ->function('transfer')
* ->args(['dest' => $address, 'value' => 1000])
* ->signer($signerAddress)
* ->signature($signature)
* ->nonce(1)
* ->tip(0)
* ->build();
*/
class ExtrinsicBuilder
{
private ?string $pallet = null;
private ?string $function = null;
private int $palletIndex = 0;
private int $functionIndex = 0;
private array $args = [];
private ?string $signer = null;
private ?string $signature = null;
private string $signatureType = 'sr25519';
private int $nonce = 0;
private int|string $tip = 0;
private ?array $era = null;
private int $version = 4;

private function __construct()
{
}

/**
* Create a new builder instance.
*/
public static function create(): self
{
return new self();
}

/**
* Set the pallet name.
*/
public function pallet(string $name): self
{
$this->pallet = $name;
return $this;
}

/**
* Set the pallet index directly.
*/
public function palletIndex(int $index): self
{
$this->palletIndex = $index;
return $this;
}

/**
* Set the function name.
*/
public function function(string $name): self
{
$this->function = $name;
return $this;
}

/**
* Set the function index directly.
*/
public function functionIndex(int $index): self
{
$this->functionIndex = $index;
return $this;
}

/**
* Set the call arguments.
*/
public function args(array $args): self
{
$this->args = $args;
return $this;
}

/**
* Add a single argument.
*/
public function arg(string $name, mixed $value): self
{
$this->args[$name] = $value;
return $this;
}

/**
* Set the signer address.
*/
public function signer(string $address): self
{
$this->signer = $address;
return $this;
}

/**
* Set the signature.
*/
public function signature(string $signature, string $type = 'sr25519'): self
{
$this->signature = $signature;
$this->signatureType = $type;
return $this;
}

/**
* Set the nonce.
*/
public function nonce(int $nonce): self
{
$this->nonce = $nonce;
return $this;
}

/**
* Set the tip.
*/
public function tip(int|string $tip): self
{
$this->tip = $tip;
return $this;
}

/**
* Set immortal era.
*/
public function immortal(): self
{
$this->era = null;
return $this;
}

/**
* Set mortal era.
*/
public function mortal(int $period, int $phase = 0): self
{
$this->era = ['period' => $period, 'phase' => $phase];
return $this;
}

/**
* Set the extrinsic version.
*/
public function version(int $version): self
{
$this->version = $version;
return $this;
}

/**
* Build an unsigned extrinsic.
*/
public function buildUnsigned(): Extrinsic
{
return new Extrinsic(
call: $this->buildCall(),
signature: null,
extra: [],
);
}

/**
* Build a signed extrinsic.
*/
public function build(): Extrinsic
{
$signature = null;

if ($this->signer !== null && $this->signature !== null) {
$signature = new Signature(
signer: $this->signer,
signature: $this->signature,
extra: [
'era' => $this->era,
'nonce' => $this->nonce,
'tip' => $this->tip,
],
signerType: $this->signatureType,
);
}

return new Extrinsic(
call: $this->buildCall(),
signature: $signature,
extra: [
'era' => $this->era,
'nonce' => $this->nonce,
'tip' => $this->tip,
],
);
}

/**
* Build the call data.
*/
private function buildCall(): array
{
return [
'pallet' => $this->pallet,
'palletIndex' => $this->palletIndex,
'function' => $this->function,
'functionIndex' => $this->functionIndex,
'args' => $this->args,
];
}
}
Loading
Loading