npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@kaio-xyz/solana-edge-gateway

v1.0.1

Published

This is the main entry point to the KAIO's Gateway protocol on Solana. The majority of token and control operations originate from this program.

Readme

Solana Gateway Bridge

This is the main entry point to the KAIO's Gateway protocol on Solana. The majority of token and control operations originate from this program.

Limitations

Solana handles state updates different from other chains due to its account model. Therefore, some of the instructions i.e. sync_addresses, sync_credential, burn_and_bridge and mint require specific handling.

sync_addresses

EVM-based spec uses encoding that combines address updates across all chains. Since the limitations described in investor registry README, solana-version requires the encoding struct to submit address update per chain id in separate instructions calls.

// EVM version
struct InvestorAddresses {
    bytes32 investorId;          // Unique investor ID
    // A list of concatenated bytes describing a change in multi-chain address
    // Format: bytes1 action ++ uint16 chainId ++ bytes accountAddress
    // for `action`: 0b0 -> Add, 0b1 -> Remove
    // Big-endiannes is used for encoding
    bytes[] addressChanges;
 }

in contrast to

// Solana version
struct InvestorAddresses {
    // Unique investor ID
    bytes32 investorId;
    // Unique chain ID
    uint16 chainId;
    // Address size for that chain
    uint8 addressSize;
    // A list of concatenated bytes describing a change in multi-chain address
    // Format `addressChanges`` := bytes1 action ++ bytes accountAddress
    // where `action`: 0b0 -> Add, 0b1 -> Remove
    // Big-endiannes is used for encoding
    bytes[] addressChanges;
}

Secondly, similar to native token program. The investor registry requires manual creation of a PDA to store address per investor per chain before actually putting the data in.

sync_credential

Similar to addresses, credentials program requires the PDA creation directly through the program before storing data there.

burn_and_bridge & mint

Similar to instructions listed above, it requires creation of token account PDA before a call submission. Furthermore, it requires address and credential account to be provided in the account list. Therefore, these accounts need to exist before submitting an instruction call.

Instructions

initialize

Initializes the main state PDA (state) of the program. Can only called once. Only admin can this instruction.

Arguments

  • supported_chains: Vec<u16>: List of chain selectors that are initially supported

Accounts

  • authority (Signer, mutable): Authority with admin role

  • state (Account, mutable): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • admin_role (Account): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • roles_program (Program): Role registry program

  • system_program (Program): System program

Errors

  • Unauthorized: Authority does not have admin role

generate_uid

Generates a unique identifier (UID) based on:

  • Counter index (u128) stored in the Gateway's state
  • Chain selectors and investor ID provided as arguments

This is a view-only function that calculates the UID without persisting any state changes. The actual UID account creation and counter increment happens in burn_and_bridge.

The program emits event:

pub struct GeneratedUid {
    uid: [u8; 32],
}

Arguments

  • source_chain_selector: u16: Source chain identifier
  • dest_chain_selector: u16: Destination chain identifier
  • investor_id: [u8; 32]: Investor identifier

Accounts

  • state (Account): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account

Errors

  • IndexOverflow: Counter has reached maximum value (u128::MAX)

mint

Called by the bridge with TokenTransfer struct. This instruction handles the incoming bridge transfer.

pub struct TokenTransfer {
    pub chain_selector: u16,
    pub instrument_id: [u8; 32],
    pub investor_id: [u8; 32],
    pub receiver: Vec<u8>,
    pub amount: u64,
}

Only Bridge or Admin role holder can call this instruction.

Emit the following event:

pub struct TokenMintedEvent {
    pub uid: [u8; 32],
    pub transfer_details: TokenTransfer,
}

Arguments

  • uid: [u8; 32]: Unique identifier for the operation
  • transfer_details: TokenTransfer: Transfer details including amounts and addresses

