Skip to main content

Before you start

Import the ePreview library:
import {ePreview, elist, ETypes} from "@inco/lightning-preview/src/Preview.Lib.sol";
import {euint256, ebool, e, inco} from "@inco/lightning/src/Lib.sol";

Important: Fee Payments and Access Control

Fee Payments: Most elist operations that consume encrypted inputs require paying fees. When calling ePreview.newEList(), ensure msg.value >= inco.getFee() * ciphertextCount. Operations like shuffle() and shuffledRange() also require fee payment.
Access Control: After creating or modifying an elist, you must explicitly grant access permissions using inco.allow() to allow addresses to decrypt the list contents. Always allow both the contract and the user who needs access:
inco.allow(elist.unwrap(myList), address(this));
inco.allow(elist.unwrap(myList), msg.sender);

Creating new empty EList

newEList(type) creates a new empty list and returns a new elist handle. Type must be specified ahead of time and can not be changed. Here is an example of how to create a new EList holding euint256 values:
elist myList = ePreview.newEList(ETypes.Uint256);
// myList = E([])
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.
Arguments:
  • ETypes listType - Type of each element in the list. This can not be changed
Returns:
  • Elist - a new elist handle

Creating new EList from existing handles

newEList(handles, type) creates a new list from 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, otherwise it will revert. Here is an example of how to create a new EList from existing euint256 handles:
bytes32[] memory handles = new bytes32[](5);
for (uint256 i = 0; i < 5; i++) {
    handles[i] = euint256.unwrap(e.asEuint256(i + 1));
}
elist myList = ePreview.newEList(handles, ETypes.Uint256);
// myList = E([1, 2, 3, 4, 5])
Arguments:
  • bytes32[] handles - An array of handles to create a new list from
  • ETypes listType - Type of each element in the list. This can not be changed and must match the type of each handle in the array.
Returns:
  • Elist - A new elist handle

Length

length(list) returns the length of the list in plaintext. It’s a pure function that doesn’t require any gas to call.
uint16 len = ePreview.length(myList);
// len == 5
Note: It is IMPORTANT to keep in mind that the length of the elist is ALWAYS PUBLIC and encoded inside the returning handle itself. It’s an intentional design choice to make contract 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.
Arguments:
  • elist list - list to read length from
Returns:
  • uint16 len - length of the list in plaintext

ListTypeOf

listTypeOf(list) returns the type of elements contained in the list. It’s a view function that doesn’t require any gas to call.
function listTypeOf() public view returns (ETypes) {
    return ePreview.listTypeOf(list);
}
// Returns ETypes.Uint256 or ETypes.Bool
Arguments:
  • elist list - list to read type from
Returns:
  • ETypes - the type of elements in the list (e.g., ETypes.Uint256, ETypes.Bool)

Creating new EList from user inputs

Sometimes it’s desired to create an elist from user inputs directly, like from a javascript dApp. newEList(inputs, type, user) can take 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. Handles type must match the type of the list container, otherwise it will revert. Here is a complete example of how to create a new EList from user encrypted inputs:
function newEList(bytes[] memory inputs, ETypes listType, address user)
    public
    payable
    returns (elist)
{
    require(msg.value >= inco.getFee() * inputs.length, "Fee not paid");
    elist list = ePreview.newEList(inputs, listType, user);
    inco.allow(elist.unwrap(list), address(this));
    inco.allow(elist.unwrap(list), msg.sender);
    return list;
}
Arguments:
  • bytes[] ciphertexts - An array of encrypted user inputs to create a new list from
  • ETypes listType - Expected type of each element in the list. This can not be changed and must match the type of each handle in the array.
  • address user - Address of the owner of the inputs, used to decrypt ciphertexts.
Returns:
  • elist - A new elist handle

Append

append(list, value) appends an euint256 or ebool element type at the end of an array, returning a new modified list handle. Example usage:
function listAppend(bytes memory ctValue) public payable returns (elist) {
    require(msg.value >= inco.getFee(), "Fee not paid");
    euint256 handle = e.newEuint256(ctValue, msg.sender);
    inco.allow(euint256.unwrap(handle), address(this));
    inco.allow(euint256.unwrap(handle), msg.sender);
    list = ePreview.append(list, handle);
    inco.allow(elist.unwrap(list), address(this));
    inco.allow(elist.unwrap(list), msg.sender);
    return list;
}
// [].append(5) == [5]
Arguments:
  • elist list - An elist handle to append to
  • euint256/ebool value - Element value to be appended to the list. Must match the elist type.
