# Tokenizing Real-World Assets

This guide shows how to create a **non-fungible token (NFT)** that represents a real-world asset — like a document, a product, or a right of ownership — on the Keeta Network.

{% stepper %}
{% step %}

### Prepare your accounts

You'll need two accounts:

* A **signer account** to send the transaction
* An **authority account** to sign the metadata (can be the same in testing)

```typescript
const signer = KeetaNet.lib.Account.fromSeed(DEMO_SEED, 0);
const authority = KeetaNet.lib.Account.fromSeed(DEMO_SEED, 1);
```

{% endstep %}

{% step %}

### Create metadata for your real-world asset

This includes:

* A unique ID for the asset (e.g. serial number, URL, or database ref)
* A digital signature proving the authority account approved it

```typescript
const assetIdBuffer = Buffer.from(ASSET_ID, 'utf-8');
const signatureBuffer = await authority.sign(assetIdBuffer);

const metadata = {
  asset_id: ASSET_ID,
  authority: authority.publicKeyString.get(),
  signature: signatureBuffer.toString('base64')
};

const metadataBase64 = Buffer.from(JSON.stringify(metadata)).toString('base64');
```

{% endstep %}

{% step %}

### Start a transaction builder

This prepares your transaction to include all operations in one publishable bundle.

```typescript
const builder = client.initBuilder();
builder.updateAccounts({
  signer,
  account: signer
});
```

{% endstep %}

{% step %}

### Create the token account

This is where the NFT will live. It’s a new account of type `TOKEN`.

```typescript
const token = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN);
await client.computeBuilderBlocks(builder); // seal block so token exists
```

{% endstep %}

{% step %}

### Make it non-fungible

Set the token’s supply to `1`. That means only one account can own this NFT at a time.

```typescript
builder.modifyTokenSupply(1n, { account: token.account });
```

{% endstep %}

{% step %}

### Attach metadata and permissions

Now link your signed metadata to the token, and set permissions so it can be held by any user.

```typescript
builder.setInfo({
  name: 'RWA-DEMO',
  description: 'Token representing a real-world asset',
  metadata: metadataBase64,
  defaultPermission: new KeetaNet.lib.Permissions(['ACCESS'], [])
}, {
  account: token.account
});
```

{% endstep %}

{% step %}

### Publish the transaction

Finalize and submit your transaction to the Keeta Network.

```typescript
await client.computeBuilderBlocks(builder);
await client.publishBuilder(builder);
```

{% endstep %}
{% endstepper %}

Your NFT now lives on-chain and holds signed, verifiable metadata that links it to a real-world asset.

To see its address:

```typescript
console.log('Token address:', token.account.publicKeyString.get());
```

## Full Code Example

```typescript
const KeetaNet = require('@keetanetwork/keetanet-client');

const DEMO_SEED = 'D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0D3M0';
const ASSET_ID = 'asset://unique-asset-id-001';

async function main() {
  // 1️⃣ Create two accounts from the seed:
  // - signer: sends the transaction
  // - authority: signs the metadata (can be same as signer for demo)
  const signer = KeetaNet.lib.Account.fromSeed(DEMO_SEED, 0);
  const authority = KeetaNet.lib.Account.fromSeed(DEMO_SEED, 1);

  // 2️⃣ Connect to the Keeta test network using the signer account
  const client = KeetaNet.UserClient.fromNetwork('test', signer);

  // 3️⃣ Create and sign the asset metadata
  const assetIdBuffer = Buffer.from(ASSET_ID, 'utf-8');
  const signatureBuffer = await authority.sign(assetIdBuffer);

  const metadata = {
    asset_id: ASSET_ID,
    authority: authority.publicKeyString.get(),
    signature: signatureBuffer.toString('base64')
  };

  const metadataBase64 = Buffer.from(JSON.stringify(metadata)).toString('base64');

  // 4️⃣ Start building a transaction
  const builder = client.initBuilder();
  builder.updateAccounts({
    signer,
    account: signer
  });

  // 5️⃣ Generate a new token account (this will be your NFT)
  const token = builder.generateIdentifier(KeetaNet.lib.Account.AccountKeyAlgorithm.TOKEN);

  // 6️⃣ Compute a block to seal the token creation (required before you can modify it)
  await client.computeBuilderBlocks(builder);

  // 7️⃣ Set the token supply to 1 — making it a non-fungible token
  builder.modifyTokenSupply(1n, {
    account: token.account
  });

  // 8️⃣ Attach the metadata and set default permissions (allow others to hold it)
  builder.setInfo({
    name: 'RWA-DEMO',
    description: 'Non-Fungible Token representing a real-world asset',
    metadata: metadataBase64,
    defaultPermission: new KeetaNet.lib.Permissions(['ACCESS'], [])
  }, {
    account: token.account
  });

  // 9️⃣ Compute and publish all blocks to the network
  await client.computeBuilderBlocks(builder);
  await client.publishBuilder(builder);

  // 🔚 Done — log the token's public address
  console.log('✅ RWA Token created at:', token.account.publicKeyString.get());
}

main().catch(console.error);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeta.com/guides/tokenizing-real-world-assets.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
