Skip to content

Commit

Permalink
Add test_getReverseKeySync
Browse files Browse the repository at this point in the history
  • Loading branch information
chongkan committed May 9, 2024
1 parent d95c43c commit 1588977
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ public function getProgramAccounts(string $programIdBs58, $dataSlice, $filters)

}

public function getMinimumBalanceForRentExemption(array $params = [1024]){
return $this->client->call('getMinimumBalanceForRentExemption', $params );
public function getMinimumBalanceForRentExemption(int $space = 1024){
return $this->client->call('getMinimumBalanceForRentExemption', [$space] );
}

}
126 changes: 124 additions & 2 deletions src/Programs/SNS/Bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,29 @@
use Attestto\SolanaPhpSdk\Exceptions\AccountNotFoundException;
use Attestto\SolanaPhpSdk\Exceptions\SNSError;
use Attestto\SolanaPhpSdk\Programs\SNS\State\NameRegistryStateAccount;
use Attestto\SolanaPhpSdk\Programs\SystemProgram;
use Attestto\SolanaPhpSdk\PublicKey;
use Attestto\SolanaPhpSdk\TransactionInstruction;
use Attestto\SolanaPhpSdk\Util\Buffer;
use Exception;


/**
* @method createInstruction($NAME_PROGRAM_ID, $programId, PublicKey $nameAccountKey, PublicKey $nameOwner, PublicKey $payerKey, Buffer $hashed_name, Buffer $param, Buffer $param1, PublicKey|null $nameClass, PublicKey|null $parentName, $nameParentOwner)
* @method getReverseKeySync(string $subdomain, true $true)
* @method createReverseName(mixed $pubkey, string $string, PublicKey $param, mixed $parent, PublicKey $owner)
* @method getNameOwner(Connection $connection, PublicKey $parentName)
* @method retrieve(Connection $connection, mixed $parent)
* @method transferInstruction(mixed $NAME_PROGRAM_ID, mixed $pubkey, PublicKey $newOwner, PublicKey|null $owner, $null, mixed $nameParent, $nameParentOwner)
*/
trait Bindings
{
use Utils;
// use Utils;
// use Instructions;

/**
* @throws SNSError
* @throws AccountNotFoundException
* @throws Exception
*/
public function createSubdomain(
Connection $connection,
Expand Down Expand Up @@ -66,4 +80,112 @@ public function createSubdomain(

return [[], $ixs];
}

/**
* Creates a name account with the given rent budget, allocated space, owner and class.
*
* @param Connection $connection The solana connection object to the RPC node
* @param string $name The name of the new account
* @param int $space The space in bytes allocated to the account
* @param PublicKey $payerKey The allocation cost payer
* @param PublicKey $nameOwner The pubkey to be set as owner of the new name account
* @param int|null $lamports The budget to be set for the name account. If not specified, it'll be the minimum for rent exemption
* @param PublicKey|null $nameClass The class of this new name
* @param PublicKey|null $parentName The parent name of the new name. If specified its owner needs to sign
* @return TransactionInstruction
* @throws Exception
*/
public function createNameRegistry(
Connection $connection,
string $name,
int $space,
PublicKey $payerKey,
PublicKey $nameOwner,
int $lamports = null,
PublicKey $nameClass = null,
PublicKey $parentName = null
): TransactionInstruction
{
$hashed_name = $this->getHashedNameSync($name);
$nameAccountKey = $this->getNameAccountKeySync($hashed_name, $nameClass, $parentName);

$balance = $lamports ?: $connection->getMinimumBalanceForRentExemption($space);

$nameParentOwner = null;
if ($parentName) {
$parentAccount = $this->getNameOwner($connection, $parentName);
$nameParentOwner = $parentAccount['registry']->owner;
}

return $this->createInstruction(
new PublicKey($this->config['NAME_PROGRAM_ID']),
SystemProgram::programId(),
$nameAccountKey,
$nameOwner,
$payerKey,
$hashed_name,
new Buffer($balance, Buffer::TYPE_LONG, false),
new Buffer($space, Buffer::TYPE_INT, false),
$nameClass,
$parentName,
$nameParentOwner
);
}



/**
* This function is used to transfer the ownership of a subdomain in the Solana Name Service.
*
* @param Connection $connection The Solana RPC connection object.
* @param string $subdomain The subdomain to transfer. It can be with or without .sol suffix (e.g., 'something.bonfida.sol' or 'something.bonfida').
* @param PublicKey $newOwner The public key of the new owner of the subdomain.
* @param bool $isParentOwnerSigner A flag indicating whether the parent name owner is signing this transfer.
* @param PublicKey|null $owner The public key of the current owner of the subdomain. This is an optional parameter. If not provided, the owner will be resolved automatically. This can be helpful to build transactions when the subdomain does not exist yet.
*
* @return TransactionInstruction
* @throws Exception
*/
public function transferSubdomain(
Connection $connection,
string $subdomain,
PublicKey $newOwner,
bool $isParentOwnerSigner = false,
PublicKey $owner = null
): TransactionInstruction
{
$domainKeySync = $this->getDomainKeySync($subdomain);
$pubkey = $domainKeySync['pubkey'];
$isSub = $domainKeySync['isSub'];
$parent = $domainKeySync['parent'];

if (!$parent || !$isSub) {
throw new SNSError(SNSError::InvalidSubdomain);
}

if (!$owner) {
$registry = $this->retrieve($connection, $pubkey);
$owner = $registry['registry']->owner;
}

$nameParent = null;
$nameParentOwner = null;

if ($isParentOwnerSigner) {
$nameParent = $parent;
$parentAccount = $this->retrieve($connection, $parent);
$nameParentOwner = $parentAccount['registry']->owner;
}

return $this->transferInstruction(
$this->config['NAME_PROGRAM_ID'],
$pubkey,
$newOwner,
$owner,
null,
$nameParent,
$nameParentOwner
);
}

}
23 changes: 23 additions & 0 deletions src/Programs/SNS/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,27 @@ function _deriveSync(string $name, PublicKey $parent = null, PublicKey $classKey
return ['pubkey' => $pubkey, 'hashed' => $hashedDomainName];
}


/**
* This function can be used to get the key of the reverse account
*
* @param string $domain The domain to compute the reverse for
* @param bool|null $isSub Whether the domain is a subdomain or not
* @return PublicKey The public key of the reverse account
* @throws Exception
* @throws SNSError
* @throws InputValidationException
*/
public function getReverseKeySync(string $domain, bool $isSub = null): PublicKey {
$domainKeySync = $this->getDomainKeySync($domain);
$pubkey = $domainKeySync['pubkey'];
$parent = $domainKeySync['parent'];
$hashedReverseLookup = $this->getHashedNameSync($pubkey->toBase58());
return $this->getNameAccountKeySync(
$hashedReverseLookup,
new PublicKey($this->config['REVERSE_LOOKUP_CLASS']),
$isSub ? $parent : null
);
}

}
2 changes: 2 additions & 0 deletions src/Programs/SnsProgram.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Attestto\SolanaPhpSdk\Exceptions\BaseSolanaPhpSdkException;
use Attestto\SolanaPhpSdk\Program;
use Attestto\SolanaPhpSdk\Programs\SNS\Bindings;
use Attestto\SolanaPhpSdk\Programs\SNS\Utils;
use Attestto\SolanaPhpSdk\Programs\SNS\Instructions\Instructions;
use Attestto\SolanaPhpSdk\PublicKey;
Expand All @@ -15,6 +16,7 @@ class SnsProgram extends Program