Returns:
  • elist - A new elist handle

Insert

insert(list, i, value) inserts a hidden element at a desired hidden position, returns a new modified list. Index can be both plaintext or encrypted.
Note however that if index is out of range, it works similarly to append() and appends element at the end of the list.
Example usage:
elist myList = ePreview.newEList(ETypes.Uint256);
elist myNewList = ePreview.append(l, e.asEuint256(10));
euint256 el = e.asEuint256(5);

elist insertedList = ePreview.insert(myNewList, uint256(0), el);
// [10].insert(0, 5) == [5, 10]
Arguments:
  • elist A - An elist handle to insert to
  • euint256/uint256 i - Index position to insert at, can be both encrypted or plaintext.
  • euint256/ebool B - Element value to be inserted to the list. Must match the elist type.
Returns:
  • Elist - A new elist handle

Get

getEuint256(list, index) and getEbool(list, index) return the hidden element at a plaintext position. These methods have separate names due to Solidity function overloading limitations. Example usage:
function listGet(uint16 index) public returns (euint256) {
    euint256 res = ePreview.getEuint256(list, index);
    inco.allow(euint256.unwrap(res), msg.sender);
    return res;
}

function boolListGet(uint16 index) public returns (ebool) {
    ebool res = ePreview.getEbool(boolList, index);
    inco.allow(ebool.unwrap(res), msg.sender);
    return res;
}
Arguments:
  • elist list - elist handle to get element from
  • uint16 index - Plaintext index position to get element at
Returns:
  • euint256 or ebool - The encrypted value at the specified index

GetOr

getOr(list, i, default) return hidden element at hidden position. Index can be either encrypted or plaintext. Returns a handle to the hidden element if the index is within range, otherwise returns the default value. Example usage:
function listGetOr(bytes memory ctIndex, bytes memory ctDefaultValue)
    public
    payable
    returns (euint256)
{
    require(msg.value >= inco.getFee() * 2, "Fee not paid");
    euint256 index = e.newEuint256(ctIndex, msg.sender);
    euint256 defaultValue = e.newEuint256(ctDefaultValue, msg.sender);
    euint256 res = ePreview.getOr(list, index, defaultValue);
    inco.allow(euint256.unwrap(res), msg.sender);
    return res;
}
// [5, 10].getOr(1, 0) == 10
Arguments:
  • elist list - elist handle to get element at
  • euint256/uint256 index - Index position to get element at. Can be either plaintext or encrypted.
  • euint256/ebool default - A default element value to be returned if index is out of range. Must match the elist type.
Returns:
  • euint256/ebool - The encrypted value at the specified index, or the default value if out of range

Set

set(list, i, value) replaces an element at hidden index and returns a new modified list. Index can be either plaintext or encrypted.
Note: If index is out of range, the element is appended to the end of the list.
Example usage:
elist myList = ePreview.newEList(ETypes.Uint256);
elist myNewList1 = ePreview.append(myList, e.asEuint256(5));
elist myNewList2 = ePreview.append(myNewList1, e.asEuint256(10));
euint256 index = e.asEuint256(1);
euint256 newValue = e.asEuint256(2);

euint256 ten = ePreview.set(myNewList2, index, newValue);
// [5, 10].set(1, 2) == [5, 2]
Arguments:
  • elist A - elist handle to modify element in
  • euint256/uint256 i - index position of element to modify. Can be either plaintext or encrypted.
  • euint256/ebool B - element value to be changed if index is within range. Element will be appended to the list if the index is out of range. Must match the elist type.
Returns:
  • Elist - a new elist handle

Concat

concat(list_a, list_b) concatenates two elists into one, returns a new concatenated elist. The length of the new list will be length(list1)+length(list2) Example usage:
elist myList = ePreview.newEList(ETypes.Uint256);
elist myNewList1 = ePreview.append(myList, e.asEuint256(5));
elist myNewList2 = ePreview.append(myNewList1, e.asEuint256(10));

elist jointList = ePreview.concat(myNewList2, myNewList2);
// [5, 10].concat([5, 10]) == [5, 10, 5, 10]
Arguments:
  • elist list_a - elist handle to be prepended
  • elist list_b - elist handle to be appended
Returns:
  • Elist - a new elist handle containing elements from both A and B

Slice

