> ## Documentation Index
> Fetch the complete documentation index at: https://docs.inco.org/llms.txt
> Use this file to discover all available pages before exploring further.

# EList

> Encrypted list operations for confidential dynamic arrays

# EList

The Inco Lightning SDK provides encrypted lists (`EList`) — confidential, immutable, homogeneous dynamic arrays of `Euint128` or `Ebool` values. Like the scalar operations, every elist operation is a cross-program invocation that returns a new handle; the covalidator network performs the actual list computation off-chain.

The `EList`, `EListType` types are documented in [Types](/svm/rust-sdk/types). Element values are passed as raw `u128` handles (`Euint128.0` / `Ebool.0`), and encrypted indices are `Euint128`.

<Note>
  Each elist handle is IMMUTABLE, so a handle will forever point to a particular list of values. Any operation on a list will return a new handle pointing to a new list, leaving the original list unchanged.
</Note>

## Constructor Operations

| Function                 | Description                                | Signature                                                 |
| ------------------------ | ------------------------------------------ | --------------------------------------------------------- |
| `new_elist_from_handles` | Create a list from existing handles        | `(CpiContext, Vec<u128>, u8) -> Result<EList>`            |
| `new_elist_from_inputs`  | Create a list from client-encrypted inputs | `(CpiContext, Vec<Vec<u8>>, u8, Pubkey) -> Result<EList>` |
| `list_range`             | Create an ordered list of `[start, end)`   | `(CpiContext, u16, u16, u8) -> Result<EList>`             |

## Read Operations

| Function      | Description                                         | Signature                                             |
| ------------- | --------------------------------------------------- | ----------------------------------------------------- |
| `list_get`    | Get the element at a plaintext index                | `(CpiContext, EList, u16) -> Result<u128>`            |
| `list_get_or` | Get the element at an encrypted index, or a default | `(CpiContext, EList, Euint128, u128) -> Result<u128>` |

## Modifying Operations

Operations that produce a new list return a new `EList` handle.

| Function       | Description                               | Signature                                                   |
| -------------- | ----------------------------------------- | ----------------------------------------------------------- |
| `list_append`  | Append an element to the end              | `(CpiContext, EList, u128) -> Result<EList>`                |
| `list_insert`  | Insert an element at an encrypted index   | `(CpiContext, EList, Euint128, u128) -> Result<EList>`      |
| `list_set`     | Replace the element at an encrypted index | `(CpiContext, EList, Euint128, u128) -> Result<EList>`      |
| `list_concat`  | Concatenate two lists                     | `(CpiContext, EList, EList) -> Result<EList>`               |
| `list_slice`   | Slice from an encrypted start for `len`   | `(CpiContext, EList, Euint128, u16, u128) -> Result<EList>` |
| `list_shuffle` | Shuffle elements                          | `(CpiContext, EList, u64) -> Result<EList>`                 |
| `list_reverse` | Reverse elements                          | `(CpiContext, EList) -> Result<EList>`                      |

<Warning>
  Every operation that produces a new handle requires calling `allow()` to grant decryption access. Use the `remaining_accounts` pattern to pass allowance PDAs and grant access in the same transaction. See [Access Control](/svm/guide/access-control) for simulation patterns on the client side.
</Warning>

## Creating new EList from existing handles

`new_elist_from_handles(handles, list_type)` creates a new list from an existing list of handles and returns a new elist handle. Type must be specified ahead of time and can not be changed. Handles type must match the type of the list container.

```rust theme={null}
use inco_lightning::cpi::accounts::Operation;
use inco_lightning::cpi::{new_elist_from_handles, as_euint128};
use inco_lightning::types::{EList, EListType, Euint128};

let mut handles: Vec<u128> = Vec::new();
for i in 1..=5u128 {
    let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
    let handle: Euint128 = as_euint128(cpi_ctx, i)?;
    handles.push(handle.0);
}

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let my_list: EList = new_elist_from_handles(cpi_ctx, handles, EListType::Euint128 as u8)?;
// my_list = E([1, 2, 3, 4, 5])
```

## Creating new EList from user inputs

Sometimes it's desired to create an elist from user inputs directly, like from a javascript dApp. `new_elist_from_inputs(inputs, list_type, user)` takes an array of user-encrypted ciphertexts and returns a new elist handle. Expected type must be specified ahead of time and can not be changed.

```rust theme={null}
use inco_lightning::cpi::{new_elist_from_inputs};

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let list: EList = new_elist_from_inputs(cpi_ctx, inputs, EListType::Euint128 as u8, user)?;
```

## Length and ListType

The length and element type of a list travel inside the `EList` handle itself and are read directly from the struct fields — no CPI is required.

```rust theme={null}
let len: u16 = my_list.length;       // == 5
let list_type: u8 = my_list.list_type; // EListType::Euint128 as u8
```

<Note>
  Note: It is IMPORTANT to keep in mind that the length of the elist is ALWAYS PUBLIC and encoded inside the handle itself. It's an intentional design choice to make program behavior more predictable at a cost of leaking the length of the list, because it can (for the most part) always be predicted by looking at history of on-chain operations.
</Note>

## Append

`list_append(list, value)` appends a `Euint128` or `Ebool` element at the end of a list, returning a new modified list handle.

