Update (04.14.23) The Proof-of-Concept version is compatible with condition evaluation on Mumbai (Polygon testnet) via the Threshold node-facing Tapir testnet.
An additional node-facing testnet (Oryx) now points to Polygon mainnet. This means access conditions can be checked and verified by Threshold nodes if they pertain to state on either of the Polygon networks. Note that it is not possible to evaluate conditions on both Mumbai and Polygon mainnet in a single runtime, because pre-configuration requires choosing between Tapir and Oryx.
This tutorial is a quick way for developers to learn about the Threshold Access Control service by building with the Proof-of-Concept (PoC) version of Conditions-Based Decryption.
Note that the underlying trust assumptions vary between versions and technologies; these are explained in detail for the CBD Proof-of-Concept, CBD Mainnet, and PRE Mainnet versions.

1. Install nucypher-ts

nucypher-ts is under active development.
To begin, we need to install the nucypher-ts library:
yarn add @nucypher/nucypher-ts
One of the nucypher-ts dependencies takes advantage of WASM. In order to run nucypher-ts in the browser, we have to load WASM from the source files. This process is mostly automated by the wrapper generated by wasm-pack.
For this tutorial we'll need a few extra packages:
yarn add ethers @metamask/detect-provider
Visit nucypher-ts/examples for more information on using nucypher-ts in your web application.

2. Build a Cohort

Next, we will create a Cohort based on our risk preferences. A Cohort is a group of nodes that work together to control access to data. Threshold and Shares are two parameters used to create a Cohort. For example, a 3-of-5 Cohort needs at least 3 of the 5 members to provide shares to access the original data.
To create a Cohort, use the following code:
import { Cohort } from '@nucypher/nucypher-ts';
const config = {
threshold: 3,
shares: 5,
porterUri: '',
const newCohort = await Cohort.create(config);
Notice that we provided a porterUri parameter. Porter is a web-based service that interacts with nodes on the network on behalf of applications. It acts as an "Infura for Threshold Access Control". In this example, we used a Porter endpoint for the tapir testnet.

3. Specify default Conditions

Conditions are the requirements for a data requester or recipient to access the plaintext data – i.e. what they will need to prove later to gain decryption rights.
The ERC721Ownership condition checks the owner of a given token ID. It can be customized by using the ownerOf contract method and comparing it with the requestor's signature. For more information, see the References section.
CBD allows developers to enforce conditional access at various runtime stages, depending on what makes the most sense for the use case. At this point, we will add the default Conditions, which will only gate-keep access if no other conditions are included later at encryption time. This is explained further in Condition Hierarchies.
We will now specify the conditions that must be met to access the data. In this tutorial, we will require that the requester owns an ERC721 token with a token ID of 5954.
import { Conditions } from '@nucypher/nucypher-ts';
const NFTOwnership = new Conditions.ERC721Ownership({
contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
chain: 5, // Tapir network uses Görli testnet
parameters: [5954],
There are multiple Condition types and it is possible to combine multiple conditions into a ConditionSet.
import { Conditions, ConditionSet } from '@nucypher/nucypher-ts';
const conditions = new ConditionSet([
// Other conditions can be added here

4. Build a Strategy

We will now combine the Cohort, ConditionSet, and any other necessary parameters into a Strategy. Strategies are a convenient way to bundle together frequently used configurations, including specific combinations of network parameters and conditionality.
import { Strategy } from '@nucypher/nucypher-ts';
const newStrategy = Strategy.create(newCohort, conditions);
Next, we will deploy this Strategy to the Threshold Network. To do that, we're going to transact on Polygon Mumbai:
import detectEthereumProvider from '@metamask/detect-provider';
import { providers } from 'ethers';
const MMprovider = await detectEthereumProvider();
const mumbai = providers.getNetwork(80001);
const web3Provider = new providers.Web3Provider(MMprovider, mumbai);
const newDeployed = await newStrategy.deploy('test', web3Provider);
Deploying a Strategy requires writing to the blockchain. This requires a wallet funded with testnet MATIC and connection to the blockchain via a provider(e.g. MetaMask).
For more information about customizing and reusing Cohort, Condition, and Strategy objects, see the References page in the documentation.

5. Encrypt the plaintext & update Conditions

We can now encrypt data using the newly deployed Strategy. At this point, we can also specify new conditions on which data access will be predicated. These will take a higher precedence and override the default conditions contained in the Strategy. For this example, let's make it so the overriding requirement is that the requester's wallet hold a minimum number (three) of NFTs. The NFTs are from the same collection that we specified in Step 3. Note that Threshold nodes will check this new condition using the balanceOf method.
To encrypt the plaintext:
const NFTBalanceConfig = {
contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
standardContractType: 'ERC721',
chain: 5,
method: 'balanceOf',
parameters: [':userAddress'],
returnValueTest: {
comparator: '>=',
value: 3,
const NFTBalance = new Conditions.Condition(NFTBalanceConfig);
const encrypter = newDeployed.encrypter;
const plaintext = 'this is a secret';
const encryptedMessageKit = encrypter.encryptMessage(
new ConditionSet([NFTBalance])
The resulting encryptedMessageKit contains the encrypted data and associated condition(s).

6. Request decryption rights

Finally, we will test the conditional access control service by requesting decryption rights:
const decrypter = newDeployed.decrypter;
const conditionContext = conditions.buildContext(web3Provider);
const decryptedMessage = await decrypter.retrieveAndDecrypt(
At decryption time, the requester will be asked to verify their address by signing a message in MetaMask. This is where conditionContext comes into play – if the requester's address controls the minimum number (or greater) of the specified NFT, they are eligible to receive the requisite number of decryption fragments. By assembling these fragments, they are able to decrypt and view the plaintext encrypted in the previous step.
Note that the requester does not need to manually sign the next time they seek access to the data, as their client can temporarily cache their signature. Fresh plaintexts encrypted under any conditions involving the same wallet address are automatically accessible to any requester who has signed at least once, provided they still fulfill the (new) conditions, and the cached signature has not expired.

Example applications

The following samples showcase integrations with React-based web apps, and serve as an 'end-to-end' reference for creating conditions-based encryption & decryption: