JSPM

lit-js-sdk

1.1.70
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 9877
  • Score
    100M100P100Q110222F
  • License GPL-3.0

LIT Javascript SDK

Package Exports

  • lit-js-sdk

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (lit-js-sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

LIT Protocol JS SDK

The LIT Protocol is a decentralized access control protocol running on top of Ethereum and other EVM chains. With LIT, you can do 4 main things:

  • Encrypt and lock static content behind an on chain condition (for example, posession of an NFT)
  • Decrypt static content that was locked behind an on chain condition
  • Authorize network signatures that provide access to dynamic content (for example, a server or network resource) behind an on chain condition
  • Request a network signed JWT that provisions access and authorization to dynamic content behind an on chain condition.

With this functionality, the LIT protocol enables the creation of locked NFTs that can only be unlocked by owners of that NFT. It also enables provisioning access to a given server or network resource only to NFT owners. Rather than a simple JPEG, LIT NFTs are HTML/JS/CSS web pages that can be interactive and dynamic.

State of the network today

Right now, the LIT Protocol is in an alpha state and the creators are running all the nodes. It is unaudited and the nodes are not distributed yet. There are various security improvements to be made, and cryptoeconomic guarantees as a result of staking are not in place yet. However, we believe it is highly unlikely that any locked or private content would leak or be exposed.

How does the LIT protocol work?

Static Content - Encrypting / locking

This SDK will encrypt your content, and upload your conditions for decryption to each LIT node. When someone wants to access the content, the SDK will request a message signature from the user's wallet that proves that they own the NFT associated with the content to each LIT node. The LIT nodes will then send down the decryption shares and the SDK will combine them and decrypt the content.

Dynamic Content - Authorizing access to a resource via JWT

This SDK has the ability to create the authorization conditions for a given resource and store them with the LIT nodes. When someone requests a network signature because they are trying to access a resource (typically a server that serves some dynamic content), the SDK will request a message signature from the user's wallet that proves that they own the NFT associated with the resource to each LIT node. The LIT nodes will each verify that the user owns the NFT, sign the JWT to create a signature share, then send down that signature share. The SDK will combine the signature shares to obtain a signed JWT which can be presented to the resource to authenticate and authorize the user.

Example

You can find a minimal example project that implements dynamic content authorization here: https://github.com/LIT-Protocol/lit-minimal-jwt-example

Installation

Use yarn or npm to add the lit-js-sdk to your product:

yarn add lit-js-sdk

You can then import it like so:

import LitJsSdk from 'lit-js-sdk'

We also provide a web-ready package with all dependencies included at build/index.web.js. You can import this into your HTML webpage using a script tag:

<script onload='litJsSdkLoaded()' src="https://jscdn.litgateway.com/index.web.js"></script>

You can then use all the sdk functions via LitJsSdk for example LitJsSdk.toggleLock()

Note that if you use a script tag like this, you will likely need to initialize a connection to the LIT Network using something like the below code snippet. The SDK requires an active connection to the LIT nodes to perform most functions (but, notably, a connection to the LIT nodes is not required if you are just verifying a JWT)

function litJsSdkLoaded(){
  var litNodeClient = new LitJsSdk.LitNodeClient()
  litNodeClient.connect()
  window.litNodeClient = litNodeClient
}

Using the LIT Protocol

Example projects and code

Connecting to the network

For most use cases, you will want an active connection to the Lit Protocol. In web apps, this is typically done on first page load and can be shared between all your pages.

To connect, use the code below. Note that client.connect() will return instantly, but that does not mean your are connected to the network. You must listen for the lit-ready event. In the code below, we make the litNodeClient available as a global variable so that it can be used throughout the web app.

const client = new LitJsSdk.LitNodeClient()
client.connect()
window.litNodeClient = client

To listen for the "lit-ready" event which is fired when the network is fully connected:

document.addEventListener('lit-ready', function (e) {
  console.log('LIT network is ready')
  setNetworkLoading(false) // replace this line with your own code that tells your app the network is ready
}, false)

Static Content - Minting HTML NFTs

HTML NFTs are essentially super-powered NFTs. To mint an HTML NFT, you should mint (or already own) any ERC721 or ERC1155 NFT that will serve as the access control token for unlocking the NFT. In the past, we called HTML NFTs "LITs" (Locked Interactive Tokens) so if you see a reference in docs or code to "a LIT" that is referring to an HTML NFT.

We provide pre-deployed ERC1155 NFT contracts on Polygon and Ethereum for your use. Usage of these contracts is optional, and you may supply your own if desired. You can find the addresses of these contracts here: https://github.com/LIT-Protocol/lit-js-sdk/blob/main/src/lib/constants.js#L74

To mint a token using our pre-deployed contracts, you can use the mintLIT function documented here: https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html#mintlit

For example:

const { tokenId, tokenAddress, mintingAddress, txHash, errorCode, authSig } = await LitJsSdk.mintLIT({ chain, quantity })

Once your have your NFT, you can lock and associate content with it on the LIT network. In our implementation in MintLIT, we render the locked content as an HTML string, embedding any media such as pictures or videos as data urls. You can do this, or you can encrypt files directly without rendering them into a HTML string. To encrypt a string, use the following function documented here: https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html#zipandencryptstring

const { symmetricKey, encryptedZip } = await LitJsSdk.zipAndEncryptString(lockedFileMediaGridHtml)

Now you need to encrypt the symmetric key, and save it to the LIT nodes. litNodeClient should be an instance of LitNodeClient that has connected to the network via the connect function.

You must also define your access control conditions (the conditions under which someone can decrypt the content). In the example below, we define a condition that requires the user holds at least 1 ERC1155 token with Token ID 9541 from the 0x3110c39b428221012934A7F617913b095BC1078C contract.

const accessControlConditions = [
  {
    contractAddress: '0x3110c39b428221012934A7F617913b095BC1078C',
    standardContractType: 'ERC1155',
    chain,
    method: 'balanceOf',
    parameters: [
      ':userAddress',
      '9541'
    ],
    returnValueTest: {
      comparator: '>',
      value: '0'
    }
  }
]

const encryptedSymmetricKey = await window.litNodeClient.saveEncryptionKey({
  accessControlConditions,
  symmetricKey,
  authSig,
  chain
})

We then pass that encrypted content to a function that creates an HTML webpage with an embedded unlock button.

const htmlString = LitJsSdk.createHtmlLIT({
    title,
    htmlBody,
    css,
    accessControlConditions,
    encryptedSymmetricKey,
    chain,
    encryptedZipDataUrl: await LitJsSdk.fileToDataUrl(encryptedZip)
  })

You'll need to store your LIT somewhere, and we use IPFS via Pinata for this purpose.

const litHtmlBlob = new Blob(
  [htmlString],
  { type: 'text/html' }
)
const formData = new FormData()
formData.append('file', litHtmlBlob)

const uploadRespBody = await new Promise((resolve, reject) => {
  fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', {
    method: 'POST',
    mode: 'cors',
    headers: {
      Authorization: `Bearer ${PINATA_API_KEY}`
    },
    body: formData
  }).then(response => response.json())
    .then(data => resolve(data))
    .catch(err => reject(err))
})
const ipfsCid = uploadRespBody.IpfsHash
const fileUrl = `https://ipfs.io/ipfs/${ipfsCid}`