```rust theme={null}
use inco_lightning::cpi::{list_append, as_euint128};

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let value: Euint128 = as_euint128(cpi_ctx, 5)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let my_list: EList = list_append(cpi_ctx, my_list, value.0)?;
// [].append(5) == [5]
```

## Insert

`list_insert(list, index, value)` inserts a hidden element at an encrypted position, returning a new modified list.

<Warning>
  Note however that if the index is out of range, it works similarly to `list_append()` and appends the element at the end of the list.
</Warning>

```rust theme={null}
use inco_lightning::cpi::{list_insert, as_euint128};

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let index: Euint128 = as_euint128(cpi_ctx, 0)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let value: Euint128 = as_euint128(cpi_ctx, 5)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let inserted_list: EList = list_insert(cpi_ctx, my_list, index, value.0)?;
// [10].insert(0, 5) == [5, 10]
```

## Get

`list_get(list, index)` returns the hidden element at a plaintext position as a raw `u128` handle.

<Warning>
  If the plaintext index is out of range, the operation reverts with `IndexOutOfRange`.
</Warning>

```rust theme={null}
use inco_lightning::cpi::list_get;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let handle: u128 = list_get(cpi_ctx, my_list, 0)?;
let element = Euint128(handle);
```

## GetOr

`list_get_or(list, index, default_value)` returns the hidden element at an encrypted position. Returns a handle to the hidden element if the index is within range, otherwise returns the default value.

```rust theme={null}
use inco_lightning::cpi::{list_get_or, as_euint128};

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let index: Euint128 = as_euint128(cpi_ctx, 1)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let default_value: Euint128 = as_euint128(cpi_ctx, 0)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let handle: u128 = list_get_or(cpi_ctx, my_list, index, default_value.0)?;
// [5, 10].getOr(1, 0) == 10
```

## Set

`list_set(list, index, value)` replaces an element at an encrypted index and returns a new modified list.

<Warning>
  Note: If the encrypted index is out of range, the operation is ignored and the same list is returned (although with a new handle, in order to not leak information).
</Warning>

```rust theme={null}
use inco_lightning::cpi::{list_set, as_euint128};

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let index: Euint128 = as_euint128(cpi_ctx, 1)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let new_value: Euint128 = as_euint128(cpi_ctx, 2)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let my_list: EList = list_set(cpi_ctx, my_list, index, new_value.0)?;
// [5, 10].set(1, 2) == [5, 2]
```

## Concat

`list_concat(lhs, rhs)` concatenates two elists into one and returns a new concatenated elist. The length of the new list will be `length(lhs) + length(rhs)`.

<Warning>
  Both lists must have the same element type, otherwise it reverts with `ListTypeMismatch`.
</Warning>

```rust theme={null}
use inco_lightning::cpi::list_concat;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let joint_list: EList = list_concat(cpi_ctx, list_a, list_b)?;
// [5, 10].concat([5, 10]) == [5, 10, 5, 10]
```

## Slice

`list_slice(list, start, len, default_value)` slices at a hidden start position for a specified length, returning a new sliced list of the specified length.

<Warning>
  Note that if the encrypted start position is out of bounds, the resulting list will be filled with the provided default value.
</Warning>

```rust theme={null}
use inco_lightning::cpi::{list_slice, as_euint128};

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let start: Euint128 = as_euint128(cpi_ctx, 1)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let default_value: Euint128 = as_euint128(cpi_ctx, 0)?;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let sliced_list: EList = list_slice(cpi_ctx, my_list, start, 2, default_value.0)?;
// [5, 10, 15].sliceLen(1, 2, 0) == [10, 15]
```

## Range

`list_range(start, end, list_type)` creates a new list (or a "set") and populates it with ordered values from within the range. The length of the new list will be equal to `end - start`.

```rust theme={null}
use inco_lightning::cpi::list_range;
use inco_lightning::types::EListType;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let my_list: EList = list_range(cpi_ctx, 0, 5, EListType::Euint128 as u8)?;
// my_list = E([0, 1, 2, 3, 4])
```

<Warning>
  `start` must be less than or equal to `end`, otherwise it reverts with `InvalidRange`. The range values `[start, end)` must fit within the bit width of the `list_type`.
</Warning>

## Reverse

`list_reverse(list)` reverses elements in a list — the first element becomes last, and so on.

```rust theme={null}
use inco_lightning::cpi::list_reverse;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let reversed_list: EList = list_reverse(cpi_ctx, my_list)?;
// [5, 10].reverse() == [10, 5]
```

## Shuffle

`list_shuffle(list, counter)` deterministically shuffles elements within a list, returning a new shuffled list with the same length. The caller-supplied `counter` is mixed into the operation as a nonce.

```rust theme={null}
use inco_lightning::cpi::list_shuffle;

let cpi_ctx = CpiContext::new(inco.clone(), Operation { signer: signer.clone() });
let shuffled_list: EList = list_shuffle(cpi_ctx, my_list, counter)?;
// [5, 10].shuffle() could return [10, 5] or [5, 10]
```

<Note>
  Handles are deterministically derived from operations, so the same operation on the same list with the same inputs always produces the same handle.
</Note>
