Writes
Understanding Mutations vs Smart Contract Operationsβ
The Intuition GraphQL API provides mutations for off-chain operations like uploading metadata to IPFS. For on-chain operations like creating atoms, triples, and positions, you must use the Intuition SDK or interact directly with the smart contracts.
GraphQL mutations handle:
- Uploading metadata to IPFS (pinThing, pinPerson, pinOrganization)
Smart contract operations handle:
- Creating atoms
- Creating triples
- Taking positions (depositing/redeeming)
- All on-chain state changes
Use the SDK for smart contract interactions.
Available Mutationsβ
IPFS & Metadata Mutationsβ
| Mutation | Description | Documentation |
|---|---|---|
pinThing | Pin Thing metadata to IPFS | Pin Thing |
pinPerson | Pin Person metadata to IPFS | Pin Person |
pinOrganization | Pin Organization metadata to IPFS | Pin Organization |
uploadImage | Upload base64 image to IPFS | Upload Image |
uploadImageFromUrl | Upload image from URL to IPFS | Upload from URL |
uploadJsonToIpfs | Upload JSON metadata to IPFS | Upload JSON |
Chart and PnL operations are queries, not mutations. See:
- PnL Queries - Profit & Loss tracking
- Chart Queries - Chart generation
Pin Mutationsβ
pinThing Mutationβ
Pin a Thing entity to IPFS:
mutation PinThing($thing: PinThingInput!) {
pinThing(thing: $thing) {
hash
name
size
}
}
Variables:
{
"thing": {
"name": "Ethereum",
"description": "A decentralized blockchain platform",
"image": "ipfs://QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy",
"url": "https://ethereum.org"
}
}
pinPerson Mutationβ
Pin a Person entity to IPFS:
mutation PinPerson($person: PinPersonInput!) {
pinPerson(person: $person) {
hash
name
size
}
}
Variables:
{
"person": {
"name": "Vitalik Buterin",
"description": "Co-founder of Ethereum",
"email": "vitalik@ethereum.org",
"identifier": "vitalik.eth",
"image": "ipfs://Qm...",
"url": "https://vitalik.ca"
}
}
pinOrganization Mutationβ
Pin an Organization entity to IPFS:
mutation PinOrganization($organization: PinOrganizationInput!) {
pinOrganization(organization: $organization) {
hash
name
size
}
}
Variables:
{
"organization": {
"name": "Ethereum Foundation",
"description": "Non-profit supporting Ethereum",
"email": "info@ethereum.org",
"image": "ipfs://Qm...",
"url": "https://ethereum.foundation"
}
}
Image Upload Mutationsβ
uploadImageβ
Upload a base64-encoded image:
mutation UploadImage($image: UploadImageInput!) {
uploadImage(image: $image) {
images {
url
original_url
safe
score
model
created_at
}
}
}
uploadImageFromUrlβ
Upload an image from a URL:
mutation UploadImageFromUrl($image: UploadImageFromUrlInput!) {
uploadImageFromUrl(image: $image) {
images {
url
original_url
safe
score
}
}
}
uploadJsonToIpfsβ
Upload JSON metadata:
mutation UploadJsonToIpfs($json: jsonb!) {
uploadJsonToIpfs(json: $json) {
hash
name
size
}
}
Complete Workflow Exampleβ
Here's how to prepare metadata for atom creation:
import { GraphQLClient } from 'graphql-request'
import { API_URL_PROD } from '@0xintuition/graphql'
import { createMultivaultClient } from '@0xintuition/sdk'
const graphqlClient = new GraphQLClient(API_URL_PROD)
// Step 1: Upload image to IPFS (via GraphQL)
const imageResult = await graphqlClient.request(`
mutation UploadImageFromUrl($image: UploadImageFromUrlInput!) {
uploadImageFromUrl(image: $image) {
images {
url
}
}
}
`, { image: { url: 'https://example.com/logo.png' } })
// Step 2: Upload metadata to IPFS (via GraphQL)
const metadataResult = await graphqlClient.request(`
mutation PinThing($thing: PinThingInput!) {
pinThing(thing: $thing) {
hash
}
}
`, {
thing: {
name: 'My Project',
description: 'Description of my project',
image: imageResult.uploadImageFromUrl.images[0].url,
url: 'https://example.com'
}
})
// Step 3: Create atom on-chain (via SDK)
const multivault = createMultivaultClient(walletClient)
const atomId = await multivault.createAtom({
uri: `ipfs://${metadataResult.pinThing.hash}`
})
Best Practicesβ
1. Complete Metadataβ
Provide comprehensive metadata for better discoverability:
const thing = {
name: 'Project Name', // Required
description: 'Full description', // Recommended
image: 'ipfs://...', // Recommended
url: 'https://...' // Optional
}
2. Use IPFS URLsβ
Always reference images using IPFS URLs for permanence:
// Good - permanent
image: 'ipfs://QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy'
// Avoid - may change
image: 'https://example.com/image.png'
3. Error Handlingβ
try {
const result = await graphqlClient.request(UPLOAD_IMAGE, { image: input })
console.log('Uploaded:', result.uploadImage.images[0].url)
} catch (error) {
if (error.message.includes('File too large')) {
console.error('Image exceeds size limit')
} else if (error.message.includes('Invalid')) {
console.error('Invalid image format')
}
}
4. Size Limitsβ
| Upload Type | Maximum Size |
|---|---|
| Image (base64) | 5 MB |
| Image from URL | 10 MB |
| JSON | 1 MB |
TypeScript Integrationβ
Use the @0xintuition/graphql package for type-safe mutations:
import {
usePinThingMutation,
useUploadImageFromUrlMutation
} from '@0xintuition/graphql'
function CreateAtomForm() {
const [pinThing] = usePinThingMutation()
const [uploadImage] = useUploadImageFromUrlMutation()
const handleSubmit = async (data: FormData) => {
// Upload image first
const imageResult = await uploadImage({
variables: { image: { url: data.imageUrl } }
})
// Then pin metadata
const thingResult = await pinThing({
variables: {
thing: {
name: data.name,
description: data.description,
image: imageResult.data.uploadImageFromUrl.images[0].url,
url: data.url
}
}
})
// Use the IPFS hash for atom creation
const ipfsUri = `ipfs://${thingResult.data.pinThing.hash}`
// ... create atom via SDK
}
return <form onSubmit={handleSubmit}>...</form>
}
Related Resourcesβ
- PnL Queries - Profit & Loss tracking
- Chart Queries - Chart generation
- SDK Documentation - On-chain operations
- GraphQL Reads - Query documentation