Your LIT is now accessible at the fileUrl variable.

Finally, you should store your token metadata somewhere, so that your LIT is backwards compatible with existing NFT websites. We use Firebase for this on MintLIT and if you are using our NFT contracts, you are welcome to use our Firebase instance to store your metadata as well. You can find this createTokenMetadata function in this repo: https://github.com/LIT-Protocol/MintLIT

await createTokenMetadata({
  chain,
  tokenAddress,
  tokenId,
  title,
  description,
  socialMediaUrl,
  quantity,
  mintingAddress,
  fileUrl,
  ipfsCid,
  txHash
})

Now, you can send the NFT that corresponds to this LIT to anyone, and they can use the website at fileUrl to unlock and decrypt the content inside it.

Static Content - Unlocking LITs

To unlock a LIT, you must retrieve the encryption key shares from the nodes. This SDK provides a convenience function to do this for you called toggleLock. It will pull down the encryption key shares and combine them into the encryption key itself, and decrypt content located at window.encryptedZipDataUrl, and then load the content into a div with id mediaGridHolder. You may use toggleLock or implement parts of it yourself if you have further customizations. Here's how it works:

First, obtain an authSig from the user. This will ask their metamask to sign a message proving they own the crypto address that presumably owns the NFT. Pass the chain you're using.