use Instructions;
use Utils;
use Bindings;

public mixed $config;

Expand Down
3 changes: 1 addition & 2 deletions tests/Feature/ConnectionFeatureTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,8 @@ public function testGetBalance()
public function test_getMinimumBalanceForRentExemption(){
$client = new SolanaRpcClient($this->endpoint);
$connection = new Connection($client);
$result = $connection->getMinimumBalanceForRentExemption([2000]);
$result = $connection->getMinimumBalanceForRentExemption(2000);
$this->assertIsInt($result);

}


Expand Down
77 changes: 77 additions & 0 deletions tests/Unit/Programs/Sns/BindingsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
namespace Attestto\SolanaPhpSdk\Tests\Unit\Programs\SNS;

use Attestto\SolanaPhpSdk\Connection;
use Attestto\SolanaPhpSdk\Exceptions\AccountNotFoundException;
use Attestto\SolanaPhpSdk\Exceptions\InputValidationException;
use Attestto\SolanaPhpSdk\Exceptions\SNSError;
use Attestto\SolanaPhpSdk\Programs\SnsProgram;
use Attestto\SolanaPhpSdk\SolanaRpcClient;
use Attestto\SolanaPhpSdk\Tests\TestCase;
use Attestto\SolanaPhpSdk\PublicKey;
use Attestto\SolanaPhpSdk\TransactionInstruction;
use Attestto\SolanaPhpSdk\Util\Buffer;
use PHPUnit\Framework\MockObject\Exception;

