npm package: @kaio-xyz/solana-edge-gateway
Description: 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.
Latest version: 1.0.1

# 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](../investor-registry/README.md#native-addresses-limitation), solana-version requires the encoding struct to submit address update per chain id in separate instructions calls.

```solidity
// 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

```solidity
// 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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:

```rust
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.

```rust
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:

```rust
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](../investor-registry/README.md)** with seeds:

  ```
  ["registry"]
  ```

  - `"registry"`: String constant for registry state account

- `access_control` (Account): PDA owned by **[security-token-hook program](../security-token-hook/README.md)** 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](../investor-id/README.md)** 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](../credentials/README.md)** 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](../investor-registry/README.md)** 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:

```rust
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](../investor-registry/README.md)** with seeds:

  ```
  ["registry"]
  ```

  - `"registry"`: String constant for registry state account

- `access_control` (Account): PDA owned by **[security-token-hook program](../security-token-hook/README.md)** 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](../investor-id/README.md)** 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](../credentials/README.md)** 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](../investor-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)
- `investor_id_program` (Program): [Investor ID program](../investor-id/README.md)
- `investor_registry_program` (Program): [Investor registry program](../investor-registry/README.md)
- `credentials_program` (Program): [Credentials program](../credentials/README.md)
- `security_token_hook_program` (Program): [Security token hook program](../security-token-hook/README.md)

#### 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](../role-registry/README.md)** 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](../role-registry/README.md)** with seeds:

  ```
  ["roles"]
  ```

  - `"roles"`: String constant for the main roles configuration account

- `roles_program` (Program): [Role registry program](../role-registry/README.md)

#### 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:

```rust
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](../credentials/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** with seeds:

  ```
  ["roles"]
  ```

  - `"roles"`: String constant for the main roles configuration account

- `roles_program` (Program): [Role registry program](../role-registry/README.md)
- `credentials_program` (Program): [Credentials program](../credentials/README.md)

#### 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.

```rust
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](../investor-registry/README.md)** with seeds:

  ```
  ["registry"]
  ```

  - `"registry"`: String constant for registry state account

- `addresses` (Account, mutable): PDA owned by **[investor-registry program](../investor-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** with seeds:

  ```
  ["roles"]
  ```

  - `"roles"`: String constant for the main roles configuration account

- `roles_program` (Program): [Role registry program](../role-registry/README.md)
- `investor_registry_program` (Program): [Investor registry program](../investor-registry/README.md)
- `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](../security-token-hook/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** with seeds:

  ```
  ["roles"]
  ```

  - `"roles"`: String constant for the main roles configuration account

- `roles_program` (Program): [Role registry program](../role-registry/README.md)
- `hook_program` (Program): [Security token hook program](../security-token-hook/README.md)
- `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](../security-token-hook/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** 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](../role-registry/README.md)** with seeds:

  ```
  ["roles"]
  ```

  - `"roles"`: String constant for the main roles configuration account

- `roles_program` (Program): [Role registry program](../role-registry/README.md)
- `hook_program` (Program): [Security token hook program](../security-token-hook/README.md)
- `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](../role-registry/README.md)**. 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](../../tests/edge-gateway.ts)