The Tokenization API enables developers to generate Lab (OCL) membership agreements and tokenize Labs into IP Tokens (IPTs) on Base. It combines offchain GraphQL mutations with onchain blockchain transactions to create legally-bound, tradeable research assets.
Capabilities:
- Generate Lab (OCL) membership agreements
- Tokenize Labs into IP Tokens (IPTs) on Base
- Manage the complete tokenization lifecycle
- Integrate with smart contracts via viem/ethers
All Tokenization API mutations require an API key.
To request an API key and access to the full technical integration guide:
- Join our Discord community
- Contact the Molecule team with:
- Your use case and project details
- Expected tokenization volume
- You'll receive:
- API Key for authentication
- Technical Integration Guide with complete code examples and ABI files
Include the API key in all requests using the x-api-key header:
x-api-key: YOUR_API_KEYProduction: https://production.graphql.api.molecule.xyz/graphql
Staging: https://staging.graphql.api.molecule.xyz/graphql
Generate the membership agreement for an onchain lab (OCL) — the terms document an IP Token holder accepts when the Lab is tokenized. It returns an agreementKey (an S3 object key) paired with a SHA-256 agreementContentHash that binds the signature to the exact document.
Input:
agreementData(AWSJSON): Stringified object with the fields below.
Required Fields in agreementData:
{
"oclId": "0x0101000000000000000000000000000000000000000000000000000000000042",
"symbol": "LAB-SYMBOL",
"title": "Lab Membership Agreement"
}| Field | Type | Required | Description |
|---|---|---|---|
| oclId | String | Yes | Canonical 32-byte oclId of the lab (0x + 64 hex) |
| symbol | String | Yes | Lab symbol |
| title | String | No | Optional agreement title |
Response:
{
"agreementKey": "0x0101...0042/agreements/3f2a....json",
"agreementUrl": "https://...",
"agreementContentHash": "0xabcdef...",
"agreementType": "OCL_MEMBERSHIP",
"generatedAt": "2026-07-15T10:30:00.000Z",
"isSuccess": true
}Example Request:
curl -X POST https://production.graphql.api.molecule.xyz/graphql \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"query": "mutation GenerateOclMembershipAgreement($agreementData: AWSJSON!) { generateOclMembershipAgreement(agreementData: $agreementData) { agreementKey agreementUrl agreementContentHash agreementType generatedAt isSuccess error { message } } }",
"variables": {
"agreementData": "{\"oclId\":\"0x0101000000000000000000000000000000000000000000000000000000000042\",\"symbol\":\"LAB-SYM\",\"title\":\"Lab Membership Agreement\"}"
}
}'The returned
agreementKeyandagreementContentHashare consumed by thegetOclTermsMessage(agreementKey, contentHash, labId, chainId)query to produce the message the member signs.
Labs are tokenized on Base through the OclTokenizer contract. It mints one fractional ERC-20 IP Token (IPT) per Lab — deployed as a LabToken contract — gated by a signed membership agreement.
Step 1: Generate Membership Agreement (API)
↓ Mutation: generateOclMembershipAgreement
↓ Returns: agreementKey (S3 key), agreementContentHash (SHA-256)
Step 2: Get OCL Terms Message (API)
↓ Query: getOclTermsMessage(agreementKey, contentHash, labId, chainId)
↓ Returns: the exact terms text to sign
Step 3: Sign Terms (CLIENT-SIDE)
↓ Plain personal_sign (EIP-191) over the message — not EIP-712
↓ ECDSA signatures and ERC-1271 smart-account signatures both accepted
Step 4: Tokenize (BLOCKCHAIN, Base)
↓ Smart contract: OclTokenizer.tokenize()
↓ Fee: Gas only
↓ Result: ERC-20 IP Token (LabToken) contract deployed ✓
The terms message is reconstructed onchain by OclTermsPermissioner.specificTermsV1() — the backend's getOclTermsMessage returns byte-identical text, so always sign exactly the string the API returns.
tokenize():
- Creates the Lab's ERC-20 IP Token
- Parameters:
labId(uint256): The LabNFT token IDamount(uint256): Initial supply issued to the caller (in wei)symbol(string): Token ticker symbol — the name is derived automatically asLab Tokens of Lab #<labId>s3Key(string):agreementKeyfrom Step 1contentHash(bytes32):agreementContentHashfrom Step 1signature(bytes): Signed terms from Step 3
- Caller must be the Lab's controller (the current LabNFT owner); reverts
MustControlLab()otherwise - One token per Lab — a second call reverts
AlreadyTokenized()
attachToken():
- "Bring your own token": wraps a pre-existing ERC-20 (≤ 18 decimals) in a read-only
WrappedLabTokencarrying the Lab's metadata instead of minting a new one - Parameters:
labId,s3Key,contentHash,signature,tokenContract
issue() / cap():
issue(labToken, amount, receiver)— mint additional supply; controller-onlycap(labToken)— permanently freeze issuance; controller-only, irreversible
| Network | Chain ID | OclTokenizer (proxy) |
|---|---|---|
| Base Mainnet | 8453 | 0x62F532C3f563D974deEc103AAb8cC597f4f9c84E |
| Base Sepolia | 84532 | 0xEe19e0Db8a7e59538710FAF6ed3ab655BCfCdB24 |
- API Key: Obtained from Molecule team
- Lab Control: The caller must be the Lab's controller (the current LabNFT owner)
- Base ETH Balance: Sufficient for gas fees on Base
- Token Details: Symbol and initial supply amount (the token name is derived automatically)
All mutations follow a consistent error response format:
{
"isSuccess": false,
"error": {
"message": "Error description",
"code": "ERROR_CODE",
"retryable": true
}
}| Error Code | Description | Solution |
|---|---|---|
| 401 Unauthorized | Missing or invalid API key | Check x-api-key header |
| 400 Bad Request | Invalid parameters or malformed JSON | Verify input data format |
INVALID_INPUT |
Required fields missing or malformed | Verify the input object shape |
Separately, the smart contracts revert with their own errors — most commonly AlreadyTokenized() (each Lab can only be tokenized once) and MustControlLab() (only the Lab's controller can tokenize) from the OclTokenizer.
Signature rejected onchain:
- Sign exactly the string returned by
getOclTermsMessage— the permissioner reconstructs the terms onchain and the texts must match byte-for-byte - Use a plain
personal_sign(EIP-191) signature, and sign with the Lab controller's wallet (or a smart account that validates via ERC-1271)
Blockchain Transaction Failures:
- Verify sufficient ETH balance for gas on Base
- Ensure using correct contract address for your network
- Wait for transaction confirmation before proceeding
For complete code examples including:
- The full Lab tokenization flow with code
- Smart contract ABIs and interfaces
- Error handling and retry logic
- Safe multisig integration
Contact the Molecule team to receive the full Technical Integration Guide.
// 1. Generate the membership agreement
const agreement = await generateOclMembershipAgreement(agreementData);
// 2. Get the exact terms text to sign
const terms = await getOclTermsMessage(
agreement.agreementKey, agreement.agreementContentHash, labId, chainId
);
// 3. Sign the terms (plain personal_sign)
const signature = await walletClient.signMessage({ message: terms.message });
// 4. Tokenize the Lab onchain (Base)
const txHash = await tokenize(labId, initialSupply, symbol,
agreement.agreementKey, agreement.agreementContentHash, signature);
console.log('Lab tokenized!', txHash);Contract ABIs:
- Available from the verified OclTokenizer on BaseScan, or request them from the Molecule team as part of the Technical Integration Guide
- Store intermediate results (agreement keys, signatures) securely
- Implement retry logic for failed API calls
- Wait for blockchain transaction confirmations
- Validate each step before proceeding to next
- Never expose API keys in client-side code
- Use environment variables for credentials
- Implement proper wallet key management
- Verify all signatures and authorizations
- Use Base Sepolia for development
- Test the complete workflow end-to-end
- Verify the agreement document and terms text before signing
For assistance with the Tokenization API:
- Technical Integration Guide: Contact Molecule team to receive complete documentation
- Discord: Join our community for support
- Smart Contracts: See contract addresses
Last updated: July 2026