Accounts

  • authority (Signer, mutable): Authority with admin or bridge role

  • state (Account): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • canonical_message_id (Account, mutable): PDA with seeds:

    ["edge_gateway", "uid", uid]
    • "edge_gateway": String constant for gateway state account
    • "uid": String constant for UID accounts
    • uid: 32-byte unique identifier from arguments
    • This account is created with init_if_needed to handle both new transfers and reverts
  • registry (Account): PDA owned by investor-registry program with seeds:

    ["registry"]
    • "registry": String constant for registry state account
  • access_control (Account): PDA owned by security-token-hook program with seeds:

    ["access_control", mint.key()]
    • "access_control": String constant for access control account
    • mint.key(): Public key of the mint account (32 bytes)
  • instrument_mint (Account): PDA with seeds:

    ["instrument_mint", transfer_details.instrument_id]
    • "instrument_mint": String constant for instrument mint mapping
    • transfer_details.instrument_id: Instrument identifier from transfer details (32 bytes)
  • investor_id (Account): PDA owned by investor-id program with seeds:

    ["id", token_account.owner]
    • "id": String constant for investor ID accounts
    • token_account.owner: Public key of the token account owner (32 bytes)
  • credentials (Account): PDA owned by credentials program with seeds:

    ["attestation", investor_id.get_id()]
    • "attestation": String constant for attestation accounts
    • investor_id.get_id(): 32-byte investor ID from investor_id account
  • addresses (Account): PDA owned by investor-registry program with seeds:

    ["investor_addresses", chain_selector.to_be_bytes(), investor_id.get_id()]
    • "investor_addresses": String constant for investor address accounts
    • chain_selector.to_be_bytes(): 2-byte chain selector in big-endian format
    • investor_id.get_id(): 32-byte investor ID from investor_id account
  • Multiple role verification accounts with similar PDA patterns as other instructions

  • Token program accounts (mint, token_account, associated_token_program, token_program)

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have admin or bridge role
  • UidUsed: The provided UID has already been used for another operation
  • ForeignChain: Transfer details specify a different chain than the native chain
  • NonInvestor: Target account does not belong to an investor
  • DifferentInvestor: Investor ID in transfer details doesn't match token account owner
  • InvalidReceiver: Receiver address doesn't match token account owner
  • InvalidMint: Provided mint doesn't match expected instrument mint

burn_and_bridge

Receives Solana serialised token transfer details. This instructions is called and signed by the investor and indicates the request to bridge assets to a different chain.

The UID for this operation is dynamically generated based on the gateway's counter, chain selectors, and investor ID rather than being passed as an argument.

Emit the following event:

pub struct TokenBurnedEvent {
    pub uid: [u8; 32],
    pub transfer_details: TokenTransfer,
}

Arguments

  • transfer_details: TokenTransfer: Transfer details including amounts and addresses

Accounts

  • authority (Signer, mutable): Authority with gateway role (investor)

  • state (Account, mutable): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
    • Counter is incremented after successful operation
  • canonical_message_id (Account, mutable): PDA with seeds:

    ["edge_gateway", "uid", generated_uid]
    • "edge_gateway": String constant for gateway state account
    • "uid": String constant for UID accounts
    • generated_uid: Dynamically generated 32-byte unique identifier
    • This account is created with init as it's the first time writing to this UID
  • registry (Account): PDA owned by investor-registry program with seeds:

    ["registry"]
    • "registry": String constant for registry state account
  • access_control (Account): PDA owned by security-token-hook program with seeds:

    ["access_control", mint.key()]
    • "access_control": String constant for access control account
    • mint.key(): Public key of the mint account (32 bytes)
  • instrument_mint (Account): PDA with seeds:

    ["instrument_mint", transfer_details.instrument_id]
    • "instrument_mint": String constant for instrument mint mapping
    • transfer_details.instrument_id: Instrument identifier from transfer details (32 bytes)
  • investor_id (Account): PDA owned by investor-id program with seeds:

    ["id", token_account.owner]
    • "id": String constant for investor ID accounts
    • token_account.owner: Public key of the token account owner (32 bytes)
  • credentials (Account): PDA owned by credentials program with seeds:

    ["attestation", investor_id.get_id()]
    • "attestation": String constant for attestation accounts
    • investor_id.get_id(): 32-byte investor ID from investor_id account
  • addresses (Account): PDA owned by investor-registry program with seeds:

    ["investor_addresses", transfer_details.chain_selector.to_be_bytes(), investor_id.get_id()]
    • "investor_addresses": String constant for investor address accounts
    • transfer_details.chain_selector.to_be_bytes(): 2-byte chain selector from transfer details in big-endian format
    • investor_id.get_id(): 32-byte investor ID from investor_id account
  • admin_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • bridge_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.bridge]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.bridge: 32-byte role identifier for bridge role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • mint (Account): The mint account for the token

  • token_account (Account, mutable): Token account to burn from

  • associated_token_program (Program): Associated Token Program

  • token_program (Program): Token Program

  • roles_program (Program): Role registry program

  • investor_id_program (Program): Investor ID program

  • investor_registry_program (Program): Investor registry program

  • credentials_program (Program): Credentials program

  • security_token_hook_program (Program): Security token hook program

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have gateway role
  • UidUsed: The provided UID has already been used for another operation
  • NativeChain: Transfer details specify the native chain (should be foreign)
  • UnsupportedChain: Target chain is not in the supported chains list
  • NonInvestor: Source account does not belong to an investor
  • InvalidMint: Provided mint doesn't match expected instrument mint
  • InvalidReceiver: Receiver address matches token account owner (should be different for bridging)
  • DifferentInvestor: Target address doesn't belong to the same investor

