Skip to content

Instantly share code, notes, and snippets.

@romanzaycev
Created May 15, 2024 03:05
Show Gist options
  • Save romanzaycev/5ffa380efabf152a272231a6bc6e0919 to your computer and use it in GitHub Desktop.
Save romanzaycev/5ffa380efabf152a272231a6bc6e0919 to your computer and use it in GitHub Desktop.
[PHP][TON] On-chain and semi-chain jetton metadata
<?php declare(strict_types=1);
use Brick\Math\BigInteger;
use Http\Client\Common\HttpMethodsClient;
use Http\Client\Common\HttpMethodsClientInterface;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Olifanton\Interop\Address;
use Olifanton\Interop\Boc\BitString;
use Olifanton\Interop\Boc\Cell;
use Olifanton\Interop\Boc\SnakeString;
use Olifanton\Ton\Contracts\Jetton\JettonMetadata;
use Olifanton\Ton\Contracts\Jetton\JettonMinter;
use Olifanton\Ton\JettonOffchainMetadataFetcher;
use Olifanton\Ton\Transport;
use Olifanton\Ton\Transports\Toncenter\ClientOptions;
use Olifanton\Ton\Transports\Toncenter\ToncenterHttpV2Client;
use Olifanton\Ton\Transports\Toncenter\ToncenterTransport;
require __DIR__ . "/vendor/autoload.php";
define("TONCENTER_API_KEY_MAINNET", "");
//
$httpClient = new HttpMethodsClient(
Psr18ClientDiscovery::find(),
Psr17FactoryDiscovery::findRequestFactory(),
Psr17FactoryDiscovery::findStreamFactory(),
);
$toncenter = new ToncenterHttpV2Client(
$httpClient,
new ClientOptions(
"https://toncenter.com/api/v2",
TONCENTER_API_KEY_MAINNET,
),
);
$transport = new ToncenterTransport($toncenter);
$metadataFetcher = new JettonOffchainMetadataFetcher($transport, $httpClient);
class UniversalMetadataFetcher
{
public function __construct(
private readonly Transport $transport,
private readonly JettonOffchainMetadataFetcher $fetcher,
private readonly HttpMethodsClientInterface $httpClient,
) {}
public function getMetadata(Address $jettonMinterAddress): ?JettonMetadata
{
try {
return $this->fetcher->getMetadataFromData(
JettonMinter::fromAddress($this->transport, $jettonMinterAddress)->getJettonData($this->transport),
);
} catch (\Throwable $e) {}
// Try parse offchain metadata
$stack = $this
->transport
->runGetMethod(
$jettonMinterAddress,
"get_jetton_data",
);
$stack->next();
$stack->next();
$stack->next();
$jettonContentCell = $stack->currentCell();
$jccSlice = $jettonContentCell->beginParse();
if ($jccSlice->loadUint(8)->isEqualTo(0x00)) {
$metadataDict = $jccSlice->loadDict(
256,
new \Olifanton\Interop\Boc\DictSerializers(
keySerializer: fn (string $key, int $keySize): array => BitString::empty()
->writeUint(BigInteger::fromBytes(hash("sha256", $key, true), false), $keySize)
->toBitsA(),
valueDeserializer: fn (Cell $v): string => SnakeString::parse($v)->getData(),
)
);
$onchainKeys = [
"name",
"description",
"image",
"symbol",
"image_data",
"decimals",
"uri",
"amount_style",
"render_type",
];
$onchainMetadata = [];
foreach ($onchainKeys as $key) {
try {
$data = $metadataDict->get($key);
$onchainMetadata[$key] = $data;
} catch (\Throwable $e) {}
}
if ($onchainMetadata["uri"]) {
$metadataJsonStr = $this->httpClient->get($onchainMetadata["uri"])->getBody()->getContents();
$metadataJson = json_decode($metadataJsonStr, true, flags: JSON_THROW_ON_ERROR);
$onchainMetadata = array_merge($onchainMetadata, $metadataJson);
}
return JettonMetadata::fromJson($onchainMetadata);
}
return null;
}
}
// Test
$cases = [
"USDt" => new Address("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs"),
"jUSDT" => new Address("EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA"),
"Glint" => new Address("EQCBdxpECfEPH2wUxi1a6QiOkSf-5qDjUWqLCUuKtD-GLINT"),
"Notcoin" => new Address("EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT"),
"durev" => new Address("EQB02DJ0cdUD4iQDRbBv4aYG3htePHBRK1tGeRtCnatescK0"),
];
$f = new UniversalMetadataFetcher($transport, $metadataFetcher, $httpClient);
foreach ($cases as $token => $jettonAddr) {
echo $token, ":", PHP_EOL;
var_dump($f->getMetadata($jettonAddr));
echo "===", PHP_EOL, PHP_EOL;
}
/**
* USDt:
* onchain-metadata.php:127:
* class Olifanton\Ton\Contracts\Jetton\JettonMetadata#297 (6) {
* public readonly string $name =>
* string(10) "Tether USD"
* public readonly string $description =>
* string(27) "Tether Token for Tether USD"
* public readonly string $symbol =>
* string(6) "USD₮"
* public readonly ?string $imageData =>
* NULL
* public readonly int $decimals =>
* int(6)
* public readonly ?string $image =>
* string(39) "https://tether.to/images/logoCircle.png"
* }
* ===
*
* jUSDT:
* onchain-metadata.php:127:
* class Olifanton\Ton\Contracts\Jetton\JettonMetadata#773 (6) {
* public readonly string $name =>
* string(13) "Bridged jUSDT"
* public readonly string $description =>
* string(127) "Tether USD transferred from Ethereum via bridge.ton.org. Token address in Ethereum: 0xdac17f958d2ee523a2206206994597c13d831ec7."
* public readonly string $symbol =>
* string(5) "jUSDT"
* public readonly ?string $imageData =>
* NULL
* public readonly int $decimals =>
* int(6)
* public readonly ?string $image =>
* string(77) "https://bridge.ton.org/token/1/0xdac17f958d2ee523a2206206994597c13d831ec7.png"
* }
* ===
*
* Glint:
* onchain-metadata.php:127:
* class Olifanton\Ton\Contracts\Jetton\JettonMetadata#1198 (6) {
* public readonly string $name =>
* string(10) "Glint Coin"
* public readonly string $description =>
* string(150) "Glint Coin is a utility token from TON Diamonds, an NFT Marketplace on TON Blockchain. Visit ton.diamonds to learn more about all utilities of $GLINT."
* public readonly string $symbol =>
* string(5) "GLINT"
* public readonly ?string $imageData =>
* NULL
* public readonly int $decimals =>
* int(9)
* public readonly ?string $image =>
* string(39) "https://nft.ton.diamonds/glint_meta.png"
* }
* ===
*
* Notcoin:
* onchain-metadata.php:127:
* class Olifanton\Ton\Contracts\Jetton\JettonMetadata#384 (6) {
* public readonly string $name =>
* string(7) "Notcoin"
* public readonly string $description =>
* string(16) "Probably nothing"
* public readonly string $symbol =>
* string(3) "NOT"
* public readonly ?string $imageData =>
* NULL
* public readonly int $decimals =>
* int(9)
* public readonly ?string $image =>
* string(50) "https://cdn.joincommunity.xyz/clicker/not_logo.png"
* }
* ===
*
* durev:
* onchain-metadata.php:127:
* class Olifanton\Ton\Contracts\Jetton\JettonMetadata#571 (6) {
* public readonly string $name =>
* string(11) "povel durev"
* public readonly string $description =>
* string(36) "mek bellions TONs of memes durev.xyz"
* public readonly string $symbol =>
* string(5) "durev"
* public readonly ?string $imageData =>
* NULL
* public readonly int $decimals =>
* int(9)
* public readonly ?string $image =>
* string(34) "https://durev.xyz/images/256-b.png"
* }
* ===
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment