Back to writing
Article Jul 14, 2025 8m read

Lanjutan Ngoprek SPL Token: Kenalan Sama PDA dan Metadata

Pernah kepikiran gimana caranya Token bisa punya gambar? Atau gimana sebuah program bisa menyimpan saldo dengan aman? Jawabannya ada di dua konsep kunci yaitu PDA dan Metadata. Yuk, kita bedah bareng cara kerjanya, sampai rahasia keamanannya.

Hutomo S. Kartiko
Hutomo S. Kartiko
Fullstack & Web3 Engineer
Lanjutan Ngoprek SPL Token: Kenalan Sama PDA dan Metadata

Pada sesi Ngoprek SPL Token di Solana: Bikin Token Sendiri kita sudah belajar bagaimana caranya membuat SPL Token di Solana. Pada dasarnya SPL Token adalah sebuah program yang bisa melakukan aksi seperti mint, transfer sampai burning token.

Selanjutnya pada sesi kali ini kita akan melanjutkan proses pembuatan SPL Token yang tidak kalah penting, yaitu menambahkan metadata berupa gambar, nama, dan detail lainnya. Tapi sebelum itu, kita akan berkenalan terlebih dahulu dengan PDA atau Program Derived Address.

Mengenal PDA - Si Akun Ajaib Tanpa Kunci

Sebelum kita bahas PDA itu apa, jawaban apa yang kamu pikirkan setelah mendapatkan pertanyaan ini: "Gimana caranya sebuah program bisa punya 'wallet' atau akun untuk menyimpan aset?".

Mungkin kamu akan kepikiran, "Ya tinggal buatkan saja wallet baru, lalu simpan private key-nya di dalam kode program."

Hmmm... Menyimpan private key di dalam kode yang bisa dilihat semua orang sama saja dengan menaruh kunci brankas di depan pintunya. Siapapun bisa mengetahui dan mengambil isinya.

Lalu, bagaimana solusinya? Di sinilah PDA bekerja.

Analogi Sederhana: Anggaplah PDA itu seperti loker digital di sebuah stasiun. Loker ini tidak punya lubang kunci fisik. Nomor lokernya (address PDA) dibuat secara khusus berdasarkan identitasmu (seeds) dan dikelola oleh sistem loker pusat (program). Kamu tidak memegang kuncinya, tapi loker ini terikat denganmu dan hanya bisa dibuka oleh sistem jika aturan terpenuhi.

Secara teknis, PDA adalah sebuah alamat yang punya dua sifat ajaib:

  1. Derived (Diturunkan): Alamatnya tidak dibuat secara acak. Ia "dihitung" atau diturunkan dari beberapa bahan, yaitu Program ID dan data unik yang kita tentukan (seeds).
  2. Tidak Punya Private Key: Ini yang paling penting. Address PDA secara matematis didesain agar tidak mungkin memiliki private key. Ini membuatnya sangat aman karena tidak ada satu individu pun yang bisa mengontrolnya secara langsung. Hanya program yang menjadi "induknya" yang bisa memberinya perintah.

Tanpa sadar, kalau kamu sudah pernah menerima atau mengirim SPL Token, kamu sebenarnya sudah berinteraksi dengan PDA.

Ingat Associated Token Account (ATA)? Akun spesifik di wallet-mu untuk menyimpan token tertentu.

ATA adalah contoh dari sebuah PDA.

Alamat ATA-mu diturunkan dari seeds berupa:

  • Address wallet utamamu.
  • Address mint tokennya.

Kombinasi ini, ditambah dengan Program ID "Associated Token Program", menghasilkan sebuah address unik yang didedikasikan untuk menyimpan token tersebut.

Bedah Metadata - Memberi "Jiwa" pada Token

Kita sudah punya "brankas" canggih bernama PDA. Sekarang, mari kita beri "jiwa" pada token yang akan kita buat. Tanpa metadata, SPL Token hanyalah sebaris alamat acak yang tidak berarti apa-apa bagi pengguna. Metadata inilah yang memberinya nama, simbol, bahkan gambar.

Anggap saja metadata adalah KTP (Kartu Tanda Penduduk) untuk token kita.

Di Solana, standar yang banyak digunakan untuk ini adalah Metaplex Token Metadata Standard. Awalnya dirancang untuk NFT, tapi sering digunakan untuk membuat token biasa (fungible).

Struktur metadata ini terbagi menjadi dua bagian utama, yaitu:

1. Data On-Chain (Di dalam Solana)

Ini adalah data inti yang disimpan langsung di dalam sebuah akun di blockchain Solana. Disimpan on-chain agar bisa diakses dengan cepat dan efisien oleh program lain. Isinya antara lain:

  • name: Nama token (contoh "Tomoskoj Coin")
  • symbol: Simbol token (contoh: "TOMS")
  • uri: Link ke data yang lebih lengkap.
  • seller_fee_basis_points: Untuk royalti (lebih sering dipakai pada NFT)

