keyFromPrivate($privateKeyHex); $sig = $key->sign($hash, ['canonical' => true]); $r = str_pad($sig->r->toString(16), 64, '0', STR_PAD_LEFT); $s = str_pad($sig->s->toString(16), 64, '0', STR_PAD_LEFT); $recovery = (int) ($sig->recoveryParam ?? 0); $v = (string) ($recovery + 35 + $chainId * 2); $signed = EthRlp::encodeList([ EthRlp::intToBin($tx['nonce']), EthRlp::intToBin($tx['gasPrice']), EthRlp::intToBin($tx['gas']), hex2bin($to) ?: '', EthRlp::intToBin($tx['value']), $data === '' ? '' : (hex2bin($data) ?: ''), EthRlp::intToBin($v), hex2bin($r) ?: '', hex2bin($s) ?: '', ]); return '0x'.bin2hex($signed); } public static function publicAddress(string $privateKeyHex): string { $privateKeyHex = strtolower($privateKeyHex); if (str_starts_with($privateKeyHex, '0x')) { $privateKeyHex = substr($privateKeyHex, 2); } $ec = new EC('secp256k1'); $pub = $ec->keyFromPrivate($privateKeyHex)->getPublic(false, 'hex'); return EthAddress::fromUncompressedPublicKey($pub); } }