slice(list, start, end) is like in any other language that takes in start and end both in plaintext. Returns a new sliced list of length “end-start”. If start and end are out of bounds, it will revert. The end index must be greater than the start index. Example usage:
elist myList = ePreview.newEList(ETypes.Uint256);
elist myNewList1 = ePreview.append(myList, e.asEuint256(5));
elist myNewList2 = ePreview.append(myNewList1, e.asEuint256(10));
elist myNewList3 = ePreview.append(myNewList2, e.asEuint256(15));

elist slicedList = ePreview.slice(myNewList3, 1, 3);
// [5, 10, 15].slice(1, 3) == [10, 15]
Arguments:
  • elist A - elist handle to be sliced
  • uint16 start - Start index of the slice, in plaintext.
  • uint16 end - End index of the slice, in plaintext. Must be greater than start and within the bounds of the list length.
Returns:
  • Elist - a new sliced list with a new length of “end-start”

SliceLen

sliceLen(list, E(start), len, defaultValue) is a variant of slice() but allows to slice at some hidden index specifying a length instead of end position. Returns a new sliced list of the specified length.
Note that if the encrypted start position is out of bounds, the resulting list will be filled with the provided default value.
Example usage:
function listSlice(bytes memory ctStart, uint16 len, bytes memory ctDefaultValue)
    public
    payable
    returns (elist)
{
    require(msg.value >= inco.getFee() * 2, "Fee not paid");
    euint256 start = e.newEuint256(ctStart, msg.sender);
    euint256 defaultValue = e.newEuint256(ctDefaultValue, msg.sender);
    list = ePreview.sliceLen(list, start, len, defaultValue);
    inco.allow(elist.unwrap(list), address(this));
    inco.allow(elist.unwrap(list), msg.sender);
    return list;
}
// [5, 10, 15].sliceLen(1, 2, 0) == [10, 15]
Arguments:
  • elist list - elist handle to be sliced
  • euint256 start - Encrypted start index of the slice
  • uint16 len - Length of the desired slice
  • euint256/ebool defaultValue - Default value to use if start position is out of bounds. Must match the elist type.
Returns:
  • elist - a new sliced list with the specified length

Range

range(start, end) creates a new list (or a “set”) and populates it with ordered values from within range. The length of the new list will be equal to “end-start”. Example usage:
elist myList = ePreview.range(0, 5);
// myList = E([0, 1, 2, 3, 4])
Arguments:
  • uint16 start - Start value of the range, inclusive.
  • uint16 end - End of the range, exclusive. Must be greater than start.
Returns:
  • Elist - a new elist handle containing elements from start to end-1 with the length of “end-start”

Reverse

reverse(list) reverses elements in a list, first element becomes last, and so on. Example usage:
elist myList = ePreview.newEList(ETypes.Uint256);
elist myNewList1 = ePreview.append(myList, e.asEuint256(5));
elist myNewList2 = ePreview.append(myNewList1, e.asEuint256(10));

elist reversedList = ePreview.reverse(myNewList2);
// [5, 10].reverse() == [10, 5]
Arguments:
  • elist A - elist handle to be reversed
Returns:
  • Elist - a new elist handle with elements in reverse order

Shuffle

shuffle(list) deterministically shuffles elements within a list, returning a new shuffled list with the same length. This operation requires fee payment. Example usage:
function listShuffle() public payable returns (elist) {
    require(msg.value >= inco.getFee(), "Fee not paid");
    list = ePreview.shuffle(list);
    inco.allow(elist.unwrap(list), address(this));
    inco.allow(elist.unwrap(list), msg.sender);
    return list;
}
// [5, 10].shuffle() could return [10, 5] or [5, 10] randomly
Arguments:
  • elist list - elist handle to be shuffled
Returns:
  • elist - a new elist handle with elements shuffled where each element is equally likely to be in any position in the new list.

ShuffledRange

shuffledRange(start, end) is a convenience function combining range() and shuffle() from example above into one function. It creates a range of elements from start to end, unlike range() the resulting elist is unordered and completely random. This operation requires fee payment. Example usage:
function listShuffledRange(uint16 start, uint16 end) public payable returns (elist) {
    require(msg.value >= inco.getFee(), "Fee not paid");
    elist newRangeList = ePreview.shuffledRange(start, end);
    inco.allow(elist.unwrap(newRangeList), address(this));
    inco.allow(elist.unwrap(newRangeList), msg.sender);
    return newRangeList;
}
// shuffledRange(1, 53) = E([42, 7, 19, 33, ...])
Arguments:
  • uint16 start - Start value of the range, inclusive.
  • uint16 end - End of the range, exclusive. Must be greater than start.
Returns:
  • elist - a new elist handle containing elements from start to end-1 with the length of “end-start” in a random order.