Dan tebak apa? Akun yang menyimpan data on-chain ini adalah sebuah PDA juga! Address-nya diturunkan dari mint address tokennya. Ini menciptakan hubungan yang permanen dan terverifikasi antara sebuah token dengan KTP digitalnya.

2. Data Off-Chain (Di luar Solana)

Ini adalah data yang lebih lengkap dan deskriptif yang alamatnya disimpan oleh field uri tadi. Biasanya ini adalah sebuah file JSON yang disimpan di layanan penyimpanan permanen seperti Arweave dan IPFS. Kenapa dipisah? Supaya hemat biaya, karena semakin besar data yang kita simpan di blockhain, akan semakin besar juga biaya yang butuhkan.

Contoh isi file metadata.json:

text
{
  "name": "Tomoskoj Coin",
  "symbol": "TOMS",
  "description": "Token untuk para pembaca blog tomoskoj.my.id",
  "image": "https://gateway.irys.xyz/49q5NRAjn8EZKZ7JVrUFmYwd9qBFJiyBY3srLu515UPz"
}

Nantinya, wallet seperti Phantom atau Solflare akan membaca data on-chain, menemukan uri, lalu mengambil data dari file JSON ini untuk menampilkan logo dan deskripsi tokenmu.

Implementasi Metadata

Sekarang gimana cara pasangnya? Proses ini pada dasarnya adalah "menempelkan" KTP digital ke token yang sudah kita buat. Pada bagian ini, kita akan memasang metadata ke SPL Token yang sudah ada menggunakan UMI (Unified Metaplex Interface), sebuah library modern dari Metaplex yang mempermudah proses ini.

Sebelum melanjutkan, pastikan kamu sudah memiliki address Mint Token atau address Token yang akan kita pasangi metadata. Kalau belum, anda bisa membuatnya dengan mengikuti tutorial pada artikel ini.

File spl_metadata.ts

text
import wallet from "./wallet/dev-wallet.json";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import {
  createMetadataAccountV3,
  CreateMetadataAccountV3InstructionAccounts,
  CreateMetadataAccountV3InstructionArgs,
  DataV2Args,
} from "@metaplex-foundation/mpl-token-metadata";
import {
  createSignerFromKeypair,
  signerIdentity,
  publicKey,
  createGenericFile,
} from "@metaplex-foundation/umi";
import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
import { printTxHash } from "../tools/print_tx_hash";
import { irysUploader } from "@metaplex-foundation/umi-uploader-irys";
import fs from "fs";

// Define our Mint address
const mint = publicKey("EHHYEEf2SYTRw3HFttWxhrjsQ32uG228EiLQ4rznEmmC");

// Create a UMI connection
const umi = createUmi("https://api.devnet.solana.com");
const keypair = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(wallet));
const signer = createSignerFromKeypair(umi, keypair);
umi.use(signerIdentity(createSignerFromKeypair(umi, keypair)));
umi.use(irysUploader());

(async () => {
  try {
    let accounts: CreateMetadataAccountV3InstructionAccounts = {
      mint: mint,
      mintAuthority: signer,
    };

    // metadata
    const imageBuffer = fs.readFileSync("./assets/toms-logo.png");
    const imageFile = createGenericFile(imageBuffer, "image.png");
    const [imageUri] = await umi.uploader.upload([imageFile]);
    console.log("Image URI:", imageUri);
    const metadata = {
      name: "Tomoskoj Coin",
      symbol: "TOMS",
      description: "Token untuk para pembaca blog tomoskoj.my.id",
      image: imageUri,
      attributes: [
        {
            trait_type: "Creator",
            value: "Tomo"
        },
        {
            trait_type: "Type", 
            value: "Custom Token"
        }
      ],
      properties: {
          files: [
              {
                  uri: imageUri,
                  type: "image/png"
              }
          ],
          category: "image"
      }
    };
    const metadataFile = createGenericFile(JSON.stringify(metadata), "metadata.json", {
      contentType: "application/json"
    });
    const [metadataUri] = await umi.uploader.upload([metadataFile]);
    console.log("Metadata Uploaded :", metadataUri);

    let data: DataV2Args = {
      name: "Tomoskoj Coin",
      symbol: "TOMS",
      uri: metadataUri,
      sellerFeeBasisPoints: 500,
      creators: null,
      collection: null,
      uses: null,
    };

    let args: CreateMetadataAccountV3InstructionArgs = {
      data,
      collectionDetails: null,
      isMutable: true,
    };

    let tx = createMetadataAccountV3(umi, {
      ...accounts,
      ...args,
    });

    let result = await tx.sendAndConfirm(umi);
    printTxHash(bs58.encode(result.signature));
  } catch (e) {
    console.error(`Oops, something went wrong: ${e}`);
  }
})();

