SINAI STANDARD

Pattern: Full Compliance (Router)

Combine KYC allowlist, transfer tax, hold period, and max balance on a single token using the Router Hook

Overview

When a token needs multiple compliance features simultaneously, the Router Hook chains them together. The SDK automatically selects the Router when 2+ hooks are specified.

Implementation

import {
  AksumKit,
  AllowlistManager,
  TaxManager,
  HoldPeriodManager,
  MaxBalanceManager,
  RouterManager,
} from "@sinai-standard/sdk";
import { PROGRAM_IDS } from "@sinai-standard/types";
 
const token = await kit.createToken({
  name: "Dubai REIT Token",
  symbol: "DREIT",
  decimals: 6,
  supply: 100_000_000,
  hooks: {
    allowlist: { mode: "allowlist" },
    tax: { bps: 150, maxBps: 500, vault: treasuryPubkey },
    holdPeriod: { seconds: 180 * 24 * 60 * 60 }, // 6 months
  },
});
 
// The SDK automatically selects Router Hook for 2+ hooks.
 
// Initialize each sub-hook
const allowlist = new AllowlistManager(allowlistProgram, token.mint);
await allowlist.initialize(issuer.publicKey, "allowlist");
await allowlist.initializeExtraAccountMetas(issuer.publicKey);
 
const tax = new TaxManager(taxProgram, token.mint);
await tax.initialize(issuer.publicKey, 150, 500, treasuryPubkey);
await tax.initializeExtraAccountMetas(issuer.publicKey);
 
const hold = new HoldPeriodManager(holdProgram, token.mint);
await hold.initialize(issuer.publicKey, 180 * 24 * 60 * 60);
await hold.initializeExtraAccountMetas(issuer.publicKey);
 
const maxBal = new MaxBalanceManager(maxBalanceProgram, token.mint);
await maxBal.initialize(issuer.publicKey, 5_000_000 * 10 ** 6);
await maxBal.initializeExtraAccountMetas(issuer.publicKey);
 
// Initialize the router
const router = new RouterManager(routerProgram, token.mint);
await router.initialize(
  issuer.publicKey,
  PROGRAM_IDS.allowlistHook,
  PROGRAM_IDS.taxHook,
  PROGRAM_IDS.holdHook,
  PROGRAM_IDS.maxBalanceHook
);
await router.initializeExtraAccountMetas(issuer.publicKey);
 
// Add issuer + investors to allowlist
await allowlist.addWallets(issuer.publicKey, [
  issuer.publicKey,
  investorA.publicKey,
  investorB.publicKey,
]);
 
// Record acquisitions for hold period
await hold.recordAcquisition(
  issuer.publicKey,
  issuer.publicKey,
  investorA.publicKey
);

Transfer Flow

When a transfer is executed, the Router enforces all sub-hooks in order:

  1. Allowlist — checks that both source and destination are on the approved list
  2. Tax — logs the transfer for later tax collection via Permanent Delegate
  3. Hold Period — verifies the source wallet's lock-up has expired
  4. Max Balance — ensures destination wallet stays within the per-wallet cap

If any check fails, the entire transaction reverts.

On this page