class BindingsTest extends TestCase
{
/**
* @throws InputValidationException
* @throws Exception
*/
#[Test]
public function testCreateSubDomain()
{
// Arrange

$nameOwnerKey = new PublicKey(Buffer::alloc(32));


$client = $this->createMock(SolanaRpcClient::class);
$connection = $this->createMock(Connection::class);
$sns = new SnsProgram($client);
try {
$instruction = $sns->createSubdomain(
$connection,
'subdomain',
$nameOwnerKey,
2000,
$nameOwnerKey
);
} catch (AccountNotFoundException|SNSError $e) {
$this->fail($e->getMessage());
}

// Assert
$this->assertInstanceOf(TransactionInstruction::class, $instruction);
$this->assertEquals(0, $instruction->data->toArray()[0]);

}

#[Test]
public function test_createNameRegistry()
{
// Arrange
$nameOwnerSigner = new PublicKey(Buffer::alloc(32));

$client = $this->createMock(SolanaRpcClient::class);
$connection = $this->createMock(Connection::class);
$sns = new SnsProgram($client);


$instruction = $sns->createNameRegistry(
$connection,
'domain',
2000,
$nameOwnerSigner,
$nameOwnerSigner, // could be someone else
null, null, null
);

// Assert
$this->assertInstanceOf(TransactionInstruction::class, $instruction);
$this->assertEquals(0, $instruction->data->toArray()[0]);
}


}
22 changes: 22 additions & 0 deletions tests/Unit/Programs/Sns/DerivationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

use Attestto\SolanaPhpSdk\Exceptions\InputValidationException;

use Attestto\SolanaPhpSdk\Exceptions\SNSError;
use Attestto\SolanaPhpSdk\Programs\SnsProgram;
use Attestto\SolanaPhpSdk\PublicKey;
use Attestto\SolanaPhpSdk\Tests\TestCase;
use Attestto\SolanaPhpSdk\SolanaRpcClient;
use PHPUnit\Framework\MockObject\Exception;

class DerivationTest extends TestCase
{
Expand Down Expand Up @@ -71,4 +73,24 @@ public function test_getDomainKeySync()
}
}

/**
* @throws SNSError
* @throws Exception
* @throws InputValidationException
*/
#[Test]
public function test_getReverseKeySync()
{
$client = $this->createMock(SolanaRpcClient::class);
$sns = new SnsProgram($client);
foreach ($this->items as $item) {
$result = $sns->getReverseKeySync($item['domain']);
$this->assertInstanceOf(PublicKey::class, $result);
$this->assertTrue(
$result->toBase58() === 'DqgmWxe2PPrfy45Ja3UPyFGwcbRzkRuwXt3NyxjX8krg' ||
$result->toBase58() === 'BrRErziYEA9oBoDyYrdVF9p6Gs1QtdpaZ6AQpaybeZgf'
);
}
}

}

0 comments on commit 1588977

Please sign in to comment.