Berikut penjelasan untuk kode di atas:

  • createUmi(...): Membuat koneksi ke jaringan Solana (dalam contoh ini Devnet).
  • createKeypairFromSecretKey(...): Membaca file wallet JSON kita dan menyimpannya sebagai keypair.
  • signerIdentity(signer): Kita memberitahu UMI, "Untuk semua transaksi yang akan kita buat, inilah identitas saya dan inilah cara saya menandatangani transaksi".
  • umi.use(irysUploader()): Baris ini mendaftarkan plugin uploader. Kita memberitahu UMI, "Jika ada perintah untuk mengunggah file, gunakan layanan Irys (Arweave) ya."
  • fs.readFileSync(...): Kita membaca file gambar di lokal device kita.
  • umi.uploader.upload(...): Pada baris ini kita mengunggah file ke Arweave. Proses yang sama kita ulangi untuk file metadata JSON, setelah memasukkan link gambar di dalamnya.
  • accounts: Di sini kita mendefinisikan akun-akun yang terlibat. Perhatikan mintAuthority: signer. Inilah momen validasi otoritas yang kita bahas, dengan menyertakan signer di sini, kita membuktikan kepada program Metaplex bahwa kita adalah pemilik sah yang berhak mengubah metada token ini.
  • data: Ini adalah "isi KTP" dari token kita. Kita bisa mengisi name, symbol, dan yang terpenting uri yang mengarah ke file metada.json yang sudah kita unggah ke Arweave.
  • args: Argumen tambahan seperti isMutable: true berarti metadata ini masih bisa diubah di kemudian hari.
  • createMetadataAcountV3(...): Fungsi ini tidak langsung mengirim transaksi, melainkan"membangun" atau mempersiapkan instruksi yang dibutuhkan.
  • .sendAndConfirm(umi): Baris inilah yang benar-benar mengirim transaksi ke jaringan Solana dan menunggu konfirmasi.

Keamanan PDA

Mungkin kamu kepikiran, "Kalau alamat PDA bisa dihitung dan ditebak dari seeds, berarti siapa saja yang tahu resepnya bisa menguras isinya, dong?"

Jawaban singkatnya Tidak Bisa, dan inilah keunggulan dari PDA.

Umumnya sebuah keamanan PDA dilindungi oleh dua lapis pertahanan yaitu:

1. Validasi di Level Aplikasi (Logika Kodemu)

Ini adalah laposan keamanan yang kamu, sebagai developer, tulis sendiri di dalam programmu. Misalnya, pada fungsi untuk menarik dana dari sebuah PDA, kamu akan menambahkan pengecekan:

require!(signer.key() == beneficiary.key(), "Anda tidak punya akses!");

  • Artinya: "Hanya izinkan transaksi ini berjalan jika orang (signer) yang memanggil fungsi ini adalah penerima (beneficiary) yang sah."
  • Analogi: Anggap ini seperti teller bank yang meminta KTP-mu. Meskipun teller punya akses ke sistem brankas, dia tidak akan memproses penarikan uang sebelum memverifikasi identitasmu.

Lapisan ini penting untuk memastikan pengguna yang tepat yang memanggil fungsi.

2. Validasi di Level Protokol (Keajaiban Solana Runtime)

Ini adalah lapisan keamanan fundamental yang bekerja otomatis di level protokol Solana. Lapisan ini menjawab pertanyaan: "Program mana yang berhak memerintah PDA ini?"

Ingat, sebuah PDA terikat secara kriptografis dengan program_id yang digunakan untuk membuatnya.

  • Artinya: Hanya program dengan program_id itulah yang bisa memberi perintah (seperti "kirim uang") kepada PDA tersebut.
  • Analogi: Ini seperti alamat pengirim di surat pos. Kamu bisa saja menulis "Dikirim dari Kantor Presiden" di dalam isi suratmu (seeds), tapi petugas pos (Solana Runtime) akan selalu tahu dari mana surat itu sebenarnya dikirim. program_id tidak bisa dimanipulasi.

Ketika program lain mencoba memerintah PDA-mu, Solana Runtime akan menghitung ulang alamat PDA menggunakan program_id si program penipu, dan hasilnya pasti tidak akan cocok. Maka transaksi pun akan gagal.

Key Takeaways (TL;DR)

  • PDA adalah akun spesial yang bisa melakukan transaksi tanpa private key.
  • Metadata merupakan program terpisah yang menjadi "jiwa" dari sebuah token atau NFT.
  • Untuk bisa memasang dan mengubah metadata sebuah token, kamu harus menjadi Update Authority dari token mint tersebut.
  • Associated Token Account (ATA) adalah contoh nyata dari PDA.
  • PDA menggunakan Solana Runtime untuk memverifikasi program yang berhak memberi perintah.