set_chain_support

Sets the chain support. Only Admin role holder can call this function.

Arguments

  • chain_selector: u16: Chain identifier to modify support for
  • is_supported: bool: Whether to enable or disable support

Accounts

  • authority (Signer, mutable): Authority with admin or bridge role

  • state (Account, mutable): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • admin_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • roles_program (Program): Role registry program

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have admin or bridge role
  • ChainNotFound: Attempting to remove support for a chain that is not currently supported

sync_credential

Receives a Attestation struct to update the on-chain copy. Calls the credentials program via CPI.

Only Admin or Bridge can call this instruction.

Emits event:

pub struct CredentialSyncedEvent {
    pub attestation_id: [u8; 32],
    pub investor_id: [u8; 32],
}

Arguments

  • attestation: Attestation: Credential attestation to synchronize

Accounts

  • authority (Signer, mutable): Authority with admin, bridge, or gateway role

  • state (Account): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • canonical_message_id (Account, mutable): PDA with seeds:

    ["edge_gateway", "uid", attestation.id]
    • "edge_gateway": String constant for gateway state account
    • "uid": String constant for UID accounts
    • attestation.id: 32-byte attestation identifier from attestation data
    • This account is created with init as credentials should only be synced once
  • credential (Account, mutable): PDA owned by credentials program with seeds:

    ["attestation", attestation.data.investor_id]
    • "attestation": String constant for attestation accounts
    • attestation.data.investor_id: 32-byte investor identifier from attestation data
    • Account ownership is validated via seeds::program = credentials_program.key()
  • admin_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • bridge_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.bridge]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.bridge: 32-byte role identifier for bridge role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • roles_program (Program): Role registry program

  • credentials_program (Program): Credentials program

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have admin or bridge role
  • UidUsed: The provided UID has already been used for another operation

sync_addresses

Applies the changes to the address list for a specific foreign chain. It accepts or removes specific based on AddressChanges. The foreign addresses are used in validation checks for the bridge operations. Only Admin and Bridge role holder can call this instruction.

pub struct AddressChanges {
    pub investor_id: Bytes32,
    pub chain_selector: u16,
    pub changes: Vec<Action>,
}

pub enum Action {
    Add(Vec<u8>),
    Remove(Vec<u8>),
}

Arguments

  • uid: [u8; 32]: Unique identifier for the operation
  • address_changes: AddressChanges: Address changes to synchronize

Accounts

  • authority (Signer, mutable): Authority with admin, bridge, or gateway role

  • state (Account): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • canonical_message_id (Account, mutable): PDA with seeds:

    ["edge_gateway", "uid", uid]
    • "edge_gateway": String constant for gateway state account
    • "uid": String constant for UID accounts
    • uid: 32-byte unique identifier from arguments
    • This account is created with init as address syncs should only happen once
  • registry (Account): PDA owned by investor-registry program with seeds:

    ["registry"]
    • "registry": String constant for registry state account
  • addresses (Account, mutable): PDA owned by investor-registry program with seeds:

    ["investor_addresses", address_changes.chain_selector.to_be_bytes(), address_changes.investor_id]
    • "investor_addresses": String constant for investor address accounts
    • address_changes.chain_selector.to_be_bytes(): 2-byte chain selector from address changes in big-endian format
    • address_changes.investor_id: 32-byte investor identifier from address changes
    • Account ownership is validated via seeds::program = investor_registry_program.key()
  • admin_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • bridge_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.bridge]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.bridge: 32-byte role identifier for bridge role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • roles_program (Program): Role registry program

  • investor_registry_program (Program): Investor registry program

  • system_program (Program): System program

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have admin or bridge role
  • UidUsed: The provided UID has already been used for another operation