const authSig = await LitJsSdk.checkAndSignAuthMessage({chain: 'polygon'})

Next, obtain the symmetric key from the LIT network. It's important that you have a connected LitNodeClient accessible at window.litNodeClient for this to work.

const symmetricKey = await window.litNodeClient.getEncryptionKey({
  accessControlConditions: window.accessControlConditions,
  toDecrypt: window.encryptedSymmetricKey,
  authSig,
  chain: window.chain
})

Finally, decrypt the content and inject it into the webpage. We provide a convenience function to unlock the LIT once you have the symmetric encryption key that does the same thing as the code below, located here: https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html#unlocklitwithkey

// convert data url to blob
const encryptedZipBlob = await (await fetch(window.encryptedZipDataUrl)).blob()
// decrypt the zip
const decryptedFiles = await LitJsSdk.decryptZip(encryptedZipBlob, symmetricKey)
// pull out the data url that contains the now-decrypted HTML
const mediaGridHtmlBody = await decryptedFiles['string.txt'].async('text')
// load the content into a div so the user can see it
document.getElementById('mediaGridHolder').innerHTML = mediaGridHtmlBody

Dynamic Content - Verifying a JWT that was signed by the Lit network

Heads up You can find a minimal example project that implements dynamic content authorization here: https://github.com/LIT-Protocol/lit-minimal-jwt-example

Verifying a JWT would typically be done on the server side (nodejs), but should work in the browser too.

First, import the SDK:

 const LitJsSdk = require('lit-js-sdk')

Now, you must have a JWT to verify. Usually this comes from the user who is trying to access the resource. You can try the jwt harcoded in the example below, which may be expired but should at least return a proper header and payload. In the real world, you should use jwt presented by the user

const jwt = "eyJhbGciOiJCTFMxMi0zODEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJMSVQiLCJzdWIiOiIweGRiZDM2MGYzMDA5N2ZiNmQ5MzhkY2M4YjdiNjI4NTRiMzYxNjBiNDUiLCJjaGFpbiI6ImZhbnRvbSIsImlhdCI6MTYyODAzMTM1OCwiZXhwIjoxNjI4MDc0NTU4LCJiYXNlVXJsIjoiaHR0cHM6Ly9teS1keW5hbWljLWNvbnRlbnQtc2VydmVyLmNvbSIsInBhdGgiOiIvYV9wYXRoLmh0bWwiLCJvcmdJZCI6IiJ9.lX_aBSgGVYWd2FL6elRHoPJ2nab0IkmmX600cwZPCyK_SazZ-pzBUGDDQ0clthPVAtoS7roHg14xpEJlcSJUZBA7VTlPiDCOrkie_Hmulj765qS44t3kxAYduLhNQ-VN"
const { verified, header, payload } = LitJsSdk.verifyJwt({jwt})
if (payload.baseUrl !== "this-website.com" || payload.path !== "/path-you-expected" || payload.orgId !== "" || payload.role !== "" || payload.extraData !== "") {
  // Reject this request!
  return false
}

The "verified" variable is a boolean that indicates whether or not the signature verified properly. Note: YOU MUST CHECK THE PAYLOAD AGAINST THE CONTENT YOU ARE PROTECTING. This means you need to look at "payload.baseUrl" which should match the hostname of the server, and you must also look at "payload.path" which should match the path being accessed. If these do not match what you're expecting, you should reject the request.

Dynamic Content - Provisoning access to a resource

Heads up You can find a minimal example project that implements dynamic content authorization here: https://github.com/LIT-Protocol/lit-minimal-jwt-example

Use dynamic content provisoning to put some dynamic content behind an on chain condition (for example, possession of an NFT). This function will essentially store that condition and the resource that users who meet that condition should be authorized to access. The resource could be a URL, for example. The dynamic content server should then verify the JWT provided by the network on every request, which proves that the user meets the on chain condition.

The "saveSigningCondition" function of the LitNodeClient is what you want to use for this, which is documented here: https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html#litnodeclientsavesigningcondition

Note that you need an active connection to the Lit Protocol nodes to use this function. This connection can be made with the following code:

