| Code Coverage
| Lines | Functions and Methods | Classes and Traits
Total | 31.11% covered (danger) | 31.11% | 14 / 45 | 40.00% covered (danger) | 40.00% | 2 / 5 | CRAP | 0.00% covered (danger) | 0.00% | 0 / 1
DidSolProgram | 31.11% covered (danger) | 31.11% | 14 / 45 | 40.00% covered (danger) | 40.00% | 2 / 5 | 78.08 | 0.00% covered (danger) | 0.00% | 0 / 1
getDidDataAcccountInfo | 0.00% covered (danger) | 0.00% | 0 / 2 | 0.00% covered (danger) | 0.00% | 0 / 1 | 2 |
getDidDataAccountId | 100.00% covered (success) | 100.00% | 5 / 5 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
deserializeDidData | 100.00% covered (success) | 100.00% | 9 / 9 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
parse | 0.00% covered (danger) | 0.00% | 0 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 42 |
getRpcEndpointFromShortcut | 0.00% covered (danger) | 0.00% | 0 / 11 | 0.00% covered (danger) | 0.00% | 0 / 1 | 30 |

1 | <?php
2 |
3 | namespace Attestto\SolanaPhpSdk\Programs;
4 |
5 | use Attestto\SolanaPhpSdk\Exceptions\BaseSolanaPhpSdkException;
6 | use Attestto\SolanaPhpSdk\Program;
7 | use Attestto\SolanaPhpSdk\Accounts\DidData;
8 | use Attestto\SolanaPhpSdk\PublicKey;
9 | use StephenHill\Base58;
10 | use Attestto\SolanaPhpSdk\SolanaRpcClient;
11 |
12 | /**
13 | * Class DidSolProgram - Work In Progress
14 | *
15 | * This class represents a program for interacting with the Solana blockchain using the DID (Decentralized Identifier) protocol.
16 | * It provides methods for creating and managing DID accounts, signing and verifying messages, and other related operations.
17 | * @version 1.0
18 | * @package Attestto\SolanaPhpSdk\
19 | * @license MIT
20 | * @author Eduardo Chongkan
21 | * @link https://chongkan.com
22 | * @see https://github.com/identity-com/sol-did
23 | */
24 |
25 | class DidSolProgram extends Program
26 | {
27 | public const DIDSOL_PROGRAM_ID = 'didso1Dpqpm4CsiCjzP766BGY89CAdD6ZBL68cRhFPc';
28 | public const DIDSOL_DEFAULT_SEED = 'did-account';
29 |
30 | /**
31 | * getDidDataAcccountInfo
32 | *
33 | * @param SolanaRpcClient|string $client The RPC client or the custom RPC endpoint URL to use.
34 | * @param string $base58SubjectPk The Public Key of the DID.
35 | * @return string (JSON) The account info of the DID data account as it comes from the RPC
36 | * @example DidSolProgram::getDidDataAcccountInfo($client, 'did:sol:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk', false);
37 | */
38 | static function getDidDataAcccountInfo($client, $base58SubjectPk)
39 | {
40 | $pdaPublicKey = self::getDidDataAccountId($base58SubjectPk);
41 | return $client->call('getAccountInfo', [$pdaPublicKey, ["encoding" => "jsonParsed"]])['value'];
42 | // Data is always returned in base54 because it exceeds 128 bytes
43 | }
44 |
45 | /**
46 | * getDidDataAccountId
47 | *
48 | * @param string $did 'did:sol:[cluster]....'
49 | * @return string The base58 encoded public key of the DID data account
50 | * @throws BaseSolanaPhpSdkException
51 | * @example DidSolProgram::getDidDataAccountId('did:sol:devnet:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk');
52 | */
53 | static function getDidDataAccountId($base58SubjectPk): string
54 | {
55 |
56 | $b58 = new Base58();
57 | $seeds = array(self::DIDSOL_DEFAULT_SEED, $b58->decode($base58SubjectPk));
58 | $pId = new PublicKey(self::DIDSOL_PROGRAM_ID);
59 | $publicKey = PublicKey::findProgramAddress($seeds, $pId);
60 |
61 | return $publicKey[0]->toBase58();
62 | }
63 |
64 | /**
65 | * deserializeDidData
66 | *
67 | * @param string $dataBase64 The base64 encoded data of the DID data account
68 | * @return DidData The deserialized DID data object
69 | * @example DidSolProgram::deserializeDidData('TVjvjfsd7fMA/gAAAA...');
70 | */
71 | static function deserializeDidData($dataBase64)
72 | {
73 |
74 | $base64String = base64_decode($dataBase64);
75 | $uint8Array = array_values(unpack('C*', $base64String));
76 | $didData = DidData::fromBuffer($uint8Array);
77 |
78 | $keyData = $didData->keyData;
79 |
80 | $binaryString = pack('C*', ...$keyData);
81 |
82 | $b58 = new Base58();
83 | $base58String = $b58->encode($binaryString);
84 | $didData->keyData = $base58String;
85 | return $didData;
86 | }
87 |
88 |
89 | public function parse(string $did) : array
90 | {
91 |
92 | $did = explode(":", $did);
93 | if ($did[0] !== 'did' || count($did) < 3) {
94 | throw new \Exception('Invalid DID format, use did:sol:[network:]base58SubjectPK');
95 | }
96 | if ($did[1] !== 'sol') {
97 | throw new \Exception('Unsupported DID method, use did:sol:[network:]base58SubjectPK');
98 | }
99 | if (count($did) == 4) {
100 | $network = $did[2];
101 | $base58SubjectPK = $did[3];
102 | }else if (count($did) == 3) {
103 | $network = 'mainnet';
104 | $base58SubjectPK = $did[2];
105 | }
106 |
107 | $rpcEndpoint = $this->getRpcEndpointFromShortcut($network);
108 |
109 | return [
110 | 'network' => $network,
111 | 'base58SubjectPK' => $base58SubjectPK,
112 | 'dataAccountId' => self::getDidDataAccountId($base58SubjectPK),
113 | 'rpcEndpoint' => $rpcEndpoint
114 | ];
115 | }
116 |
117 | private function getRpcEndpointFromShortcut(string $network){
118 | switch ($network) {
119 | case 'mainnet':
120 | $rpcEndpoint = SolanaRpcClient::MAINNET_ENDPOINT;
121 | break;
122 | case 'devnet':
123 | $rpcEndpoint = SolanaRpcClient::DEVNET_ENDPOINT;
124 | break;
125 | case 'testnet':
126 | $rpcEndpoint = SolanaRpcClient::TESTNET_ENDPOINT;
127 | break;
128 | default:
129 | $rpcEndpoint = SolanaRpcClient::MAINNET_ENDPOINT;
130 | }
131 | return $rpcEndpoint;
132 | }
133 |
134 |
135 | }