set_instrument_mint

Creates a new instrument mint PDA that associates a mint account with an instrument identifier. This instruction initializes the mapping between a mint and its corresponding instrument. The instrument ID must match the one already configured in the token's access control.

Only Admin and Token Manager role holders can call this instruction.

Arguments

  • instrument_id: [u8; 32]: Instrument identifier to associate with mint

Accounts

  • authority (Signer, mutable): Authority with admin or token manager role

  • mint (Account, mutable): The mint account to associate with the instrument

  • instrument_mint (Account, mutable): PDA with seeds:

    ["instrument_mint", instrument_id]
    • "instrument_mint": String constant for instrument mint mapping
    • instrument_id: Instrument identifier from arguments (32 bytes)
    • This account will be initialized with the mint address
  • state (Account): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • token_access_control (Account, mutable): PDA owned by security-token-hook program with seeds:

    ["access_control", mint.key()]
    • "access_control": String constant for access control account
    • mint.key(): Public key of the mint account (32 bytes)
  • admin_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • token_manager (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.token_manager]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.token_manager: 32-byte role identifier for token manager role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • roles_program (Program): Role registry program

  • hook_program (Program): Security token hook program

  • system_program (Program): System program

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have admin or token manager role
  • MismatchedInstrumentId: Provided instrument ID doesn't match the token's access control configuration

update_instrument_mint

Updates the instrument mint configuration by creating a new instrument mint PDA with a different instrument ID and closing the old one. This is used when an instrument needs to be reconfigured with a new identifier while maintaining the same mint account.

Only Admin and Token Manager role holders can call this instruction.

Arguments

  • _old_instrument_id: [u8; 32]: Previous instrument identifier (used for PDA seeds)
  • new_instrument_id: [u8; 32]: New instrument identifier to associate with the mint

Accounts

  • authority (Signer, mutable): Authority with admin or token manager role

  • mint (Account, mutable): The mint account being updated

  • old_instrument_mint (Account, mutable): PDA with seeds:

    ["instrument_mint", old_instrument_id]
    • "instrument_mint": String constant for instrument mint mapping
    • old_instrument_id: Previous instrument identifier (32 bytes)
    • This account will be closed and rent returned to authority
  • new_instrument_mint (Account, mutable): PDA with seeds:

    ["instrument_mint", new_instrument_id]
    • "instrument_mint": String constant for instrument mint mapping
    • new_instrument_id: New instrument identifier (32 bytes)
    • This account will be initialized with the mint address
  • state (Account): PDA with seeds:

    ["edge_gateway"]
    • "edge_gateway": String constant for gateway state account
  • token_access_control (Account, mutable): PDA owned by security-token-hook program with seeds:

    ["access_control", mint.key()]
    • "access_control": String constant for access control account
    • mint.key(): Public key of the mint account (32 bytes)
  • admin_role (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.admin]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.admin: 32-byte role identifier for admin role from roles account
  • token_manager (Account, optional): PDA owned by role-registry program with seeds:

    ["assigned_roles", authority.key(), roles.token_manager]
    • "assigned_roles": String constant for role assignment accounts
    • authority.key(): Public key of the authority account (32 bytes)
    • roles.token_manager: 32-byte role identifier for token manager role from roles account
  • roles (Account): PDA owned by role-registry program with seeds:

    ["roles"]
    • "roles": String constant for the main roles configuration account
  • roles_program (Program): Role registry program

  • hook_program (Program): Security token hook program

  • system_program (Program): System program

Errors

  • Paused: Gateway operations are paused
  • Unauthorized: Authority does not have admin or token manager role
  • SameInstrumentIds: New instrument ID is the same as the current one

Protocol Pause

The pause/unpause functionality for protocol operations is managed by the role-registry program. All edge-gateway instructions (except for generate_uid and initialize) check the protocol pause state through the role registry before execution.

Tests

Integration tests are located in edge-gateway.ts