const litNodeClient = new LitJsSdk.LitNodeClient()
litNodeClient.connect()

Now, you should define you access control conditions. In the example below, we define a condition that requires the user holds at least 1 ERC1155 token with Token ID 9541 from the 0x3110c39b428221012934A7F617913b095BC1078C contract.

const accessControlConditions = [
  {
    contractAddress: '0x3110c39b428221012934A7F617913b095BC1078C',
    standardContractType: 'ERC1155',
    chain,
    method: 'balanceOf',
    parameters: [
      ':userAddress',
      '9541'
    ],
    returnValueTest: {
      comparator: '>',
      value: '0'
    }
  }
]

Next, obtain an authSig from the user. This will ask their metamask to sign a message proving they own the crypto address in their wallet. Pass the chain you're using.

const authSig = await LitJsSdk.checkAndSignAuthMessage({chain: 'polygon'})

Next, define the Resource ID of the resource you are granting access to. This is typically a URL.

const resourceId = {
  baseUrl: 'my-dynamic-content-server.com',
  path: '/a_path.html',
  orgId: "",
  role: "",
  extraData: ""
}

Finally, you can save all this to the Lit nodes, and then users will be able to request a JWT that grants access to the resource.

await litNodeClient.saveSigningCondition({ accessControlConditions, chain, authSig, resourceId })

Make sure that you save the accessControlConditions and resourceId, because the user will have to present them when requesting a JWT that would grant them access. You will typically want to store them wherever you will auth the user, so wherever your "log in" or "authorize" button would live.

Dynamic Content - Accessing a resource via a JWT

Heads up You can find a minimal example project that implements dynamic content authorization here: https://github.com/LIT-Protocol/lit-minimal-jwt-example

Obtaining a signed JWT from the Lit network can be done via the getSignedToken function of the LitNodeClient documented here: https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html#litnodeclientgetsignedtoken

Important You must call litNodeClient.saveSigningCondition to save a signing condition before you can request a signature and access a resource via a JWT. See the docs above for "Dynamic Content - Provisoning access to a resource" to learn how to do this.

Note that you need an active connection to the Lit Protocol nodes to use this function. This connection can be made with the following code:

const litNodeClient = new LitJsSdk.LitNodeClient()
litNodeClient.connect()

First, obtain an authSig from the user. This will ask their metamask to sign a message proving they own the crypto address in their wallet. Pass the chain you're using.

const authSig = await LitJsSdk.checkAndSignAuthMessage({chain: 'polygon'})

Now, using the accessControlConditions and resourceId you defined when provisoning access to the resource, you can use the getSignedToken function to get the token:

const jwt = await litNodeClient.getSignedToken({ accessControlConditions, chain, authSig, resourceId })

You can then present this JWT to a server, which can verify it using the verifyJwt function documented here https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html#verifyjwt

Examples of access control conditions

Must posess at least one ERC1155 token with a given token id

In this example, the token contract's address is 0x3110c39b428221012934A7F617913b095BC1078C and the token id we are checking for is 9541.

const accessControlConditions = [
  {
    contractAddress: '0x3110c39b428221012934A7F617913b095BC1078C',
    standardContractType: 'ERC1155',
    chain,
    method: 'balanceOf',
    parameters: [
      ':userAddress',
      '9541'
    ],
    returnValueTest: {
      comparator: '>',
      value: '0'
    }
  }
]

Must posess a specific ERC721 token (NFT)

In this example, the token contract's address is 0x319ba3aab86e04a37053e984bd411b2c63bf229e and the token id we are checking for is 9541.

const accessControlConditions = [
  {
    contractAddress: '0x319ba3aab86e04a37053e984bd411b2c63bf229e',
    standardContractType: 'ERC721',
    chain,
    method: 'ownerOf',
    parameters: [
      '5954'
    ],
    returnValueTest: {
      comparator: '=',
      value: ':userAddress'
    }
  }
]

Must posess any token in an ERC721 collection (NFT Collection)

In this example, the token contract's address is 0x319ba3aab86e04a37053e984bd411b2c63bf229e.

const accessControlConditions = [
  {
    contractAddress: '0x319ba3aab86e04a37053e984bd411b2c63bf229e',
    standardContractType: 'ERC721',
    chain,
    method: 'balanceOf',
    parameters: [
      ':userAddress'
    ],
    returnValueTest: {
      comparator: '>',
      value: '0'
    }
  }
]

Must posess at least one ERC20 token

In this example, the token contract's address is 0x3110c39b428221012934A7F617913b095BC1078C.

const accessControlConditions = [
  {
    contractAddress: '0xc0ad7861fe8848002a3d9530999dd29f6b6cae75',
    standardContractType: 'ERC20',
    chain,
    method: 'balanceOf',
    parameters: [
      ':userAddress'
    ],
    returnValueTest: {
      comparator: '>',
      value: '0'
    }
  }
]

Must posess at least 0.00001 ETH

In this example, we are checking the ETH balance of the user's address and making sure it's above 0.00001 ETH. Note that the return value is in Wei, so we specified 0.00001 ETH as 10000000000000 Wei.

const accessControlConditions = [
  {
    contractAddress: '',
    standardContractType: '',
    chain,
    method: 'eth_getBalance',
    parameters: [
      ':userAddress',
      'latest'
    ],
    returnValueTest: {
      comparator: '>=',
      value: '10000000000000'
    }
  }
]

Must be a member of a DAO (MolochDAOv2.1, also supports DAOHaus)

In this example, we are checking that the user is a member of a MolochDAOv2.1. DAOHaus DAOs are also MolochDAOv2.1 and therefore are also supported. This checks that the user is a member of the DAO and also that they are not jailed. This example checks the DAO contract at 0x50D8EB685a9F262B13F28958aBc9670F06F819d9 on the xDai chain.

const accessControlConditions = [
  {
    contractAddress: '0x50D8EB685a9F262B13F28958aBc9670F06F819d9',
    standardContractType: 'MolochDAOv2.1',
    chain,
    method: 'members',
    parameters: [
      ':userAddress',
    ],
    returnValueTest: {
      comparator: '=',
      value: 'true'
    }
  }
]

Must be a subscriber to a creator on creaton.io

In this example, we are checking that the user is a subscriber to a creator on creaton.io. This example checks the Creator contract at 0x50D8EB685a9F262B13F28958aBc9670F06F819d9 on the Mumbai chain.

const accessControlConditions = [
  {
    contractAddress: '0x77c0612bb672a52c60c7a71b898853570bd2bbbb',
    standardContractType: 'Creaton',
    chain,
    method: 'subscribers',
    parameters: [
      ':userAddress',
    ],
    returnValueTest: {
      comparator: '=',
      value: 'true'
    }
  }
]

A specific wallet address

In this example, we are checking that the user is in posession of a specific wallet address 0x50e2dac5e78B5905CB09495547452cEE64426db2

const accessControlConditions = [
  {
    contractAddress: '',
    standardContractType: '',
    chain,
    method: '',
    parameters: [
      ':userAddress',
    ],
    returnValueTest: {
      comparator: '=',
      value: '0x50e2dac5e78B5905CB09495547452cEE64426db2'
    }
  }
]

SDK Error Handling

Errors are thrown as exceptions when something has gone wrong. Errors are objects with a message, name, and errorCode. Possible codes are documented below.

Not Authorized

  • errorCode: not_authorized
  • Reason: Thrown when the user does not have access to decrypt or is unauthorized to receive a JWT for an item.

Wrong Network

  • errorCode: wrong_network
  • Reason: The user is on the wrong network. For example, this may mean the user has ethereum selected in their wallet but they were trying to use polygon for the current operation.

Wallet Error Handling

Metamask and other wallets throw errors themselves. The format for those exceptions can be found here: https://docs.metamask.io/guide/ethereum-provider.html#errors

API

You can find API documentation at https://lit-protocol.github.io/lit-js-sdk/api_docs_html/index.html

Tests

Currently we have manual tests that you can run in the browser in manual_tests.html. To run these, set up a HTTP server in the build folder. We use python for this with the built in SimpleHTTPServer module by running "python2 -m SimpleHTTPServer" and then going to "http://localhost:8000/manual_tests.html" in a browser.

There is also an attempt at automated tests in the tests folder but running it with nodejs does not work because this project is bundled. An attempt at bundling the tests as well is in esbuild-tests.js which should work someday, but the project depends on fetch and I gave up when trying to inject fetch into esbuild.

Questions or Support

Email chris@litprotocol.com for help.